Claude Code Agents and Model Context Protocol (MCP)¶
Overview¶
Claude Code agents are specialized AI assistants that can perform complex, multi-step tasks autonomously. Combined with MCP (Model Context Protocol), they provide powerful automation capabilities for software development.
MCP Architecture¶
graph TB
subgraph "Claude Code"
CC[Claude Code Client]
AGENT[Agent Runtime]
end
subgraph "MCP Servers"
MCP1[ec-mcp-server]
MCP2[ec-mcp-server-ai]
MCP3[Custom MCP Server]
end
subgraph "Resources"
DB[(Database)]
API[External APIs]
FS[File System]
TOOLS[Dev Tools]
end
CC <--> AGENT
AGENT <--> MCP1
AGENT <--> MCP2
AGENT <--> MCP3
MCP1 --> DB
MCP2 --> API
MCP3 --> FS
MCP3 --> TOOLS
Creating Custom Agents¶
Agent Configuration Structure¶
# .claude/agents/backend-expert.yaml
name: backend-expert
description: Senior backend architect with 20+ years experience
model: claude-3-opus
temperature: 0.3
max_tokens: 4096
capabilities:
- microservices_design
- database_optimization
- api_development
- performance_tuning
- security_implementation
tools:
- read
- write
- edit
- multi_edit
- bash
- grep
- glob
- web_fetch
- task
prompts:
system: |
You are a senior backend architect with deep expertise in:
- .NET Core, Python, Node.js
- Microservices and distributed systems
- Database design and optimization
- API design (REST, GraphQL, gRPC)
- Message queuing and event-driven architecture
- Security best practices
- Performance optimization
Always:
- Write production-quality code
- Consider scalability and maintainability
- Implement proper error handling
- Add comprehensive tests
- Document complex logic
analyze_codebase: |
Analyze the codebase architecture and identify:
1. Current patterns and practices
2. Potential improvements
3. Security vulnerabilities
4. Performance bottlenecks
5. Technical debt
implement_feature: |
Implement the requested feature following these steps:
1. Understand existing architecture
2. Design solution with proper patterns
3. Implement with tests
4. Add documentation
5. Ensure backward compatibility
Creating Specialized Agents¶
// meta-agent configuration for creating new agents
interface AgentDefinition {
name: string;
description: string;
expertise: string[];
tools: ToolName[];
behaviors: BehaviorConfig;
prompts: PromptTemplates;
}
class MetaAgent {
async createAgent(spec: string): Promise<AgentDefinition> {
const definition = await this.analyzeRequirements(spec);
return {
name: definition.name,
description: definition.description,
expertise: this.identifyExpertise(spec),
tools: this.selectTools(definition.capabilities),
behaviors: this.configureBehaviors(definition),
prompts: this.generatePrompts(definition)
};
}
private selectTools(capabilities: string[]): ToolName[] {
const toolMap = {
'code_analysis': ['read', 'grep', 'glob'],
'code_generation': ['write', 'edit', 'multi_edit'],
'testing': ['bash', 'read', 'write'],
'debugging': ['read', 'bash', 'grep'],
'documentation': ['read', 'write', 'edit'],
'deployment': ['bash', 'edit', 'task']
};
const tools = new Set<ToolName>();
for (const capability of capabilities) {
const mappedTools = toolMap[capability] || [];
mappedTools.forEach(tool => tools.add(tool));
}
return Array.from(tools);
}
private generatePrompts(definition: AgentDefinition): PromptTemplates {
return {
system: this.buildSystemPrompt(definition),
task_planning: this.buildPlanningPrompt(definition),
code_review: this.buildReviewPrompt(definition),
error_handling: this.buildErrorPrompt(definition)
};
}
}
MCP Server Implementation¶
Basic MCP Server¶
// ec-mcp-server implementation
import { Server } from '@modelcontextprotocol/sdk';
import {
CallToolRequest,
ListResourcesRequest,
ReadResourceRequest
} from '@modelcontextprotocol/sdk/types';
class EasyChampMCPServer {
private server: Server;
private db: DatabaseConnection;
constructor() {
this.server = new Server({
name: 'ec-mcp-server',
version: '1.0.0'
});
this.setupHandlers();
}
private setupHandlers() {
// List available resources
this.server.setRequestHandler(
'resources/list',
async (request: ListResourcesRequest) => {
return {
resources: [
{
uri: 'championship://list',
name: 'Championships',
mimeType: 'application/json'
},
{
uri: 'standings://current',
name: 'Current Standings',
mimeType: 'application/json'
},
{
uri: 'matches://upcoming',
name: 'Upcoming Matches',
mimeType: 'application/json'
}
]
};
}
);
// Read specific resource
this.server.setRequestHandler(
'resources/read',
async (request: ReadResourceRequest) => {
const { uri } = request.params;
switch (uri) {
case 'championship://list':
return await this.getChampionships();
case 'standings://current':
return await this.getCurrentStandings();
case 'matches://upcoming':
return await this.getUpcomingMatches();
default:
throw new Error(`Unknown resource: ${uri}`);
}
}
);
// Tool execution
this.server.setRequestHandler(
'tools/call',
async (request: CallToolRequest) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'create_championship':
return await this.createChampionship(args);
case 'update_match_result':
return await this.updateMatchResult(args);
case 'calculate_standings':
return await this.calculateStandings(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
}
);
}
private async getChampionships() {
const championships = await this.db.query(`
SELECT id, name, status, start_date, end_date
FROM championships
WHERE status = 'active'
ORDER BY start_date DESC
`);
return {
contents: [{
uri: 'championship://list',
mimeType: 'application/json',
text: JSON.stringify(championships, null, 2)
}]
};
}
private async createChampionship(args: any) {
const { name, startDate, endDate, rules } = args;
const result = await this.db.transaction(async (trx) => {
// Create championship
const [championship] = await trx('championships').insert({
name,
start_date: startDate,
end_date: endDate,
rules: JSON.stringify(rules),
status: 'pending'
}).returning('*');
// Initialize standings table
await trx('standings').insert({
championship_id: championship.id,
initialized_at: new Date()
});
return championship;
});
return {
content: [{
type: 'text',
text: `Championship "${name}" created with ID: ${result.id}`
}]
};
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('EasyChamp MCP Server started');
}
}
// Start server
const server = new EasyChampMCPServer();
server.start().catch(console.error);
Advanced MCP Server with AI¶
// ec-mcp-server-ai implementation
class AIEnhancedMCPServer {
private llm: LLMClient;
private vectorStore: VectorStore;
constructor() {
this.llm = new ClaudeClient();
this.vectorStore = new QdrantClient();
this.setupAITools();
}
private setupAITools() {
this.server.setRequestHandler(
'tools/call',
async (request: CallToolRequest) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'analyze_performance':
return await this.analyzePerformance(args);
case 'suggest_lineup':
return await this.suggestLineup(args);
case 'predict_match':
return await this.predictMatch(args);
case 'generate_report':
return await this.generateReport(args);
}
}
);
}
private async analyzePerformance(args: any) {
const { teamId, matches = 5 } = args;
// Fetch recent match data
const recentMatches = await this.db.query(`
SELECT m.*,
t1.name as home_team,
t2.name as away_team
FROM matches m
JOIN teams t1 ON m.home_team_id = t1.id
JOIN teams t2 ON m.away_team_id = t2.id
WHERE (m.home_team_id = $1 OR m.away_team_id = $1)
AND m.status = 'completed'
ORDER BY m.played_at DESC
LIMIT $2
`, [teamId, matches]);
// Generate AI analysis
const analysis = await this.llm.complete({
prompt: `Analyze the performance of team ${teamId} based on their recent matches:
${JSON.stringify(recentMatches, null, 2)}
Provide insights on:
1. Win/loss pattern
2. Goal scoring trends
3. Defensive performance
4. Key strengths and weaknesses
5. Recommendations for improvement`,
maxTokens: 1000
});
return {
content: [{
type: 'text',
text: analysis
}]
};
}
private async suggestLineup(args: any) {
const { teamId, opponentId, matchDate } = args;
// Get team roster and player stats
const players = await this.getPlayerStats(teamId);
const opponentStyle = await this.getTeamPlayStyle(opponentId);
// Use AI to suggest optimal lineup
const suggestion = await this.llm.complete({
prompt: `Suggest the optimal lineup for team ${teamId} against ${opponentId}.
Available players and their recent form:
${JSON.stringify(players, null, 2)}
Opponent play style:
${JSON.stringify(opponentStyle, null, 2)}
Consider:
- Player fitness and recent performance
- Tactical matchups
- Historical performance against similar opponents
Provide:
1. Recommended formation
2. Starting XI with positions
3. Key tactical instructions
4. Substitution strategy`,
maxTokens: 1500
});
// Store suggestion for future analysis
await this.vectorStore.upsert({
id: `lineup-${teamId}-${opponentId}-${matchDate}`,
values: await this.llm.embed(suggestion),
metadata: {
teamId,
opponentId,
matchDate,
suggestion
}
});
return {
content: [{
type: 'text',
text: suggestion
}]
};
}
private async predictMatch(args: any) {
const { homeTeamId, awayTeamId } = args;
// Fetch historical data
const headToHead = await this.getHeadToHeadStats(homeTeamId, awayTeamId);
const homeForm = await this.getTeamForm(homeTeamId);
const awayForm = await this.getTeamForm(awayTeamId);
// Retrieve similar match patterns
const similarMatches = await this.findSimilarMatches({
homeForm,
awayForm,
headToHead
});
// Generate prediction
const prediction = await this.llm.complete({
prompt: `Predict the outcome of the match:
Home Team (${homeTeamId}):
- Recent form: ${JSON.stringify(homeForm)}
Away Team (${awayTeamId}):
- Recent form: ${JSON.stringify(awayForm)}
Head-to-head history:
${JSON.stringify(headToHead)}
Similar match patterns:
${JSON.stringify(similarMatches)}
Provide:
1. Predicted score
2. Probability of each outcome (home win, draw, away win)
3. Key factors influencing the prediction
4. Potential game-changing moments
5. Confidence level (0-100%)`,
maxTokens: 1000,
temperature: 0.3 // Lower temperature for more consistent predictions
});
return {
content: [{
type: 'text',
text: prediction
}]
};
}
}
Agent Patterns¶
Task Decomposition Agent¶
class TaskDecompositionAgent {
async planTask(description: string): Promise<TaskPlan> {
// Analyze task complexity
const complexity = await this.assessComplexity(description);
if (complexity.score > 0.7) {
// Complex task - decompose into subtasks
return await this.decomposeComplexTask(description);
} else {
// Simple task - direct execution
return {
steps: [{
description,
tools: this.identifyRequiredTools(description),
estimatedTime: complexity.estimatedTime
}]
};
}
}
private async decomposeComplexTask(description: string): Promise<TaskPlan> {
const decomposition = await this.llm.complete({
prompt: `Decompose this task into atomic steps:
Task: ${description}
Requirements:
1. Each step should be independently executable
2. Steps should be ordered by dependency
3. Include required tools for each step
4. Estimate time for each step
5. Identify potential blockers
Format as JSON:
{
"steps": [
{
"id": "step-1",
"description": "...",
"dependencies": [],
"tools": ["read", "edit"],
"estimatedMinutes": 5,
"blockers": []
}
]
}`,
responseFormat: 'json'
});
return JSON.parse(decomposition);
}
}
Code Review Agent¶
class CodeReviewAgent {
async reviewCode(files: string[]): Promise<ReviewReport> {
const report: ReviewReport = {
summary: '',
issues: [],
suggestions: [],
score: 0
};
for (const file of files) {
const content = await this.readFile(file);
const language = this.detectLanguage(file);
// Analyze different aspects
const [
syntaxIssues,
securityIssues,
performanceIssues,
styleIssues
] = await Promise.all([
this.checkSyntax(content, language),
this.checkSecurity(content, language),
this.checkPerformance(content, language),
this.checkStyle(content, language)
]);
report.issues.push(
...syntaxIssues,
...securityIssues,
...performanceIssues,
...styleIssues
);
// Generate improvement suggestions
const suggestions = await this.generateSuggestions(
content,
report.issues,
language
);
report.suggestions.push(...suggestions);
}
// Calculate overall score
report.score = this.calculateScore(report);
// Generate summary
report.summary = await this.generateSummary(report);
return report;
}
private async checkSecurity(code: string, language: string): Promise<Issue[]> {
const patterns = {
typescript: [
{ pattern: /eval\(/, severity: 'high', message: 'Avoid using eval()' },
{ pattern: /innerHTML\s*=/, severity: 'medium', message: 'Potential XSS vulnerability' },
{ pattern: /password.*=.*["']/, severity: 'high', message: 'Hardcoded password detected' }
],
python: [
{ pattern: /exec\(/, severity: 'high', message: 'Avoid using exec()' },
{ pattern: /pickle\.loads/, severity: 'medium', message: 'Unsafe deserialization' },
{ pattern: /os\.system/, severity: 'medium', message: 'Command injection risk' }
]
};
const issues: Issue[] = [];
const langPatterns = patterns[language] || [];
for (const { pattern, severity, message } of langPatterns) {
const matches = code.matchAll(pattern);
for (const match of matches) {
issues.push({
type: 'security',
severity,
message,
line: this.getLineNumber(code, match.index),
suggestion: await this.getSuggestion(message, code, match)
});
}
}
return issues;
}
}
Debugging Agent¶
class DebuggingAgent {
async debugError(error: Error, context: DebugContext): Promise<Solution> {
// Collect relevant information
const debugInfo = await this.gatherDebugInfo(error, context);
// Search for similar issues
const similarIssues = await this.searchSimilarIssues(error);
// Analyze error pattern
const errorAnalysis = await this.analyzeError(error, debugInfo);
// Generate solution
const solution = await this.llm.complete({
prompt: `Debug this error:
Error: ${error.message}
Stack: ${error.stack}
Context:
${JSON.stringify(debugInfo, null, 2)}
Similar issues found:
${JSON.stringify(similarIssues, null, 2)}
Analysis:
${JSON.stringify(errorAnalysis, null, 2)}
Provide:
1. Root cause explanation
2. Step-by-step fix
3. Code changes required
4. Prevention strategies`,
maxTokens: 2000
});
// Verify solution
const verification = await this.verifySolution(solution, context);
return {
explanation: solution.explanation,
steps: solution.steps,
code: solution.code,
verified: verification.success,
confidence: verification.confidence
};
}
private async gatherDebugInfo(error: Error, context: DebugContext) {
return {
environment: process.env.NODE_ENV,
nodeVersion: process.version,
dependencies: await this.getDependencies(),
recentChanges: await this.getRecentChanges(context.file),
relatedFiles: await this.findRelatedFiles(context.file),
logs: await this.getRecentLogs(context.timestamp)
};
}
}
Testing Agents¶
describe('Agent Testing Framework', () => {
let agent: BackendExpertAgent;
let mockMCP: MockMCPServer;
beforeEach(() => {
mockMCP = new MockMCPServer();
agent = new BackendExpertAgent({
mcp: mockMCP,
tools: ['read', 'write', 'edit']
});
});
describe('Task Execution', () => {
it('should implement CRUD operations correctly', async () => {
// Arrange
const task = 'Create a REST API for user management';
// Act
const result = await agent.executeTask(task);
// Assert
expect(result.files).toContain('user.controller.ts');
expect(result.files).toContain('user.service.ts');
expect(result.files).toContain('user.dto.ts');
expect(result.tests).toHaveLength(greaterThan(0));
});
it('should handle errors gracefully', async () => {
// Arrange
mockMCP.simulateError('Database connection failed');
// Act
const result = await agent.executeTask('Query database');
// Assert
expect(result.error).toBeDefined();
expect(result.recovery).toBeDefined();
expect(result.recovery.steps).toHaveLength(greaterThan(0));
});
});
describe('Code Quality', () => {
it('should generate secure code', async () => {
// Act
const code = await agent.generateCode('Authentication service');
// Assert
const securityAudit = await runSecurityAudit(code);
expect(securityAudit.vulnerabilities).toHaveLength(0);
expect(code).toContain('bcrypt');
expect(code).toContain('jwt');
expect(code).not.toContain('eval');
});
});
});
Performance Optimization¶
Agent Response Caching¶
class AgentCache {
private cache = new Map<string, CachedResponse>();
async getCachedOrExecute(
agent: Agent,
task: string,
ttl: number = 3600000
): Promise<any> {
const cacheKey = this.generateKey(agent.name, task);
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.response;
}
const response = await agent.execute(task);
this.cache.set(cacheKey, {
response,
timestamp: Date.now()
});
return response;
}
}
Parallel Agent Execution¶
class AgentOrchestrator {
async executeParallel(tasks: AgentTask[]): Promise<any[]> {
const groups = this.groupByDependency(tasks);
const results = [];
for (const group of groups) {
// Execute independent tasks in parallel
const groupResults = await Promise.all(
group.map(task => this.executeTask(task))
);
results.push(...groupResults);
// Update context for dependent tasks
this.updateContext(groupResults);
}
return results;
}
private groupByDependency(tasks: AgentTask[]): AgentTask[][] {
// Topological sort based on dependencies
const graph = this.buildDependencyGraph(tasks);
return this.topologicalSort(graph);
}
}
Best Practices¶
- Agent Specialization: Create focused agents for specific domains
- Tool Selection: Only include necessary tools to reduce complexity
- Prompt Engineering: Craft clear, specific prompts for consistent behavior
- Error Handling: Implement robust error recovery mechanisms
- Testing: Comprehensive testing of agent behaviors
- Monitoring: Track agent performance and accuracy
- Security: Implement access controls and input validation
- Documentation: Document agent capabilities and limitations
References¶
Last updated: 2025-09-09