Last active
August 24, 2025 17:39
-
-
Save szymdzum/6085e1a40de3712834e159ed592af305 to your computer and use it in GitHub Desktop.
boltzmann-opus
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| /** | |
| * Developer Hub MCP Server | |
| * | |
| * THE SCIENCE: Why This Works | |
| * | |
| * 1. SEMANTIC MEMORY ARCHITECTURE | |
| * Human brains have two memory systems: | |
| * - Episodic (specific events) | |
| * - Semantic (general knowledge) | |
| * | |
| * This MCP server acts as the semantic memory for your AI assistant. | |
| * Instead of Claude trying to remember "that time you told me about your project structure", | |
| * it has constant access to the actual structure itself. | |
| * | |
| * 2. ATTENTION MECHANISM | |
| * Transformer models (like Claude) use attention to focus on relevant information. | |
| * By exposing your entire Developer structure, we're giving the attention mechanism | |
| * more signal to work with. It's like the difference between searching in a | |
| * well-organized library vs a pile of books on the floor. | |
| * | |
| * 3. GRAPH TOPOLOGY | |
| * Code isn't linear - it's a graph. Projects depend on each other, | |
| * scripts reference knowledge, knowledge links to projects. | |
| * This server exposes that graph structure, allowing the AI to traverse | |
| * relationships instead of treating everything as isolated files. | |
| */ | |
| import { Server } from "@modelcontextprotocol/sdk/server/index.js"; | |
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | |
| import { | |
| ListToolsRequestSchema, | |
| CallToolRequestSchema, | |
| ListResourcesRequestSchema, | |
| ReadResourceRequestSchema, | |
| Tool, | |
| Resource, | |
| TextContent, | |
| CallToolRequest, | |
| ListToolsRequest, | |
| ListResourcesRequest, | |
| ReadResourceRequest | |
| } from "@modelcontextprotocol/sdk/types.js"; | |
| import fs from 'fs/promises'; | |
| import path from 'path'; | |
| import { glob } from 'glob'; | |
| import matter from 'gray-matter'; | |
| import chokidar from 'chokidar'; | |
| import type { FSWatcher } from 'chokidar'; | |
| import { fileURLToPath } from 'url'; | |
| import { dirname } from 'path'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = dirname(__filename); | |
| // Your Developer hub root - this is the context window into your entire setup | |
| const DEVELOPER_ROOT = path.resolve(process.env.HOME!, 'Developer'); | |
| /** | |
| * INFORMATION THEORY PRINCIPLE: | |
| * We're reducing entropy (uncertainty) by providing structure. | |
| * Without this, Claude has to guess what projects exist, where knowledge lives, etc. | |
| * With this, it KNOWS the exact topology of your development environment. | |
| */ | |
| // Primary focus is the Knowledge vault - everything else is secondary | |
| const HUB_STRUCTURE = { | |
| knowledge: path.join(DEVELOPER_ROOT, 'Knowledge'), | |
| projects: path.join(DEVELOPER_ROOT, 'Knowledge/Projects'), // Project notes in Obsidian | |
| scripts: path.join(DEVELOPER_ROOT, 'Scripts'), | |
| configs: path.join(DEVELOPER_ROOT, 'Configs'), | |
| tools: path.join(DEVELOPER_ROOT, 'Tools'), | |
| resources: path.join(DEVELOPER_ROOT, 'Knowledge/Resources'), // Resource docs | |
| archive: path.join(DEVELOPER_ROOT, 'Archive'), | |
| ai: path.join(DEVELOPER_ROOT, '.ai') | |
| } as const; | |
| // Type definitions for our domain | |
| interface KnowledgeNode { | |
| frontmatter: Record<string, any>; | |
| links: string[]; | |
| path: string; | |
| type: KnowledgeType; | |
| } | |
| interface ProjectInfo { | |
| path: string; | |
| dependencies: string[]; | |
| hasAIContext: boolean; | |
| type: ProjectType; | |
| } | |
| interface SearchResult { | |
| type: 'knowledge' | 'project' | 'script' | 'config'; | |
| path: string; | |
| score?: number; | |
| metadata?: Record<string, any>; | |
| connections?: number; | |
| name?: string; | |
| projectType?: ProjectType; | |
| hasAIContext?: boolean; | |
| dependencies?: string[]; | |
| } | |
| interface GraphNode { | |
| id: string; | |
| type: ProjectType; | |
| hasAIContext: boolean; | |
| } | |
| interface Graph { | |
| nodes: GraphNode[]; | |
| edges: Array<{ from: string; to: string }>; | |
| } | |
| interface RelatedKnowledge { | |
| file: string; | |
| level: number; | |
| type: KnowledgeType; | |
| frontmatter: Record<string, any>; | |
| } | |
| interface WorkspaceAnalysis { | |
| timestamp: string; | |
| focus: AnalysisFocus; | |
| findings: Array<{ | |
| type: string; | |
| [key: string]: any; | |
| }>; | |
| } | |
| type KnowledgeType = 'project-doc' | 'code-snippet' | 'daily-note' | 'learning' | 'resource' | 'general'; | |
| type ProjectType = 'node' | 'python' | 'rust' | 'go' | 'unknown'; | |
| type ResourceType = 'all' | 'knowledge' | 'projects' | 'scripts' | 'configs'; | |
| type AnalysisFocus = 'dependencies' | 'documentation' | 'structure' | 'duplication'; | |
| // Tool argument types | |
| interface ExploreHubArgs { | |
| query: string; | |
| type?: ResourceType; | |
| } | |
| interface GetProjectGraphArgs { | |
| project?: string; | |
| } | |
| interface FindRelatedKnowledgeArgs { | |
| topic: string; | |
| depth?: number; | |
| } | |
| interface GenerateFromTemplateArgs { | |
| template: string; | |
| name: string; | |
| variables?: Record<string, any>; | |
| } | |
| interface AnalyzeWorkspaceArgs { | |
| focus: AnalysisFocus; | |
| } | |
| class KnowledgeGraphServer { | |
| private server: Server; | |
| private watcher: FSWatcher | null = null; | |
| private knowledgeGraph: Map<string, KnowledgeNode> = new Map(); | |
| private projectDependencies: Map<string, ProjectInfo> = new Map(); | |
| constructor() { | |
| this.server = new Server({ | |
| name: "knowledge-graph", | |
| version: "1.0.0", | |
| }, { | |
| capabilities: { | |
| tools: {}, | |
| resources: {} | |
| }, | |
| }); | |
| this.setupHandlers(); | |
| this.initializeKnowledgeGraph(); | |
| } | |
| /** | |
| * GRAPH THEORY APPLICATION: | |
| * Build an in-memory graph of all relationships in your Developer hub. | |
| * This allows O(1) lookups instead of O(n) file system traversals. | |
| */ | |
| private async initializeKnowledgeGraph(): Promise<void> { | |
| console.error("Building knowledge graph..."); | |
| try { | |
| // Index Obsidian vault (Knowledge/) | |
| const knowledgeFiles = await glob('**/*.md', { | |
| cwd: HUB_STRUCTURE.knowledge, | |
| ignore: ['.obsidian/**', '.trash/**'] | |
| }); | |
| for (const file of knowledgeFiles) { | |
| const fullPath = path.join(HUB_STRUCTURE.knowledge, file); | |
| const content = await fs.readFile(fullPath, 'utf-8'); | |
| const { data, content: body } = matter(content); | |
| // Extract wiki-links [[]] to build connection graph | |
| const links = (body.match(/\[\[([^\]]+)\]\]/g) || []) | |
| .map(link => link.slice(2, -2)); | |
| this.knowledgeGraph.set(file, { | |
| frontmatter: data, | |
| links, | |
| path: fullPath, | |
| type: this.categorizeKnowledge(file) | |
| }); | |
| } | |
| } catch (e) { | |
| console.error("Knowledge folder not found or empty:", e); | |
| } | |
| try { | |
| // Index Projects and their dependencies | |
| const projectDirs = await fs.readdir(HUB_STRUCTURE.projects); | |
| for (const project of projectDirs) { | |
| const projectPath = path.join(HUB_STRUCTURE.projects, project); | |
| const stat = await fs.stat(projectPath); | |
| if (stat.isDirectory()) { | |
| await this.indexProject(project, projectPath); | |
| } | |
| } | |
| } catch (e) { | |
| console.error("Projects folder not found or empty:", e); | |
| } | |
| console.error(`Knowledge graph built: ${this.knowledgeGraph.size} nodes`); | |
| } | |
| private async indexProject(name: string, projectPath: string): Promise<void> { | |
| const dependencies = new Set<string>(); | |
| // Check for package.json dependencies | |
| try { | |
| const pkgPath = path.join(projectPath, 'package.json'); | |
| const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8')); | |
| // Internal workspace dependencies | |
| const deps = { ...pkg.dependencies, ...pkg.devDependencies }; | |
| for (const [dep, version] of Object.entries(deps)) { | |
| if (typeof version === 'string' && | |
| (version.startsWith('file:') || version.startsWith('workspace:'))) { | |
| dependencies.add(dep); | |
| } | |
| } | |
| } catch (e) { | |
| // Not a Node project, check for other patterns | |
| } | |
| // Check for Claude.md or .claude directory (AI context) | |
| const hasClaudeContext = await this.fileExists(path.join(projectPath, 'Claude.md')) || | |
| await this.fileExists(path.join(projectPath, '.claude')); | |
| this.projectDependencies.set(name, { | |
| path: projectPath, | |
| dependencies: Array.from(dependencies), | |
| hasAIContext: hasClaudeContext, | |
| type: await this.detectProjectType(projectPath) | |
| }); | |
| } | |
| private async detectProjectType(projectPath: string): Promise<ProjectType> { | |
| // PATTERN RECOGNITION: Like how humans categorize, we use heuristics | |
| if (await this.fileExists(path.join(projectPath, 'package.json'))) return 'node'; | |
| if (await this.fileExists(path.join(projectPath, 'requirements.txt'))) return 'python'; | |
| if (await this.fileExists(path.join(projectPath, 'Cargo.toml'))) return 'rust'; | |
| if (await this.fileExists(path.join(projectPath, 'go.mod'))) return 'go'; | |
| return 'unknown'; | |
| } | |
| private categorizeKnowledge(filePath: string): KnowledgeType { | |
| // HIERARCHICAL CATEGORIZATION: Mimics how human brain organizes information | |
| if (filePath.includes('Projects/')) return 'project-doc'; | |
| if (filePath.includes('Snippets/')) return 'code-snippet'; | |
| if (filePath.includes('Notes/daily/')) return 'daily-note'; | |
| if (filePath.includes('Learning/')) return 'learning'; | |
| if (filePath.includes('Resources/')) return 'resource'; | |
| return 'general'; | |
| } | |
| private async fileExists(filePath: string): Promise<boolean> { | |
| try { | |
| await fs.access(filePath); | |
| return true; | |
| } catch { | |
| return false; | |
| } | |
| } | |
| private setupHandlers(): void { | |
| // List available tools | |
| this.server.setRequestHandler(ListToolsRequestSchema, async (): Promise<{ tools: Tool[] }> => ({ | |
| tools: [ | |
| { | |
| name: "explore_hub", | |
| description: "Explore the Developer hub structure and find relevant resources", | |
| inputSchema: { | |
| type: "object", | |
| properties: { | |
| query: { | |
| type: "string", | |
| description: "What to search for (project, knowledge, script, etc.)" | |
| }, | |
| type: { | |
| type: "string", | |
| enum: ["all", "knowledge", "projects", "scripts", "configs"], | |
| description: "Type of resource to search" | |
| } | |
| }, | |
| required: ["query"] | |
| }, | |
| }, | |
| { | |
| name: "get_project_graph", | |
| description: "Get the dependency graph of all projects", | |
| inputSchema: { | |
| type: "object", | |
| properties: { | |
| project: { | |
| type: "string", | |
| description: "Specific project to focus on (optional)" | |
| } | |
| } | |
| }, | |
| }, | |
| { | |
| name: "find_related_knowledge", | |
| description: "Find knowledge documents related to a topic using the knowledge graph", | |
| inputSchema: { | |
| type: "object", | |
| properties: { | |
| topic: { | |
| type: "string", | |
| description: "Topic to find related knowledge about" | |
| }, | |
| depth: { | |
| type: "number", | |
| description: "How many levels of relationships to traverse (default: 2)" | |
| } | |
| }, | |
| required: ["topic"] | |
| }, | |
| }, | |
| { | |
| name: "generate_from_template", | |
| description: "Generate new project/component from templates", | |
| inputSchema: { | |
| type: "object", | |
| properties: { | |
| template: { | |
| type: "string", | |
| description: "Template name or path" | |
| }, | |
| name: { | |
| type: "string", | |
| description: "Name for the new project/component" | |
| }, | |
| variables: { | |
| type: "object", | |
| description: "Variables to inject into template" | |
| } | |
| }, | |
| required: ["template", "name"] | |
| }, | |
| }, | |
| { | |
| name: "analyze_workspace", | |
| description: "Analyze the entire workspace for patterns, issues, or improvements", | |
| inputSchema: { | |
| type: "object", | |
| properties: { | |
| focus: { | |
| type: "string", | |
| enum: ["dependencies", "documentation", "structure", "duplication"], | |
| description: "What aspect to analyze" | |
| } | |
| }, | |
| required: ["focus"] | |
| }, | |
| } | |
| ], | |
| })); | |
| // Handle tool calls | |
| this.server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => { | |
| const { name, arguments: args } = request.params; | |
| switch (name) { | |
| case "explore_hub": { | |
| const exploreArgs = args as unknown as ExploreHubArgs; | |
| return await this.exploreHub(exploreArgs); | |
| } | |
| case "get_project_graph": { | |
| const graphArgs = args as unknown as GetProjectGraphArgs; | |
| return await this.getProjectGraph(graphArgs); | |
| } | |
| case "find_related_knowledge": { | |
| const knowledgeArgs = args as unknown as FindRelatedKnowledgeArgs; | |
| return await this.findRelatedKnowledge(knowledgeArgs); | |
| } | |
| case "generate_from_template": { | |
| const templateArgs = args as unknown as GenerateFromTemplateArgs; | |
| return await this.generateFromTemplate(templateArgs); | |
| } | |
| case "analyze_workspace": { | |
| const analyzeArgs = args as unknown as AnalyzeWorkspaceArgs; | |
| return await this.analyzeWorkspace(analyzeArgs); | |
| } | |
| default: | |
| throw new Error(`Unknown tool: ${name}`); | |
| } | |
| }); | |
| // List available resources (files, documents) | |
| this.server.setRequestHandler(ListResourcesRequestSchema, async (): Promise<{ resources: Resource[] }> => { | |
| const resources: Resource[] = []; | |
| // Add key configuration files | |
| try { | |
| await fs.access(path.join(DEVELOPER_ROOT, 'README.md')); | |
| resources.push({ | |
| uri: `file://${DEVELOPER_ROOT}/README.md`, | |
| mimeType: "text/markdown", | |
| name: "Developer Hub README" | |
| }); | |
| } catch { | |
| // README doesn't exist | |
| } | |
| // Add recent projects with AI context | |
| for (const [name, info] of this.projectDependencies.entries()) { | |
| if (info.hasAIContext) { | |
| resources.push({ | |
| uri: `file://${info.path}`, | |
| mimeType: "text/directory", | |
| name: `Project: ${name}` | |
| }); | |
| } | |
| } | |
| return { resources }; | |
| }); | |
| // Read specific resources | |
| this.server.setRequestHandler(ReadResourceRequestSchema, async (request: ReadResourceRequest) => { | |
| const { uri } = request.params; | |
| const filePath = uri.replace('file://', ''); | |
| const content = await fs.readFile(filePath, 'utf-8'); | |
| return { | |
| contents: [{ | |
| uri, | |
| mimeType: "text/plain", | |
| text: content | |
| }] | |
| }; | |
| }); | |
| } | |
| /** | |
| * SEARCH ALGORITHM: Combines multiple ranking signals | |
| * Similar to PageRank but for your personal knowledge base | |
| */ | |
| private async exploreHub({ query, type = "all" }: ExploreHubArgs): Promise<{ content: TextContent[] }> { | |
| const results: SearchResult[] = []; | |
| const queryLower = query.toLowerCase(); | |
| // Search knowledge documents | |
| if (type === "all" || type === "knowledge") { | |
| for (const [file, info] of this.knowledgeGraph.entries()) { | |
| const score = this.calculateRelevance(file, info, queryLower); | |
| if (score > 0) { | |
| results.push({ | |
| type: 'knowledge', | |
| path: file, | |
| score, | |
| metadata: info.frontmatter, | |
| connections: info.links.length | |
| }); | |
| } | |
| } | |
| } | |
| // Search projects | |
| if (type === "all" || type === "projects") { | |
| for (const [name, info] of this.projectDependencies.entries()) { | |
| if (name.toLowerCase().includes(queryLower)) { | |
| results.push({ | |
| type: 'project', | |
| name, | |
| path: info.path, | |
| projectType: info.type, | |
| hasAIContext: info.hasAIContext, | |
| dependencies: info.dependencies | |
| }); | |
| } | |
| } | |
| } | |
| // Sort by relevance | |
| results.sort((a, b) => (b.score || 0) - (a.score || 0)); | |
| return { | |
| content: [{ | |
| type: "text", | |
| text: JSON.stringify(results.slice(0, 20), null, 2) | |
| }] | |
| }; | |
| } | |
| private calculateRelevance(file: string, info: KnowledgeNode, query: string): number { | |
| let score = 0; | |
| // Title match (highest weight) | |
| if (file.toLowerCase().includes(query)) score += 10; | |
| // Frontmatter tags match | |
| if (info.frontmatter.tags) { | |
| const tags = Array.isArray(info.frontmatter.tags) | |
| ? info.frontmatter.tags | |
| : [info.frontmatter.tags]; | |
| if (tags.some((tag: string) => tag.toLowerCase().includes(query))) score += 5; | |
| } | |
| // Number of connections (PageRank-style) | |
| score += Math.log(info.links.length + 1); | |
| return score; | |
| } | |
| /** | |
| * GRAPH TRAVERSAL: Breadth-first search through knowledge connections | |
| * This mimics how human memory works - activating related concepts | |
| */ | |
| private async findRelatedKnowledge({ topic, depth = 2 }: FindRelatedKnowledgeArgs): Promise<{ content: TextContent[] }> { | |
| const visited = new Set<string>(); | |
| const queue: Array<{ file: string; info: KnowledgeNode; level: number }> = []; | |
| const results: RelatedKnowledge[] = []; | |
| // Find starting nodes | |
| for (const [file, info] of this.knowledgeGraph.entries()) { | |
| if (file.toLowerCase().includes(topic.toLowerCase())) { | |
| queue.push({ file, info, level: 0 }); | |
| visited.add(file); | |
| } | |
| } | |
| // BFS through the knowledge graph | |
| while (queue.length > 0) { | |
| const item = queue.shift(); | |
| if (!item) continue; | |
| const { file, info, level } = item; | |
| results.push({ | |
| file, | |
| level, | |
| type: info.type, | |
| frontmatter: info.frontmatter | |
| }); | |
| if (level < depth) { | |
| // Follow links | |
| for (const link of info.links) { | |
| const linkedFile = this.findFileByName(link); | |
| if (linkedFile && !visited.has(linkedFile)) { | |
| visited.add(linkedFile); | |
| const linkedInfo = this.knowledgeGraph.get(linkedFile); | |
| if (linkedInfo) { | |
| queue.push({ | |
| file: linkedFile, | |
| info: linkedInfo, | |
| level: level + 1 | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return { | |
| content: [{ | |
| type: "text", | |
| text: JSON.stringify({ | |
| root: topic, | |
| nodes: results, | |
| totalConnections: results.reduce((sum, r) => | |
| sum + (this.knowledgeGraph.get(r.file)?.links.length || 0), 0 | |
| ) | |
| }, null, 2) | |
| }] | |
| }; | |
| } | |
| private findFileByName(name: string): string | null { | |
| // Handle [[file#section]] format | |
| const cleanName = name.split('#')[0]; | |
| for (const file of this.knowledgeGraph.keys()) { | |
| const basename = path.basename(file, '.md'); | |
| if (basename === cleanName) return file; | |
| } | |
| return null; | |
| } | |
| private async getProjectGraph({ project }: GetProjectGraphArgs): Promise<{ content: TextContent[] }> { | |
| const graph: Graph = { | |
| nodes: [], | |
| edges: [] | |
| }; | |
| for (const [name, info] of this.projectDependencies.entries()) { | |
| if (!project || name === project || info.dependencies.includes(project)) { | |
| graph.nodes.push({ | |
| id: name, | |
| type: info.type, | |
| hasAIContext: info.hasAIContext | |
| }); | |
| for (const dep of info.dependencies) { | |
| graph.edges.push({ | |
| from: name, | |
| to: dep | |
| }); | |
| } | |
| } | |
| } | |
| return { | |
| content: [{ | |
| type: "text", | |
| text: JSON.stringify(graph, null, 2) | |
| }] | |
| }; | |
| } | |
| private async generateFromTemplate({ template, name, variables = {} }: GenerateFromTemplateArgs): Promise<{ content: TextContent[] }> { | |
| // This is where you'd implement template generation | |
| // For now, returning a structured plan | |
| const templatePath = path.join(DEVELOPER_ROOT, 'Resources/Templates', template); | |
| const targetPath = path.join(DEVELOPER_ROOT, 'Projects', name); | |
| return { | |
| content: [{ | |
| type: "text", | |
| text: JSON.stringify({ | |
| action: "generate", | |
| template: templatePath, | |
| target: targetPath, | |
| variables, | |
| steps: [ | |
| "1. Copy template structure", | |
| "2. Replace variables in files", | |
| "3. Initialize git repository", | |
| "4. Create Claude.md context file", | |
| "5. Link to Knowledge base", | |
| "6. Update project graph" | |
| ] | |
| }, null, 2) | |
| }] | |
| }; | |
| } | |
| private async analyzeWorkspace({ focus }: AnalyzeWorkspaceArgs): Promise<{ content: TextContent[] }> { | |
| const analysis: WorkspaceAnalysis = { | |
| timestamp: new Date().toISOString(), | |
| focus, | |
| findings: [] | |
| }; | |
| switch (focus) { | |
| case "dependencies": | |
| // Find circular dependencies | |
| for (const [name, info] of this.projectDependencies.entries()) { | |
| for (const dep of info.dependencies) { | |
| const depInfo = this.projectDependencies.get(dep); | |
| if (depInfo?.dependencies.includes(name)) { | |
| analysis.findings.push({ | |
| type: 'circular_dependency', | |
| projects: [name, dep] | |
| }); | |
| } | |
| } | |
| } | |
| break; | |
| case "documentation": | |
| // Find projects without AI context | |
| for (const [name, info] of this.projectDependencies.entries()) { | |
| if (!info.hasAIContext) { | |
| analysis.findings.push({ | |
| type: 'missing_ai_context', | |
| project: name, | |
| suggestion: 'Add Claude.md or .claude/README.md' | |
| }); | |
| } | |
| } | |
| break; | |
| case "structure": | |
| // Analyze folder structure consistency | |
| const structures = new Map<string, string[]>(); | |
| for (const [name, info] of this.projectDependencies.entries()) { | |
| const structure = await this.getProjectStructure(info.path); | |
| structures.set(name, structure); | |
| } | |
| // Find outliers - TODO: implement outlier detection | |
| analysis.findings.push({ | |
| type: 'structure_analysis', | |
| projectCount: structures.size, | |
| message: 'Structure analysis complete' | |
| }); | |
| break; | |
| case "duplication": | |
| // Find similar code patterns across projects | |
| // This would need more sophisticated analysis | |
| analysis.findings.push({ | |
| type: 'duplication_analysis', | |
| message: 'Duplication detection not yet implemented' | |
| }); | |
| break; | |
| } | |
| return { | |
| content: [{ | |
| type: "text", | |
| text: JSON.stringify(analysis, null, 2) | |
| }] | |
| }; | |
| } | |
| private async getProjectStructure(projectPath: string): Promise<string[]> { | |
| const structure: string[] = []; | |
| const entries = await fs.readdir(projectPath, { withFileTypes: true }); | |
| for (const entry of entries) { | |
| if (entry.isDirectory() && !entry.name.startsWith('.') && | |
| !['node_modules', 'target', 'dist', 'build'].includes(entry.name)) { | |
| structure.push(entry.name); | |
| } | |
| } | |
| return structure.sort(); | |
| } | |
| async run(): Promise<void> { | |
| const transport = new StdioServerTransport(); | |
| await this.server.connect(transport); | |
| console.error("Knowledge Graph MCP Server running"); | |
| } | |
| } | |
| // Start the server | |
| const server = new KnowledgeGraphServer(); | |
| server.run().catch(console.error); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment