With tool-use, you’re not steering language toward a good answer anymore, you’re handing Claude a set of actions and trusting it to pick the right one; that pick is driven almost entirely by what you wrote in the schema.
A developer registers two tools, including search_knowledge_base and get_cached_result. The tool names are distinct, but Claude’s tool selection weighs descriptions (tools->schema->description) heavily; when descriptions overlap, name alone is not sufficient to disambiguate.
Handles well
Routing Claude to the right tool reliably when descriptions are specific and exclusion conditions are stated.
Poor fit.
Two tools that do similar things and need ever-longer descriptions to keep apart: at that point, merge them into one tool with a type parameter instead.
**Description should be of two sentences, one to say when to use it, one to say when not to. That's the whole fix.**
Examples:
Every tool definition you load from an MCP server eats into your context window — even if that tool is never called in the current conversation. Connect three MCP servers with 20 tools each, and 60 tool definitions are sitting in your context before the first user message even arrives.
Two ways to control this:
defer_loading — delays loading a tool definition until Claude actually needs it. Reduces upfront context cost when a server has a large tool list.
enabled — lets you register a server but selectively expose only the tools you want Claude to see. Others are hidden entirely.
Communication Protocols
How the client actually talks to the server — two transports:
stdio — for local servers. Your app spawns the MCP server as a subprocess and communicates over standard input/output. Simple, no network needed.
Streamable HTTP — for remote servers. Your app connects over the network, uses HTTP POST for sending messages to the server, and an optional SSE stream for server-initiated messages. This is the current standard — an older SSE-only transport exists but is deprecated, don't use it for new integrations.
The two sides of MCP
MCP has a server side and a client side. You wrote the server side.
Your Java Code MCP Client (your app) Claude
───────────────── ────────────────────── ──────
You define tools → ListToolsRequest fired → Receives tool
schemas manually to your server definitions
in buildToolsSchema() automatically automaticallyWhat you automated vs what you didn't
You still wrote the tool schemas — that part is manual and always will be on the server side. Someone has to define what get_weather accepts as input.
What MCP automates is the delivery of those schemas to Claude. Without MCP, you would write the schemas twice:
- Once in your server/tool implementation
- Once again in your Claude API call's
toolsarray
With MCP, you write them once in buildToolsSchema(), and the MCP client fetches them via ListToolsRequest and passes them to Claude automatically. Claude never needs you to manually copy-paste those definitions into the API call.
In your code specifically
case "tools/list" -> {
ObjectNode result = mapper.createObjectNode();
result.set("tools", buildToolsSchema()); // ← you defined these once
send(out, successResponse(id, result));
}
When a MCP client connects to your server and fires ListToolsRequest, it gets these definitions back and forwards them to Claude — without you writing them again in the API call.
example :
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"tools": [
{
"name": "get_current_time",
"description": "Returns the current time for a given timezone",
"input_schema": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone e.g. Asia/Kolkata, America/New_York"
}
},
"required": [
"timezone"
]
}
},
{
"name": "get_weather",
"description": "Returns current weather for a given city",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name e.g. Mumbai, New York"
}
},
"required": [
"city"
]
}
},
{
"name": "get_traffic",
"description": "Returns current traffic for a given city",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name e.g. Mumbai, New York"
}
},
"required": [
"city"
]
}
},
{
"name": "convert_currency",
"description": "Converts an amount from one currency to another",
"input_schema": {
"type": "object",
"properties": {
"amount": {
"type": "number",
"description": "Amount to convert"
},
"from": {
"type": "string",
"description": "Source currency e.g. INR"
},
"to": {
"type": "string",
"description": "Target currency e.g. USD"
}
},
"required": [
"amount",
"from",
"to"
]
}
}
],
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "I'm flying from Mumbai to New York tomorrow. What's the weather there, what time is it currently in New York, and how much is 5000 INR in USD? and what's the traffic like in New York at the same time."
}
]
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "Sure! Let me fetch all of that information for you simultaneously right away!"
},
{
"type": "tool_use",
"id": "toolu_01HySPSs563HzdhK84sE8xEA",
"name": "get_weather",
"input": {
"city": "New York"
},
"caller": {
"type": "direct"
}
},
{
"type": "tool_use",
"id": "toolu_01VD6agYVE8nAWVqohwrr8Pk",
"name": "get_current_time",
"input": {
"timezone": "America/New_York"
},
"caller": {
"type": "direct"
}
},
{
"type": "tool_use",
"id": "toolu_01BE92qZfTnz7NrWu4DCK8S9",
"name": "convert_currency",
"input": {
"amount": 5000,
"from": "INR",
"to": "USD"
},
"caller": {
"type": "direct"
}
},
{
"type": "tool_use",
"id": "toolu_01JHtuSTZzdHjUXBeULddVFQ",
"name": "get_traffic",
"input": {
"city": "New York"
},
"caller": {
"type": "direct"
}
}
]
},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01HySPSs563HzdhK84sE8xEA",
"content": "28°C, Sunny"
},
{
"type": "tool_result",
"tool_use_id": "toolu_01VD6agYVE8nAWVqohwrr8Pk",
"content": "12:15 PM, Tuesday 28 Jul 2026"
},
{
"type": "tool_result",
"tool_use_id": "toolu_01BE92qZfTnz7NrWu4DCK8S9",
"content": "5000.00 INR = 60.00 USD"
},
{
"type": "tool_result",
"tool_use_id": "toolu_01JHtuSTZzdHjUXBeULddVFQ",
"content": "6am to 12pm, moderate traffic, 12pm-4pm light traffic, after 4pm heavy traffic due to office hours"
}
]
}
]
}
No comments:
Post a Comment