Skip to content

addTool/removeTool throw NullPointerException when server capabilities are derived from registered features #1177

Closed

Description

@itzjb

Bug description
When you build a server without .capabilities(...) and register tools with .tools(...), McpServerFeatures derives ToolCapabilities.builder().build(), which leaves listChanged as null. McpAsyncServer.addTool then evaluates if (this.serverCapabilities.tools().listChanged()) and throws NullPointerException. The tool is already added at that point. removeTool, addResource, removeResource, addResourceTemplate, addPrompt and removePrompt use the same check.

I found this while checking whether a runtime addTool sends list_changed on a server that registers its tools on the builder. The test below reproduces it on main.

This is a regression from #928 (22a0efa). In 1.1.4, McpServerFeatures derived new ToolCapabilities(false).

Environment
mcp-core 2.0.1 (also 2.0.0). Java 21.0.10 (Temurin), macOS 26.2. 1.1.4 is not affected.

Steps to reproduce

  1. Build an McpAsyncServer or McpSyncServer with .tools(...) and without .capabilities(...).
  2. Call addTool(...) on the built server.
  3. The call fails with NullPointerException from McpAsyncServer.addTool.

Expected behavior
addTool completes. No notifications/tools/list_changed goes out, because listChanged was not declared. A fix could use Boolean.TRUE.equals(...listChanged()), as McpAsyncServer already does for resources().subscribe().

Minimal Complete Reproducible example
Failing test in the mcp-test module, package io.modelcontextprotocol.server:

@Test
void addToolWithDerivedCapabilities() {
	MockMcpServerTransport transport = new MockMcpServerTransport();
	McpAsyncServer server = McpServer.async(new MockMcpServerTransportProvider(transport))
		.serverInfo("test-server", "1.0.0")
		.tools(tool("initial"))
		.build();

	// Fails: NullPointerException in McpAsyncServer.addTool
	StepVerifier.create(server.addTool(tool("added"))).verifyComplete();
}

private static McpServerFeatures.AsyncToolSpecification tool(String name) {
	return McpServerFeatures.AsyncToolSpecification.builder()
		.tool(McpSchema.Tool.builder(name).description(name).build())
		.callHandler((exchange, request) -> Mono.just(McpSchema.CallToolResult.builder().build()))
		.build();
}

Activity

  1. added
    bugSomething isn't working
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    on Oct 13, 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

    P2Moderate issues affecting some users, edge cases, potentially valuable featurearea/serverbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions