feat(transport): register all 9 tools and add HTTP transport
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -10,7 +10,8 @@
|
|||||||
"dev": "tsx src/index.ts",
|
"dev": "tsx src/index.ts",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit",
|
||||||
|
"start:http": "MCP_TRANSPORT=http node dist/index.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
71
src/index.ts
71
src/index.ts
@@ -1,13 +1,82 @@
|
|||||||
|
import { createServer as createHttpServer } from 'node:http';
|
||||||
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||||
import { loadConfig } from './config/index.js';
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||||
|
import { loadConfig, getTransportConfig } from './config/index.js';
|
||||||
import { createServer } from './server.js';
|
import { createServer } from './server.js';
|
||||||
|
|
||||||
|
async function readRequestBody(req: IncomingMessage): Promise<unknown> {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
for await (const chunk of req) {
|
||||||
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||||
|
}
|
||||||
|
const raw = Buffer.concat(chunks).toString('utf-8');
|
||||||
|
if (!raw) return undefined;
|
||||||
|
return JSON.parse(raw);
|
||||||
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
|
const rawTransport = process.env['MCP_TRANSPORT'];
|
||||||
|
if (rawTransport && rawTransport !== 'stdio' && rawTransport !== 'http') {
|
||||||
|
console.error(
|
||||||
|
`FATAL: Invalid MCP_TRANSPORT '${rawTransport}': must be 'stdio' or 'http'`,
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
const { transport: transportType, httpPort } = getTransportConfig();
|
||||||
|
|
||||||
|
if (transportType === 'stdio') {
|
||||||
const server = createServer(config);
|
const server = createServer(config);
|
||||||
const transport = new StdioServerTransport();
|
const transport = new StdioServerTransport();
|
||||||
await server.connect(transport);
|
await server.connect(transport);
|
||||||
console.error('Sage X3 MCP server started (stdio transport)');
|
console.error('Sage X3 MCP server started (stdio transport)');
|
||||||
|
} else {
|
||||||
|
const httpServer = createHttpServer(
|
||||||
|
async (req: IncomingMessage, res: ServerResponse) => {
|
||||||
|
if (req.url !== '/mcp') {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end('Not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const body =
|
||||||
|
req.method === 'POST' ? await readRequestBody(req) : undefined;
|
||||||
|
const transport = new StreamableHTTPServerTransport({
|
||||||
|
sessionIdGenerator: undefined,
|
||||||
|
});
|
||||||
|
const server = createServer(config);
|
||||||
|
await server.connect(transport);
|
||||||
|
await transport.handleRequest(req, res, body);
|
||||||
|
} catch (error) {
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end('Internal server error');
|
||||||
|
}
|
||||||
|
console.error(
|
||||||
|
'Request error:',
|
||||||
|
error instanceof Error ? error.message : String(error),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
httpServer.listen(httpPort, () => {
|
||||||
|
console.error(
|
||||||
|
`Sage X3 MCP server started (HTTP transport on port ${httpPort})`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const shutdown = (): void => {
|
||||||
|
console.error('Shutting down...');
|
||||||
|
httpServer.close();
|
||||||
|
process.exit(0);
|
||||||
|
};
|
||||||
|
process.on('SIGINT', shutdown);
|
||||||
|
process.on('SIGTERM', shutdown);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch((error: unknown) => {
|
main().catch((error: unknown) => {
|
||||||
|
|||||||
@@ -3,6 +3,14 @@ import type { SageConfig } from './types/index.js';
|
|||||||
import { RestClient } from './clients/rest-client.js';
|
import { RestClient } from './clients/rest-client.js';
|
||||||
import { SoapClient } from './clients/soap-client.js';
|
import { SoapClient } from './clients/soap-client.js';
|
||||||
import { registerHealthTool } from './tools/sage-health.js';
|
import { registerHealthTool } from './tools/sage-health.js';
|
||||||
|
import { registerQueryTool } from './tools/sage-query.js';
|
||||||
|
import { registerReadTool } from './tools/sage-read.js';
|
||||||
|
import { registerSearchTool } from './tools/sage-search.js';
|
||||||
|
import { registerListEntitiesTool } from './tools/sage-list-entities.js';
|
||||||
|
import { registerGetContextTool } from './tools/sage-get-context.js';
|
||||||
|
import { registerSoapReadTool } from './tools/sage-soap-read.js';
|
||||||
|
import { registerSoapQueryTool } from './tools/sage-soap-query.js';
|
||||||
|
import { registerDescribeEntityTool } from './tools/sage-describe-entity.js';
|
||||||
|
|
||||||
export function createServer(config: SageConfig): McpServer {
|
export function createServer(config: SageConfig): McpServer {
|
||||||
const server = new McpServer(
|
const server = new McpServer(
|
||||||
@@ -14,6 +22,14 @@ export function createServer(config: SageConfig): McpServer {
|
|||||||
const soapClient = new SoapClient(config);
|
const soapClient = new SoapClient(config);
|
||||||
|
|
||||||
registerHealthTool(server, restClient, soapClient, config);
|
registerHealthTool(server, restClient, soapClient, config);
|
||||||
|
registerQueryTool(server, restClient);
|
||||||
|
registerReadTool(server, restClient);
|
||||||
|
registerSearchTool(server, restClient);
|
||||||
|
registerListEntitiesTool(server, restClient);
|
||||||
|
registerGetContextTool(server, restClient);
|
||||||
|
registerSoapReadTool(server, soapClient);
|
||||||
|
registerSoapQueryTool(server, soapClient);
|
||||||
|
registerDescribeEntityTool(server, soapClient);
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/tools/index.ts
Normal file
9
src/tools/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export { registerHealthTool } from './sage-health.js';
|
||||||
|
export { registerQueryTool } from './sage-query.js';
|
||||||
|
export { registerReadTool } from './sage-read.js';
|
||||||
|
export { registerSearchTool } from './sage-search.js';
|
||||||
|
export { registerListEntitiesTool } from './sage-list-entities.js';
|
||||||
|
export { registerGetContextTool } from './sage-get-context.js';
|
||||||
|
export { registerSoapReadTool } from './sage-soap-read.js';
|
||||||
|
export { registerSoapQueryTool } from './sage-soap-query.js';
|
||||||
|
export { registerDescribeEntityTool } from './sage-describe-entity.js';
|
||||||
@@ -5,5 +5,6 @@ export default defineConfig({
|
|||||||
globals: true,
|
globals: true,
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
passWithNoTests: true,
|
passWithNoTests: true,
|
||||||
|
exclude: ['**/node_modules/**', '**/dist/**'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user