Repository navigation
Simplify registering tools, resources and prompts on the modern server #1200
Description
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:
- Implement
McpSyncToolRepository(orMcpAsyncToolRepository), which has three methods:list(ctx, cursor),find(ctx, name)andcall(ctx, request). - Wrap that repository in a feature:
ToolsFeature.ofSync(repository). - 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,findandcallthemselves. That means holding their own name → tool map, producingToolsPageresults, handling cursors (an unrecognized cursor must be an invalid-params error) and keepinglistandfindconsistent 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.
ModernConformanceServlethand-rolls aConformanceTools implements McpSyncToolRepositorywith 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/ofAsyncrelate them.McpFeature, whose contract ismethods(),handle(ctx, params)andcapabilities(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
x-mcp-headerin tool input schema #1269bhuvnsh commentedon Oct 24, 2026
Hi, I tried a first version of this on a branch in my fork: main...bhuvnsh:java-sdk:issue-1200-tool-shortcuts-example. It only covers tools for now.
The idea is simple. A
McpServerToolholds aTooland the function that handles it, and you pass it to the builder with.tool(...). The builder puts all of them into a small in-memory repository and serves it through the normalToolsFeature, so schema validation still works and nothing changes for people who already write their own repositories.I didn't add a
.tool(Tool, handler)overload, because the sync and async versions clash when you pass a lambda and the code doesn't compile. So you wrap the handler inMcpServerTool.sync(...)orMcpServerTool.async(...)instead.Let me know if this direction works for you. If it does, I'll do the same for prompts, resources and completions. The conformance server's tools already use it on that branch. I'm also happy to change the names if you have something else in mind.