Large Language Models (LLMs) are incredibly powerful at understanding natural language, generating content, and assisting with complex reasoning tasks. However, on their own, they are limited to the information available within their training data and the immediate conversation context. They cannot directly fetch live weather updates, query enterprise databases, call REST APIs, access files, or perform real-time actions in external systems.
This is where tools come into the picture.
The Model Context Protocol (MCP) introduces a standardized and secure way for external systems to expose capabilities that language models can discover and invoke dynamically. Through MCP tools, an LLM can move beyond text generation and safely interact with real-world systems, whether that means retrieving customer data, triggering workflows, performing calculations, or integrating with third-party services.
At its core, MCP tools act as a bridge between AI reasoning and system execution.
For example, instead of merely suggesting what the weather might be, a model can invoke a get_weather tool to fetch real-time weather information from an external API. Similarly, in enterprise environments, tools can enable models to query databases, access internal services, analyze documents, or automate operational tasks.
What makes MCP especially powerful is its focus on standardization, schema-driven contracts, and safety.
Each tool is defined with:
· a unique name
· a clear description
· an input schema
· optional output schema
· execution metadata
· strong security and validation controls
This structured approach helps clients, servers, and language models communicate reliably while ensuring that tool invocations remain transparent, auditable, and safe.
In this post, we will explore how MCP tools work, how language models discover and invoke them, the underlying protocol messages involved, and the security best practices required to safely connect AI systems with external applications.
By the end of this post, you will have a clear understanding of how MCP transforms LLMs from passive text generators into intelligent systems capable of secure real-world actions.
1. How MCP Servers Advertise Supported Features?
Before a client or language model can start interacting with tools, it first needs to understand what capabilities the MCP server supports. This is handled through the capabilities contract exchanged during the initialize phase.
In MCP, any server that supports tool invocation must explicitly declare the tools capability.
{
"capabilities": {
"tools": {
"listChanged": true
}
}
}
This declaration acts as a formal agreement between the client and the server.
It tells the client:
· the server supports tool discovery
· tools can be invoked using MCP protocol messages
· the available tool list may change dynamically over time
The listChanged flag is particularly important. When set to true, it indicates that the server may add, remove, or update tools during runtime and will notify clients whenever this happens. This is very useful in real-world systems where tool availability is not always static.
Here is a sample curl request sent by a client during initialization:
curl --location 'http://localhost:8080/mcp' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"clientInfo": {
"name": "my-client",
"version": "1.0.0"
},
"capabilities": {}
}
}'
In this request:
· jsonrpc specifies the protocol version
· id helps correlate request and response
· method is initialize
· clientInfo identifies the calling client
· capabilities represents what the client itself supports
Sample Server Response
A typical server response looks like this:
{
"result": {
"capabilities": {
"resources": {
"listChanged": true,
"subscribe": false
},
"tools": {
"listChanged": true
},
"prompts": {
"listChanged": true
}
},
"serverInfo": {
"name": "employee-mcp-server",
"version": "0.0.1"
},
"protocolVersion": "2024-11-05"
},
"id": 1,
"jsonrpc": "2.0"
}
From the above response, LLM can understand that server supports:
· resources
· tools
· prompts
This means the client can now interact with all three MCP components. This capability declaration is foundational to MCP’s design. Instead of assuming what a server supports, clients discover features explicitly.
2. Listing Tools: How Clients Discover Available Actions
Once the initialization handshake is complete and the server declares support for tools, the next step is tool discovery. In MCP, clients discover available tools by sending a tools/list request.
This is the mechanism through which the client and the language model learns what operations are available for invocation.
For example, the server may expose tools for:
· fetching employee details
· listing department members
· checking leave balances
· generating reports
· performing business calculations
This discovery step is critical because it allows the LLM to make context-aware decisions about which tool to use based on the user's prompt.
2.1 Request Format
The client sends a JSON-RPC request with method tools/list.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"cursor": "optional-cursor-value"
}
}
Here:
· method: identifies the request type
· cursor: used for pagination
· id: correlates request and response
The cursor field is optional and becomes useful when the server exposes a large number of tools.
2.2 Calling tools/list Using cURL
Here is the equivalent curl request:
curl --location 'http://localhost:8080/mcp' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"cursor": "optional-cursor-value"
}
}'
This request asks the server "What tools are currently available?"
Sample Response
A typical response may look like this:
{
"result": {
"tools": [
{
"inputSchema": {
"type": "object",
"properties": {
"employeeId": {
"type": "string"
}
},
"required": [
"employeeId"
]
},
"name": "get-employee-details",
"description": "Retrieve comprehensive details about a specific employee including contact information, role, department, and employment history"
},
{
"inputSchema": {
"type": "object",
"properties": {
"departmentId": {
"type": "string"
}
},
"required": [
"departmentId"
]
},
"name": "list-department-employees",
"description": "List all employees in a specific department with their roles and tenure"
},
{
"inputSchema": {
"type": "object",
"properties": {
"employeeId": {
"type": "string"
}
},
"required": [
"employeeId"
]
},
"name": "calculate-employee-tenure",
"description": "Calculate the tenure of an employee in the organization based on their start date"
},
{
"inputSchema": {
"type": "object",
"properties": {
"teamId": {
"type": "string"
}
},
"required": [
"teamId"
]
},
"name": "get-team-members",
"description": "Retrieve all members of a specific team including their roles and reporting structure"
},
{
"inputSchema": {
"type": "object",
"properties": {
"skill": {
"type": "string"
}
},
"required": [
"skill"
]
},
"name": "search-employees-by-skill",
"description": "Find employees with specific technical or professional skills"
},
{
"inputSchema": {
"type": "object",
"properties": {
"role": {
"type": "string"
},
"level": {
"type": "string"
}
},
"required": [
"role",
"level"
]
},
"name": "get-compensation-info",
"description": "Retrieve salary band information for a specific role and level"
},
{
"inputSchema": {
"type": "object",
"properties": {
"leaveType": {
"type": "string"
},
"employeeId": {
"type": "string"
}
},
"required": [
"employeeId",
"leaveType"
]
},
"name": "check-leave-balance",
"description": "Check remaining leave balance for an employee"
},
{
"inputSchema": {
"type": "object",
"properties": {
"reportType": {
"type": "string"
}
},
"required": [
"reportType"
]
},
"name": "generate-employee-report",
"description": "Generate various employee reports including headcount, turnover, and compensation analysis"
}
]
},
"id": 1,
"jsonrpc": "2.0"
}
The sample response shows how an MCP server returns the catalog of tools currently available for the client and the language model to use. At the top level, the response follows the JSON-RPC 2.0 structure, where:
· jsonrpc indicates the protocol version
· id matches the original request ID
· result contains the actual response payload
Inside the result object, the tools array lists all available tools exposed by the server. Each tool definition contains three key pieces of information:
· Tool Name: The name field uniquely identifies the tool.
· Description: The description explains what the tool does in human-readable form.
· Input Schema: The inputSchema defines the expected parameters using JSON Schema.
For example, the get-employee-details tool requires:
{
"employeeId": "string"
}
Similarly, the get-compensation-info tool requires both:
· role
· level
This schema acts as a strict contract that ensures the model sends valid arguments while invoking the tool.
3. Calling Tools: Executing Actions with tools/call
Once the client has discovered the available tools using tools/list, the next step is to invoke a specific tool. This is done using the tools/call request. This is the moment where the language model moves from understanding the user’s intent to performing a real action through the MCP server.
For example, if a user asks "Show me details for employee AliceJohnson", the language model can identify that the get-employee-details tool is the most appropriate tool for this request, and the client application can then invoke the tool using the MCP tools/call request.
Request Format
A tool invocation request follows the JSON-RPC structure:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get-employee-details",
"arguments": {
"employeeId": "AliceJohnson"
}
}
}
Here:
· method: tools/call tells the server this is a tool execution request
· name: the exact tool name discovered from tools/list
· arguments: input values matching the tool’s inputSchema
· id: correlates request and response
This request tells the server "Execute the get-employee-details tool using employeeId = AliceJohnson".
Calling the Tool Using cURL
Here is the equivalent curl request:
curl --location 'http://localhost:8080/mcp' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get-employee-details",
"arguments": {
"employeeId": "AliceJohnson"
}
}
}'
Sample Response
A successful tool execution returns the result inside the content field.{
"result": {
"content": [
{
"type": "text",
"text": "{\"role\":\"Senior Software Engineer\",\"manager\":\"Alice Johnson\",\"phone\":\"+1-555-0123\",\"employmentType\":\"Full-time\",\"name\":\"John Doe\",\"employeeId\":\"AliceJohnson\",\"location\":\"San Francisco, CA\",\"team\":\"Backend\",\"department\":\"Engineering\",\"email\":\"john.doe@acmecorp.com\",\"startDate\":\"2020-01-15\",\"officeType\":\"Hybrid\"}"
}
]
},
"id": 2,
"jsonrpc": "2.0"
}
Understanding the Response
The response contains the result inside:
"content": [ { "type": "text", "text": "..." } ]
This means the tool returned text-based output. In this case, the text itself is a serialized JSON object containing employee details. Once parsed, the data represents like below.
{
"name": "John Doe",
"employeeId": "AliceJohnson",
"role": "Senior Software Engineer",
"department": "Engineering",
"team": "Backend",
"manager": "Alice Johnson",
"location": "San Francisco, CA",
"email": "john.doe@acmecorp.com",
"phone": "+1-555-0123",
"employmentType": "Full-time",
"officeType": "Hybrid",
"startDate": "2020-01-15"
}
This gives the LLM structured business data that it can now use to answer the user naturally like "John Doe is a Senior Software Engineer in the Backend team under Alice Johnson".
This is where MCP truly enables real-world AI workflows. The LLM is no longer limited to static knowledge. Instead, it can now fetch live, system-backed information.
For example:
· employee data from HR systems
· leave balances
· compensation bands
· internal reports
· database records
· API responses
This is what turns an LLM into an AI-powered application layer.
4. List Changed Notification: Keeping Tool Discovery in Sync
In many real-world systems, the list of available tools is not always static. New tools may be added, existing tools may be updated, or certain tools may become unavailable based on runtime conditions such as authentication, feature flags, tenant configuration, or system state.
To handle this, MCP provides a tool list change notification mechanism. If the server declared the following capability during initialization, then the server should notify the client whenever the available tool list changes.
This is done using the following notification message:
{
"capabilities": {
"tools": {
"listChanged": true
}
}
}
Unlike regular requests, this is a notification, not a request-response interaction. Its purpose is simply to inform the client that the previously cached tool list may no longer be valid.
5. Conclusion
The Model Context Protocol (MCP) provides a standardized and secure way for language model, powered applications to interact with external systems through tools.
In this post, we explored how MCP tools enable clients to move from simple text-based conversations to real-world actions and data retrieval. We started with capability negotiation, where the server declares support for tools and informs the client whether the tool list can change dynamically.
Next, we looked at tool discovery using tools/list, which allows the client to retrieve all available tools along with their descriptions and input schemas. This step is essential because it gives the language model the context needed to decide which tool best matches the user’s request.
We then covered tool invocation with tools/call, where the client executes the selected tool by sending the required arguments to the MCP server. This is the core of the interaction flow, where AI reasoning is connected to actual system execution.
Finally, we discussed list change notifications, which help keep the client synchronized whenever the server’s available tools change at runtime.
Together, these capabilities make MCP a powerful protocol for building tool-driven AI applications that are dynamic, secure, and extensible.
Whether you are integrating with enterprise APIs, internal databases, reporting systems, or workflow engines, MCP provides a clear and scalable contract for exposing those capabilities to language models safely.
No comments:
Post a Comment