Skip to content

Conversation

@cliffhall
Copy link
Member

@cliffhall cliffhall commented Dec 13, 2025

Description

  • Refactor/extracted all features from everything.ts into a more modular and maintainable structure.
src/everything
     ├── index.ts
     ├── AGENTS.md
     ├── Dockerfile
     ├── package.json
     ├── tsconfig.json
     ├── docs
     │   ├── architecture.md
     │   ├── extension.md
     │   ├── features.md
     │   ├── how-it-works.md
     │   ├── instructions.md
     │   ├── startup.md
     │   └── structure.md
     ├── prompts
     │   ├── index.ts
     │   ├── args.ts
     │   ├── completions.ts
     │   ├── simple.ts
     │   └── resource.ts
     ├── resources
     │   ├── index.ts
     │   ├── files.ts
     │   ├── session.ts
     │   ├── subscriptions.ts
     │   └── templates.ts
     ├── server
     │   ├── index.ts
     │   ├── logging.ts
     │   └── roots.ts
     ├── tools
     │   ├── index.ts
     │   ├── echo.ts
     │   ├── get-annotated-message.ts
     │   ├── get-env.ts
     │   ├── get-resource-links.ts
     │   ├── get-resource-reference.ts
     │   ├── get-roots-list.ts
     │   ├── get-structured-content.ts
     │   ├── get-sum.ts
     │   ├── get-tiny-image.ts
     │   ├── gzip-file-as-resource.ts
     │   ├── toggle-logging.ts
     │   ├── toggle-subscriber-updates.ts
     │   ├── trigger-elicitation-request.ts
     │   ├── trigger-long-running-operation.ts
     │   └── trigger-sampling-request.ts
     └── transports
         ├── sse.ts
         ├── stdio.ts
         └── streamableHttp.ts

Key Differences

Feature Naming

  • Naming of tools and prompts are now in kebab-case as are files.

Logging

  • Simulated random-leveled logging messages are now opt-in and toggled on or off by a tool.

Resources

  • The dynamic text/blob resources that formed the resource list are now only accessed via resource templates and are no longer odd/even or upper-bounded. The Dynamic Text Resource and Dynamic Blob Resource templates both take a positive integer and return a dynamically created resource with an included timestamp.
  • The listed resources are the files found in the docs/ folder, and demonstrates exposing actual static filesystem resources.
  • Simulated updates of subscribed resources are now opt-in, toggled on or off by a tool.
  • A new concept - session resources - is implemented and the gzip-file-as-resource tool 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

  • The prompt complex_prompt is now called args-prompt because it demonstrates prompts with arguments.
  • A new prompt completable-prompt demonstrates completable arguments where the selection of the first arg narrows the completions for the second arg.
  • The prompt resource-prompt now takes a resourceType argument (Text or Blob).

Tools

  • Tools are now presented in alphabetical order by name.
  • Some tools have been renamed with the goal of making them all verbs.
    • If the tool is meant to get something like an annotated message, its name has a prefix get-, e.g., get-annotated-message.
    • NOTE: The previous add tool is get-sum but echo remains 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.
  • The zip tool is now gzip-file-as-resource and incorporates changes in this PR by @ochafik
  • The structured-contenttools is nowget-structured-content` and provides an enum of city names, returning a different content block for each city.

Roots

  • Although there was code that should have done it, the automatic request to a client supporting roots for the initial list was not being sent. The listener for roots/list_changed was set and so when you added some via the Inspector, it would respond with a roots/list request.
    • The problem was that the call for roots needed to happen after the notification/initialized handler was done, otherwise, the request gets lost; the client never gets it.
    • This has been fixed. Screenshot below.
  • Now that there should always be a server-cached roots list for each connected client, that is used in the get-roots-list tool, unless for whatever reason, the list isn't present when the tool is run, in which case the list is requested.

Documentation

  • README.md is minimal and focused on how to launch and use with agents. It contains links to all the other pages in docs/.
  • CLAUDE.md is now AGENTS.md
  • The files in docs/ describe Architecture, Project Structure, The Startup Process, Server Features, Extension Points, and How It Works.

NPM Scripts

  • Launcher scripts go through dist/index.js rather than pointing to transport manager directly.
    • start:stdio 👉 node dist/index.js stdio
    • start:sse 👉 node dist/index.js sse
    • start:streamableHttp 👉node dist/index.js
  • Added prettier
    • prettier:fix
    • prettier:check

Server Details

  • Server: everything
  • Changes to: literally everything

Motivation and Context

Use new APIs

The existing implementation of the Everything server uses the low-level Server class rather than McpServer which 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.ts file. 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.md links 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/list request now being sent and list received.

Screenshot 2025-12-13 at 4 10 19 PM

Resources now include static files and templates for dynamic text and blobs.

Screenshot 2025-12-13 at 4 33 22 PM

New completable-prompt demonstrates two stage argument filtering

  • Choose department from list
Screenshot 2025-12-13 at 4 35 19 PM
  • Employees shown are only those in selected department
Screenshot 2025-12-13 at 4 35 28 PM
  • Final result combines both arguments
Screenshot 2025-12-13 at 4 35 38 PM

New toggle-subscriber-updates tool

  • Simulated Resource Subscription updates are no longer sent automatically, but toggled with a tool that provides more information about what is happening.
Screenshot 2025-12-13 at 4 47 37 PM

New toggle-simulated-logging tool

  • Simulated logging is also no longer sent automatically, but instead turned on or off with a tool that provides more information about what is happening.
Screenshot 2025-12-13 at 4 49 31 PM

Renamed gzip-file-as-resource tool creates session resource

  • Resource or resource link is returned when running tool
Screenshot 2025-12-13 at 4 52 16 PM
  • Resource exists in resource list for duration of session with URI that marks it as a session resource rather than static.
Screenshot 2025-12-13 at 4 53 15 PM

Breaking 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

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactor

Checklist

  • I have read the MCP Protocol Documentation
  • My changes follows MCP security best practices
  • I have updated the server's README accordingly
  • I have tested this with an LLM client
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have documented all environment variables and configuration options

Additional context

This fixes #3080

…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

* 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.

* 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.

* 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.

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

* Refactor/renamed sampling-request.ts to get-sampling-request.ts

* In tools/index.ts
  - sorted presenation order
@olaservo
Copy link
Member

This discussion question made me think we could add something like this, to demonstrate a collection of resources?

…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.
@cliffhall
Copy link
Member Author

cliffhall commented Dec 16, 2025

This discussion question made me think we could add something like this, to demonstrate a collection of resources?

@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.

@cliffhall cliffhall requested a review from olaservo December 17, 2025 21:03
@cliffhall cliffhall marked this pull request as draft December 17, 2025 22:36
…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
@cliffhall cliffhall marked this pull request as ready for review December 17, 2025 22:52
@cliffhall cliffhall dismissed olaservo’s stale review December 18, 2025 16:46

Requests resolved.


// Register the prompt
server.registerPrompt(
"args-prompt",
Copy link
Member

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.

Copy link
Member Author

@cliffhall cliffhall Dec 18, 2025

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.

Copy link
Member

@PederHP PederHP Dec 19, 2025

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
domdomegg previously approved these changes Dec 18, 2025
Copy link
Member

@domdomegg domdomegg left a 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 :)

olaservo
olaservo previously approved these changes Dec 19, 2025
Copy link
Member

@olaservo olaservo left a 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.

@cliffhall cliffhall merged commit dcb47d2 into modelcontextprotocol:main Dec 19, 2025
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor Everything Server to use modern APIs

4 participants