@modelcontextprotocol/sdk 1.18.2 → 1.19.1
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 +766 -738
- package/dist/cjs/cli.js +35 -37
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/client/auth.d.ts +12 -12
- package/dist/cjs/client/auth.d.ts.map +1 -1
- package/dist/cjs/client/auth.js +76 -83
- package/dist/cjs/client/auth.js.map +1 -1
- package/dist/cjs/client/index.d.ts +186 -123
- package/dist/cjs/client/index.d.ts.map +1 -1
- package/dist/cjs/client/index.js +40 -41
- package/dist/cjs/client/index.js.map +1 -1
- package/dist/cjs/client/middleware.d.ts +2 -2
- package/dist/cjs/client/middleware.d.ts.map +1 -1
- package/dist/cjs/client/middleware.js +22 -27
- package/dist/cjs/client/middleware.js.map +1 -1
- package/dist/cjs/client/sse.d.ts +4 -4
- package/dist/cjs/client/sse.d.ts.map +1 -1
- package/dist/cjs/client/sse.js +34 -21
- package/dist/cjs/client/sse.js.map +1 -1
- package/dist/cjs/client/stdio.d.ts +4 -4
- package/dist/cjs/client/stdio.d.ts.map +1 -1
- package/dist/cjs/client/stdio.js +32 -32
- package/dist/cjs/client/stdio.js.map +1 -1
- package/dist/cjs/client/streamableHttp.d.ts +7 -6
- package/dist/cjs/client/streamableHttp.d.ts.map +1 -1
- package/dist/cjs/client/streamableHttp.js +55 -38
- package/dist/cjs/client/streamableHttp.js.map +1 -1
- package/dist/cjs/client/websocket.d.ts +2 -2
- package/dist/cjs/client/websocket.d.ts.map +1 -1
- package/dist/cjs/client/websocket.js +5 -7
- package/dist/cjs/client/websocket.js.map +1 -1
- package/dist/cjs/examples/client/multipleClientsParallel.js +2 -2
- package/dist/cjs/examples/client/multipleClientsParallel.js.map +1 -1
- package/dist/cjs/examples/client/parallelToolCallsClient.js +6 -5
- package/dist/cjs/examples/client/parallelToolCallsClient.js.map +1 -1
- package/dist/cjs/examples/client/simpleOAuthClient.js +15 -13
- package/dist/cjs/examples/client/simpleOAuthClient.js.map +1 -1
- package/dist/cjs/examples/client/simpleStreamableHttp.js +15 -11
- package/dist/cjs/examples/client/simpleStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js +2 -2
- package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js.map +1 -1
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts +1 -1
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts.map +1 -1
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.js +18 -16
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.js.map +1 -1
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.js +18 -18
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/mcpServerOutputSchema.js +19 -17
- package/dist/cjs/examples/server/mcpServerOutputSchema.js.map +1 -1
- package/dist/cjs/examples/server/simpleSseServer.js +8 -8
- package/dist/cjs/examples/server/simpleSseServer.js.map +1 -1
- package/dist/cjs/examples/server/simpleStatelessStreamableHttp.js +22 -22
- package/dist/cjs/examples/server/simpleStatelessStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/simpleStreamableHttp.js +78 -78
- package/dist/cjs/examples/server/simpleStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js +18 -18
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
- package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js +8 -8
- package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/toolWithSampleServer.js +19 -19
- package/dist/cjs/examples/server/toolWithSampleServer.js.map +1 -1
- package/dist/cjs/examples/shared/inMemoryEventStore.d.ts.map +1 -1
- package/dist/cjs/examples/shared/inMemoryEventStore.js.map +1 -1
- package/dist/cjs/inMemory.d.ts +3 -3
- package/dist/cjs/inMemory.d.ts.map +1 -1
- package/dist/cjs/inMemory.js +1 -1
- package/dist/cjs/inMemory.js.map +1 -1
- package/dist/cjs/server/auth/clients.d.ts +2 -2
- package/dist/cjs/server/auth/clients.d.ts.map +1 -1
- package/dist/cjs/server/auth/errors.d.ts +1 -1
- package/dist/cjs/server/auth/errors.d.ts.map +1 -1
- package/dist/cjs/server/auth/errors.js +17 -17
- package/dist/cjs/server/auth/errors.js.map +1 -1
- package/dist/cjs/server/auth/handlers/authorize.d.ts +3 -3
- package/dist/cjs/server/auth/handlers/authorize.d.ts.map +1 -1
- package/dist/cjs/server/auth/handlers/authorize.js +21 -18
- package/dist/cjs/server/auth/handlers/authorize.js.map +1 -1
- package/dist/cjs/server/auth/handlers/metadata.d.ts +2 -2
- package/dist/cjs/server/auth/handlers/metadata.js +1 -1
- package/dist/cjs/server/auth/handlers/metadata.js.map +1 -1
- package/dist/cjs/server/auth/handlers/register.d.ts +4 -4
- package/dist/cjs/server/auth/handlers/register.d.ts.map +1 -1
- package/dist/cjs/server/auth/handlers/register.js +7 -9
- package/dist/cjs/server/auth/handlers/register.js.map +1 -1
- package/dist/cjs/server/auth/handlers/revoke.d.ts +4 -4
- package/dist/cjs/server/auth/handlers/revoke.d.ts.map +1 -1
- package/dist/cjs/server/auth/handlers/revoke.js +9 -9
- package/dist/cjs/server/auth/handlers/revoke.js.map +1 -1
- package/dist/cjs/server/auth/handlers/token.d.ts +3 -3
- package/dist/cjs/server/auth/handlers/token.d.ts.map +1 -1
- package/dist/cjs/server/auth/handlers/token.js +14 -14
- package/dist/cjs/server/auth/handlers/token.js.map +1 -1
- package/dist/cjs/server/auth/middleware/allowedMethods.d.ts +1 -1
- package/dist/cjs/server/auth/middleware/allowedMethods.d.ts.map +1 -1
- package/dist/cjs/server/auth/middleware/allowedMethods.js +1 -3
- package/dist/cjs/server/auth/middleware/allowedMethods.js.map +1 -1
- package/dist/cjs/server/auth/middleware/bearerAuth.d.ts +4 -4
- package/dist/cjs/server/auth/middleware/bearerAuth.d.ts.map +1 -1
- package/dist/cjs/server/auth/middleware/bearerAuth.js +7 -7
- package/dist/cjs/server/auth/middleware/bearerAuth.js.map +1 -1
- package/dist/cjs/server/auth/middleware/clientAuth.d.ts +4 -4
- package/dist/cjs/server/auth/middleware/clientAuth.d.ts.map +1 -1
- package/dist/cjs/server/auth/middleware/clientAuth.js +6 -6
- package/dist/cjs/server/auth/middleware/clientAuth.js.map +1 -1
- package/dist/cjs/server/auth/provider.d.ts +4 -4
- package/dist/cjs/server/auth/provider.d.ts.map +1 -1
- package/dist/cjs/server/auth/providers/proxyProvider.d.ts +10 -10
- package/dist/cjs/server/auth/providers/proxyProvider.d.ts.map +1 -1
- package/dist/cjs/server/auth/providers/proxyProvider.js +34 -34
- package/dist/cjs/server/auth/providers/proxyProvider.js.map +1 -1
- package/dist/cjs/server/auth/router.d.ts +11 -11
- package/dist/cjs/server/auth/router.d.ts.map +1 -1
- package/dist/cjs/server/auth/router.js +16 -18
- package/dist/cjs/server/auth/router.js.map +1 -1
- package/dist/cjs/server/auth/types.d.ts +1 -1
- package/dist/cjs/server/auth/types.d.ts.map +1 -1
- package/dist/cjs/server/completable.d.ts +5 -5
- package/dist/cjs/server/completable.d.ts.map +1 -1
- package/dist/cjs/server/completable.js +5 -5
- package/dist/cjs/server/completable.js.map +1 -1
- package/dist/cjs/server/index.d.ts +9 -9
- package/dist/cjs/server/index.d.ts.map +1 -1
- package/dist/cjs/server/index.js +38 -42
- package/dist/cjs/server/index.js.map +1 -1
- package/dist/cjs/server/mcp.d.ts +8 -8
- package/dist/cjs/server/mcp.d.ts.map +1 -1
- package/dist/cjs/server/mcp.js +87 -82
- package/dist/cjs/server/mcp.js.map +1 -1
- package/dist/cjs/server/sse.d.ts +4 -4
- package/dist/cjs/server/sse.d.ts.map +1 -1
- package/dist/cjs/server/sse.js +16 -15
- package/dist/cjs/server/sse.js.map +1 -1
- package/dist/cjs/server/stdio.d.ts +3 -3
- package/dist/cjs/server/stdio.d.ts.map +1 -1
- package/dist/cjs/server/stdio.js +7 -7
- package/dist/cjs/server/stdio.js.map +1 -1
- package/dist/cjs/server/streamableHttp.d.ts +5 -5
- package/dist/cjs/server/streamableHttp.d.ts.map +1 -1
- package/dist/cjs/server/streamableHttp.js +63 -64
- package/dist/cjs/server/streamableHttp.js.map +1 -1
- package/dist/cjs/shared/auth-utils.d.ts.map +1 -1
- package/dist/cjs/shared/auth-utils.js +3 -3
- package/dist/cjs/shared/auth-utils.js.map +1 -1
- package/dist/cjs/shared/auth.d.ts +1 -1
- package/dist/cjs/shared/auth.d.ts.map +1 -1
- package/dist/cjs/shared/auth.js +42 -46
- package/dist/cjs/shared/auth.js.map +1 -1
- package/dist/cjs/shared/metadataUtils.d.ts +1 -1
- package/dist/cjs/shared/metadataUtils.js.map +1 -1
- package/dist/cjs/shared/protocol.d.ts +6 -6
- package/dist/cjs/shared/protocol.d.ts.map +1 -1
- package/dist/cjs/shared/protocol.js +42 -43
- package/dist/cjs/shared/protocol.js.map +1 -1
- package/dist/cjs/shared/stdio.d.ts +1 -1
- package/dist/cjs/shared/stdio.d.ts.map +1 -1
- package/dist/cjs/shared/stdio.js +3 -3
- package/dist/cjs/shared/stdio.js.map +1 -1
- package/dist/cjs/shared/transport.d.ts +1 -1
- package/dist/cjs/shared/transport.d.ts.map +1 -1
- package/dist/cjs/shared/uriTemplate.d.ts.map +1 -1
- package/dist/cjs/shared/uriTemplate.js +69 -71
- package/dist/cjs/shared/uriTemplate.js.map +1 -1
- package/dist/cjs/types.d.ts +9650 -4790
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +199 -234
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/cli.js +45 -47
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/client/auth.d.ts +12 -12
- package/dist/esm/client/auth.d.ts.map +1 -1
- package/dist/esm/client/auth.js +82 -89
- package/dist/esm/client/auth.js.map +1 -1
- package/dist/esm/client/index.d.ts +186 -123
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +43 -44
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/middleware.d.ts +2 -2
- package/dist/esm/client/middleware.d.ts.map +1 -1
- package/dist/esm/client/middleware.js +23 -28
- package/dist/esm/client/middleware.js.map +1 -1
- package/dist/esm/client/sse.d.ts +4 -4
- package/dist/esm/client/sse.d.ts.map +1 -1
- package/dist/esm/client/sse.js +37 -24
- package/dist/esm/client/sse.js.map +1 -1
- package/dist/esm/client/stdio.d.ts +4 -4
- package/dist/esm/client/stdio.d.ts.map +1 -1
- package/dist/esm/client/stdio.js +36 -36
- package/dist/esm/client/stdio.js.map +1 -1
- package/dist/esm/client/streamableHttp.d.ts +7 -6
- package/dist/esm/client/streamableHttp.d.ts.map +1 -1
- package/dist/esm/client/streamableHttp.js +58 -41
- package/dist/esm/client/streamableHttp.js.map +1 -1
- package/dist/esm/client/websocket.d.ts +2 -2
- package/dist/esm/client/websocket.d.ts.map +1 -1
- package/dist/esm/client/websocket.js +6 -8
- package/dist/esm/client/websocket.js.map +1 -1
- package/dist/esm/examples/client/multipleClientsParallel.js +3 -3
- package/dist/esm/examples/client/multipleClientsParallel.js.map +1 -1
- package/dist/esm/examples/client/parallelToolCallsClient.js +7 -6
- package/dist/esm/examples/client/parallelToolCallsClient.js.map +1 -1
- package/dist/esm/examples/client/simpleOAuthClient.js +15 -13
- package/dist/esm/examples/client/simpleOAuthClient.js.map +1 -1
- package/dist/esm/examples/client/simpleStreamableHttp.js +17 -13
- package/dist/esm/examples/client/simpleStreamableHttp.js.map +1 -1
- package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js +3 -3
- package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js.map +1 -1
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts +1 -1
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts.map +1 -1
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.js +19 -17
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.js.map +1 -1
- package/dist/esm/examples/server/jsonResponseStreamableHttp.js +18 -18
- package/dist/esm/examples/server/jsonResponseStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/mcpServerOutputSchema.js +22 -20
- package/dist/esm/examples/server/mcpServerOutputSchema.js.map +1 -1
- package/dist/esm/examples/server/simpleSseServer.js +8 -8
- package/dist/esm/examples/server/simpleSseServer.js.map +1 -1
- package/dist/esm/examples/server/simpleStatelessStreamableHttp.js +22 -22
- package/dist/esm/examples/server/simpleStatelessStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/simpleStreamableHttp.js +78 -78
- package/dist/esm/examples/server/simpleStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js +19 -19
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
- package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js +8 -8
- package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/toolWithSampleServer.js +22 -22
- package/dist/esm/examples/server/toolWithSampleServer.js.map +1 -1
- package/dist/esm/examples/shared/inMemoryEventStore.d.ts.map +1 -1
- package/dist/esm/examples/shared/inMemoryEventStore.js.map +1 -1
- package/dist/esm/inMemory.d.ts +3 -3
- package/dist/esm/inMemory.d.ts.map +1 -1
- package/dist/esm/inMemory.js +1 -1
- package/dist/esm/inMemory.js.map +1 -1
- package/dist/esm/server/auth/clients.d.ts +2 -2
- package/dist/esm/server/auth/clients.d.ts.map +1 -1
- package/dist/esm/server/auth/errors.d.ts +1 -1
- package/dist/esm/server/auth/errors.d.ts.map +1 -1
- package/dist/esm/server/auth/errors.js +17 -17
- package/dist/esm/server/auth/errors.js.map +1 -1
- package/dist/esm/server/auth/handlers/authorize.d.ts +3 -3
- package/dist/esm/server/auth/handlers/authorize.d.ts.map +1 -1
- package/dist/esm/server/auth/handlers/authorize.js +26 -23
- package/dist/esm/server/auth/handlers/authorize.js.map +1 -1
- package/dist/esm/server/auth/handlers/metadata.d.ts +2 -2
- package/dist/esm/server/auth/handlers/metadata.js +3 -3
- package/dist/esm/server/auth/handlers/metadata.js.map +1 -1
- package/dist/esm/server/auth/handlers/register.d.ts +4 -4
- package/dist/esm/server/auth/handlers/register.d.ts.map +1 -1
- package/dist/esm/server/auth/handlers/register.js +12 -14
- package/dist/esm/server/auth/handlers/register.js.map +1 -1
- package/dist/esm/server/auth/handlers/revoke.d.ts +4 -4
- package/dist/esm/server/auth/handlers/revoke.d.ts.map +1 -1
- package/dist/esm/server/auth/handlers/revoke.js +16 -16
- package/dist/esm/server/auth/handlers/revoke.js.map +1 -1
- package/dist/esm/server/auth/handlers/token.d.ts +3 -3
- package/dist/esm/server/auth/handlers/token.d.ts.map +1 -1
- package/dist/esm/server/auth/handlers/token.js +22 -22
- package/dist/esm/server/auth/handlers/token.js.map +1 -1
- package/dist/esm/server/auth/middleware/allowedMethods.d.ts +1 -1
- package/dist/esm/server/auth/middleware/allowedMethods.d.ts.map +1 -1
- package/dist/esm/server/auth/middleware/allowedMethods.js +2 -4
- package/dist/esm/server/auth/middleware/allowedMethods.js.map +1 -1
- package/dist/esm/server/auth/middleware/bearerAuth.d.ts +4 -4
- package/dist/esm/server/auth/middleware/bearerAuth.d.ts.map +1 -1
- package/dist/esm/server/auth/middleware/bearerAuth.js +8 -8
- package/dist/esm/server/auth/middleware/bearerAuth.js.map +1 -1
- package/dist/esm/server/auth/middleware/clientAuth.d.ts +4 -4
- package/dist/esm/server/auth/middleware/clientAuth.d.ts.map +1 -1
- package/dist/esm/server/auth/middleware/clientAuth.js +8 -8
- package/dist/esm/server/auth/middleware/clientAuth.js.map +1 -1
- package/dist/esm/server/auth/provider.d.ts +4 -4
- package/dist/esm/server/auth/provider.d.ts.map +1 -1
- package/dist/esm/server/auth/providers/proxyProvider.d.ts +10 -10
- package/dist/esm/server/auth/providers/proxyProvider.d.ts.map +1 -1
- package/dist/esm/server/auth/providers/proxyProvider.js +36 -36
- package/dist/esm/server/auth/providers/proxyProvider.js.map +1 -1
- package/dist/esm/server/auth/router.d.ts +11 -11
- package/dist/esm/server/auth/router.d.ts.map +1 -1
- package/dist/esm/server/auth/router.js +22 -24
- package/dist/esm/server/auth/router.js.map +1 -1
- package/dist/esm/server/auth/types.d.ts +1 -1
- package/dist/esm/server/auth/types.d.ts.map +1 -1
- package/dist/esm/server/completable.d.ts +5 -5
- package/dist/esm/server/completable.d.ts.map +1 -1
- package/dist/esm/server/completable.js +6 -6
- package/dist/esm/server/completable.js.map +1 -1
- package/dist/esm/server/index.d.ts +9 -9
- package/dist/esm/server/index.d.ts.map +1 -1
- package/dist/esm/server/index.js +41 -45
- package/dist/esm/server/index.js.map +1 -1
- package/dist/esm/server/mcp.d.ts +8 -8
- package/dist/esm/server/mcp.d.ts.map +1 -1
- package/dist/esm/server/mcp.js +93 -88
- package/dist/esm/server/mcp.js.map +1 -1
- package/dist/esm/server/sse.d.ts +4 -4
- package/dist/esm/server/sse.d.ts.map +1 -1
- package/dist/esm/server/sse.js +20 -19
- package/dist/esm/server/sse.js.map +1 -1
- package/dist/esm/server/stdio.d.ts +3 -3
- package/dist/esm/server/stdio.d.ts.map +1 -1
- package/dist/esm/server/stdio.js +9 -9
- package/dist/esm/server/stdio.js.map +1 -1
- package/dist/esm/server/streamableHttp.d.ts +5 -5
- package/dist/esm/server/streamableHttp.d.ts.map +1 -1
- package/dist/esm/server/streamableHttp.js +67 -68
- package/dist/esm/server/streamableHttp.js.map +1 -1
- package/dist/esm/shared/auth-utils.d.ts.map +1 -1
- package/dist/esm/shared/auth-utils.js +3 -3
- package/dist/esm/shared/auth-utils.js.map +1 -1
- package/dist/esm/shared/auth.d.ts +1 -1
- package/dist/esm/shared/auth.d.ts.map +1 -1
- package/dist/esm/shared/auth.js +43 -47
- package/dist/esm/shared/auth.js.map +1 -1
- package/dist/esm/shared/metadataUtils.d.ts +1 -1
- package/dist/esm/shared/metadataUtils.js.map +1 -1
- package/dist/esm/shared/protocol.d.ts +6 -6
- package/dist/esm/shared/protocol.d.ts.map +1 -1
- package/dist/esm/shared/protocol.js +43 -44
- package/dist/esm/shared/protocol.js.map +1 -1
- package/dist/esm/shared/stdio.d.ts +1 -1
- package/dist/esm/shared/stdio.d.ts.map +1 -1
- package/dist/esm/shared/stdio.js +4 -4
- package/dist/esm/shared/stdio.js.map +1 -1
- package/dist/esm/shared/transport.d.ts +1 -1
- package/dist/esm/shared/transport.d.ts.map +1 -1
- package/dist/esm/shared/uriTemplate.d.ts.map +1 -1
- package/dist/esm/shared/uriTemplate.js +69 -71
- package/dist/esm/shared/uriTemplate.js.map +1 -1
- package/dist/esm/types.d.ts +9650 -4790
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +197 -232
- package/dist/esm/types.js.map +1 -1
- package/package.json +100 -98
package/README.md
CHANGED
|
@@ -7,25 +7,25 @@
|
|
|
7
7
|
- [Quickstart](#quick-start)
|
|
8
8
|
- [What is MCP?](#what-is-mcp)
|
|
9
9
|
- [Core Concepts](#core-concepts)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
- [Server](#server)
|
|
11
|
+
- [Resources](#resources)
|
|
12
|
+
- [Tools](#tools)
|
|
13
|
+
- [Prompts](#prompts)
|
|
14
|
+
- [Completions](#completions)
|
|
15
|
+
- [Sampling](#sampling)
|
|
16
16
|
- [Running Your Server](#running-your-server)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
- [stdio](#stdio)
|
|
18
|
+
- [Streamable HTTP](#streamable-http)
|
|
19
|
+
- [Testing and Debugging](#testing-and-debugging)
|
|
20
20
|
- [Examples](#examples)
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
- [Echo Server](#echo-server)
|
|
22
|
+
- [SQLite Explorer](#sqlite-explorer)
|
|
23
23
|
- [Advanced Usage](#advanced-usage)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
- [Dynamic Servers](#dynamic-servers)
|
|
25
|
+
- [Low-Level Server](#low-level-server)
|
|
26
|
+
- [Writing MCP Clients](#writing-mcp-clients)
|
|
27
|
+
- [Proxy Authorization Requests Upstream](#proxy-authorization-requests-upstream)
|
|
28
|
+
- [Backwards Compatibility](#backwards-compatibility)
|
|
29
29
|
- [Documentation](#documentation)
|
|
30
30
|
- [Contributing](#contributing)
|
|
31
31
|
- [License](#license)
|
|
@@ -52,42 +52,45 @@ npm install @modelcontextprotocol/sdk
|
|
|
52
52
|
Let's create a simple MCP server that exposes a calculator tool and some data:
|
|
53
53
|
|
|
54
54
|
```typescript
|
|
55
|
-
import { McpServer, ResourceTemplate } from
|
|
56
|
-
import { StdioServerTransport } from
|
|
57
|
-
import { z } from
|
|
55
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
56
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
57
|
+
import { z } from 'zod';
|
|
58
58
|
|
|
59
59
|
// Create an MCP server
|
|
60
60
|
const server = new McpServer({
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
name: 'demo-server',
|
|
62
|
+
version: '1.0.0'
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
// Add an addition tool
|
|
66
|
-
server.registerTool(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
server.registerTool(
|
|
67
|
+
'add',
|
|
68
|
+
{
|
|
69
|
+
title: 'Addition Tool',
|
|
70
|
+
description: 'Add two numbers',
|
|
71
|
+
inputSchema: { a: z.number(), b: z.number() }
|
|
72
|
+
},
|
|
73
|
+
async ({ a, b }) => ({
|
|
74
|
+
content: [{ type: 'text', text: String(a + b) }]
|
|
75
|
+
})
|
|
75
76
|
);
|
|
76
77
|
|
|
77
78
|
// Add a dynamic greeting resource
|
|
78
79
|
server.registerResource(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
'greeting',
|
|
81
|
+
new ResourceTemplate('greeting://{name}', { list: undefined }),
|
|
82
|
+
{
|
|
83
|
+
title: 'Greeting Resource', // Display name for UI
|
|
84
|
+
description: 'Dynamic greeting generator'
|
|
85
|
+
},
|
|
86
|
+
async (uri, { name }) => ({
|
|
87
|
+
contents: [
|
|
88
|
+
{
|
|
89
|
+
uri: uri.href,
|
|
90
|
+
text: `Hello, ${name}!`
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
})
|
|
91
94
|
);
|
|
92
95
|
|
|
93
96
|
// Start receiving messages on stdin and sending messages on stdout
|
|
@@ -112,8 +115,8 @@ The McpServer is your core interface to the MCP protocol. It handles connection
|
|
|
112
115
|
|
|
113
116
|
```typescript
|
|
114
117
|
const server = new McpServer({
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
name: 'my-app',
|
|
119
|
+
version: '1.0.0'
|
|
117
120
|
});
|
|
118
121
|
```
|
|
119
122
|
|
|
@@ -124,62 +127,68 @@ Resources are how you expose data to LLMs. They're similar to GET endpoints in a
|
|
|
124
127
|
```typescript
|
|
125
128
|
// Static resource
|
|
126
129
|
server.registerResource(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
'config',
|
|
131
|
+
'config://app',
|
|
132
|
+
{
|
|
133
|
+
title: 'Application Config',
|
|
134
|
+
description: 'Application configuration data',
|
|
135
|
+
mimeType: 'text/plain'
|
|
136
|
+
},
|
|
137
|
+
async uri => ({
|
|
138
|
+
contents: [
|
|
139
|
+
{
|
|
140
|
+
uri: uri.href,
|
|
141
|
+
text: 'App configuration here'
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
})
|
|
140
145
|
);
|
|
141
146
|
|
|
142
147
|
// Dynamic resource with parameters
|
|
143
148
|
server.registerResource(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
'user-profile',
|
|
150
|
+
new ResourceTemplate('users://{userId}/profile', { list: undefined }),
|
|
151
|
+
{
|
|
152
|
+
title: 'User Profile',
|
|
153
|
+
description: 'User profile information'
|
|
154
|
+
},
|
|
155
|
+
async (uri, { userId }) => ({
|
|
156
|
+
contents: [
|
|
157
|
+
{
|
|
158
|
+
uri: uri.href,
|
|
159
|
+
text: `Profile data for user ${userId}`
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
})
|
|
156
163
|
);
|
|
157
164
|
|
|
158
165
|
// Resource with context-aware completion
|
|
159
166
|
server.registerResource(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
'repository',
|
|
168
|
+
new ResourceTemplate('github://repos/{owner}/{repo}', {
|
|
169
|
+
list: undefined,
|
|
170
|
+
complete: {
|
|
171
|
+
// Provide intelligent completions based on previously resolved parameters
|
|
172
|
+
repo: (value, context) => {
|
|
173
|
+
if (context?.arguments?.['owner'] === 'org1') {
|
|
174
|
+
return ['project1', 'project2', 'project3'].filter(r => r.startsWith(value));
|
|
175
|
+
}
|
|
176
|
+
return ['default-repo'].filter(r => r.startsWith(value));
|
|
177
|
+
}
|
|
168
178
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
})
|
|
179
|
+
}),
|
|
180
|
+
{
|
|
181
|
+
title: 'GitHub Repository',
|
|
182
|
+
description: 'Repository information'
|
|
183
|
+
},
|
|
184
|
+
async (uri, { owner, repo }) => ({
|
|
185
|
+
contents: [
|
|
186
|
+
{
|
|
187
|
+
uri: uri.href,
|
|
188
|
+
text: `Repository: ${owner}/${repo}`
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
})
|
|
183
192
|
);
|
|
184
193
|
```
|
|
185
194
|
|
|
@@ -190,68 +199,70 @@ Tools let LLMs take actions through your server. Unlike resources, tools are exp
|
|
|
190
199
|
```typescript
|
|
191
200
|
// Simple tool with parameters
|
|
192
201
|
server.registerTool(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
202
|
+
'calculate-bmi',
|
|
203
|
+
{
|
|
204
|
+
title: 'BMI Calculator',
|
|
205
|
+
description: 'Calculate Body Mass Index',
|
|
206
|
+
inputSchema: {
|
|
207
|
+
weightKg: z.number(),
|
|
208
|
+
heightM: z.number()
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
async ({ weightKg, heightM }) => ({
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
text: String(weightKg / (heightM * heightM))
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
})
|
|
208
219
|
);
|
|
209
220
|
|
|
210
221
|
// Async tool with external API call
|
|
211
222
|
server.registerTool(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
'fetch-weather',
|
|
224
|
+
{
|
|
225
|
+
title: 'Weather Fetcher',
|
|
226
|
+
description: 'Get weather data for a city',
|
|
227
|
+
inputSchema: { city: z.string() }
|
|
228
|
+
},
|
|
229
|
+
async ({ city }) => {
|
|
230
|
+
const response = await fetch(`https://api.weather.com/${city}`);
|
|
231
|
+
const data = await response.text();
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: 'text', text: data }]
|
|
234
|
+
};
|
|
235
|
+
}
|
|
225
236
|
);
|
|
226
237
|
|
|
227
238
|
// Tool that returns ResourceLinks
|
|
228
239
|
server.registerTool(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
240
|
+
'list-files',
|
|
241
|
+
{
|
|
242
|
+
title: 'List Files',
|
|
243
|
+
description: 'List project files',
|
|
244
|
+
inputSchema: { pattern: z.string() }
|
|
245
|
+
},
|
|
246
|
+
async ({ pattern }) => ({
|
|
247
|
+
content: [
|
|
248
|
+
{ type: 'text', text: `Found files matching "${pattern}":` },
|
|
249
|
+
// ResourceLinks let tools return references without file content
|
|
250
|
+
{
|
|
251
|
+
type: 'resource_link',
|
|
252
|
+
uri: 'file:///project/README.md',
|
|
253
|
+
name: 'README.md',
|
|
254
|
+
mimeType: 'text/markdown',
|
|
255
|
+
description: 'A README file'
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
type: 'resource_link',
|
|
259
|
+
uri: 'file:///project/src/index.ts',
|
|
260
|
+
name: 'index.ts',
|
|
261
|
+
mimeType: 'text/typescript',
|
|
262
|
+
description: 'An index file'
|
|
263
|
+
}
|
|
264
|
+
]
|
|
265
|
+
})
|
|
255
266
|
);
|
|
256
267
|
```
|
|
257
268
|
|
|
@@ -264,60 +275,64 @@ Tools can return `ResourceLink` objects to reference resources without embedding
|
|
|
264
275
|
Prompts are reusable templates that help LLMs interact with your server effectively:
|
|
265
276
|
|
|
266
277
|
```typescript
|
|
267
|
-
import { completable } from
|
|
278
|
+
import { completable } from '@modelcontextprotocol/sdk/server/completable.js';
|
|
268
279
|
|
|
269
280
|
server.registerPrompt(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
'review-code',
|
|
282
|
+
{
|
|
283
|
+
title: 'Code Review',
|
|
284
|
+
description: 'Review code for best practices and potential issues',
|
|
285
|
+
argsSchema: { code: z.string() }
|
|
286
|
+
},
|
|
287
|
+
({ code }) => ({
|
|
288
|
+
messages: [
|
|
289
|
+
{
|
|
290
|
+
role: 'user',
|
|
291
|
+
content: {
|
|
292
|
+
type: 'text',
|
|
293
|
+
text: `Please review this code:\n\n${code}`
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
})
|
|
285
298
|
);
|
|
286
299
|
|
|
287
300
|
// Prompt with context-aware completion
|
|
288
301
|
server.registerPrompt(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
302
|
+
'team-greeting',
|
|
303
|
+
{
|
|
304
|
+
title: 'Team Greeting',
|
|
305
|
+
description: 'Generate a greeting for team members',
|
|
306
|
+
argsSchema: {
|
|
307
|
+
department: completable(z.string(), value => {
|
|
308
|
+
// Department suggestions
|
|
309
|
+
return ['engineering', 'sales', 'marketing', 'support'].filter(d => d.startsWith(value));
|
|
310
|
+
}),
|
|
311
|
+
name: completable(z.string(), (value, context) => {
|
|
312
|
+
// Name suggestions based on selected department
|
|
313
|
+
const department = context?.arguments?.['department'];
|
|
314
|
+
if (department === 'engineering') {
|
|
315
|
+
return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));
|
|
316
|
+
} else if (department === 'sales') {
|
|
317
|
+
return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));
|
|
318
|
+
} else if (department === 'marketing') {
|
|
319
|
+
return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));
|
|
320
|
+
}
|
|
321
|
+
return ['Guest'].filter(n => n.startsWith(value));
|
|
322
|
+
})
|
|
307
323
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
})
|
|
324
|
+
},
|
|
325
|
+
({ department, name }) => ({
|
|
326
|
+
messages: [
|
|
327
|
+
{
|
|
328
|
+
role: 'assistant',
|
|
329
|
+
content: {
|
|
330
|
+
type: 'text',
|
|
331
|
+
text: `Hello ${name}, welcome to the ${department} team!`
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
})
|
|
321
336
|
);
|
|
322
337
|
```
|
|
323
338
|
|
|
@@ -330,21 +345,21 @@ MCP supports argument completions to help users fill in prompt arguments and res
|
|
|
330
345
|
```typescript
|
|
331
346
|
// Request completions for any argument
|
|
332
347
|
const result = await client.complete({
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
348
|
+
ref: {
|
|
349
|
+
type: 'ref/prompt', // or "ref/resource"
|
|
350
|
+
name: 'example' // or uri: "template://..."
|
|
351
|
+
},
|
|
352
|
+
argument: {
|
|
353
|
+
name: 'argumentName',
|
|
354
|
+
value: 'partial' // What the user has typed so far
|
|
355
|
+
},
|
|
356
|
+
context: {
|
|
357
|
+
// Optional: Include previously resolved arguments
|
|
358
|
+
arguments: {
|
|
359
|
+
previousArg: 'value'
|
|
360
|
+
}
|
|
344
361
|
}
|
|
345
|
-
}
|
|
346
362
|
});
|
|
347
|
-
|
|
348
363
|
```
|
|
349
364
|
|
|
350
365
|
### Display Names and Metadata
|
|
@@ -356,6 +371,7 @@ All resources, tools, and prompts support an optional `title` field for better U
|
|
|
356
371
|
#### Title Precedence for Tools
|
|
357
372
|
|
|
358
373
|
For tools specifically, there are two ways to specify a title:
|
|
374
|
+
|
|
359
375
|
- `title` field in the tool configuration
|
|
360
376
|
- `annotations.title` field (when using the older `tool()` method with annotations)
|
|
361
377
|
|
|
@@ -363,23 +379,32 @@ The precedence order is: `title` → `annotations.title` → `name`
|
|
|
363
379
|
|
|
364
380
|
```typescript
|
|
365
381
|
// Using registerTool (recommended)
|
|
366
|
-
server.registerTool(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
382
|
+
server.registerTool(
|
|
383
|
+
'my_tool',
|
|
384
|
+
{
|
|
385
|
+
title: 'My Tool', // This title takes precedence
|
|
386
|
+
annotations: {
|
|
387
|
+
title: 'Annotation Title' // This is ignored if title is set
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
handler
|
|
391
|
+
);
|
|
372
392
|
|
|
373
393
|
// Using tool with annotations (older API)
|
|
374
|
-
server.tool(
|
|
375
|
-
|
|
376
|
-
|
|
394
|
+
server.tool(
|
|
395
|
+
'my_tool',
|
|
396
|
+
'description',
|
|
397
|
+
{
|
|
398
|
+
title: 'Annotation Title' // This is used as title
|
|
399
|
+
},
|
|
400
|
+
handler
|
|
401
|
+
);
|
|
377
402
|
```
|
|
378
403
|
|
|
379
404
|
When building clients, use the provided utility to get the appropriate display name:
|
|
380
405
|
|
|
381
406
|
```typescript
|
|
382
|
-
import { getDisplayName } from
|
|
407
|
+
import { getDisplayName } from '@modelcontextprotocol/sdk/shared/metadataUtils.js';
|
|
383
408
|
|
|
384
409
|
// Automatically handles the precedence: title → annotations.title → name
|
|
385
410
|
const displayName = getDisplayName(tool);
|
|
@@ -390,63 +415,62 @@ const displayName = getDisplayName(tool);
|
|
|
390
415
|
MCP servers can request LLM completions from connected clients that support sampling.
|
|
391
416
|
|
|
392
417
|
```typescript
|
|
393
|
-
import { McpServer } from
|
|
394
|
-
import { StdioServerTransport } from
|
|
395
|
-
import { z } from
|
|
418
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
419
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
420
|
+
import { z } from 'zod';
|
|
396
421
|
|
|
397
422
|
const mcpServer = new McpServer({
|
|
398
|
-
|
|
399
|
-
|
|
423
|
+
name: 'tools-with-sample-server',
|
|
424
|
+
version: '1.0.0'
|
|
400
425
|
});
|
|
401
426
|
|
|
402
427
|
// Tool that uses LLM sampling to summarize any text
|
|
403
428
|
mcpServer.registerTool(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
429
|
+
'summarize',
|
|
430
|
+
{
|
|
431
|
+
description: 'Summarize any text using an LLM',
|
|
432
|
+
inputSchema: {
|
|
433
|
+
text: z.string().describe('Text to summarize')
|
|
434
|
+
}
|
|
409
435
|
},
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
});
|
|
436
|
+
async ({ text }) => {
|
|
437
|
+
// Call the LLM through MCP sampling
|
|
438
|
+
const response = await mcpServer.server.createMessage({
|
|
439
|
+
messages: [
|
|
440
|
+
{
|
|
441
|
+
role: 'user',
|
|
442
|
+
content: {
|
|
443
|
+
type: 'text',
|
|
444
|
+
text: `Please summarize the following text concisely:\n\n${text}`
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
],
|
|
448
|
+
maxTokens: 500
|
|
449
|
+
});
|
|
425
450
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
451
|
+
return {
|
|
452
|
+
content: [
|
|
453
|
+
{
|
|
454
|
+
type: 'text',
|
|
455
|
+
text: response.content.type === 'text' ? response.content.text : 'Unable to generate summary'
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
};
|
|
459
|
+
}
|
|
435
460
|
);
|
|
436
461
|
|
|
437
462
|
async function main() {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
463
|
+
const transport = new StdioServerTransport();
|
|
464
|
+
await mcpServer.connect(transport);
|
|
465
|
+
console.error('MCP server is running...');
|
|
441
466
|
}
|
|
442
467
|
|
|
443
|
-
main().catch(
|
|
444
|
-
|
|
445
|
-
|
|
468
|
+
main().catch(error => {
|
|
469
|
+
console.error('Server error:', error);
|
|
470
|
+
process.exit(1);
|
|
446
471
|
});
|
|
447
472
|
```
|
|
448
473
|
|
|
449
|
-
|
|
450
474
|
## Running Your Server
|
|
451
475
|
|
|
452
476
|
MCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport:
|
|
@@ -456,12 +480,12 @@ MCP servers in TypeScript need to be connected to a transport to communicate wit
|
|
|
456
480
|
For command-line tools and direct integrations:
|
|
457
481
|
|
|
458
482
|
```typescript
|
|
459
|
-
import { McpServer } from
|
|
460
|
-
import { StdioServerTransport } from
|
|
483
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
484
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
461
485
|
|
|
462
486
|
const server = new McpServer({
|
|
463
|
-
|
|
464
|
-
|
|
487
|
+
name: 'example-server',
|
|
488
|
+
version: '1.0.0'
|
|
465
489
|
});
|
|
466
490
|
|
|
467
491
|
// ... set up server resources, tools, and prompts ...
|
|
@@ -479,13 +503,11 @@ For remote servers, set up a Streamable HTTP transport that handles both client
|
|
|
479
503
|
In some cases, servers need to be stateful. This is achieved by [session management](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management).
|
|
480
504
|
|
|
481
505
|
```typescript
|
|
482
|
-
import express from
|
|
483
|
-
import { randomUUID } from
|
|
484
|
-
import { McpServer } from
|
|
485
|
-
import { StreamableHTTPServerTransport } from
|
|
486
|
-
import { isInitializeRequest } from
|
|
487
|
-
|
|
488
|
-
|
|
506
|
+
import express from 'express';
|
|
507
|
+
import { randomUUID } from 'node:crypto';
|
|
508
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
509
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
510
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
489
511
|
|
|
490
512
|
const app = express();
|
|
491
513
|
app.use(express.json());
|
|
@@ -495,69 +517,69 @@ const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
|
|
|
495
517
|
|
|
496
518
|
// Handle POST requests for client-to-server communication
|
|
497
519
|
app.post('/mcp', async (req, res) => {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
520
|
+
// Check for existing session ID
|
|
521
|
+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
|
522
|
+
let transport: StreamableHTTPServerTransport;
|
|
523
|
+
|
|
524
|
+
if (sessionId && transports[sessionId]) {
|
|
525
|
+
// Reuse existing transport
|
|
526
|
+
transport = transports[sessionId];
|
|
527
|
+
} else if (!sessionId && isInitializeRequest(req.body)) {
|
|
528
|
+
// New initialization request
|
|
529
|
+
transport = new StreamableHTTPServerTransport({
|
|
530
|
+
sessionIdGenerator: () => randomUUID(),
|
|
531
|
+
onsessioninitialized: sessionId => {
|
|
532
|
+
// Store the transport by session ID
|
|
533
|
+
transports[sessionId] = transport;
|
|
534
|
+
}
|
|
535
|
+
// DNS rebinding protection is disabled by default for backwards compatibility. If you are running this server
|
|
536
|
+
// locally, make sure to set:
|
|
537
|
+
// enableDnsRebindingProtection: true,
|
|
538
|
+
// allowedHosts: ['127.0.0.1'],
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
// Clean up transport when closed
|
|
542
|
+
transport.onclose = () => {
|
|
543
|
+
if (transport.sessionId) {
|
|
544
|
+
delete transports[transport.sessionId];
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
const server = new McpServer({
|
|
548
|
+
name: 'example-server',
|
|
549
|
+
version: '1.0.0'
|
|
550
|
+
});
|
|
529
551
|
|
|
530
|
-
|
|
552
|
+
// ... set up server resources, tools, and prompts ...
|
|
531
553
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
554
|
+
// Connect to the MCP server
|
|
555
|
+
await server.connect(transport);
|
|
556
|
+
} else {
|
|
557
|
+
// Invalid request
|
|
558
|
+
res.status(400).json({
|
|
559
|
+
jsonrpc: '2.0',
|
|
560
|
+
error: {
|
|
561
|
+
code: -32000,
|
|
562
|
+
message: 'Bad Request: No valid session ID provided'
|
|
563
|
+
},
|
|
564
|
+
id: null
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
546
568
|
|
|
547
|
-
|
|
548
|
-
|
|
569
|
+
// Handle the request
|
|
570
|
+
await transport.handleRequest(req, res, req.body);
|
|
549
571
|
});
|
|
550
572
|
|
|
551
573
|
// Reusable handler for GET and DELETE requests
|
|
552
574
|
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
575
|
+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
|
576
|
+
if (!sessionId || !transports[sessionId]) {
|
|
577
|
+
res.status(400).send('Invalid or missing session ID');
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const transport = transports[sessionId];
|
|
582
|
+
await transport.handleRequest(req, res);
|
|
561
583
|
};
|
|
562
584
|
|
|
563
585
|
// Handle GET requests for server-to-client notifications via SSE
|
|
@@ -569,9 +591,7 @@ app.delete('/mcp', handleSessionRequest);
|
|
|
569
591
|
app.listen(3000);
|
|
570
592
|
```
|
|
571
593
|
|
|
572
|
-
> [!TIP]
|
|
573
|
-
> When using this in a remote environment, make sure to allow the header parameter `mcp-session-id` in CORS. Otherwise, it may result in a `Bad Request: No valid session ID provided` error. Read the following section for examples.
|
|
574
|
-
|
|
594
|
+
> [!TIP] When using this in a remote environment, make sure to allow the header parameter `mcp-session-id` in CORS. Otherwise, it may result in a `Bad Request: No valid session ID provided` error. Read the following section for examples.
|
|
575
595
|
|
|
576
596
|
#### CORS Configuration for Browser-Based Clients
|
|
577
597
|
|
|
@@ -581,15 +601,18 @@ If you'd like your server to be accessible by browser-based MCP clients, you'll
|
|
|
581
601
|
import cors from 'cors';
|
|
582
602
|
|
|
583
603
|
// Add CORS middleware before your MCP routes
|
|
584
|
-
app.use(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
604
|
+
app.use(
|
|
605
|
+
cors({
|
|
606
|
+
origin: '*', // Configure appropriately for production, for example:
|
|
607
|
+
// origin: ['https://your-remote-domain.com', 'https://your-other-remote-domain.com'],
|
|
608
|
+
exposedHeaders: ['Mcp-Session-Id'],
|
|
609
|
+
allowedHeaders: ['Content-Type', 'mcp-session-id']
|
|
610
|
+
})
|
|
611
|
+
);
|
|
590
612
|
```
|
|
591
613
|
|
|
592
614
|
This configuration is necessary because:
|
|
615
|
+
|
|
593
616
|
- The MCP streamable HTTP transport uses the `Mcp-Session-Id` header for session management
|
|
594
617
|
- Browsers restrict access to response headers unless explicitly exposed via CORS
|
|
595
618
|
- Without this configuration, browser-based clients won't be able to read the session ID from initialization responses
|
|
@@ -603,79 +626,83 @@ const app = express();
|
|
|
603
626
|
app.use(express.json());
|
|
604
627
|
|
|
605
628
|
app.post('/mcp', async (req: Request, res: Response) => {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
629
|
+
// In stateless mode, create a new instance of transport and server for each request
|
|
630
|
+
// to ensure complete isolation. A single instance would cause request ID collisions
|
|
631
|
+
// when multiple clients connect concurrently.
|
|
632
|
+
|
|
633
|
+
try {
|
|
634
|
+
const server = getServer();
|
|
635
|
+
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
|
|
636
|
+
sessionIdGenerator: undefined
|
|
637
|
+
});
|
|
638
|
+
res.on('close', () => {
|
|
639
|
+
console.log('Request closed');
|
|
640
|
+
transport.close();
|
|
641
|
+
server.close();
|
|
642
|
+
});
|
|
643
|
+
await server.connect(transport);
|
|
644
|
+
await transport.handleRequest(req, res, req.body);
|
|
645
|
+
} catch (error) {
|
|
646
|
+
console.error('Error handling MCP request:', error);
|
|
647
|
+
if (!res.headersSent) {
|
|
648
|
+
res.status(500).json({
|
|
649
|
+
jsonrpc: '2.0',
|
|
650
|
+
error: {
|
|
651
|
+
code: -32603,
|
|
652
|
+
message: 'Internal server error'
|
|
653
|
+
},
|
|
654
|
+
id: null
|
|
655
|
+
});
|
|
656
|
+
}
|
|
633
657
|
}
|
|
634
|
-
}
|
|
635
658
|
});
|
|
636
659
|
|
|
637
660
|
// SSE notifications not supported in stateless mode
|
|
638
661
|
app.get('/mcp', async (req: Request, res: Response) => {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
662
|
+
console.log('Received GET MCP request');
|
|
663
|
+
res.writeHead(405).end(
|
|
664
|
+
JSON.stringify({
|
|
665
|
+
jsonrpc: '2.0',
|
|
666
|
+
error: {
|
|
667
|
+
code: -32000,
|
|
668
|
+
message: 'Method not allowed.'
|
|
669
|
+
},
|
|
670
|
+
id: null
|
|
671
|
+
})
|
|
672
|
+
);
|
|
648
673
|
});
|
|
649
674
|
|
|
650
675
|
// Session termination not needed in stateless mode
|
|
651
676
|
app.delete('/mcp', async (req: Request, res: Response) => {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
677
|
+
console.log('Received DELETE MCP request');
|
|
678
|
+
res.writeHead(405).end(
|
|
679
|
+
JSON.stringify({
|
|
680
|
+
jsonrpc: '2.0',
|
|
681
|
+
error: {
|
|
682
|
+
code: -32000,
|
|
683
|
+
message: 'Method not allowed.'
|
|
684
|
+
},
|
|
685
|
+
id: null
|
|
686
|
+
})
|
|
687
|
+
);
|
|
661
688
|
});
|
|
662
689
|
|
|
663
|
-
|
|
664
690
|
// Start the server
|
|
665
691
|
const PORT = 3000;
|
|
666
|
-
setupServer()
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
})
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
692
|
+
setupServer()
|
|
693
|
+
.then(() => {
|
|
694
|
+
app.listen(PORT, error => {
|
|
695
|
+
if (error) {
|
|
696
|
+
console.error('Failed to start server:', error);
|
|
697
|
+
process.exit(1);
|
|
698
|
+
}
|
|
699
|
+
console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
|
|
700
|
+
});
|
|
701
|
+
})
|
|
702
|
+
.catch(error => {
|
|
703
|
+
console.error('Failed to set up the server:', error);
|
|
704
|
+
process.exit(1);
|
|
705
|
+
});
|
|
679
706
|
```
|
|
680
707
|
|
|
681
708
|
This stateless approach is useful for:
|
|
@@ -711,57 +738,61 @@ To test your server, you can use the [MCP Inspector](https://github.com/modelcon
|
|
|
711
738
|
A simple server demonstrating resources, tools, and prompts:
|
|
712
739
|
|
|
713
740
|
```typescript
|
|
714
|
-
import { McpServer, ResourceTemplate } from
|
|
715
|
-
import { z } from
|
|
741
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
742
|
+
import { z } from 'zod';
|
|
716
743
|
|
|
717
744
|
const server = new McpServer({
|
|
718
|
-
|
|
719
|
-
|
|
745
|
+
name: 'echo-server',
|
|
746
|
+
version: '1.0.0'
|
|
720
747
|
});
|
|
721
748
|
|
|
722
749
|
server.registerResource(
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
750
|
+
'echo',
|
|
751
|
+
new ResourceTemplate('echo://{message}', { list: undefined }),
|
|
752
|
+
{
|
|
753
|
+
title: 'Echo Resource',
|
|
754
|
+
description: 'Echoes back messages as resources'
|
|
755
|
+
},
|
|
756
|
+
async (uri, { message }) => ({
|
|
757
|
+
contents: [
|
|
758
|
+
{
|
|
759
|
+
uri: uri.href,
|
|
760
|
+
text: `Resource echo: ${message}`
|
|
761
|
+
}
|
|
762
|
+
]
|
|
763
|
+
})
|
|
735
764
|
);
|
|
736
765
|
|
|
737
766
|
server.registerTool(
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
767
|
+
'echo',
|
|
768
|
+
{
|
|
769
|
+
title: 'Echo Tool',
|
|
770
|
+
description: 'Echoes back the provided message',
|
|
771
|
+
inputSchema: { message: z.string() }
|
|
772
|
+
},
|
|
773
|
+
async ({ message }) => ({
|
|
774
|
+
content: [{ type: 'text', text: `Tool echo: ${message}` }]
|
|
775
|
+
})
|
|
747
776
|
);
|
|
748
777
|
|
|
749
778
|
server.registerPrompt(
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
779
|
+
'echo',
|
|
780
|
+
{
|
|
781
|
+
title: 'Echo Prompt',
|
|
782
|
+
description: 'Creates a prompt to process a message',
|
|
783
|
+
argsSchema: { message: z.string() }
|
|
784
|
+
},
|
|
785
|
+
({ message }) => ({
|
|
786
|
+
messages: [
|
|
787
|
+
{
|
|
788
|
+
role: 'user',
|
|
789
|
+
content: {
|
|
790
|
+
type: 'text',
|
|
791
|
+
text: `Please process this message: ${message}`
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
]
|
|
795
|
+
})
|
|
765
796
|
);
|
|
766
797
|
```
|
|
767
798
|
|
|
@@ -770,81 +801,85 @@ server.registerPrompt(
|
|
|
770
801
|
A more complex example showing database integration:
|
|
771
802
|
|
|
772
803
|
```typescript
|
|
773
|
-
import { McpServer } from
|
|
774
|
-
import sqlite3 from
|
|
775
|
-
import { promisify } from
|
|
776
|
-
import { z } from
|
|
804
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
805
|
+
import sqlite3 from 'sqlite3';
|
|
806
|
+
import { promisify } from 'util';
|
|
807
|
+
import { z } from 'zod';
|
|
777
808
|
|
|
778
809
|
const server = new McpServer({
|
|
779
|
-
|
|
780
|
-
|
|
810
|
+
name: 'sqlite-explorer',
|
|
811
|
+
version: '1.0.0'
|
|
781
812
|
});
|
|
782
813
|
|
|
783
814
|
// Helper to create DB connection
|
|
784
815
|
const getDb = () => {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
816
|
+
const db = new sqlite3.Database('database.db');
|
|
817
|
+
return {
|
|
818
|
+
all: promisify<string, any[]>(db.all.bind(db)),
|
|
819
|
+
close: promisify(db.close.bind(db))
|
|
820
|
+
};
|
|
790
821
|
};
|
|
791
822
|
|
|
792
823
|
server.registerResource(
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
824
|
+
'schema',
|
|
825
|
+
'schema://main',
|
|
826
|
+
{
|
|
827
|
+
title: 'Database Schema',
|
|
828
|
+
description: 'SQLite database schema',
|
|
829
|
+
mimeType: 'text/plain'
|
|
830
|
+
},
|
|
831
|
+
async uri => {
|
|
832
|
+
const db = getDb();
|
|
833
|
+
try {
|
|
834
|
+
const tables = await db.all("SELECT sql FROM sqlite_master WHERE type='table'");
|
|
835
|
+
return {
|
|
836
|
+
contents: [
|
|
837
|
+
{
|
|
838
|
+
uri: uri.href,
|
|
839
|
+
text: tables.map((t: { sql: string }) => t.sql).join('\n')
|
|
840
|
+
}
|
|
841
|
+
]
|
|
842
|
+
};
|
|
843
|
+
} finally {
|
|
844
|
+
await db.close();
|
|
845
|
+
}
|
|
814
846
|
}
|
|
815
|
-
}
|
|
816
847
|
);
|
|
817
848
|
|
|
818
849
|
server.registerTool(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
850
|
+
'query',
|
|
851
|
+
{
|
|
852
|
+
title: 'SQL Query',
|
|
853
|
+
description: 'Execute SQL queries on the database',
|
|
854
|
+
inputSchema: { sql: z.string() }
|
|
855
|
+
},
|
|
856
|
+
async ({ sql }) => {
|
|
857
|
+
const db = getDb();
|
|
858
|
+
try {
|
|
859
|
+
const results = await db.all(sql);
|
|
860
|
+
return {
|
|
861
|
+
content: [
|
|
862
|
+
{
|
|
863
|
+
type: 'text',
|
|
864
|
+
text: JSON.stringify(results, null, 2)
|
|
865
|
+
}
|
|
866
|
+
]
|
|
867
|
+
};
|
|
868
|
+
} catch (err: unknown) {
|
|
869
|
+
const error = err as Error;
|
|
870
|
+
return {
|
|
871
|
+
content: [
|
|
872
|
+
{
|
|
873
|
+
type: 'text',
|
|
874
|
+
text: `Error: ${error.message}`
|
|
875
|
+
}
|
|
876
|
+
],
|
|
877
|
+
isError: true
|
|
878
|
+
};
|
|
879
|
+
} finally {
|
|
880
|
+
await db.close();
|
|
881
|
+
}
|
|
846
882
|
}
|
|
847
|
-
}
|
|
848
883
|
);
|
|
849
884
|
```
|
|
850
885
|
|
|
@@ -855,57 +890,49 @@ server.registerTool(
|
|
|
855
890
|
If you want to offer an initial set of tools/prompts/resources, but later add additional ones based on user action or external state change, you can add/update/remove them _after_ the Server is connected. This will automatically emit the corresponding `listChanged` notifications:
|
|
856
891
|
|
|
857
892
|
```ts
|
|
858
|
-
import { McpServer } from
|
|
859
|
-
import { z } from
|
|
893
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
894
|
+
import { z } from 'zod';
|
|
860
895
|
|
|
861
896
|
const server = new McpServer({
|
|
862
|
-
|
|
863
|
-
|
|
897
|
+
name: 'Dynamic Example',
|
|
898
|
+
version: '1.0.0'
|
|
864
899
|
});
|
|
865
900
|
|
|
866
|
-
const listMessageTool = server.tool(
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
async ({ channel }) => ({
|
|
870
|
-
content: [{ type: "text", text: await listMessages(channel) }]
|
|
871
|
-
})
|
|
872
|
-
);
|
|
901
|
+
const listMessageTool = server.tool('listMessages', { channel: z.string() }, async ({ channel }) => ({
|
|
902
|
+
content: [{ type: 'text', text: await listMessages(channel) }]
|
|
903
|
+
}));
|
|
873
904
|
|
|
874
|
-
const putMessageTool = server.tool(
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
async ({ channel, message }) => ({
|
|
878
|
-
content: [{ type: "text", text: await putMessage(channel, message) }]
|
|
879
|
-
})
|
|
880
|
-
);
|
|
905
|
+
const putMessageTool = server.tool('putMessage', { channel: z.string(), message: z.string() }, async ({ channel, message }) => ({
|
|
906
|
+
content: [{ type: 'text', text: await putMessage(channel, message) }]
|
|
907
|
+
}));
|
|
881
908
|
// Until we upgrade auth, `putMessage` is disabled (won't show up in listTools)
|
|
882
|
-
putMessageTool.disable()
|
|
909
|
+
putMessageTool.disable();
|
|
883
910
|
|
|
884
911
|
const upgradeAuthTool = server.tool(
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
912
|
+
'upgradeAuth',
|
|
913
|
+
{ permission: z.enum(['write', 'admin']) },
|
|
914
|
+
// Any mutations here will automatically emit `listChanged` notifications
|
|
915
|
+
async ({ permission }) => {
|
|
916
|
+
const { ok, err, previous } = await upgradeAuthAndStoreToken(permission);
|
|
917
|
+
if (!ok) return { content: [{ type: 'text', text: `Error: ${err}` }] };
|
|
918
|
+
|
|
919
|
+
// If we previously had read-only access, 'putMessage' is now available
|
|
920
|
+
if (previous === 'read') {
|
|
921
|
+
putMessageTool.enable();
|
|
922
|
+
}
|
|
896
923
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
924
|
+
if (permission === 'write') {
|
|
925
|
+
// If we've just upgraded to 'write' permissions, we can still call 'upgradeAuth'
|
|
926
|
+
// but can only upgrade to 'admin'.
|
|
927
|
+
upgradeAuthTool.update({
|
|
928
|
+
paramsSchema: { permission: z.enum(['admin']) } // change validation rules
|
|
929
|
+
});
|
|
930
|
+
} else {
|
|
931
|
+
// If we're now an admin, we no longer have anywhere to upgrade to, so fully remove that tool
|
|
932
|
+
upgradeAuthTool.remove();
|
|
933
|
+
}
|
|
906
934
|
}
|
|
907
|
-
|
|
908
|
-
)
|
|
935
|
+
);
|
|
909
936
|
|
|
910
937
|
// Connect as normal
|
|
911
938
|
const transport = new StdioServerTransport();
|
|
@@ -918,8 +945,8 @@ When performing bulk updates that trigger notifications (e.g., enabling or disab
|
|
|
918
945
|
|
|
919
946
|
This feature coalesces multiple, rapid calls for the same notification type into a single message. For example, if you disable five tools in a row, only one `notifications/tools/list_changed` message will be sent instead of five.
|
|
920
947
|
|
|
921
|
-
> [!IMPORTANT]
|
|
922
|
-
>
|
|
948
|
+
> [!IMPORTANT] This feature is designed for "simple" notifications that do not carry unique data in their parameters. To prevent silent data loss, debouncing is **automatically bypassed** for any notification that contains a `params` object or a `relatedRequestId`. Such
|
|
949
|
+
> notifications will always be sent immediately.
|
|
923
950
|
|
|
924
951
|
This is an opt-in feature configured during server initialization.
|
|
925
952
|
|
|
@@ -954,53 +981,56 @@ server.registerTool("tool3", ...).disable();
|
|
|
954
981
|
For more control, you can use the low-level Server class directly:
|
|
955
982
|
|
|
956
983
|
```typescript
|
|
957
|
-
import { Server } from
|
|
958
|
-
import { StdioServerTransport } from
|
|
959
|
-
import {
|
|
960
|
-
ListPromptsRequestSchema,
|
|
961
|
-
GetPromptRequestSchema
|
|
962
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
984
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
985
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
986
|
+
import { ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
963
987
|
|
|
964
988
|
const server = new Server(
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
989
|
+
{
|
|
990
|
+
name: 'example-server',
|
|
991
|
+
version: '1.0.0'
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
capabilities: {
|
|
995
|
+
prompts: {}
|
|
996
|
+
}
|
|
972
997
|
}
|
|
973
|
-
}
|
|
974
998
|
);
|
|
975
999
|
|
|
976
1000
|
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1001
|
+
return {
|
|
1002
|
+
prompts: [
|
|
1003
|
+
{
|
|
1004
|
+
name: 'example-prompt',
|
|
1005
|
+
description: 'An example prompt template',
|
|
1006
|
+
arguments: [
|
|
1007
|
+
{
|
|
1008
|
+
name: 'arg1',
|
|
1009
|
+
description: 'Example argument',
|
|
1010
|
+
required: true
|
|
1011
|
+
}
|
|
1012
|
+
]
|
|
1013
|
+
}
|
|
1014
|
+
]
|
|
1015
|
+
};
|
|
988
1016
|
});
|
|
989
1017
|
|
|
990
|
-
server.setRequestHandler(GetPromptRequestSchema, async
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1018
|
+
server.setRequestHandler(GetPromptRequestSchema, async request => {
|
|
1019
|
+
if (request.params.name !== 'example-prompt') {
|
|
1020
|
+
throw new Error('Unknown prompt');
|
|
1021
|
+
}
|
|
1022
|
+
return {
|
|
1023
|
+
description: 'Example prompt',
|
|
1024
|
+
messages: [
|
|
1025
|
+
{
|
|
1026
|
+
role: 'user',
|
|
1027
|
+
content: {
|
|
1028
|
+
type: 'text',
|
|
1029
|
+
text: 'Example prompt text'
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
]
|
|
1033
|
+
};
|
|
1004
1034
|
});
|
|
1005
1035
|
|
|
1006
1036
|
const transport = new StdioServerTransport();
|
|
@@ -1014,72 +1044,73 @@ MCP servers can request additional information from users through the elicitatio
|
|
|
1014
1044
|
```typescript
|
|
1015
1045
|
// Server-side: Restaurant booking tool that asks for alternatives
|
|
1016
1046
|
server.tool(
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1047
|
+
'book-restaurant',
|
|
1048
|
+
{
|
|
1049
|
+
restaurant: z.string(),
|
|
1050
|
+
date: z.string(),
|
|
1051
|
+
partySize: z.number()
|
|
1052
|
+
},
|
|
1053
|
+
async ({ restaurant, date, partySize }) => {
|
|
1054
|
+
// Check availability
|
|
1055
|
+
const available = await checkAvailability(restaurant, date, partySize);
|
|
1056
|
+
|
|
1057
|
+
if (!available) {
|
|
1058
|
+
// Ask user if they want to try alternative dates
|
|
1059
|
+
const result = await server.server.elicitInput({
|
|
1060
|
+
message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
|
|
1061
|
+
requestedSchema: {
|
|
1062
|
+
type: 'object',
|
|
1063
|
+
properties: {
|
|
1064
|
+
checkAlternatives: {
|
|
1065
|
+
type: 'boolean',
|
|
1066
|
+
title: 'Check alternative dates',
|
|
1067
|
+
description: 'Would you like me to check other dates?'
|
|
1068
|
+
},
|
|
1069
|
+
flexibleDates: {
|
|
1070
|
+
type: 'string',
|
|
1071
|
+
title: 'Date flexibility',
|
|
1072
|
+
description: 'How flexible are your dates?',
|
|
1073
|
+
enum: ['next_day', 'same_week', 'next_week'],
|
|
1074
|
+
enumNames: ['Next day', 'Same week', 'Next week']
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
required: ['checkAlternatives']
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
if (result.action === 'accept' && result.content?.checkAlternatives) {
|
|
1082
|
+
const alternatives = await findAlternatives(restaurant, date, partySize, result.content.flexibleDates as string);
|
|
1083
|
+
return {
|
|
1084
|
+
content: [
|
|
1085
|
+
{
|
|
1086
|
+
type: 'text',
|
|
1087
|
+
text: `Found these alternatives: ${alternatives.join(', ')}`
|
|
1088
|
+
}
|
|
1089
|
+
]
|
|
1090
|
+
};
|
|
1045
1091
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1092
|
+
|
|
1093
|
+
return {
|
|
1094
|
+
content: [
|
|
1095
|
+
{
|
|
1096
|
+
type: 'text',
|
|
1097
|
+
text: 'No booking made. Original date not available.'
|
|
1098
|
+
}
|
|
1099
|
+
]
|
|
1100
|
+
};
|
|
1048
1101
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
const alternatives = await findAlternatives(
|
|
1053
|
-
restaurant,
|
|
1054
|
-
date,
|
|
1055
|
-
partySize,
|
|
1056
|
-
result.content.flexibleDates as string
|
|
1057
|
-
);
|
|
1102
|
+
|
|
1103
|
+
// Book the table
|
|
1104
|
+
await makeBooking(restaurant, date, partySize);
|
|
1058
1105
|
return {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1106
|
+
content: [
|
|
1107
|
+
{
|
|
1108
|
+
type: 'text',
|
|
1109
|
+
text: `Booked table for ${partySize} at ${restaurant} on ${date}`
|
|
1110
|
+
}
|
|
1111
|
+
]
|
|
1063
1112
|
};
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
return {
|
|
1067
|
-
content: [{
|
|
1068
|
-
type: "text",
|
|
1069
|
-
text: "No booking made. Original date not available."
|
|
1070
|
-
}]
|
|
1071
|
-
};
|
|
1072
1113
|
}
|
|
1073
|
-
|
|
1074
|
-
// Book the table
|
|
1075
|
-
await makeBooking(restaurant, date, partySize);
|
|
1076
|
-
return {
|
|
1077
|
-
content: [{
|
|
1078
|
-
type: "text",
|
|
1079
|
-
text: `Booked table for ${partySize} at ${restaurant} on ${date}`
|
|
1080
|
-
}]
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
1114
|
);
|
|
1084
1115
|
```
|
|
1085
1116
|
|
|
@@ -1087,24 +1118,24 @@ Client-side: Handle elicitation requests
|
|
|
1087
1118
|
|
|
1088
1119
|
```typescript
|
|
1089
1120
|
// This is a placeholder - implement based on your UI framework
|
|
1090
|
-
async function getInputFromUser(
|
|
1091
|
-
|
|
1092
|
-
|
|
1121
|
+
async function getInputFromUser(
|
|
1122
|
+
message: string,
|
|
1123
|
+
schema: any
|
|
1124
|
+
): Promise<{
|
|
1125
|
+
action: 'accept' | 'decline' | 'cancel';
|
|
1126
|
+
data?: Record<string, any>;
|
|
1093
1127
|
}> {
|
|
1094
|
-
|
|
1095
|
-
|
|
1128
|
+
// This should be implemented depending on the app
|
|
1129
|
+
throw new Error('getInputFromUser must be implemented for your platform');
|
|
1096
1130
|
}
|
|
1097
1131
|
|
|
1098
|
-
client.setRequestHandler(ElicitRequestSchema, async
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
action: userResponse.action,
|
|
1106
|
-
content: userResponse.action === "accept" ? userResponse.data : undefined
|
|
1107
|
-
};
|
|
1132
|
+
client.setRequestHandler(ElicitRequestSchema, async request => {
|
|
1133
|
+
const userResponse = await getInputFromUser(request.params.message, request.params.requestedSchema);
|
|
1134
|
+
|
|
1135
|
+
return {
|
|
1136
|
+
action: userResponse.action,
|
|
1137
|
+
content: userResponse.action === 'accept' ? userResponse.data : undefined
|
|
1138
|
+
};
|
|
1108
1139
|
});
|
|
1109
1140
|
```
|
|
1110
1141
|
|
|
@@ -1115,20 +1146,18 @@ client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
|
1115
1146
|
The SDK provides a high-level client interface:
|
|
1116
1147
|
|
|
1117
1148
|
```typescript
|
|
1118
|
-
import { Client } from
|
|
1119
|
-
import { StdioClientTransport } from
|
|
1149
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
1150
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
1120
1151
|
|
|
1121
1152
|
const transport = new StdioClientTransport({
|
|
1122
|
-
|
|
1123
|
-
|
|
1153
|
+
command: 'node',
|
|
1154
|
+
args: ['server.js']
|
|
1124
1155
|
});
|
|
1125
1156
|
|
|
1126
|
-
const client = new Client(
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
);
|
|
1157
|
+
const client = new Client({
|
|
1158
|
+
name: 'example-client',
|
|
1159
|
+
version: '1.0.0'
|
|
1160
|
+
});
|
|
1132
1161
|
|
|
1133
1162
|
await client.connect(transport);
|
|
1134
1163
|
|
|
@@ -1137,10 +1166,10 @@ const prompts = await client.listPrompts();
|
|
|
1137
1166
|
|
|
1138
1167
|
// Get a prompt
|
|
1139
1168
|
const prompt = await client.getPrompt({
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1169
|
+
name: 'example-prompt',
|
|
1170
|
+
arguments: {
|
|
1171
|
+
arg1: 'value'
|
|
1172
|
+
}
|
|
1144
1173
|
});
|
|
1145
1174
|
|
|
1146
1175
|
// List resources
|
|
@@ -1148,17 +1177,16 @@ const resources = await client.listResources();
|
|
|
1148
1177
|
|
|
1149
1178
|
// Read a resource
|
|
1150
1179
|
const resource = await client.readResource({
|
|
1151
|
-
|
|
1180
|
+
uri: 'file:///example.txt'
|
|
1152
1181
|
});
|
|
1153
1182
|
|
|
1154
1183
|
// Call a tool
|
|
1155
1184
|
const result = await client.callTool({
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1185
|
+
name: 'example-tool',
|
|
1186
|
+
arguments: {
|
|
1187
|
+
arg1: 'value'
|
|
1188
|
+
}
|
|
1160
1189
|
});
|
|
1161
|
-
|
|
1162
1190
|
```
|
|
1163
1191
|
|
|
1164
1192
|
### Proxy Authorization Requests Upstream
|
|
@@ -1174,31 +1202,33 @@ const app = express();
|
|
|
1174
1202
|
|
|
1175
1203
|
const proxyProvider = new ProxyOAuthServerProvider({
|
|
1176
1204
|
endpoints: {
|
|
1177
|
-
authorizationUrl:
|
|
1178
|
-
tokenUrl:
|
|
1179
|
-
revocationUrl:
|
|
1205
|
+
authorizationUrl: 'https://auth.external.com/oauth2/v1/authorize',
|
|
1206
|
+
tokenUrl: 'https://auth.external.com/oauth2/v1/token',
|
|
1207
|
+
revocationUrl: 'https://auth.external.com/oauth2/v1/revoke'
|
|
1180
1208
|
},
|
|
1181
|
-
verifyAccessToken: async
|
|
1209
|
+
verifyAccessToken: async token => {
|
|
1182
1210
|
return {
|
|
1183
1211
|
token,
|
|
1184
|
-
clientId:
|
|
1185
|
-
scopes: [
|
|
1186
|
-
}
|
|
1212
|
+
clientId: '123',
|
|
1213
|
+
scopes: ['openid', 'email', 'profile']
|
|
1214
|
+
};
|
|
1187
1215
|
},
|
|
1188
|
-
getClient: async
|
|
1216
|
+
getClient: async client_id => {
|
|
1189
1217
|
return {
|
|
1190
1218
|
client_id,
|
|
1191
|
-
redirect_uris: [
|
|
1192
|
-
}
|
|
1219
|
+
redirect_uris: ['http://localhost:3000/callback']
|
|
1220
|
+
};
|
|
1193
1221
|
}
|
|
1194
|
-
})
|
|
1195
|
-
|
|
1196
|
-
app.use(
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
app.use(
|
|
1225
|
+
mcpAuthRouter({
|
|
1226
|
+
provider: proxyProvider,
|
|
1227
|
+
issuerUrl: new URL('http://auth.external.com'),
|
|
1228
|
+
baseUrl: new URL('http://mcp.example.com'),
|
|
1229
|
+
serviceDocumentationUrl: new URL('https://docs.example.com/')
|
|
1230
|
+
})
|
|
1231
|
+
);
|
|
1202
1232
|
```
|
|
1203
1233
|
|
|
1204
1234
|
This setup allows you to:
|
|
@@ -1218,31 +1248,29 @@ Clients and servers with StreamableHttp transport can maintain [backwards compat
|
|
|
1218
1248
|
For clients that need to work with both Streamable HTTP and older SSE servers:
|
|
1219
1249
|
|
|
1220
1250
|
```typescript
|
|
1221
|
-
import { Client } from
|
|
1222
|
-
import { StreamableHTTPClientTransport } from
|
|
1223
|
-
import { SSEClientTransport } from
|
|
1224
|
-
let client: Client|undefined = undefined
|
|
1251
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
1252
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
1253
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
1254
|
+
let client: Client | undefined = undefined;
|
|
1225
1255
|
const baseUrl = new URL(url);
|
|
1226
1256
|
try {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
await client.connect(transport);
|
|
1235
|
-
console.log("Connected using Streamable HTTP transport");
|
|
1257
|
+
client = new Client({
|
|
1258
|
+
name: 'streamable-http-client',
|
|
1259
|
+
version: '1.0.0'
|
|
1260
|
+
});
|
|
1261
|
+
const transport = new StreamableHTTPClientTransport(new URL(baseUrl));
|
|
1262
|
+
await client.connect(transport);
|
|
1263
|
+
console.log('Connected using Streamable HTTP transport');
|
|
1236
1264
|
} catch (error) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1265
|
+
// If that fails with a 4xx error, try the older SSE transport
|
|
1266
|
+
console.log('Streamable HTTP connection failed, falling back to SSE transport');
|
|
1267
|
+
client = new Client({
|
|
1268
|
+
name: 'sse-client',
|
|
1269
|
+
version: '1.0.0'
|
|
1270
|
+
});
|
|
1271
|
+
const sseTransport = new SSEClientTransport(baseUrl);
|
|
1272
|
+
await client.connect(sseTransport);
|
|
1273
|
+
console.log('Connected using SSE transport');
|
|
1246
1274
|
}
|
|
1247
1275
|
```
|
|
1248
1276
|
|
|
@@ -1251,14 +1279,14 @@ try {
|
|
|
1251
1279
|
For servers that need to support both Streamable HTTP and older clients:
|
|
1252
1280
|
|
|
1253
1281
|
```typescript
|
|
1254
|
-
import express from
|
|
1255
|
-
import { McpServer } from
|
|
1256
|
-
import { StreamableHTTPServerTransport } from
|
|
1257
|
-
import { SSEServerTransport } from
|
|
1282
|
+
import express from 'express';
|
|
1283
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
1284
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
1285
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
1258
1286
|
|
|
1259
1287
|
const server = new McpServer({
|
|
1260
|
-
|
|
1261
|
-
|
|
1288
|
+
name: 'backwards-compatible-server',
|
|
1289
|
+
version: '1.0.0'
|
|
1262
1290
|
});
|
|
1263
1291
|
|
|
1264
1292
|
// ... set up server resources, tools, and prompts ...
|
|
@@ -1268,39 +1296,39 @@ app.use(express.json());
|
|
|
1268
1296
|
|
|
1269
1297
|
// Store transports for each session type
|
|
1270
1298
|
const transports = {
|
|
1271
|
-
|
|
1272
|
-
|
|
1299
|
+
streamable: {} as Record<string, StreamableHTTPServerTransport>,
|
|
1300
|
+
sse: {} as Record<string, SSEServerTransport>
|
|
1273
1301
|
};
|
|
1274
1302
|
|
|
1275
1303
|
// Modern Streamable HTTP endpoint
|
|
1276
1304
|
app.all('/mcp', async (req, res) => {
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1305
|
+
// Handle Streamable HTTP transport for modern clients
|
|
1306
|
+
// Implementation as shown in the "With Session Management" example
|
|
1307
|
+
// ...
|
|
1280
1308
|
});
|
|
1281
1309
|
|
|
1282
1310
|
// Legacy SSE endpoint for older clients
|
|
1283
1311
|
app.get('/sse', async (req, res) => {
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1312
|
+
// Create SSE transport for legacy clients
|
|
1313
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
1314
|
+
transports.sse[transport.sessionId] = transport;
|
|
1315
|
+
|
|
1316
|
+
res.on('close', () => {
|
|
1317
|
+
delete transports.sse[transport.sessionId];
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
await server.connect(transport);
|
|
1293
1321
|
});
|
|
1294
1322
|
|
|
1295
1323
|
// Legacy message endpoint for older clients
|
|
1296
1324
|
app.post('/messages', async (req, res) => {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1325
|
+
const sessionId = req.query.sessionId as string;
|
|
1326
|
+
const transport = transports.sse[sessionId];
|
|
1327
|
+
if (transport) {
|
|
1328
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
1329
|
+
} else {
|
|
1330
|
+
res.status(400).send('No transport found for sessionId');
|
|
1331
|
+
}
|
|
1304
1332
|
});
|
|
1305
1333
|
|
|
1306
1334
|
app.listen(3000);
|