Skip to content

MCP Server Development Guide for Mnemoverse Docs ​

Version: 1.1.0
Date: 2025-07-30
Status: Implementation complete - production ready


βœ… IMPLEMENTATION COMPLETE
This document covers the technical implementation of MCP servers for Mnemoverse documentation. We have successfully published two production-ready packages: @mnemoverse/mcp-docs-server (production server for Mnemoverse docs) and @mcp-x/mcp-docs-server (universal template for any documentation project). The architecture leverages VitePress's search capabilities and provides standardized MCP protocol access. Both servers are live, tested, and ready for use in IDEs and AI tools.


Document Purpose ​

This document provides technical specifications and implementation details for MCP (Model Context Protocol) servers in the Mnemoverse ecosystem. It covers both the production server powering Mnemoverse documentation and the universal template for community use.

Available Packages ​

@mnemoverse/mcp-docs-server ​

Production server - Powers Mnemoverse's live documentation with research library access, optimized for our specific content structure.

@mcp-x/mcp-docs-server ​

Universal template - Community-friendly template for creating MCP servers for any documentation project. Features include markdown parsing, frontmatter support, and configurable search.

Architectural Overview ​

Existing System ​

  • VitePress documentation with search
  • Document structure: /docs/research/, /docs/guides/, /docs/vision/
  • Search index: MiniSearch with fuzzy search settings
  • Static hosting: GitHub Pages
  • Published packages: Both production and template servers on npm

Target MCP Server Architecture ​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     MCP Client                              β”‚
β”‚                   (AI Agent/Claude)                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚ JSON-RPC 2.0 / HTTP
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 β–Ό                                           β”‚
β”‚              MCP Server                                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Tools API     β”‚  β”‚  Resources API  β”‚  β”‚ Search API  β”‚  β”‚
β”‚  β”‚                 β”‚  β”‚                 β”‚  β”‚             β”‚  β”‚
β”‚  β”‚ β€’ search_docs   β”‚  β”‚ β€’ get_document  β”‚  β”‚ VitePress   β”‚  β”‚
β”‚  β”‚ β€’ list_docs     β”‚  β”‚ β€’ get_section   β”‚  β”‚ Search      β”‚  β”‚
β”‚  β”‚ β€’ validate_refs β”‚  β”‚ β€’ get_metadata  β”‚  β”‚ Integration β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                            β”‚                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚          File System    β–Ό                              β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
β”‚  β”‚  β”‚         /docs/                                   β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”œβ”€β”€ research/                                   β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”‚   β”œβ”€β”€ mcp-x-protocol-spec.md                  β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”‚   β”œβ”€β”€ memory-solutions-landscape.md           β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”‚   └── ...                                     β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”œβ”€β”€ guides/                                     β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  β”œβ”€β”€ vision/                                     β”‚  β”‚ β”‚
β”‚  β”‚  β”‚  └── .vitepress/config.mjs                       β”‚  β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Start Guide ​

πŸš€ Try the Production Server (2 minutes) ​

bash
# Install the Mnemoverse docs server
npm install -g @mnemoverse/mcp-docs-server

# Test it works
@mnemoverse/mcp-docs-server --version

Configure Claude Desktop:

json
{
  "mcpServers": {
    "mnemoverse-docs": {
      "command": "node",
      "args": ["/usr/local/lib/node_modules/@mnemoverse/mcp-docs-server/index.js"]
    }
  }
}

πŸ› οΈ Create Your Own MCP Server (5 minutes) ​

bash
# Use the universal template
npm install -g @mcp-x/mcp-docs-server

# Clone for customization
git clone https://github.com/mnemoverse/mcp-docs-server.git
cd mcp-docs-server
npm install
npm run build

Test your server:

bash
# Start in development mode
npm run dev

# Test with MCP Inspector
npx @modelcontextprotocol/inspector node dist/index.js

πŸ§ͺ Development Workflow ​

bash
# 1. Fork the template
git clone https://github.com/mnemoverse/mcp-docs-server.git my-docs-server
cd my-docs-server

# 2. Install dependencies
npm install

# 3. Configure for your docs
export DOCS_BASE_URL="https://your-site.com"
export PROJECT_NAME="Your Project"

# 4. Start development
npm run dev

# 5. Test integration
npx @modelcontextprotocol/inspector node dist/index.js

Implementation Status ​

Production Deployment βœ… ​

Both MCP servers are live and production-ready:

@mnemoverse/mcp-docs-server

  • πŸ“¦ npm: Published and maintained
  • 🌐 GitHub: mnemoverse/mnemoverse-docs
  • 🎯 Purpose: Production server for Mnemoverse documentation
  • πŸ”§ Tools: mnemoverseDocs, mnemoverseResearch, mnemoverseSearch

@mcp-x/mcp-docs-server

  • πŸ“¦ npm: Published as universal template
  • 🌐 GitHub: mnemoverse/mcp-docs-server
  • 🎯 Purpose: Universal template for any documentation project
  • πŸ”§ Tools: list_documents, get_document, search_docs

Usage Examples ​

bash
# Use Mnemoverse production server
npx @mnemoverse/mcp-docs-server

# Use universal template for your docs  
npx @mcp-x/mcp-docs-server /path/to/your/docs

Technical Requirements ​

SDK Selection Analysis ​

Current Implementation: TypeScript SDK

Production advantages realized:

  1. VitePress Integration: Direct integration with existing Node.js ecosystem βœ…
  2. Fast Development: Rapid deployment and iteration βœ…
  3. npm Distribution: Easy installation via npx βœ…
  4. Cross-platform: Works on macOS, Linux, Windows βœ…

Alternative for Enterprise/Scale: Java SDK

Java SDK advantages:

  • πŸ”₯ Official Maturity: 2.1k stars, active development, enterprise-ready
  • ⚑ Performance: JVM optimizations for high loads
  • πŸ› οΈ Rich Features: Full MCP support + Spring Boot integration
  • πŸ“¦ Multiple Transports: STDIO, HTTP SSE, WebFlux, WebMVC
  • πŸ§ͺ Testing Suite: Dedicated mcp-test utilities

When to choose Java SDK:

  • Phase 2/3: Standalone MCP server deployment
  • High-load production environment (>1000 concurrent requests)
  • Microservices architecture
  • Enterprise integration requirements

Alternatives also considered:

  • Python SDK: Good for ML integrations, but requires additional infrastructure
  • Go SDK: High performance, but harder to integrate with VitePress
  • Rust SDK: Maximum performance, but overly complex for this task

Migration Path to Java SDK (Phase 2/3) ​

Architecture for Java SDK deployment:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 Load Balancer                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 β–Ό                               β”‚
β”‚         Spring Boot MCP Server                  β”‚ 
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚          Spring WebFlux                     β”‚ β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚ β”‚
β”‚  β”‚  β”‚ MCP Tools   β”‚  β”‚  Document Service   β”‚   β”‚  
β”‚  β”‚  β”‚ Controller  β”‚  β”‚  (Cache Layer)      β”‚   β”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                     β”‚                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚              File Watcher                   β”‚ β”‚
β”‚  β”‚        (Spring Boot Actuator)               β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              File System                        β”‚
β”‚         (VitePress docs via NFS/Volume)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Java SDK Implementation Highlights:

java
@RestController
@RequestMapping("/mcp")
public class McpController {
    
    @Autowired
    private McpServer mcpServer;
    
    @PostMapping
    public Mono<McpResponse> handleMcpRequest(@RequestBody McpRequest request) {
        return mcpServer.handleRequest(request)
            .timeout(Duration.ofSeconds(30))
            .onErrorResume(this::handleError);
    }
}

@Service
public class DocumentSearchService {
    
    @Cacheable("document-search")
    public List<SearchResult> searchDocuments(String query, SearchOptions options) {
        // High-performance search with Lucene/Elasticsearch integration
        return luceneSearchEngine.search(query, options);
    }
}

Migration Benefits:

  • πŸš€ 10x Performance: JVM optimizations for high-load scenarios
  • πŸ“Š Spring Metrics: Built-in monitoring with Micrometer/Prometheus
  • πŸ”„ Auto-scaling: Container orchestration ready (K8s/Docker Swarm)
  • πŸ›‘οΈ Security: Spring Security integration for enterprise auth
  • πŸ“ˆ Observability: Distributed tracing with Zipkin/Jaeger

Migration Timeline:

  • Phase 1: TypeScript MVP (current) - proof of concept
  • Phase 2: Performance evaluation - if >1000 concurrent users
  • Phase 3: Java SDK migration - enterprise deployment

Core Dependencies ​

Production Dependencies (Current Implementation):

json
{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.4",
    "gray-matter": "^4.0.3",
    "markdown-it": "^14.1.0", 
    "path": "^0.12.7",
    "fs": "^0.0.1-security"
  },
  "devDependencies": {
    "@types/node": "^20.11.30",
    "typescript": "^5.4.3",
    "tsx": "^4.7.1"
  },
  "peerDependencies": {
    "node": ">=18.0.0"
  }
}

Published Packages:

Installation and Usage:

bash
# Install production server
npm install -g @mnemoverse/mcp-docs-server

# Or use universal template
npm install -g @mcp-x/mcp-docs-server

# Test local installation
node -e "console.log(require('@mnemoverse/mcp-docs-server/package.json').version)"

Detailed MCP Server Specification ​

1. Server Configuration ​

typescript
// src/config/mcpServerConfig.ts
export interface McpServerConfig {
  name: string;
  version: string;
  description: string;
  
  // Transport configuration
  transport: {
    type: 'streamable-http' | 'stdio';
    host: string;
    port: number;
    cors: {
      origin: string[] | string;
      allowedHeaders: string[];
      exposedHeaders: string[];
    };
  };
  
  // Documentation paths
  docs: {
    basePath: string;
    searchIndexPath: string;
    cacheEnabled: boolean;
    watchForChanges: boolean;
  };
  
  // Feature flags
  features: {
    semanticSearch: boolean;
    documentValidation: boolean;
    citationExtraction: boolean;
    fullTextIndexing: boolean;
  };
}

export const defaultConfig: McpServerConfig = {
  name: "mnemoverse-docs-server",
  version: "1.0.0",
  description: "MCP Server for Mnemoverse Research Documentation",
  
  transport: {
    type: 'streamable-http',
    host: 'localhost',
    port: 3001,
    cors: {
      origin: ['http://localhost:3000', 'https://docs.mnemoverse.org'],
      allowedHeaders: ['Content-Type', 'mcp-session-id'],
      exposedHeaders: ['mcp-session-id']
    }
  },
  
  docs: {
    basePath: '../docs',  // Relative path from mcp-server/
    searchIndexPath: '../docs/.vitepress/cache',  // Use existing VitePress cache
    cacheEnabled: true,
    watchForChanges: true
  },
  
  features: {
    semanticSearch: false, // Phase 2
    documentValidation: true,
    citationExtraction: true,
    fullTextIndexing: true
  }
};

2. Tools Implementation ​

2.1 search_docs Tool ​

typescript
// src/tools/searchDocs.ts
import { z } from 'zod';
import { Tool } from '@modelcontextprotocol/sdk/types.js';

const SearchDocsInputSchema = z.object({
  query: z.string().min(1).describe("Search query for documentation"),
  filters: z.object({
    sections: z.array(z.enum(['research', 'guides', 'vision', 'legal'])).optional()
      .describe("Restrict search to specific documentation sections"),
    fileTypes: z.array(z.enum(['markdown', 'md'])).optional()
      .describe("File types to search (default: markdown)"),
    dateRange: z.object({
      from: z.string().optional().describe("ISO date string (YYYY-MM-DD)"),
      to: z.string().optional().describe("ISO date string (YYYY-MM-DD)")
    }).optional(),
    maxResults: z.number().min(1).max(50).default(10)
      .describe("Maximum number of results to return")
  }).optional(),
  options: z.object({
    fuzzy: z.boolean().default(true).describe("Enable fuzzy search"),
    prefix: z.boolean().default(true).describe("Enable prefix matching"),
    includeContent: z.boolean().default(false)
      .describe("Include document content in results"),
    highlightMatches: z.boolean().default(true)
      .describe("Highlight search matches in results")
  }).optional()
});

export const searchDocsTool: Tool = {
  name: "search_docs",
  description: "Search through Mnemoverse documentation using advanced search capabilities",
  inputSchema: SearchDocsInputSchema,
};

export interface SearchResult {
  title: string;
  uri: string;
  section: string;
  excerpt: string;
  score: number;
  lastModified: string;
  highlights?: string[];
  metadata?: DocumentMetadata;
}

export interface DocumentMetadata {
  tags: string[];
  authors: string[];
  dateCreated: string;
  wordCount: number;
  readingTime: number;
  citations?: Citation[];
}

export interface Citation {
  key: string;
  title: string;
  authors: string[];
  year: number;
  doi?: string;
  url?: string;
}

2.2 list_documents Tool ​

typescript
// src/tools/listDocuments.ts
const ListDocumentsInputSchema = z.object({
  section: z.enum(['research', 'guides', 'vision', 'legal', 'all'])
    .default('all').describe("Documentation section to list"),
  sortBy: z.enum(['title', 'date', 'size', 'relevance'])
    .default('title').describe("Sort criteria for documents"),
  order: z.enum(['asc', 'desc']).default('asc').describe("Sort order"),
  includeMetadata: z.boolean().default(true)
    .describe("Include document metadata in results"),
  pagination: z.object({
    page: z.number().min(1).default(1),
    limit: z.number().min(1).max(100).default(20)
  }).optional()
});

export const listDocumentsTool: Tool = {
  name: "list_documents",
  description: "List all available documents in the Mnemoverse documentation",
  inputSchema: ListDocumentsInputSchema,
};

2.3 validate_citations Tool ​

typescript
// src/tools/validateCitations.ts
const ValidateCitationsInputSchema = z.object({
  documentPath: z.string().describe("Path to document to validate"),
  checkExternalLinks: z.boolean().default(false)
    .describe("Check if external citation links are accessible"),
  fixBrokenRefs: z.boolean().default(false)
    .describe("Attempt to fix broken citation references")
});

export const validateCitationsTool: Tool = {
  name: "validate_citations",
  description: "Validate citations and references in documentation",
  inputSchema: ValidateCitationsInputSchema,
};

3. Resources Implementation ​

3.1 Document Resource ​

typescript
// src/resources/documentResource.ts
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

export const DocumentResourceTemplate = new ResourceTemplate(
  "mnemoverse://docs/{section}/{path...}",
  {
    list: undefined, // Will be handled by list_documents tool
    complete: {
      section: (value: string) => ['research', 'guides', 'vision', 'legal']
        .filter(s => s.startsWith(value)),
      path: async (value: string, context?: any) => {
        // Auto-complete file paths based on filesystem
        return await getDocumentPaths(context?.arguments?.section, value);
      }
    }
  }
);

export interface DocumentContent {
  uri: string;
  title: string;
  content: string;
  metadata: DocumentMetadata;
  sections: DocumentSection[];
  citations: Citation[];
  lastModified: string;
}

export interface DocumentSection {
  id: string;
  title: string;
  level: number;
  content: string;
  startLine: number;
  endLine: number;
}

3.2 Search Index Resource ​

typescript
// src/resources/searchIndexResource.ts
export const SearchIndexResourceTemplate = new ResourceTemplate(
  "mnemoverse://search-index/{type}",
  {
    list: undefined,
    complete: {
      type: () => ['statistics', 'terms', 'documents', 'configuration']
    }
  }
);

export interface SearchIndexInfo {
  totalDocuments: number;
  totalTerms: number;
  lastUpdated: string;
  indexSize: string;
  averageDocumentSize: number;
  mostSearchedTerms: Array<{
    term: string;
    frequency: number;
  }>;
}

4. Core Implementation Classes ​

4.1 Document Parser ​

typescript
// src/core/DocumentParser.ts
import matter from 'gray-matter';
import MarkdownIt from 'markdown-it';

export class DocumentParser {
  private markdown: MarkdownIt;
  
  constructor() {
    this.markdown = new MarkdownIt({
      html: true,
      linkify: true,
      typographer: true
    });
  }
  
  async parseDocument(filePath: string): Promise<DocumentContent> {
    const content = await fs.readFile(filePath, 'utf-8');
    const parsed = matter(content);
    
    return {
      uri: this.pathToUri(filePath),
      title: this.extractTitle(parsed),
      content: parsed.content,
      metadata: this.extractMetadata(parsed.data, filePath),
      sections: this.extractSections(parsed.content),
      citations: this.extractCitations(parsed.content),
      lastModified: await this.getLastModified(filePath)
    };
  }
  
  private extractSections(content: string): DocumentSection[] {
    const lines = content.split('\n');
    const sections: DocumentSection[] = [];
    
    lines.forEach((line, index) => {
      const match = line.match(/^(#{1,6})\s+(.+)$/);
      if (match) {
        sections.push({
          id: this.generateSectionId(match[2]),
          title: match[2],
          level: match[1].length,
          content: '', // Will be filled in post-processing
          startLine: index + 1,
          endLine: -1 // Will be calculated
        });
      }
    });
    
    return this.fillSectionContent(sections, lines);
  }
  
  private extractCitations(content: string): Citation[] {
    // Extract citations from various formats:
    // **[key]** *(citation not found)*, \cite{key}, [^key], etc.
    const citationRegex = /\**[([^\]** *(citation not found)*]+)\]|\\\w*cite\{([^}]+)\}|\[\^([^\]]+)\]/g;
    const citations: Set<string> = new Set();
    
    let match;
    while ((match = citationRegex.exec(content)) !== null) {
      const key = match[1] || match[2] || match[3];
      if (key) citations.add(key);
    }
    
    return Array.from(citations).map(key => ({
      key,
      title: '', // Will be resolved from bibliography
      authors: [],
      year: 0
    }));
  }
}

4.2 Document Structure Discovery ​

typescript
// src/core/DocumentStructureDiscovery.ts
import fs from 'fs-extra';
import path from 'path';

export interface DocumentStructure {
  sections: string[];
  paths: Map<string, string[]>;
  metadata: Map<string, any>;
}

export class DocumentStructureDiscovery {
  constructor(private docsBasePath: string = '../docs') {} // Relative path from mcp-server/
  
  async discoverStructure(): Promise<DocumentStructure> {
    const structure: DocumentStructure = {
      sections: [],
      paths: new Map(),
      metadata: new Map()
    };
    
    // First, try to load VitePress config from existing docs
    const vitePressStructure = await this.loadVitePressStructure();
    if (vitePressStructure) {
      return vitePressStructure;
    }
    
    // Fallback: discover from filesystem
    return await this.discoverFromFilesystem();
  }
  
  private async loadVitePressStructure(): Promise<DocumentStructure | null> {
    try {
      // Path to existing VitePress config
      const configPath = path.resolve(__dirname, this.docsBasePath, '.vitepress/config.mjs');
      if (!await fs.pathExists(configPath)) {
        console.warn(`VitePress config not found at: ${configPath}`);
        return null;
      }
      
      // Import VitePress config dynamically
      const configModule = await import(`file://${configPath}`);
      const config = configModule.default;
      
      console.log('βœ… Loaded VitePress config successfully');
      return this.parseVitePressConfig(config);
    } catch (error) {
      console.warn('Failed to load VitePress config:', error);
      return null;
    }
  }
  
  private parseVitePressConfig(config: any): DocumentStructure {
    const structure: DocumentStructure = {
      sections: [],
      paths: new Map(),
      metadata: new Map()
    };
    
    // Parse sidebar structure
    if (config.themeConfig?.sidebar) {
      for (const item of config.themeConfig.sidebar) {
        if (item.text && item.items) {
          const sectionName = item.text.toLowerCase().replace(/\s+/g, '-');
          structure.sections.push(sectionName);
          
          const sectionPaths: string[] = [];
          for (const subItem of item.items) {
            if (subItem.link) {
              sectionPaths.push(subItem.link);
              structure.metadata.set(subItem.link, {
                title: subItem.text,
                section: sectionName,
                order: sectionPaths.length
              });
            }
          }
          structure.paths.set(sectionName, sectionPaths);
        }
      }
    }
    
    return structure;
  }
  
  private async discoverFromFilesystem(): Promise<DocumentStructure> {
    const structure: DocumentStructure = {
      sections: [],
      paths: new Map(),
      metadata: new Map()
    };
    
    const docsDir = path.join(process.cwd(), this.docsBasePath);
    const entries = await fs.readdir(docsDir, { withFileTypes: true });
    
    for (const entry of entries) {
      if (entry.isDirectory() && !entry.name.startsWith('.')) {
        const sectionName = entry.name;
        structure.sections.push(sectionName);
        
        const sectionPath = path.join(docsDir, sectionName);
        const files = await this.findMarkdownFiles(sectionPath);
        structure.paths.set(sectionName, files);
        
        // Extract metadata from each file
        for (const filePath of files) {
          const metadata = await this.extractFileMetadata(path.join(sectionPath, filePath));
          structure.metadata.set(`/${sectionName}/${filePath}`, metadata);
        }
      }
    }
    
    return structure;
  }
  
  private async findMarkdownFiles(dirPath: string): Promise<string[]> {
    const files: string[] = [];
    
    try {
      const entries = await fs.readdir(dirPath, { withFileTypes: true });
      
      for (const entry of entries) {
        if (entry.isFile() && entry.name.endsWith('.md')) {
          files.push(entry.name);
        } else if (entry.isDirectory()) {
          const subFiles = await this.findMarkdownFiles(path.join(dirPath, entry.name));
          files.push(...subFiles.map(f => `${entry.name}/${f}`));
        }
      }
    } catch (error) {
      console.warn(`Failed to read directory ${dirPath}:`, error);
    }
    
    return files;
  }
  
  private async extractFileMetadata(filePath: string): Promise<any> {
    try {
      const content = await fs.readFile(filePath, 'utf-8');
      const matter = require('gray-matter');
      const parsed = matter(content);
      
      return {
        title: parsed.data.title || this.extractTitleFromContent(parsed.content),
        description: parsed.data.description,
        tags: parsed.data.tags || [],
        date: parsed.data.date,
        wordCount: parsed.content.split(/\s+/).length,
        readingTime: Math.ceil(parsed.content.split(/\s+/).length / 200)
      };
    } catch (error) {
      console.warn(`Failed to extract metadata from ${filePath}:`, error);
      return {};
    }
  }
  
  private extractTitleFromContent(content: string): string {
    const match = content.match(/^#\s+(.+)$/m);
    return match ? match[1] : 'Untitled';
  }
}

4.3 Search Engine Integration ​

typescript
// src/core/SearchEngine.ts
import { createRequire } from 'module';
import { DocumentStructureDiscovery } from './DocumentStructureDiscovery.js';

export class SearchEngine {
  private searchIndex: any; // VitePress MiniSearch index
  private documentsCache: Map<string, DocumentContent> = new Map();
  private structureDiscovery: DocumentStructureDiscovery;
  private documentStructure: DocumentStructure | null = null;
  
  constructor(private config: McpServerConfig) {
    this.structureDiscovery = new DocumentStructureDiscovery(config.docs.basePath);
  }
  
  async initialize(): Promise<void> {
    // Discover document structure first
    this.documentStructure = await this.structureDiscovery.discoverStructure();
    
    await this.loadVitePressIndex();
    await this.buildDocumentCache();
    
    if (this.config.docs.watchForChanges) {
      this.setupFileWatcher();
    }
  }
  
  getSections(): string[] {
    return this.documentStructure?.sections || [];
  }
  
  getDocumentsInSection(section: string): string[] {
    return this.documentStructure?.paths.get(section) || [];
  }
  
  async search(query: string, options: SearchOptions = {}): Promise<SearchResult[]> {
    const searchOptions = {
      combineWith: 'AND',
      fuzzy: options.fuzzy ? 0.2 : false,
      prefix: options.prefix,
      boost: {
        title: 4,
        text: 2,
        headers: 3
      },
      ...options
    };
    
    const results = this.searchIndex.search(query, searchOptions);
    
    return results.map((result: any) => ({
      title: result.title || this.extractTitleFromPath(result.id),
      uri: this.pathToUri(result.id),
      section: this.extractSection(result.id),
      excerpt: this.generateExcerpt(result, query),
      score: result.score,
      lastModified: this.getDocumentModified(result.id),
      highlights: options.highlightMatches ? 
        this.generateHighlights(result, query) : undefined,
      metadata: this.getDocumentMetadata(result.id)
    }));
  }
  
  private async loadVitePressIndex(): Promise<void> {
    try {
      // Load VitePress search index
      const indexPath = path.join(
        process.cwd(), 
        this.config.docs.basePath, 
        '.vitepress/cache/search-index.js'
      );
      
      if (await fs.pathExists(indexPath)) {
        const indexData = await import(indexPath);
        this.searchIndex = indexData.default || indexData;
      } else {
        // Fallback: build our own index
        await this.buildCustomIndex();
      }
    } catch (error) {
      console.warn('Failed to load VitePress index, building custom index:', error);
      await this.buildCustomIndex();
    }
  }
  
  private async buildCustomIndex(): Promise<void> {
    const MiniSearch = (await import('minisearch')).default;
    
    this.searchIndex = new MiniSearch({
      fields: ['title', 'text', 'headers'],
      storeFields: ['title', 'url', 'text'],
      searchOptions: {
        boost: { title: 2 },
        fuzzy: 0.2,
        prefix: true
      }
    });
    
    const documents = await this.indexAllDocuments();
    this.searchIndex.addAll(documents);
  }
}

4.3 HTTP Server ​

typescript
// src/server/httpServer.ts
import express from 'express';
import cors from 'cors';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';

export class MnemoverseMcpHttpServer {
  private app: express.Application;
  private mcpServer: McpServer;
  private transports: Map<string, StreamableHTTPServerTransport> = new Map();
  
  constructor(private config: McpServerConfig) {
    this.app = express();
    this.mcpServer = new McpServer({
      name: config.name,
      version: config.version
    });
    
    this.setupMiddleware();
    this.setupRoutes();
    this.registerMcpHandlers();
  }
  
  private setupMiddleware(): void {
    this.app.use(express.json());
    this.app.use(cors(this.config.transport.cors));
    
    // Request logging
    this.app.use((req, res, next) => {
      console.log(`${new Date().toISOString()} ${req.method} ${req.path}`);
      next();
    });
  }
  
  private setupRoutes(): void {
    // Health check
    this.app.get('/health', (req, res) => {
      res.json({ 
        status: 'healthy', 
        version: this.config.version,
        timestamp: new Date().toISOString()
      });
    });
    
    // MCP endpoint
    this.app.all('/mcp', async (req, res) => {
      await this.handleMcpRequest(req, res);
    });
    
    // MCP Discovery endpoints (.well-known)
    this.setupMcpDiscoveryEndpoints();
    
    // Documentation endpoint for debugging
    this.app.get('/docs', (req, res) => {
      res.json({
        tools: this.mcpServer.listTools(),
        resources: this.mcpServer.listResources(),
        endpoints: ['/mcp', '/health', '/docs', '/.well-known/mcp']
      });
    });
  }
  
  private setupMcpDiscoveryEndpoints(): void {
    // MCP Server Discovery - RFC compatible
    this.app.get('/.well-known/mcp', (req, res) => {
      res.json({
        version: "2025-03-26",
        name: this.config.name,
        description: this.config.description,
        version: this.config.version,
        vendor: "Mnemoverse",
        capabilities: {
          tools: true,
          resources: true,
          prompts: false,
          experimental: {
            streaming: false
          }
        },
        transports: {
          "streamable-http": {
            endpoint: "/mcp",
            methods: ["GET", "POST", "DELETE"]
          }
        },
        documentation: {
          openapi: "/openapi.json",
          examples: "/examples"
        }
      });
    });
    
    // OpenAPI 3.1 Specification
    this.app.get('/openapi.json', (req, res) => {
      res.json(this.generateOpenApiSpec());
    });
    
    // Usage examples
    this.app.get('/examples', (req, res) => {
      res.json(this.generateUsageExamples());
    });
  }
  
  private generateOpenApiSpec(): any {
    return {
      openapi: "3.1.0",
      info: {
        title: this.config.name,
        description: this.config.description,
        version: this.config.version,
        contact: {
          name: "Mnemoverse Team",
          url: "https://github.com/mnemoverse"
        },
        license: {
          name: "MIT",
          url: "https://opensource.org/licenses/MIT"
        }
      },
      servers: [
        {
          url: `http://${this.config.transport.host}:${this.config.transport.port}`,
          description: "Development server"
        }
      ],
      paths: {
        "/mcp": {
          post: {
            summary: "MCP JSON-RPC Endpoint",
            description: "Main endpoint for MCP communication using JSON-RPC 2.0",
            requestBody: {
              required: true,
              content: {
                "application/json": {
                  schema: {
                    type: "object",
                    properties: {
                      jsonrpc: { type: "string", enum: ["2.0"] },
                      id: { type: ["string", "number", "null"] },
                      method: { type: "string" },
                      params: { type: "object" }
                    },
                    required: ["jsonrpc", "method"]
                  },
                  examples: {
                    search_docs: {
                      summary: "Search documentation",
                      value: {
                        jsonrpc: "2.0",
                        id: 1,
                        method: "tools/call",
                        params: {
                          name: "search_docs",
                          arguments: {
                            query: "MCP protocol",
                            filters: { maxResults: 5 }
                          }
                        }
                      }
                    },
                    list_tools: {
                      summary: "List available tools",
                      value: {
                        jsonrpc: "2.0",
                        id: 2,
                        method: "tools/list"
                      }
                    }
                  }
                }
              }
            },
            responses: {
              "200": {
                description: "Successful response",
                content: {
                  "application/json": {
                    schema: {
                      type: "object",
                      properties: {
                        jsonrpc: { type: "string", enum: ["2.0"] },
                        id: { type: ["string", "number", "null"] },
                        result: { type: "object" },
                        error: {
                          type: "object",
                          properties: {
                            code: { type: "number" },
                            message: { type: "string" },
                            data: { type: "object" }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "/health": {
          get: {
            summary: "Health check endpoint",
            responses: {
              "200": {
                description: "Server is healthy",
                content: {
                  "application/json": {
                    schema: {
                      type: "object",
                      properties: {
                        status: { type: "string", enum: ["healthy"] },
                        version: { type: "string" },
                        timestamp: { type: "string", format: "date-time" }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      components: {
        schemas: {
          MCPTool: {
            type: "object",
            properties: {
              name: { type: "string" },
              description: { type: "string" },
              inputSchema: { type: "object" }
            },
            required: ["name", "description"]
          },
          SearchResult: {
            type: "object",
            properties: {
              title: { type: "string" },
              uri: { type: "string" },
              section: { type: "string" },
              excerpt: { type: "string" },
              score: { type: "number" },
              lastModified: { type: "string", format: "date-time" }
            }
          }
        }
      }
    };
  }
  
  private generateUsageExamples(): any {
    return {
      "curl_examples": {
        "search_docs": {
          description: "Search for documents containing 'memory'",
          command: `curl -X POST ${this.getBaseUrl()}/mcp \\
  -H "Content-Type: application/json" \\
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "search_docs",
      "arguments": {
        "query": "memory",
        "filters": {"maxResults": 3}
      }
    }
  }'`
        },
        "list_documents": {
          description: "List all research documents",
          command: `curl -X POST ${this.getBaseUrl()}/mcp \\
  -H "Content-Type: application/json" \\
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "list_documents",
      "arguments": {
        "section": "research",
        "includeMetadata": true
      }
    }
  }'`
        }
      },
      "javascript_examples": {
        "mcp_client": `
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const client = new Client({
  name: 'mnemoverse-client',
  version: '1.0.0'
});

const transport = new StreamableHTTPClientTransport('${this.getBaseUrl()}/mcp');
await client.connect(transport);

// Search documents
const searchResult = await client.callTool('search_docs', {
  query: 'artificial intelligence',
  filters: { sections: ['research'], maxResults: 5 }
});

console.log(searchResult);
`,
        "simple_fetch": `
const response = await fetch('${this.getBaseUrl()}/mcp', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: Date.now(),
    method: 'tools/call',
    params: {
      name: 'search_docs',
      arguments: { query: 'MCP protocol' }
    }
  })
});

const result = await response.json();
console.log(result);
`
      }
    };
  }
  
  private getBaseUrl(): string {
    return `http://${this.config.transport.host}:${this.config.transport.port}`;
  }
  }
  
  private async handleMcpRequest(req: express.Request, res: express.Response): Promise<void> {
    const sessionId = req.headers['mcp-session-id'] as string;
    
    try {
      let transport = this.transports.get(sessionId);
      
      if (!transport) {
        transport = new StreamableHTTPServerTransport({
          sessionIdGenerator: () => require('crypto').randomUUID()
        });
        
        transport.onclose = () => {
          if (transport?.sessionId) {
            this.transports.delete(transport.sessionId);
          }
        };
        
        await this.mcpServer.connect(transport);
        this.transports.set(transport.sessionId!, transport);
      }
      
      await transport.handleRequest(req, res, req.body);
    } catch (error) {
      console.error('MCP request error:', error);
      res.status(500).json({
        jsonrpc: '2.0',
        error: {
          code: -32603,
          message: 'Internal server error'
        },
        id: null
      });
    }
  }
  
  async start(): Promise<void> {
    return new Promise((resolve) => {
      this.app.listen(this.config.transport.port, this.config.transport.host, () => {
        console.log(`MCP Server running on ${this.config.transport.host}:${this.config.transport.port}`);
        resolve();
      });
    });
  }
}

5. Implementation Plan ​

Phase 1: Core Foundation (Week 1) ​

  1. Setup project structure

    • Initialize TypeScript project
    • Install MCP SDK and dependencies
    • Setup build and dev scripts
  2. Basic MCP Server

    • Implement HTTP server with Express
    • Setup MCP server with StreamableHTTP transport
    • Add health check and documentation endpoints
  3. Document Parser

    • Implement markdown parsing with frontmatter
    • Extract sections, titles, metadata
    • Basic citation extraction

Phase 2: Search Integration (Week 2) ​

  1. VitePress Search Integration

    • Load existing VitePress search index
    • Fallback to custom MiniSearch implementation
    • File watching for index updates
  2. Tools Implementation

    • search_docs with full search options
    • list_documents with filtering and pagination
    • Error handling and validation
  3. Resources Implementation

    • Document resource with URI template
    • Search index statistics resource
    • Auto-completion support

Phase 3: Advanced Features (Week 3) ​

  1. Citation Validation

    • validate_citations tool
    • Link checking for external references
    • Bibliography integration
  2. Performance Optimization

    • Document caching strategy
    • Search result caching
    • Memory usage optimization
  3. Testing & Documentation

    • Unit tests for all components
    • Integration tests with real MCP clients
    • API documentation generation

Phase 4 (Optional): Enterprise Migration to Java SDK ​

When to Consider Java SDK Migration:

  • Production load >1000 concurrent requests
  • Microservices architecture requirements
  • Enterprise Spring Boot integration needs
  • Standalone deployment separate from VitePress

Java SDK Migration Benefits:

java
// Example Java MCP Server with Spring Boot
@Component
public class MnemoverseMcpServer {
    
    @McpTool
    public SearchResult searchDocs(
        @McpToolParam("query") String query,
        @McpToolParam("filters") SearchFilters filters) {
        
        return documentSearchService.search(query, filters);
    }
    
    @McpResource("mnemoverse://docs/{section}/{path}")
    public DocumentContent getDocument(String section, String path) {
        return documentService.getDocument(section, path);
    }
}

Migration Path:

  1. Keep TypeScript server for VitePress integration
  2. Add Java MCP server for external API access
  3. Bridge pattern: Java server calls TypeScript for VitePress data
  4. Gradual migration based on performance needs

6. Testing Strategy ​

6.1 Test Environment Setup ​

typescript
// tests/setup/testEnvironment.ts
import { McpServerConfig } from '../../src/config/mcpServerConfig.js';
import { MnemoverseMcpHttpServer } from '../../src/server/httpServer.js';
import path from 'path';

export const testConfig: McpServerConfig = {
  name: "mnemoverse-docs-server-test",
  version: "1.0.0-test",
  description: "Test MCP Server for Mnemoverse Documentation",
  
  transport: {
    type: 'streamable-http',
    host: 'localhost',
    port: 0, // Let the system assign a port
    cors: {
      origin: '*',
      allowedHeaders: ['Content-Type', 'mcp-session-id'],
      exposedHeaders: ['mcp-session-id']
    }
  },
  
  docs: {
    basePath: path.join(__dirname, '../fixtures/docs'),
    searchIndexPath: '/.vitepress/cache',
    cacheEnabled: false, // Disable caching in tests
    watchForChanges: false
  },
  
  features: {
    semanticSearch: false,
    documentValidation: true,
    citationExtraction: true,
    fullTextIndexing: true
  }
};

export class TestServer {
  private server: MnemoverseMcpHttpServer;
  private port: number = 0;
  
  async start(): Promise<string> {
    this.server = new MnemoverseMcpHttpServer(testConfig);
    await this.server.start();
    this.port = this.server.getPort();
    return `http://localhost:${this.port}`;
  }
  
  async stop(): Promise<void> {
    await this.server.stop();
  }
  
  getBaseUrl(): string {
    return `http://localhost:${this.port}`;
  }
}

// Test fixtures setup
export async function setupTestFixtures(): Promise<void> {
  const fixturesPath = path.join(__dirname, '../fixtures/docs');
  
  // Create test documents
  const testDocs = {
    'research/test-paper.md': `---
title: "Test Research Paper"
authors: ["Test Author"]
date: "2025-01-15"
tags: ["AI", "testing"]
---

# Test Research Paper

This is a test document for MCP server testing.

## Introduction

Testing the search and parsing capabilities.

## Methodology

We use automated tests **[testref2025]** *(citation not found)*.

## Conclusion

The tests should pass.
`,
    'guides/test-guide.md': `---
title: "Test Guide"
description: "A guide for testing"
---

# Test Guide

This guide explains how to test the MCP server.

## Setup

Follow these steps...

## Running Tests

Execute the test suite...
`,
    'vision/test-vision.md': `---
title: "Test Vision Document"
---

# Test Vision

Our vision for testing.
`
  };
  
  for (const [filePath, content] of Object.entries(testDocs)) {
    const fullPath = path.join(fixturesPath, filePath);
    await fs.ensureDir(path.dirname(fullPath));
    await fs.writeFile(fullPath, content);
  }
}

6.2 Unit Tests ​

typescript
// tests/tools/searchDocs.test.ts
import { SearchEngine } from '../../src/core/SearchEngine.js';
import { testConfig, setupTestFixtures } from '../setup/testEnvironment.js';

describe('SearchEngine', () => {
  let searchEngine: SearchEngine;
  
  beforeAll(async () => {
    await setupTestFixtures();
  });
  
  beforeEach(async () => {
    searchEngine = new SearchEngine(testConfig);
    await searchEngine.initialize();
  });
  
  describe('Document Discovery', () => {
    test('should discover document structure automatically', async () => {
      const sections = searchEngine.getSections();
      
      expect(sections).toContain('research');
      expect(sections).toContain('guides');
      expect(sections).toContain('vision');
    });
    
    test('should list documents in each section', async () => {
      const researchDocs = searchEngine.getDocumentsInSection('research');
      
      expect(researchDocs).toContain('test-paper.md');
      expect(researchDocs.length).toBeGreaterThan(0);
    });
  });
  
  describe('Search Functionality', () => {
    test('should return relevant results for query', async () => {
      const results = await searchEngine.search('test research');
      
      expect(results).toHaveLength(greaterThan(0));
      expect(results[0]).toMatchObject({
        title: expect.stringMatching(/test/i),
        score: expect.any(Number),
        uri: expect.stringMatching(/^mnemoverse:\/\/docs\//),
        section: expect.any(String)
      });
    });
    
    test('should filter by sections correctly', async () => {
      const results = await searchEngine.search('test', {
        filters: { sections: ['research'] }
      });
      
      results.forEach(result => {
        expect(result.section).toBe('research');
      });
    });
    
    test('should respect maxResults limit', async () => {
      const results = await searchEngine.search('test', {
        filters: { maxResults: 2 }
      });
      
      expect(results).toHaveLength(lessThanOrEqualTo(2));
    });
    
    test('should include highlights when requested', async () => {
      const results = await searchEngine.search('methodology', {
        options: { highlightMatches: true }
      });
      
      expect(results[0].highlights).toBeDefined();
      expect(results[0].highlights).toContain(expect.stringMatching(/methodology/i));
    });
    
    test('should handle fuzzy search', async () => {
      const results = await searchEngine.search('methdology', { // Intentional typo
        options: { fuzzy: true }
      });
      
      expect(results).toHaveLength(greaterThan(0));
    });
    
    test('should include content when requested', async () => {
      const results = await searchEngine.search('test', {
        options: { includeContent: true }
      });
      
      expect(results[0]).toHaveProperty('content');
      expect(results[0].content).toContain('test');
    });
  });
  
  describe('Error Handling', () => {
    test('should handle empty queries gracefully', async () => {
      await expect(searchEngine.search('')).rejects.toThrow();
    });
    
    test('should handle non-existent sections', async () => {
      const results = await searchEngine.search('test', {
        filters: { sections: ['nonexistent'] }
      });
      
      expect(results).toHaveLength(0);
    });
  });
});

// tests/tools/documentParser.test.ts
import { DocumentParser } from '../../src/core/DocumentParser.js';
import path from 'path';

describe('DocumentParser', () => {
  let parser: DocumentParser;
  
  beforeEach(() => {
    parser = new DocumentParser();
  });
  
  test('should parse frontmatter correctly', async () => {
    const testFile = path.join(__dirname, '../fixtures/docs/research/test-paper.md');
    const result = await parser.parseDocument(testFile);
    
    expect(result.metadata.title).toBe('Test Research Paper');
    expect(result.metadata.authors).toContain('Test Author');
    expect(result.metadata.tags).toContain('AI');
  });
  
  test('should extract sections correctly', async () => {
    const testFile = path.join(__dirname, '../fixtures/docs/research/test-paper.md');
    const result = await parser.parseDocument(testFile);
    
    expect(result.sections).toHaveLength(greaterThan(2));
    expect(result.sections[0].title).toBe('Introduction');
    expect(result.sections[0].level).toBe(2);
  });
  
  test('should extract citations', async () => {
    const testFile = path.join(__dirname, '../fixtures/docs/research/test-paper.md');
    const result = await parser.parseDocument(testFile);
    
    expect(result.citations).toContain(
      expect.objectContaining({ key: 'testref2025' })
    );
  });
  
  test('should calculate reading time', async () => {
    const testFile = path.join(__dirname, '../fixtures/docs/research/test-paper.md');
    const result = await parser.parseDocument(testFile);
    
    expect(result.metadata.readingTime).toBeGreaterThan(0);
    expect(result.metadata.wordCount).toBeGreaterThan(0);
  });
});

6.3 Integration Tests ​

typescript
// tests/integration/mcpServer.test.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { TestServer, setupTestFixtures } from '../setup/testEnvironment.js';

describe('MCP Server Integration', () => {
  let testServer: TestServer;
  let client: Client;
  let baseUrl: string;
  
  beforeAll(async () => {
    await setupTestFixtures();
    testServer = new TestServer();
    baseUrl = await testServer.start();
    
    client = new Client({ 
      name: 'test-client', 
      version: '1.0.0' 
    });
    
    const transport = new StreamableHTTPClientTransport(`${baseUrl}/mcp`);
    await client.connect(transport);
  });
  
  afterAll(async () => {
    if (client) {
      await client.close();
    }
    if (testServer) {
      await testServer.stop();
    }
  });
  
  describe('MCP Protocol Compliance', () => {
    test('should handle initialize request', async () => {
      const result = await client.request({
        method: 'initialize',
        params: {
          protocolVersion: '2025-03-26',
          capabilities: {},
          clientInfo: { name: 'test-client', version: '1.0.0' }
        }
      });
      
      expect(result.capabilities).toBeDefined();
      expect(result.serverInfo.name).toBe('mnemoverse-docs-server-test');
    });
    
    test('should list available tools', async () => {
      const tools = await client.listTools();
      
      expect(tools.tools).toEqual(
        expect.arrayContaining([
          expect.objectContaining({ name: 'search_docs' }),
          expect.objectContaining({ name: 'list_documents' }),
          expect.objectContaining({ name: 'validate_citations' })
        ])
      );
    });
    
    test('should list available resources', async () => {
      const resources = await client.listResources();
      
      expect(resources.resources).toBeDefined();
    });
  });
  
  describe('Tool Execution', () => {
    test('should execute search_docs tool successfully', async () => {
      const result = await client.callTool('search_docs', {
        query: 'test research',
        filters: { maxResults: 3 }
      });
      
      expect(result.content).toHaveLength(1);
      expect(result.content[0].type).toBe('text');
      
      const searchResults = JSON.parse(result.content[0].text);
      expect(searchResults.results).toBeDefined();
      expect(searchResults.results).toHaveLength(lessThanOrEqualTo(3));
    });
    
    test('should execute list_documents tool successfully', async () => {
      const result = await client.callTool('list_documents', {
        section: 'research',
        includeMetadata: true
      });
      
      expect(result.content).toHaveLength(1);
      
      const docs = JSON.parse(result.content[0].text);
      expect(docs.documents).toBeDefined();
      expect(docs.documents[0]).toHaveProperty('metadata');
    });
    
    test('should validate tool input schemas', async () => {
      await expect(
        client.callTool('search_docs', { /* empty args */ })
      ).rejects.toThrow();
    });
    
    test('should handle tool errors gracefully', async () => {
      const result = await client.callTool('search_docs', {
        query: 'test',
        filters: { sections: ['nonexistent'] }
      });
      
      expect(result.isError).toBeFalsy();
      const searchResults = JSON.parse(result.content[0].text);
      expect(searchResults.results).toHaveLength(0);
    });
  });
  
  describe('Performance Tests', () => {
    test('should respond within acceptable time limits', async () => {
      const startTime = Date.now();
      
      await client.callTool('search_docs', {
        query: 'test',
        filters: { maxResults: 10 }
      });
      
      const duration = Date.now() - startTime;
      expect(duration).toBeLessThan(500); // 500ms max
    });
    
    test('should handle concurrent requests', async () => {
      const promises = Array(10).fill(0).map((_, i) => 
        client.callTool('search_docs', {
          query: `test ${i}`,
          filters: { maxResults: 5 }
        })
      );
      
      const results = await Promise.all(promises);
      
      results.forEach(result => {
        expect(result.isError).toBeFalsy();
      });
    });
  });
});

6.4 End-to-End Tests ​

typescript
// tests/e2e/discoveryEndpoints.test.ts
import fetch from 'node-fetch';
import { TestServer, setupTestFixtures } from '../setup/testEnvironment.js';

describe('MCP Discovery Endpoints E2E', () => {
  let testServer: TestServer;
  let baseUrl: string;
  
  beforeAll(async () => {
    await setupTestFixtures();
    testServer = new TestServer();
    baseUrl = await testServer.start();
  });
  
  afterAll(async () => {
    await testServer.stop();
  });
  
  test('should serve .well-known/mcp discovery endpoint', async () => {
    const response = await fetch(`${baseUrl}/.well-known/mcp`);
    
    expect(response.status).toBe(200);
    
    const data = await response.json();
    expect(data).toMatchObject({
      version: "2025-03-26",
      name: "mnemoverse-docs-server-test",
      capabilities: {
        tools: true,
        resources: true
      },
      transports: {
        "streamable-http": {
          endpoint: "/mcp"
        }
      }
    });
  });
  
  test('should serve OpenAPI specification', async () => {
    const response = await fetch(`${baseUrl}/openapi.json`);
    
    expect(response.status).toBe(200);
    
    const spec = await response.json();
    expect(spec.openapi).toBe("3.1.0");
    expect(spec.info.title).toBe("mnemoverse-docs-server-test");
    expect(spec.paths['/mcp']).toBeDefined();
  });
  
  test('should serve usage examples', async () => {
    const response = await fetch(`${baseUrl}/examples`);
    
    expect(response.status).toBe(200);
    
    const examples = await response.json();
    expect(examples.curl_examples).toBeDefined();
    expect(examples.javascript_examples).toBeDefined();
  });
  
  test('should handle CORS preflight requests', async () => {
    const response = await fetch(`${baseUrl}/mcp`, {
      method: 'OPTIONS',
      headers: {
        'Origin': 'https://example.com',
        'Access-Control-Request-Method': 'POST',
        'Access-Control-Request-Headers': 'Content-Type'
      }
    });
    
    expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*');
    expect(response.headers.get('Access-Control-Allow-Methods')).toContain('POST');
  });
});

6.5 Load Testing ​

typescript
// tests/load/loadTest.ts
import { performance } from 'perf_hooks';
import { TestServer } from '../setup/testEnvironment.js';

describe('Load Testing', () => {
  let testServer: TestServer;
  let baseUrl: string;
  
  beforeAll(async () => {
    testServer = new TestServer();
    baseUrl = await testServer.start();
  });
  
  afterAll(async () => {
    await testServer.stop();
  });
  
  test('should handle sustained load', async () => {
    const concurrency = 20;
    const requestsPerClient = 10;
    const totalRequests = concurrency * requestsPerClient;
    
    const startTime = performance.now();
    
    const clients = Array(concurrency).fill(0).map(async (_, clientId) => {
      const results = [];
      
      for (let i = 0; i < requestsPerClient; i++) {
        const requestStart = performance.now();
        
        const response = await fetch(`${baseUrl}/mcp`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            jsonrpc: '2.0',
            id: `${clientId}-${i}`,
            method: 'tools/call',
            params: {
              name: 'search_docs',
              arguments: { query: `test query ${i}` }
            }
          })
        });
        
        const requestEnd = performance.now();
        
        results.push({
          success: response.ok,
          duration: requestEnd - requestStart,
          status: response.status
        });
      }
      
      return results;
    });
    
    const allResults = await Promise.all(clients);
    const flatResults = allResults.flat();
    
    const endTime = performance.now();
    const totalDuration = endTime - startTime;
    
    // Analyze results
    const successCount = flatResults.filter(r => r.success).length;
    const avgDuration = flatResults.reduce((sum, r) => sum + r.duration, 0) / flatResults.length;
    const p95Duration = flatResults
      .map(r => r.duration)
      .sort((a, b) => a - b)[Math.floor(flatResults.length * 0.95)];
    
    console.log(`Load Test Results:
      Total Requests: ${totalRequests}
      Success Rate: ${(successCount / totalRequests * 100).toFixed(2)}%
      Total Duration: ${totalDuration.toFixed(2)}ms
      Avg Request Duration: ${avgDuration.toFixed(2)}ms
      P95 Request Duration: ${p95Duration.toFixed(2)}ms
      Throughput: ${(totalRequests / (totalDuration / 1000)).toFixed(2)} RPS
    `);
    
    // Assertions
    expect(successCount / totalRequests).toBeGreaterThan(0.95); // 95% success rate
    expect(avgDuration).toBeLessThan(200); // Average < 200ms
    expect(p95Duration).toBeLessThan(500); // P95 < 500ms
  });
});

6.6 Test Configuration ​

json
// jest.config.js
{
  "preset": "ts-jest",
  "testEnvironment": "node",
  "roots": ["<rootDir>/tests"],
  "testMatch": ["**/*.test.ts"],
  "collectCoverageFrom": [
    "src/**/*.ts",
    "!src/**/*.d.ts",
    "!src/index.ts"
  ],
  "coverageDirectory": "coverage",
  "coverageReporters": ["text", "lcov", "html"],
  "setupFilesAfterEnv": ["<rootDir>/tests/setup/jest.setup.ts"],
  "testTimeout": 30000,
  "maxWorkers": 4
}
typescript
// tests/setup/jest.setup.ts
import 'jest-extended';

// Global test setup
beforeAll(async () => {
  // Setup global test environment
});

afterAll(async () => {
  // Cleanup global test environment
});

// Custom matchers
expect.extend({
  toBeValidMcpResponse(received) {
    const pass = 
      received.jsonrpc === '2.0' &&
      (received.result !== undefined || received.error !== undefined) &&
      received.id !== undefined;
    
    return {
      message: () => `expected ${received} to be a valid MCP response`,
      pass
    };
  }
});

7. Deployment Configuration ​

7.1 TypeScript Deployment (Phase 1) ​

Docker Setup

dockerfile
# Dockerfile
FROM node:20-alpine

WORKDIR /app

# Copy package files
COPY mcp-server/package*.json ./
RUN npm ci --only=production

# Copy source code
COPY mcp-server/dist/ ./dist/
COPY docs/ ./docs/

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S mcpserver -u 1001
USER mcpserver

EXPOSE 3001

CMD ["node", "dist/index.js"]

Docker Compose (Development)

yaml
# docker-compose.yml
version: '3.8'
services:
  vitepress-docs:
    build:
      context: .
      dockerfile: docs/Dockerfile
    ports:
      - "3000:3000"
    volumes:
      - ./docs:/app/docs
    environment:
      - NODE_ENV=development
  
  mcp-server:
    build:
      context: .
      dockerfile: mcp-server/Dockerfile
    ports:
      - "3001:3001"
    volumes:
      - ./docs:/app/docs:ro  # Read-only access to docs
    environment:
      - NODE_ENV=development
      - MCP_DOCS_PATH=/app/docs
    depends_on:
      - vitepress-docs

7.2 Java SDK Deployment (Phase 2/3) ​

Spring Boot Configuration

yaml
# application.yml
server:
  port: 8080
  servlet:
    context-path: /api

spring:
  application:
    name: mnemoverse-mcp-server
  
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=1000,expireAfterWrite=10m
  
  jackson:
    property-naming-strategy: SNAKE_CASE

mcp:
  server:
    name: "mnemoverse-docs-server-java"
    version: "2.0.0" 
    docs:
      base-path: "/data/docs"
      cache-enabled: true
      watch-for-changes: true
  
  cors:
    allowed-origins: 
      - "https://docs.mnemoverse.com"
      - "https://app.mnemoverse.com"
    allowed-methods: ["GET", "POST", "OPTIONS"]

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      show-details: always

Kubernetes Deployment

yaml
# k8s-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server-java
  labels:
    app: mcp-server
    version: java
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mcp-server
  template:
    metadata:
      labels:
        app: mcp-server
    spec:
      containers:
      - name: mcp-server
        image: mnemoverse/mcp-server-java:2.0.0
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "production"
        - name: MCP_DOCS_PATH
          value: "/data/docs"
        volumeMounts:
        - name: docs-volume
          mountPath: /data/docs
          readOnly: true
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi" 
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: docs-volume
        persistentVolumeClaim:
          claimName: docs-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-server-service
spec:
  selector:
    app: mcp-server
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

7.3 Environment Configuration ​

TypeScript Environment

bash
# .env.production
NODE_ENV=production
MCP_SERVER_HOST=0.0.0.0
MCP_SERVER_PORT=3001
MCP_DOCS_PATH=/app/docs
MCP_CACHE_ENABLED=true
MCP_WATCH_FILES=false
MCP_LOG_LEVEL=info

# CORS settings
MCP_CORS_ORIGIN=https://docs.mnemoverse.org,https://app.mnemoverse.org

Java Environment

properties
# application-production.properties
server.port=8080
logging.level.com.mnemoverse.mcp=INFO
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n

mcp.server.docs.base-path=/data/docs
mcp.server.cache.enabled=true
mcp.server.performance.max-concurrent-requests=1000
mcp.server.security.rate-limit.requests-per-minute=600

spring.datasource.url=jdbc:h2:mem:mcp
spring.jpa.hibernate.ddl-auto=create-drop

8. Monitoring & Logging ​

8.1 Structured Logging ​

typescript
// src/utils/logger.ts
import winston from 'winston';

export const logger = winston.createLogger({
  level: process.env.MCP_LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: { service: 'mnemoverse-mcp-server' },
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
    new winston.transports.File({ filename: 'logs/combined.log' })
  ]
});

8.2 Health Monitoring ​

typescript
// src/monitoring/healthCheck.ts
export interface HealthStatus {
  status: 'healthy' | 'degraded' | 'unhealthy';
  timestamp: string;
  version: string;
  uptime: number;
  checks: {
    filesystem: boolean;
    searchIndex: boolean;
    memoryUsage: number;
    documentCache: number;
  };
}

export class HealthMonitor {
  async getHealthStatus(): Promise<HealthStatus> {
    const checks = await Promise.all([
      this.checkFilesystem(),
      this.checkSearchIndex(),
      this.checkMemoryUsage(),
      this.checkDocumentCache()
    ]);
    
    return {
      status: checks.every(c => c.healthy) ? 'healthy' : 'degraded',
      timestamp: new Date().toISOString(),
      version: process.env.npm_package_version || '1.0.0',
      uptime: process.uptime(),
      checks: {
        filesystem: checks[0].healthy,
        searchIndex: checks[1].healthy,
        memoryUsage: checks[2].value,
        documentCache: checks[3].value
      }
    };
  }
}

Readiness Criteria ​

Phase 1: TypeScript MVP - Functional Requirements ​

  • [ ] All three core tools are implemented and tested
  • [ ] Search works with the VitePress index
  • [ ] Documents are parsed correctly with metadata
  • [ ] HTTP API responds to all MCP requests
  • [ ] CORS is configured for production domains

Phase 1: Performance (TypeScript) ​

  • [ ] Search responds in < 500ms for basic queries
  • [ ] Server handles 50+ concurrent requests
  • [ ] Memory usage < 256MB with full document load
  • [ ] Documents are cached efficiently

Phase 1: Reliability ​

  • [ ] Graceful handling of all errors
  • [ ] Automatic fallback if VitePress index is unavailable
  • [ ] Health check endpoint is operational
  • [ ] Logging of all operations

Phase 1: Security ​

  • [ ] Validation of all input parameters
  • [ ] CORS restricts access to allowed domains
  • [ ] No path traversal vulnerabilities
  • [ ] Basic rate limiting to prevent abuse

Phase 2/3: Java SDK - Enterprise Readiness ​

  • [ ] Performance: < 200ms P95, 1000+ concurrent requests
  • [ ] Scalability: Horizontal scaling with load balancer
  • [ ] Monitoring: Prometheus metrics, distributed tracing
  • [ ] Security: Spring Security integration, advanced rate limiting
  • [ ] Resilience: Circuit breakers, retry mechanisms, health checks
  • [ ] Caching: Redis/Hazelcast for distributed caching
  • [ ] Testing: 95%+ test coverage, performance benchmarks

Migration Trigger Points (TypeScript β†’ Java) ​

  • [ ] Load: > 500 concurrent users consistently
  • [ ] Performance: Response times consistently > 500ms
  • [ ] Scale: Need for horizontal scaling across multiple instances
  • [ ] Enterprise: Need for advanced monitoring, security, governance
  • [ ] Integration: Need for microservices architecture

πŸ—οΈ Project Structure in mnemoverse-docs ​

Integration into existing repository:

mnemoverse-docs/
β”œβ”€β”€ docs/                           # Existing VitePress documentation
β”‚   β”œβ”€β”€ .vitepress/config.mjs      # Configuration for MCP server
β”‚   β”œβ”€β”€ research/                   # Documents for indexing
β”‚   └── guides/                     # Including this guide
β”œβ”€β”€ mcp-server/                     # πŸ†• NEW folder for MCP server  
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”‚   └── mcpServerConfig.ts
β”‚   β”‚   β”œβ”€β”€ tools/
β”‚   β”‚   β”‚   β”œβ”€β”€ searchDocs.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ listDocuments.ts
β”‚   β”‚   β”‚   └── validateCitations.ts
β”‚   β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”‚   β”œβ”€β”€ DocumentParser.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ SearchEngine.ts
β”‚   β”‚   β”‚   └── DocumentStructureDiscovery.ts
β”‚   β”‚   β”œβ”€β”€ server/
β”‚   β”‚   β”‚   └── httpServer.ts
β”‚   β”‚   └── index.ts
β”‚   β”œβ”€β”€ tests/
β”‚   β”‚   β”œβ”€β”€ setup/
β”‚   β”‚   β”œβ”€β”€ tools/
β”‚   β”‚   β”œβ”€β”€ integration/
β”‚   β”‚   └── e2e/
β”‚   β”œβ”€β”€ package.json               # MCP server dependencies
β”‚   β”œβ”€β”€ tsconfig.json
β”‚   └── Dockerfile
β”œβ”€β”€ package.json                   # ΠšΠΎΡ€Π½Π΅Π²ΠΎΠΉ для VitePress docs
β”œβ”€β”€ scripts/                       # ΠžΠ±Ρ‰ΠΈΠ΅ скрипты + MCP utilities
└── README.md

Advantages of this structure:

  • βœ… Single repository - everything in one place
  • βœ… VitePress config reuse - automatic structure discovery
  • βœ… Shared CI/CD - one pipeline for docs + MCP server
  • βœ… Data proximity - MCP server next to documentation
  • βœ… Simple deployment - one Docker compose for everything

Next steps for implementation:

  1. Create MCP server structure in mnemoverse-docs

    bash
    cd /Users/eduardizgorodin/Projects/mnemoverse/mnemoverse-docs
    mkdir -p mcp-server/src/{config,tools,core,server}
    mkdir -p mcp-server/tests/{setup,tools,integration,e2e}
  2. Configure package.json for MCP server

    bash
    cd mcp-server
    npm init -y
    npm install @modelcontextprotocol/sdk express cors typescript
  3. Integrate with existing VitePress configuration

    • Use docs/.vitepress/config.mjs for auto-discovery
    • Reuse existing search index
    • Integrate with existing scripts
  4. Add MCP server commands to root package.json

    json
    {
      "scripts": {
        "mcp:dev": "cd mcp-server && npm run dev",
        "mcp:build": "cd mcp-server && npm run build", 
        "mcp:test": "cd mcp-server && npm test"
      }
    }
  5. Create unified Docker setup

    • VitePress docs on port 3000
    • MCP server on port 3001
    • Shared volumes for documentation

Each step should be independently testable and integrate with the existing structure.

Troubleshooting Guide ​

πŸ”§ Common Installation Issues ​

npm Installation Fails ​

bash
# Clear npm cache
npm cache clean --force

# Update npm to latest version
npm install -g npm@latest

# Install with verbose logging
npm install -g @mnemoverse/mcp-docs-server --verbose

Permission Errors on macOS/Linux ​

bash
# Fix npm permissions
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# Alternative: use npx instead of global install
npx @mnemoverse/mcp-docs-server

Windows Installation Issues ​

powershell
# Run as Administrator
npm install -g @mnemoverse/mcp-docs-server

# Or use Windows-specific path
npm config set prefix %APPDATA%\npm

πŸ› Runtime Debugging ​

Server Won't Start ​

bash
# Check Node.js version (requires 18+)
node --version

# Test direct execution
node /usr/local/lib/node_modules/@mnemoverse/mcp-docs-server/index.js

# Check for missing dependencies
npm list -g @mnemoverse/mcp-docs-server

Claude Desktop Connection Issues ​

bash
# Verify config file location:
# macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
# Windows: %APPDATA%/Claude/claude_desktop_config.json

# Validate JSON syntax
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json | python -m json.tool

# Check server process
ps aux | grep mcp-docs-server

MCP Inspector Testing ​

bash
# Install MCP Inspector
npm install -g @modelcontextprotocol/inspector

# Test your server
npx @modelcontextprotocol/inspector node /path/to/your/server/index.js

# Test with specific args
npx @modelcontextprotocol/inspector node dist/index.js --docs-path ./docs

πŸ“Š Performance Issues ​

Slow Response Times ​

bash
# Enable debug logging
DEBUG=mcp:* node /path/to/server/index.js

# Monitor memory usage  
node --inspect /path/to/server/index.js

Memory Leaks ​

bash
# Run with memory monitoring
node --max-old-space-size=4096 --inspect /path/to/server/index.js

# Profile memory usage
node --prof /path/to/server/index.js
node --prof-process isolate-*.log > prof.txt

πŸ” Development Debugging ​

TypeScript Build Errors ​

bash
# Clean build artifacts
rm -rf dist/
npm run clean

# Rebuild with verbose output
npm run build -- --verbose

# Check TypeScript config
npx tsc --showConfig

Test Failures ​

bash
# Run tests with verbose output
npm test -- --verbose

# Run specific test file
npm test -- --testNamePattern="document search"

# Debug tests
node --inspect-brk ./node_modules/.bin/jest --runInBand

πŸ’‘ Custom Development Setup ​

Local Development Server ​

typescript
// dev-server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";

const server = new Server({
  name: "dev-docs-server",
  version: "1.0.0-dev"
}, {
  capabilities: {
    tools: {},
    resources: {}
  }
});

// Enable hot reload
process.on('SIGTERM', () => server.close());

Custom Environment Variables ​

bash
# .env.development
DEBUG=mcp:*
DOCS_BASE_PATH=/custom/docs/path
CACHE_ENABLED=true
LOG_LEVEL=debug
PORT=3001

# Load in development
npm run dev -- --env-file .env.development

πŸ†˜ Getting Help ​

Error Reporting Checklist ​

When reporting issues, include:

  1. Environment info:

    bash
    node --version
    npm --version
    cat /etc/os-release || sw_vers  # Linux || macOS
  2. Installation method:

    bash
    npm list -g @mnemoverse/mcp-docs-server
    which @mnemoverse/mcp-docs-server
  3. Error logs:

    bash
    DEBUG=mcp:* your-command 2>&1 | tee debug.log
  4. Configuration:

    bash
    # Sanitized config (remove sensitive data)
    cat claude_desktop_config.json

Support Channels ​

Contributing Fixes ​

Found a bug? Help improve the server:

bash
# Fork and clone
git clone https://github.com/mnemoverse/mcp-docs-server.git
cd mcp-docs-server

# Create feature branch
git checkout -b fix/your-bug-description

# Install dependencies
npm install

# Make changes and test
npm test
npm run build

# Submit PR
git push origin fix/your-bug-description

Ready to build your own MCP server? Start with the Quick Start or explore the main MCP Server Guide for user-focused documentation.

Explore related documentation:

  • Getting Started with Mnemoverse - πŸš€ Getting Started with Mnemoverse | Quick start guide for Mnemoverse AI memory engine. Set up spatial memory systems in 5 minutes!
  • How to Contribute to Mnemoverse - πŸ“– How to Contribute to Mnemoverse | Step-by-step tutorial for Mnemoverse. Learn practical implementation with code examples.
  • MCP Quick Start - πŸ“– MCP Quick Start | Step-by-step tutorial for Mnemoverse AI memory engine. Learn spatial memory concepts with practical examples.
  • MCP Server - πŸ“– MCP Server | Step-by-step tutorial for Mnemoverse AI memory engine. Learn spatial memory concepts with practical examples.
  • πŸ” Advanced Research Search - πŸ“– πŸ” Advanced Research Search | Step-by-step tutorial for Mnemoverse AI memory engine. Learn spatial memory concepts with practical examples.