UNPKG

@battle-racing/br-common-lib

Version:

Common library for all Battle Racing Repositorios

172 lines (150 loc) 4.85 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; import * as fs from "fs"; import * as path from "path"; /** * MCP Server for Battle Racing Common Library * This server allows other AI agents to query types, schemas and constants * from the br-common-lib repository locally. */ const DOMAINS_DIR = path.resolve(__dirname, "../../src/domains"); const server = new McpServer({ name: "br-common-lib-mcp", version: "1.1.1", }); /** * List all available domains in src/domains */ async function listDomains() { if (!fs.existsSync(DOMAINS_DIR)) { throw new Error(`Domains directory not found at ${DOMAINS_DIR}`); } const entries = fs.readdirSync(DOMAINS_DIR, { withFileTypes: true }); return entries .filter((entry: fs.Dirent) => entry.isDirectory()) .map((entry: fs.Dirent) => entry.name); } /** * Get details for a specific domain (recursive) */ async function getDomainDetails(domain: string) { const domainPath = path.join(DOMAINS_DIR, domain); if (!fs.existsSync(domainPath)) { throw new Error(`Domain "${domain}" not found at ${domainPath}`); } let output = `## Domain: ${domain}\n\n`; function readDirRecursive(dir: string) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { readDirRecursive(fullPath); } else if ( entry.name.endsWith(".types.ts") || entry.name.endsWith(".schema.ts") || entry.name.endsWith(".const.ts") || entry.name === "index.ts" ) { const relativePath = path.relative(domainPath, fullPath); const content = fs.readFileSync(fullPath, "utf-8"); output += `### File: ${relativePath}\n\`\`\`typescript\n${content}\n\`\`\`\n\n`; } } } readDirRecursive(domainPath); return output; } /** * Search for a symbol (type, constant, etc) across all domains */ async function searchSymbol(query: string) { const domains = await listDomains(); let results: { domain: string; file: string; match: string }[] = []; for (const domain of domains) { const domainPath = path.join(DOMAINS_DIR, domain); function searchInDir(dir: string) { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { searchInDir(fullPath); } else if (entry.isFile() && (entry.name.endsWith(".ts"))) { const content = fs.readFileSync(fullPath, "utf-8"); if (content.includes(query)) { const lines = content.split("\n"); const matchLine = lines.find(l => l.includes(query)) || ""; results.push({ domain, file: path.relative(DOMAINS_DIR, fullPath), match: matchLine.trim() }); } } } } searchInDir(domainPath); } if (results.length === 0) return `No results found for "${query}"`; return results .map(r => `- [${r.domain}] ${r.file}: \`${r.match}\``) .join("\n"); } // Register tools using the high-level McpServer API (registerTool) server.registerTool( "list_domains", { description: "List all available domains in br-common-lib (e.g., kart, game, powerUp)", }, async () => { const domains = await listDomains(); return { content: [ { type: "text", text: `Available domains: ${domains.join(", ")}`, }, ], }; } ); server.registerTool( "get_domain_details", { description: "Get all types, schemas, and constants for a specific domain (recursive)", inputSchema: z.object({ domain: z.string().describe("The name of the domain (e.g., 'kart')"), }), }, async ({ domain }) => { const details = await getDomainDetails(domain); return { content: [{ type: "text", text: details }], }; } ); server.registerTool( "search_symbol", { description: "Search for a specific type, constant or property across all domains", inputSchema: z.object({ query: z.string().describe("The symbol or string to search for (e.g., 'hardwareState')"), }), }, async ({ query }) => { const results = await searchSymbol(query); return { content: [{ type: "text", text: results }], }; } ); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Battle Racing Common Lib MCP Server running on stdio"); } main().catch((error) => { console.error("Fatal error in main():", error); process.exit(1); });