The upstream main added simulate-research-query and async tools that
use server.experimental.tasks.registerToolTask. Update mock servers
to include this API.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Removed ~29 tests that were adding noise rather than coverage:
- Registration boilerplate tests (16): redundant with registrations.test.ts
- Redundant role/type checks (3): consolidated into behavioral tests
- "Should not throw" tests (6): consolidated into single lifecycle test
- Constant identity tests (2): provided no safety net
- expect(true).toBe(true) test (1): replaced with actual assertion
- Weak capability test (1): removed, handler check already exists
Strengthened remaining tests:
- Resource templates test now verifies specific resource names
- File resources test now asserts registerResource was called
Test count: 124 → 95 (29 removed)
Coverage unchanged at ~71%
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a tool like `gzip-file-as-resource` is called multiple times with the
same output name (especially the default `README.md.gz`), the server would
throw "Resource already registered" because the SDK doesn't allow
registering duplicate URIs.
This fix:
- Tracks registered resources by URI in a module-level Map
- Before registering a new resource, checks if the URI already exists
- If it does, removes the old resource using the SDK's `remove()` method
- Then registers the new resource with fresh content
This allows tools to be called repeatedly with the same parameters without
errors, which is important for LLM agents that may retry tool calls.
Found using Bellwether (https://bellwether.sh), an MCP server validation tool.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
"Everything Server crashes when multiple clients reconnect"
* In index.ts
- added a variable to hold the initialize timeout
- store the timeout in the oninitialized handler
- clear the timeout in the cleanup callback
* In roots.ts
- In the catch block of syncRoots, log the error to the console via .error rather than attempting to send to the client because the most probable case here is that we don't have a connection.
"Everything Server crashes when multiple clients reconnect"
* In index.ts
- added a variable to hold the initialize timeout
- store the timeout in the oninitialized handler
- clear the timeout in the cleanup callback
* In roots.ts
- In the catch block of syncRoots, log the error to the console via .error rather than attempting to send to the client because the most probable case here is that we don't have a connection.
* In simulate-research-query.ts
- remove redundant local variable in getTask
* Everywhere else, prettier.
- Use params.task instead of params._meta.task for task metadata
- Remove pollInterval from task requests (only available on result)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement graceful degradation for elicitation on HTTP transport:
- STDIO: Full elicitation works via sendRequest
- HTTP: Catches elicitation failure, uses default interpretation
- Task completes successfully on both transports
simulate-research-query now uses try-catch around sendRequest and
includes explanatory message when elicitation is skipped on HTTP.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update documentation to reflect that simulate-research-query now sends
elicitation requests directly from the background task instead of using
the tasks/result side-channel approach.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of waiting for the client to call tasks/result to trigger
elicitation, the server now sends elicitation/create directly from
the background process using sendRequest. This simplifies the flow:
- Server sends elicitation proactively when clarification is needed
- Client receives and handles it via existing elicitation handler
- Task resumes and completes after receiving the response
- Client's polling sees completed status
This approach avoids requiring the client to detect input_required
status and call tasks/result as a side-channel.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tools that demonstrate bidirectional MCP tasks where the server
sends requests to the client for async execution:
- trigger-sampling-request-async: Send sampling request with task
params, client creates task and executes LLM call in background,
server polls for completion and retrieves result
- trigger-elicitation-request-async: Same pattern for user input,
useful when user may take time to fill out forms
Both tools:
- Check client capabilities (tasks.requests.sampling/elicitation)
- Accept both CreateTaskResult and direct result responses
- Poll tasks/get for status updates
- Fetch final result via tasks/result
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tasks capability with list, cancel, and requests.tools.call
- Add InMemoryTaskStore and InMemoryTaskMessageQueue from SDK experimental
- Add simulate-research-query tool demonstrating task lifecycle
- Task demonstrates working -> input_required -> completed status flow
- Uses elicitation for ambiguous queries when client supports it
Closes#3037🦉 Generated with [Claude Code](https://claude.ai/code)
The syncRoots call should be idempotent, requesting roots if they haven't been yet for the session, but always retuning the cached roots otherwise. That could be deferred but setting the handler for roots_list changed note should not.
* In server/roots.ts
- only set the notification handler and call for initial roots list if the roots aren't already cached for this client.
* In server/index.ts
- in the oninitialized handler
- get the sessionId from the transport
- set a 350ms timeout to call syncRoots with the server and sessionId
- this delay cause it to run after the `notifications/initialized` handler finishes, otherwise, the request gets lost.
* All other changes attributable to prettier
* In server/roots.ts
- in syncRoots, if roots are supported fetch them
* In get-roots-list.ts,
- Don't import and inspect current roots map, just call syncRoots to get the list.
* 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`
* In get-roots-list.ts, query the server's cache of the latest roots from the client and only request the list from the client if it doesn't exist.
* In roots.ts, export the roots map
* In get-roots-list.ts, query the server's cache of the latest roots from the client and only request the list from the client if it doesn't exist.
* In roots.ts, export the roots map