Merge pull request #6 from modelcontextprotocol/zack-lee/specs-4
Added specification, some quickstart updates
This commit is contained in:
commit
044378efd8
28 changed files with 4008 additions and 1497 deletions
282
docs/concepts/architecture.mdx
Normal file
282
docs/concepts/architecture.mdx
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
---
|
||||
title: "Core Architecture"
|
||||
description: "Understand how MCP connects clients, servers, and LLMs"
|
||||
---
|
||||
|
||||
The Model Context Protocol (MCP) is built on a flexible, extensible architecture that enables seamless communication between LLM applications and external services. This document covers the core architectural components and concepts.
|
||||
|
||||
## Overview
|
||||
|
||||
MCP follows a client-server architecture where:
|
||||
|
||||
- **Clients** are LLM applications (like claude.ai or IDEs) that initiate connections
|
||||
- **Servers** provide context, tools, and prompts to clients
|
||||
- **Hosts** are processes that run MCP clients
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Host (e.g., claude.ai)"
|
||||
client[MCP Client]
|
||||
end
|
||||
subgraph "Server Process"
|
||||
server[MCP Server]
|
||||
end
|
||||
|
||||
client <-->|Transport Layer| server
|
||||
```
|
||||
|
||||
## Core components
|
||||
|
||||
### Protocol Layer
|
||||
|
||||
The protocol layer handles message framing, request/response linking, and high-level communication patterns. Key classes include:
|
||||
|
||||
```typescript
|
||||
class Protocol<SendRequestT, SendNotificationT, SendResultT> {
|
||||
// Handle incoming requests
|
||||
setRequestHandler<T>(schema: T, handler: (request: T) => Result): void
|
||||
|
||||
// Handle incoming notifications
|
||||
setNotificationHandler<T>(schema: T, handler: (notification: T) => void): void
|
||||
|
||||
// Send requests and await responses
|
||||
request<T>(request: Request, schema: T): Promise<T>
|
||||
|
||||
// Send one-way notifications
|
||||
notification(notification: Notification): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
### Transport layer
|
||||
|
||||
The transport layer handles the actual communication between clients and servers. MCP supports multiple transport mechanisms:
|
||||
|
||||
1. **stdio Transport**
|
||||
- Uses standard input/output for communication
|
||||
- Ideal for local processes
|
||||
```typescript
|
||||
class StdioTransport implements Transport {
|
||||
constructor(stdin: Readable, stdout: Writable)
|
||||
send(message: JSONRPCMessage): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
2. **WebSocket Transport**
|
||||
- Enables real-time bidirectional communication
|
||||
- Supports remote connections
|
||||
```typescript
|
||||
class WebSocketTransport implements Transport {
|
||||
constructor(url: URL)
|
||||
send(message: JSONRPCMessage): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
3. **SSE Transport**
|
||||
- Uses Server-Sent Events for server-to-client messages
|
||||
- HTTP POST for client-to-server messages
|
||||
```typescript
|
||||
class SSETransport implements Transport {
|
||||
constructor(url: URL)
|
||||
send(message: JSONRPCMessage): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
### Message types
|
||||
|
||||
MCP uses JSON-RPC 2.0 as its message format with three main types:
|
||||
|
||||
1. **Requests**
|
||||
```typescript
|
||||
interface Request {
|
||||
method: string
|
||||
params?: {
|
||||
_meta?: {
|
||||
progressToken?: string | number
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Notifications**
|
||||
```typescript
|
||||
interface Notification {
|
||||
method: string
|
||||
params?: {
|
||||
_meta?: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Results**
|
||||
```typescript
|
||||
interface Result {
|
||||
_meta?: Record<string, unknown>
|
||||
[key: string]: unknown
|
||||
}
|
||||
```
|
||||
|
||||
## Connection lifecycle
|
||||
|
||||
### 1. Initialization
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Server
|
||||
|
||||
Client->>Server: initialize request
|
||||
Server->>Client: initialize response
|
||||
Client->>Server: initialized notification
|
||||
|
||||
Note over Client,Server: Connection ready for use
|
||||
```
|
||||
|
||||
1. Client sends `initialize` request with capabilities
|
||||
2. Server responds with supported features
|
||||
3. Client sends `initialized` notification
|
||||
4. Normal message exchange begins
|
||||
|
||||
### 2. Message Exchange
|
||||
|
||||
After initialization, the following patterns are supported:
|
||||
|
||||
- **Request-Response**: Client sends request, server responds
|
||||
- **Notifications**: Either party sends one-way messages
|
||||
- **Progress Updates**: Long-running operations can report progress
|
||||
- **Resource Updates**: Servers notify about changed resources
|
||||
|
||||
### 3. Termination
|
||||
|
||||
Either party can terminate the connection:
|
||||
- Clean shutdown via `close()`
|
||||
- Transport disconnection
|
||||
- Error conditions
|
||||
|
||||
## Error handling
|
||||
|
||||
MCP defines standard error codes and handling:
|
||||
|
||||
```typescript
|
||||
enum ErrorCode {
|
||||
ConnectionClosed = -1,
|
||||
ParseError = -32700,
|
||||
InvalidRequest = -32600,
|
||||
MethodNotFound = -32601,
|
||||
InvalidParams = -32602,
|
||||
InternalError = -32603
|
||||
}
|
||||
```
|
||||
|
||||
Errors are propagated through:
|
||||
- Error responses to requests
|
||||
- Error events on transports
|
||||
- Protocol-level error handlers
|
||||
|
||||
## Implementation example
|
||||
|
||||
Here's a basic example of implementing an MCP server:
|
||||
|
||||
```typescript
|
||||
import { Server } from "@modelcontextprotocol/sdk/server";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
|
||||
|
||||
const server = new Server({
|
||||
name: "example-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
// Handle requests
|
||||
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
||||
return {
|
||||
resources: [
|
||||
{
|
||||
uri: "example://resource",
|
||||
name: "Example Resource"
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
// Connect transport
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
### Transport selection
|
||||
|
||||
1. **Local Communication**
|
||||
- Use stdio transport for local processes
|
||||
- Efficient for same-machine communication
|
||||
- Simple process management
|
||||
|
||||
2. **Remote Communication**
|
||||
- WebSocket for full-duplex communication
|
||||
- SSE for scenarios requiring HTTP compatibility
|
||||
- Consider security implications
|
||||
|
||||
### Message Handling
|
||||
|
||||
1. **Request Processing**
|
||||
- Validate inputs thoroughly
|
||||
- Use type-safe schemas
|
||||
- Handle errors gracefully
|
||||
- Implement timeouts
|
||||
|
||||
2. **Progress Reporting**
|
||||
- Use progress tokens for long operations
|
||||
- Report progress incrementally
|
||||
- Include total progress when known
|
||||
|
||||
3. **Error Management**
|
||||
- Use appropriate error codes
|
||||
- Include helpful error messages
|
||||
- Clean up resources on errors
|
||||
|
||||
## Security considerations
|
||||
|
||||
1. **Transport Security**
|
||||
- Use TLS for remote connections
|
||||
- Validate connection origins
|
||||
- Implement authentication when needed
|
||||
|
||||
2. **Message Validation**
|
||||
- Validate all incoming messages
|
||||
- Sanitize inputs
|
||||
- Check message size limits
|
||||
- Verify JSON-RPC format
|
||||
|
||||
3. **Resource Protection**
|
||||
- Implement access controls
|
||||
- Validate resource paths
|
||||
- Monitor resource usage
|
||||
- Rate limit requests
|
||||
|
||||
4. **Error Handling**
|
||||
- Don't leak sensitive information
|
||||
- Log security-relevant errors
|
||||
- Implement proper cleanup
|
||||
- Handle DoS scenarios
|
||||
|
||||
## Debugging and monitoring
|
||||
|
||||
1. **Logging**
|
||||
- Log protocol events
|
||||
- Track message flow
|
||||
- Monitor performance
|
||||
- Record errors
|
||||
|
||||
2. **Diagnostics**
|
||||
- Implement health checks
|
||||
- Monitor connection state
|
||||
- Track resource usage
|
||||
- Profile performance
|
||||
|
||||
3. **Testing**
|
||||
- Test different transports
|
||||
- Verify error handling
|
||||
- Check edge cases
|
||||
- Load test servers
|
||||
|
||||
This architecture provides a robust foundation for building MCP integrations while maintaining flexibility for different use cases and deployment scenarios.
|
||||
393
docs/concepts/prompts.mdx
Normal file
393
docs/concepts/prompts.mdx
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
---
|
||||
title: "Prompts"
|
||||
description: "Create resuable prompt templates and workflows"
|
||||
---
|
||||
|
||||
MCP Prompts enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs. They provide a powerful way to standardize and share common LLM interactions.
|
||||
|
||||
## Overview
|
||||
|
||||
Prompts in MCP are predefined templates that can:
|
||||
- Accept dynamic arguments
|
||||
- Include context from resources
|
||||
- Chain multiple interactions
|
||||
- Guide specific workflows
|
||||
- Surface as UI elements (like slash commands)
|
||||
|
||||
## Prompt structure
|
||||
|
||||
Each prompt is defined with:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: string; // Unique identifier for the prompt
|
||||
description?: string; // Human-readable description
|
||||
arguments?: [ // Optional list of arguments
|
||||
{
|
||||
name: string; // Argument identifier
|
||||
description?: string; // Argument description
|
||||
required?: boolean; // Whether argument is required
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Discovering prompts
|
||||
|
||||
Clients can discover available prompts through the `prompts/list` endpoint:
|
||||
|
||||
```typescript
|
||||
// Request
|
||||
{
|
||||
method: "prompts/list"
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
prompts: [
|
||||
{
|
||||
name: "analyze-code",
|
||||
description: "Analyze code for potential improvements",
|
||||
arguments: [
|
||||
{
|
||||
name: "language",
|
||||
description: "Programming language",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Using prompts
|
||||
|
||||
To use a prompt, clients make a `prompts/get` request:
|
||||
|
||||
```typescript
|
||||
// Request
|
||||
{
|
||||
method: "prompts/get",
|
||||
params: {
|
||||
name: "analyze-code",
|
||||
arguments: {
|
||||
language: "python"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
description?: string, // Optional context about the prompt
|
||||
messages: [ // Messages to send to the LLM
|
||||
{
|
||||
role: "user" | "assistant",
|
||||
content: {
|
||||
type: "text" | "image",
|
||||
text?: string,
|
||||
data?: string, // base64 for images
|
||||
mimeType?: string
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Dynamic prompts
|
||||
|
||||
Prompts can be dynamic and include:
|
||||
|
||||
### Context Integration
|
||||
|
||||
```typescript
|
||||
const analyzeLogsPrompt = {
|
||||
name: "analyze-logs",
|
||||
description: "Analyze recent system logs",
|
||||
arguments: [
|
||||
{
|
||||
name: "timeframe",
|
||||
description: "Time period to analyze",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
||||
if (request.params.name === "analyze-logs") {
|
||||
const logs = await getSystemLogs(request.params.arguments?.timeframe);
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `Analyze these system logs and identify any issues:\n\n${logs}`
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Resource references
|
||||
|
||||
```typescript
|
||||
const codeReviewPrompt = {
|
||||
name: "review-code",
|
||||
async getMessages(fileUri: string) {
|
||||
const code = await readResource(fileUri);
|
||||
return [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `Review this code for best practices:\n\n${code}`
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Multi-step workflows
|
||||
|
||||
```typescript
|
||||
const debugWorkflow = {
|
||||
name: "debug-error",
|
||||
async getMessages(error: string) {
|
||||
return [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: "Here's an error I'm seeing:"
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
content: {
|
||||
type: "text",
|
||||
text: "I'll help analyze this error. What have you tried so far?"
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: error
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Example implementation
|
||||
|
||||
Here's a complete example of implementing prompts in an MCP server:
|
||||
|
||||
```typescript
|
||||
import { Server } from "@modelcontextprotocol/sdk/server";
|
||||
import {
|
||||
ListPromptsRequestSchema,
|
||||
GetPromptRequestSchema
|
||||
} from "@modelcontextprotocol/sdk/types";
|
||||
|
||||
const PROMPTS = {
|
||||
"git-commit": {
|
||||
name: "git-commit",
|
||||
description: "Generate a Git commit message",
|
||||
arguments: [
|
||||
{
|
||||
name: "changes",
|
||||
description: "Git diff or description of changes",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
"explain-code": {
|
||||
name: "explain-code",
|
||||
description: "Explain how code works",
|
||||
arguments: [
|
||||
{
|
||||
name: "code",
|
||||
description: "Code to explain",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "language",
|
||||
description: "Programming language",
|
||||
required: false
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const server = new Server({
|
||||
name: "example-prompts-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
// List available prompts
|
||||
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
||||
return {
|
||||
prompts: Object.values(PROMPTS)
|
||||
};
|
||||
});
|
||||
|
||||
// Get specific prompt
|
||||
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
||||
const prompt = PROMPTS[request.params.name];
|
||||
if (!prompt) {
|
||||
throw new Error(`Prompt not found: ${request.params.name}`);
|
||||
}
|
||||
|
||||
if (request.params.name === "git-commit") {
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `Generate a concise but descriptive commit message for these changes:\n\n${request.params.arguments?.changes}`
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
if (request.params.name === "explain-code") {
|
||||
const language = request.params.arguments?.language || "Unknown";
|
||||
return {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `Explain how this ${language} code works:\n\n${request.params.arguments?.code}`
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error("Prompt implementation not found");
|
||||
});
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
When implementing prompts:
|
||||
|
||||
1. Use clear, descriptive prompt names
|
||||
2. Provide detailed descriptions for prompts and arguments
|
||||
3. Validate all required arguments
|
||||
4. Handle missing arguments gracefully
|
||||
5. Consider versioning for prompt templates
|
||||
6. Cache dynamic content when appropriate
|
||||
7. Implement error handling
|
||||
8. Document expected argument formats
|
||||
9. Consider prompt composability
|
||||
10. Test prompts with various inputs
|
||||
|
||||
## UI integration
|
||||
|
||||
Prompts can be surfaced in client UIs as:
|
||||
|
||||
- Slash commands
|
||||
- Quick actions
|
||||
- Context menu items
|
||||
- Command palette entries
|
||||
- Guided workflows
|
||||
- Interactive forms
|
||||
|
||||
## Updates and changes
|
||||
|
||||
Servers can notify clients about prompt changes:
|
||||
|
||||
1. Server capability: `prompts.listChanged`
|
||||
2. Notification: `notifications/prompts/list_changed`
|
||||
3. Client re-fetches prompt list
|
||||
|
||||
## Security considerations
|
||||
|
||||
When implementing prompts:
|
||||
|
||||
- Validate all arguments
|
||||
- Sanitize user input
|
||||
- Consider rate limiting
|
||||
- Implement access controls
|
||||
- Audit prompt usage
|
||||
- Handle sensitive data appropriately
|
||||
- Validate generated content
|
||||
- Implement timeouts
|
||||
- Consider prompt injection risks
|
||||
- Document security requirements
|
||||
|
||||
## Advanced features
|
||||
|
||||
### Prompt chaining
|
||||
|
||||
```typescript
|
||||
const analyzePRPrompt = {
|
||||
async getMessages(prId: string) {
|
||||
// Chain multiple prompts
|
||||
const diffPrompt = await getPrompt("analyze-diff");
|
||||
const testsPrompt = await getPrompt("review-tests");
|
||||
const securityPrompt = await getPrompt("security-review");
|
||||
|
||||
return [
|
||||
...diffPrompt.messages,
|
||||
...testsPrompt.messages,
|
||||
...securityPrompt.messages
|
||||
];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Context enrichment
|
||||
|
||||
```typescript
|
||||
const troubleshootPrompt = {
|
||||
async getMessages(error: string) {
|
||||
// Enrich with system context
|
||||
const systemInfo = await getSystemInfo();
|
||||
const recentLogs = await getRecentLogs();
|
||||
const stackTrace = await parseError(error);
|
||||
|
||||
return [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: `Help troubleshoot this error:
|
||||
Error: ${error}
|
||||
System: ${systemInfo}
|
||||
Logs: ${recentLogs}
|
||||
Stack: ${stackTrace}`
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Interactive workflows
|
||||
|
||||
```typescript
|
||||
const debugWorkflow = {
|
||||
async getMessages(stage: string, context: any) {
|
||||
switch (stage) {
|
||||
case "initial":
|
||||
return promptForError();
|
||||
case "analysis":
|
||||
return analyzeError(context.error);
|
||||
case "solution":
|
||||
return suggestFixes(context.analysis);
|
||||
case "verification":
|
||||
return verifyFix(context.solution);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
197
docs/concepts/resources.mdx
Normal file
197
docs/concepts/resources.mdx
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
title: "Resources"
|
||||
description: "Expose data and content from your servers to LLMs"
|
||||
---
|
||||
|
||||
Resources are a core primitive in the Model Context Protocol (MCP) that allow servers to expose data and content that can be read by clients and used as context for LLM interactions.
|
||||
|
||||
## Overview
|
||||
|
||||
Resources represent any kind of data that an MCP server wants to make available to clients. This can include:
|
||||
|
||||
- File contents
|
||||
- Database records
|
||||
- API responses
|
||||
- Live system data
|
||||
- Screenshots and images
|
||||
- Log files
|
||||
- And more
|
||||
|
||||
Each resource is identified by a unique URI and can contain either text or binary data.
|
||||
|
||||
## Resource URIs
|
||||
|
||||
Resources are identified using URIs that follow this format:
|
||||
|
||||
```
|
||||
[protocol]://[path]
|
||||
```
|
||||
|
||||
For example:
|
||||
- `file:///home/user/documents/report.pdf`
|
||||
- `postgres://database/customers/schema`
|
||||
- `screen://localhost/display1`
|
||||
|
||||
The protocol and path structure is defined by the MCP server implementation. Servers can define their own custom URI schemes.
|
||||
|
||||
## Resource types
|
||||
|
||||
Resources can contain two types of content:
|
||||
|
||||
### Text Resources
|
||||
|
||||
Text resources contain UTF-8 encoded text data. These are suitable for:
|
||||
- Source code
|
||||
- Configuration files
|
||||
- Log files
|
||||
- JSON/XML data
|
||||
- Plain text
|
||||
|
||||
### Binary resources
|
||||
|
||||
Binary resources contain raw binary data encoded in base64. These are suitable for:
|
||||
- Images
|
||||
- PDFs
|
||||
- Audio files
|
||||
- Video files
|
||||
- Other non-text formats
|
||||
|
||||
## Resource discovery
|
||||
|
||||
Clients can discover available resources through two main methods:
|
||||
|
||||
### Direct resources
|
||||
|
||||
Servers expose a list of concrete resources via the `resources/list` endpoint. Each resource includes:
|
||||
|
||||
```typescript
|
||||
{
|
||||
uri: string; // Unique identifier for the resource
|
||||
name: string; // Human-readable name
|
||||
description?: string; // Optional description
|
||||
mimeType?: string; // Optional MIME type
|
||||
}
|
||||
```
|
||||
|
||||
### Resource templates
|
||||
|
||||
For dynamic resources, servers can expose URI templates that clients can use to construct valid resource URIs:
|
||||
|
||||
```typescript
|
||||
{
|
||||
uriTemplate: string; // URI template following RFC 6570
|
||||
name: string; // Human-readable name for this type
|
||||
description?: string; // Optional description
|
||||
mimeType?: string; // Optional MIME type for all matching resources
|
||||
}
|
||||
```
|
||||
|
||||
## Reading resources
|
||||
|
||||
To read a resource, clients make a `resources/read` request with the resource URI. The server responds with:
|
||||
|
||||
```typescript
|
||||
{
|
||||
contents: [
|
||||
{
|
||||
uri: string; // The requested URI
|
||||
mimeType?: string; // Optional MIME type
|
||||
|
||||
// One of:
|
||||
text?: string; // For text resources
|
||||
blob?: string; // For binary resources (base64 encoded)
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Resource updates
|
||||
|
||||
MCP supports real-time updates for resources through two mechanisms:
|
||||
|
||||
### List changes
|
||||
|
||||
Servers can notify clients when their list of available resources changes via the `notifications/resources/list_changed` notification.
|
||||
|
||||
### Content changes
|
||||
|
||||
Clients can subscribe to updates for specific resources:
|
||||
|
||||
1. Client sends `resources/subscribe` with resource URI
|
||||
2. Server sends `notifications/resources/updated` when the resource changes
|
||||
3. Client can fetch latest content with `resources/read`
|
||||
4. Client can unsubscribe with `resources/unsubscribe`
|
||||
|
||||
## Example implementation
|
||||
|
||||
Here's a simple example of implementing resource support in an MCP server:
|
||||
|
||||
```typescript
|
||||
const server = new Server({
|
||||
name: "example-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
// List available resources
|
||||
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
||||
return {
|
||||
resources: [
|
||||
{
|
||||
uri: "file:///logs/app.log",
|
||||
name: "Application Logs",
|
||||
mimeType: "text/plain"
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
// Read resource contents
|
||||
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
||||
const uri = request.params.uri;
|
||||
|
||||
if (uri === "file:///logs/app.log") {
|
||||
const logContents = await readLogFile();
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri,
|
||||
mimeType: "text/plain",
|
||||
text: logContents
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error("Resource not found");
|
||||
});
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
When implementing resource support:
|
||||
|
||||
1. Use clear, descriptive resource names and URIs
|
||||
2. Include helpful descriptions to guide LLM understanding
|
||||
3. Set appropriate MIME types when known
|
||||
4. Implement resource templates for dynamic content
|
||||
5. Use subscriptions for frequently changing resources
|
||||
6. Handle errors gracefully with clear error messages
|
||||
7. Consider pagination for large resource lists
|
||||
8. Cache resource contents when appropriate
|
||||
9. Validate URIs before processing
|
||||
10. Document your custom URI schemes
|
||||
|
||||
## Security considerations
|
||||
|
||||
When exposing resources:
|
||||
|
||||
- Validate all resource URIs
|
||||
- Implement appropriate access controls
|
||||
- Sanitize file paths to prevent directory traversal
|
||||
- Be cautious with binary data handling
|
||||
- Consider rate limiting for resource reads
|
||||
- Audit resource access
|
||||
- Encrypt sensitive data in transit
|
||||
- Validate MIME types
|
||||
- Implement timeouts for long-running reads
|
||||
- Handle resource cleanup appropriately
|
||||
221
docs/concepts/sampling.mdx
Normal file
221
docs/concepts/sampling.mdx
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
---
|
||||
title: "Sampling"
|
||||
description: "Let your servers request completions from LLMs"
|
||||
---
|
||||
|
||||
Sampling is a powerful MCP feature that allows servers to request LLM completions through the client, enabling sophisticated agentic behaviors while maintaining security and privacy.
|
||||
|
||||
## How sampling works
|
||||
|
||||
The sampling flow follows these steps:
|
||||
|
||||
1. Server sends a `sampling/createMessage` request to the client
|
||||
2. Client reviews the request and can modify it
|
||||
3. Client samples from an LLM
|
||||
4. Client reviews the completion
|
||||
5. Client returns the result to the server
|
||||
|
||||
This human-in-the-loop design ensures users maintain control over what the LLM sees and generates.
|
||||
|
||||
## Message format
|
||||
|
||||
Sampling requests use a standardized message format:
|
||||
|
||||
```typescript
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
role: "user" | "assistant",
|
||||
content: {
|
||||
type: "text" | "image",
|
||||
// For text:
|
||||
text?: string,
|
||||
// For images:
|
||||
data?: string, // base64 encoded
|
||||
mimeType?: string
|
||||
}
|
||||
}
|
||||
],
|
||||
systemPrompt?: string,
|
||||
includeContext?: "none" | "thisServer" | "allServers",
|
||||
temperature?: number,
|
||||
maxTokens: number,
|
||||
stopSequences?: string[],
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
```
|
||||
|
||||
## Request parameters
|
||||
|
||||
### Messages
|
||||
|
||||
The `messages` array contains the conversation history to send to the LLM. Each message has:
|
||||
|
||||
- `role`: Either "user" or "assistant"
|
||||
- `content`: The message content, which can be:
|
||||
- Text content with a `text` field
|
||||
- Image content with `data` (base64) and `mimeType` fields
|
||||
|
||||
### System prompt
|
||||
|
||||
An optional `systemPrompt` field allows servers to request a specific system prompt. The client may modify or ignore this.
|
||||
|
||||
### Context inclusion
|
||||
|
||||
The `includeContext` parameter specifies what MCP context to include:
|
||||
|
||||
- `"none"`: No additional context
|
||||
- `"thisServer"`: Include context from the requesting server
|
||||
- `"allServers"`: Include context from all connected MCP servers
|
||||
|
||||
The client controls what context is actually included.
|
||||
|
||||
### Sampling parameters
|
||||
|
||||
Fine-tune the LLM sampling with:
|
||||
|
||||
- `temperature`: Controls randomness (0.0 to 1.0)
|
||||
- `maxTokens`: Maximum tokens to generate
|
||||
- `stopSequences`: Array of sequences that stop generation
|
||||
- `metadata`: Additional provider-specific parameters
|
||||
|
||||
## Response format
|
||||
|
||||
The client returns a completion result:
|
||||
|
||||
```typescript
|
||||
{
|
||||
model: string, // Name of the model used
|
||||
stopReason: "endTurn" | "stopSequence" | "maxTokens",
|
||||
role: "user" | "assistant",
|
||||
content: {
|
||||
type: "text" | "image",
|
||||
text?: string,
|
||||
data?: string,
|
||||
mimeType?: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example implementation
|
||||
|
||||
Here's an example of requesting sampling from a client:
|
||||
|
||||
```typescript
|
||||
// Server-side code
|
||||
const response = await server.request({
|
||||
method: "sampling/createMessage",
|
||||
params: {
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: {
|
||||
type: "text",
|
||||
text: "What files are in the current directory?"
|
||||
}
|
||||
}
|
||||
],
|
||||
systemPrompt: "You are a helpful file system assistant.",
|
||||
includeContext: "thisServer",
|
||||
maxTokens: 100,
|
||||
temperature: 0.7
|
||||
}
|
||||
}, CreateMessageResultSchema);
|
||||
|
||||
// Handle the completion
|
||||
console.log(response.content.text);
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
When implementing sampling:
|
||||
|
||||
1. Always provide clear, well-structured prompts
|
||||
2. Handle both text and image content appropriately
|
||||
3. Set reasonable token limits
|
||||
4. Include relevant context through `includeContext`
|
||||
5. Validate responses before using them
|
||||
6. Handle errors gracefully
|
||||
7. Consider rate limiting sampling requests
|
||||
8. Document expected sampling behavior
|
||||
9. Test with various model parameters
|
||||
10. Monitor sampling costs
|
||||
|
||||
## Human-in-the-loop controls
|
||||
|
||||
Sampling is designed with human oversight in mind:
|
||||
|
||||
### For prompts
|
||||
|
||||
- Clients should show users the proposed prompt
|
||||
- Users should be able to modify or reject prompts
|
||||
- System prompts can be filtered or modified
|
||||
- Context inclusion is controlled by the client
|
||||
|
||||
### For completions
|
||||
|
||||
- Clients should show users the completion
|
||||
- Users should be able to modify or reject completions
|
||||
- Clients can filter or modify completions
|
||||
- Users control which model is used
|
||||
|
||||
## Security considerations
|
||||
|
||||
When implementing sampling:
|
||||
|
||||
- Validate all message content
|
||||
- Sanitize sensitive information
|
||||
- Implement appropriate rate limits
|
||||
- Monitor sampling usage
|
||||
- Encrypt data in transit
|
||||
- Handle user data privacy
|
||||
- Audit sampling requests
|
||||
- Control cost exposure
|
||||
- Implement timeouts
|
||||
- Handle model errors gracefully
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Agentic workflows
|
||||
|
||||
Sampling enables agentic patterns like:
|
||||
|
||||
- Reading and analyzing resources
|
||||
- Making decisions based on context
|
||||
- Generating structured data
|
||||
- Handling multi-step tasks
|
||||
- Providing interactive assistance
|
||||
|
||||
### Context management
|
||||
|
||||
Best practices for context:
|
||||
|
||||
- Request minimal necessary context
|
||||
- Structure context clearly
|
||||
- Handle context size limits
|
||||
- Update context as needed
|
||||
- Clean up stale context
|
||||
|
||||
### Error handling
|
||||
|
||||
Robust error handling should:
|
||||
|
||||
- Catch sampling failures
|
||||
- Handle timeout errors
|
||||
- Manage rate limits
|
||||
- Validate responses
|
||||
- Provide fallback behaviors
|
||||
- Log errors appropriately
|
||||
|
||||
## Limitations
|
||||
|
||||
Be aware of these limitations:
|
||||
|
||||
- Sampling depends on client capabilities
|
||||
- Users control sampling behavior
|
||||
- Context size has limits
|
||||
- Rate limits may apply
|
||||
- Costs should be considered
|
||||
- Model availability varies
|
||||
- Response times vary
|
||||
- Not all content types supported
|
||||
286
docs/concepts/tools.mdx
Normal file
286
docs/concepts/tools.mdx
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
---
|
||||
title: "Tools"
|
||||
description: "Enable LLMs to perform actions through your server"
|
||||
---
|
||||
|
||||
Tools are a powerful primitive in the Model Context Protocol (MCP) that enable servers to expose executable functionality to clients. Through tools, LLMs can interact with external systems, perform computations, and take actions in the real world.
|
||||
|
||||
## How tools work
|
||||
|
||||
Tools in MCP follow a request-response pattern where:
|
||||
|
||||
1. Clients discover available tools through the `tools/list` endpoint
|
||||
2. Clients can invoke tools using the `tools/call` endpoint
|
||||
3. Servers execute the requested operation and return results
|
||||
4. Progress updates can be sent during long-running operations
|
||||
|
||||
## Tool definition structure
|
||||
|
||||
Each tool is defined with the following structure:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: string; // Unique identifier for the tool
|
||||
description?: string; // Human-readable description
|
||||
inputSchema: { // JSON Schema for the tool's parameters
|
||||
type: "object",
|
||||
properties: {
|
||||
// Tool-specific parameters
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `inputSchema` uses JSON Schema to define the expected parameters and their types. This enables:
|
||||
|
||||
- Parameter validation
|
||||
- Type checking
|
||||
- Documentation of required vs optional parameters
|
||||
- Complex nested parameter structures
|
||||
|
||||
## Implementing tools
|
||||
|
||||
Here's an example of implementing a basic tool in an MCP server:
|
||||
|
||||
```typescript
|
||||
const server = new Server({
|
||||
name: "example-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
// Define available tools
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
return {
|
||||
tools: [{
|
||||
name: "calculate_sum",
|
||||
description: "Add two numbers together",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
a: { type: "number" },
|
||||
b: { type: "number" }
|
||||
},
|
||||
required: ["a", "b"]
|
||||
}
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
// Handle tool execution
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if (request.params.name === "calculate_sum") {
|
||||
const { a, b } = request.params.arguments;
|
||||
return {
|
||||
toolResult: a + b
|
||||
};
|
||||
}
|
||||
throw new Error("Tool not found");
|
||||
});
|
||||
```
|
||||
|
||||
## Progress reporting
|
||||
|
||||
For long-running operations, tools can report progress using the progress notification system:
|
||||
|
||||
```typescript
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
if (request.params.name === "long_process") {
|
||||
const progressToken = request.params._meta?.progressToken;
|
||||
|
||||
for (let i = 0; i < total; i++) {
|
||||
await doWork();
|
||||
|
||||
// Send progress update
|
||||
await server.notification({
|
||||
method: "notifications/progress",
|
||||
params: {
|
||||
progress: i,
|
||||
total: total,
|
||||
progressToken
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { toolResult: "Complete" };
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Common tool patterns
|
||||
|
||||
Here are some common patterns for implementing tools:
|
||||
|
||||
### System operations
|
||||
|
||||
Tools that interact with the local system:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: "execute_command",
|
||||
description: "Run a shell command",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
command: { type: "string" },
|
||||
args: { type: "array", items: { type: "string" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API integrations
|
||||
|
||||
Tools that wrap external APIs:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: "github_create_issue",
|
||||
description: "Create a GitHub issue",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: { type: "string" },
|
||||
body: { type: "string" },
|
||||
labels: { type: "array", items: { type: "string" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Data processing
|
||||
|
||||
Tools that transform or analyze data:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: "analyze_csv",
|
||||
description: "Analyze a CSV file",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
filepath: { type: "string" },
|
||||
operations: {
|
||||
type: "array",
|
||||
items: {
|
||||
enum: ["sum", "average", "count"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
When implementing tools:
|
||||
|
||||
1. Provide clear, descriptive names and descriptions
|
||||
2. Use detailed JSON Schema definitions for parameters
|
||||
3. Include examples in tool descriptions when helpful
|
||||
4. Implement proper error handling and validation
|
||||
5. Use progress reporting for long operations
|
||||
6. Keep tool operations focused and atomic
|
||||
7. Document expected return value structures
|
||||
8. Implement proper timeouts
|
||||
9. Consider rate limiting for resource-intensive operations
|
||||
10. Log tool usage for debugging and monitoring
|
||||
|
||||
## Security considerations
|
||||
|
||||
When exposing tools:
|
||||
|
||||
### Input validation
|
||||
|
||||
- Validate all parameters against the schema
|
||||
- Sanitize file paths and system commands
|
||||
- Validate URLs and external identifiers
|
||||
- Check parameter sizes and ranges
|
||||
- Prevent command injection
|
||||
|
||||
### Access control
|
||||
|
||||
- Implement authentication where needed
|
||||
- Use appropriate authorization checks
|
||||
- Audit tool usage
|
||||
- Rate limit requests
|
||||
- Monitor for abuse
|
||||
|
||||
### Error handling
|
||||
|
||||
- Don't expose internal errors to clients
|
||||
- Log security-relevant errors
|
||||
- Handle timeouts appropriately
|
||||
- Clean up resources after errors
|
||||
- Validate return values
|
||||
|
||||
## Tool discovery and updates
|
||||
|
||||
MCP supports dynamic tool discovery:
|
||||
|
||||
1. Clients can list available tools at any time
|
||||
2. Servers can notify clients when tools change using `notifications/tools/list_changed`
|
||||
3. Tools can be added or removed during runtime
|
||||
4. Tool definitions can be updated (though this should be done carefully)
|
||||
|
||||
## Common tool categories
|
||||
|
||||
Tools typically fall into these categories:
|
||||
|
||||
### System integration
|
||||
- File operations
|
||||
- Process management
|
||||
- System information
|
||||
- Network operations
|
||||
|
||||
### External services
|
||||
- API calls
|
||||
- Database operations
|
||||
- Authentication services
|
||||
- Cloud services
|
||||
|
||||
### Data processing
|
||||
- Format conversion
|
||||
- Data analysis
|
||||
- Content generation
|
||||
- Media processing
|
||||
|
||||
### Device control
|
||||
- Hardware interaction
|
||||
- Device configuration
|
||||
- Sensor reading
|
||||
- Output control
|
||||
|
||||
## Error handling
|
||||
|
||||
Tools should return clear error information:
|
||||
|
||||
```typescript
|
||||
try {
|
||||
// Tool operation
|
||||
} catch (error) {
|
||||
return {
|
||||
toolResult: {
|
||||
error: {
|
||||
code: "OPERATION_FAILED",
|
||||
message: "Failed to process request",
|
||||
details: error.message
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Testing tools
|
||||
|
||||
Consider these testing approaches:
|
||||
|
||||
1. Unit test parameter validation
|
||||
2. Mock external dependencies
|
||||
3. Test progress reporting
|
||||
4. Verify error handling
|
||||
5. Test concurrent execution
|
||||
6. Validate return values
|
||||
7. Test rate limiting
|
||||
8. Check resource cleanup
|
||||
9. Test timeout handling
|
||||
10. Verify security constraints
|
||||
278
docs/concepts/transport.mdx
Normal file
278
docs/concepts/transport.mdx
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
---
|
||||
title: "Transport"
|
||||
description: "Learn about MCP's communication mechanisms"
|
||||
---
|
||||
|
||||
Transport in the Model Context Protocol (MCP) provides the foundation for communication between clients and servers. It handles the underlying mechanics of how messages are sent and received.
|
||||
|
||||
## How transport works
|
||||
|
||||
MCP transport is built on a simple message-passing architecture where both clients and servers can send messages to each other. The transport layer is responsible for:
|
||||
|
||||
- Establishing connections
|
||||
- Sending messages
|
||||
- Receiving messages
|
||||
- Handling connection lifecycles
|
||||
- Managing errors
|
||||
- Ensuring reliable delivery
|
||||
|
||||
## Available transport types
|
||||
|
||||
MCP supports three main transport types:
|
||||
|
||||
### Standard input/output (stdio)
|
||||
|
||||
The stdio transport enables communication through standard input and output streams. This is particularly useful for local integrations and command-line tools.
|
||||
|
||||
```typescript
|
||||
// Server implementation
|
||||
const server = new Server({
|
||||
name: "example-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
|
||||
// Client implementation
|
||||
const client = new Client({
|
||||
name: "example-client",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
const transport = new StdioClientTransport({
|
||||
command: "./server",
|
||||
args: ["--option", "value"]
|
||||
});
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
||||
### WebSocket
|
||||
|
||||
WebSocket transport provides real-time, bidirectional communication over a persistent connection. This is ideal for web-based applications that need low-latency messaging.
|
||||
|
||||
```typescript
|
||||
// Server implementation
|
||||
const server = new Server({
|
||||
name: "example-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
const transport = new WebSocketServerTransport();
|
||||
await server.connect(transport);
|
||||
|
||||
// Client implementation
|
||||
const client = new Client({
|
||||
name: "example-client",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
const transport = new WebSocketClientTransport(
|
||||
new URL("ws://localhost:3000")
|
||||
);
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
||||
### Server-sent events (SSE)
|
||||
|
||||
SSE transport enables server-to-client streaming with HTTP POST requests for client-to-server communication. This is useful when WebSocket support isn't available.
|
||||
|
||||
```typescript
|
||||
// Server implementation
|
||||
const server = new Server({
|
||||
name: "example-server",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
const transport = new SSEServerTransport("/message", response);
|
||||
await server.connect(transport);
|
||||
|
||||
// Client implementation
|
||||
const client = new Client({
|
||||
name: "example-client",
|
||||
version: "1.0.0"
|
||||
});
|
||||
|
||||
const transport = new SSEClientTransport(
|
||||
new URL("http://localhost:3000/sse")
|
||||
);
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
||||
## Transport interface
|
||||
|
||||
All transport implementations must conform to the Transport interface:
|
||||
|
||||
```typescript
|
||||
interface Transport {
|
||||
// Start processing messages
|
||||
start(): Promise<void>;
|
||||
|
||||
// Send a JSON-RPC message
|
||||
send(message: JSONRPCMessage): Promise<void>;
|
||||
|
||||
// Close the connection
|
||||
close(): Promise<void>;
|
||||
|
||||
// Callbacks
|
||||
onclose?: () => void;
|
||||
onerror?: (error: Error) => void;
|
||||
onmessage?: (message: JSONRPCMessage) => void;
|
||||
}
|
||||
```
|
||||
|
||||
## Message format
|
||||
|
||||
Messages sent over MCP transport follow the JSON-RPC 2.0 specification. There are three types of messages:
|
||||
|
||||
### Requests
|
||||
|
||||
```typescript
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
id: number | string,
|
||||
method: string,
|
||||
params?: object
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
```typescript
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
id: number | string,
|
||||
result?: object,
|
||||
error?: {
|
||||
code: number,
|
||||
message: string,
|
||||
data?: unknown
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Notifications
|
||||
|
||||
```typescript
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
method: string,
|
||||
params?: object
|
||||
}
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
Transport implementations should handle various error scenarios:
|
||||
|
||||
1. Connection errors
|
||||
2. Message parsing errors
|
||||
3. Protocol errors
|
||||
4. Network timeouts
|
||||
5. Resource cleanup
|
||||
|
||||
Example error handling:
|
||||
|
||||
```typescript
|
||||
class ExampleTransport implements Transport {
|
||||
async start() {
|
||||
try {
|
||||
// Connection logic
|
||||
} catch (error) {
|
||||
this.onerror?.(new Error(`Failed to connect: ${error}`));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async send(message: JSONRPCMessage) {
|
||||
try {
|
||||
// Sending logic
|
||||
} catch (error) {
|
||||
this.onerror?.(new Error(`Failed to send message: ${error}`));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
When implementing or using MCP transport:
|
||||
|
||||
1. Handle connection lifecycle properly
|
||||
2. Implement proper error handling
|
||||
3. Clean up resources on connection close
|
||||
4. Use appropriate timeouts
|
||||
5. Validate messages before sending
|
||||
6. Log transport events for debugging
|
||||
7. Implement reconnection logic when appropriate
|
||||
8. Handle backpressure in message queues
|
||||
9. Monitor connection health
|
||||
10. Implement proper security measures
|
||||
|
||||
## Security considerations
|
||||
|
||||
When implementing transport:
|
||||
|
||||
### Authentication and authorization
|
||||
|
||||
- Implement proper authentication mechanisms
|
||||
- Validate client credentials
|
||||
- Use secure token handling
|
||||
- Implement authorization checks
|
||||
|
||||
### Data security
|
||||
|
||||
- Use TLS for network transport
|
||||
- Encrypt sensitive data
|
||||
- Validate message integrity
|
||||
- Implement message size limits
|
||||
- Sanitize input data
|
||||
|
||||
### Network security
|
||||
|
||||
- Implement rate limiting
|
||||
- Use appropriate timeouts
|
||||
- Handle denial of service scenarios
|
||||
- Monitor for unusual patterns
|
||||
- Implement proper firewall rules
|
||||
|
||||
## Debugging transport
|
||||
|
||||
Tips for debugging transport issues:
|
||||
|
||||
1. Enable debug logging
|
||||
2. Monitor message flow
|
||||
3. Check connection states
|
||||
4. Validate message formats
|
||||
5. Test error scenarios
|
||||
6. Use network analysis tools
|
||||
7. Implement health checks
|
||||
8. Monitor resource usage
|
||||
9. Test edge cases
|
||||
10. Use proper error tracking
|
||||
|
||||
## Transport selection guide
|
||||
|
||||
Choose the appropriate transport based on your needs:
|
||||
|
||||
### Use stdio when:
|
||||
|
||||
- Building command-line tools
|
||||
- Implementing local integrations
|
||||
- Needing simple process communication
|
||||
- Working with shell scripts
|
||||
|
||||
### Use WebSocket when:
|
||||
|
||||
- Real-time updates are needed
|
||||
- Low latency is required
|
||||
- Bidirectional communication is necessary
|
||||
- Working with web applications
|
||||
|
||||
### Use SSE when:
|
||||
|
||||
- Only server-to-client streaming is needed
|
||||
- WebSocket support is limited
|
||||
- Working with restricted networks
|
||||
- Implementing simple updates
|
||||
750
docs/first-server/python.mdx
Normal file
750
docs/first-server/python.mdx
Normal file
|
|
@ -0,0 +1,750 @@
|
|||
---
|
||||
title: "Python"
|
||||
description: "Create a simple MCP server in Python in 15 minutes"
|
||||
---
|
||||
|
||||
Let's build your first MCP server in Python! We'll create a weather server that provides current weather data as a resource and lets Claude fetch forecasts using tools.
|
||||
|
||||
<Note>
|
||||
This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Python">
|
||||
You'll need Python 3.9 or higher:
|
||||
|
||||
```bash
|
||||
python --version # Should be 3.9 or higher
|
||||
pip --version
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new project">
|
||||
```bash
|
||||
mkdir weather-server
|
||||
cd weather-server
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows use: venv\Scripts\activate
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install dependencies">
|
||||
```bash
|
||||
pip install modelcontextprotocol requests python-dotenv
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set up environment">
|
||||
Create `.env`:
|
||||
```bash
|
||||
OPENWEATHER_API_KEY=your-api-key-here
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Create your server
|
||||
|
||||
<Steps>
|
||||
<Step title="Create project structure">
|
||||
```bash
|
||||
touch weather_server.py
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the base code">
|
||||
In `weather_server.py`:
|
||||
|
||||
```python
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
from modelcontextprotocol import Server, StdioServerTransport
|
||||
from modelcontextprotocol.types import (
|
||||
ListResourcesRequest,
|
||||
ReadResourceRequest,
|
||||
ListToolsRequest,
|
||||
CallToolRequest,
|
||||
ErrorCode,
|
||||
McpError
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
API_KEY = os.getenv("OPENWEATHER_API_KEY")
|
||||
if not API_KEY:
|
||||
raise ValueError("OPENWEATHER_API_KEY environment variable is required")
|
||||
|
||||
API_CONFIG = {
|
||||
"BASE_URL": "http://api.openweathermap.org/data/2.5",
|
||||
"DEFAULT_CITY": "San Francisco",
|
||||
"ENDPOINTS": {
|
||||
"CURRENT": "weather",
|
||||
"FORECAST": "forecast"
|
||||
}
|
||||
}
|
||||
|
||||
class WeatherServer:
|
||||
def __init__(self):
|
||||
self.server = Server(
|
||||
name="example-weather-server",
|
||||
version="0.1.0"
|
||||
)
|
||||
|
||||
self.session = requests.Session()
|
||||
self.session.params = {
|
||||
"appid": API_KEY,
|
||||
"units": "metric"
|
||||
}
|
||||
|
||||
self.setup_handlers()
|
||||
self.setup_error_handling()
|
||||
|
||||
def setup_error_handling(self) -> None:
|
||||
def on_error(error: Exception) -> None:
|
||||
print("[MCP Error]", error, flush=True)
|
||||
|
||||
self.server.onerror = on_error
|
||||
|
||||
def setup_handlers(self) -> None:
|
||||
self.server.set_request_handler(
|
||||
ListResourcesRequest, self.handle_list_resources)
|
||||
self.server.set_request_handler(
|
||||
ReadResourceRequest, self.handle_read_resource)
|
||||
self.server.set_request_handler(
|
||||
ListToolsRequest, self.handle_list_tools)
|
||||
self.server.set_request_handler(
|
||||
CallToolRequest, self.handle_call_tool)
|
||||
|
||||
async def run(self) -> None:
|
||||
transport = StdioServerTransport()
|
||||
await self.server.connect(transport)
|
||||
print("Weather MCP server running on stdio", flush=True)
|
||||
|
||||
# Handler implementations will continue in next sections
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add resource handlers">
|
||||
Add these methods to the `WeatherServer` class:
|
||||
|
||||
```python
|
||||
async def handle_list_resources(
|
||||
self,
|
||||
request: ListResourcesRequest
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"resources": [{
|
||||
"uri": f"weather://{API_CONFIG['DEFAULT_CITY']}/current",
|
||||
"name": f"Current weather in {API_CONFIG['DEFAULT_CITY']}",
|
||||
"mimeType": "application/json",
|
||||
"description": "Real-time weather data including temperature, conditions, humidity, and wind speed"
|
||||
}]
|
||||
}
|
||||
|
||||
async def handle_read_resource(
|
||||
self,
|
||||
request: ReadResourceRequest
|
||||
) -> Dict[str, Any]:
|
||||
city = API_CONFIG["DEFAULT_CITY"]
|
||||
if request.params.uri != f"weather://{city}/current":
|
||||
raise McpError(
|
||||
ErrorCode.INVALID_REQUEST,
|
||||
f"Unknown resource: {request.params.uri}"
|
||||
)
|
||||
|
||||
try:
|
||||
response = self.session.get(
|
||||
f"{API_CONFIG['BASE_URL']}/{API_CONFIG['ENDPOINTS']['CURRENT']}",
|
||||
params={"q": city}
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
weather_data = {
|
||||
"temperature": data["main"]["temp"],
|
||||
"conditions": data["weather"][0]["description"],
|
||||
"humidity": data["main"]["humidity"],
|
||||
"wind_speed": data["wind"]["speed"],
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
return {
|
||||
"contents": [{
|
||||
"uri": request.params.uri,
|
||||
"mimeType": "application/json",
|
||||
"text": json.dumps(weather_data, indent=2)
|
||||
}]
|
||||
}
|
||||
except requests.RequestException as e:
|
||||
raise McpError(
|
||||
ErrorCode.INTERNAL_ERROR,
|
||||
f"Weather API error: {str(e)}"
|
||||
)
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add tool handlers">
|
||||
Add these methods to the `WeatherServer` class:
|
||||
|
||||
```python
|
||||
async def handle_list_tools(
|
||||
self,
|
||||
request: ListToolsRequest
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"tools": [{
|
||||
"name": "get_forecast",
|
||||
"description": "Get weather forecast for a city",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "City name"
|
||||
},
|
||||
"days": {
|
||||
"type": "number",
|
||||
"description": "Number of days (1-5)",
|
||||
"minimum": 1,
|
||||
"maximum": 5
|
||||
}
|
||||
},
|
||||
"required": ["city"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
async def handle_call_tool(
|
||||
self,
|
||||
request: CallToolRequest
|
||||
) -> Dict[str, Any]:
|
||||
if request.params.name != "get_forecast":
|
||||
raise McpError(
|
||||
ErrorCode.METHOD_NOT_FOUND,
|
||||
f"Unknown tool: {request.params.name}"
|
||||
)
|
||||
|
||||
args = request.params.arguments
|
||||
if not isinstance(args, dict) or "city" not in args:
|
||||
raise McpError(
|
||||
ErrorCode.INVALID_PARAMS,
|
||||
"Invalid forecast arguments"
|
||||
)
|
||||
|
||||
city = args["city"]
|
||||
days = min(int(args.get("days", 3)), 5)
|
||||
|
||||
try:
|
||||
response = self.session.get(
|
||||
f"{API_CONFIG['BASE_URL']}/{API_CONFIG['ENDPOINTS']['FORECAST']}",
|
||||
params={
|
||||
"q": city,
|
||||
"cnt": days * 8 # API returns 3-hour intervals
|
||||
}
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
forecasts = []
|
||||
for i in range(0, len(data["list"]), 8):
|
||||
day_data = data["list"][i]
|
||||
forecasts.append({
|
||||
"date": day_data["dt_txt"].split()[0],
|
||||
"temperature": day_data["main"]["temp"],
|
||||
"conditions": day_data["weather"][0]["description"]
|
||||
})
|
||||
|
||||
return {"toolResult": forecasts}
|
||||
except requests.RequestException as e:
|
||||
raise McpError(
|
||||
ErrorCode.INTERNAL_ERROR,
|
||||
f"Weather API error: {str(e)}"
|
||||
)
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the main entry point">
|
||||
Add this to the end of `weather_server.py`:
|
||||
|
||||
```python
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
import signal
|
||||
|
||||
async def main():
|
||||
server = WeatherServer()
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
print("Shutting down...", flush=True)
|
||||
asyncio.create_task(server.server.close())
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
await server.run()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Make the script executable">
|
||||
```bash
|
||||
chmod +x weather_server.py
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
Create `setup.py`:
|
||||
```python
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="weather-server",
|
||||
version="0.1.0",
|
||||
py_modules=["weather_server"],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"weather-server=weather_server:main",
|
||||
],
|
||||
},
|
||||
install_requires=[
|
||||
"modelcontextprotocol",
|
||||
"requests",
|
||||
"python-dotenv"
|
||||
],
|
||||
)
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding the code
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Type Hints">
|
||||
```python
|
||||
async def handle_read_resource(
|
||||
self,
|
||||
request: ReadResourceRequest
|
||||
) -> Dict[str, Any]:
|
||||
# ...
|
||||
```
|
||||
Python type hints help catch errors early and improve code maintainability.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Resources">
|
||||
```python
|
||||
async def handle_list_resources(
|
||||
self,
|
||||
request: ListResourcesRequest
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"resources": [{
|
||||
"uri": f"weather://{DEFAULT_CITY}/current",
|
||||
"name": f"Current weather in {DEFAULT_CITY}",
|
||||
"mimeType": "application/json"
|
||||
}]
|
||||
}
|
||||
```
|
||||
Resources provide data that Claude can access as context.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Tools">
|
||||
```python
|
||||
{
|
||||
"name": "get_forecast",
|
||||
"description": "Get weather forecast for a city",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": { "type": "string" },
|
||||
"days": { "type": "number" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Tools let Claude take actions through your server with validated inputs.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Best practices
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card
|
||||
title="Error Handling"
|
||||
icon="shield"
|
||||
>
|
||||
```python
|
||||
try:
|
||||
response = self.session.get(...)
|
||||
response.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
raise McpError(
|
||||
ErrorCode.INTERNAL_ERROR,
|
||||
f"API error: {str(e)}"
|
||||
)
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Type Validation"
|
||||
icon="check"
|
||||
>
|
||||
```python
|
||||
if not isinstance(args, dict) or "city" not in args:
|
||||
raise McpError(
|
||||
ErrorCode.INVALID_PARAMS,
|
||||
"Invalid forecast arguments"
|
||||
)
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Environment Variables"
|
||||
icon="gear"
|
||||
>
|
||||
```python
|
||||
if not API_KEY:
|
||||
raise ValueError("OPENWEATHER_API_KEY is required")
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Constants"
|
||||
icon="box"
|
||||
>
|
||||
```python
|
||||
API_CONFIG = {
|
||||
"BASE_URL": "...",
|
||||
"ENDPOINTS": {
|
||||
"CURRENT": "weather",
|
||||
"FORECAST": "forecast"
|
||||
}
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Available Transports
|
||||
|
||||
While this guide uses stdio transport, MCP supports multiple transport options:
|
||||
|
||||
### WebSocket
|
||||
```python
|
||||
from modelcontextprotocol import WebSocketServerTransport
|
||||
transport = WebSocketServerTransport()
|
||||
```
|
||||
|
||||
### SSE (Server-Sent Events)
|
||||
```python
|
||||
from modelcontextprotocol import SSEServerTransport
|
||||
transport = SSEServerTransport("/events", response)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Installation Issues
|
||||
```bash
|
||||
# Check Python version
|
||||
python --version
|
||||
|
||||
# Reinstall dependencies
|
||||
pip uninstall -r requirements.txt
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Runtime Errors
|
||||
```bash
|
||||
# Enable debug logging
|
||||
export MCP_LOG_LEVEL=DEBUG
|
||||
python weather_server.py
|
||||
```
|
||||
|
||||
### Type Checking
|
||||
```bash
|
||||
# Install mypy
|
||||
pip install mypy
|
||||
|
||||
# Run type checker
|
||||
mypy weather_server.py
|
||||
```
|
||||
|
||||
## Connect to Claude Desktop
|
||||
|
||||
<Steps>
|
||||
<Step title="Update Claude config">
|
||||
Add to `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"command": "weather-server",
|
||||
"env": {
|
||||
"OPENWEATHER_API_KEY": "your-api-key",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart Claude">
|
||||
1. Quit Claude completely
|
||||
2. Start Claude again
|
||||
3. Look for your weather server in the 🔌 menu
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Try it out!
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Check Current Weather" active>
|
||||
Ask Claude:
|
||||
```
|
||||
What's the current weather in San Francisco? Can you analyze the conditions and tell me if it's a good day for outdoor activities?
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Get a Forecast">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you get me a 5-day forecast for Tokyo and help me plan what clothes to pack for my trip?
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Compare Weather">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you analyze the forecast for both Tokyo and San Francisco and tell me which city would be better for outdoor photography this week?
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Advanced Features
|
||||
|
||||
<Steps>
|
||||
<Step title="Add caching">
|
||||
```python
|
||||
from functools import lru_cache
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class WeatherServer:
|
||||
def __init__(self):
|
||||
self._cache_timeout = timedelta(minutes=15)
|
||||
self._last_cache_time = None
|
||||
self._cached_weather = None
|
||||
|
||||
@lru_cache(maxsize=32)
|
||||
async def _fetch_weather(self, city: str) -> Dict[str, Any]:
|
||||
# Cache expires after 15 minutes
|
||||
now = datetime.now()
|
||||
if (self._cached_weather is None or
|
||||
self._last_cache_time is None or
|
||||
now - self._last_cache_time > self._cache_timeout):
|
||||
|
||||
response = self.session.get(...)
|
||||
self._cached_weather = response.json()
|
||||
self._last_cache_time = now
|
||||
|
||||
return self._cached_weather
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add progress notifications">
|
||||
```python
|
||||
async def handle_call_tool(
|
||||
self,
|
||||
request: CallToolRequest
|
||||
) -> Dict[str, Any]:
|
||||
if progress_token := request.params._meta.get("progressToken"):
|
||||
# Send progress notifications
|
||||
await self.server.notification({
|
||||
"method": "notifications/progress",
|
||||
"params": {
|
||||
"progress": 1,
|
||||
"total": 2,
|
||||
"progressToken": progress_token
|
||||
}
|
||||
})
|
||||
|
||||
# Fetch data...
|
||||
|
||||
await self.server.notification({
|
||||
"method": "notifications/progress",
|
||||
"params": {
|
||||
"progress": 2,
|
||||
"total": 2,
|
||||
"progressToken": progress_token
|
||||
}
|
||||
})
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add logging support">
|
||||
```python
|
||||
import logging
|
||||
|
||||
class WeatherServer:
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger("weather-server")
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
# Set up logging handler
|
||||
self.server.set_request_handler(
|
||||
SetLevelRequest,
|
||||
self.handle_set_log_level
|
||||
)
|
||||
|
||||
async def handle_set_log_level(
|
||||
self,
|
||||
request: SetLevelRequest
|
||||
) -> Dict[str, Any]:
|
||||
level = request.params.level.upper()
|
||||
self.logger.setLevel(getattr(logging, level))
|
||||
|
||||
await self.server.notification({
|
||||
"method": "notifications/message",
|
||||
"params": {
|
||||
"level": "info",
|
||||
"logger": "weather-server",
|
||||
"data": f"Log level set to {level}"
|
||||
}
|
||||
})
|
||||
return {}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add resource templates">
|
||||
```python
|
||||
async def handle_list_resources(
|
||||
self,
|
||||
request: ListResourcesRequest
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"resources": [...],
|
||||
"resourceTemplates": [{
|
||||
"uriTemplate": "weather://{city}/current",
|
||||
"name": "Current weather for any city",
|
||||
"mimeType": "application/json"
|
||||
}]
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Testing
|
||||
|
||||
<Steps>
|
||||
<Step title="Create test file">
|
||||
Create `test_weather_server.py`:
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
from weather_server import WeatherServer
|
||||
|
||||
@pytest.fixture
|
||||
async def server():
|
||||
server = WeatherServer()
|
||||
yield server
|
||||
await server.server.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_resources(server):
|
||||
request = Mock()
|
||||
result = await server.handle_list_resources(request)
|
||||
assert len(result["resources"]) > 0
|
||||
assert result["resources"][0]["mimeType"] == "application/json"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_forecast():
|
||||
with patch("requests.Session") as mock_session:
|
||||
mock_session.return_value.get.return_value.json.return_value = {
|
||||
"list": [{
|
||||
"dt_txt": "2024-01-01 12:00:00",
|
||||
"main": {"temp": 20},
|
||||
"weather": [{"description": "sunny"}]
|
||||
}]
|
||||
}
|
||||
|
||||
server = WeatherServer()
|
||||
request = Mock()
|
||||
request.params.name = "get_forecast"
|
||||
request.params.arguments = {"city": "Tokyo", "days": 1}
|
||||
|
||||
result = await server.handle_call_tool(request)
|
||||
assert len(result["toolResult"]) == 1
|
||||
assert result["toolResult"][0]["temperature"] == 20
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Run tests">
|
||||
```bash
|
||||
pip install pytest pytest-asyncio
|
||||
pytest test_weather_server.py -v
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Deployment
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card
|
||||
title="Docker"
|
||||
icon="docker"
|
||||
>
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["python", "weather_server.py"]
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Systemd Service"
|
||||
icon="server"
|
||||
>
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Weather MCP Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/path/to/venv/bin/python /path/to/weather_server.py
|
||||
Environment=OPENWEATHER_API_KEY=your-key-here
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Architecture overview"
|
||||
icon="sitemap"
|
||||
href="/docs/concepts/architecture"
|
||||
>
|
||||
Learn more about the MCP architecture
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Python SDK"
|
||||
icon="python"
|
||||
href="/api-reference/python/class-reference"
|
||||
>
|
||||
Read more about the Python SDK
|
||||
</Card>
|
||||
</CardGroup>
|
||||
631
docs/first-server/typescript.mdx
Normal file
631
docs/first-server/typescript.mdx
Normal file
|
|
@ -0,0 +1,631 @@
|
|||
---
|
||||
title: "TypeScript"
|
||||
description: "Create a simple MCP server in TypeScript in 15 minutes"
|
||||
---
|
||||
|
||||
Let's build your first MCP server in TypeScript! We'll create a weather server that provides current weather data as a resource and lets Claude fetch forecasts using tools.
|
||||
|
||||
<Note>
|
||||
This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Node.js">
|
||||
You'll need Node.js 18 or higher:
|
||||
|
||||
```bash
|
||||
node --version # Should be v18 or higher
|
||||
npm --version
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new project">
|
||||
```bash
|
||||
mkdir weather-server
|
||||
cd weather-server
|
||||
npm init -y
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install dependencies">
|
||||
```bash
|
||||
npm install @modelcontextprotocol/sdk axios dotenv typescript @types/node
|
||||
npm install --save-dev ts-node
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure TypeScript">
|
||||
Create `tsconfig.json`:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set up environment">
|
||||
Create `.env`:
|
||||
```bash
|
||||
OPENWEATHER_API_KEY=your-api-key-here
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Create your server
|
||||
|
||||
<Steps>
|
||||
<Step title="Create project structure">
|
||||
```bash
|
||||
mkdir src
|
||||
touch src/index.ts
|
||||
touch src/types.ts
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Define types">
|
||||
In `src/types.ts`:
|
||||
|
||||
```typescript
|
||||
export interface OpenWeatherResponse {
|
||||
main: {
|
||||
temp: number;
|
||||
humidity: number;
|
||||
};
|
||||
weather: Array<{
|
||||
description: string;
|
||||
}>;
|
||||
wind: {
|
||||
speed: number;
|
||||
};
|
||||
dt_txt?: string;
|
||||
}
|
||||
|
||||
export interface WeatherData {
|
||||
temperature: number;
|
||||
conditions: string;
|
||||
humidity: number;
|
||||
wind_speed: number;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface ForecastDay {
|
||||
date: string;
|
||||
temperature: number;
|
||||
conditions: string;
|
||||
}
|
||||
|
||||
export interface GetForecastArgs {
|
||||
city: string;
|
||||
days?: number;
|
||||
}
|
||||
|
||||
// Type guard for forecast arguments
|
||||
export function isValidForecastArgs(args: unknown): args is GetForecastArgs {
|
||||
return (
|
||||
typeof args === "object" &&
|
||||
args !== null &&
|
||||
"city" in args &&
|
||||
typeof args.city === "string" &&
|
||||
(args.days === undefined || typeof args.days === "number")
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the base code">
|
||||
In `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import {
|
||||
ListResourcesRequestSchema,
|
||||
ReadResourceRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
CallToolRequestSchema,
|
||||
ErrorCode,
|
||||
McpError
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import axios from "axios";
|
||||
import dotenv from "dotenv";
|
||||
import {
|
||||
WeatherData,
|
||||
ForecastDay,
|
||||
GetForecastArgs,
|
||||
OpenWeatherResponse,
|
||||
isValidForecastArgs
|
||||
} from "./types.js";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const API_KEY = process.env.OPENWEATHER_API_KEY;
|
||||
if (!API_KEY) {
|
||||
throw new Error("OPENWEATHER_API_KEY environment variable is required");
|
||||
}
|
||||
|
||||
const API_CONFIG = {
|
||||
BASE_URL: 'http://api.openweathermap.org/data/2.5',
|
||||
DEFAULT_CITY: 'San Francisco',
|
||||
ENDPOINTS: {
|
||||
CURRENT: 'weather',
|
||||
FORECAST: 'forecast'
|
||||
}
|
||||
} as const;
|
||||
|
||||
class WeatherServer {
|
||||
private server: Server;
|
||||
private axiosInstance;
|
||||
|
||||
constructor() {
|
||||
this.server = new Server({
|
||||
name: "example-weather-server",
|
||||
version: "0.1.0"
|
||||
});
|
||||
|
||||
// Configure axios with defaults
|
||||
this.axiosInstance = axios.create({
|
||||
baseURL: API_CONFIG.BASE_URL,
|
||||
params: {
|
||||
appid: API_KEY,
|
||||
units: "metric"
|
||||
}
|
||||
});
|
||||
|
||||
this.setupHandlers();
|
||||
this.setupErrorHandling();
|
||||
}
|
||||
|
||||
private setupErrorHandling(): void {
|
||||
this.server.onerror = (error) => {
|
||||
console.error("[MCP Error]", error);
|
||||
};
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
await this.server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
private setupHandlers(): void {
|
||||
this.setupResourceHandlers();
|
||||
this.setupToolHandlers();
|
||||
}
|
||||
|
||||
private setupResourceHandlers(): void {
|
||||
// Implementation continues in next section
|
||||
}
|
||||
|
||||
private setupToolHandlers(): void {
|
||||
// Implementation continues in next section
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
console.log("Weather MCP server running on stdio");
|
||||
}
|
||||
}
|
||||
|
||||
const server = new WeatherServer();
|
||||
server.run().catch(console.error);
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add resource handlers">
|
||||
Add this to the `setupHandlers` method:
|
||||
|
||||
```typescript
|
||||
private setupResourceHandlers(): void {
|
||||
this.server.setRequestHandler(
|
||||
ListResourcesRequestSchema,
|
||||
async () => ({
|
||||
resources: [{
|
||||
uri: `weather://${API_CONFIG.DEFAULT_CITY}/current`,
|
||||
name: `Current weather in ${API_CONFIG.DEFAULT_CITY}`,
|
||||
mimeType: "application/json",
|
||||
description: "Real-time weather data including temperature, conditions, humidity, and wind speed"
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
||||
this.server.setRequestHandler(
|
||||
ReadResourceRequestSchema,
|
||||
async (request) => {
|
||||
const city = API_CONFIG.DEFAULT_CITY;
|
||||
if (request.params.uri !== `weather://${city}/current`) {
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidRequest,
|
||||
`Unknown resource: ${request.params.uri}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.axiosInstance.get<OpenWeatherResponse>(
|
||||
API_CONFIG.ENDPOINTS.CURRENT,
|
||||
{
|
||||
params: { q: city }
|
||||
}
|
||||
);
|
||||
|
||||
const weatherData: WeatherData = {
|
||||
temperature: response.data.main.temp,
|
||||
conditions: response.data.weather[0].description,
|
||||
humidity: response.data.main.humidity,
|
||||
wind_speed: response.data.wind.speed,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
return {
|
||||
contents: [{
|
||||
uri: request.params.uri,
|
||||
mimeType: "application/json",
|
||||
text: JSON.stringify(weatherData, null, 2)
|
||||
}]
|
||||
};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Weather API error: ${error.response?.data.message ?? error.message}`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add tool handlers">
|
||||
Add these handlers to the `setupHandlers` method:
|
||||
|
||||
```typescript
|
||||
private setupToolHandlers(): void {
|
||||
this.server.setRequestHandler(
|
||||
ListToolsRequestSchema,
|
||||
async () => ({
|
||||
tools: [{
|
||||
name: "get_forecast",
|
||||
description: "Get weather forecast for a city",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
city: {
|
||||
type: "string",
|
||||
description: "City name"
|
||||
},
|
||||
days: {
|
||||
type: "number",
|
||||
description: "Number of days (1-5)",
|
||||
minimum: 1,
|
||||
maximum: 5
|
||||
}
|
||||
},
|
||||
required: ["city"]
|
||||
}
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
||||
this.server.setRequestHandler(
|
||||
CallToolRequestSchema,
|
||||
async (request) => {
|
||||
if (request.params.name !== "get_forecast") {
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
`Unknown tool: ${request.params.name}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!isValidForecastArgs(request.params.arguments)) {
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
"Invalid forecast arguments"
|
||||
);
|
||||
}
|
||||
|
||||
const city = request.params.arguments.city;
|
||||
const days = Math.min(request.params.arguments.days || 3, 5);
|
||||
|
||||
try {
|
||||
const response = await this.axiosInstance.get<{
|
||||
list: OpenWeatherResponse[]
|
||||
}>(API_CONFIG.ENDPOINTS.FORECAST, {
|
||||
params: {
|
||||
q: city,
|
||||
cnt: days * 8 // API returns 3-hour intervals
|
||||
}
|
||||
});
|
||||
|
||||
const forecasts: ForecastDay[] = [];
|
||||
for (let i = 0; i < response.data.list.length; i += 8) {
|
||||
const dayData = response.data.list[i];
|
||||
forecasts.push({
|
||||
date: dayData.dt_txt?.split(' ')[0] ?? new Date().toISOString().split('T')[0],
|
||||
temperature: dayData.main.temp,
|
||||
conditions: dayData.weather[0].description
|
||||
});
|
||||
}
|
||||
|
||||
return { toolResult: forecasts };
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Weather API error: ${error.response?.data.message ?? error.message}`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Build and test">
|
||||
Add these scripts to `package.json`:
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "ts-node src/index.ts"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
```bash
|
||||
npm run build
|
||||
npm install -g .
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Connect to Claude Desktop
|
||||
|
||||
<Steps>
|
||||
<Step title="Update Claude config">
|
||||
Add to `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"command": "weather-server",
|
||||
"env": {
|
||||
"OPENWEATHER_API_KEY": "your-api-key",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart Claude">
|
||||
1. Quit Claude completely
|
||||
2. Start Claude again
|
||||
3. Look for your weather server in the 🔌 menu
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Try it out!
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Check Current Weather" active>
|
||||
Ask Claude:
|
||||
```
|
||||
What's the current weather in San Francisco? Can you analyze the conditions?
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Get a Forecast">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella?
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Compare Weather">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you analyze the forecast for both Tokyo and San Francisco and tell me which city will be warmer this week?
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Understanding the code
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Type Safety">
|
||||
```typescript
|
||||
interface WeatherData {
|
||||
temperature: number;
|
||||
conditions: string;
|
||||
humidity: number;
|
||||
wind_speed: number;
|
||||
timestamp: string;
|
||||
}
|
||||
```
|
||||
TypeScript adds type safety to our MCP server, making it more reliable and easier to maintain.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Resources">
|
||||
```typescript
|
||||
this.server.setRequestHandler(
|
||||
ListResourcesRequestSchema,
|
||||
async () => ({
|
||||
resources: [{
|
||||
uri: `weather://${DEFAULT_CITY}/current`,
|
||||
name: `Current weather in ${DEFAULT_CITY}`,
|
||||
mimeType: "application/json"
|
||||
}]
|
||||
})
|
||||
);
|
||||
```
|
||||
Resources provide data that Claude can access as context.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Tools">
|
||||
```typescript
|
||||
{
|
||||
name: "get_forecast",
|
||||
description: "Get weather forecast for a city",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
city: { type: "string" },
|
||||
days: { type: "number" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Tools let Claude take actions through your server with type-safe inputs.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Best practices
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card
|
||||
title="Error Handling"
|
||||
icon="shield"
|
||||
>
|
||||
```typescript
|
||||
try {
|
||||
const response = await this.axiosInstance.get(...);
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`API error: ${error.response?.data.message}`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Type Guards"
|
||||
icon="check"
|
||||
>
|
||||
```typescript
|
||||
function isValidForecastArgs(args: unknown): args is GetForecastArgs {
|
||||
return (
|
||||
typeof args === "object" &&
|
||||
args !== null &&
|
||||
"city" in args &&
|
||||
typeof args.city === "string"
|
||||
);
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Environment Variables"
|
||||
icon="gear"
|
||||
>
|
||||
```typescript
|
||||
if (!process.env.OPENWEATHER_API_KEY) {
|
||||
throw new Error("OPENWEATHER_API_KEY is required");
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Constants"
|
||||
icon="box"
|
||||
>
|
||||
```typescript
|
||||
const API_CONFIG = {
|
||||
BASE_URL: '...',
|
||||
ENDPOINTS: {
|
||||
CURRENT: 'weather',
|
||||
FORECAST: 'forecast'
|
||||
}
|
||||
} as const;
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Available Transports
|
||||
|
||||
While this guide uses stdio transport, MCP supports multiple transport options:
|
||||
|
||||
### WebSocket
|
||||
```typescript
|
||||
import { WebSocketServerTransport } from "@modelcontextprotocol/sdk/server/websocket.js";
|
||||
const transport = new WebSocketServerTransport();
|
||||
```
|
||||
|
||||
### SSE (Server-Sent Events)
|
||||
```typescript
|
||||
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
||||
const transport = new SSEServerTransport("/events", response);
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Errors
|
||||
```bash
|
||||
# Check TypeScript version
|
||||
npx tsc --version
|
||||
|
||||
# Clean and rebuild
|
||||
rm -rf dist/
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Runtime Errors
|
||||
Look for detailed error messages in the Claude app logs:
|
||||
```bash
|
||||
# Launch Claude with logging
|
||||
open /Applications/Claude.app --stdout ~/Claude.log --stderr ~/Claude.log
|
||||
|
||||
# Monitor logs
|
||||
tail -f ~/Claude.log
|
||||
```
|
||||
|
||||
### Type Errors
|
||||
```bash
|
||||
# Check types without building
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Architecture overview"
|
||||
icon="sitemap"
|
||||
href="/docs/concepts/architecture"
|
||||
>
|
||||
Learn more about the MCP architecture
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="TypeScript SDK"
|
||||
icon="node-js"
|
||||
href="/api-reference/typescript/class-reference"
|
||||
>
|
||||
Read more about the TypeScript SDK
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
Need help? Ask Claude! Since it has access to the MCP SDK documentation, it can help you debug issues and suggest improvements to your server.
|
||||
</Note>
|
||||
|
|
@ -1,422 +0,0 @@
|
|||
---
|
||||
title: "Python"
|
||||
description: "Create a simple MCP server in Python in 15 minutes"
|
||||
---
|
||||
|
||||
Let's build your first MCP server in Python! We'll create a weather server that provides current weather data as a resource and lets Claude fetch forecasts using tools.
|
||||
|
||||
<Note>
|
||||
This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Python">
|
||||
You'll need Python 3.8 or higher:
|
||||
|
||||
```bash
|
||||
python3 --version # Should be 3.8 or higher
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set up a virtual environment">
|
||||
```bash
|
||||
# Create and activate virtual environment
|
||||
python3 -m venv mcp-weather
|
||||
cd mcp-weather
|
||||
source bin/activate # On Windows: .\Scripts\activate
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install MCP SDK">
|
||||
```bash
|
||||
pip install modelcontextprotocol-sdk requests
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Get API Key">
|
||||
1. Sign up at [OpenWeatherMap](https://openweathermap.org/api)
|
||||
2. Copy your API key
|
||||
3. Set it as an environment variable:
|
||||
```bash
|
||||
export OPENWEATHER_API_KEY='your-api-key'
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Create your server
|
||||
|
||||
<Steps>
|
||||
<Step title="Create project structure">
|
||||
```bash
|
||||
mkdir weather_server
|
||||
cd weather_server
|
||||
touch weather_server.py
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the base code">
|
||||
Open `weather_server.py` and add this starter code:
|
||||
|
||||
```python
|
||||
from modelcontextprotocol.server import Server
|
||||
from modelcontextprotocol.server.stdio import StdioServerTransport
|
||||
from modelcontextprotocol.types import (
|
||||
ListResourcesRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
ReadResourceRequestSchema,
|
||||
CallToolRequestSchema
|
||||
)
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
API_KEY = os.getenv('OPENWEATHER_API_KEY')
|
||||
BASE_URL = 'http://api.openweathermap.org/data/2.5'
|
||||
|
||||
class WeatherServer:
|
||||
def __init__(self):
|
||||
self.server = Server(
|
||||
name="example-weather-server",
|
||||
version="0.1.0"
|
||||
)
|
||||
self.default_city = "San Francisco"
|
||||
self.setup_handlers()
|
||||
|
||||
def setup_handlers(self):
|
||||
# We'll add handlers here
|
||||
pass
|
||||
|
||||
async def run(self):
|
||||
transport = StdioServerTransport()
|
||||
await self.server.connect(transport)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import asyncio
|
||||
server = WeatherServer()
|
||||
asyncio.run(server.run())
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add resource handlers">
|
||||
Let's add handlers to expose current weather as a resource:
|
||||
|
||||
```python
|
||||
def setup_handlers(self):
|
||||
self.server.set_request_handler(
|
||||
ListResourcesRequestSchema,
|
||||
self.handle_list_resources
|
||||
)
|
||||
self.server.set_request_handler(
|
||||
ReadResourceRequestSchema,
|
||||
self.handle_read_resource
|
||||
)
|
||||
|
||||
async def handle_list_resources(self, request):
|
||||
return {
|
||||
"resources": [{
|
||||
"uri": f"weather://{self.default_city}/current",
|
||||
"name": f"Current weather in {self.default_city}",
|
||||
"mimeType": "application/json"
|
||||
}]
|
||||
}
|
||||
|
||||
async def handle_read_resource(self, request):
|
||||
city = self.default_city
|
||||
if request.params.uri != f"weather://{city}/current":
|
||||
raise ValueError(f"Unknown resource: {request.params.uri}")
|
||||
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/weather",
|
||||
params={
|
||||
"q": city,
|
||||
"appid": API_KEY,
|
||||
"units": "metric"
|
||||
}
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
return {
|
||||
"contents": [{
|
||||
"uri": request.params.uri,
|
||||
"mimeType": "application/json",
|
||||
"text": json.dumps({
|
||||
"temperature": data["main"]["temp"],
|
||||
"conditions": data["weather"][0]["description"],
|
||||
"humidity": data["main"]["humidity"],
|
||||
"wind_speed": data["wind"]["speed"],
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}, indent=2)
|
||||
}]
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add tool handlers">
|
||||
Now let's add a tool to fetch weather forecasts:
|
||||
|
||||
```python
|
||||
def setup_handlers(self):
|
||||
# Add these lines to the existing method
|
||||
self.server.set_request_handler(
|
||||
ListToolsRequestSchema,
|
||||
self.handle_list_tools
|
||||
)
|
||||
self.server.set_request_handler(
|
||||
CallToolRequestSchema,
|
||||
self.handle_call_tool
|
||||
)
|
||||
|
||||
async def handle_list_tools(self, request):
|
||||
return {
|
||||
"tools": [{
|
||||
"name": "get_forecast",
|
||||
"description": "Get weather forecast for a city",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "City name"
|
||||
},
|
||||
"days": {
|
||||
"type": "number",
|
||||
"description": "Number of days (1-5)"
|
||||
}
|
||||
},
|
||||
"required": ["city"]
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
async def handle_call_tool(self, request):
|
||||
if request.params.name != "get_forecast":
|
||||
raise ValueError(f"Unknown tool: {request.params.name}")
|
||||
|
||||
args = request.params.arguments
|
||||
city = args.get("city", self.default_city)
|
||||
days = min(int(args.get("days", 3)), 5)
|
||||
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/forecast",
|
||||
params={
|
||||
"q": city,
|
||||
"appid": API_KEY,
|
||||
"units": "metric",
|
||||
"cnt": days * 8 # API returns 3-hour intervals
|
||||
}
|
||||
)
|
||||
data = response.json()
|
||||
|
||||
# Process and return daily forecasts
|
||||
forecasts = []
|
||||
for i in range(0, len(data["list"]), 8): # One day = 8 intervals
|
||||
day_data = data["list"][i]
|
||||
forecasts.append({
|
||||
"date": day_data["dt_txt"].split()[0],
|
||||
"temperature": day_data["main"]["temp"],
|
||||
"conditions": day_data["weather"][0]["description"]
|
||||
})
|
||||
|
||||
return {"toolResult": forecasts}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Test your server">
|
||||
1. Save all changes
|
||||
2. Run your server:
|
||||
```bash
|
||||
python weather_server.py
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Connect to Claude
|
||||
|
||||
<Steps>
|
||||
<Step title="Install the server">
|
||||
```bash
|
||||
# From your project directory
|
||||
pip install -e .
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update Claude config">
|
||||
Add this to your `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"command": "python",
|
||||
"args": ["-m", "weather_server"],
|
||||
"env": {
|
||||
"OPENWEATHER_API_KEY": "your-api-key",
|
||||
"HOME": "/Users/YOUR_USERNAME"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart Claude">
|
||||
1. Quit Claude completely
|
||||
2. Start Claude again
|
||||
3. Verify your weather server appears in the 🔌 menu
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Try it out!
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Check Current Weather" active>
|
||||
Ask Claude:
|
||||
```
|
||||
What's the current weather in San Francisco? Can you analyze the conditions?
|
||||
```
|
||||
|
||||
Claude will:
|
||||
1. Access the weather resource
|
||||
2. Parse the JSON data
|
||||
3. Provide a natural language summary
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Get a Forecast">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella?
|
||||
```
|
||||
|
||||
Claude will:
|
||||
1. Use the `get_forecast` tool
|
||||
2. Analyze the forecast data
|
||||
3. Give you packing advice
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Understanding the code
|
||||
|
||||
Let's break down the key MCP concepts used in this server:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Resources">
|
||||
```python
|
||||
async def handle_list_resources(self, request):
|
||||
return {
|
||||
"resources": [{
|
||||
"uri": f"weather://{self.default_city}/current",
|
||||
"name": f"Current weather in {self.default_city}",
|
||||
"mimeType": "application/json"
|
||||
}]
|
||||
}
|
||||
```
|
||||
Resources provide data that Claude can access as context. In this case, we're exposing current weather data that Claude can read and analyze.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Tools">
|
||||
```python
|
||||
async def handle_list_tools(self, request):
|
||||
return {
|
||||
"tools": [{
|
||||
"name": "get_forecast",
|
||||
"description": "Get weather forecast for a city",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {"type": "string"},
|
||||
"days": {"type": "number"}
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
Tools let Claude take actions through your server. The `inputSchema` defines what parameters Claude can provide.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Transport">
|
||||
```python
|
||||
async def run(self):
|
||||
transport = StdioServerTransport()
|
||||
await self.server.connect(transport)
|
||||
```
|
||||
The transport layer handles communication between Claude and your server. Here we're using stdio, but MCP also supports WebSocket and SSE.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Next steps
|
||||
|
||||
Now that you've built your first MCP server, try these enhancements:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Add More Resources"
|
||||
icon="database"
|
||||
>
|
||||
- Air quality data
|
||||
- Weather alerts
|
||||
- Historical weather
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Enhance Tools"
|
||||
icon="wrench"
|
||||
>
|
||||
- Compare multiple cities
|
||||
- Get detailed hourly forecasts
|
||||
- Save favorite locations
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Improve Error Handling"
|
||||
icon="shield"
|
||||
>
|
||||
- Handle API errors gracefully
|
||||
- Add input validation
|
||||
- Rate limiting
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Add Subscriptions"
|
||||
icon="bell"
|
||||
>
|
||||
- Weather alerts
|
||||
- Temperature changes
|
||||
- Condition updates
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Import Errors">
|
||||
```bash
|
||||
pip install modelcontextprotocol-sdk --upgrade
|
||||
```
|
||||
Ensure you're using the latest SDK version.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="API Key Issues">
|
||||
1. Verify your API key is set:
|
||||
```bash
|
||||
echo $OPENWEATHER_API_KEY
|
||||
```
|
||||
2. Check the API response for error messages
|
||||
3. Ensure you're within free tier limits
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Connection Issues">
|
||||
1. Check Claude's logs:
|
||||
```bash
|
||||
tail -f ~/Claude.log
|
||||
```
|
||||
2. Verify your config JSON syntax
|
||||
3. Ensure Python path is correct in config
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
<Note>
|
||||
Remember: This is just the beginning! MCP servers can provide any type of data or functionality to Claude. Think about what unique capabilities you could add to Claude through MCP.
|
||||
</Note>
|
||||
|
|
@ -1,533 +0,0 @@
|
|||
---
|
||||
title: "TypeScript"
|
||||
description: "Create a simple MCP server in TypeScript in 15 minutes"
|
||||
---
|
||||
|
||||
Let's build your first MCP server in TypeScript! We'll create a weather server that provides current weather data as a resource and lets Claude fetch forecasts using tools.
|
||||
|
||||
<Note>
|
||||
This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Install Node.js">
|
||||
You'll need Node.js 16 or higher:
|
||||
|
||||
```bash
|
||||
node --version # Should be v16 or higher
|
||||
npm --version
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new project">
|
||||
```bash
|
||||
mkdir weather-server
|
||||
cd weather-server
|
||||
npm init -y
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install dependencies">
|
||||
```bash
|
||||
npm install @modelcontextprotocol/sdk axios dotenv typescript @types/node
|
||||
npm install --save-dev ts-node
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure TypeScript">
|
||||
Create `tsconfig.json`:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set up environment">
|
||||
Create `.env`:
|
||||
```bash
|
||||
OPENWEATHER_API_KEY=your-api-key-here
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Create your server
|
||||
|
||||
<Steps>
|
||||
<Step title="Create project structure">
|
||||
```bash
|
||||
mkdir src
|
||||
touch src/index.ts
|
||||
touch src/types.ts
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Define types">
|
||||
In `src/types.ts`:
|
||||
|
||||
```typescript
|
||||
export interface WeatherData {
|
||||
temperature: number;
|
||||
conditions: string;
|
||||
humidity: number;
|
||||
wind_speed: number;
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface ForecastDay {
|
||||
date: string;
|
||||
temperature: number;
|
||||
conditions: string;
|
||||
}
|
||||
|
||||
export interface GetForecastArgs {
|
||||
city: string;
|
||||
days?: number;
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the base code">
|
||||
In `src/index.ts`:
|
||||
|
||||
```typescript
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import axios from "axios";
|
||||
import dotenv from "dotenv";
|
||||
import { WeatherData, ForecastDay, GetForecastArgs } from "./types.js";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const API_KEY = process.env.OPENWEATHER_API_KEY;
|
||||
const BASE_URL = 'http://api.openweathermap.org/data/2.5';
|
||||
const DEFAULT_CITY = 'San Francisco';
|
||||
|
||||
class WeatherServer {
|
||||
private server: Server;
|
||||
|
||||
constructor() {
|
||||
this.server = new Server({
|
||||
name: "example-weather-server",
|
||||
version: "0.1.0"
|
||||
});
|
||||
this.setupHandlers();
|
||||
}
|
||||
|
||||
private setupHandlers(): void {
|
||||
// We'll add handlers here
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
}
|
||||
}
|
||||
|
||||
const server = new WeatherServer();
|
||||
server.run().catch(console.error);
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add resource handlers">
|
||||
Add this to the `setupHandlers` method:
|
||||
|
||||
```typescript
|
||||
private setupHandlers(): void {
|
||||
this.server.setRequestHandler(
|
||||
ListResourcesRequestSchema,
|
||||
async () => ({
|
||||
resources: [{
|
||||
uri: `weather://${DEFAULT_CITY}/current`,
|
||||
name: `Current weather in ${DEFAULT_CITY}`,
|
||||
mimeType: "application/json"
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
||||
this.server.setRequestHandler(
|
||||
ReadResourceRequestSchema,
|
||||
async (request) => {
|
||||
const city = DEFAULT_CITY;
|
||||
if (request.params.uri !== `weather://${city}/current`) {
|
||||
throw new Error(`Unknown resource: ${request.params.uri}`);
|
||||
}
|
||||
|
||||
const response = await axios.get(`${BASE_URL}/weather`, {
|
||||
params: {
|
||||
q: city,
|
||||
appid: API_KEY,
|
||||
units: "metric"
|
||||
}
|
||||
});
|
||||
|
||||
const weatherData: WeatherData = {
|
||||
temperature: response.data.main.temp,
|
||||
conditions: response.data.weather[0].description,
|
||||
humidity: response.data.main.humidity,
|
||||
wind_speed: response.data.wind.speed,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
return {
|
||||
contents: [{
|
||||
uri: request.params.uri,
|
||||
mimeType: "application/json",
|
||||
text: JSON.stringify(weatherData, null, 2)
|
||||
}]
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add tool handlers">
|
||||
Add these handlers to the `setupHandlers` method:
|
||||
|
||||
```typescript
|
||||
this.server.setRequestHandler(
|
||||
ListToolsRequestSchema,
|
||||
async () => ({
|
||||
tools: [{
|
||||
name: "get_forecast",
|
||||
description: "Get weather forecast for a city",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
city: {
|
||||
type: "string",
|
||||
description: "City name"
|
||||
},
|
||||
days: {
|
||||
type: "number",
|
||||
description: "Number of days (1-5)"
|
||||
}
|
||||
},
|
||||
required: ["city"]
|
||||
}
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
||||
this.server.setRequestHandler(
|
||||
CallToolRequestSchema,
|
||||
async (request) => {
|
||||
if (request.params.name !== "get_forecast") {
|
||||
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||
}
|
||||
|
||||
const args = request.params.arguments as GetForecastArgs;
|
||||
const city = args.city || DEFAULT_CITY;
|
||||
const days = Math.min(args.days || 3, 5);
|
||||
|
||||
const response = await axios.get(`${BASE_URL}/forecast`, {
|
||||
params: {
|
||||
q: city,
|
||||
appid: API_KEY,
|
||||
units: "metric",
|
||||
cnt: days * 8 // API returns 3-hour intervals
|
||||
}
|
||||
});
|
||||
|
||||
const forecasts: ForecastDay[] = [];
|
||||
for (let i = 0; i < response.data.list.length; i += 8) {
|
||||
const dayData = response.data.list[i];
|
||||
forecasts.push({
|
||||
date: dayData.dt_txt.split(' ')[0],
|
||||
temperature: dayData.main.temp,
|
||||
conditions: dayData.weather[0].description
|
||||
});
|
||||
}
|
||||
|
||||
return { toolResult: forecasts };
|
||||
}
|
||||
);
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Build and test">
|
||||
Add these scripts to `package.json`:
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "ts-node src/index.ts"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
```bash
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Connect to Claude
|
||||
|
||||
<Steps>
|
||||
<Step title="Install globally">
|
||||
```bash
|
||||
npm install -g
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update Claude config">
|
||||
Add to `claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"weather": {
|
||||
"command": "weather-server",
|
||||
"env": {
|
||||
"OPENWEATHER_API_KEY": "your-api-key",
|
||||
"HOME": "/Users/YOUR_USERNAME"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart Claude">
|
||||
1. Quit Claude completely
|
||||
2. Start Claude again
|
||||
3. Look for your weather server in the 🔌 menu
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Try it out!
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Check Current Weather" active>
|
||||
Ask Claude:
|
||||
```
|
||||
What's the current weather in San Francisco? Can you analyze the conditions?
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Get a Forecast">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella?
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Compare Weather">
|
||||
Ask Claude:
|
||||
```
|
||||
Can you analyze the forecast for both Tokyo and San Francisco and tell me which city will be warmer this week?
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Understanding the code
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Type Safety">
|
||||
```typescript
|
||||
interface WeatherData {
|
||||
temperature: number;
|
||||
conditions: string;
|
||||
humidity: number;
|
||||
wind_speed: number;
|
||||
timestamp: string;
|
||||
}
|
||||
```
|
||||
TypeScript adds type safety to our MCP server, making it more reliable and easier to maintain.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Resources">
|
||||
```typescript
|
||||
this.server.setRequestHandler(
|
||||
ListResourcesRequestSchema,
|
||||
async () => ({
|
||||
resources: [{
|
||||
uri: `weather://${DEFAULT_CITY}/current`,
|
||||
name: `Current weather in ${DEFAULT_CITY}`,
|
||||
mimeType: "application/json"
|
||||
}]
|
||||
})
|
||||
);
|
||||
```
|
||||
Resources provide data that Claude can access as context.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Tools">
|
||||
```typescript
|
||||
{
|
||||
name: "get_forecast",
|
||||
description: "Get weather forecast for a city",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
city: { type: "string" },
|
||||
days: { type: "number" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Tools let Claude take actions through your server with type-safe inputs.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Best practices
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Error Handling"
|
||||
icon="shield"
|
||||
>
|
||||
```typescript
|
||||
try {
|
||||
const response = await axios.get(url);
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
throw new Error(`API error: ${error.response?.data.message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Type Guards"
|
||||
icon="check"
|
||||
>
|
||||
```typescript
|
||||
function isValidForecastArgs(args: unknown): args is GetForecastArgs {
|
||||
return typeof args === "object" && args !== null &&
|
||||
"city" in args;
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Environment Variables"
|
||||
icon="gear"
|
||||
>
|
||||
```typescript
|
||||
if (!process.env.OPENWEATHER_API_KEY) {
|
||||
throw new Error("OPENWEATHER_API_KEY is required");
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Constants"
|
||||
icon="box"
|
||||
>
|
||||
```typescript
|
||||
const API_ENDPOINTS = {
|
||||
CURRENT: "weather",
|
||||
FORECAST: "forecast"
|
||||
} as const;
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Enhancements
|
||||
|
||||
Try adding these features to your server:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card
|
||||
title="Resource Templates"
|
||||
icon="square-terminal"
|
||||
>
|
||||
Add dynamic city resources:
|
||||
```typescript
|
||||
uriTemplate: "weather://{city}/current"
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Caching"
|
||||
icon="database"
|
||||
>
|
||||
Cache API responses:
|
||||
```typescript
|
||||
import NodeCache from "node-cache"
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Rate Limiting"
|
||||
icon="gauge"
|
||||
>
|
||||
Add API rate limiting:
|
||||
```typescript
|
||||
import rateLimit from "axios-rate-limit"
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Build Errors">
|
||||
```bash
|
||||
# Check TypeScript version
|
||||
npx tsc --version
|
||||
|
||||
# Clean and rebuild
|
||||
rm -rf dist/
|
||||
npm run build
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Runtime Errors">
|
||||
Add better error logging:
|
||||
```typescript
|
||||
server.onerror = (error) => {
|
||||
console.error("[MCP Error]", error);
|
||||
};
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Type Errors">
|
||||
```bash
|
||||
# Check types without building
|
||||
npx tsc --noEmit
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Add WebSocket Transport"
|
||||
icon="bolt"
|
||||
href="/guides/websocket"
|
||||
>
|
||||
Switch to real-time updates with WebSocket transport
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Add Testing"
|
||||
icon="vial"
|
||||
href="/guides/testing"
|
||||
>
|
||||
Add Jest tests for your MCP server
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
Need help? Ask Claude! Since it has access to the MCP SDK documentation, it can help you debug issues and suggest improvements to your server.
|
||||
</Note>
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
---
|
||||
title: "Integrating MCP into Your LLM Application"
|
||||
description: 'Learn how to integrate the Model Context Protocol (MCP) into your LLM-powered applications'
|
||||
---
|
||||
|
||||
The Model Context Protocol (MCP) enables seamless integration between LLM applications and various data sources and tools. This guide will walk you through integrating MCP into your own LLM-powered chatbot application.
|
||||
|
||||
## Our Example Chatbot
|
||||
|
||||
For this guide, we'll be working with a hypothetical customer service chatbot for a software company. Our chatbot currently:
|
||||
|
||||
- Uses an LLM API (like Anthropic's Claude API) to generate responses
|
||||
- Maintains conversation history in memory
|
||||
- Has basic error handling and retry logic
|
||||
- Runs as a web service that customers interact with through a REST API
|
||||
|
||||
However, our chatbot faces some limitations:
|
||||
- It can't access real-time product documentation or knowledge base articles
|
||||
- It can't look up customer information or ticket history
|
||||
- It can't perform actions like creating tickets or updating customer records
|
||||
- It doesn't stay up-to-date with product changes and new features
|
||||
|
||||
By integrating MCP, we'll enhance our chatbot to:
|
||||
- Access documentation and knowledge base articles as contextual resources
|
||||
- Look up customer information through a CRM integration
|
||||
- Create and update support tickets through a ticketing system integration
|
||||
- Get real-time updates when documentation or product information changes
|
||||
|
||||
Let's get started!
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Clone the example MCP servers:
|
||||
```bash
|
||||
git clone https://github.com/modelcontextprotocol/example-servers.git
|
||||
cd example-servers
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Install the MCP SDK in your chatbot project:
|
||||
```bash
|
||||
npm install @modelcontextprotocol/sdk
|
||||
```
|
||||
|
||||
## Starting Point: Our Basic Chatbot
|
||||
|
||||
Here's our starting chatbot implementation that we'll enhance with MCP:
|
||||
|
||||
```typescript
|
||||
interface Message {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
class CustomerServiceBot {
|
||||
private conversation: Message[] = [];
|
||||
private llmClient: LLMClient; // Your LLM API client
|
||||
|
||||
constructor(llmClient: LLMClient) {
|
||||
this.llmClient = llmClient;
|
||||
}
|
||||
|
||||
async handleMessage(message: string): Promise<string> {
|
||||
// Add user message to conversation
|
||||
this.conversation.push({ role: 'user', content: message });
|
||||
|
||||
// Get LLM response
|
||||
const response = await this.llmClient.complete({
|
||||
messages: this.conversation,
|
||||
maxTokens: 1000
|
||||
});
|
||||
|
||||
// Add assistant response to conversation
|
||||
this.conversation.push({ role: 'assistant', content: response });
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 1: Adding Documentation Context
|
||||
|
||||
First, let's enhance our chatbot to incorporate product documentation. We'll use the example PostgreSQL server which can store and serve our docs.
|
||||
|
||||
```typescript
|
||||
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio";
|
||||
import {
|
||||
ListResourcesResultSchema,
|
||||
ReadResourceResultSchema
|
||||
} from "@modelcontextprotocol/sdk/types";
|
||||
|
||||
class EnhancedCustomerServiceBot {
|
||||
private conversation: Message[] = [];
|
||||
private llmClient: LLMClient;
|
||||
private mcpClient: Client;
|
||||
|
||||
constructor(llmClient: LLMClient) {
|
||||
this.llmClient = llmClient;
|
||||
|
||||
// Initialize MCP client
|
||||
this.mcpClient = new Client({
|
||||
name: "customer-service-bot",
|
||||
version: "1.0.0"
|
||||
});
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
// Connect to PostgreSQL server for documentation
|
||||
const transport = new StdioClientTransport({
|
||||
command: "./example-servers/postgres/index.js",
|
||||
args: ["postgres://localhost/docs"] // Your docs database
|
||||
});
|
||||
await this.mcpClient.connect(transport);
|
||||
}
|
||||
|
||||
async getRelevantDocs(message: string): Promise<string> {
|
||||
// List available documentation resources
|
||||
const { resources } = await this.mcpClient.request(
|
||||
{ method: "resources/list" },
|
||||
ListResourcesResultSchema
|
||||
);
|
||||
|
||||
// Read each documentation resource
|
||||
const docs = await Promise.all(
|
||||
resources.map(async (resource) => {
|
||||
const { contents } = await this.mcpClient.request(
|
||||
{
|
||||
method: "resources/read",
|
||||
params: { uri: resource.uri }
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
return contents[0].text;
|
||||
})
|
||||
);
|
||||
|
||||
return docs.join("\n\n");
|
||||
}
|
||||
|
||||
async handleMessage(message: string): Promise<string> {
|
||||
// Get relevant documentation
|
||||
const docs = await this.getRelevantDocs(message);
|
||||
|
||||
// Build prompt with documentation context
|
||||
const prompt = `
|
||||
Here is relevant documentation about our product:
|
||||
${docs}
|
||||
|
||||
Please use this information to help answer the customer's question.
|
||||
|
||||
Customer: ${message}
|
||||
`;
|
||||
|
||||
// Add user message to conversation
|
||||
this.conversation.push({ role: 'user', content: message });
|
||||
|
||||
// Get LLM response with documentation context
|
||||
const response = await this.llmClient.complete({
|
||||
messages: [...this.conversation, { role: 'user', content: prompt }],
|
||||
maxTokens: 1000
|
||||
});
|
||||
|
||||
// Add assistant response to conversation
|
||||
this.conversation.push({ role: 'assistant', content: response });
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 2: Adding Ticket Management
|
||||
|
||||
Now let's add the ability to create and update support tickets. We'll use the example tools server for this:
|
||||
|
||||
```typescript
|
||||
import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types";
|
||||
|
||||
interface Ticket {
|
||||
id: string;
|
||||
subject: string;
|
||||
description: string;
|
||||
status: 'open' | 'closed';
|
||||
}
|
||||
|
||||
class EnhancedCustomerServiceBot {
|
||||
// ... previous code ...
|
||||
|
||||
async createTicket(subject: string, description: string): Promise<Ticket> {
|
||||
const result = await this.mcpClient.request(
|
||||
{
|
||||
method: "tools/call",
|
||||
params: {
|
||||
name: "create_ticket",
|
||||
arguments: {
|
||||
subject,
|
||||
description
|
||||
}
|
||||
}
|
||||
},
|
||||
CallToolResultSchema
|
||||
);
|
||||
|
||||
return result.toolResult as Ticket;
|
||||
}
|
||||
|
||||
async handleMessage(message: string): Promise<string> {
|
||||
// Get relevant documentation
|
||||
const docs = await this.getRelevantDocs(message);
|
||||
|
||||
// Build prompt with documentation context and ticket creation ability
|
||||
const prompt = `
|
||||
Here is relevant documentation about our product:
|
||||
${docs}
|
||||
|
||||
You can create support tickets when needed using the createTicket function.
|
||||
Only create tickets for actual technical issues that need follow-up.
|
||||
|
||||
Customer: ${message}
|
||||
`;
|
||||
|
||||
// Get LLM response
|
||||
const response = await this.llmClient.complete({
|
||||
messages: [...this.conversation, { role: 'user', content: prompt }],
|
||||
maxTokens: 1000,
|
||||
tools: [{
|
||||
name: 'createTicket',
|
||||
description: 'Create a support ticket for technical issues',
|
||||
parameters: {
|
||||
subject: 'string',
|
||||
description: 'string'
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
// Check if the LLM wants to create a ticket
|
||||
if (response.toolCalls?.length > 0) {
|
||||
const call = response.toolCalls[0];
|
||||
const ticket = await this.createTicket(
|
||||
call.arguments.subject,
|
||||
call.arguments.description
|
||||
);
|
||||
|
||||
// Add ticket creation to conversation
|
||||
this.conversation.push({
|
||||
role: 'system',
|
||||
content: `Created ticket #${ticket.id}: ${ticket.subject}`
|
||||
});
|
||||
}
|
||||
|
||||
// Add messages to conversation
|
||||
this.conversation.push({ role: 'user', content: message });
|
||||
this.conversation.push({ role: 'assistant', content: response.content });
|
||||
|
||||
return response.content;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Step 3: Real-time Documentation Updates
|
||||
|
||||
Finally, let's make our chatbot aware of documentation changes in real-time:
|
||||
|
||||
```typescript
|
||||
class EnhancedCustomerServiceBot {
|
||||
// ... previous code ...
|
||||
|
||||
async initialize() {
|
||||
// Connect to PostgreSQL server
|
||||
const transport = new StdioClientTransport({
|
||||
command: "./example-servers/postgres/index.js",
|
||||
args: ["postgres://localhost/docs"]
|
||||
});
|
||||
await this.mcpClient.connect(transport);
|
||||
|
||||
// Subscribe to documentation updates
|
||||
const { resources } = await this.mcpClient.request(
|
||||
{ method: "resources/list" },
|
||||
ListResourcesResultSchema
|
||||
);
|
||||
|
||||
for (const resource of resources) {
|
||||
await this.mcpClient.request(
|
||||
{
|
||||
method: "resources/subscribe",
|
||||
params: { uri: resource.uri }
|
||||
},
|
||||
EmptyResultSchema
|
||||
);
|
||||
}
|
||||
|
||||
// Handle update notifications
|
||||
this.mcpClient.setNotificationHandler(
|
||||
ResourceUpdatedNotificationSchema,
|
||||
async (notification) => {
|
||||
console.log(`Documentation updated: ${notification.params.uri}`);
|
||||
// Optionally clear any cached documentation
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the Enhanced Chatbot
|
||||
|
||||
Here's how to use our enhanced chatbot:
|
||||
|
||||
```typescript
|
||||
const bot = new EnhancedCustomerServiceBot(llmClient);
|
||||
await bot.initialize();
|
||||
|
||||
// Example conversation
|
||||
const response1 = await bot.handleMessage(
|
||||
"How do I reset my password?"
|
||||
);
|
||||
// Bot responds with information from documentation
|
||||
|
||||
const response2 = await bot.handleMessage(
|
||||
"I can't log in even after resetting my password"
|
||||
);
|
||||
// Bot creates a ticket and responds with next steps
|
||||
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Add robust error handling around MCP operations:
|
||||
|
||||
```typescript
|
||||
class EnhancedCustomerServiceBot {
|
||||
// ... previous code ...
|
||||
|
||||
async getRelevantDocs(message: string): Promise<string> {
|
||||
try {
|
||||
const { resources } = await this.mcpClient.request(
|
||||
{ method: "resources/list" },
|
||||
ListResourcesResultSchema
|
||||
);
|
||||
|
||||
const docs = await Promise.all(
|
||||
resources.map(async (resource) => {
|
||||
try {
|
||||
const { contents } = await this.mcpClient.request(
|
||||
{
|
||||
method: "resources/read",
|
||||
params: { uri: resource.uri }
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
return contents[0].text;
|
||||
} catch (error) {
|
||||
console.error(`Failed to read resource ${resource.uri}:`, error);
|
||||
return ""; // Skip failed resources
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return docs.join("\n\n");
|
||||
} catch (error) {
|
||||
console.error("Failed to list resources:", error);
|
||||
return ""; // Fallback to no documentation
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've enhanced your chatbot with MCP, you can:
|
||||
|
||||
1. Add more integrations:
|
||||
- Connect to your actual documentation system
|
||||
- Integrate with your production ticketing system
|
||||
- Add CRM integration for customer context
|
||||
|
||||
2. Improve the implementation:
|
||||
- Add better documentation relevance filtering
|
||||
- Implement more sophisticated prompt engineering
|
||||
- Add authentication and access control
|
||||
- Add monitoring and logging
|
||||
- Implement caching for frequently accessed resources
|
||||
|
||||
3. Consider building custom MCP servers for your specific needs
|
||||
|
||||
The example servers repository includes other servers you can experiment with, and you can build your own servers to expose custom functionality to your chatbot.
|
||||
442
docs/unused/integrate-python.mdx
Normal file
442
docs/unused/integrate-python.mdx
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
---
|
||||
title: "Integrate MCP into Your Python App"
|
||||
description: "Add MCP support to your LLM application in Python"
|
||||
---
|
||||
|
||||
Learn how to integrate MCP into your Python LLM application. We'll build a simple web analyzer that uses Claude to analyze websites, enhanced with MCP's web automation capabilities.
|
||||
|
||||
<Note>
|
||||
This guide uses an existing MCP server ([puppeteer](/servers/puppeteer)) rather than building one from scratch. This lets us focus on the integration patterns.
|
||||
</Note>
|
||||
|
||||
## The example app
|
||||
|
||||
We'll build an "AI Web Analyzer" that:
|
||||
1. Takes a URL from the user
|
||||
2. Captures the webpage using MCP
|
||||
3. Has the LLM analyze the page's content, design, and accessibility
|
||||
|
||||
<Frame>
|
||||
<img src="/api/placeholder/800/400" alt="Screenshot of the Web Analyzer CLI interface" />
|
||||
</Frame>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Install dependencies">
|
||||
```bash
|
||||
pip install modelcontextprotocol-sdk anthropic click rich
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set up puppeteer server">
|
||||
```bash
|
||||
# Install example MCP servers
|
||||
git clone https://github.com/modelcontextprotocol/servers
|
||||
cd servers
|
||||
npm install
|
||||
npm run build
|
||||
npm install -g
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Get API key">
|
||||
```bash
|
||||
# Set your Anthropic API key
|
||||
export ANTHROPIC_API_KEY='your-key-here'
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Building the base app
|
||||
|
||||
First, let's create a basic version without MCP:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create project structure">
|
||||
```bash
|
||||
mkdir web-analyzer
|
||||
cd web-analyzer
|
||||
touch analyzer.py
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add base implementation">
|
||||
```python
|
||||
# analyzer.py
|
||||
import click
|
||||
import anthropic
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich import print as rprint
|
||||
|
||||
console = Console()
|
||||
client = anthropic.Client()
|
||||
|
||||
def analyze_url(url: str) -> str:
|
||||
messages = [{
|
||||
"role": "user",
|
||||
"content": f"Please analyze this website: {url}\n\n"
|
||||
f"I'd like to know about:\n"
|
||||
f"1. Content quality and organization\n"
|
||||
f"2. Visual design (based on the URL/description)\n"
|
||||
f"3. Potential improvements"
|
||||
}]
|
||||
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
max_tokens=1000,
|
||||
messages=messages
|
||||
)
|
||||
return response.content[0].text
|
||||
|
||||
@click.command()
|
||||
@click.argument('url')
|
||||
def main(url: str):
|
||||
with console.status("[bold green]Analyzing website..."):
|
||||
analysis = analyze_url(url)
|
||||
|
||||
rprint(Panel.fit(
|
||||
analysis,
|
||||
title="Website Analysis",
|
||||
border_style="blue"
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Test the base app">
|
||||
```bash
|
||||
python analyzer.py https://example.com
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Adding MCP support
|
||||
|
||||
Now let's enhance the app with MCP capabilities:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create MCP client">
|
||||
```python
|
||||
# At the top of analyzer.py
|
||||
from modelcontextprotocol.client import Client
|
||||
from modelcontextprotocol.client.stdio import StdioClientTransport
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
class MCPWebClient:
|
||||
def __init__(self):
|
||||
self.client = Client(
|
||||
client_info={
|
||||
"name": "web-analyzer",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
)
|
||||
self.transport = None
|
||||
|
||||
async def connect(self):
|
||||
self.transport = StdioClientTransport(
|
||||
server={
|
||||
"command": "mcp-server-puppeteer",
|
||||
}
|
||||
)
|
||||
await self.client.connect(self.transport)
|
||||
|
||||
async def navigate(self, url: str) -> Optional[str]:
|
||||
try:
|
||||
result = await self.client.request(
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "navigate",
|
||||
"arguments": {"url": url}
|
||||
}
|
||||
},
|
||||
schema=CallToolResultSchema
|
||||
)
|
||||
return result.get("toolResult")
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error navigating to URL: {e}")
|
||||
return None
|
||||
|
||||
async def get_console_logs(self) -> str:
|
||||
result = await self.client.request(
|
||||
{
|
||||
"method": "resources/read",
|
||||
"params": {"uri": "console://logs"}
|
||||
},
|
||||
schema=ReadResourceResultSchema
|
||||
)
|
||||
return result["contents"][0]["text"]
|
||||
|
||||
async def close(self):
|
||||
if self.transport:
|
||||
await self.client.close()
|
||||
```
|
||||
</Step>
|
||||
<Step title="Update the analyzer">
|
||||
```python
|
||||
async def analyze_url_with_mcp(url: str) -> str:
|
||||
# Create and connect MCP client
|
||||
mcp = MCPWebClient()
|
||||
await mcp.connect()
|
||||
# Navigate to the URL
|
||||
await mcp.navigate(url)
|
||||
|
||||
# Get console logs (includes errors, warnings, etc.)
|
||||
console_logs = await mcp.get_console_logs()
|
||||
|
||||
# Close the connection
|
||||
await mcp.close()
|
||||
|
||||
# Analyze with Claude
|
||||
messages = [{
|
||||
"role": "user",
|
||||
"content": (
|
||||
f"Please analyze this website: {url}\n\n"
|
||||
f"Here are the browser console logs:\n{console_logs}\n\n"
|
||||
f"Please analyze:\n"
|
||||
f"1. Content quality and organization\n"
|
||||
f"2. Visual design\n"
|
||||
f"3. Technical issues (based on console logs)\n"
|
||||
f"4. Potential improvements"
|
||||
)
|
||||
}]
|
||||
|
||||
response = client.messages.create(
|
||||
model="claude-3-opus-20240229",
|
||||
max_tokens=1000,
|
||||
messages=messages
|
||||
)
|
||||
return response.content[0].text
|
||||
|
||||
@click.command()
|
||||
@click.argument('url')
|
||||
def main(url: str):
|
||||
with console.status("[bold green]Analyzing website..."):
|
||||
analysis = asyncio.run(analyze_url_with_mcp(url))
|
||||
|
||||
rprint(Panel.fit(
|
||||
analysis,
|
||||
title="Website Analysis",
|
||||
border_style="blue"
|
||||
))
|
||||
```
|
||||
</Step>
|
||||
<Step title="Run the enhanced version">
|
||||
```bash
|
||||
python analyzer.py https://example.com
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding MCP integration
|
||||
|
||||
Let's break down the key components of MCP integration:
|
||||
<Tabs>
|
||||
<Tab title="Client Setup">
|
||||
```python
|
||||
class MCPWebClient:
|
||||
def __init__(self):
|
||||
self.client = Client(
|
||||
client_info={
|
||||
"name": "web-analyzer",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
)
|
||||
```
|
||||
The MCP client identifies your application and manages the connection to MCP servers.
|
||||
</Tab>
|
||||
<Tab title="Transport">
|
||||
```python
|
||||
self.transport = StdioClientTransport(
|
||||
server={
|
||||
"command": "mcp-server-puppeteer",
|
||||
}
|
||||
)
|
||||
```
|
||||
The transport layer handles communication with the MCP server. Here we're using stdio to talk to the puppeteer server.
|
||||
</Tab>
|
||||
<Tab title="Tools">
|
||||
```python
|
||||
result = await self.client.request(
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "navigate",
|
||||
"arguments": {"url": url}
|
||||
}
|
||||
},
|
||||
schema=CallToolResultSchema
|
||||
)
|
||||
```
|
||||
Tools let you take actions through MCP servers. Here we're using puppeteer's navigate tool.
|
||||
</Tab>
|
||||
<Tab title="Resources">
|
||||
```python
|
||||
result = await self.client.request(
|
||||
{
|
||||
"method": "resources/read",
|
||||
"params": {"uri": "console://logs"}
|
||||
},
|
||||
schema=ReadResourceResultSchema
|
||||
)
|
||||
```
|
||||
Resources provide data from MCP servers. Here we're reading console logs that puppeteer collected.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Enhancement ideas
|
||||
|
||||
Now that you have MCP working, here are some ways to enhance the analyzer:
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Visual Analysis"
|
||||
icon="image"
|
||||
>
|
||||
```python
|
||||
# Take screenshots with puppeteer
|
||||
await client.request({
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "screenshot",
|
||||
"arguments": {...}
|
||||
}
|
||||
})
|
||||
```
|
||||
</Card>
|
||||
<Card
|
||||
title="Multiple Pages"
|
||||
icon="layer-group">
|
||||
```python
|
||||
# Analyze multiple pages in sequence
|
||||
for url in urls:
|
||||
await navigate(url)
|
||||
await analyze()
|
||||
```
|
||||
</Card>
|
||||
<Card
|
||||
title="Interactive Elements"
|
||||
icon="mouse-pointer">
|
||||
```python
|
||||
# Test interactive features
|
||||
await client.request({
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
"name": "click",
|
||||
"arguments": {"selector": "button"}
|
||||
}
|
||||
})
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Common Patterns
|
||||
Here are some patterns you'll often use when integrating MCP:
|
||||
<AccordionGroup>
|
||||
<Accordion title="Resource Polling" active>
|
||||
When you need to wait for resource updates:
|
||||
```python
|
||||
async def wait_for_resource(uri: str, timeout: int = 30):
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
result = await client.request(
|
||||
{
|
||||
"method": "resources/read",
|
||||
"params": {"uri": uri}
|
||||
},
|
||||
schema=ReadResourceResultSchema
|
||||
)
|
||||
if some_condition(result):
|
||||
return result
|
||||
await asyncio.sleep(1)
|
||||
raise TimeoutError()
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Error Handling">
|
||||
Robust error handling for MCP operations:
|
||||
```python
|
||||
try:
|
||||
await client.request(...)
|
||||
except MCPError as e:
|
||||
if e.code == ErrorCode.ResourceNotFound:
|
||||
# Handle missing resource
|
||||
elif e.code == ErrorCode.InvalidRequest:
|
||||
# Handle invalid parameters
|
||||
else:
|
||||
# Handle other errors
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Resource Subscriptions">
|
||||
Subscribe to resource updates:
|
||||
```python
|
||||
await client.request(
|
||||
{
|
||||
"method": "resources/subscribe",
|
||||
"params": {"uri": "console://logs"}
|
||||
},
|
||||
schema=EmptyResultSchema
|
||||
)
|
||||
# Now you'll get notifications when logs change
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Connection Issues">
|
||||
```python
|
||||
# Enable debug logging
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
```
|
||||
Watch for connection errors and MCP protocol messages.
|
||||
</Accordion>
|
||||
<Accordion title="Resource Errors">
|
||||
1. Check if the resource exists:
|
||||
```python
|
||||
resources = await client.request(
|
||||
{"method": "resources/list"},
|
||||
schema=ListResourcesResultSchema
|
||||
)
|
||||
print(resources)
|
||||
```
|
||||
2. Verify URI format
|
||||
3. Check server logs
|
||||
</Accordion>
|
||||
<Accordion title="Tool Errors">
|
||||
1. List available tools:
|
||||
```python
|
||||
tools = await client.request(
|
||||
{"method": "tools/list"},
|
||||
schema=ListToolsResultSchema
|
||||
)
|
||||
print(tools)
|
||||
```
|
||||
2. Check argument schema
|
||||
3. Verify server capabilities
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Add More Servers"
|
||||
icon="plus"
|
||||
href="/servers"
|
||||
>
|
||||
Connect to additional MCP servers for more capabilities
|
||||
</Card>
|
||||
<Card
|
||||
title="Build Custom Server"
|
||||
icon="code"
|
||||
href="/docs/guides/first-server-python">
|
||||
Create your own MCP server for custom functionality
|
||||
</Card>
|
||||
</CardGroup>
|
||||
885
docs/unused/integrate-ts.mdx
Normal file
885
docs/unused/integrate-ts.mdx
Normal file
|
|
@ -0,0 +1,885 @@
|
|||
---
|
||||
title: "Integrate MCP into Your TypeScript App"
|
||||
description: "Add MCP support to your LLM application in TypeScript"
|
||||
---
|
||||
|
||||
Learn how to integrate MCP into your TypeScript/React application. We'll build an AI Web Analyzer that uses Claude to analyze websites, enhanced with MCP's web automation capabilities.
|
||||
|
||||
<Note>
|
||||
This guide uses an existing MCP server ([puppeteer](/servers/puppeteer)) rather than building one from scratch. This lets us focus on the integration patterns.
|
||||
</Note>
|
||||
|
||||
## The example app
|
||||
|
||||
We'll build an "AI Web Analyzer" that:
|
||||
1. Takes a URL from the user
|
||||
2. Captures the webpage using MCP
|
||||
3. Has Claude analyze the page's content, design, and accessibility
|
||||
|
||||
<Frame>
|
||||
<img src="/api/placeholder/800/400" alt="Screenshot of the Web Analyzer interface" />
|
||||
</Frame>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Create React project">
|
||||
```bash
|
||||
# Create new Next.js project with TypeScript
|
||||
npx create-next-app@latest web-analyzer --typescript --tailwind
|
||||
cd web-analyzer
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install dependencies">
|
||||
```bash
|
||||
npm install @modelcontextprotocol/sdk @anthropic-ai/sdk
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Set up puppeteer server">
|
||||
```bash
|
||||
# Install example MCP servers
|
||||
git clone https://github.com/modelcontextprotocol/servers
|
||||
cd servers
|
||||
npm install
|
||||
npm run build
|
||||
npm install -g
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Building the base app
|
||||
|
||||
First, let's create a basic version without MCP:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create components">
|
||||
Create `components/WebAnalyzer.tsx`:
|
||||
|
||||
```tsx
|
||||
import { useState } from 'react';
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY
|
||||
});
|
||||
|
||||
export default function WebAnalyzer() {
|
||||
const [url, setUrl] = useState('');
|
||||
const [analysis, setAnalysis] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const analyzeUrl = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await anthropic.messages.create({
|
||||
model: 'claude-3-opus-20240229',
|
||||
max_tokens: 1000,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: `Please analyze this website: ${url}\n\n`
|
||||
+ `I'd like to know about:\n`
|
||||
+ `1. Content quality and organization\n`
|
||||
+ `2. Visual design (based on URL/description)\n`
|
||||
+ `3. Potential improvements`
|
||||
}]
|
||||
});
|
||||
setAnalysis(response.content[0].text);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
setAnalysis('Error analyzing website');
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto p-4">
|
||||
<h1 className="text-2xl font-bold mb-4">
|
||||
AI Web Analyzer
|
||||
</h1>
|
||||
|
||||
<div className="flex gap-2 mb-4">
|
||||
<input
|
||||
type="url"
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
placeholder="Enter website URL"
|
||||
className="flex-1 p-2 border rounded"
|
||||
/>
|
||||
<button
|
||||
onClick={analyzeUrl}
|
||||
disabled={loading}
|
||||
className="px-4 py-2 bg-blue-500 text-white rounded"
|
||||
>
|
||||
Analyze
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{loading && (
|
||||
<div className="animate-pulse text-gray-500">
|
||||
Analyzing website...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{analysis && (
|
||||
<div className="mt-4 p-4 border rounded bg-gray-50">
|
||||
<pre className="whitespace-pre-wrap">{analysis}</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update app page">
|
||||
Update `app/page.tsx`:
|
||||
|
||||
```tsx
|
||||
import WebAnalyzer from '@/components/WebAnalyzer';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main>
|
||||
<WebAnalyzer />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add environment variables">
|
||||
Create `.env.local`:
|
||||
|
||||
```bash
|
||||
NEXT_PUBLIC_ANTHROPIC_API_KEY=your-key-here
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Adding MCP support
|
||||
|
||||
Now let's enhance the app with MCP capabilities:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create MCP client">
|
||||
Create `lib/mcp.ts`:
|
||||
|
||||
```typescript
|
||||
import { Client } from '@modelcontextprotocol/sdk/client';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio';
|
||||
import {
|
||||
CallToolResultSchema,
|
||||
ReadResourceResultSchema
|
||||
} from '@modelcontextprotocol/sdk/types';
|
||||
|
||||
export class MCPWebClient {
|
||||
private client: Client;
|
||||
private transport: StdioClientTransport | null = null;
|
||||
|
||||
constructor() {
|
||||
this.client = new Client({
|
||||
name: 'web-analyzer',
|
||||
version: '0.1.0'
|
||||
});
|
||||
}
|
||||
|
||||
async connect() {
|
||||
this.transport = new StdioClientTransport({
|
||||
command: 'mcp-server-puppeteer'
|
||||
});
|
||||
await this.client.connect(this.transport);
|
||||
}
|
||||
|
||||
async navigate(url: string): Promise<string | null> {
|
||||
try {
|
||||
const result = await this.client.request(
|
||||
{
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'navigate',
|
||||
arguments: { url }
|
||||
}
|
||||
},
|
||||
CallToolResultSchema
|
||||
);
|
||||
return result.toolResult as string;
|
||||
} catch (error) {
|
||||
console.error('Error navigating:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async getConsoleLogs(): Promise<string> {
|
||||
const result = await this.client.request(
|
||||
{
|
||||
method: 'resources/read',
|
||||
params: { uri: 'console://logs' }
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
return result.contents[0].text;
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.transport) {
|
||||
await this.client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create MCP context">
|
||||
Create `contexts/MCPContext.tsx`:
|
||||
|
||||
```typescript
|
||||
import { createContext, useContext, useEffect, useRef } from 'react';
|
||||
import { MCPWebClient } from '@/lib/mcp';
|
||||
|
||||
const MCPContext = createContext<MCPWebClient | null>(null);
|
||||
|
||||
export function MCPProvider({ children }: { children: React.ReactNode }) {
|
||||
const clientRef = useRef<MCPWebClient | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const initMCP = async () => {
|
||||
clientRef.current = new MCPWebClient();
|
||||
await clientRef.current.connect();
|
||||
};
|
||||
|
||||
initMCP();
|
||||
|
||||
return () => {
|
||||
if (clientRef.current) {
|
||||
clientRef.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MCPContext.Provider value={clientRef.current}>
|
||||
{children}
|
||||
</MCPContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useMCP() {
|
||||
const context = useContext(MCPContext);
|
||||
if (!context) {
|
||||
throw new Error('useMCP must be used within MCPProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update the analyzer component">
|
||||
Update `components/WebAnalyzer.tsx`:
|
||||
|
||||
```typescript
|
||||
import { useState } from 'react';
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { useMCP } from '@/contexts/MCPContext';
|
||||
|
||||
const anthropic = new Anthropic({
|
||||
apiKey: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY
|
||||
});
|
||||
|
||||
export default function WebAnalyzer() {
|
||||
const mcp = useMCP();
|
||||
const [url, setUrl] = useState('');
|
||||
const [analysis, setAnalysis] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const analyzeUrl = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// Navigate to the URL with MCP
|
||||
await mcp.navigate(url);
|
||||
|
||||
// Get console logs
|
||||
const consoleLogs = await mcp.getConsoleLogs();
|
||||
|
||||
// Analyze with Claude
|
||||
const response = await anthropic.messages.create({
|
||||
model: 'claude-3-opus-20240229',
|
||||
max_tokens: 1000,
|
||||
messages: [{
|
||||
role: 'user',
|
||||
content: `Please analyze this website: ${url}\n\n`
|
||||
+ `Here are the browser console logs:\n${consoleLogs}\n\n`
|
||||
+ `Please analyze:\n`
|
||||
+ `1. Content quality and organization\n`
|
||||
+ `2. Visual design\n`
|
||||
+ `3. Technical issues (based on console logs)\n`
|
||||
+ `4. Potential improvements`
|
||||
}]
|
||||
});
|
||||
setAnalysis(response.content[0].text);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
setAnalysis('Error analyzing website');
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
// ... rest of the component remains the same
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Add the provider">
|
||||
Update `app/layout.tsx`:
|
||||
|
||||
```typescript
|
||||
import { MCPProvider } from '@/contexts/MCPContext';
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<MCPProvider>
|
||||
{children}
|
||||
</MCPProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Understanding MCP integration
|
||||
|
||||
Let's break down the key components:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Client Setup">
|
||||
```typescript
|
||||
export class MCPWebClient {
|
||||
private client: Client;
|
||||
|
||||
constructor() {
|
||||
this.client = new Client({
|
||||
name: 'web-analyzer',
|
||||
version: '0.1.0'
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
The MCP client manages the connection to MCP servers and handles requests.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Context Provider">
|
||||
```typescript
|
||||
export function MCPProvider({ children }: { children: React.ReactNode }) {
|
||||
const clientRef = useRef<MCPWebClient | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const initMCP = async () => {
|
||||
clientRef.current = new MCPWebClient();
|
||||
await clientRef.current.connect();
|
||||
};
|
||||
|
||||
initMCP();
|
||||
// ... cleanup
|
||||
}, []);
|
||||
}
|
||||
```
|
||||
The context provider manages the MCP client lifecycle.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Tools">
|
||||
```typescript
|
||||
async navigate(url: string): Promise<string | null> {
|
||||
const result = await this.client.request(
|
||||
{
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'navigate',
|
||||
arguments: { url }
|
||||
}
|
||||
},
|
||||
CallToolResultSchema
|
||||
);
|
||||
return result.toolResult as string;
|
||||
}
|
||||
```
|
||||
Tools let you take actions through MCP servers.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Resources">
|
||||
```typescript
|
||||
async getConsoleLogs(): Promise<string> {
|
||||
const result = await this.client.request(
|
||||
{
|
||||
method: 'resources/read',
|
||||
params: { uri: 'console://logs' }
|
||||
},
|
||||
ReadResourceResultSchema
|
||||
);
|
||||
return result.contents[0].text;
|
||||
}
|
||||
```
|
||||
Resources provide data from MCP servers.
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Enhancement ideas
|
||||
|
||||
Here are some ways to enhance the analyzer:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Visual Snapshots"
|
||||
icon="image"
|
||||
>
|
||||
```typescript
|
||||
// Add screenshot capability
|
||||
async function captureScreenshot() {
|
||||
const result = await mcp.client.request({
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'screenshot',
|
||||
arguments: { /* ... */ }
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Batch Analysis"
|
||||
icon="layer-group"
|
||||
>
|
||||
```typescript
|
||||
// Analyze multiple pages
|
||||
async function analyzeBatch(urls: string[]) {
|
||||
const results = [];
|
||||
for (const url of urls) {
|
||||
await mcp.navigate(url);
|
||||
// ... analyze
|
||||
}
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Interactive Testing"
|
||||
icon="mouse-pointer"
|
||||
>
|
||||
```typescript
|
||||
// Test interactive elements
|
||||
async function testInteraction() {
|
||||
await mcp.client.request({
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'click',
|
||||
arguments: { selector: 'button' }
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Performance Analysis"
|
||||
icon="gauge"
|
||||
>
|
||||
```typescript
|
||||
// Add performance metrics
|
||||
async function getPerformance() {
|
||||
const metrics = await mcp.client.request({
|
||||
method: 'resources/read',
|
||||
params: { uri: 'metrics://performance' }
|
||||
});
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Common patterns
|
||||
|
||||
Here are some patterns you'll often use with MCP in React:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Resource Polling" active>
|
||||
```typescript
|
||||
function useResourcePolling(uri: string, interval = 1000) {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
async function poll() {
|
||||
while (mounted) {
|
||||
const result = await mcp.client.request({
|
||||
method: 'resources/read',
|
||||
params: { uri }
|
||||
});
|
||||
if (mounted) {
|
||||
setData(result);
|
||||
await new Promise(r => setTimeout(r, interval));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
poll();
|
||||
return () => { mounted = false; };
|
||||
}, [uri, interval]);
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Error Boundaries">
|
||||
```typescript
|
||||
class MCPErrorBoundary extends React.Component {
|
||||
state = { hasError: false };
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
if (error instanceof MCPError) {
|
||||
// Handle MCP-specific errors
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return <ErrorDisplay />;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Resource Subscriptions">
|
||||
```typescript
|
||||
function useResourceSubscription(uri: string) {
|
||||
useEffect(() => {
|
||||
mcp.client.request({
|
||||
method: 'resources/subscribe',
|
||||
params: { uri }
|
||||
});
|
||||
|
||||
return () => {
|
||||
mcp.client.request({
|
||||
method: 'resources/unsubscribe',
|
||||
params: { uri }
|
||||
});
|
||||
};
|
||||
}, [uri]);
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Connection Issues">
|
||||
```typescript
|
||||
// Enable debug logging
|
||||
const initMCP = async () => {
|
||||
const client = new MCPWebClient({
|
||||
debug: true, // Enable debug logging
|
||||
onError: (error) => {
|
||||
console.error('MCP Error:', error);
|
||||
}
|
||||
});
|
||||
await client.connect();
|
||||
};
|
||||
```
|
||||
Watch the browser console for connection errors and protocol messages.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="React Component Lifecycle">
|
||||
Common issues and solutions:
|
||||
```typescript
|
||||
function WebAnalyzer() {
|
||||
// Don't create client on every render
|
||||
const mcpRef = useRef<MCPWebClient | null>(null);
|
||||
|
||||
// Initialize in useEffect
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
mcpRef.current = new MCPWebClient();
|
||||
await mcpRef.current.connect();
|
||||
};
|
||||
init();
|
||||
|
||||
// Clean up properly
|
||||
return () => {
|
||||
if (mcpRef.current) {
|
||||
mcpRef.current.close();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Type Safety">
|
||||
Use TypeScript to catch MCP issues early:
|
||||
```typescript
|
||||
// Define expected response types
|
||||
interface ToolResponse {
|
||||
toolResult: {
|
||||
screenshot?: string;
|
||||
logs?: string[];
|
||||
error?: string;
|
||||
};
|
||||
}
|
||||
|
||||
// Use with request
|
||||
const result = await client.request<ToolResponse>({
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'screenshot',
|
||||
arguments: { /* ... */ }
|
||||
}
|
||||
});
|
||||
|
||||
// TypeScript will catch missing properties
|
||||
console.log(result.toolResult.screenshot);
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Performance optimization
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Caching Resources"
|
||||
icon="database"
|
||||
>
|
||||
```typescript
|
||||
function useCachedResource(uri: string) {
|
||||
const [data, setData] = useState<string | null>(null);
|
||||
const cache = useRef(new Map<string, string>());
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchResource() {
|
||||
if (cache.current.has(uri)) {
|
||||
setData(cache.current.get(uri)!);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await mcp.client.request({
|
||||
method: 'resources/read',
|
||||
params: { uri }
|
||||
});
|
||||
|
||||
cache.current.set(uri, result.contents[0].text);
|
||||
setData(result.contents[0].text);
|
||||
}
|
||||
|
||||
fetchResource();
|
||||
}, [uri]);
|
||||
|
||||
return data;
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Batching Requests"
|
||||
icon="layer-group"
|
||||
>
|
||||
```typescript
|
||||
class BatchedMCPClient extends MCPWebClient {
|
||||
private batchQueue: Map<string, Promise<any>>;
|
||||
|
||||
async request(method: string, params: any) {
|
||||
const key = JSON.stringify({ method, params });
|
||||
|
||||
if (!this.batchQueue.has(key)) {
|
||||
this.batchQueue.set(
|
||||
key,
|
||||
super.request(method, params)
|
||||
);
|
||||
|
||||
// Clear after resolution
|
||||
this.batchQueue.get(key)!.finally(() => {
|
||||
this.batchQueue.delete(key);
|
||||
});
|
||||
}
|
||||
|
||||
return this.batchQueue.get(key);
|
||||
}
|
||||
}
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Building for production
|
||||
|
||||
<Steps>
|
||||
<Step title="Error handling">
|
||||
Add proper error boundaries:
|
||||
|
||||
```typescript
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
|
||||
function ErrorFallback({ error, resetErrorBoundary }) {
|
||||
return (
|
||||
<div className="p-4 border border-red-500 rounded">
|
||||
<h2 className="text-red-500">Something went wrong:</h2>
|
||||
<pre>{error.message}</pre>
|
||||
<button
|
||||
onClick={resetErrorBoundary}
|
||||
className="mt-2 px-4 py-2 bg-red-500 text-white rounded"
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
FallbackComponent={ErrorFallback}
|
||||
onReset={() => {
|
||||
// Reset application state
|
||||
}}
|
||||
>
|
||||
<WebAnalyzer />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Loading states">
|
||||
Add proper loading indicators:
|
||||
|
||||
```typescript
|
||||
function WebAnalyzer() {
|
||||
const [state, setState] = useState<{
|
||||
status: 'idle' | 'loading' | 'success' | 'error';
|
||||
error?: Error;
|
||||
data?: string;
|
||||
}>({
|
||||
status: 'idle'
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{state.status === 'loading' && (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
||||
<div className="space-y-3 mt-4">
|
||||
<div className="h-4 bg-gray-200 rounded"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{state.status === 'error' && (
|
||||
<div className="text-red-500">
|
||||
{state.error?.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{state.status === 'success' && (
|
||||
<div>{state.data}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Environment configuration">
|
||||
Set up proper environment variables:
|
||||
|
||||
```typescript
|
||||
// next.config.js
|
||||
module.exports = {
|
||||
env: {
|
||||
NEXT_PUBLIC_ANTHROPIC_API_KEY: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY,
|
||||
MCP_DEBUG: process.env.NODE_ENV === 'development'
|
||||
}
|
||||
};
|
||||
|
||||
// Usage in code
|
||||
const mcpClient = new MCPWebClient({
|
||||
debug: process.env.MCP_DEBUG === 'true'
|
||||
});
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Security considerations
|
||||
|
||||
<Warning>
|
||||
Always validate and sanitize URLs before passing them to MCP servers. The puppeteer server has access to execute JavaScript and interact with web pages.
|
||||
</Warning>
|
||||
|
||||
```typescript
|
||||
function validateUrl(url: string): boolean {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
// Add your validation rules
|
||||
return parsed.protocol === 'https:';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function WebAnalyzer() {
|
||||
const analyzeUrl = async () => {
|
||||
if (!validateUrl(url)) {
|
||||
setError('Please enter a valid HTTPS URL');
|
||||
return;
|
||||
}
|
||||
// ... rest of the analysis code
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Add Authentication"
|
||||
icon="lock"
|
||||
href="/guides/auth"
|
||||
>
|
||||
Learn how to handle authenticated web pages with MCP
|
||||
</Card>
|
||||
<Card
|
||||
title="Custom MCP Server"
|
||||
icon="server"
|
||||
href="/quickstarts/first-server-typescript">
|
||||
Build your own MCP server for custom functionality
|
||||
</Card>
|
||||
<Card
|
||||
title="Testing"
|
||||
icon="vial"
|
||||
href="/guides/testing">
|
||||
Learn how to test MCP integrations
|
||||
</Card>
|
||||
<Card
|
||||
title="Production Setup"
|
||||
icon="rocket"
|
||||
href="/guides/production">
|
||||
Deploy your MCP-enabled app to production
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
55
favicon.svg
55
favicon.svg
|
|
@ -1,49 +1,12 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="white"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="url(#paint0_radial_101_2703)"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="url(#paint1_linear_101_2703)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M8.68359 10.4755C9.94543 13.63 9.56145 17.5723 7.9354 21.1112C4.89702 21.0957 2.27411 19.4306 1.01347 16.279C-0.248375 13.1245 0.135612 9.18218 1.76165 5.64328C4.80004 5.65883 7.42295 7.32386 8.68359 10.4755Z" stroke="url(#paint2_linear_101_2703)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="white"/>
|
||||
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="url(#paint3_radial_101_2703)"/>
|
||||
<path d="M16.6026 13.2251C14.8642 17.349 11.3512 20.1866 7.32411 21.2248C5.25257 17.624 4.82926 13.1324 6.56764 9.00855C8.30603 4.88472 11.819 2.04706 15.8461 1.00889C17.9176 4.60967 18.3409 9.10131 16.6026 13.2251Z" stroke="url(#paint4_linear_101_2703)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="white"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint5_radial_101_2703)"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint6_linear_101_2703)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M16.5682 22.7874C13.2176 23.9184 9.81361 23.2124 7.2672 21.1975C8.49194 17.9068 11.0444 15.189 14.3959 14.0577C17.7465 12.9266 21.1504 13.6326 23.6968 15.6476C22.4721 18.9383 19.9196 21.656 16.5682 22.7874Z" stroke="url(#paint7_linear_101_2703)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_19_13)">
|
||||
<path d="M18 84.8528L85.8822 16.9706C95.2548 7.59798 110.451 7.59798 119.823 16.9706V16.9706C129.196 26.3431 129.196 41.5391 119.823 50.9117L68.5581 102.177" stroke="black" stroke-width="12" stroke-linecap="round"/>
|
||||
<path d="M69.2652 101.47L119.823 50.9117C129.196 41.5391 144.392 41.5391 153.765 50.9117L154.118 51.2652C163.491 60.6378 163.491 75.8338 154.118 85.2063L92.7248 146.6C89.6006 149.724 89.6006 154.789 92.7248 157.913L105.331 170.52" stroke="black" stroke-width="12" stroke-linecap="round"/>
|
||||
<path d="M102.853 33.9411L52.6482 84.1457C43.2756 93.5183 43.2756 108.714 52.6482 118.087V118.087C62.0208 127.459 77.2167 127.459 86.5893 118.087L136.794 67.8822" stroke="black" stroke-width="12" stroke-linecap="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_101_2703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-3.00503 15.023) rotate(-10.029) scale(17.9572 17.784)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint1_linear_101_2703" x1="7.39036" y1="4.81308" x2="1.62975" y2="18.6894" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_101_2703" x1="7.94816" y1="8.01563" x2="1.7612" y2="18.746" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint3_radial_101_2703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(8.11404 20.8822) rotate(-75.7542) scale(21.6246 23.7772)">
|
||||
<stop stop-color="#00BBBB"/>
|
||||
<stop offset="0.712616" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint4_linear_101_2703" x1="7.60205" y1="5.8709" x2="15.5561" y2="16.3719" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint5_radial_101_2703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7.84537 21.5181) rotate(-20.3525) scale(18.5603 17.32)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint6_linear_101_2703" x1="16.8078" y1="13.0071" x2="10.0409" y2="22.9937" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00B1BC"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_101_2703" x1="16.8078" y1="13.0071" x2="14.1687" y2="23.841" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_19_13">
|
||||
<rect width="180" height="180" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 973 B |
|
|
@ -9,120 +9,79 @@ The Model Context Protocol (MCP) is an open protocol that enables seamless integ
|
|||
|
||||
Choose the path that best fits your needs:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<CardGroup cols={1}>
|
||||
<Card
|
||||
title="Quickstart"
|
||||
icon="bolt"
|
||||
href="/docs/guides/quickstart"
|
||||
href="/quickstart"
|
||||
>
|
||||
The fastest way to see MCP in action - connect example servers to Claude Desktop
|
||||
</Card>
|
||||
<Card
|
||||
title="Build Your First Server (TypeScript)"
|
||||
icon="code"
|
||||
href="/docs/guides/first-server-ts"
|
||||
>
|
||||
Create a simple MCP server in TypeScript to understand the basics
|
||||
</Card>
|
||||
<Card
|
||||
title="Build Your First Server (Python)"
|
||||
icon="python"
|
||||
href="/docs/guides/first-server-python"
|
||||
href="/docs/first-server/python"
|
||||
>
|
||||
Create a simple MCP server in Python to understand the basics
|
||||
</Card>
|
||||
<Card
|
||||
title="Build Your First Server (TypeScript)"
|
||||
icon="code"
|
||||
href="/docs/first-server/typescript"
|
||||
>
|
||||
Create a simple MCP server in TypeScript to understand the basics
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Explore MCP
|
||||
|
||||
Dive deeper into MCP's core concepts and capabilities:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Core Architecture"
|
||||
icon="sitemap"
|
||||
href="/docs/core/architecture"
|
||||
href="/docs/concepts/architecture"
|
||||
>
|
||||
Understand how MCP connects clients, servers, and LLMs
|
||||
</Card>
|
||||
<Card
|
||||
title="Resources"
|
||||
icon="database"
|
||||
href="/docs/core/resources"
|
||||
href="/docs/concepts/resources"
|
||||
>
|
||||
Expose data and content from your servers to LLMs
|
||||
</Card>
|
||||
<Card
|
||||
title="Prompts"
|
||||
icon="message"
|
||||
href="/docs/core/prompts"
|
||||
href="/docs/concepts/prompts"
|
||||
>
|
||||
Create reusable prompt templates and workflows
|
||||
</Card>
|
||||
<Card
|
||||
title="Tools"
|
||||
icon="wrench"
|
||||
href="/docs/core/tools"
|
||||
href="/docs/concepts/tools"
|
||||
>
|
||||
Enable LLMs to perform actions through your server
|
||||
</Card>
|
||||
<Card
|
||||
title="Sampling"
|
||||
icon="robot"
|
||||
href="/docs/core/sampling"
|
||||
href="/docs/concepts/sampling"
|
||||
>
|
||||
Let your servers request completions from LLMs
|
||||
</Card>
|
||||
<Card
|
||||
title="Transport"
|
||||
icon="network-wired"
|
||||
href="/docs/core/transport"
|
||||
href="/docs/concepts/transport"
|
||||
>
|
||||
Learn about MCP's communication mechanisms
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
See what you can build with MCP:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Custom Knowledge Base"
|
||||
icon="book"
|
||||
>
|
||||
Connect your documentation, wikis, and internal knowledge bases to LLMs
|
||||
</Card>
|
||||
<Card
|
||||
title="Database Integration"
|
||||
icon="database"
|
||||
>
|
||||
Query databases and analyze results with natural language
|
||||
</Card>
|
||||
<Card
|
||||
title="Local Development"
|
||||
icon="code"
|
||||
>
|
||||
Build AI-powered development tools with access to local files and git
|
||||
</Card>
|
||||
<Card
|
||||
title="Enterprise Systems"
|
||||
icon="building"
|
||||
>
|
||||
Securely connect enterprise tools and data to AI interfaces
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Security and Best Practices
|
||||
|
||||
MCP is designed with security in mind, but requires careful implementation:
|
||||
|
||||
<Card
|
||||
title="Security Guide"
|
||||
icon="shield"
|
||||
href="/security"
|
||||
>
|
||||
Learn about MCP's security model, best practices, and implementation guidelines
|
||||
</Card>
|
||||
## Contributing
|
||||
|
||||
Want to contribute? Check out our [GitHub repository](https://github.com/modelcontextprotocol) or join our growing community of developers building with MCP.
|
||||
39
mint.json
39
mint.json
|
|
@ -47,24 +47,21 @@
|
|||
"navigation": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"pages": ["introduction", "docs/guides/quickstart"]
|
||||
"pages": ["introduction", "quickstart"]
|
||||
},
|
||||
{
|
||||
"group": "Your First MCP Server",
|
||||
"pages": [
|
||||
"docs/guides/first-server-python",
|
||||
"docs/guides/first-server-ts"
|
||||
]
|
||||
"pages": ["docs/first-server/python", "docs/first-server/typescript"]
|
||||
},
|
||||
{
|
||||
"group": "Concepts",
|
||||
"pages": [
|
||||
"docs/core/architecture",
|
||||
"docs/core/resources",
|
||||
"docs/core/prompts",
|
||||
"docs/core/tools",
|
||||
"docs/core/sampling",
|
||||
"docs/core/transport"
|
||||
"docs/concepts/architecture",
|
||||
"docs/concepts/resources",
|
||||
"docs/concepts/prompts",
|
||||
"docs/concepts/tools",
|
||||
"docs/concepts/sampling",
|
||||
"docs/concepts/transport"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -104,11 +101,25 @@
|
|||
"api-reference/typescript/class-reference",
|
||||
"api-reference/typescript/type-definitions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Specification",
|
||||
"pages": [
|
||||
"specification/overview",
|
||||
"specification/jsonrpc",
|
||||
"specification/lifecycle",
|
||||
"specification/prompts",
|
||||
"specification/resources",
|
||||
"specification/cancellation",
|
||||
"specification/logging",
|
||||
"specification/tools",
|
||||
"specification/completion",
|
||||
"specification/sampling",
|
||||
"specification/roots"
|
||||
]
|
||||
}
|
||||
],
|
||||
"footerSocials": {
|
||||
"x": "https://x.com/mintlify",
|
||||
"github": "https://github.com/mintlify",
|
||||
"linkedin": "https://linkedin.com/company/mintlify"
|
||||
"github": "https://github.com/modelcontextprotocol"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
336
quickstart.mdx
Normal file
336
quickstart.mdx
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
---
|
||||
title: "Quickstart"
|
||||
description: "Get started with MCP in less than 5 minutes"
|
||||
---
|
||||
|
||||
MCP is a protocol that enables secure connections between clients, such as the Claude Desktop app, and local services. In this quickstart guide, you'll learn how to:
|
||||
|
||||
- Set up a local SQLite database
|
||||
- Connect Claude Desktop to it through MCP
|
||||
- Query and analyze your data securely
|
||||
|
||||
<Note>
|
||||
While this guide focuses on using Claude Desktop as an example MCP client, the protocol is open and can be implemented by any application. IDEs, AI tools, and other software can all act as MCP clients to access local services in a standardized way.
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
Claude Desktop's MCP support is currently in developer preview and only supports connecting to local servers running on your machine. Remote connections are not yet supported. MCP integration is only available in the Claude Desktop app, not the Claude web interface (claude.ai).
|
||||
</Warning>
|
||||
|
||||
## How MCP works
|
||||
|
||||
MCP (Model Context Protocol) is an open protocol that enables secure, controlled interactions between AI applications and local resources on your computer. Let's break down how it works, then look at how we'll use it in this guide.
|
||||
|
||||
### General Architecture
|
||||
At its core, MCP follows a client-server architecture where multiple services can connect to any compatible client:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Your Computer"
|
||||
Client["MCP Clients\n(Claude, IDEs, Tools)"]
|
||||
S1["MCP Server A"]
|
||||
S2["MCP Server B"]
|
||||
S3["MCP Server C"]
|
||||
|
||||
Client <-->|"MCP Protocol"| S1
|
||||
Client <-->|"MCP Protocol"| S2
|
||||
Client <-->|"MCP Protocol"| S3
|
||||
|
||||
S1 <--> R1[("Local\nResource A")]
|
||||
S2 <--> R2[("Local\nResource B")]
|
||||
S3 <--> R3[("Local\nResource C")]
|
||||
end
|
||||
```
|
||||
|
||||
- **MCP Clients**: Applications like Claude Desktop, IDEs, or AI tools that want to access local resources
|
||||
- **MCP Servers**: Lightweight programs that each expose specific capabilities through the standardized MCP protocol
|
||||
- **Local Resources**: Your computer's resources (databases, files, services) that MCP servers can securely access
|
||||
|
||||
### In This Guide
|
||||
|
||||
For this quickstart, we'll implement a focused example using SQLite:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Your Computer"
|
||||
direction LR
|
||||
Claude["Claude Desktop"]
|
||||
MCP["SQLite MCP Server"]
|
||||
DB[(SQLite Database\n~/test.db)]
|
||||
|
||||
Claude <-->|"MCP Protocol\n(Queries & Results)"| MCP
|
||||
MCP <-->|"Local Access\n(SQL Operations)"| DB
|
||||
end
|
||||
```
|
||||
|
||||
1. Claude Desktop acts as our MCP client
|
||||
2. A SQLite MCP Server provides secure database access
|
||||
3. Your local SQLite database stores the actual data
|
||||
|
||||
All communication happens entirely on your machine - no data leaves your computer. The MCP protocol ensures that Claude Desktop can only perform approved database operations through well-defined interfaces.
|
||||
This gives you a secure way to let Claude analyze and interact with your local data while maintaining complete control over what it can access.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- macOS (Windows/Linux guides coming soon)
|
||||
- Node.js v18 or higher (`node --version` to check)
|
||||
- Git (`git --version` to check)
|
||||
- [Claude Desktop app](https://claude.ai/download) installed
|
||||
- SQLite (`sqlite3 --version` to check)
|
||||
|
||||
<Accordion title="Need to install the prerequisites?">
|
||||
```bash
|
||||
# Using Homebrew
|
||||
brew install node git sqlite3
|
||||
|
||||
# Or download directly:
|
||||
# Node.js: https://nodejs.org
|
||||
# Git: https://git-scm.com
|
||||
# SQLite: https://www.sqlite.org/download.html
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
## Installation
|
||||
|
||||
<Steps>
|
||||
<Step title="Install example servers">
|
||||
Let's start by getting the pre-built MCP servers:
|
||||
|
||||
```bash
|
||||
# Clone the example servers
|
||||
git clone https://github.com/modelcontextprotocol/servers
|
||||
cd servers
|
||||
|
||||
# Install dependencies and build
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# Make servers available globally
|
||||
npm install -g
|
||||
```
|
||||
|
||||
This makes the MCP servers available as command-line tools that Claude Desktop can launch.
|
||||
</Step>
|
||||
|
||||
<Step title="Create a sample database">
|
||||
Let's create a simple SQLite database for testing:
|
||||
|
||||
```bash
|
||||
# Create a new SQLite database
|
||||
sqlite3 ~/test.db <<EOF
|
||||
CREATE TABLE products (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT,
|
||||
price REAL
|
||||
);
|
||||
|
||||
INSERT INTO products (name, price) VALUES
|
||||
('Widget', 19.99),
|
||||
('Gadget', 29.99),
|
||||
('Gizmo', 39.99);
|
||||
EOF
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Claude Desktop">
|
||||
Open your Claude Desktop App configuration:
|
||||
|
||||
```bash
|
||||
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
||||
```
|
||||
|
||||
Add this configuration (replace YOUR_USERNAME with your actual username):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"sqlite": {
|
||||
"command": "mcp-server-sqlite",
|
||||
"args": ["/Users/YOUR_USERNAME/test.db"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This tells Claude Desktop:
|
||||
1. There's an MCP server named "sqlite"
|
||||
2. Launch it using the `mcp-server-sqlite` command
|
||||
3. Connect it to your test database
|
||||
4. Provide necessary environment variables
|
||||
|
||||
Save and **restart Claude Desktop App**.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Test it out
|
||||
|
||||
Let's verify everything is working. Try sending this prompt to Claude Desktop:
|
||||
|
||||
```
|
||||
Can you connect to my SQLite database and tell me what products are available and their prices?
|
||||
```
|
||||
|
||||
Claude Desktop will:
|
||||
1. Connect to the SQLite MCP server
|
||||
2. Query your local database
|
||||
3. Format and present the results
|
||||
|
||||
<Frame>
|
||||
<img src="/api/placeholder/800/400" alt="Example Claude Desktop conversation showing database query results" />
|
||||
</Frame>
|
||||
|
||||
## What's happening under the hood?
|
||||
|
||||
When you interact with Claude Desktop using MCP:
|
||||
|
||||
1. **Server Discovery**: Claude Desktop loads your configured MCP servers on startup
|
||||
2. **Protocol Handshake**: When you ask about data, Claude Desktop:
|
||||
- Identifies which MCP server can help (sqlite in this case)
|
||||
- Negotiates capabilities through the MCP protocol
|
||||
- Establishes a secure local connection
|
||||
|
||||
3. **Interaction Flow**:
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Claude Desktop
|
||||
participant M as MCP Server
|
||||
participant D as SQLite DB
|
||||
|
||||
C->>M: Initialize connection
|
||||
M-->>C: Available capabilities
|
||||
|
||||
C->>M: Query request
|
||||
M->>D: SQL query
|
||||
D-->>M: Results
|
||||
M-->>C: Formatted results
|
||||
```
|
||||
|
||||
4. **Security**:
|
||||
- MCP servers only expose specific, controlled capabilities
|
||||
- All communication is local to your machine
|
||||
- Claude Desktop requires user confirmation for sensitive operations
|
||||
|
||||
## Try these examples
|
||||
|
||||
Now that MCP is working, try these increasingly powerful examples:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Basic Queries" active>
|
||||
```
|
||||
What's the average price of all products in the database?
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Data Analysis">
|
||||
```
|
||||
Can you analyze the price distribution and suggest any pricing optimizations?
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Complex Operations">
|
||||
```
|
||||
Could you help me design and create a new table for storing customer orders?
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Add more capabilities
|
||||
|
||||
Want to give Claude Desktop more local integration capabilities? Add these servers to your configuration:
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="File System Access" icon="folder-open">
|
||||
Add this to your config to let Claude Desktop read and analyze files:
|
||||
```json
|
||||
"tee": {
|
||||
"command": "mcp-server-tee",
|
||||
"args": ["tail", "-f", "/var/log/system.log"],
|
||||
"env": {
|
||||
"HOME": "/Users/YOUR_USERNAME"
|
||||
}
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="PostgreSQL Connection" icon="database">
|
||||
Connect Claude Desktop to your PostgreSQL database:
|
||||
```json
|
||||
"postgres": {
|
||||
"command": "mcp-server-postgres",
|
||||
"args": ["postgresql://localhost/mydb"],
|
||||
"env": {
|
||||
"HOME": "/Users/YOUR_USERNAME"
|
||||
}
|
||||
}
|
||||
```
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## MCP clients
|
||||
|
||||
While this guide demonstrates MCP using Claude Desktop, several other applications support MCP integration:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Zed Editor"
|
||||
icon="pen-to-square"
|
||||
href="https://zed.dev">
|
||||
A high-performance, multiplayer code editor with built-in MCP support for AI-powered coding assistance
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Sourcegraph"
|
||||
icon="magnifying-glass"
|
||||
href="https://sourcegraph.com">
|
||||
Code intelligence platform featuring MCP integration for enhanced code search and analysis capabilities
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Each client may implement MCP features differently or support different capabilities. Check their respective documentation for specific setup instructions and supported features.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Nothing showing up in Claude Desktop?">
|
||||
1. Check if MCP is enabled:
|
||||
- Click the 🔌 icon in Claude Desktop
|
||||
- You should see your configured servers
|
||||
|
||||
2. Verify your config:
|
||||
```bash
|
||||
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
||||
```
|
||||
|
||||
3. Restart Claude Desktop completely:
|
||||
- Quit the app (not just close the window)
|
||||
- Start it again
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database connection errors?">
|
||||
1. Check Claude Desktop's logs:
|
||||
```bash
|
||||
tail -f ~/Claude.log
|
||||
```
|
||||
|
||||
2. Verify database access:
|
||||
```bash
|
||||
# Test database connection
|
||||
sqlite3 ~/test.db ".tables"
|
||||
```
|
||||
|
||||
3. Common fixes:
|
||||
- Check file paths in your config
|
||||
- Verify database file permissions
|
||||
- Ensure SQLite is installed properly
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Build your first MCP server" icon="code" href="/docs/first-server/python">
|
||||
Create your own MCP servers to give your LLM clients new capabilities.
|
||||
</Card>
|
||||
|
||||
<Card title="Explore examples" icon="github" href="https://github.com/modelcontextprotocol/servers">
|
||||
Browse our collection of example servers to see what's possible.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
67
specification/cancellation.mdx
Normal file
67
specification/cancellation.mdx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
title: Cancellation
|
||||
---
|
||||
|
||||
MCP supports request cancellation through a dedicated notification message. This allows either party to signal that they are no longer interested in the result of an in-flight request.
|
||||
|
||||
## Cancellation Protocol
|
||||
|
||||
When a party wants to cancel a request, they send a "cancelled" notification containing the ID of the request they wish to cancel:
|
||||
|
||||
For example, initiating a request:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "abc123",
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
"uri": "example://large-file"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
… then some time later, cancelling that request:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "notifications/cancelled",
|
||||
"params": {
|
||||
"requestId": "abc123",
|
||||
"reason": "User interrupted operation"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Handling Cancellation
|
||||
|
||||
Upon receiving a cancellation notification, the receiver SHOULD:
|
||||
|
||||
1. Stop any processing related to the cancelled request
|
||||
2. Free any resources associated with that request
|
||||
3. Not send a response for the cancelled request (even if already computed)
|
||||
|
||||
However, due to race conditions and message latency:
|
||||
|
||||
- The notification may arrive after processing is complete
|
||||
- A response may already be in transit when cancellation is received
|
||||
- Multiple cancellation notifications for the same request ID may be received
|
||||
|
||||
## Special Cases
|
||||
|
||||
### Initialize Request
|
||||
|
||||
The client MUST NOT attempt to cancel its `initialize` request. Cancelling initialization leaves the protocol in an undefined state.
|
||||
|
||||
### Long-Running Operations
|
||||
|
||||
For long-running operations that support progress notifications, progress notifications SHOULD stop being sent after receiving cancellation.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Implement cancellation handlers for all long-running operations
|
||||
2. Free resources promptly when receiving cancellation
|
||||
3. Make cancellation handling idempotent
|
||||
4. Include meaningful reason strings to aid debugging
|
||||
5. Log cancellations appropriately for troubleshooting
|
||||
188
specification/completion.mdx
Normal file
188
specification/completion.mdx
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
---
|
||||
title: Argument Completion
|
||||
---
|
||||
Argument Completion enables servers to provide argument completion for prompt and resource URI arguments. Clients can request completion options for specific arguments, and servers can return ranked suggestions. This allows clients to build rich user interfaces with intelligent argument completion for argument values.
|
||||
|
||||
> **_NOTE:_** Argument Completion in MCP is similar to traditional IDE argument completion - it provides contextual suggestions based on available options, rather than AI-powered completion. The server maintains a fixed set of valid values for each argument and returns matching suggestions based on partial input.
|
||||
|
||||
## Capabilities
|
||||
|
||||
Support for argument completion is not indicated by a dedicated capability - servers that expose prompts or resources with arguments implicitly support argument completion for those arguments. Clients may attempt argument completion requests for any prompt or resource argument.
|
||||
|
||||
## Concepts
|
||||
|
||||
### Completion References
|
||||
|
||||
When requesting completions, clients must specify what is being completed using a reference type:
|
||||
|
||||
- `ref/prompt`: References a prompt by name
|
||||
- `ref/resource`: References a resource by URI
|
||||
|
||||
The reference identifies the context for completion suggestions.
|
||||
|
||||
### Completion Results
|
||||
|
||||
Servers return an array of completion values ranked by relevance, with a maximum of 100 items per response. If more results are available beyond the first 100, servers MUST set `hasMore: true` in the response to indicate that additional results can be retrieved with subsequent requests. The optional `total` field allows servers to specify the complete number of matches available, even if not all are returned in a single response.
|
||||
|
||||
> **_NOTE:_** MCP does not currently support pagination of completion results - clients that need more than the first 100 matches must issue new completion requests with more specific argument values to narrow down the results.
|
||||
|
||||
## Use Cases
|
||||
|
||||
Common use cases for argument completion include:
|
||||
|
||||
### Prompt Argument Completion
|
||||
|
||||
A client requesting completion options for a prompt argument:
|
||||
|
||||
```json
|
||||
{
|
||||
"ref": {
|
||||
"type": "ref/prompt",
|
||||
"name": "code_review"
|
||||
},
|
||||
"argument": {
|
||||
"name": "language",
|
||||
"value": "py"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The server might respond with language suggestions:
|
||||
|
||||
```json
|
||||
{
|
||||
"completion": {
|
||||
"values": ["python", "pytorch", "pyside", "pyyaml"],
|
||||
"total": 10,
|
||||
"hasMore": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource URI Completion
|
||||
|
||||
A client completing a path in a resource URI template:
|
||||
|
||||
```json
|
||||
{
|
||||
"ref": {
|
||||
"type": "ref/resource",
|
||||
"uri": "file://{path}"
|
||||
},
|
||||
"argument": {
|
||||
"name": "path",
|
||||
"value": "/home/user/doc"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The server could respond with matching paths:
|
||||
|
||||
```json
|
||||
{
|
||||
"completion": {
|
||||
"values": [
|
||||
"/home/user/documents",
|
||||
"/home/user/docker",
|
||||
"/home/user/downloads"
|
||||
],
|
||||
"hasMore": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Diagram
|
||||
|
||||
The following diagram visualizes a typical argument completion interaction between client and server:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant Server
|
||||
|
||||
Note over Client,Server: Client requests completion options
|
||||
Client->>Server: completion/complete
|
||||
Server-->>Client: CompleteResult
|
||||
|
||||
Note over Client,Server: Client may request more specific results
|
||||
opt New completions requested
|
||||
Client->>Server: completion/complete
|
||||
Server-->>Client: CompleteResult
|
||||
end
|
||||
```
|
||||
|
||||
## Messages
|
||||
|
||||
This section defines the protocol messages for argument completion in the Model Context Protocol (MCP).
|
||||
|
||||
### Requesting Completions
|
||||
|
||||
#### Request
|
||||
|
||||
To get completion suggestions, the client MUST send a `completion/complete` request.
|
||||
|
||||
Method: `completion/complete`
|
||||
Params:
|
||||
- `ref`: A `PromptReference` or `ResourceReference` indicating what is being completed
|
||||
- `argument`: Object containing:
|
||||
- `name`: The name of the argument being completed
|
||||
- `value`: The current value to get completions for
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "completion/complete",
|
||||
"params": {
|
||||
"ref": {
|
||||
"type": "ref/prompt",
|
||||
"name": "code_review"
|
||||
},
|
||||
"argument": {
|
||||
"name": "language",
|
||||
"value": "py"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Response
|
||||
|
||||
The server MUST respond with a `CompleteResult` containing:
|
||||
|
||||
- `completion`: Object containing:
|
||||
- `values`: Array of completion suggestions (maximum 100)
|
||||
- `total`: Optional total number of matches available
|
||||
- `hasMore`: Optional boolean indicating if additional results exist
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"completion": {
|
||||
"values": ["python", "pytorch", "pyside"],
|
||||
"total": 10,
|
||||
"hasMore": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Servers MUST return appropriate errors if:
|
||||
- The referenced prompt or resource does not exist
|
||||
- The argument name is invalid
|
||||
- Completion cannot be provided for other reasons
|
||||
|
||||
Clients SHOULD be prepared to handle cases where completion is temporarily unavailable or returns errors.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Implementations MUST carefully consider:
|
||||
- Rate limiting completion requests to prevent abuse
|
||||
- Access control for sensitive completion suggestions
|
||||
- Validation of completion inputs to prevent injection attacks
|
||||
166
specification/jsonrpc.mdx
Normal file
166
specification/jsonrpc.mdx
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
---
|
||||
title: Base Protocol
|
||||
---
|
||||
|
||||
The Model Context Protocol (MCP) defines a set of JSON-RPC methods for communication between clients and servers in AI-assisted applications. MCP uses [JSON-RPC 2.0](https://www.jsonrpc.org/specification) as its base protocol.
|
||||
|
||||
### Transport
|
||||
|
||||
MCP client **SHOULD** use one of the following transport mechanisms, but are free to use alternative transports where applicable.:
|
||||
|
||||
Standard Input/Output (STDIO)
|
||||
: Communication occurs through the standard input and output streams. JSON-RPC
|
||||
messages are sent as individual lines over these streams. Each message is a
|
||||
complete JSON object serialized as a single line of text, terminated by a
|
||||
newline character. The client writes requests to the server's standard input
|
||||
and reads responses from the server's standard output. See [transports](transports.md) for more details.
|
||||
|
||||
HTTP with Server-Sent Events (SSE)
|
||||
: This transport uses HTTP (1.1 or above) as the underlying protocol. The client sends
|
||||
requests to the server using standard HTTP POST requests with JSON-RPC
|
||||
payloads. For server-to-client communication, Server-Sent Events (SSE) are
|
||||
employed. The server keeps an open connection to the client and sends
|
||||
messages as SSE events. See [transports](transports.md) for more details.
|
||||
|
||||
### Message Format
|
||||
|
||||
### Progress Notifications
|
||||
|
||||
MCP supports progress tracking for long-running operations through out-of-band notifications. Progress notifications allow the sender of a request to provide updates about the operation's progress.
|
||||
|
||||
To request progress notifications, include a `progressToken` in the request metadata:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "some_method",
|
||||
"params": {
|
||||
"_meta": {
|
||||
"progressToken": "abc123"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The receiver MAY then send progress notifications using the same token:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "notifications/progress",
|
||||
"params": {
|
||||
"progressToken": "abc123",
|
||||
"progress": 50,
|
||||
"total": 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
Many list operations in MCP support pagination using cursors. The server response will include a `nextCursor` if more results are available:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"resources": [...],
|
||||
"nextCursor": "next_page_token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clients can include a `cursor` parameter containing the opaque token from the server,
|
||||
to obtain the next page starting from the cursor:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "resources/list",
|
||||
"params": {
|
||||
"cursor": "some_opaque_token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Clients should continue making requests with the provided cursor until no `nextCursor` is returned.
|
||||
|
||||
All messages in MCP **MUST** follow the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines three types of messages:
|
||||
|
||||
1. Requests
|
||||
2. Responses
|
||||
3. Notifications
|
||||
|
||||
#### Requests
|
||||
|
||||
Requests are sent from the client to the server or vice versa. They **MUST** include a unique `id` and expect a response.
|
||||
|
||||
#### Responses
|
||||
|
||||
Responses are sent in reply to requests. They **MUST** include the same `id` as the corresponding request.
|
||||
|
||||
#### Notifications
|
||||
|
||||
Notifications are sent from the client to the server or vice versa. They do not expect a response and **MUST NOT** include an `id`.
|
||||
|
||||
### Base Protocol
|
||||
|
||||
The base protocol consists of the following JSON-RPC methods:
|
||||
|
||||
- `initialize`: Sent from the client to the server to initialize the connection.
|
||||
- `initialized`: Sent from the client to the server after initialization is complete.
|
||||
- `ping`: Sent by either party to check if the other is still alive.
|
||||
|
||||
Implementations **MUST** support these base protocol methods.
|
||||
|
||||
### Model Context Protocol specific methods
|
||||
|
||||
MCP defines methods based on JSON-RPC that clients and servers implement:
|
||||
|
||||
- [Lifecycle](lifecycle.md) handling
|
||||
- [Resource](resources.md) management
|
||||
- [Prompt](prompts.md) handling
|
||||
- [Tool](tools.md) execution
|
||||
- [Logging](logging.md)
|
||||
- [Sampling](sampling.md)
|
||||
|
||||
### Authentication
|
||||
|
||||
Authentication mechanisms are not part of the core MCP specification. Implementations **MAY** provide authentication based on the transport they use..
|
||||
|
||||
### Versioning
|
||||
|
||||
The MCP version is declared in the `initialize` request and response. Clients and servers **MUST** agree on a compatible protocol version to proceed with communication.
|
||||
|
||||
### Ping Messages
|
||||
|
||||
Either party can send ping messages to check if the other is still alive and responsive:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "ping"
|
||||
}
|
||||
```
|
||||
|
||||
The receiver MUST respond promptly with an empty result:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {}
|
||||
}
|
||||
```
|
||||
|
||||
If the receiver fails to respond in a timely manner, the sender MAY disconnect.
|
||||
|
||||
## Specification
|
||||
|
||||
The conical specification can be found at [Model Context Protocol](http://github.com/modelcontextprotocol/spec/tree/main/spec/schema.ts). The specification of the data format
|
||||
is available in TypeScript (source of truth) and JSON Schema format (generated).
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue