## Curation Note
The Model Context Protocol (MCP) has become the standard for extending AI agent capabilities beyond chat. This skill gained massive adoption after Anthropic open-sourced the specification. MCP servers allow Claude and other AI agents to interact with databases, APIs, file systems, and external services in a structured way. The four-phase approach (Research, Implementation, Review, Evaluation) has proven effective for producing production-quality integrations.
## Development Phases
### Phase 1: Deep Research and Planning
#### Understanding Modern MCP Design
**API Coverage vs. Workflow Tools:**
Balance comprehensive API endpoint coverage with specialized workflow tools. Workflow tools can be more convenient for specific tasks, while comprehensive coverage gives agents flexibility to compose operations.
**Tool Naming and Discoverability:**
Clear, descriptive tool names help agents find the right tools quickly:
```
github_create_issue
github_list_repos
github_close_pr
makeIssue
repos
closeit
```
**Context Management:**
Agents benefit from concise tool descriptions and the ability to filter/paginate results. Design tools that return focused, relevant data.
**Actionable Error Messages:**
Error messages should guide agents toward solutions:
```python
raise Exception("API error")
raise Exception(
"GitHub API rate limited. Retry after 60 seconds. "
"Consider using github_get_rate_limit to check status first."
)
```
#### Study MCP Protocol Documentation
Key concepts to understand:
- Transport mechanisms (streamable HTTP, stdio)
- Tool, resource, and prompt definitions
- Input/output schemas
### Phase 2: Implementation
#### Project Structure (TypeScript)
```
my-mcp-server/
├── src/
│ ├── index.ts # Main entry point
│ ├── server.ts # MCP server setup
│ ├── tools/ # Tool implementations
│ │ ├── index.ts
│ │ └── github.ts
│ ├── utils/
│ │ ├── auth.ts # Authentication helpers
│ │ └── api.ts # API client
│ └── types.ts # TypeScript types
├── package.json
├── tsconfig.json
└── README.md
```
#### Basic Server Setup (TypeScript)
```typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server(
{ name: 'my-mcp-server', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
// Register tools
server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: 'github_list_repos',
description: 'List repositories for the authenticated user',
inputSchema: {
type: 'object',
properties: {
per_page: { type: 'number', default: 30 },
page: { type: 'number', default: 1 }
}
}
}
]
}));
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'github_list_repos':
return await listRepos(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
```
#### Tool Implementation Best Practices
```typescript
// Input validation with Zod
import { z } from 'zod';
const CreateIssueSchema = z.object({
owner: z.string().describe('Repository owner'),
repo: z.string().describe('Repository name'),
title: z.string().describe('Issue title'),
body: z.string().optional().describe('Issue body (markdown)')
});
async function createIssue(args: unknown) {
const validated = CreateIssueSchema.parse(args);
try {
const response = await githubClient.post(`/repos/${validated.owner}/${validated.repo}/issues`, {
title: validated.title,
body: validated.body
});
return {
content: [
{
type: 'text',
text: `Created issue #${response.data.number}: ${response.data.html_url}`
}
],
structuredContent: response.data
};
} catch (error) {
if (error.response?.status === 404) {
throw new Error(
`Repository ${validated.owner}/${validated.repo} not found. ` +
`Check the owner and repo names, and verify you have access.`
);
}
throw error;
}
}
```
#### Tool Annotations
```typescript
{
name: "github_delete_repo",
description: "Delete a repository (DESTRUCTIVE)",
annotations: {
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: false
},
inputSchema: { /* ... */ }
}
```
### Phase 3: Review and Test
#### Testing Patterns
```typescript
// Unit tests for tool logic
describe('github_create_issue', () => {
it('creates an issue with required fields', async () => {
const result = await createIssue({
owner: 'test-owner',
repo: 'test-repo',
title: 'Test Issue'
});
expect(result.content[0].text).toContain('Created issue #');
});
it('returns actionable error for missing repo', async () => {
await expect(
createIssue({
owner: 'test-owner',
repo: 'nonexistent',
title: 'Test'
})
).rejects.toThrow(/not found/);
});
});
```
### Phase 4: Create Evaluations
Test with realistic agent workflows:
```
Evaluation: "Create a GitHub issue for a bug report"
Steps:
1. Agent calls github_list_repos to find the target repo
2. Agent calls github_create_issue with appropriate details
3. Verify issue was created with correct content
Success Criteria:
- Issue created within 3 tool calls
- Issue contains bug report template structure
- No authentication errors
```
## Configuration and Deployment
### Claude Code Configuration
Add to `~/.config/claude-code/mcp_servers.json`:
```json
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/path/to/my-mcp-server/dist/index.js"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
}
}
```
### Environment Variables
```typescript
// Secure credential handling
function getConfig() {
const token = process.env.GITHUB_TOKEN;
if (!token) {
throw new Error(
'GITHUB_TOKEN not set. Create a token at ' +
'https://github.com/settings/tokens and set it in your environment.'
);
}
return { token };
}
```
## Best Practices
1. **Comprehensive tool descriptions** - Help agents understand when to use each tool
2. **Validate inputs strictly** - Fail fast with clear error messages
3. **Return structured data** - Enable agents to process results programmatically
4. **Paginate large responses** - Don't overwhelm context windows
5. **Handle rate limits gracefully** - Include retry guidance in errors
6. **Test with real agents** - Unit tests aren't enough
7. **Log for debugging** - Include request IDs and timestamps
## Related Resources
- [MCP Specification](https://modelcontextprotocol.io/)
- [TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [Python SDK](https://github.com/modelcontextprotocol/python-sdk)
- [Example MCP Servers](https://github.com/modelcontextprotocol/servers)