@mastra/mcp 0.5.0-alpha.2 → 0.5.0-alpha.4
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +18 -0
- package/dist/_tsup-dts-rollup.d.cts +28 -1
- package/dist/_tsup-dts-rollup.d.ts +28 -1
- package/dist/index.cjs +232 -41
- package/dist/index.js +232 -40
- package/package.json +7 -3
- package/src/__fixtures__/fire-crawl-complex-schema.ts +1013 -0
- package/src/client.test.ts +4 -4
- package/src/client.ts +69 -38
- package/src/configuration.test.ts +37 -0
- package/src/server.test.ts +183 -23
- package/src/server.ts +98 -10
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/mcp@0.5.0-alpha.
|
|
2
|
+
> @mastra/mcp@0.5.0-alpha.4 build /home/runner/work/mastra/mastra/packages/mcp
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.4.0
|
|
8
8
|
[34mTSC[39m Build start
|
|
9
|
-
[32mTSC[39m ⚡️ Build success in
|
|
9
|
+
[32mTSC[39m ⚡️ Build success in 17734ms
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
11
|
[34mCLI[39m Target: es2022
|
|
12
12
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
13
13
|
[36mWriting package typings: /home/runner/work/mastra/mastra/packages/mcp/dist/_tsup-dts-rollup.d.ts[39m
|
|
14
14
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
15
15
|
[36mWriting package typings: /home/runner/work/mastra/mastra/packages/mcp/dist/_tsup-dts-rollup.d.cts[39m
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 13432ms
|
|
17
17
|
[34mCLI[39m Cleaning output folder
|
|
18
18
|
[34mESM[39m Build start
|
|
19
19
|
[34mCJS[39m Build start
|
|
20
|
-
[
|
|
21
|
-
[
|
|
22
|
-
[
|
|
23
|
-
[
|
|
20
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m32.44 KB[39m
|
|
21
|
+
[32mCJS[39m ⚡️ Build success in 1027ms
|
|
22
|
+
[32mESM[39m [1mdist/index.js [22m[32m32.14 KB[39m
|
|
23
|
+
[32mESM[39m ⚡️ Build success in 1029ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @mastra/mcp
|
|
2
2
|
|
|
3
|
+
## 0.5.0-alpha.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 396be50: updated mcp server routes for MCP SSE for use with hono server
|
|
8
|
+
- da082f8: Switch from serializing json schema string as a function to a library that creates a zod object in memory from the json schema. This reduces the errors we were seeing from zod schema code that could not be serialized.
|
|
9
|
+
- Updated dependencies [396be50]
|
|
10
|
+
- Updated dependencies [c3bd795]
|
|
11
|
+
- Updated dependencies [da082f8]
|
|
12
|
+
- Updated dependencies [a5810ce]
|
|
13
|
+
- @mastra/core@0.9.4-alpha.3
|
|
14
|
+
|
|
15
|
+
## 0.5.0-alpha.3
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- bb1e2c8: Make sure mcp handlers can only be registered once
|
|
20
|
+
|
|
3
21
|
## 0.5.0-alpha.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -4,11 +4,14 @@ import type * as http from 'node:http';
|
|
|
4
4
|
import { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { MastraBase } from '@mastra/core/base';
|
|
6
6
|
import { MCPServerBase } from '@mastra/core/mcp';
|
|
7
|
+
import type { MCPServerHonoSSEOptions } from '@mastra/core/mcp';
|
|
7
8
|
import type { MCPServerSSEOptions } from '@mastra/core/mcp';
|
|
8
9
|
import type { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
9
10
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
11
|
import type { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
11
12
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
13
|
+
import type { SSEStreamingApi } from 'hono/streaming';
|
|
14
|
+
import { SSETransport } from 'hono-mcp-server-sse-transport';
|
|
12
15
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
16
|
import type { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
14
17
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
@@ -18,6 +21,8 @@ import { ToolExecutionContext } from '@mastra/core';
|
|
|
18
21
|
import type { ToolsInput } from '@mastra/core/agent';
|
|
19
22
|
import { z } from 'zod';
|
|
20
23
|
|
|
24
|
+
export declare const allTools: ToolsInput;
|
|
25
|
+
|
|
21
26
|
declare type BaseServerOptions = {
|
|
22
27
|
logger?: LogHandler;
|
|
23
28
|
timeout?: number;
|
|
@@ -188,7 +193,10 @@ declare class MCPServer extends MCPServerBase {
|
|
|
188
193
|
private server;
|
|
189
194
|
private stdioTransport?;
|
|
190
195
|
private sseTransport?;
|
|
196
|
+
private sseHonoTransports;
|
|
191
197
|
private streamableHTTPTransport?;
|
|
198
|
+
private listToolsHandlerIsRegistered;
|
|
199
|
+
private callToolHandlerIsRegistered;
|
|
192
200
|
/**
|
|
193
201
|
* Get the current stdio transport.
|
|
194
202
|
*/
|
|
@@ -197,6 +205,10 @@ declare class MCPServer extends MCPServerBase {
|
|
|
197
205
|
* Get the current SSE transport.
|
|
198
206
|
*/
|
|
199
207
|
getSseTransport(): SSEServerTransport | undefined;
|
|
208
|
+
/**
|
|
209
|
+
* Get the current SSE Hono transport.
|
|
210
|
+
*/
|
|
211
|
+
getSseHonoTransport(sessionId: string): SSETransport | undefined;
|
|
200
212
|
/**
|
|
201
213
|
* Get the current streamable HTTP transport.
|
|
202
214
|
*/
|
|
@@ -241,6 +253,16 @@ declare class MCPServer extends MCPServerBase {
|
|
|
241
253
|
* @param res HTTP response (must support .write/.end)
|
|
242
254
|
*/
|
|
243
255
|
startSSE({ url, ssePath, messagePath, req, res }: MCPServerSSEOptions): Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* Handles MCP-over-SSE protocol for user-provided HTTP servers.
|
|
258
|
+
* Call this from your HTTP server for both the SSE and message endpoints.
|
|
259
|
+
*
|
|
260
|
+
* @param url Parsed URL of the incoming request
|
|
261
|
+
* @param ssePath Path for establishing the SSE connection (e.g. '/sse')
|
|
262
|
+
* @param messagePath Path for POSTing client messages (e.g. '/message')
|
|
263
|
+
* @param context Incoming Hono context
|
|
264
|
+
*/
|
|
265
|
+
startHonoSSE({ url, ssePath, messagePath, context }: MCPServerHonoSSEOptions): Promise<Response>;
|
|
244
266
|
/**
|
|
245
267
|
* Handles MCP-over-StreamableHTTP protocol for user-provided HTTP servers.
|
|
246
268
|
* Call this from your HTTP server for the streamable HTTP endpoint.
|
|
@@ -258,11 +280,14 @@ declare class MCPServer extends MCPServerBase {
|
|
|
258
280
|
res: http.ServerResponse<http.IncomingMessage>;
|
|
259
281
|
options?: StreamableHTTPServerTransportOptions;
|
|
260
282
|
}): Promise<void>;
|
|
261
|
-
handlePostMessage(req: http.IncomingMessage, res: http.ServerResponse<http.IncomingMessage>): Promise<void>;
|
|
262
283
|
connectSSE({ messagePath, res, }: {
|
|
263
284
|
messagePath: string;
|
|
264
285
|
res: http.ServerResponse<http.IncomingMessage>;
|
|
265
286
|
}): Promise<void>;
|
|
287
|
+
connectHonoSSE({ messagePath, stream }: {
|
|
288
|
+
messagePath: string;
|
|
289
|
+
stream: SSEStreamingApi;
|
|
290
|
+
}): Promise<void>;
|
|
266
291
|
/**
|
|
267
292
|
* Close the MCP server and all its connections
|
|
268
293
|
*/
|
|
@@ -271,6 +296,8 @@ declare class MCPServer extends MCPServerBase {
|
|
|
271
296
|
export { MCPServer }
|
|
272
297
|
export { MCPServer as MCPServer_alias_1 }
|
|
273
298
|
|
|
299
|
+
export declare const mcpServerName = "firecrawl-mcp-fixture";
|
|
300
|
+
|
|
274
301
|
export declare const server: Server<{
|
|
275
302
|
method: string;
|
|
276
303
|
params?: {
|
|
@@ -4,11 +4,14 @@ import type * as http from 'node:http';
|
|
|
4
4
|
import { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { MastraBase } from '@mastra/core/base';
|
|
6
6
|
import { MCPServerBase } from '@mastra/core/mcp';
|
|
7
|
+
import type { MCPServerHonoSSEOptions } from '@mastra/core/mcp';
|
|
7
8
|
import type { MCPServerSSEOptions } from '@mastra/core/mcp';
|
|
8
9
|
import type { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
9
10
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
11
|
import type { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
11
12
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
13
|
+
import type { SSEStreamingApi } from 'hono/streaming';
|
|
14
|
+
import { SSETransport } from 'hono-mcp-server-sse-transport';
|
|
12
15
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
16
|
import type { StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
14
17
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
@@ -18,6 +21,8 @@ import { ToolExecutionContext } from '@mastra/core';
|
|
|
18
21
|
import type { ToolsInput } from '@mastra/core/agent';
|
|
19
22
|
import { z } from 'zod';
|
|
20
23
|
|
|
24
|
+
export declare const allTools: ToolsInput;
|
|
25
|
+
|
|
21
26
|
declare type BaseServerOptions = {
|
|
22
27
|
logger?: LogHandler;
|
|
23
28
|
timeout?: number;
|
|
@@ -188,7 +193,10 @@ declare class MCPServer extends MCPServerBase {
|
|
|
188
193
|
private server;
|
|
189
194
|
private stdioTransport?;
|
|
190
195
|
private sseTransport?;
|
|
196
|
+
private sseHonoTransports;
|
|
191
197
|
private streamableHTTPTransport?;
|
|
198
|
+
private listToolsHandlerIsRegistered;
|
|
199
|
+
private callToolHandlerIsRegistered;
|
|
192
200
|
/**
|
|
193
201
|
* Get the current stdio transport.
|
|
194
202
|
*/
|
|
@@ -197,6 +205,10 @@ declare class MCPServer extends MCPServerBase {
|
|
|
197
205
|
* Get the current SSE transport.
|
|
198
206
|
*/
|
|
199
207
|
getSseTransport(): SSEServerTransport | undefined;
|
|
208
|
+
/**
|
|
209
|
+
* Get the current SSE Hono transport.
|
|
210
|
+
*/
|
|
211
|
+
getSseHonoTransport(sessionId: string): SSETransport | undefined;
|
|
200
212
|
/**
|
|
201
213
|
* Get the current streamable HTTP transport.
|
|
202
214
|
*/
|
|
@@ -241,6 +253,16 @@ declare class MCPServer extends MCPServerBase {
|
|
|
241
253
|
* @param res HTTP response (must support .write/.end)
|
|
242
254
|
*/
|
|
243
255
|
startSSE({ url, ssePath, messagePath, req, res }: MCPServerSSEOptions): Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* Handles MCP-over-SSE protocol for user-provided HTTP servers.
|
|
258
|
+
* Call this from your HTTP server for both the SSE and message endpoints.
|
|
259
|
+
*
|
|
260
|
+
* @param url Parsed URL of the incoming request
|
|
261
|
+
* @param ssePath Path for establishing the SSE connection (e.g. '/sse')
|
|
262
|
+
* @param messagePath Path for POSTing client messages (e.g. '/message')
|
|
263
|
+
* @param context Incoming Hono context
|
|
264
|
+
*/
|
|
265
|
+
startHonoSSE({ url, ssePath, messagePath, context }: MCPServerHonoSSEOptions): Promise<Response>;
|
|
244
266
|
/**
|
|
245
267
|
* Handles MCP-over-StreamableHTTP protocol for user-provided HTTP servers.
|
|
246
268
|
* Call this from your HTTP server for the streamable HTTP endpoint.
|
|
@@ -258,11 +280,14 @@ declare class MCPServer extends MCPServerBase {
|
|
|
258
280
|
res: http.ServerResponse<http.IncomingMessage>;
|
|
259
281
|
options?: StreamableHTTPServerTransportOptions;
|
|
260
282
|
}): Promise<void>;
|
|
261
|
-
handlePostMessage(req: http.IncomingMessage, res: http.ServerResponse<http.IncomingMessage>): Promise<void>;
|
|
262
283
|
connectSSE({ messagePath, res, }: {
|
|
263
284
|
messagePath: string;
|
|
264
285
|
res: http.ServerResponse<http.IncomingMessage>;
|
|
265
286
|
}): Promise<void>;
|
|
287
|
+
connectHonoSSE({ messagePath, stream }: {
|
|
288
|
+
messagePath: string;
|
|
289
|
+
stream: SSEStreamingApi;
|
|
290
|
+
}): Promise<void>;
|
|
266
291
|
/**
|
|
267
292
|
* Close the MCP server and all its connections
|
|
268
293
|
*/
|
|
@@ -271,6 +296,8 @@ declare class MCPServer extends MCPServerBase {
|
|
|
271
296
|
export { MCPServer }
|
|
272
297
|
export { MCPServer as MCPServer_alias_1 }
|
|
273
298
|
|
|
299
|
+
export declare const mcpServerName = "firecrawl-mcp-fixture";
|
|
300
|
+
|
|
274
301
|
export declare const server: Server<{
|
|
275
302
|
method: string;
|
|
276
303
|
params?: {
|
package/dist/index.cjs
CHANGED
|
@@ -10,11 +10,11 @@ var streamableHttp_js$1 = require('@modelcontextprotocol/sdk/client/streamableHt
|
|
|
10
10
|
var protocol_js = require('@modelcontextprotocol/sdk/shared/protocol.js');
|
|
11
11
|
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
12
12
|
var exitHook = require('exit-hook');
|
|
13
|
-
var jsonSchemaToZod = require('json-schema-to-zod');
|
|
14
13
|
var zod = require('zod');
|
|
14
|
+
var zodFromJsonSchema = require('zod-from-json-schema');
|
|
15
15
|
var equal = require('fast-deep-equal');
|
|
16
16
|
var uuid = require('uuid');
|
|
17
|
-
var crypto = require('crypto');
|
|
17
|
+
var crypto$1 = require('crypto');
|
|
18
18
|
var core = require('@mastra/core');
|
|
19
19
|
var mcp = require('@mastra/core/mcp');
|
|
20
20
|
var runtimeContext = require('@mastra/core/runtime-context');
|
|
@@ -22,10 +22,10 @@ var index_js = require('@modelcontextprotocol/sdk/server/index.js');
|
|
|
22
22
|
var sse_js = require('@modelcontextprotocol/sdk/server/sse.js');
|
|
23
23
|
var stdio_js = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
24
24
|
var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
|
|
25
|
+
var streaming = require('hono/streaming');
|
|
25
26
|
|
|
26
27
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
27
28
|
|
|
28
|
-
var jsonSchemaToZod__default = /*#__PURE__*/_interopDefault(jsonSchemaToZod);
|
|
29
29
|
var equal__default = /*#__PURE__*/_interopDefault(equal);
|
|
30
30
|
|
|
31
31
|
// src/client.ts
|
|
@@ -238,7 +238,29 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
238
238
|
});
|
|
239
239
|
}
|
|
240
240
|
convertInputSchema(inputSchema) {
|
|
241
|
-
|
|
241
|
+
if (utils.isZodType(inputSchema)) {
|
|
242
|
+
return inputSchema;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
const rawShape = zodFromJsonSchema.jsonSchemaObjectToZodRawShape(inputSchema);
|
|
246
|
+
return zod.z.object(rawShape);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
let errorDetails;
|
|
249
|
+
if (error instanceof Error) {
|
|
250
|
+
errorDetails = error.stack;
|
|
251
|
+
} else {
|
|
252
|
+
try {
|
|
253
|
+
errorDetails = JSON.stringify(error);
|
|
254
|
+
} catch {
|
|
255
|
+
errorDetails = String(error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
this.log("error", "Failed to convert JSON schema to Zod schema using zodFromJsonSchema", {
|
|
259
|
+
error: errorDetails,
|
|
260
|
+
originalJsonSchema: inputSchema
|
|
261
|
+
});
|
|
262
|
+
throw new Error(errorDetails);
|
|
263
|
+
}
|
|
242
264
|
}
|
|
243
265
|
async tools() {
|
|
244
266
|
this.log("debug", `Requesting tools from MCP server`);
|
|
@@ -246,36 +268,43 @@ var InternalMastraMCPClient = class extends base.MastraBase {
|
|
|
246
268
|
const toolsRes = {};
|
|
247
269
|
tools$1.forEach((tool) => {
|
|
248
270
|
this.log("debug", `Processing tool: ${tool.name}`);
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
error
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
try {
|
|
272
|
+
const mastraTool = tools.createTool({
|
|
273
|
+
id: `${this.name}_${tool.name}`,
|
|
274
|
+
description: tool.description || "",
|
|
275
|
+
inputSchema: this.convertInputSchema(tool.inputSchema),
|
|
276
|
+
execute: async ({ context }) => {
|
|
277
|
+
try {
|
|
278
|
+
this.log("debug", `Executing tool: ${tool.name}`, { toolArgs: context });
|
|
279
|
+
const res = await this.client.callTool(
|
|
280
|
+
{
|
|
281
|
+
name: tool.name,
|
|
282
|
+
arguments: context
|
|
283
|
+
},
|
|
284
|
+
types_js.CallToolResultSchema,
|
|
285
|
+
{
|
|
286
|
+
timeout: this.timeout
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
this.log("debug", `Tool executed successfully: ${tool.name}`);
|
|
290
|
+
return res;
|
|
291
|
+
} catch (e) {
|
|
292
|
+
this.log("error", `Error calling tool: ${tool.name}`, {
|
|
293
|
+
error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
|
|
294
|
+
toolArgs: context
|
|
295
|
+
});
|
|
296
|
+
throw e;
|
|
297
|
+
}
|
|
274
298
|
}
|
|
299
|
+
});
|
|
300
|
+
if (tool.name) {
|
|
301
|
+
toolsRes[tool.name] = mastraTool;
|
|
275
302
|
}
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
|
|
303
|
+
} catch (toolCreationError) {
|
|
304
|
+
this.log("error", `Failed to create Mastra tool wrapper for MCP tool: ${tool.name}`, {
|
|
305
|
+
error: toolCreationError instanceof Error ? toolCreationError.stack : String(toolCreationError),
|
|
306
|
+
mcpToolDefinition: tool
|
|
307
|
+
});
|
|
279
308
|
}
|
|
280
309
|
});
|
|
281
310
|
return toolsRes;
|
|
@@ -477,11 +506,98 @@ var MCPConfiguration = class extends MCPClient {
|
|
|
477
506
|
);
|
|
478
507
|
}
|
|
479
508
|
};
|
|
509
|
+
var MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
510
|
+
var SSETransport = class {
|
|
511
|
+
messageUrl;
|
|
512
|
+
stream;
|
|
513
|
+
_sessionId;
|
|
514
|
+
onclose;
|
|
515
|
+
onerror;
|
|
516
|
+
onmessage;
|
|
517
|
+
/**
|
|
518
|
+
* Creates a new SSETransport, which will direct the MPC client to POST messages to messageUrl
|
|
519
|
+
*/
|
|
520
|
+
constructor(messageUrl, stream) {
|
|
521
|
+
this.messageUrl = messageUrl;
|
|
522
|
+
this.stream = stream;
|
|
523
|
+
this._sessionId = crypto.randomUUID();
|
|
524
|
+
this.stream.onAbort(() => {
|
|
525
|
+
void this.close();
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
get sessionId() {
|
|
529
|
+
return this._sessionId;
|
|
530
|
+
}
|
|
531
|
+
// start() is automatically called after MCP Server connects to the transport
|
|
532
|
+
async start() {
|
|
533
|
+
if (this.stream == null) {
|
|
534
|
+
throw new Error("Stream not initialized");
|
|
535
|
+
}
|
|
536
|
+
if (this.stream.closed) {
|
|
537
|
+
throw new Error("SSE transport already closed!");
|
|
538
|
+
}
|
|
539
|
+
await this.stream.writeSSE({
|
|
540
|
+
event: "endpoint",
|
|
541
|
+
data: `${this.messageUrl}?sessionId=${this.sessionId}`
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
async handlePostMessage(context) {
|
|
545
|
+
if (this.stream?.closed == null) {
|
|
546
|
+
return context.text("SSE connection not established", 500);
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const contentType = context.req.header("content-type") || "";
|
|
550
|
+
if (!contentType.includes("application/json")) {
|
|
551
|
+
throw new Error(`Unsupported content-type: ${contentType}`);
|
|
552
|
+
}
|
|
553
|
+
const contentLength = Number.parseInt(context.req.header("content-length") || "0", 10);
|
|
554
|
+
if (contentLength > MAXIMUM_MESSAGE_SIZE) {
|
|
555
|
+
throw new Error(`Request body too large: ${contentLength} bytes`);
|
|
556
|
+
}
|
|
557
|
+
const body = await context.req.json();
|
|
558
|
+
await this.handleMessage(body);
|
|
559
|
+
return context.text("Accepted", 202);
|
|
560
|
+
} catch (error) {
|
|
561
|
+
this.onerror?.(error);
|
|
562
|
+
return context.text("Error", 400);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Handle a client message, regardless of how it arrived. This can be used to inform the server of messages that arrive via a means different than HTTP POST.
|
|
567
|
+
*/
|
|
568
|
+
async handleMessage(message) {
|
|
569
|
+
let parsedMessage;
|
|
570
|
+
try {
|
|
571
|
+
parsedMessage = types_js.JSONRPCMessageSchema.parse(message);
|
|
572
|
+
} catch (error) {
|
|
573
|
+
this.onerror?.(error);
|
|
574
|
+
throw error;
|
|
575
|
+
}
|
|
576
|
+
this.onmessage?.(parsedMessage);
|
|
577
|
+
}
|
|
578
|
+
async close() {
|
|
579
|
+
if (this.stream?.closed) {
|
|
580
|
+
this.stream.abort();
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
async send(message) {
|
|
584
|
+
if (this.stream?.closed) {
|
|
585
|
+
throw new Error("Not connected");
|
|
586
|
+
}
|
|
587
|
+
await this.stream.writeSSE({
|
|
588
|
+
event: "message",
|
|
589
|
+
data: JSON.stringify(message)
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
};
|
|
480
593
|
var MCPServer = class extends mcp.MCPServerBase {
|
|
481
594
|
server;
|
|
482
595
|
stdioTransport;
|
|
483
596
|
sseTransport;
|
|
597
|
+
sseHonoTransports;
|
|
484
598
|
streamableHTTPTransport;
|
|
599
|
+
listToolsHandlerIsRegistered = false;
|
|
600
|
+
callToolHandlerIsRegistered = false;
|
|
485
601
|
/**
|
|
486
602
|
* Get the current stdio transport.
|
|
487
603
|
*/
|
|
@@ -494,6 +610,12 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
494
610
|
getSseTransport() {
|
|
495
611
|
return this.sseTransport;
|
|
496
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Get the current SSE Hono transport.
|
|
615
|
+
*/
|
|
616
|
+
getSseHonoTransport(sessionId) {
|
|
617
|
+
return this.sseHonoTransports.get(sessionId);
|
|
618
|
+
}
|
|
497
619
|
/**
|
|
498
620
|
* Get the current streamable HTTP transport.
|
|
499
621
|
*/
|
|
@@ -512,6 +634,7 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
512
634
|
this.logger.info(
|
|
513
635
|
`Initialized MCPServer '${name}' v${version} with tools: ${Object.keys(this.convertedTools).join(", ")}`
|
|
514
636
|
);
|
|
637
|
+
this.sseHonoTransports = /* @__PURE__ */ new Map();
|
|
515
638
|
this.registerListToolsHandler();
|
|
516
639
|
this.registerCallToolHandler();
|
|
517
640
|
}
|
|
@@ -555,6 +678,10 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
555
678
|
* Register the ListTools handler for listing all available tools.
|
|
556
679
|
*/
|
|
557
680
|
registerListToolsHandler() {
|
|
681
|
+
if (this.listToolsHandlerIsRegistered) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
this.listToolsHandlerIsRegistered = true;
|
|
558
685
|
this.server.setRequestHandler(types_js.ListToolsRequestSchema, async () => {
|
|
559
686
|
this.logger.debug("Handling ListTools request");
|
|
560
687
|
return {
|
|
@@ -570,6 +697,10 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
570
697
|
* Register the CallTool handler for executing a tool by name.
|
|
571
698
|
*/
|
|
572
699
|
registerCallToolHandler() {
|
|
700
|
+
if (this.callToolHandlerIsRegistered) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
this.callToolHandlerIsRegistered = true;
|
|
573
704
|
this.server.setRequestHandler(types_js.CallToolRequestSchema, async (request) => {
|
|
574
705
|
const startTime = Date.now();
|
|
575
706
|
try {
|
|
@@ -675,6 +806,43 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
675
806
|
res.end();
|
|
676
807
|
}
|
|
677
808
|
}
|
|
809
|
+
/**
|
|
810
|
+
* Handles MCP-over-SSE protocol for user-provided HTTP servers.
|
|
811
|
+
* Call this from your HTTP server for both the SSE and message endpoints.
|
|
812
|
+
*
|
|
813
|
+
* @param url Parsed URL of the incoming request
|
|
814
|
+
* @param ssePath Path for establishing the SSE connection (e.g. '/sse')
|
|
815
|
+
* @param messagePath Path for POSTing client messages (e.g. '/message')
|
|
816
|
+
* @param context Incoming Hono context
|
|
817
|
+
*/
|
|
818
|
+
async startHonoSSE({ url, ssePath, messagePath, context }) {
|
|
819
|
+
if (url.pathname === ssePath) {
|
|
820
|
+
return streaming.streamSSE(context, async (stream) => {
|
|
821
|
+
await this.connectHonoSSE({
|
|
822
|
+
messagePath,
|
|
823
|
+
stream
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
} else if (url.pathname === messagePath) {
|
|
827
|
+
this.logger.debug("Received message");
|
|
828
|
+
const sessionId = context.req.query("sessionId");
|
|
829
|
+
this.logger.debug("Received message for sessionId", { sessionId });
|
|
830
|
+
if (!sessionId) {
|
|
831
|
+
return context.text("No sessionId provided", 400);
|
|
832
|
+
}
|
|
833
|
+
if (!this.sseHonoTransports.has(sessionId)) {
|
|
834
|
+
return context.text(`No transport found for sessionId ${sessionId}`, 400);
|
|
835
|
+
}
|
|
836
|
+
const message = await this.sseHonoTransports.get(sessionId)?.handlePostMessage(context);
|
|
837
|
+
if (!message) {
|
|
838
|
+
return context.text("Transport not found", 400);
|
|
839
|
+
}
|
|
840
|
+
return message;
|
|
841
|
+
} else {
|
|
842
|
+
this.logger.debug("Unknown path:", { path: url.pathname });
|
|
843
|
+
return context.text("Unknown path", 404);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
678
846
|
/**
|
|
679
847
|
* Handles MCP-over-StreamableHTTP protocol for user-provided HTTP servers.
|
|
680
848
|
* Call this from your HTTP server for the streamable HTTP endpoint.
|
|
@@ -690,7 +858,7 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
690
858
|
httpPath,
|
|
691
859
|
req,
|
|
692
860
|
res,
|
|
693
|
-
options = { sessionIdGenerator: () => crypto.randomUUID() }
|
|
861
|
+
options = { sessionIdGenerator: () => crypto$1.randomUUID() }
|
|
694
862
|
}) {
|
|
695
863
|
if (url.pathname === httpPath) {
|
|
696
864
|
this.streamableHTTPTransport = new streamableHttp_js.StreamableHTTPServerTransport(options);
|
|
@@ -722,14 +890,6 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
722
890
|
res.end();
|
|
723
891
|
}
|
|
724
892
|
}
|
|
725
|
-
async handlePostMessage(req, res) {
|
|
726
|
-
if (!this.sseTransport) {
|
|
727
|
-
res.writeHead(503);
|
|
728
|
-
res.end("SSE connection not established");
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
await this.sseTransport.handlePostMessage(req, res);
|
|
732
|
-
}
|
|
733
893
|
async connectSSE({
|
|
734
894
|
messagePath,
|
|
735
895
|
res
|
|
@@ -745,10 +905,35 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
745
905
|
this.sseTransport = void 0;
|
|
746
906
|
});
|
|
747
907
|
}
|
|
908
|
+
async connectHonoSSE({ messagePath, stream }) {
|
|
909
|
+
this.logger.debug("Received SSE connection");
|
|
910
|
+
const sseTransport = new SSETransport(messagePath, stream);
|
|
911
|
+
const sessionId = sseTransport.sessionId;
|
|
912
|
+
this.logger.debug("SSE Transport created with sessionId:", { sessionId });
|
|
913
|
+
this.sseHonoTransports.set(sessionId, sseTransport);
|
|
914
|
+
stream.onAbort(() => {
|
|
915
|
+
this.logger.debug("SSE Transport aborted with sessionId:", { sessionId });
|
|
916
|
+
this.sseHonoTransports.delete(sessionId);
|
|
917
|
+
});
|
|
918
|
+
await this.server.connect(sseTransport);
|
|
919
|
+
this.server.onclose = async () => {
|
|
920
|
+
this.logger.debug("SSE Transport closed with sessionId:", { sessionId });
|
|
921
|
+
this.sseHonoTransports.delete(sessionId);
|
|
922
|
+
await this.server.close();
|
|
923
|
+
};
|
|
924
|
+
while (true) {
|
|
925
|
+
const sessionIds = Array.from(this.sseHonoTransports.keys() || []);
|
|
926
|
+
this.logger.debug("Active Hono SSE sessions:", { sessionIds });
|
|
927
|
+
await stream.write(":keep-alive\n\n");
|
|
928
|
+
await stream.sleep(6e4);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
748
931
|
/**
|
|
749
932
|
* Close the MCP server and all its connections
|
|
750
933
|
*/
|
|
751
934
|
async close() {
|
|
935
|
+
this.callToolHandlerIsRegistered = false;
|
|
936
|
+
this.listToolsHandlerIsRegistered = false;
|
|
752
937
|
try {
|
|
753
938
|
if (this.stdioTransport) {
|
|
754
939
|
await this.stdioTransport.close?.();
|
|
@@ -758,6 +943,12 @@ var MCPServer = class extends mcp.MCPServerBase {
|
|
|
758
943
|
await this.sseTransport.close?.();
|
|
759
944
|
this.sseTransport = void 0;
|
|
760
945
|
}
|
|
946
|
+
if (this.sseHonoTransports) {
|
|
947
|
+
for (const transport of this.sseHonoTransports.values()) {
|
|
948
|
+
await transport.close?.();
|
|
949
|
+
}
|
|
950
|
+
this.sseHonoTransports.clear();
|
|
951
|
+
}
|
|
761
952
|
if (this.streamableHTTPTransport) {
|
|
762
953
|
await this.streamableHTTPTransport.close?.();
|
|
763
954
|
this.streamableHTTPTransport = void 0;
|