mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-28 03:39:41 +02:00
Demonstrate registration of tools conditioned upon client capability support. Also, obviated need for clientConnected callback to pass sessionId because we defer initial fetching of roots happens when you run the get-roots-list tool.
* In how-it-works.md, - added a section on conditional tool registration * In server/index.ts - import registerConditionalTools - in an oninitialized handler for the server, call registerConditionalTools - removed clientConnected from ServerFactoryResponse and all mentions in docs * In tools/index.ts - export a registerConditionalTools function - refactor/move calls to registerGetRootsListTool, registerTriggerElicitationRequestTool, and registerTriggerSamplingRequestTool out of registerTools and into registerConditionalTools * In server/roots.ts - only act if client supports roots - remove setInterval from call to requestRoots. It isn't happening during the initialze handshake anymore, so it doesn't interfere with that process if called immediaately * In get-roots-list.ts, trigger-elicitation-request.ts, and trigger-sampling-request.ts, - only register tool if client supports capability * Throughout the rest of the files, removing all references to `clientConnected`
This commit is contained in:
@@ -7,9 +7,19 @@
|
||||
| [Extension Points](extension.md)
|
||||
| How It Works**
|
||||
|
||||
## Resource Subscriptions
|
||||
# Conditional Tool Registration
|
||||
|
||||
Each client manages its own resource subscriptions and receives notifications only for the URIs it subscribed to, independent of other clients.
|
||||
### Module: `server/index.ts`
|
||||
|
||||
- Some tools require client support for the capability they demonstrate. These are:
|
||||
- `get-roots-list`
|
||||
- `trigger-elicitation-request`
|
||||
- `trigger-sampling-request`
|
||||
- Client capabilities aren't known until after initilization handshake is complete.
|
||||
- Most tools are registered immediately during the Server Factory execution, prior to client connection.
|
||||
- To defer registration of these commands until client capabilities are known, a `registerConditionalTools(server)` function is invoked from an `onintitialized` handler.
|
||||
|
||||
## Resource Subscriptions
|
||||
|
||||
### Module: `resources/subscriptions.ts`
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
- Creates a server instance using `createServer()` from `server/index.ts`
|
||||
- Connects it to the chosen transport type from the MCP SDK.
|
||||
- Calls the `clientConnected()` callback upon transport connection.
|
||||
- Handles communication according to the MCP specs for the chosen transport.
|
||||
- **STDIO**:
|
||||
- One simple, process‑bound connection.
|
||||
@@ -72,43 +71,3 @@
|
||||
|
||||
Some of the transport managers defined in the `transports` folder can support multiple clients.
|
||||
In order to do so, they must map certain data to a session identifier.
|
||||
|
||||
### About the `clientConnected` callback returned by the Server Factory
|
||||
|
||||
Some server functions require a `sessionId` but can't reach it via its scope.
|
||||
For instance, the automatic log-level handling in the Typescript SDK tracks
|
||||
the client's requested logging level by `sessionId`. In order
|
||||
|
||||
So, the Server Factory provides a callback to allow the chosen Transport Manager
|
||||
to provide the server with the `sessionId` (or `undefined`) for each new connection.
|
||||
|
||||
### On `clientConnected` vs `server.oninitialized` for post-connection setup
|
||||
|
||||
#### Q:
|
||||
|
||||
> Why not hook `server.server.oninitialized` to trigger post-connection setup?
|
||||
> You could call `syncRoots` in a handler, obviating the `clientConnected` hook.
|
||||
|
||||
#### A:
|
||||
|
||||
In `oninitialized`, a transport is connected, but there is no way to access it
|
||||
or its `sessionId`. Therefore, calling any function that needs a `sessionId` is
|
||||
right out.
|
||||
|
||||
#### Q:
|
||||
|
||||
> Why is it important to have access to the `sessionId` anywhere but in a request
|
||||
> handler?
|
||||
|
||||
### A:
|
||||
|
||||
When setting up a server that tracks any data per session, you need to map
|
||||
that data to a `sessionId`. See `logging.ts` and `subscriptions.ts` for examples.
|
||||
|
||||
In an STDIO server, it doesn't matter because there is one client per server.
|
||||
Features that track data by `sessionId` can accept `undefined` for that value
|
||||
and still track session-scoped data for STDIO clients.
|
||||
|
||||
But with HTTP protocols, you can have multiple clients. So you have to track
|
||||
their logging intervals, resource subscriptions, and other session-scoped
|
||||
data per client.
|
||||
|
||||
@@ -168,7 +168,6 @@ src/everything
|
||||
|
||||
- `stdio.ts`
|
||||
- Starts a `StdioServerTransport`, created the server via `createServer()`, and connects it.
|
||||
- Calls `clientConnected()` to inform the server of the connection.
|
||||
- Handles `SIGINT` to close cleanly and calls `cleanup()` to remove any live intervals.
|
||||
- `sse.ts`
|
||||
- Express server exposing:
|
||||
@@ -176,10 +175,8 @@ src/everything
|
||||
- `POST /message` for client messages.
|
||||
- Manages multiple connected clients via a transport map.
|
||||
- Starts an `SSEServerTransport`, created the server via `createServer()`, and connects it to a new transport.
|
||||
- Calls `clientConnected(sessionId)` to inform the server of the connection.
|
||||
- On server disconnect, calls `cleanup()` to remove any live intervals.
|
||||
- `streamableHttp.ts`
|
||||
- Express server exposing a single `/mcp` endpoint for POST (JSON‑RPC), GET (SSE stream), and DELETE (session termination) using `StreamableHTTPServerTransport`.
|
||||
- Uses an `InMemoryEventStore` for resumable sessions and tracks transports by `sessionId`.
|
||||
- Connects a fresh server instance on initialization POST and reuses the transport for subsequent requests.
|
||||
- Calls `clientConnected(sessionId)` to inform the server of the connection.
|
||||
|
||||
Reference in New Issue
Block a user