Skip to main content
Glama
angrysky56

Narrative Graph MCP

index.ts5.2 kB
#!/usr/bin/env node /** * Narrative Graph MCP Server * Implements the Model Context Protocol for Random Tree Model operations */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; import winston from 'winston'; // Configure Winston logger const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'logs/server.log' }), ], }); import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; // Import our tools import createNarrativeTree from './tools/createNarrativeTree.js'; import generateEnsemble from './tools/generateEnsemble.js'; import traverseNarrative from './tools/traverseNarrative.js'; import findOptimalDepth from './tools/findOptimalDepth.js'; // Import schemas import { createNarrativeTreeSchema } from './schemas/createNarrativeTreeSchema.js'; import { generateEnsembleSchema } from './schemas/generateEnsembleSchema.js'; import { traverseNarrativeSchema } from './schemas/traverseNarrativeSchema.js'; import { findOptimalDepthSchema } from './schemas/findOptimalDepthSchema.js'; // Tool registry const tools = { rtm_create_narrative_tree: createNarrativeTree, rtm_generate_ensemble: generateEnsemble, rtm_traverse_narrative: traverseNarrative, rtm_find_optimal_depth: findOptimalDepth, }; // Centralized tool definitions const toolDefinitions = [ { name: 'rtm_create_narrative_tree', description: 'Create a Random Tree Model encoding of a narrative text', inputSchema: createNarrativeTreeSchema, }, { name: 'rtm_generate_ensemble', description: 'Generate a statistical ensemble of Random Trees to model population-level recall', inputSchema: generateEnsembleSchema, }, { name: 'rtm_traverse_narrative', description: 'Traverse a narrative tree at different depths to get summaries at varying abstraction levels', inputSchema: traverseNarrativeSchema, }, { name: 'rtm_find_optimal_depth', description: 'Find the optimal traversal depth to achieve a target recall length', inputSchema: findOptimalDepthSchema, }, ]; // Create server instance const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const packageJsonPath = join(__dirname, '../package.json'); const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); const server = new Server( { name: packageJson.name, version: packageJson.version, }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async (request) => { logger.debug('Received ListToolsRequest', { request }); return { tools: toolDefinitions.map(tool => ({ name: tool.name, description: tool.description, inputSchema: zodToJsonSchema(tool.inputSchema, { target: 'openApi3' }), // Convert Zod schema to JSON schema })), }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const toolDefinition = toolDefinitions.find(tool => tool.name === name); if (!toolDefinition) { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } try { // Validate arguments against the schema const parsedArgs = toolDefinition.inputSchema.parse(args); // Get the tool handler const toolHandler = tools[name as keyof typeof tools]; // Execute the tool const result = await toolHandler(parsedArgs as any); return result; } catch (error) { logger.error(`Error executing tool ${name}:`, error); if (error instanceof z.ZodError) { throw new McpError( ErrorCode.InvalidParams, `Invalid arguments for tool ${name}: ${error.errors.map(e => e.message).join(', ')}` ); } else { throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : JSON.stringify(error)}` ); } } }); // Start the server async function main() { logger.debug('Entering main function.'); logger.info('Starting Narrative Graph MCP Server...'); logger.debug('Creating StdioServerTransport.'); const transport = new StdioServerTransport(); logger.debug('Connecting server to transport.'); await server.connect(transport); logger.debug('Server connected to transport.'); logger.debug('Resuming stdin.'); process.stdin.resume(); logger.info('Narrative Graph MCP Server running on stdio'); } // Handle errors process.on('unhandledRejection', (error) => { logger.error('Unhandled rejection:', error); process.exit(1); }); // Run the server main().catch((error) => { logger.error('Fatal error:', error); process.exit(1); });

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/angrysky56/narrative-graph-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server