/** * 0xRay Architect Tools MCP Server * * Converts architect-tools.ts functions into MCP server tools * Provides contextual analysis capabilities via MCP protocol */ import { CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js"; import * as fs from "fs"; import * as path from "path"; import { frameworkLogger } from "../core/framework-logger.js"; import { type GovernanceVote } from "../governance/llm-governance-provider.js"; import { contextAnalysis as architectContextAnalysis, codebaseStructure as architectCodebaseStructure, dependencyAnalysis as architectDependencyAnalysis, architectureAssessment as architectArchitectureAssessment, type ArchitectureAssessment, } from "../architect/architect-tools.js"; export type ConferModelVerdict = "PASS" | "CONDITIONAL" | "FAIL" | "UNREVIEWED"; /** A confer verdict counts only when a model vote exists. Metrics alone are UNREVIEWED. */ export function conferVerdictFromModelVote( vote: Pick | null, ): ConferModelVerdict { if (!vote) return "UNREVIEWED"; if (vote.decision === "reject") return "FAIL"; if (vote.decision === "approve") return "PASS"; return "CONDITIONAL"; } export function formatArchitectureAssessmentConferText( conferPrompt: string, assessment: ArchitectureAssessment, verdict: ConferModelVerdict = "UNREVIEWED", ): string { const assessmentJson = JSON.stringify(assessment, null, 2); const trimmed = conferPrompt.trim(); if (!trimmed) return assessmentJson; return `${trimmed}\n\n## Architecture assessment\n${assessmentJson}\n\nVerdict: ${verdict}\nTop risks: review assessment metrics above\nHardening: address high-complexity or coupling findings before resuming`; } interface DirectoryNode { name: string; path: string; type: "directory"; children: Array, } interface FileNode { name: string; type: "file"; extension: string; } export async function renderArchitectureAssessment( args: Record | undefined, ): Promise<{ content: Array<{ type: "text"; text: string }> }> { const projectRoot = typeof args?.projectRoot === "string" ? args.projectRoot : ""; const assessmentType = args?.assessmentType === "quick" || args?.assessmentType === "comprehensive" ? args.assessmentType : "comprehensive"; const conferPrompt = typeof args?.conferPrompt === "string" ? args.conferPrompt : ""; frameworkLogger.log("mcps/architect-tools", "architecture-assessment", "info", { projectRoot }); const result = await architectArchitectureAssessment(projectRoot, assessmentType); return { content: [ { type: "text", text: formatArchitectureAssessmentConferText(conferPrompt, result, "UNREVIEWED"), }, ], }; } class XrayArchitectToolsServer extends XrayKnowledgeSkillBase { constructor() { super("architect-tools", "3.1.0"); this.tools = [ { name: "context-analysis", description: "Perform comprehensive codebase intelligence gathering including structure analysis, dependency mapping, and architectural pattern recognition", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Root directory of the project to analyze", }, depth: { type: "string", enum: ["overview", "detailed", "comprehensive"], default: "detailed", description: "Analysis depth level", }, includeFiles: { type: "array", items: { type: "string" }, description: "Specific files to include in analysis", }, }, required: ["projectRoot"], }, }, { name: "codebase-structure", description: "Analyze file organization, module distribution, and directory hierarchy with optional metrics", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Root directory of the project", }, includeMetrics: { type: "boolean", default: true, description: "Include detailed metrics", }, maxDepth: { type: "number", default: 10, description: "Maximum directory depth to analyze", }, }, required: ["projectRoot"], }, }, { name: "dependency-analysis", description: "Map component dependencies, identify coupling patterns, and assess architectural relationships", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Root directory of the project", }, focusAreas: { type: "array", items: { type: "string" }, description: "Specific areas to focus dependency analysis on", }, includeGraphs: { type: "boolean", default: true, description: "Include dependency graphs in output", }, }, required: ["projectRoot"], }, }, { name: "architecture-assessment", description: "Evaluate overall architectural health with scores, issues, and improvement recommendations", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Root directory of the project", }, assessmentType: { type: "string", enum: ["quick", "comprehensive"], default: "comprehensive", description: "Type of assessment to perform", }, focusMetrics: { type: "array", items: { type: "string", enum: [ "complexity", "coupling", "cohesion", "testability", "scalability", ], }, description: "Specific metrics to focus assessment on", }, }, required: ["projectRoot"], }, }, ]; this.handlers = { "context-analysis": async (args) => this.contextAnalysis(args as Record | undefined), "codebase-structure": async (args) => this.codebaseStructure(args as Record | undefined), "dependency-analysis": async (args) => this.dependencyAnalysis(args as Record | undefined), "architecture-assessment": async (args) => this.architectureAssessment(args as Record | undefined), }; this.setupToolHandlers(); frameworkLogger.log("mcps/architect-tools", "init", "info", { message: "0xRay Architect Tools MCP Server initialized" }); } override setupToolHandlers(): void { this.setupListToolsHandler(); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = this.handlers[name]; if (!handler) throw new Error(`Unknown tool: ${name}`); try { return await handler(args); } catch (error) { frameworkLogger.log("mcps/architect-tools", "tool", "error", { tool: name, error: String(error) }); throw error; } }); } // Tool implementations — delegates to real architect-tools library private async contextAnalysis(args: Record | undefined) { const projectRoot = (args?.projectRoot as string) || ""; const depth = (args?.depth as "overview" | "detailed" | "comprehensive") || "detailed"; const includeFiles = args?.includeFiles as string[] | undefined; frameworkLogger.log("mcps/architect-tools", "context-analysis", "info", { projectRoot }); const result = await architectContextAnalysis(projectRoot, includeFiles, depth); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } private async codebaseStructure(args: Record | undefined) { const projectRoot = (args?.projectRoot as string) || ""; const includeMetrics = (args?.includeMetrics as boolean) ?? true; frameworkLogger.log("mcps/architect-tools", "codebase-structure", "info", { projectRoot }); const result = await architectCodebaseStructure(projectRoot, includeMetrics); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } private async dependencyAnalysis(args: Record | undefined) { const projectRoot = (args?.projectRoot as string) || ""; const focusAreas = args?.focusAreas as string[] | undefined; frameworkLogger.log("mcps/architect-tools", "dependency-analysis", "info", { projectRoot }); const result = await architectDependencyAnalysis(projectRoot, focusAreas); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } private async architectureAssessment(args: Record | undefined) { return renderArchitectureAssessment(args); } } if (import.meta.url === `file://${fs.realpathSync(process.argv[1]!)}`) { const server = new XrayArchitectToolsServer(); server.run().catch((error) => frameworkLogger.log("mcps/architect-tools", "run", "error", { error: String(error) })); } export default XrayArchitectToolsServer;