Skip to content

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

  1. Agent Specialization: Create focused agents for specific domains
  2. Tool Selection: Only include necessary tools to reduce complexity
  3. Prompt Engineering: Craft clear, specific prompts for consistent behavior
  4. Error Handling: Implement robust error recovery mechanisms
  5. Testing: Comprehensive testing of agent behaviors
  6. Monitoring: Track agent performance and accuracy
  7. Security: Implement access controls and input validation
  8. Documentation: Document agent capabilities and limitations

References


Last updated: 2025-09-09