Skip to content

Simplify registering tools, resources and prompts on the modern server #1200

Description

@Kehrlann

Problem

On the modern server (io.modelcontextprotocol.modern.server), exposing a single tool means building three layers by hand. McpFeature is the first-class unit of composition: McpServer dispatches by method name to whichever feature serves it. The built-in features (ToolsFeature, ResourcesFeature, PromptsFeature, CompletionsFeature) each wrap a user-implemented repository. The repository is the only place where a user's actual tool logic lives.

To add one tool, a user must currently:

  1. Implement McpSyncToolRepository (or McpAsyncToolRepository), which has three methods: list(ctx, cursor), find(ctx, name) and call(ctx, request).
  2. Wrap that repository in a feature: ToolsFeature.ofSync(repository).
  3. Register the feature on the server: McpServer.builder().feature(...).

The same applies to resources (including templates) and prompts.

Why this is cumbersome

  • The repository is a catalogue interface, not a tool interface. Users must implement list, find and call themselves. That means holding their own name → tool map, producing ToolsPage results, handling cursors (an unrecognized cursor must be an invalid-params error) and keeping list and find consistent with each other. None of this is specific to the tool they want to write.
  • The common case is one tool == one function. The API makes no distinction between that and a dynamic, database-backed catalogue, so the simple case pays the full cost of the general one.
  • Boilerplate shows up in our own code. ModernConformanceServlet hand-rolls a ConformanceTools implements McpSyncToolRepository with an internal entry map and wrapper helpers (add(...), respond(...), stream(...)) just to register a handful of tools. Resources and prompts get the same treatment.
  • Three concepts to learn before the first tool works. Users need to understand feature, repository and handler, plus how ofSync/ofAsync relate them. McpFeature, whose contract is methods(), handle(ctx, params) and capabilities(builder), is a protocol-level abstraction. It isn't something users should need to meet to register a tool.

Compared with the previous server API

The existing server API lets users register a tool as a specification (tool definition plus handler) with one builder call. The modern API has no equivalent shortcut. Moving to it makes the simplest use case noticeably heavier.

Desired outcome

Registering a tool, resource or prompt on the modern server should take roughly as much code as the thing being registered. Users shouldn't have to implement a repository or know about McpFeature for the common case. The explicit feature and repository model should stay available for advanced cases, such as dynamic catalogues, custom pagination and extensions.

Scope

  • Tools, resources, resource templates, prompts and completions on the modern server
  • Sync and async variants

Activity

  1. added this to the 2.2.0-M2 milestone on Oct 20, 2026
  2. added
    enhancementNew feature or request
    P1Significant bug affecting many users, highly requested feature
    on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Significant bug affecting many users, highly requested featurearea/serverenhancementNew feature or request

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions