@ttoss/http-server-mcp 0.30.2 → 0.31.0
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/README.md +28 -2
- package/dist/index.cjs +49 -9
- package/dist/index.d.cts +41 -3
- package/dist/index.d.mts +41 -3
- package/dist/index.mjs +45 -11
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -611,6 +611,9 @@ Creates a Koa router configured to handle MCP protocol requests.
|
|
|
611
611
|
- `path` (`string`) — HTTP path for MCP endpoint (default: `'/mcp'`)
|
|
612
612
|
- `aliases` (`string[]`) — Additional paths where the MCP handler is also mounted; use `['/']` to also handle requests at the bare root (default: `[]`)
|
|
613
613
|
- `sessionIdGenerator` (`() => string`) — Session ID generator for stateful servers (default: `undefined` for stateless)
|
|
614
|
+
- `createMcpServer` (`McpServerFactory`) — Per-request factory serving the `2026-07-28` revision; see [Serving the `2026-07-28` revision](#serving-the-2026-07-28-revision)
|
|
615
|
+
- `bus` (`ServerEventBus`) — Change-event bus `subscriptions/listen` streams subscribe to (default: in-process); see [Change notifications and shutdown](#change-notifications-and-shutdown)
|
|
616
|
+
- `keepAliveMs` (`number`) — SSE keepalive interval for `2026-07-28` streams (default: `15000`)
|
|
614
617
|
- `apiBaseUrl` (`string`) — Base URL prepended to relative paths in `apiCall`
|
|
615
618
|
- `getApiHeaders` (`(ctx: Context) => Record<string, string>`) — Return headers to inject into every `apiCall` for this request
|
|
616
619
|
- `auth` (`McpAuthOptions`) — OAuth/JWT authentication; see [Authentication](#authentication)
|
|
@@ -622,7 +625,7 @@ Creates a Koa router configured to handle MCP protocol requests.
|
|
|
622
625
|
- `auth.resourceMetadataUrl` — URL advertised in the RFC 9728 `WWW-Authenticate: Bearer resource_metadata="…"` header on a 401. Defaults to the location derived from `resourceServerUrl` + `path` (the one this router serves), so the header cannot drift from the routes; set it only when a separate `oauthServer()` serves the document and this router is mounted without `resourceServerUrl`/`authorizationServerUrl`
|
|
623
626
|
- `auth.resourceIndicator` — Expected `aud` value(s) (RFC 8707); rejects tokens minted for a different resource. See [Resource indicator validation](#resource-indicator-validation-rfc-8707)
|
|
624
627
|
|
|
625
|
-
**Returns:** `
|
|
628
|
+
**Returns:** `McpRouter` — the Koa router, plus `notify` (publish `list_changed` notifications to open `subscriptions/listen` streams) and `close()` (announce that the lists may have changed, then end every open `2026-07-28` exchange)
|
|
626
629
|
|
|
627
630
|
### `apiCall(method, url, options?)`
|
|
628
631
|
|
|
@@ -962,7 +965,7 @@ app.listen(3000);
|
|
|
962
965
|
|
|
963
966
|
## Protocol Details
|
|
964
967
|
|
|
965
|
-
This package implements the [Model Context Protocol](https://spec.modelcontextprotocol.io/) over HTTP using JSON responses
|
|
968
|
+
This package implements the [Model Context Protocol](https://spec.modelcontextprotocol.io/) over HTTP using JSON responses — SSE only for `2026-07-28` `subscriptions/listen` streams — and serves each request over the protocol revision it actually speaks.
|
|
966
969
|
|
|
967
970
|
Requests are classified once at the boundary, and the classifier's answer is three-way. Traffic from today's MCP clients is served over `NodeStreamableHTTPServerTransport` with `enableJsonResponse: true`, adapting Koa's context-based middleware to the SDK's Node.js request/response expectations. Requests carrying the `2026-07-28` revision's per-request envelope are served by that revision's stateless core (`createMcpHandler`). Requests the classifier refuses outright are answered with its own rejection — the status, code, message and data it chose — rather than passed to either era's handler.
|
|
968
971
|
|
|
@@ -986,6 +989,29 @@ It has to be a factory, and cannot default to the server the router was given, b
|
|
|
986
989
|
|
|
987
990
|
Without `createMcpServer`, `2026-07-28` requests get the unsupported-protocol-version error listing the revisions this endpoint does serve, so that client renegotiates and no other client is affected.
|
|
988
991
|
|
|
992
|
+
### Change notifications and shutdown
|
|
993
|
+
|
|
994
|
+
A `2026-07-28` client learns about a changed tool list over a `subscriptions/listen` stream it holds open. The router serves those streams; `notify` publishes to them. Call `notify.toolsChanged()` after registering or removing a tool at runtime:
|
|
995
|
+
|
|
996
|
+
```typescript
|
|
997
|
+
const mcpRouter = createMcpRouter(buildServer(), {
|
|
998
|
+
createMcpServer: buildServer,
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
mcpRouter.notify.toolsChanged();
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
A deploy changes the surface too, and the new process cannot know what a client cached — so the old one says so on its way out. Call `close()` on shutdown, before the HTTP server's own `close()`. It tells every open stream that the tool, prompt and resource lists may have changed, then ends the streams. That order matters twice: a client that does not re-list on reconnect learns to, and `server.close()` — which waits for every open request — is no longer held open by streams that never finish on their own, which would otherwise make every graceful shutdown wait out its whole grace period.
|
|
1005
|
+
|
|
1006
|
+
```typescript
|
|
1007
|
+
process.on('SIGTERM', async () => {
|
|
1008
|
+
await mcpRouter.close();
|
|
1009
|
+
httpServer.close();
|
|
1010
|
+
});
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
The default bus is in-process, so `notify` reaches the streams this process holds. Behind several replicas, pass a `bus` implementing `ServerEventBus` over a shared pub/sub. Keep `keepAliveMs` below the idle timeout of anything in front of the server, or a quiet stream is cut there. 2025-era traffic is served statelessly, so it holds no stream to notify.
|
|
1014
|
+
|
|
989
1015
|
**Supported HTTP methods:**
|
|
990
1016
|
|
|
991
1017
|
- `POST /mcp` - Send JSON-RPC requests/notifications
|
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,12 @@ var getRequestLocale = () => {
|
|
|
48
48
|
|
|
49
49
|
//#endregion
|
|
50
50
|
//#region src/serveMcpRequest.ts
|
|
51
|
+
var NO_NOTIFIER = {
|
|
52
|
+
toolsChanged: () => {},
|
|
53
|
+
promptsChanged: () => {},
|
|
54
|
+
resourcesChanged: () => {},
|
|
55
|
+
resourceUpdated: () => {}
|
|
56
|
+
};
|
|
51
57
|
/**
|
|
52
58
|
* Reads a header as `string | undefined`. Koa's `ctx.get` returns `''` for a
|
|
53
59
|
* missing header, which the classifier would read as "header present but
|
|
@@ -133,12 +139,17 @@ var REVISION_NOT_SERVED_ANSWER = {
|
|
|
133
139
|
var createMcpRequestServer = ({
|
|
134
140
|
createMcpServer,
|
|
135
141
|
server,
|
|
136
|
-
sessionIdGenerator
|
|
142
|
+
sessionIdGenerator,
|
|
143
|
+
bus,
|
|
144
|
+
keepAliveMs
|
|
137
145
|
}) => {
|
|
138
146
|
const isStateful = sessionIdGenerator !== void 0;
|
|
139
|
-
const
|
|
140
|
-
legacy: "reject"
|
|
141
|
-
|
|
147
|
+
const modern = createMcpServer ? (0, _modelcontextprotocol_server.createMcpHandler)(createMcpServer, {
|
|
148
|
+
legacy: "reject",
|
|
149
|
+
bus,
|
|
150
|
+
keepAliveMs
|
|
151
|
+
}) : void 0;
|
|
152
|
+
const modernHandler = modern ? (0, _modelcontextprotocol_node.toNodeHandler)(modern) : void 0;
|
|
142
153
|
let sharedTransport;
|
|
143
154
|
if (isStateful) {
|
|
144
155
|
sharedTransport = new _modelcontextprotocol_node.NodeStreamableHTTPServerTransport({
|
|
@@ -155,7 +166,7 @@ var createMcpRequestServer = ({
|
|
|
155
166
|
statelessQueue = result.catch(() => {});
|
|
156
167
|
return result;
|
|
157
168
|
};
|
|
158
|
-
|
|
169
|
+
const serve = async (ctx, body) => {
|
|
159
170
|
const outcome = (0, _modelcontextprotocol_server.classifyInboundRequest)({
|
|
160
171
|
httpMethod: ctx.method,
|
|
161
172
|
protocolVersionHeader: header(ctx, "mcp-protocol-version"),
|
|
@@ -192,6 +203,13 @@ var createMcpRequestServer = ({
|
|
|
192
203
|
}
|
|
193
204
|
});
|
|
194
205
|
};
|
|
206
|
+
return {
|
|
207
|
+
serve,
|
|
208
|
+
notify: modern ? modern.notify : NO_NOTIFIER,
|
|
209
|
+
close: async () => {
|
|
210
|
+
await modern?.close();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
195
213
|
};
|
|
196
214
|
|
|
197
215
|
//#endregion
|
|
@@ -849,15 +867,19 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
849
867
|
aliases = [],
|
|
850
868
|
sessionIdGenerator,
|
|
851
869
|
createMcpServer,
|
|
870
|
+
bus,
|
|
871
|
+
keepAliveMs,
|
|
852
872
|
apiBaseUrl,
|
|
853
873
|
getApiHeaders,
|
|
854
874
|
auth
|
|
855
875
|
} = options;
|
|
856
876
|
const needsContext = apiBaseUrl !== void 0 || getApiHeaders !== void 0 || auth !== void 0;
|
|
857
|
-
const
|
|
877
|
+
const requestServer = createMcpRequestServer({
|
|
858
878
|
server,
|
|
859
879
|
sessionIdGenerator,
|
|
860
|
-
createMcpServer
|
|
880
|
+
createMcpServer,
|
|
881
|
+
bus,
|
|
882
|
+
keepAliveMs
|
|
861
883
|
});
|
|
862
884
|
const router = new _ttoss_http_server.Router();
|
|
863
885
|
let authCheck;
|
|
@@ -906,7 +928,7 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
906
928
|
const identity = ctx.state.user;
|
|
907
929
|
ctx.req.auth = identity;
|
|
908
930
|
const runRequest = async () => {
|
|
909
|
-
await
|
|
931
|
+
await requestServer.serve(ctx, body);
|
|
910
932
|
ctx.respond = false;
|
|
911
933
|
};
|
|
912
934
|
const acceptLanguage = ctx.get("Accept-Language") || void 0;
|
|
@@ -949,10 +971,28 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
949
971
|
router.post(mcpPath, postHandler);
|
|
950
972
|
router.delete(mcpPath, deleteHandler);
|
|
951
973
|
}
|
|
952
|
-
|
|
974
|
+
const close = async () => {
|
|
975
|
+
requestServer.notify.toolsChanged();
|
|
976
|
+
requestServer.notify.promptsChanged();
|
|
977
|
+
requestServer.notify.resourcesChanged();
|
|
978
|
+
await new Promise(resolve => {
|
|
979
|
+
setImmediate(resolve);
|
|
980
|
+
});
|
|
981
|
+
await requestServer.close();
|
|
982
|
+
};
|
|
983
|
+
return Object.assign(router, {
|
|
984
|
+
notify: requestServer.notify,
|
|
985
|
+
close
|
|
986
|
+
});
|
|
953
987
|
};
|
|
954
988
|
|
|
955
989
|
//#endregion
|
|
990
|
+
Object.defineProperty(exports, 'InMemoryServerEventBus', {
|
|
991
|
+
enumerable: true,
|
|
992
|
+
get: function () {
|
|
993
|
+
return _modelcontextprotocol_server.InMemoryServerEventBus;
|
|
994
|
+
}
|
|
995
|
+
});
|
|
956
996
|
Object.defineProperty(exports, 'McpServer', {
|
|
957
997
|
enumerable: true,
|
|
958
998
|
get: function () {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
|
-
import { CallToolResult, McpServer, McpServer as McpServer$1, McpServerFactory, RegisteredResource } from "@modelcontextprotocol/server";
|
|
3
|
+
import { CallToolResult, InMemoryServerEventBus, McpServer, McpServer as McpServer$1, McpServerFactory, RegisteredResource, ServerEvent, ServerEventBus, ServerEventBus as ServerEventBus$1, ServerNotifier, ServerNotifier as ServerNotifier$1 } from "@modelcontextprotocol/server";
|
|
4
4
|
import { Router } from "@ttoss/http-server";
|
|
5
5
|
import accepts from "accepts";
|
|
6
6
|
import { AsyncLocalStorage } from "async_hooks";
|
|
@@ -1333,6 +1333,29 @@ declare const apiCall: (method: string, url: string, options?: ApiCallOptions) =
|
|
|
1333
1333
|
* ```
|
|
1334
1334
|
*/
|
|
1335
1335
|
declare const checkScopes: (required: string[]) => void;
|
|
1336
|
+
/**
|
|
1337
|
+
* The router {@link createMcpRouter} returns: a Koa router, plus the two
|
|
1338
|
+
* operations on the `2026-07-28` streams it serves.
|
|
1339
|
+
*/
|
|
1340
|
+
type McpRouter = Router & {
|
|
1341
|
+
/**
|
|
1342
|
+
* Publishes the `list_changed` notifications (and `resources/updated`) to
|
|
1343
|
+
* every open `subscriptions/listen` stream that opted in. Call
|
|
1344
|
+
* `notify.toolsChanged()` after registering or removing a tool at runtime.
|
|
1345
|
+
* A no-op without `createMcpServer`, or with no stream open.
|
|
1346
|
+
*/
|
|
1347
|
+
notify: ServerNotifier$1;
|
|
1348
|
+
/**
|
|
1349
|
+
* Tells every open `subscriptions/listen` stream that the tool, prompt and
|
|
1350
|
+
* resource lists may have changed, then ends every open `2026-07-28`
|
|
1351
|
+
* exchange. Call it on shutdown, before `server.close()`: an open stream
|
|
1352
|
+
* never finishes on its own, so a graceful close would otherwise wait out
|
|
1353
|
+
* the whole grace period, and the notification is what tells a client that
|
|
1354
|
+
* does not re-list on reconnect that the next process may serve a different
|
|
1355
|
+
* surface.
|
|
1356
|
+
*/
|
|
1357
|
+
close: () => Promise<void>;
|
|
1358
|
+
};
|
|
1336
1359
|
/**
|
|
1337
1360
|
* Options for configuring the MCP router
|
|
1338
1361
|
*/
|
|
@@ -1389,6 +1412,21 @@ interface McpRouterOptions {
|
|
|
1389
1412
|
* ```
|
|
1390
1413
|
*/
|
|
1391
1414
|
createMcpServer?: McpServerFactory;
|
|
1415
|
+
/**
|
|
1416
|
+
* The change-event bus `2026-07-28` `subscriptions/listen` streams subscribe
|
|
1417
|
+
* to, which {@link McpRouter.notify} publishes onto. Defaults to an
|
|
1418
|
+
* in-process bus, which reaches only the streams this process holds: with
|
|
1419
|
+
* several replicas, pass one over a shared pub/sub so a change published by
|
|
1420
|
+
* one replica reaches every client.
|
|
1421
|
+
*/
|
|
1422
|
+
bus?: ServerEventBus$1;
|
|
1423
|
+
/**
|
|
1424
|
+
* SSE keepalive interval for `2026-07-28` streams, in milliseconds. Keep it
|
|
1425
|
+
* below the idle timeout of whatever sits in front of the server (a CDN or
|
|
1426
|
+
* load balancer), or a quiet `subscriptions/listen` stream is cut there.
|
|
1427
|
+
* @default 15000
|
|
1428
|
+
*/
|
|
1429
|
+
keepAliveMs?: number;
|
|
1392
1430
|
/**
|
|
1393
1431
|
* Base URL prepended to relative paths passed to `apiCall` (paths starting
|
|
1394
1432
|
* with `/`). Tool handlers can then call `apiCall('GET', '/resource')` without
|
|
@@ -1496,6 +1534,6 @@ interface McpRouterOptions {
|
|
|
1496
1534
|
* app.listen(3000);
|
|
1497
1535
|
* ```
|
|
1498
1536
|
*/
|
|
1499
|
-
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) =>
|
|
1537
|
+
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => McpRouter;
|
|
1500
1538
|
//#endregion
|
|
1501
|
-
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, type JsonObjectSchema, McpAuthOptions, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
|
1539
|
+
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, InMemoryServerEventBus, type JsonObjectSchema, McpAuthOptions, McpRouter, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ServerEvent, type ServerEventBus, type ServerNotifier, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import { Router } from "@ttoss/http-server";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
-
import { CallToolResult, McpServer, McpServer as McpServer$1, McpServerFactory, RegisteredResource } from "@modelcontextprotocol/server";
|
|
5
|
+
import { CallToolResult, InMemoryServerEventBus, McpServer, McpServer as McpServer$1, McpServerFactory, RegisteredResource, ServerEvent, ServerEventBus, ServerEventBus as ServerEventBus$1, ServerNotifier, ServerNotifier as ServerNotifier$1 } from "@modelcontextprotocol/server";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import accepts from "accepts";
|
|
8
8
|
import { AsyncLocalStorage as AsyncLocalStorage$1 } from "async_hooks";
|
|
@@ -1334,6 +1334,29 @@ declare const apiCall: (method: string, url: string, options?: ApiCallOptions) =
|
|
|
1334
1334
|
* ```
|
|
1335
1335
|
*/
|
|
1336
1336
|
declare const checkScopes: (required: string[]) => void;
|
|
1337
|
+
/**
|
|
1338
|
+
* The router {@link createMcpRouter} returns: a Koa router, plus the two
|
|
1339
|
+
* operations on the `2026-07-28` streams it serves.
|
|
1340
|
+
*/
|
|
1341
|
+
type McpRouter = Router & {
|
|
1342
|
+
/**
|
|
1343
|
+
* Publishes the `list_changed` notifications (and `resources/updated`) to
|
|
1344
|
+
* every open `subscriptions/listen` stream that opted in. Call
|
|
1345
|
+
* `notify.toolsChanged()` after registering or removing a tool at runtime.
|
|
1346
|
+
* A no-op without `createMcpServer`, or with no stream open.
|
|
1347
|
+
*/
|
|
1348
|
+
notify: ServerNotifier$1;
|
|
1349
|
+
/**
|
|
1350
|
+
* Tells every open `subscriptions/listen` stream that the tool, prompt and
|
|
1351
|
+
* resource lists may have changed, then ends every open `2026-07-28`
|
|
1352
|
+
* exchange. Call it on shutdown, before `server.close()`: an open stream
|
|
1353
|
+
* never finishes on its own, so a graceful close would otherwise wait out
|
|
1354
|
+
* the whole grace period, and the notification is what tells a client that
|
|
1355
|
+
* does not re-list on reconnect that the next process may serve a different
|
|
1356
|
+
* surface.
|
|
1357
|
+
*/
|
|
1358
|
+
close: () => Promise<void>;
|
|
1359
|
+
};
|
|
1337
1360
|
/**
|
|
1338
1361
|
* Options for configuring the MCP router
|
|
1339
1362
|
*/
|
|
@@ -1390,6 +1413,21 @@ interface McpRouterOptions {
|
|
|
1390
1413
|
* ```
|
|
1391
1414
|
*/
|
|
1392
1415
|
createMcpServer?: McpServerFactory;
|
|
1416
|
+
/**
|
|
1417
|
+
* The change-event bus `2026-07-28` `subscriptions/listen` streams subscribe
|
|
1418
|
+
* to, which {@link McpRouter.notify} publishes onto. Defaults to an
|
|
1419
|
+
* in-process bus, which reaches only the streams this process holds: with
|
|
1420
|
+
* several replicas, pass one over a shared pub/sub so a change published by
|
|
1421
|
+
* one replica reaches every client.
|
|
1422
|
+
*/
|
|
1423
|
+
bus?: ServerEventBus$1;
|
|
1424
|
+
/**
|
|
1425
|
+
* SSE keepalive interval for `2026-07-28` streams, in milliseconds. Keep it
|
|
1426
|
+
* below the idle timeout of whatever sits in front of the server (a CDN or
|
|
1427
|
+
* load balancer), or a quiet `subscriptions/listen` stream is cut there.
|
|
1428
|
+
* @default 15000
|
|
1429
|
+
*/
|
|
1430
|
+
keepAliveMs?: number;
|
|
1393
1431
|
/**
|
|
1394
1432
|
* Base URL prepended to relative paths passed to `apiCall` (paths starting
|
|
1395
1433
|
* with `/`). Tool handlers can then call `apiCall('GET', '/resource')` without
|
|
@@ -1497,6 +1535,6 @@ interface McpRouterOptions {
|
|
|
1497
1535
|
* app.listen(3000);
|
|
1498
1536
|
* ```
|
|
1499
1537
|
*/
|
|
1500
|
-
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) =>
|
|
1538
|
+
declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => McpRouter;
|
|
1501
1539
|
//#endregion
|
|
1502
|
-
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, type JsonObjectSchema, McpAuthOptions, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
|
1540
|
+
export { ApiCallOptions, CognitoUserPoolConfig, type CreateGatedToolRegistrarOptions, type GatedToolDef, InMemoryServerEventBus, type JsonObjectSchema, McpAuthOptions, McpRouter, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolFromSchemaParams, type RegisteredAppResource, type ServerEvent, type ServerEventBus, type ServerNotifier, type ToolCallContext, type ToolIdentity, type ToolMetaParams, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { Router } from "@ttoss/http-server";
|
|
|
5
5
|
import { authMiddleware } from "@ttoss/http-server-auth";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { NodeStreamableHTTPServerTransport, toNodeHandler } from "@modelcontextprotocol/node";
|
|
8
|
-
import { McpServer, ProtocolErrorCode, SUPPORTED_PROTOCOL_VERSIONS, classifyInboundRequest, createMcpHandler, fromJsonSchema } from "@modelcontextprotocol/server";
|
|
8
|
+
import { InMemoryServerEventBus, McpServer, ProtocolErrorCode, SUPPORTED_PROTOCOL_VERSIONS, classifyInboundRequest, createMcpHandler, fromJsonSchema } from "@modelcontextprotocol/server";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
|
|
11
11
|
//#region src/context.ts
|
|
@@ -45,6 +45,12 @@ var getRequestLocale = () => {
|
|
|
45
45
|
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/serveMcpRequest.ts
|
|
48
|
+
var NO_NOTIFIER = {
|
|
49
|
+
toolsChanged: () => {},
|
|
50
|
+
promptsChanged: () => {},
|
|
51
|
+
resourcesChanged: () => {},
|
|
52
|
+
resourceUpdated: () => {}
|
|
53
|
+
};
|
|
48
54
|
/**
|
|
49
55
|
* Reads a header as `string | undefined`. Koa's `ctx.get` returns `''` for a
|
|
50
56
|
* missing header, which the classifier would read as "header present but
|
|
@@ -130,12 +136,17 @@ var REVISION_NOT_SERVED_ANSWER = {
|
|
|
130
136
|
var createMcpRequestServer = ({
|
|
131
137
|
createMcpServer,
|
|
132
138
|
server,
|
|
133
|
-
sessionIdGenerator
|
|
139
|
+
sessionIdGenerator,
|
|
140
|
+
bus,
|
|
141
|
+
keepAliveMs
|
|
134
142
|
}) => {
|
|
135
143
|
const isStateful = sessionIdGenerator !== void 0;
|
|
136
|
-
const
|
|
137
|
-
legacy: "reject"
|
|
138
|
-
|
|
144
|
+
const modern = createMcpServer ? createMcpHandler(createMcpServer, {
|
|
145
|
+
legacy: "reject",
|
|
146
|
+
bus,
|
|
147
|
+
keepAliveMs
|
|
148
|
+
}) : void 0;
|
|
149
|
+
const modernHandler = modern ? toNodeHandler(modern) : void 0;
|
|
139
150
|
let sharedTransport;
|
|
140
151
|
if (isStateful) {
|
|
141
152
|
sharedTransport = new NodeStreamableHTTPServerTransport({
|
|
@@ -152,7 +163,7 @@ var createMcpRequestServer = ({
|
|
|
152
163
|
statelessQueue = result.catch(() => {});
|
|
153
164
|
return result;
|
|
154
165
|
};
|
|
155
|
-
|
|
166
|
+
const serve = async (ctx, body) => {
|
|
156
167
|
const outcome = classifyInboundRequest({
|
|
157
168
|
httpMethod: ctx.method,
|
|
158
169
|
protocolVersionHeader: header(ctx, "mcp-protocol-version"),
|
|
@@ -189,6 +200,13 @@ var createMcpRequestServer = ({
|
|
|
189
200
|
}
|
|
190
201
|
});
|
|
191
202
|
};
|
|
203
|
+
return {
|
|
204
|
+
serve,
|
|
205
|
+
notify: modern ? modern.notify : NO_NOTIFIER,
|
|
206
|
+
close: async () => {
|
|
207
|
+
await modern?.close();
|
|
208
|
+
}
|
|
209
|
+
};
|
|
192
210
|
};
|
|
193
211
|
|
|
194
212
|
//#endregion
|
|
@@ -846,15 +864,19 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
846
864
|
aliases = [],
|
|
847
865
|
sessionIdGenerator,
|
|
848
866
|
createMcpServer,
|
|
867
|
+
bus,
|
|
868
|
+
keepAliveMs,
|
|
849
869
|
apiBaseUrl,
|
|
850
870
|
getApiHeaders,
|
|
851
871
|
auth
|
|
852
872
|
} = options;
|
|
853
873
|
const needsContext = apiBaseUrl !== void 0 || getApiHeaders !== void 0 || auth !== void 0;
|
|
854
|
-
const
|
|
874
|
+
const requestServer = createMcpRequestServer({
|
|
855
875
|
server,
|
|
856
876
|
sessionIdGenerator,
|
|
857
|
-
createMcpServer
|
|
877
|
+
createMcpServer,
|
|
878
|
+
bus,
|
|
879
|
+
keepAliveMs
|
|
858
880
|
});
|
|
859
881
|
const router = new Router();
|
|
860
882
|
let authCheck;
|
|
@@ -903,7 +925,7 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
903
925
|
const identity = ctx.state.user;
|
|
904
926
|
ctx.req.auth = identity;
|
|
905
927
|
const runRequest = async () => {
|
|
906
|
-
await
|
|
928
|
+
await requestServer.serve(ctx, body);
|
|
907
929
|
ctx.respond = false;
|
|
908
930
|
};
|
|
909
931
|
const acceptLanguage = ctx.get("Accept-Language") || void 0;
|
|
@@ -946,8 +968,20 @@ var createMcpRouter = (server, options = {}) => {
|
|
|
946
968
|
router.post(mcpPath, postHandler);
|
|
947
969
|
router.delete(mcpPath, deleteHandler);
|
|
948
970
|
}
|
|
949
|
-
|
|
971
|
+
const close = async () => {
|
|
972
|
+
requestServer.notify.toolsChanged();
|
|
973
|
+
requestServer.notify.promptsChanged();
|
|
974
|
+
requestServer.notify.resourcesChanged();
|
|
975
|
+
await new Promise(resolve => {
|
|
976
|
+
setImmediate(resolve);
|
|
977
|
+
});
|
|
978
|
+
await requestServer.close();
|
|
979
|
+
};
|
|
980
|
+
return Object.assign(router, {
|
|
981
|
+
notify: requestServer.notify,
|
|
982
|
+
close
|
|
983
|
+
});
|
|
950
984
|
};
|
|
951
985
|
|
|
952
986
|
//#endregion
|
|
953
|
-
export { McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
|
987
|
+
export { InMemoryServerEventBus, McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createGatedToolRegistrar, createMcpRouter, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, registerAppResource, registerToolFromSchema, z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttoss/http-server-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server integration for @ttoss/http-server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
39
39
|
"zod": "^4.4.3",
|
|
40
40
|
"@ttoss/auth-core": "^0.15.1",
|
|
41
|
-
"@ttoss/http-server": "^0.
|
|
42
|
-
"@ttoss/i18n-core": "^0.
|
|
43
|
-
"@ttoss/http-server
|
|
41
|
+
"@ttoss/http-server-auth": "^0.7.4",
|
|
42
|
+
"@ttoss/i18n-core": "^0.4.0",
|
|
43
|
+
"@ttoss/http-server": "^0.10.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@modelcontextprotocol/client": "^2.0.0",
|