-
Notifications
You must be signed in to change notification settings - Fork 8.9k
fix(everything): convert to modern TypeScript SDK APIs #3017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Convert the everything server to use the modern McpServer API instead of the low-level Server API. Key changes: - Replace Server with McpServer from @modelcontextprotocol/sdk/server/mcp.js - Convert simple tools to use registerTool() for cleaner code - Convert simple prompts to use registerPrompt() - Keep advanced features using server.server.setRequestHandler() for low-level access - Add type dependencies (@types/jszip, @types/cors) - Update all transport files to use server.server.connect/close pattern - Fix type literals to use 'as const' assertions Tools converted to registerTool(): - echo, add, printEnv, getTinyImage, annotatedMessage, getResourceLinks, structuredContent, zip Tools still using setRequestHandler() (need low-level access): - longRunningOperation (progress tokens) - sampleLLM (sampling protocol) - elicitation (elicitation protocol) - listRoots (client capabilities) - getResourceReference (strict resource content typing) All features maintained: - 13 tools including progress notifications, sampling, elicitation - 3 prompts (simple, complex, resource) - 100 paginated resources with templates - Resource subscriptions - Completions for prompts and resources - Roots protocol support - Logging at various levels - All three transports (stdio, SSE, streamable HTTP) The modern API provides: - Less boilerplate code where applicable - Better type safety with Zod - More declarative registration - Cleaner, more maintainable code
- Convert remaining tools to use registerTool() instead of setRequestHandler - Use server.sendLoggingMessage() instead of server.server.sendLoggingMessage() - Use server.connect/close instead of server.server.connect/close in transports - Use transport.onclose instead of server.server.onclose - Tools can still access extra parameter for low-level features (sendRequest, requestId, progressToken)
- Remove manual resource request handlers - Use ResourceTemplate for dynamic resources - Remove pagination logic (not needed with templates) - Keep subscribe/unsubscribe as setRequestHandler (no modern API alternative) - Properly handle both text and blob resource types
Resources are now registered individually so they appear in
resources/list responses, while also keeping the template for
dynamic access via test://static/resource/{id}.
|
This was largely vibed by Claude, but I have checked the changes and it looks good. The tests are also all still passing. I also went through manually to do a bit of code cleanup, especially getting the TypeScript types correct (they were wrong in the old version, which confused poor Claude a little). Sadly we still need to use the old style APIs for a couple things, but I think this is unavoidable as they don't have equivalents on the new APIs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment was marked as outdated.
This comment was marked as outdated.
|
I'm sorry I'm being silly 🤦 I had forgotten to push changes that fixed this. Pushed now and should be working for real now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few broken bits.
| { | ||
| capabilities: { | ||
| prompts: {}, | ||
| resources: { subscribe: true }, | ||
| tools: {}, | ||
| logging: {}, | ||
| completions: {} | ||
| }, | ||
| instructions | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| { | |
| capabilities: { | |
| prompts: {}, | |
| resources: { subscribe: true }, | |
| tools: {}, | |
| logging: {}, | |
| completions: {} | |
| }, | |
| instructions | |
| } | |
| { | |
| capabilities: { | |
| prompts: {}, | |
| resources: { subscribe: true }, | |
| tools: {}, | |
| logging: {}, | |
| completions: {} | |
| }, | |
| instructions | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Logging capability wasn't detected, so the Inspector didn't send a
logging/setLevelrequest, and didn't display the dropdown for log level. - Resources / subscribe capability wasn't detected, so subscribe button did not display over selected resource, so subscribe/unsubscribe and updated notifications couldn't be sent.
| console.error("Starting logs update interval"); | ||
| logsUpdateInterval = setInterval(async () => { | ||
| await server.sendLoggingMessage( messages[Math.floor(Math.random() * messages.length)], sessionId); | ||
| await server.sendLoggingMessage(messages[Math.floor(Math.random() * messages.length)]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| await server.sendLoggingMessage(messages[Math.floor(Math.random() * messages.length)]); | |
| await server.sendLoggingMessage(messages[Math.floor(Math.random() * messages.length)], sessionId); |
The sessionId argument was removed from every call to sendLoggingMessage.
This is the mechanism that allows multiple clients to have different logging levels set and respected.
You wouldn't have been able to test with the current PR, because the logging capability was removed, but after you put that back in, try opening multiple Inspector instances connected to the same Everything server. Only the latest requested level by any of them will be respected. Whereas if you put the sessionId params back in the sendLoggingMessage calls, you'll see that each instance can request its own level and messages above that level will not be sent to it.
| logger: "everything-server", | ||
| data: `Roots updated: ${currentRoots.length} root(s) received from client`, | ||
| }, sessionId); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | |
| }, sessionId); |
| logger: "everything-server", | ||
| data: `Failed to request roots from client: ${error instanceof Error ? error.message : String(error)}`, | ||
| }, sessionId); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | |
| }, sessionId); |
| logger: "everything-server", | ||
| data: `Initial roots received: ${currentRoots.length} root(s) from client`, | ||
| }, sessionId); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | |
| }, sessionId); |
| logger: "everything-server", | ||
| data: "Client returned no roots set", | ||
| }, sessionId); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | |
| }, sessionId); |
| logger: "everything-server", | ||
| data: `Failed to request initial roots from client: ${error instanceof Error ? error.message : String(error)}`, | ||
| }, sessionId); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | |
| }, sessionId); |
| logger: "everything-server", | ||
| data: "Client does not support MCP roots protocol", | ||
| }, sessionId); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }); | |
| }, sessionId); |
| // Resource prompt must use setRequestHandler for now due to type compatibility issues | ||
| server.server.setRequestHandler(GetPromptRequestSchema, async (request) => { | ||
| const { name, arguments: args } = request.params; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (name === PromptName.SIMPLE) { | |
| return { | |
| messages: [ | |
| { | |
| role: "user", | |
| content: { | |
| type: "text", | |
| text: "This is a simple prompt without arguments.", | |
| }, | |
| }, | |
| ], | |
| }; | |
| } | |
| if (name === PromptName.COMPLEX) { | |
| return { | |
| messages: [ | |
| { | |
| role: "user", | |
| content: { | |
| type: "text", | |
| text: `This is a complex prompt with arguments: temperature=${args?.temperature}, style=${args?.style}`, | |
| }, | |
| }, | |
| { | |
| role: "assistant", | |
| content: { | |
| type: "text", | |
| text: "I understand. You've provided a complex prompt with temperature and style arguments. How would you like me to proceed?", | |
| }, | |
| }, | |
| { | |
| role: "user", | |
| content: { | |
| type: "image", | |
| data: MCP_TINY_IMAGE, | |
| mimeType: "image/png", | |
| }, | |
| }, | |
| ], | |
| }; | |
| } |
This section shouldn't have been removed. The simple and complex prompts can be listed, but if you select one and click Get Prompt it fails:

Convert the everything server to use the modern McpServer API instead of the low-level Server API.
Key changes
ServerwithMcpServerfrom@modelcontextprotocol/sdk/server/mcp.jsregisterTool()for cleaner coderegisterPrompt()server.server.setRequestHandler()for low-level access@types/jszip,@types/cors)server.server.connect/closepatternas constassertionsTools converted to registerTool()
These tools now use the modern declarative API:
echo,add,printEnv,getTinyImage,annotatedMessage,getResourceLinks,structuredContent,zipTools still using setRequestHandler()
These tools need low-level access to request metadata:
longRunningOperation- needs access to progressToken and requestIdsampleLLM- uses sampling protocol (sendRequest)elicitation- uses elicitation protocol (sendRequest)listRoots- needs client capabilities checkgetResourceReference- uses strict resource content typingAll features maintained
Benefits
The modern API provides:
Testing