-
Notifications
You must be signed in to change notification settings - Fork 9.1k
New everything server #3121
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
New everything server #3121
Conversation
…ed APIs
In src/everything:
* Refactor / move streamableHttp.ts, sse.ts, and stdio.ts to transports/
* Move everything.ts to server/ for reference
* Add server/index.js
- exports the createServer function
- import registerTools from tools/index.js
- in createServer()
- read instructions.md and include in ServerOptions for McpServer constructor
- construct McpServer instead of Server
- call registerTools, passing server
* Add tools/echo.ts
- define EchoSchema
- define tool config
- export addToolEcho function
- in addToolEcho()
- register handler for Echo tool
* Add tools/index.ts
- import addToolEcho
- export registerTools function
- in registerTools()
- call addToolEcho
…ed APIs.
* Adding dynamic resources
* Add server/index.js
- import registerResources from resources/index.js
- in createServer()
- call registerResources, passing server
* Add resources/dynamic.ts
- in addDynamicResources()
- define formatGmtTimestamp to create a time stamp to include in the resource text or encoded blob
- define parseIndex to ensure the index variable of the URI is a number
* Add resources/index.ts
- import addDynamicResources
- export registerResources function
- in registerResources()
- call addDynamicResources
* In package.json
- update the start commands to launch each of the transports properly
…ed APIs.
* Adding static resources, move server instructions to
the new docs folder, and add code formatting
* Add docs folder
* Add docs/architecture.md which describes the architecture of the project thus far.
* Refactor moved instructions.md to docs/server-instructions.md
* Add resources/static.ts
- in addStaticResources()
- read the file entries from the docs folder
- register each file as a resource (no template), with a readResource function that reads the file and returns it in a contents block with the appropriate mime type and contents
- getMimeType helper function gets the mime type for a filename
- readSafe helper function reads the file synchronously as utf-8 or returns an error string
* Add resources/index.ts
- import addStaticResources
- export registerResources function
- in registerResources()
- call addStaticResources
* In package.json
- add prettier devDependency
- add prettier:check script
- add prettier:fix script
- in build script, copy docs folder to dist
* All other changes were prettier formatting
…ed APIs.
* Adding prompts (simple, complex, with completions)
* Add prompts/simple.ts
- in addSimplePrompt()
- register a simple prompt with no arguments
* Add prompts/complex.ts
- in addComplexPrompt()
- define promptArgsSchema containing a required city arg of type string and an optional state arg of type state
- register the complex prompt with a prompt callback that combines the city and state into a prompt asking for the weather in that location
* Add prompts/completions.ts
- in addPromptWithCompletions()
- define promptArgsSchema containing department and name string fields with completion handlers
- register the completable prompt with a prompt callback that combines the inputs into a prompt asking to promote the selected name to head of the selected department
* Add prompts/index.ts
- import addSimplePrompt, addComplexPrompt, and addPromptWithCompletions
- export registerPrompts function
- in registerPrompts()
- call addSimplePrompt
- call addComplexPrompt
- call addPromptWithCompletions
* In package.json
- add prettier devDependency
- add prettier:check script
- add prettier:fix script
- in build script, copy docs folder to dist
* All other changes were prettier formatting
…ed APIs. * Updated architecture.md
…ed APIs. * Updated architecture.md * Refactor / renamed all addXPrompt, addXTool, and addXResource functions to registerX... * Added the add tool
…ed APIs. * Updated architecture.md * Refactor / renamed uris from test://X/resource/Y to demo://resource/X/Y
…ed APIs.
* Updated architecture.md
* Refactor/renamed resources/dynamic.ts to resources/template.ts
- refactor/renamed registerDynamicResources to registerResourceTemplates
- this highlights the more salient fact that we are demonstrating registration of resource templates in this example.
- exposed the ability to dynamically create the text resources from elsewhere (namely the resource-prompt example
* Added prompts/resource.ts
- in registerEmbeddedResourcePrompt()
- register a prompt that takes a resourceId and returns the prompt with the corresponding dynamically created resource embedded
…ed APIs. * Ran prettier
…ed APIs. * Updated architecture.md * Refactor/renamed static.ts to file.ts * Refactor/renamed complex.ts to args.ts * Refactor/renamed template.ts to templates.ts. * In resource.ts, - improved registerEmbeddedResourcePrompt to allow selection of blob or text resource type. * In file.ts - refactor/renamed registerStaticResources to registerFileResources to highlight the fact that it is using files as resources. * In args.ts - refactor/renamed registerComplexPrompt to registerArgumentsPrompt to highlight the fact that it is demonstrating prompt arguments. * Updated inline documentation throughout
…ed APIs.
Adding resource subscriptions:
* Updated architecture.md
* In server/index.ts
- imported Transport, setSubscriptionHandlers,beginSimulatedResourceUpdates, and stopSimulatedResourceUpdates
- call setSubscriptionHandlers passing server
- in returned object,
- refactor/renamed startNotificationIntervals placehodler to clientConnected, which takes a transport argument and calls beginSimulatedResourceUpdates, passing the transport
- replaced cleanup placeholder with a function that takes an optional sessionId and calls stopSimulatedResourceUpdates, passing the sessionId
* In sse.ts, stdio.ts, and streamableHttp.ts
- when transport is connected, called clientConnect, passing transport
- when disconnecting, called cleanup, passing sessionId
* Added subscriptions.ts
- tracks subscriber session id lists by URI
- tracks transport by session id
- tracks subscription update intervals by sessionId
- in setSubscriptionHandlers
- set request handlers for SubscribeRequestSchema and UnsubscribeRequestSchema
- in beginSimulatedResourceUpdates
- starts an interval to send updates to the transport for all subscribed resources
- in stopSimulatedResourceUpdates
- removes intervals and transport for gien session id
…ed APIs. * In subscriptions.ts - updated inline doc
…ed APIs.
Adding simulated logging and refactoring subscriptions to not need to track transports
* Updated architecture.md
* In server/index.ts
- remove import of Transport
- import beginSimulatedLogging and stopSimulatedLogging
- in clientConnected()
- change argument to sessionId? instead of transport
- add call to beginSimulatedLogging
- send server and sessionId to beginSimulatedResourceUpdates and beginSimulatedLogging
- in cleanup()
- add call to stopSimulatedLogging passing sessionId
* Added server/logging.ts
- Initialize logsUpdateIntervals to Map session ID to the interval for sending logging messages to the client
- in beginSimulatedLogging()
- create an array of logging meesages, customized with the sessionId if present
- if the interval for the sessionId hasn't been set, create one, calling server.sendLoggingMessage with a random message to the client each time the interval elapses
- in stopSimulatedLogging()
- if a logging interval exists for the sessionId, clear it and remove it
* In subscriptions.ts
- remove import of Transport
- remove transports map
- in beginSimulatedResourceUpdates()
- change arguments to server and sessionId
- check for the subsUpdateInterval for the session
- remove all transport storage and interaction
- instead use the server to send the notification
- in stopSimulatedResourceUpdates()
- remove management of transports map
* In sse.ts and streamableHttp.ts
- when calling clientConnected, pass sessionId instead of transport
* In stdio.ts,
- when calling clientConnected, pass nothing instead of transport
* In subscriptions.ts
- updated inline doc
…ed APIs.
Added tools to toggle simulated logging and resource updates on and off rather than have them start immediately upon connection
* Updated architecture.md
* In server/index.ts
- remove import of beginSimulatedResourceUpdates and beginSimulatedLogging
- remove clientConnected from createServer factory result
* In tools/index.ts
- import registerToggleLoggingTool and registerToggleSubscriberUpdatesTool
- in registerTools
- call registerToggleLoggingTool and registerToggleSubscriberUpdatesTool
* In logging.ts
- in beginSimulatedLogging
- refactor extract inline interval callback into function sendSimulatedLoggingMessage
- call sendSimulatedLoggingMessage right away to send the first message
- supply sendSimulatedLoggingMessage as interval callback
* In subscriptions.ts
- remove import of Transport
- remove transports map
- in beginSimulatedResourceUpdates()
- change arguments to server and sessionId
- check for the subsUpdateInterval for the session
- remove all transport storage and interaction
- instead use the server to send the notification
- in stopSimulatedResourceUpdates()
- remove management of transports map
* In stdio.ts, sse.ts, and streamableHttp.ts
- remove destructure and calling of clientConnected
* Added tools/toggle-logging.ts
- registers a tool that
- takes no arguments
- tracks clients that have been enabled by session id in a set
- if client isn't enabled,
- calls beginSimulatedLogging
- adds session id to client set
- else
- calls stopSimulatedLogging
- deletes session id from client set
- returns a message explaining what was done including what to expect when logging is enabled
* Added tools/toggle-subscriber-updates.ts
- registers a tool that
- takes no arguments
- tracks clients that have been enabled by session id in a set
- if client isn't enabled,
- calls beginSimulatedResourceUpdates
- adds session id to client set
- else
- calls stopSimulatedResourceUpdates
- deletes session id from client set
- returns a message explaining what was done including what to expect when logging is enabled
…ed APIs.
Added long-running-operation tool and improved comments in all tools
* Updated architecture.md
* In tools/
- add.ts
- echo.ts
- toggle-logging.ts
- toggle-subscriber-updates.ts
- Add better function and inline docs
* Added tools/long-running-operation.ts
- similar implementation as in everything v1
* In tools/index.ts
- import registerLongRunningOperationTool
- in registerTools
- registerLongRunningOperationTool
…ed APIs.
Added print-env, and sampling-request tools
* Updated architecture.md
* In tools/index.ts
- import registerPrintEnvTool and registerSamplingRequestTool
- in registerTools
- call registerPrintEnvTool and registerSamplingRequestTool
* Added tools/print-env.ts
- registers a tool that takes no args and returns the environment variables
* Added tools/sampling-request
- registers a tool that
- takes prompt and maxTokens args
- sends client a sampling request
- returns the client response in the result
…ed APIs. * Updated architecture.md
…ed APIs. * In sampling-request.ts - renamed SampleLLMSchema to SamplingRequestSchema
…ed APIs. * Added git-tiny-image.ts - registers a tool that returns a tiny MCP logo * In all other tools, updated function docs
…ed APIs. Adding the annotated message tool * Updated architecture.md * Added annotated-message.ts - registers a tool that returns a message with annotations on different content types * In package.json - updated TS SDK version
…ed APIs. Adding the annotated message tool * Updated architecture.md * In annotated-message.ts - prettier
…ed APIs.
Adding the get-resource-reference tool
* Updated architecture.md
* In prompts/resource.ts
- Refactor/extracted the prompt argument completers into exported functions in resources/templates.ts
- Refactor/extracted BLOB_TYPE, TEXT_TYPE, and resourceTypes into exported constants in resources/templates.ts as RESOURCE_TYPE_BLOB, RESOURCE_TYPE_TEXT, and RESOURCE_TYPES
- In resources/templates.ts
- refactor renamed index to resourceId throughout for consistency with prompts and tool references
* Added tools/get-resource-reference.ts
- Registers the 'get-resource-reference' tool with the provided McpServer instance.
- uses enum and number schema for tools to provide resourceType and resourceId arguments. Completables don't work for tool arguments.
- Returns the corresponding dynamic resource
* In tools/index.ts
- imported registerGetResourceReferenceTool
- in registerTools
- called registerGetResourceReferenceTool
…ed APIs.
Adding the get-resource-reference and get-resource-reference tools
* Updated architecture.md
* Added get-resource-links.ts
- Registers the 'get-resource-reference' tool with the provided McpServer instance.
- The registered tool retrieves a specified number of resource links and their metadata.
* In get-resource-reference.ts
- fixed tool description
* In tools/index.ts
- import registerGetResourceLinksTool
- in registerTool
- call registerGetResourceLinksTool
…ed APIs. Refactor/rename `print-env` tool to `get-env` * Updated architecture.md * Refactor rename print-env.ts to get-env.ts * In tools/index.ts - reorder tools alphabetically
…ed APIs. * In get-env.ts - update tool description
…ed APIs. * Updated architecture.md
…ed APIs.
Adding get-structured-content tool
* Updated architecture.md
* added get-structured-content.ts
- Registers the 'get-structured-content' tool with the provided McpServer instance.
- The registered tool processes incoming arguments using a predefined input schema,
generates structured content with weather information including temperature,
conditions, and humidity, and returns both backward-compatible content blocks
and structured content in the response.
…ed APIs. For tools where we seek to get some response from the server, renamed as an action, e.g., "Get Sum" rather than "Add" or "Get Annotated Message" rather than "Annotated Message", so that it's clear what the intent of the tool is in a quick review. * Updated architecture.md * Refactor/renamed add.ts to get-sum.ts * Refactor/renamed annotated-message.ts to get-annotated-message.ts * In tools/index.ts - sorted presentation order
…ed APIs. * Updated architecture.md
…ed APIs. * Updated architecture.md * Refactor/renamed sampling-request.ts to get-sampling-request.ts * In tools/index.ts - sorted presenation order
|
This discussion question made me think we could add something like this, to demonstrate a collection of resources? |
…igger-sampling-request.ts
…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`
* 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.
@olaservo This sounds good. I also want to add icons to everything that supports them. Maybe we can do this as separate PRs? UPDATE: I added this issue for the resources collection example and assigned to both of us, just so it doesn't get lost in the wash. |
…but I'm putting it back.
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
…but I'm putting it back. * In server/roots.ts - Updated function doc
|
|
||
| // Register the prompt | ||
| server.registerPrompt( | ||
| "args-prompt", |
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.
nit: The convention I've generally seen for prompts and tool names is snake_case - is there a reason we're using kebab case? Seems good to follow conventions where we can in case agents use this to see how to implement particular features etc. and pick up on patterns.
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.
The snake_case convention comes mainly from Python server development, where it matches Python idioms. The point of deviating here is that it illustrates that snake is a convention, but tool names can be whatever you want them to be within the allowed parameters.
If you go by convention on the hopes of appeasing models that somehow respond to snake_case better, you to cede the DevX benefit entirely to Python. You reinforce a trend that's already self-abetted by LLMs spitting out Python by default when someone asks for an MCP server. If you mix case in your app, with kebab-case files and snake_case primitive names, it will inevitably lead to a mix of cases in your filesystem where either case is admitted.
A kebab-case primitive can line up with thev TypeScript file name that registers it. All the benefit of having a single case pattern for naming at the runtime and filesystem levels, just with TS friendly idioms.
It's a design choice one can make. Making it, particularly in this instructive resource, could end up helping LLMs not to overfit on Python idioms.
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.
I recall looking at evals showing snake_case and PEP 8 style guidelines performed better. Prompts aren't a model-facing capability, so it doesn't make too much of a difference here.
But you're right that it's a self-reinforcing convention. I sadly think we've passed the point where it's difficult to fight (the C# MCP SDK converts from PascalCase to snake_case when using reflection-based convenience, last I checked, unless overriden, for this very reason). Models simply do better with Python-style tools names.
I am generally in favor of using idiomatic names for the language a server is written, however, and then doing any mapping in a library or even on the client side. Because some models might do better with kebab-case. A model which is using code mode using TypeScript might prefer kebab-case.
domdomegg
left a comment
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.
Generally LGTM, very excited about this! Thank you :)
Co-authored-by: adam jones <[email protected]>
olaservo
left a comment
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 looks good to me - I think we can merge this and start adding the other couple of examples we've mentioned.
I also did one more claude-driven Playwright test run for all the features for each transport using inspector v 0.18.0, along with manual testing for stdio and didn't run into any errors.
Description
everything.tsinto a more modular and maintainable structure.Key Differences
Feature Naming
kebab-caseas are files.Logging
Resources
docs/folder, and demonstrates exposing actual static filesystem resources.gzip-file-as-resourcetool demonstrates it. A file is compressed by the tool and then added as a session resource, which is available for the duration of the session and is listed along with the other documents when you list resources. Its URI sets it apart from the static resources.Prompts
complex_promptis now calledargs-promptbecause it demonstrates prompts with arguments.completable-promptdemonstrates completable arguments where the selection of the first arg narrows the completions for the second arg.resource-promptnow takes aresourceTypeargument (Text or Blob).Tools
get-, e.g.,get-annotated-message.get-sumbutechoremains as is. These two tools demonstrate the same thing: a tool with an input schema that has a different response based on the input. For this reason, I believe we just should keep one, but they both exist for now.ziptool is nowgzip-file-as-resourceand incorporates changes in this PR by @ochafiktools is nowget-structured-content` and provides an enum of city names, returning a different content block for each city.Roots
roots/list_changedwas set and so when you added some via the Inspector, it would respond with aroots/listrequest.notification/initializedhandler was done, otherwise, the request gets lost; the client never gets it.get-roots-listtool, unless for whatever reason, the list isn't present when the tool is run, in which case the list is requested.Documentation
README.mdis minimal and focused on how to launch and use with agents. It contains links to all the other pages indocs/.CLAUDE.mdis nowAGENTS.mddocs/describe Architecture, Project Structure, The Startup Process, Server Features, Extension Points, and How It Works.NPM Scripts
dist/index.jsrather than pointing to transport manager directly.start:stdio👉node dist/index.js stdiostart:sse👉node dist/index.js ssestart:streamableHttp👉node dist/index.jsprettierprettier:fixprettier:checkServer Details
Motivation and Context
Use new APIs
The existing implementation of the Everything server uses the low-level
Serverclass rather thanMcpServerwhich does a lot of heavy lifting. As a result, the code is more complicated than it should be. Since this is meant to be an educational resource, a model for how to build servers with the TypeScript SDK, it should use the latest features wherever possible.Make it easier to extend
When adding a new feature, such as some of the things we need to add in order to demonstrate the new spec additions, you had to edit multiple places inside the monolithic
everything.tsfile. Add an input schema in one place, a tool description in another, and the tool request handler in a third. Now all of that goes in a single file and an orchestrator registers the tool (or resource, or prompt). Adding other behaviors like simulated logging and resource subscription updates was tough because there was no pattern to follow. Now it is easy to do with those examples in place to follow.Add more inline and high-level documentation
Since this server is intended to exercise all of the MCP features, having all the code in a single, monolithic file made it hard to understand and reason about. Further, because of the size of the file, inline documentation was dissuaded as it only made scrolling to a feature area a longer journey. Breaking all the features out into separate files allowed for much better function docs and inline comments. And the system itself is now more fully documented in a set of markdown files, which the server exposes as resources (and the
README.mdlinks to).How Has This Been Tested?
Locally with the Inspector on the branch for support of the new spec's Elicitation Enum shapes modelcontextprotocol/inspector#952. This feature will be in 0.18.0.
Screenshots
Most of the tools, prompts, and resources operate in the same fashion as previously. Here are some screenshots showing new behavior.
Initial
roots/listrequest now being sent and list received.Resources now include static files and templates for dynamic text and blobs.
New
completable-promptdemonstrates two stage argument filteringNew
toggle-subscriber-updatestoolNew
toggle-simulated-loggingtoolRenamed
gzip-file-as-resourcetool creates session resourceBreaking Changes
Yes. Tools and Resources are named differently, but the way that you launch it has not changed, so it shouldn't actually affect anyone.
Types of changes
Checklist
Additional context
This fixes #3080