@ttoss/http-server-mcp 0.12.4 → 0.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +319 -0
- package/dist/index.d.cts +749 -88
- package/dist/index.d.mts +905 -0
- package/dist/index.mjs +302 -0
- package/package.json +5 -5
- package/dist/esm/index.js +0 -205
- package/dist/index.d.ts +0 -244
- package/dist/index.js +0 -244
package/dist/index.d.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import * as koa from 'koa';
|
|
2
|
-
import { Context } from 'koa';
|
|
3
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
-
export { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
-
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
-
import { Router } from '@ttoss/http-server';
|
|
7
|
-
export { z } from 'zod';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Options for a single `apiCall` request.
|
|
11
|
-
*/
|
|
12
|
-
interface ApiCallOptions {
|
|
13
|
-
/**
|
|
14
|
-
* JSON-serialisable request body. Automatically serialised and sent with
|
|
15
|
-
* `Content-Type: application/json`.
|
|
16
|
-
*/
|
|
17
|
-
body?: unknown;
|
|
18
|
-
/**
|
|
19
|
-
* Additional or override headers for this specific request.
|
|
20
|
-
* These are merged on top of any headers injected from the MCP request
|
|
21
|
-
* context via `getApiHeaders`, allowing per-call overrides.
|
|
22
|
-
*/
|
|
23
|
-
headers?: Record<string, string>;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Generic HTTP helper for use inside MCP tool handlers.
|
|
27
|
-
*
|
|
28
|
-
* Accepts any full URL (third-party APIs, public APIs, etc.) or a path
|
|
29
|
-
* relative to the `apiBaseUrl` configured in `createMcpRouter`.
|
|
30
|
-
*
|
|
31
|
-
* Headers configured via `getApiHeaders` in `createMcpRouter` are injected
|
|
32
|
-
* automatically into every request, allowing transparent forwarding of auth
|
|
33
|
-
* tokens, API keys, or any other header — without coupling this helper to a
|
|
34
|
-
* specific authentication scheme. Per-call `options.headers` take precedence
|
|
35
|
-
* over context-injected headers.
|
|
36
|
-
*
|
|
37
|
-
* @param method - HTTP method (e.g. `'GET'`, `'POST'`, `'PUT'`, `'DELETE'`)
|
|
38
|
-
* @param url - Full URL **or** a path starting with `/` (appended to `apiBaseUrl`)
|
|
39
|
-
* @param options - Optional body and per-call header overrides
|
|
40
|
-
* @returns Parsed JSON response body
|
|
41
|
-
*
|
|
42
|
-
* @example Bearer token forwarding (configured once in `createMcpRouter`)
|
|
43
|
-
* ```typescript
|
|
44
|
-
* import { apiCall, createMcpRouter, McpServer } from '@ttoss/http-server-mcp';
|
|
45
|
-
*
|
|
46
|
-
* // Tool handler – no manual auth wiring needed
|
|
47
|
-
* mcpServer.registerTool('list-portfolios', { description: '...', inputSchema: {} }, async () => {
|
|
48
|
-
* const data = await apiCall('GET', '/portfolios');
|
|
49
|
-
* return { content: [{ type: 'text', text: JSON.stringify(data) }] };
|
|
50
|
-
* });
|
|
51
|
-
*
|
|
52
|
-
* const mcpRouter = createMcpRouter(mcpServer, {
|
|
53
|
-
* apiBaseUrl: `http://localhost:${process.env.PORT}/api/v1`,
|
|
54
|
-
* // Forward the caller's Bearer token to every apiCall
|
|
55
|
-
* getApiHeaders: (ctx) => ({ Authorization: ctx.headers.authorization ?? '' }),
|
|
56
|
-
* });
|
|
57
|
-
* ```
|
|
58
|
-
*
|
|
59
|
-
* @example x-api-key forwarding
|
|
60
|
-
* ```typescript
|
|
61
|
-
* const mcpRouter = createMcpRouter(mcpServer, {
|
|
62
|
-
* apiBaseUrl: 'https://internal-service/api',
|
|
63
|
-
* getApiHeaders: (ctx) => ({
|
|
64
|
-
* 'x-api-key': ctx.headers['x-api-key'] as string,
|
|
65
|
-
* }),
|
|
66
|
-
* });
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* @example Third-party or public API (full URL, no context required)
|
|
70
|
-
* ```typescript
|
|
71
|
-
* const weather = await apiCall('GET', 'https://api.weather.com/current?city=Berlin');
|
|
72
|
-
* const created = await apiCall('POST', 'https://api.example.com/items', {
|
|
73
|
-
* body: { name: 'widget' },
|
|
74
|
-
* headers: { Authorization: 'Bearer fixed-service-token' },
|
|
75
|
-
* });
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
declare const apiCall: (method: string, url: string, options?: ApiCallOptions) => Promise<unknown>;
|
|
79
|
-
/**
|
|
80
|
-
* Options for configuring the MCP router
|
|
81
|
-
*/
|
|
82
|
-
interface McpRouterOptions {
|
|
83
|
-
/**
|
|
84
|
-
* The HTTP path where the MCP server will be mounted
|
|
85
|
-
* @default '/mcp'
|
|
86
|
-
*/
|
|
87
|
-
path?: string;
|
|
88
|
-
/**
|
|
89
|
-
* Optional session ID generator for stateful MCP servers.
|
|
90
|
-
* When provided, a single shared transport is created and sessions are tracked.
|
|
91
|
-
* When undefined (default), the server operates in stateless mode where each
|
|
92
|
-
* HTTP request uses its own transport instance.
|
|
93
|
-
*/
|
|
94
|
-
sessionIdGenerator?: () => string;
|
|
95
|
-
/**
|
|
96
|
-
* Base URL prepended to relative paths passed to `apiCall` (paths starting
|
|
97
|
-
* with `/`). Tool handlers can then call `apiCall('GET', '/resource')` without
|
|
98
|
-
* specifying a host.
|
|
99
|
-
*
|
|
100
|
-
* @example 'http://localhost:3000/api/v1'
|
|
101
|
-
*/
|
|
102
|
-
apiBaseUrl?: string;
|
|
103
|
-
/**
|
|
104
|
-
* Called once per incoming MCP HTTP request. Return a plain object whose
|
|
105
|
-
* key-value pairs will be merged into the headers of every `apiCall` made
|
|
106
|
-
* within that request's tool handlers.
|
|
107
|
-
*
|
|
108
|
-
* Use this to forward any header from the MCP request — Bearer tokens, API
|
|
109
|
-
* keys, tenant IDs, trace headers, etc. — without coupling tool handlers to
|
|
110
|
-
* a specific authentication scheme.
|
|
111
|
-
*
|
|
112
|
-
* @example Forward a Bearer token
|
|
113
|
-
* ```typescript
|
|
114
|
-
* getApiHeaders: (ctx) => ({ Authorization: ctx.headers.authorization ?? '' })
|
|
115
|
-
* ```
|
|
116
|
-
*
|
|
117
|
-
* @example Forward an x-api-key header
|
|
118
|
-
* ```typescript
|
|
119
|
-
* getApiHeaders: (ctx) => ({ 'x-api-key': ctx.headers['x-api-key'] as string })
|
|
120
|
-
* ```
|
|
121
|
-
*
|
|
122
|
-
* @example Inject a static service-to-service key
|
|
123
|
-
* ```typescript
|
|
124
|
-
* getApiHeaders: () => ({ 'x-internal-key': process.env.INTERNAL_API_KEY! })
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
getApiHeaders?: (ctx: Context) => Record<string, string>;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Creates a Koa router configured to handle MCP protocol requests
|
|
131
|
-
*
|
|
132
|
-
* @param server - The MCP server instance with registered tools and resources
|
|
133
|
-
* @param options - Configuration options for the router
|
|
134
|
-
* @returns A Koa Router instance configured for MCP
|
|
135
|
-
*
|
|
136
|
-
* @example
|
|
137
|
-
* ```typescript
|
|
138
|
-
* import { App, bodyParser } from '@ttoss/http-server';
|
|
139
|
-
* import { createMcpRouter, McpServer, z } from '@ttoss/http-server-mcp';
|
|
140
|
-
*
|
|
141
|
-
* const mcpServer = new McpServer({
|
|
142
|
-
* name: 'my-server',
|
|
143
|
-
* version: '1.0.0',
|
|
144
|
-
* });
|
|
145
|
-
*
|
|
146
|
-
* mcpServer.registerTool(
|
|
147
|
-
* 'hello',
|
|
148
|
-
* {
|
|
149
|
-
* description: 'Say hello',
|
|
150
|
-
* inputSchema: { name: z.string() },
|
|
151
|
-
* },
|
|
152
|
-
* async ({ name }) => ({
|
|
153
|
-
* content: [{ type: 'text', text: `Hello, ${name}!` }],
|
|
154
|
-
* })
|
|
155
|
-
* );
|
|
156
|
-
*
|
|
157
|
-
* const app = new App();
|
|
158
|
-
* app.use(bodyParser());
|
|
159
|
-
*
|
|
160
|
-
* const mcpRouter = createMcpRouter(mcpServer);
|
|
161
|
-
* app.use(mcpRouter.routes());
|
|
162
|
-
*
|
|
163
|
-
* app.listen(3000);
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
declare const createMcpRouter: (server: McpServer, options?: McpRouterOptions) => Router<koa.DefaultState, koa.DefaultContext>;
|
|
167
|
-
/**
|
|
168
|
-
* A plain JSON Schema object (draft-07 compatible) describing the shape of a
|
|
169
|
-
* tool's input. Used with {@link registerToolFromSchema} as an alternative to
|
|
170
|
-
* providing a Zod shape, enabling lossless round-trips for schemas that contain
|
|
171
|
-
* features not expressible in Zod v3 (`anyOf`, `$ref`, `pattern`, `allOf`, …).
|
|
172
|
-
*/
|
|
173
|
-
interface JsonObjectSchema {
|
|
174
|
-
type: 'object';
|
|
175
|
-
properties?: Record<string, unknown>;
|
|
176
|
-
required?: string[];
|
|
177
|
-
[key: string]: unknown;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Parameters accepted by {@link registerToolFromSchema}.
|
|
181
|
-
*/
|
|
182
|
-
interface RegisterToolFromSchemaParams {
|
|
183
|
-
/** Unique tool name. */
|
|
184
|
-
name: string;
|
|
185
|
-
/** Human-readable description shown to the AI client. */
|
|
186
|
-
description?: string;
|
|
187
|
-
/**
|
|
188
|
-
* Plain JSON Schema that describes the tool's input object.
|
|
189
|
-
* This schema is forwarded verbatim over the MCP wire protocol, so any
|
|
190
|
-
* JSON Schema feature (`anyOf`, `$ref`, `pattern`, …) is preserved without
|
|
191
|
-
* loss. Defaults to `{ type: 'object', properties: {} }` when omitted.
|
|
192
|
-
*/
|
|
193
|
-
inputSchema?: JsonObjectSchema;
|
|
194
|
-
/**
|
|
195
|
-
* Tool handler invoked when the AI client calls the tool.
|
|
196
|
-
* Receives the raw request arguments as a plain object (no Zod validation is
|
|
197
|
-
* applied, so the shape matches whatever the client sends).
|
|
198
|
-
*/
|
|
199
|
-
handler: (args: Record<string, unknown>) => CallToolResult | Promise<CallToolResult>;
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Registers a tool on an MCP server using a **plain JSON Schema** object for
|
|
203
|
-
* `inputSchema` instead of a Zod shape.
|
|
204
|
-
*
|
|
205
|
-
* This is useful when tool definitions are shared between the MCP server and an
|
|
206
|
-
* AI SDK agent (e.g. Vercel AI SDK's `tool()` helper), because both consume a
|
|
207
|
-
* plain JSON Schema at runtime. Using this helper eliminates the lossy
|
|
208
|
-
* JSON-Schema→Zod conversion that would otherwise be required.
|
|
209
|
-
*
|
|
210
|
-
* Internally the helper:
|
|
211
|
-
* 1. Registers the tool via the standard `McpServer.registerTool` API using a
|
|
212
|
-
* Zod `z.record(z.unknown())` pass-through schema so that the existing MCP
|
|
213
|
-
* `tools/call` handler correctly routes requests and delivers raw args to
|
|
214
|
-
* your handler.
|
|
215
|
-
* 2. Stores the original JSON Schema and patches the `tools/list` response
|
|
216
|
-
* handler so clients receive the verbatim JSON Schema over the wire.
|
|
217
|
-
*
|
|
218
|
-
* @param server - The `McpServer` instance to register the tool on.
|
|
219
|
-
* @param params - Tool configuration including name, description, inputSchema,
|
|
220
|
-
* and handler.
|
|
221
|
-
*
|
|
222
|
-
* @example
|
|
223
|
-
* ```typescript
|
|
224
|
-
* import { registerToolFromSchema, McpServer } from '@ttoss/http-server-mcp';
|
|
225
|
-
*
|
|
226
|
-
* const server = new McpServer({ name: 'my-server', version: '1.0.0' });
|
|
227
|
-
*
|
|
228
|
-
* registerToolFromSchema(server, {
|
|
229
|
-
* name: 'get-project',
|
|
230
|
-
* description: 'Get a project by ID',
|
|
231
|
-
* inputSchema: {
|
|
232
|
-
* type: 'object',
|
|
233
|
-
* properties: { id: { type: 'string', description: 'Project public ID' } },
|
|
234
|
-
* required: ['id'],
|
|
235
|
-
* },
|
|
236
|
-
* handler: async ({ id }) => ({
|
|
237
|
-
* content: [{ type: 'text', text: `Project: ${id}` }],
|
|
238
|
-
* }),
|
|
239
|
-
* });
|
|
240
|
-
* ```
|
|
241
|
-
*/
|
|
242
|
-
declare const registerToolFromSchema: (server: McpServer, params: RegisterToolFromSchemaParams) => void;
|
|
243
|
-
|
|
244
|
-
export { type ApiCallOptions, type JsonObjectSchema, type McpRouterOptions, type RegisterToolFromSchemaParams, apiCall, createMcpRouter, registerToolFromSchema };
|
package/dist/index.js
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __name = (target, value) => __defProp(target, "name", {
|
|
9
|
-
value,
|
|
10
|
-
configurable: true
|
|
11
|
-
});
|
|
12
|
-
var __export = (target, all) => {
|
|
13
|
-
for (var name in all) __defProp(target, name, {
|
|
14
|
-
get: all[name],
|
|
15
|
-
enumerable: true
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
var __copyProps = (to, from, except, desc) => {
|
|
19
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
-
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
21
|
-
get: () => from[key],
|
|
22
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
return to;
|
|
26
|
-
};
|
|
27
|
-
var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
|
|
28
|
-
value: true
|
|
29
|
-
}), mod);
|
|
30
|
-
|
|
31
|
-
// src/index.ts
|
|
32
|
-
var index_exports = {};
|
|
33
|
-
__export(index_exports, {
|
|
34
|
-
McpServer: () => import_mcp.McpServer,
|
|
35
|
-
apiCall: () => apiCall,
|
|
36
|
-
createMcpRouter: () => createMcpRouter,
|
|
37
|
-
registerToolFromSchema: () => registerToolFromSchema,
|
|
38
|
-
z: () => import_zod2.z
|
|
39
|
-
});
|
|
40
|
-
module.exports = __toCommonJS(index_exports);
|
|
41
|
-
var import_node_async_hooks = require("async_hooks");
|
|
42
|
-
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
43
|
-
var import_http_server = require("@ttoss/http-server");
|
|
44
|
-
var import_zod = require("zod");
|
|
45
|
-
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
46
|
-
var import_zod2 = require("zod");
|
|
47
|
-
var requestContextStore = new import_node_async_hooks.AsyncLocalStorage();
|
|
48
|
-
var apiCall = /* @__PURE__ */__name(async (method, url, options) => {
|
|
49
|
-
const context = requestContextStore.getStore();
|
|
50
|
-
let resolvedUrl = url;
|
|
51
|
-
if (url.startsWith("/")) {
|
|
52
|
-
if (!context?.apiBaseUrl) {
|
|
53
|
-
throw new Error(`apiCall received a relative path ("${url}") but no apiBaseUrl is configured. Either pass a full URL or set apiBaseUrl in createMcpRouter options.`);
|
|
54
|
-
}
|
|
55
|
-
resolvedUrl = `${context.apiBaseUrl.replace(/\/$/, "")}${url}`;
|
|
56
|
-
}
|
|
57
|
-
const hasBody = options?.body !== void 0;
|
|
58
|
-
const headers = {
|
|
59
|
-
...(context !== void 0 ? context.apiHeaders : {}),
|
|
60
|
-
...(options?.headers ?? {})
|
|
61
|
-
};
|
|
62
|
-
const hasExplicitContentType = Object.keys(headers).some(headerName => {
|
|
63
|
-
return headerName.toLowerCase() === "content-type";
|
|
64
|
-
});
|
|
65
|
-
if (hasBody && !hasExplicitContentType) {
|
|
66
|
-
headers["Content-Type"] = "application/json";
|
|
67
|
-
}
|
|
68
|
-
const response = await fetch(resolvedUrl, {
|
|
69
|
-
method,
|
|
70
|
-
headers,
|
|
71
|
-
body: hasBody ? JSON.stringify(options.body) : void 0
|
|
72
|
-
});
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
const err = await response.json().catch(() => {
|
|
75
|
-
return {
|
|
76
|
-
error: response.statusText
|
|
77
|
-
};
|
|
78
|
-
});
|
|
79
|
-
throw new Error(err.error || `HTTP ${response.status}`);
|
|
80
|
-
}
|
|
81
|
-
if (response.status === 204 || response.status === 205) {
|
|
82
|
-
return void 0;
|
|
83
|
-
}
|
|
84
|
-
const contentType = response.headers.get("content-type");
|
|
85
|
-
if (contentType?.includes("application/json")) {
|
|
86
|
-
return response.json();
|
|
87
|
-
}
|
|
88
|
-
return response.text();
|
|
89
|
-
}, "apiCall");
|
|
90
|
-
var createMcpRouter = /* @__PURE__ */__name((server, options = {}) => {
|
|
91
|
-
const {
|
|
92
|
-
path = "/mcp",
|
|
93
|
-
sessionIdGenerator,
|
|
94
|
-
apiBaseUrl,
|
|
95
|
-
getApiHeaders
|
|
96
|
-
} = options;
|
|
97
|
-
const isStateful = sessionIdGenerator !== void 0;
|
|
98
|
-
const needsContext = apiBaseUrl !== void 0 || getApiHeaders !== void 0;
|
|
99
|
-
let sharedTransport;
|
|
100
|
-
if (isStateful) {
|
|
101
|
-
sharedTransport = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
102
|
-
sessionIdGenerator,
|
|
103
|
-
enableJsonResponse: true
|
|
104
|
-
});
|
|
105
|
-
server.connect(sharedTransport);
|
|
106
|
-
}
|
|
107
|
-
let statelessQueue = Promise.resolve();
|
|
108
|
-
const enqueueStateless = /* @__PURE__ */__name(work => {
|
|
109
|
-
const result = statelessQueue.then(() => {
|
|
110
|
-
return work();
|
|
111
|
-
});
|
|
112
|
-
statelessQueue = result.catch(() => {});
|
|
113
|
-
return result;
|
|
114
|
-
}, "enqueueStateless");
|
|
115
|
-
const router = new import_http_server.Router();
|
|
116
|
-
const handleWithContext = /* @__PURE__ */__name(async (ctx, body) => {
|
|
117
|
-
const apiHeaders = getApiHeaders ? getApiHeaders(ctx) : {};
|
|
118
|
-
const runRequest = /* @__PURE__ */__name(async transport => {
|
|
119
|
-
await transport.handleRequest(ctx.req, ctx.res, body);
|
|
120
|
-
ctx.respond = false;
|
|
121
|
-
}, "runRequest");
|
|
122
|
-
if (isStateful && sharedTransport) {
|
|
123
|
-
if (needsContext) {
|
|
124
|
-
await requestContextStore.run({
|
|
125
|
-
apiBaseUrl,
|
|
126
|
-
apiHeaders
|
|
127
|
-
}, () => {
|
|
128
|
-
return runRequest(sharedTransport);
|
|
129
|
-
});
|
|
130
|
-
} else {
|
|
131
|
-
await runRequest(sharedTransport);
|
|
132
|
-
}
|
|
133
|
-
} else {
|
|
134
|
-
await enqueueStateless(async () => {
|
|
135
|
-
const transport = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
136
|
-
sessionIdGenerator: void 0,
|
|
137
|
-
enableJsonResponse: true
|
|
138
|
-
});
|
|
139
|
-
try {
|
|
140
|
-
await server.connect(transport);
|
|
141
|
-
if (needsContext) {
|
|
142
|
-
await requestContextStore.run({
|
|
143
|
-
apiBaseUrl,
|
|
144
|
-
apiHeaders
|
|
145
|
-
}, () => {
|
|
146
|
-
return runRequest(transport);
|
|
147
|
-
});
|
|
148
|
-
} else {
|
|
149
|
-
await runRequest(transport);
|
|
150
|
-
}
|
|
151
|
-
} finally {
|
|
152
|
-
await transport.close();
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}, "handleWithContext");
|
|
157
|
-
router.post(path, async ctx => {
|
|
158
|
-
try {
|
|
159
|
-
await handleWithContext(ctx, ctx.request.body);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
if (!ctx.res.headersSent) {
|
|
162
|
-
ctx.status = 500;
|
|
163
|
-
ctx.body = {
|
|
164
|
-
error: error instanceof Error ? error.message : "Internal server error"
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
router.delete(path, async ctx => {
|
|
170
|
-
try {
|
|
171
|
-
await handleWithContext(ctx);
|
|
172
|
-
} catch (error) {
|
|
173
|
-
if (!ctx.res.headersSent) {
|
|
174
|
-
ctx.status = 500;
|
|
175
|
-
ctx.body = {
|
|
176
|
-
error: error instanceof Error ? error.message : "Internal server error"
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
return router;
|
|
182
|
-
}, "createMcpRouter");
|
|
183
|
-
var rawSchemaRegistryMap = /* @__PURE__ */new WeakMap();
|
|
184
|
-
var patchedServerSet = /* @__PURE__ */new WeakSet();
|
|
185
|
-
var registerToolFromSchema = /* @__PURE__ */__name((server, params) => {
|
|
186
|
-
const {
|
|
187
|
-
name,
|
|
188
|
-
description,
|
|
189
|
-
inputSchema = {
|
|
190
|
-
type: "object",
|
|
191
|
-
properties: {}
|
|
192
|
-
},
|
|
193
|
-
handler
|
|
194
|
-
} = params;
|
|
195
|
-
if (!rawSchemaRegistryMap.has(server)) {
|
|
196
|
-
rawSchemaRegistryMap.set(server, /* @__PURE__ */new Map());
|
|
197
|
-
}
|
|
198
|
-
const registry = rawSchemaRegistryMap.get(server);
|
|
199
|
-
registry.set(name, inputSchema);
|
|
200
|
-
server.registerTool(name, {
|
|
201
|
-
description,
|
|
202
|
-
inputSchema: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown())
|
|
203
|
-
}, async args => {
|
|
204
|
-
return handler(args);
|
|
205
|
-
});
|
|
206
|
-
if (!patchedServerSet.has(server)) {
|
|
207
|
-
patchedServerSet.add(server);
|
|
208
|
-
const rawServer = server.server;
|
|
209
|
-
const origHandler = rawServer?._requestHandlers?.get("tools/list");
|
|
210
|
-
if (!origHandler && process.env.NODE_ENV !== "production") {
|
|
211
|
-
console.warn("[registerToolFromSchema] Could not patch tools/list \u2014 internal MCP SDK structure may have changed. The tool will still be callable, but tools/list may return a Zod-derived schema instead of the verbatim JSON Schema.");
|
|
212
|
-
}
|
|
213
|
-
if (origHandler) {
|
|
214
|
-
rawServer._requestHandlers.set("tools/list", async (rawRequest, extra) => {
|
|
215
|
-
const result = await origHandler(rawRequest, extra);
|
|
216
|
-
const schemas = rawSchemaRegistryMap.get(server);
|
|
217
|
-
if (!schemas) {
|
|
218
|
-
return result;
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
...result,
|
|
222
|
-
tools: result.tools.map(tool => {
|
|
223
|
-
const raw = schemas.get(tool.name);
|
|
224
|
-
if (raw !== void 0) {
|
|
225
|
-
return {
|
|
226
|
-
...tool,
|
|
227
|
-
inputSchema: raw
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
return tool;
|
|
231
|
-
})
|
|
232
|
-
};
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}, "registerToolFromSchema");
|
|
237
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
238
|
-
0 && (module.exports = {
|
|
239
|
-
McpServer,
|
|
240
|
-
apiCall,
|
|
241
|
-
createMcpRouter,
|
|
242
|
-
registerToolFromSchema,
|
|
243
|
-
z
|
|
244
|
-
});
|