AI Tools Compared

Setting up a Model Context Protocol server for your custom project documentation transforms how AI coding assistants understand and interact with your codebase. When your AI tool can access your internal docs, architecture decisions, and API specifications directly through MCP, you get more accurate suggestions that align with your project’s conventions.

This guide walks you through building an MCP server that serves your documentation to Claude, Cursor, and other compatible AI tools.

Prerequisites

Before you begin, ensure you have:

Step 1: Initialize Your MCP Server Project

Create a new directory for your MCP server and initialize it with the necessary dependencies:

mkdir myproject-mcp-server
cd myproject-mcp-server
npm init -y
npm install @modelcontextprotocol/server-sdk @types/node typescript

Create a TypeScript configuration file:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"]
}

Step 2: Structure Your Documentation

Organize your project documentation in a way that MCP can easily parse. A clean structure helps your server deliver relevant information to AI tools:

myproject/
├── docs/
│   ├── architecture/
│   │   └── system-design.md
│   ├── api/
│   │   └── endpoints.md
│   ├── setup/
│   │   └── local-development.md
│   └── conventions/
│       └── coding-standards.md

Each document should have clear headings and practical content that an AI assistant can reference when generating code or answering questions.

Step 3: Build the MCP Server

Create your server implementation in src/index.ts. This server will read your documentation files and expose them through MCP tools:

import { Server } from '@modelcontextprotocol/server-sdk';
import { StdioServerTransport } from '@modelcontextprotocol/server-sdk/stdio';
import * as fs from 'fs';
import * as path from 'path';

interface DocFile {
  path: string;
  content: string;
  category: string;
}

function loadDocumentation(docsDir: string): DocFile[] {
  const docs: DocFile[] = [];

  function walkDir(dir: string, category: string) {
    const files = fs.readdirSync(dir);

    for (const file of files) {
      const fullPath = path.join(dir, file);
      const stat = fs.statSync(fullPath);

      if (stat.isDirectory()) {
        walkDir(fullPath, file);
      } else if (file.endsWith('.md')) {
        const content = fs.readFileSync(fullPath, 'utf-8');
        docs.push({
          path: fullPath.replace(docsDir, '').replace('.md', ''),
          content,
          category
        });
      }
    }
  }

  walkDir(docsDir, 'general');
  return docs;
}

const docsDir = path.join(process.cwd(), 'docs');
const documentation = loadDocumentation(docsDir);

const server = new Server(
  {
    name: 'project-docs-server',
    version: '1.0.0'
  },
  {
    capabilities: {
      tools: {}
    }
  }
);

server.setRequestHandler('tools/list', async () => {
  return {
    tools: [
      {
        name: 'search_docs',
        description: 'Search through project documentation by keyword or topic',
        inputSchema: {
          type: 'object',
          properties: {
            query: {
              type: 'string',
              description: 'Search query for documentation'
            },
            category: {
              type: 'string',
              description: 'Optional category to filter results',
              enum: ['architecture', 'api', 'setup', 'conventions', 'general']
            }
          },
          required: ['query']
        }
      },
      {
        name: 'get_doc',
        description: 'Retrieve a specific documentation file by path',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: 'Documentation path (e.g., /api/endpoints)'
            }
          },
          required: ['path']
        }
      },
      {
        name: 'list_docs',
        description: 'List all available documentation with summaries',
        inputSchema: {
          type: 'object',
          properties: {}
        }
      }
    ]
  };
});

server.setRequestHandler('tools/call', async (request) => {
  const { name, arguments: args } = request.params;

  if (name === 'search_docs') {
    const query = args.query.toLowerCase();
    const results = documentation.filter(doc =>
      doc.content.toLowerCase().includes(query) ||
      doc.path.toLowerCase().includes(query)
    );

    if (args.category) {
      return {
        content: [{
          type: 'text',
          text: JSON.stringify(
            results.filter(d => d.category === args.category).map(d => ({
              path: d.path,
              category: d.category,
              preview: d.content.substring(0, 200)
            })),
            null,
            2
          )
        }]
      };
    }

    return {
      content: [{
        type: 'text',
        text: JSON.stringify(
          results.map(d => ({
            path: d.path,
            category: d.category,
            preview: d.content.substring(0, 200)
          })),
          null,
          2
        )
      }]
    };
  }

  if (name === 'get_doc') {
    const doc = documentation.find(d => d.path === args.path);

    if (!doc) {
      return {
        content: [{
          type: 'text',
          text: `Document not found: ${args.path}`
        }]
      };
    }

    return {
      content: [{
        type: 'text',
        text: doc.content
      }]
    };
  }

  if (name === 'list_docs') {
    return {
      content: [{
        type: 'text',
        text: JSON.stringify(
          documentation.map(d => ({
            path: d.path,
            category: d.category,
            title: d.content.match(/^#\s+(.+)$/m)?.[1] || 'Untitled'
          })),
          null,
          2
        )
      }]
    };
  }

  throw new Error(`Unknown tool: ${name}`);
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('Project documentation MCP server running');
}

main().catch(console.error);

Step 4: Configure Your AI Tool

Each AI tool has its own method for adding MCP servers. For Claude Desktop, create or edit ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "project-docs": {
      "command": "node",
      "args": ["/path/to/your/myproject-mcp-server/dist/index.js"],
      "env": {
        "DOCS_DIR": "/path/to/your/project/docs"
      }
    }
  }
}

For Cursor, add the server configuration in Settings → MCP Servers:

{
  "mcpServers": {
    "project-docs": {
      "command": "node",
      "args": ["C:\\path\\to\\your\\myproject-mcp-server\\dist\\index.js"]
    }
  }
}

Step 5: Test Your Implementation

Build and test your server:

npm run build
node dist/index.js

Verify the server is working by checking if your AI tool can access the documentation tools. In Claude, you should see three new tools available: search_docs, get_doc, and list_docs.

Using Your Documentation Server

Once configured, your AI coding assistant can now reference your project documentation. Here are practical examples:

Asking about architecture:

“How is authentication handled in this project?”

The AI will search your docs for authentication-related content and provide accurate guidance based on your actual implementation.

Getting setup instructions:

“What are the steps to set up local development?”

The AI retrieves your setup documentation and provides precise instructions.

Finding API conventions:

“What format should I use for API response errors?”

Your conventions document gets searched and relevant details are extracted.

Best Practices

Keep your documentation server effective by following these practices:

Update documentation regularly. Your MCP server serves static content, so rebuild and restart when docs change.

Use clear, scannable headings. AI tools parse markdown headings to understand document structure quickly.

Include code examples. Practical examples help AI tools generate more accurate code that matches your patterns.

Add troubleshooting sections. Common issues and solutions in your docs help AI assistants diagnose problems faster.

Troubleshooting

If your server isn’t connecting, verify the path to your compiled JavaScript is correct. Check that your documentation directory exists and contains markdown files. For permission issues, ensure Node has read access to your docs directory.

Some AI tools require a restart after adding MCP server configuration. Close and reopen the application to load the new server.

Built by theluckystrike — More at zovo.one