added specs
This commit is contained in:
parent
9681ec94e6
commit
21a38ebe74
3 changed files with 3532 additions and 60 deletions
|
|
@ -1,8 +1,355 @@
|
|||
---
|
||||
title: "Connection Management"
|
||||
description: "Manage MCP client connections"
|
||||
title: 'Connection Management'
|
||||
description: 'Learn how to establish and manage MCP client connections'
|
||||
---
|
||||
|
||||
# Connection Management
|
||||
## Overview
|
||||
|
||||
Manage MCP client connections
|
||||
The MCP client API provides flexible connection management options with support for different transport types. This guide covers establishing connections, handling connection events, and managing connection lifecycles.
|
||||
|
||||
## Creating a client
|
||||
|
||||
First, create an MCP client instance with your application's information.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { Client } from "@modelcontextprotocol/sdk";
|
||||
|
||||
const client = new Client({
|
||||
name: "my-application",
|
||||
version: "1.0.0"
|
||||
});
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp import Client
|
||||
|
||||
client = Client(
|
||||
name="my-application",
|
||||
version="1.0.0"
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Transport types
|
||||
|
||||
MCP supports multiple transport types for different use cases. Choose the appropriate transport based on your needs.
|
||||
|
||||
### WebSocket transport
|
||||
|
||||
Use WebSocket transport for real-time bidirectional communication.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk";
|
||||
|
||||
const transport = new WebSocketClientTransport(
|
||||
new URL("ws://localhost:3000")
|
||||
);
|
||||
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.transports import WebSocketTransport
|
||||
|
||||
transport = WebSocketTransport(
|
||||
url="ws://localhost:3000"
|
||||
)
|
||||
|
||||
await client.connect(transport)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### SSE transport
|
||||
|
||||
Server-Sent Events transport is suitable for scenarios where the server pushes data to the client.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk";
|
||||
|
||||
const transport = new SSEClientTransport(
|
||||
new URL("http://localhost:3000/events")
|
||||
);
|
||||
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.transports import SSETransport
|
||||
|
||||
transport = SSETransport(
|
||||
url="http://localhost:3000/events"
|
||||
)
|
||||
|
||||
await client.connect(transport)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Stdio transport
|
||||
|
||||
For local server processes, use the stdio transport to communicate via standard input/output.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk";
|
||||
|
||||
const transport = new StdioClientTransport({
|
||||
command: "/path/to/server",
|
||||
args: ["--arg1", "--arg2"],
|
||||
env: { "KEY": "value" }
|
||||
});
|
||||
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.transports import StdioTransport
|
||||
|
||||
transport = StdioTransport(
|
||||
command="/path/to/server",
|
||||
args=["--arg1", "--arg2"],
|
||||
env={"KEY": "value"}
|
||||
)
|
||||
|
||||
await client.connect(transport)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Connection lifecycle
|
||||
|
||||
### Event handling
|
||||
|
||||
Set up event handlers to manage the connection lifecycle.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
client.onclose = () => {
|
||||
console.log("Connection closed");
|
||||
};
|
||||
|
||||
client.onerror = (error) => {
|
||||
console.error("Connection error:", error);
|
||||
};
|
||||
|
||||
// Transport-specific events
|
||||
transport.onclose = () => {
|
||||
console.log("Transport closed");
|
||||
};
|
||||
|
||||
transport.onerror = (error) => {
|
||||
console.error("Transport error:", error);
|
||||
};
|
||||
```
|
||||
|
||||
```python Python
|
||||
@client.on_close
|
||||
async def handle_close():
|
||||
print("Connection closed")
|
||||
|
||||
@client.on_error
|
||||
async def handle_error(error):
|
||||
print(f"Connection error: {error}")
|
||||
|
||||
# Transport-specific events
|
||||
@transport.on_close
|
||||
async def handle_transport_close():
|
||||
print("Transport closed")
|
||||
|
||||
@transport.on_error
|
||||
async def handle_transport_error(error):
|
||||
print(f"Transport error: {error}")
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Connection states
|
||||
|
||||
Monitor and manage connection states throughout your application's lifecycle.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
// Check if connected
|
||||
if (client.transport) {
|
||||
console.log("Client is connected");
|
||||
}
|
||||
|
||||
// Get server capabilities after connection
|
||||
const capabilities = client.getServerCapabilities();
|
||||
const serverVersion = client.getServerVersion();
|
||||
|
||||
// Clean up connection
|
||||
await client.close();
|
||||
```
|
||||
|
||||
```python Python
|
||||
# Check if connected
|
||||
if client.transport:
|
||||
print("Client is connected")
|
||||
|
||||
# Get server capabilities after connection
|
||||
capabilities = client.get_server_capabilities()
|
||||
server_version = client.get_server_version()
|
||||
|
||||
# Clean up connection
|
||||
await client.close()
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Error handling
|
||||
|
||||
Implement robust error handling for connection-related issues.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
try {
|
||||
await client.connect(transport);
|
||||
} catch (error) {
|
||||
if (error.code === ErrorCode.ConnectionClosed) {
|
||||
// Handle connection closure
|
||||
} else if (error.code === ErrorCode.InvalidRequest) {
|
||||
// Handle invalid request
|
||||
} else {
|
||||
// Handle other errors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
try:
|
||||
await client.connect(transport)
|
||||
except ConnectionClosedError:
|
||||
# Handle connection closure
|
||||
pass
|
||||
except InvalidRequestError:
|
||||
# Handle invalid request
|
||||
pass
|
||||
except McpError as e:
|
||||
# Handle other errors
|
||||
pass
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Best practices
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Connection management" icon="plug">
|
||||
Always close connections when they're no longer needed to free up resources
|
||||
</Card>
|
||||
<Card title="Error handling" icon="shield-check">
|
||||
Implement comprehensive error handling for all connection states
|
||||
</Card>
|
||||
<Card title="Event handling" icon="bell">
|
||||
Set up appropriate event handlers before establishing connections
|
||||
</Card>
|
||||
<Card title="Resource cleanup" icon="broom">
|
||||
Use try/finally blocks to ensure proper cleanup of resources
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Connection examples
|
||||
|
||||
Here are some common connection patterns:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Basic connection with retry">
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
async function connectWithRetry(
|
||||
client: Client,
|
||||
transport: Transport,
|
||||
maxAttempts = 3
|
||||
) {
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
await client.connect(transport);
|
||||
return;
|
||||
} catch (error) {
|
||||
if (attempt === maxAttempts) throw error;
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
async def connect_with_retry(
|
||||
client: Client,
|
||||
transport: Transport,
|
||||
max_attempts: int = 3
|
||||
):
|
||||
for attempt in range(max_attempts):
|
||||
try:
|
||||
await client.connect(transport)
|
||||
return
|
||||
except Exception as e:
|
||||
if attempt == max_attempts - 1:
|
||||
raise
|
||||
await asyncio.sleep(attempt + 1)
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Connection with timeout">
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
async function connectWithTimeout(
|
||||
client: Client,
|
||||
transport: Transport,
|
||||
timeoutMs = 5000
|
||||
) {
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error("Connection timeout")), timeoutMs);
|
||||
});
|
||||
|
||||
await Promise.race([
|
||||
client.connect(transport),
|
||||
timeoutPromise
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
async def connect_with_timeout(
|
||||
client: Client,
|
||||
transport: Transport,
|
||||
timeout: float = 5.0
|
||||
):
|
||||
async with asyncio.timeout(timeout):
|
||||
await client.connect(transport)
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Connection refused">
|
||||
- Verify the server is running and accessible
|
||||
- Check if the port is correct and available
|
||||
- Ensure firewalls aren't blocking the connection
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Authentication failures">
|
||||
- Verify credentials if required
|
||||
- Check if environment variables are properly set
|
||||
- Ensure transport configuration is correct
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Connection drops">
|
||||
- Implement reconnection logic
|
||||
- Check network stability
|
||||
- Monitor server health
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Transport selection guide
|
||||
|
||||
Choose the appropriate transport based on your use case:
|
||||
|
||||
| Transport | Use Case | Advantages | Considerations |
|
||||
|-----------|----------|------------|----------------|
|
||||
| WebSocket | Real-time bidirectional | Full-duplex, low latency | Requires WebSocket server |
|
||||
| SSE | Server push updates | Simple, firewall-friendly | One-way server push |
|
||||
| Stdio | Local process communication | Simple, reliable | Limited to local processes |
|
||||
|
|
@ -1,8 +1,418 @@
|
|||
---
|
||||
title: "Resource Consumption"
|
||||
description: "Work with MCP resources from clients"
|
||||
title: 'Resource Consumption'
|
||||
description: 'Learn how to consume resources from MCP servers'
|
||||
---
|
||||
|
||||
# Resource Consumption
|
||||
MCP servers can expose resources that clients can access. This guide explains how to list available resources, read their contents, and subscribe to resource updates using both Python and TypeScript.
|
||||
|
||||
Work with MCP resources from clients
|
||||
## Listing resources
|
||||
|
||||
To get a list of available resources from an MCP server:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript list_resources.ts
|
||||
import { Client } from "@modelcontextprotocol/sdk";
|
||||
|
||||
const client = new Client({
|
||||
name: "my-client",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
// Connect to the server
|
||||
await client.connect(transport);
|
||||
|
||||
// List available resources
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "resources/list"
|
||||
},
|
||||
ListResourcesResultSchema
|
||||
);
|
||||
|
||||
// Available resources
|
||||
for (const resource of result.resources) {
|
||||
console.log(`Found resource: ${resource.name} (${resource.uri})`);
|
||||
}
|
||||
```
|
||||
|
||||
```python list_resources.py
|
||||
from modelcontextprotocol.client import Client
|
||||
|
||||
client = Client(
|
||||
name="my-client",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# Connect to the server
|
||||
await client.connect(transport)
|
||||
|
||||
# List available resources
|
||||
result = await client.request({
|
||||
"method": "resources/list"
|
||||
})
|
||||
|
||||
# Available resources
|
||||
for resource in result["resources"]:
|
||||
print(f"Found resource: {resource['name']} ({resource['uri']})")
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Reading resources
|
||||
|
||||
Once you have a resource URI, you can read its contents:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript read_resource.ts
|
||||
// Read a specific resource
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "resources/read",
|
||||
params: {
|
||||
uri: "file:///example.txt"
|
||||
}
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
|
||||
// Process the contents
|
||||
for (const content of result.contents) {
|
||||
if ("text" in content) {
|
||||
console.log("Text content:", content.text);
|
||||
} else if ("blob" in content) {
|
||||
// Handle binary data (base64 encoded)
|
||||
const binaryData = Buffer.from(content.blob, "base64");
|
||||
console.log("Binary content length:", binaryData.length);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python read_resource.py
|
||||
# Read a specific resource
|
||||
result = await client.request({
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
"uri": "file:///example.txt"
|
||||
}
|
||||
})
|
||||
|
||||
# Process the contents
|
||||
for content in result["contents"]:
|
||||
if "text" in content:
|
||||
print("Text content:", content["text"])
|
||||
elif "blob" in content:
|
||||
# Handle binary data (base64 encoded)
|
||||
import base64
|
||||
binary_data = base64.b64decode(content["blob"])
|
||||
print("Binary content length:", len(binary_data))
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Resource templates
|
||||
|
||||
Some servers provide resource templates that can be used to construct resource URIs dynamically:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript list_templates.ts
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "resources/templates/list"
|
||||
},
|
||||
ListResourceTemplatesResultSchema
|
||||
);
|
||||
|
||||
for (const template of result.resourceTemplates) {
|
||||
console.log(`Template: ${template.name}`);
|
||||
console.log(`URI template: ${template.uriTemplate}`);
|
||||
}
|
||||
```
|
||||
|
||||
```python list_templates.py
|
||||
result = await client.request({
|
||||
"method": "resources/templates/list"
|
||||
})
|
||||
|
||||
for template in result["resourceTemplates"]:
|
||||
print(f"Template: {template['name']}")
|
||||
print(f"URI template: {template['uriTemplate']}")
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Subscribing to updates
|
||||
|
||||
You can subscribe to receive notifications when resources change:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript subscribe_resource.ts
|
||||
// Subscribe to changes
|
||||
await client.request(
|
||||
{
|
||||
method: "resources/subscribe",
|
||||
params: {
|
||||
uri: "file:///example.txt"
|
||||
}
|
||||
},
|
||||
EmptyResultSchema
|
||||
);
|
||||
|
||||
// Set up notification handler
|
||||
client.setNotificationHandler(
|
||||
ResourceUpdatedNotificationSchema,
|
||||
async (notification) => {
|
||||
console.log(`Resource updated: ${notification.params.uri}`);
|
||||
|
||||
// Re-read the resource
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "resources/read",
|
||||
params: {
|
||||
uri: notification.params.uri
|
||||
}
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
|
||||
// Process updated contents...
|
||||
}
|
||||
);
|
||||
|
||||
// Later, unsubscribe when done
|
||||
await client.request(
|
||||
{
|
||||
method: "resources/unsubscribe",
|
||||
params: {
|
||||
uri: "file:///example.txt"
|
||||
}
|
||||
},
|
||||
EmptyResultSchema
|
||||
);
|
||||
```
|
||||
|
||||
```python subscribe_resource.py
|
||||
# Subscribe to changes
|
||||
await client.request({
|
||||
"method": "resources/subscribe",
|
||||
"params": {
|
||||
"uri": "file:///example.txt"
|
||||
}
|
||||
})
|
||||
|
||||
# Set up notification handler
|
||||
@client.notification_handler("notifications/resources/updated")
|
||||
async def handle_update(notification):
|
||||
print(f"Resource updated: {notification['params']['uri']}")
|
||||
|
||||
# Re-read the resource
|
||||
result = await client.request({
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
"uri": notification["params"]["uri"]
|
||||
}
|
||||
})
|
||||
|
||||
# Process updated contents...
|
||||
|
||||
# Later, unsubscribe when done
|
||||
await client.request({
|
||||
"method": "resources/unsubscribe",
|
||||
"params": {
|
||||
"uri": "file:///example.txt"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Handling pagination
|
||||
|
||||
When listing resources, servers may paginate results. Here's how to handle pagination:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript pagination.ts
|
||||
let cursor: string | undefined = undefined;
|
||||
|
||||
do {
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "resources/list",
|
||||
params: { cursor }
|
||||
},
|
||||
ListResourcesResultSchema
|
||||
);
|
||||
|
||||
// Process current page of resources
|
||||
for (const resource of result.resources) {
|
||||
console.log(`Resource: ${resource.name}`);
|
||||
}
|
||||
|
||||
// Update cursor for next page
|
||||
cursor = result.nextCursor;
|
||||
} while (cursor !== undefined);
|
||||
```
|
||||
|
||||
```python pagination.py
|
||||
cursor = None
|
||||
|
||||
while True:
|
||||
result = await client.request({
|
||||
"method": "resources/list",
|
||||
"params": { "cursor": cursor }
|
||||
})
|
||||
|
||||
# Process current page of resources
|
||||
for resource in result["resources"]:
|
||||
print(f"Resource: {resource['name']}")
|
||||
|
||||
# Update cursor for next page
|
||||
cursor = result.get("nextCursor")
|
||||
if cursor is None:
|
||||
break
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Error handling
|
||||
|
||||
When working with resources, handle potential errors appropriately:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript error_handling.ts
|
||||
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk";
|
||||
|
||||
try {
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "resources/read",
|
||||
params: {
|
||||
uri: "file:///nonexistent.txt"
|
||||
}
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof McpError) {
|
||||
switch (error.code) {
|
||||
case ErrorCode.InvalidRequest:
|
||||
console.error("Invalid resource URI");
|
||||
break;
|
||||
case ErrorCode.MethodNotFound:
|
||||
console.error("Server doesn't support resource reading");
|
||||
break;
|
||||
default:
|
||||
console.error(`Resource error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python error_handling.py
|
||||
from modelcontextprotocol.errors import McpError, ErrorCode
|
||||
|
||||
try:
|
||||
result = await client.request({
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
"uri": "file:///nonexistent.txt"
|
||||
}
|
||||
})
|
||||
except McpError as error:
|
||||
if error.code == ErrorCode.INVALID_REQUEST:
|
||||
print("Invalid resource URI")
|
||||
elif error.code == ErrorCode.METHOD_NOT_FOUND:
|
||||
print("Server doesn't support resource reading")
|
||||
else:
|
||||
print(f"Resource error: {error.message}")
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Best practices
|
||||
|
||||
When consuming resources from MCP servers:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Resource cleanup" icon="broom">
|
||||
Always unsubscribe from resources when you're done with them
|
||||
</Card>
|
||||
<Card title="Error handling" icon="shield">
|
||||
Implement proper error handling for all resource operations
|
||||
</Card>
|
||||
<Card title="Content types" icon="file">
|
||||
Check MIME types before processing resource contents
|
||||
</Card>
|
||||
<Card title="Pagination" icon="list">
|
||||
Always handle pagination when listing resources
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Type safety
|
||||
|
||||
To ensure type safety when working with resources:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```typescript type_safety.ts
|
||||
import {
|
||||
Resource,
|
||||
ResourceContents,
|
||||
TextResourceContents,
|
||||
BlobResourceContents
|
||||
} from "@modelcontextprotocol/sdk";
|
||||
|
||||
function isTextContent(
|
||||
content: ResourceContents
|
||||
): content is TextResourceContents {
|
||||
return "text" in content;
|
||||
}
|
||||
|
||||
function isBlobContent(
|
||||
content: ResourceContents
|
||||
): content is BlobResourceContents {
|
||||
return "blob" in content;
|
||||
}
|
||||
|
||||
// Use type guards when processing contents
|
||||
for (const content of result.contents) {
|
||||
if (isTextContent(content)) {
|
||||
// TypeScript knows this is text content
|
||||
const text: string = content.text;
|
||||
} else if (isBlobContent(content)) {
|
||||
// TypeScript knows this is binary content
|
||||
const blob: string = content.blob;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python type_safety.py
|
||||
from typing import TypedDict, Union, Literal
|
||||
|
||||
class TextContent(TypedDict):
|
||||
type: Literal["text"]
|
||||
text: str
|
||||
uri: str
|
||||
mimeType: str
|
||||
|
||||
class BlobContent(TypedDict):
|
||||
type: Literal["blob"]
|
||||
blob: str
|
||||
uri: str
|
||||
mimeType: str
|
||||
|
||||
ResourceContent = Union[TextContent, BlobContent]
|
||||
|
||||
def process_content(content: ResourceContent) -> None:
|
||||
if content["type"] == "text":
|
||||
text: str = content["text"]
|
||||
else:
|
||||
blob: str = content["blob"]
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
|
@ -1,8 +1,329 @@
|
|||
---
|
||||
title: "Tool Invocation"
|
||||
description: "Invoke MCP tools from clients"
|
||||
title: 'Tool Invocation'
|
||||
description: 'Learn how to invoke tools through the MCP client API'
|
||||
---
|
||||
|
||||
# Tool Invocation
|
||||
MCP allows clients to invoke tools exposed by servers. Tools are discrete operations that servers can perform on behalf of clients, similar to function calls.
|
||||
|
||||
Invoke MCP tools from clients
|
||||
## Getting available tools
|
||||
|
||||
Before invoking tools, you'll need to get a list of available tools from the server.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||
import { ListToolsResultSchema } from "@modelcontextprotocol/sdk/types";
|
||||
|
||||
// Assuming client is already connected
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "tools/list"
|
||||
},
|
||||
ListToolsResultSchema
|
||||
);
|
||||
|
||||
// Tools are now available in result.tools
|
||||
for (const tool of result.tools) {
|
||||
console.log(`Found tool: ${tool.name}`);
|
||||
console.log(`Description: ${tool.description}`);
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.client import Client
|
||||
from mcp.types import ListToolsRequest
|
||||
|
||||
# Assuming client is already connected
|
||||
result = await client.request(
|
||||
ListToolsRequest(method="tools/list"),
|
||||
ListToolsResultSchema
|
||||
)
|
||||
|
||||
# Tools are now available in result.tools
|
||||
for tool in result.tools:
|
||||
print(f"Found tool: {tool.name}")
|
||||
print(f"Description: {tool.description}")
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Tool invocation
|
||||
|
||||
To invoke a tool, you'll use the `tools/call` method with the tool name and any required arguments.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { CallToolRequest, CallToolResultSchema } from "@modelcontextprotocol/sdk/types";
|
||||
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: "example_tool",
|
||||
arguments: {
|
||||
param1: "value1",
|
||||
param2: 42
|
||||
}
|
||||
}
|
||||
},
|
||||
CallToolResultSchema
|
||||
);
|
||||
|
||||
// Tool result is available in result.toolResult
|
||||
console.log("Tool result:", result.toolResult);
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.types import CallToolRequest, CallToolResult
|
||||
|
||||
result = await client.request(
|
||||
CallToolRequest(
|
||||
method="tools/call",
|
||||
params={
|
||||
"name": "example_tool",
|
||||
"arguments": {
|
||||
"param1": "value1",
|
||||
"param2": 42
|
||||
}
|
||||
}
|
||||
),
|
||||
CallToolResult
|
||||
)
|
||||
|
||||
# Tool result is available in result.tool_result
|
||||
print("Tool result:", result.tool_result)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Progress tracking
|
||||
|
||||
For long-running tools, you can track progress using progress notifications.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: "long_running_tool",
|
||||
arguments: {
|
||||
duration: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
CallToolResultSchema,
|
||||
(progress) => {
|
||||
console.log(`Progress: ${progress.progress}/${progress.total ?? '?'}`);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
```python Python
|
||||
def progress_callback(progress):
|
||||
print(f"Progress: {progress.progress}/{progress.total or '?'}")
|
||||
|
||||
result = await client.request(
|
||||
CallToolRequest(
|
||||
method="tools/call",
|
||||
params={
|
||||
"name": "long_running_tool",
|
||||
"arguments": {
|
||||
"duration": 10
|
||||
}
|
||||
}
|
||||
),
|
||||
CallToolResult,
|
||||
on_progress=progress_callback
|
||||
)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Error handling
|
||||
|
||||
Tool invocations may fail for various reasons. Always handle potential errors appropriately.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
try {
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: "risky_tool",
|
||||
arguments: {
|
||||
important_param: "value"
|
||||
}
|
||||
}
|
||||
},
|
||||
CallToolResultSchema
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof McpError) {
|
||||
console.error(`Tool error (${error.code}): ${error.message}`);
|
||||
if (error.data) {
|
||||
console.error("Additional error data:", error.data);
|
||||
}
|
||||
} else {
|
||||
console.error("Unexpected error:", error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.errors import McpError
|
||||
|
||||
try:
|
||||
result = await client.request(
|
||||
CallToolRequest(
|
||||
method="tools/call",
|
||||
params={
|
||||
"name": "risky_tool",
|
||||
"arguments": {
|
||||
"important_param": "value"
|
||||
}
|
||||
}
|
||||
),
|
||||
CallToolResult
|
||||
)
|
||||
except McpError as e:
|
||||
print(f"Tool error ({e.code}): {e.message}")
|
||||
if e.data:
|
||||
print("Additional error data:", e.data)
|
||||
except Exception as e:
|
||||
print("Unexpected error:", e)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Complete example
|
||||
|
||||
Here's a complete example showing how to discover and use tools with proper error handling.
|
||||
|
||||
<CodeGroup>
|
||||
```typescript TypeScript
|
||||
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||
import {
|
||||
ListToolsResultSchema,
|
||||
CallToolResultSchema,
|
||||
McpError
|
||||
} from "@modelcontextprotocol/sdk/types";
|
||||
|
||||
async function invokeTools(client: Client) {
|
||||
try {
|
||||
// Get available tools
|
||||
const toolsList = await client.request(
|
||||
{ method: "tools/list" },
|
||||
ListToolsResultSchema
|
||||
);
|
||||
|
||||
// Find a specific tool
|
||||
const tool = toolsList.tools.find(t => t.name === "example_tool");
|
||||
if (!tool) {
|
||||
console.error("Tool not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke the tool with progress tracking
|
||||
const result = await client.request(
|
||||
{
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: tool.name,
|
||||
arguments: {
|
||||
// Tool-specific arguments
|
||||
param1: "value1"
|
||||
}
|
||||
}
|
||||
},
|
||||
CallToolResultSchema,
|
||||
(progress) => {
|
||||
console.log(
|
||||
`Progress: ${progress.progress}/${progress.total ?? '?'}`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
console.log("Tool result:", result.toolResult);
|
||||
} catch (error) {
|
||||
if (error instanceof McpError) {
|
||||
console.error(`Tool error (${error.code}): ${error.message}`);
|
||||
} else {
|
||||
console.error("Unexpected error:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```python Python
|
||||
from mcp.client import Client
|
||||
from mcp.types import ListToolsRequest, CallToolRequest
|
||||
from mcp.errors import McpError
|
||||
|
||||
async def invoke_tools(client: Client):
|
||||
try:
|
||||
# Get available tools
|
||||
tools_list = await client.request(
|
||||
ListToolsRequest(method="tools/list"),
|
||||
ListToolsResultSchema
|
||||
)
|
||||
|
||||
# Find a specific tool
|
||||
tool = next(
|
||||
(t for t in tools_list.tools if t.name == "example_tool"),
|
||||
None
|
||||
)
|
||||
if not tool:
|
||||
print("Tool not found")
|
||||
return
|
||||
|
||||
# Invoke the tool with progress tracking
|
||||
result = await client.request(
|
||||
CallToolRequest(
|
||||
method="tools/call",
|
||||
params={
|
||||
"name": tool.name,
|
||||
"arguments": {
|
||||
# Tool-specific arguments
|
||||
"param1": "value1"
|
||||
}
|
||||
}
|
||||
),
|
||||
CallToolResult,
|
||||
on_progress=lambda p: print(
|
||||
f"Progress: {p.progress}/{p.total or '?'}"
|
||||
)
|
||||
)
|
||||
|
||||
print("Tool result:", result.tool_result)
|
||||
except McpError as e:
|
||||
print(f"Tool error ({e.code}): {e.message}")
|
||||
except Exception as e:
|
||||
print("Unexpected error:", e)
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Best practices
|
||||
|
||||
When working with tools:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Validation" icon="check-circle">
|
||||
Always check tool availability before invoking
|
||||
</Card>
|
||||
<Card title="Error handling" icon="shield-check">
|
||||
Implement comprehensive error handling
|
||||
</Card>
|
||||
<Card title="Progress" icon="bars-progress">
|
||||
Use progress callbacks for long-running operations
|
||||
</Card>
|
||||
<Card title="Arguments" icon="input-text">
|
||||
Validate arguments against the tool's input schema
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Common errors
|
||||
|
||||
| Error Code | Description | Resolution |
|
||||
|------------|-------------|------------|
|
||||
| -32601 | Method not found | Verify the tool name exists |
|
||||
| -32602 | Invalid params | Check the tool's input schema |
|
||||
| -32603 | Internal error | Server-side error, check server logs |
|
||||
| -1 | Connection closed | Check connection status |
|
||||
Loading…
Reference in a new issue