Developer Copilot

This example demonstrates how to use Model Context Protocol to build a coding assistant that understands your codebase, development patterns, and can access APIs and documentation.

Overview

The Developer Copilot uses MCP to structure all the context needed for effective code assistance, including:

  • Code Repository Context

    Project structure, file contents, and dependencies

  • Documentation Access

    API documentation, coding standards, and best practices

  • Development Tools

    Tools for code generation, refactoring, and documentation lookup

// Developer Copilot MCP Context
import { MCPContext } from '@modl/mcp';

const developerCopilot = new MCPContext({
  systemInstruction: "You are a helpful coding assistant that helps developers write, understand, and debug code.",
  userGoal: "Help the developer implement and improve their code efficiently.",
  projectContext: {
    name: "TaskManager",
    language: "TypeScript",
    framework: "React",
    dependencies: [
      { name: "react", version: "18.2.0" },
      { name: "next", version: "14.0.0" },
      { name: "tailwindcss", version: "3.3.0" }
    ],
    structure: [
      { path: "src/components/", description: "React components" },
      { path: "src/pages/", description: "Next.js pages" },
      { path: "src/lib/", description: "Utility functions and hooks" }
    ],
    codingStandards: {
      style: "Use functional components with hooks",
      naming: "camelCase for variables, PascalCase for components",
      testing: "Jest and React Testing Library"
    }
  },
  tools: [
    {
      name: "searchDocumentation",
      description: "Search API documentation",
      parameters: {
        query: "string",
        source: "string"
      }
    },
    {
      name: "generateCode",
      description: "Generate code based on a description",
      parameters: {
        description: "string",
        language: "string",
        context: "string"
      }
    },
    {
      name: "explainCode",
      description: "Explain what a piece of code does",
      parameters: {
        code: "string",
        detailLevel: "string"
      }
    },
    {
      name: "refactorCode",
      description: "Suggest refactoring for a piece of code",
      parameters: {
        code: "string",
        goal: "string"
      }
    }
  ],
  memory: {}
});

export default developerCopilot;

Key Components

Project Context

Structured representation of the codebase, dependencies, and coding standards.

projectContext: {
  name: "TaskManager",
  language: "TypeScript",
  framework: "React",
  dependencies: [
    { name: "react", version: "18.2.0" },
    // More dependencies...
  ],
  // More project details...
}

Development Tools

Tools for code generation, explanation, and refactoring.

tools: [
  {
    name: "generateCode",
    description: "Generate code based on a description",
    parameters: {
      description: "string",
      language: "string",
      context: "string"
    }
  },
  // More tools...
]

Using the Context

Once the MCP context is defined, it can be used with any LLM provider to create a personalized coding assistant:

import OpenAI from 'openai';
import developerCopilot from './developer-copilot.js';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

async function getCodeAssistance(userQuery, codeSnippet = null) {
  // Update the context with the current code snippet if provided
  if (codeSnippet) {
    developerCopilot.updateContext({
      currentCode: codeSnippet
    });
  }
  
  // Compile the context for OpenAI
  const context = developerCopilot.compileFor('openai');
  
  // Prepare the messages
  const messages = [
    {
      role: 'system',
      content: context.systemMessage
    },
    {
      role: 'user',
      content: codeSnippet 
        ? `${userQuery}

Here's the code I'm working with:
```
${codeSnippet}
```` 
        : userQuery
    }
  ];
  
  // Use the context in your OpenAI call
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    messages: messages,
    tools: context.tools
  });
  
  return response.choices[0].message.content;
}

// Example usage
getCodeAssistance(
  "Can you help me implement a React hook for fetching data with loading and error states?",
  null
);

Live Demo

Try out the Developer Copilot with this interactive demo. Ask for code examples, explanations, or refactoring suggestions.

Developer Copilot

D

Hello! I'm your Developer Copilot for the TaskManager project. How can I help you with your code today?

Can you help me implement a React hook for fetching data with loading and error states?

D

I'd be happy to help you implement a React hook for data fetching with loading and error states. Here's a custom hook that follows the project's coding standards:

// src/lib/hooks/useFetch.ts
import { useState, useEffect } from 'react';

interface UseFetchOptions {
  initialData?: any;
  dependencies?: any[];
}

export function useFetch<T>(
  url: string, 
  options?: UseFetchOptions
) {
  const [data, setData] = useState<T | undefined>(
    options?.initialData
  );
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let isMounted = true;
    const fetchData = async () => {
      setIsLoading(true);
      setError(null);
      
      try {
        const response = await fetch(url);
        
        if (!response.ok) {
          throw new Error(
            `Error ${response.status}: ${response.statusText}`
          );
        }
        
        const result = await response.json();
        
        if (isMounted) {
          setData(result);
        }
      } catch (err) {
        if (isMounted) {
          setError(err instanceof Error ? err : new Error(String(err)));
        }
      } finally {
        if (isMounted) {
          setIsLoading(false);
        }
      }
    };

    fetchData();

    return () => {
      isMounted = false;
    };
  }, [url, ...(options?.dependencies || [])]);

  return { data, isLoading, error };
}

You can use this hook in your components like this:

// src/components/TaskList.tsx
import { useFetch } from '../lib/hooks/useFetch';

interface Task {
  id: string;
  title: string;
  completed: boolean;
}

export function TaskList() {
  const { 
    data: tasks, 
    isLoading, 
    error 
  } = useFetch<Task[]>('/api/tasks');

  if (isLoading) return <div>Loading tasks...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <ul>
      {tasks?.map(task => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

This hook follows the project's TypeScript and React standards. Would you like me to explain any part of this implementation in more detail?

This is a demo interface. The copilot is not actually functional.