Files
msd-core/gsd-memory/dist/index.js
Lex Christopherson af7a057294 feat: add GSD Memory cross-project knowledge system
Implements MCP server for semantic search across GSD projects:

MCP Tools:
- gsd_memory_search: Search across all registered projects
- gsd_memory_decisions: Find decisions from SUMMARY/PROJECT.md
- gsd_memory_patterns: Find patterns from SUMMARY.md
- gsd_memory_pitfalls: Find pitfalls from RESEARCH.md
- gsd_memory_stack: Find tech stack entries
- gsd_memory_register: Register project with memory
- gsd_memory_index: Trigger indexing/update
- gsd_memory_status: Show system status

Architecture:
- QMD wrapper with grep fallback when QMD unavailable
- YAML frontmatter extractors for GSD document types
- Project registry at ~/.gsd/projects.json
- Auto-install via bin/install.js for Claude Code

Workflow integrations:
- new-project: Registers project with memory
- plan-phase: Indexes after planning
- execute-phase: Indexes after execution
- complete-milestone: Indexes milestone completion
- Researcher agents query memory before Context7

Tests: 117 passing (66 unit + 51 integration)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 13:13:22 -06:00

298 lines
9.9 KiB
JavaScript

#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { search } from './tools/search.js';
import { findDecisions } from './tools/decisions.js';
import { findPatterns } from './tools/patterns.js';
import { findPitfalls } from './tools/pitfalls.js';
import { findStack } from './tools/stack.js';
import { register } from './tools/register.js';
import { index, indexAll } from './tools/index-tool.js';
import { getStatus } from './tools/status.js';
// Define available tools
const tools = [
{
name: 'gsd_memory_search',
description: 'Search across all registered GSD projects for content matching a query. Returns enriched results with project, phase, and document type context.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The search query'
},
project: {
type: 'string',
description: 'Optional: limit search to a specific project'
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 20)'
}
},
required: ['query']
}
},
{
name: 'gsd_memory_decisions',
description: 'Find decisions made across GSD projects. Searches KEY-DECISIONS from SUMMARY.md frontmatter and Key Decisions tables from PROJECT.md.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Optional: filter decisions by keyword'
},
project: {
type: 'string',
description: 'Optional: limit to a specific project'
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Optional: filter by tags'
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 50)'
}
}
}
},
{
name: 'gsd_memory_patterns',
description: 'Find patterns established across GSD projects. Extracts PATTERNS-ESTABLISHED from SUMMARY.md frontmatter.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Optional: filter patterns by keyword'
},
project: {
type: 'string',
description: 'Optional: limit to a specific project'
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Optional: filter by tags'
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 50)'
}
}
}
},
{
name: 'gsd_memory_pitfalls',
description: 'Find documented pitfalls from RESEARCH.md files. Includes what went wrong, causes, and prevention strategies.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Optional: filter pitfalls by keyword'
},
project: {
type: 'string',
description: 'Optional: limit to a specific project'
},
domain: {
type: 'string',
description: 'Optional: filter by technology domain'
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 50)'
}
}
}
},
{
name: 'gsd_memory_stack',
description: 'Find tech stack entries across GSD projects. Extracts from SUMMARY.md (tech-stack.added) and RESEARCH.md (standard stack tables).',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Optional: filter by library name or purpose'
},
project: {
type: 'string',
description: 'Optional: limit to a specific project'
},
limit: {
type: 'number',
description: 'Maximum number of results (default: 100)'
}
}
}
},
{
name: 'gsd_memory_register',
description: 'Register a project with GSD memory. Creates a QMD collection if QMD is available.',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Path to the project root (must contain .planning directory)'
},
name: {
type: 'string',
description: 'Optional: project name (defaults to directory name)'
}
},
required: ['path']
}
},
{
name: 'gsd_memory_index',
description: 'Trigger indexing/update for a project. Updates QMD collection if available, otherwise marks project as indexed for grep fallback.',
inputSchema: {
type: 'object',
properties: {
project: {
type: 'string',
description: 'Project name to index'
},
path: {
type: 'string',
description: 'Alternative: project path to index'
}
}
}
},
{
name: 'gsd_memory_status',
description: 'Show status of GSD memory system: QMD availability, registered projects, document counts.',
inputSchema: {
type: 'object',
properties: {}
}
}
];
// Create server instance
const server = new Server({
name: 'gsd-memory',
version: '1.0.0'
}, {
capabilities: {
tools: {}
}
});
// Handle list tools request
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools
}));
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
let result;
switch (name) {
case 'gsd_memory_search':
result = await search({
query: args?.query,
project: args?.project,
limit: args?.limit
});
break;
case 'gsd_memory_decisions':
result = await findDecisions({
query: args?.query,
project: args?.project,
tags: args?.tags,
limit: args?.limit
});
break;
case 'gsd_memory_patterns':
result = await findPatterns({
query: args?.query,
project: args?.project,
tags: args?.tags,
limit: args?.limit
});
break;
case 'gsd_memory_pitfalls':
result = await findPitfalls({
query: args?.query,
project: args?.project,
domain: args?.domain,
limit: args?.limit
});
break;
case 'gsd_memory_stack':
result = await findStack({
query: args?.query,
project: args?.project,
limit: args?.limit
});
break;
case 'gsd_memory_register':
result = await register({
path: args?.path,
name: args?.name
});
break;
case 'gsd_memory_index':
if (args?.project || args?.path) {
result = await index({
project: args?.project,
path: args?.path
});
}
else {
result = await indexAll();
}
break;
case 'gsd_memory_status':
result = await getStatus();
break;
default:
return {
content: [
{
type: 'text',
text: `Unknown tool: ${name}`
}
],
isError: true
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}
],
isError: true
};
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('GSD Memory MCP server started');
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map