added specs

This commit is contained in:
Zack Lee 2024-11-18 11:36:07 -05:00
commit 21a38ebe74
No known key found for this signature in database
3 changed files with 3532 additions and 60 deletions

View file

@ -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 |

View file

@ -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>

View file

@ -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 |