---
description:
globs:
alwaysApply: true
---
# MCP Server Development Guide
This guide covers best practices for developing Model Context Protocol (MCP) servers using the TypeScript SDK.
## **Tool Registration Pattern**
- **Use McpServer with Proper Description Format**
- Tool descriptions are provided as a separate string parameter, not within the schema object
- Pass the Zod schema shape directly as the third parameter
- Import `z` from 'zod' for schema definitions
- See [typescript-patterns.mdc](mdc:.cursor/rules/typescript-patterns.mdc) for detailed Zod schema patterns
```typescript
// ✅ DO: Correct tool registration with description
server.tool(
'my_tool',
'Clear description of what this tool does and its purpose',
{
param1: z.string().min(1),
param2: z.number().optional().default(10),
param3: z.array(z.string()).optional()
},
async (input) => {
// input is properly typed and validated
return {
content: [{ type: 'text', text: 'result' }]
};
}
);
// ❌ DON'T: Include description in schema object
server.tool(
'my_tool',
{
description: 'Tool description', // This causes TypeScript errors
param1: z.string().min(1),
param2: z.number().optional().default(10)
},
async (input) => {
// This pattern is incorrect
}
);
// ❌ DON'T: Missing description entirely
server.tool(
'my_tool',
{
param1: z.string().min(1),
param2: z.number().optional().default(10)
},
async (input) => {
// Tools without descriptions are less discoverable
}
);
```
## **Parameter Access Pattern**
- **Direct Parameter Access**
- Tool handlers receive validated parameters directly as the first argument
- No need to access `request.params.arguments`
- Parameters are automatically validated against the Zod schema
```typescript
// ✅ DO: Direct parameter access
async (input) => {
// input is typed and validated
const { conversationId, includeMetadata } = input;
}
// ❌ DON'T: Manual parameter extraction
async (request) => {
const input = request.params.arguments as MyInputType;
}
```
## **MCP-Specific Error Handling**
- **MCP Tool Response Format**
- Always return content in the expected MCP format
- Use consistent error response structure
- Include meaningful error messages for debugging
```typescript
async (input) => {
try {
const result = await myOperation(input);
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`
}]
};
}
}
```
## **Server Setup Pattern**
- **Standard Server Initialization**
- Use `McpServer` from the official SDK
- Connect with `StdioServerTransport` for CLI tools
- Await the connection to ensure proper setup
```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
// Register tools with descriptions...
const transport = new StdioServerTransport();
await server.connect(transport);
```
## **Tool Description Best Practices**
- **Write Clear, Actionable Descriptions**
- Describe what the tool does and its purpose
- Include key functionality and expected use cases
- Mention important parameters or behavior
- Keep descriptions concise but informative
```typescript
// ✅ DO: Clear, informative descriptions
server.tool(
'list_conversations',
'List Cursor conversations with optional filtering by keywords, code blocks, file patterns, and more. Returns conversation summaries ordered by most recent first.',
{ /* schema */ },
async (input) => { /* handler */ }
);
server.tool(
'get_conversation',
'Retrieve the full content of a specific Cursor conversation by ID, including messages, code blocks, file references, and metadata.',
{ /* schema */ },
async (input) => { /* handler */ }
);
// ❌ DON'T: Vague or missing descriptions
server.tool(
'process_data',
'Processes data', // Too vague
{ /* schema */ },
async (input) => { /* handler */ }
);
```
## **Testing MCP Servers**
- **Manual Testing with JSON-RPC**
- Test initialization with proper protocol version
- Use `tools/list` to verify tool registration and descriptions
- Use `tools/call` to test actual tool functionality
```bash
# Test initialization
echo '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test-client", "version": "1.0.0"}}}' | node dist/server.js
# List available tools (should show descriptions)
echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}' | node dist/server.js
# Call a tool
echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "my_tool", "arguments": {"param1": "value"}}}' | node dist/server.js
```
## **Common MCP Issues and Solutions**
- **"Cannot read properties of undefined" Error**
- Usually caused by incorrect tool registration format
- Check that you're using the correct parameter order: name, description, schema, handler
- Verify parameter access pattern (direct vs request.params.arguments)
- **Tool Not Appearing in List**
- Ensure tool is registered before server.connect()
- Check for TypeScript compilation errors
- Verify Zod schema syntax is correct
- Ensure description is provided as separate parameter
- **Missing Tool Descriptions**
- Descriptions must be the second parameter in server.tool()
- Don't include description in the schema object
- Descriptions improve tool discoverability for clients
- **Type Errors in Tool Handlers**
- Follow patterns in [typescript-patterns.mdc](mdc:.cursor/rules/typescript-patterns.mdc)
- Use Zod inference for input types: `z.infer<typeof mySchema>`
- Ensure async/await patterns are correct
## **File References**
- Main server implementation: [src/server.ts](mdc:src/server.ts)
- Tool implementations: [src/tools/conversation-tools.ts](mdc:src/tools/conversation-tools.ts)
- Database utilities: [src/database/reader.ts](mdc:src/database/reader.ts)
- TypeScript patterns: [typescript-patterns.mdc](mdc:.cursor/rules/typescript-patterns.mdc)