Part of #1200. Tools only for now.
Motivation and Context
Today, adding a single tool to the modern server means writing a whole McpSyncToolRepository (a name map, list/find/call, cursor handling), wrapping it in ToolsFeature.ofSync(...) and registering that feature. That's a lot of ceremony for what is usually one function. ModernConformanceServlet carries a fair bit of boilerplate for the same reason.
This adds a shortcut for the common case:
McpServer.builder()
.serverInfo(info)
.tool(McpServerTool.sync(greet, (ctx, request) -> greet(request)))
.tool(McpServerTool.syncStreaming(count, (ctx, request, notifier) -> count(notifier)))
.build();
McpServerTool is a Tool plus its handler, with sync, syncStreaming, async and asyncStreaming factories. The builder collects the tools into a small package-private in-memory repository and serves it through the regular ToolsFeature. Input/output schema validation and everything else in the feature still applies. If you need the same thing as an explicit feature, for example to set cache hints, there's ToolsFeature.ofTools(...).
A few mistakes now fail when you register the tool instead of when a request comes in:
- duplicate tool names;
- invalid tool names, checked by
ToolNameValidator as the legacy builder does;
- mixing
tool(...) with another feature that serves tools/*. The error names tool(...) instead of "served by both ToolsFeature and ToolsFeature".
How Has This Been Tested?
I added ToolRegistrationComparisonTests. It registers the same three tools twice, once through a hand-written repository and once through tool(...). One tool is plain, one streams progress, and one asks the user for input. The test checks that both servers answer the same way for:
- listing and calling tools;
- schema validation failures;
- unknown tools and bad cursors;
- the input-required round trip;
- progress streaming.
There are also focused tests in McpServerToolTests (the four factories, which thread a sync handler runs on, errors, output schema checks and ofTools) and new builder tests in McpServerTests.
./mvnw -pl mcp-core test passes locally, and so do the modern server tests in mcp-test with both the Jackson 3 and Jackson 2 profiles. I also ran the conformance suite (0.2.0-alpha.12, --requirements 2026-07-28) against the modern conformance server. It exits 0, and the only failures are the tasks-* and http-custom-header-server-validation scenarios, which aren't scored and are already listed in VALIDATION_RESULTS.md. I haven't run the full clean verify.
Breaking Changes
None. The change only adds new API.
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
Checklist
- I have read the MCP Documentation
- My code follows the repository's style guidelines
- New and existing tests pass locally
- I have added appropriate error handling
- I have added or updated documentation as needed
Additional context
I kept this to tools on purpose, so the shape can be discussed in #1200 before I do the same for prompts, resources, templates and completions. The conformance server's tools now use the shortcut, so its hand-written tool repository is gone. One small difference there: the old repository ignored a tools/list cursor, and now a cursor is rejected as invalid params, as the repository contract says.
I also left out a .tool(Tool, handler) overload. Sync and async versions with the same lambda shape don't compile side by side (the call is ambiguous), so the builder takes an McpServerTool instead.