Friday, 7 August 2026

Tool Schemas (MCP)

 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:

get_weather

"description": "Returns current weather for a given city. 
Use this when the user asks about current weather, temperature, 
or climate conditions for a specific city. Do not use this for 
historical weather data or weather forecasts."

convert_currency

"description": "Converts an amount from one currency to another 
using current exchange rates. Use this when the user asks to 
convert a specific amount between currencies. Do not use this 
for fetching exchange rates without a conversion amount, or for 
cryptocurrency conversions."

get_traffic

"description": "Returns current traffic conditions for a given 
city. Use this when the user asks about traffic, road conditions, 
or travel delays in a city. Do not use this for route planning, 
estimated travel time between two specific addresses, or traffic 
outside supported cities."

The context cost problem with MCP

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                automatically

What 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 tools array

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

java
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