UNPKG

@creedspace/mcp-server

Version:

Universal MCP server for Creed Space - AI safety guardrails in 10 seconds

260 lines 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CreedSpaceMCPServer = void 0; const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const api_client_js_1 = require("./api-client.js"); const tools_js_1 = require("./tools.js"); class CreedSpaceMCPServer { server; client; currentPersona; constructor(config = {}) { this.currentPersona = config.persona || 'ambassador'; this.client = new api_client_js_1.CreedSpaceClient(config); this.server = new index_js_1.Server({ name: 'creedspace', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.setupHandlers(); } setupHandlers() { // List available tools this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: tools_js_1.CREEDSPACE_TOOLS, })); // Handle tool calls this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'get_constitution': { const personaId = args?.persona_id || this.currentPersona; const constitution = await this.client.getMergedConstitution(personaId); return { content: [ { type: 'text', text: JSON.stringify({ persona: constitution.persona.name, icon: constitution.persona.icon, totalRules: constitution.totalRules, content: constitution.mergedContent, uvcToken: constitution.uvcToken, }, null, 2), }, ], }; } case 'list_personas': { const personas = await this.client.getPersonas(); return { content: [ { type: 'text', text: JSON.stringify({ current: this.currentPersona, available: personas.map((p) => ({ id: p.id, name: p.name, icon: p.icon, description: p.description, active: p.isActive, })), }, null, 2), }, ], }; } case 'set_persona': { const personaId = args?.persona_id; if (!personaId) { throw new Error('persona_id is required'); } // Verify persona exists const persona = await this.client.getPersona(personaId); this.currentPersona = personaId; this.client.setPersona(personaId); return { content: [ { type: 'text', text: `Switched to ${persona.name} persona (${persona.icon}). ${persona.description}`, }, ], }; } case 'get_uvc_qualities': { const personaId = args?.persona_id || this.currentPersona; const uvc = await this.client.getUvcQualities(personaId); if (!uvc) { return { content: [ { type: 'text', text: `No UVC qualities configured for ${personaId}`, }, ], }; } return { content: [ { type: 'text', text: JSON.stringify(uvc, null, 2), }, ], }; } case 'get_system_prompt': { const personaId = args?.persona_id || this.currentPersona; const systemPrompt = await this.client.getSystemPrompt(personaId); return { content: [ { type: 'text', text: systemPrompt, }, ], }; } case 'preview_export': { const config = { personaId: args?.persona_id || this.currentPersona, personaName: '', includeSystemPrompt: args?.include_system_prompt ?? true, includeConstitutions: args?.include_constitutions ?? true, includeUvc: args?.include_uvc ?? true, selectedConstitutionIds: [], }; // Get persona name const persona = await this.client.getPersona(config.personaId); config.personaName = persona.name; // Get all constitution IDs for this persona const constitutions = await this.client.getConstitutions(config.personaId); config.selectedConstitutionIds = constitutions.map((c) => c.id); const preview = await this.client.getExportPreview(config); return { content: [ { type: 'text', text: preview, }, ], }; } case 'get_constitution_by_id': { const constitutionId = args?.constitution_id; if (!constitutionId) { throw new Error('constitution_id is required'); } const constitutions = await this.client.getConstitutions(); const constitution = constitutions.find((c) => c.id === constitutionId); if (!constitution) { throw new Error(`Constitution not found: ${constitutionId}`); } return { content: [ { type: 'text', text: JSON.stringify(constitution, null, 2), }, ], }; } case 'search_constitutions': { const query = args?.query || ''; const personaId = args?.persona_id; let constitutions = await this.client.getConstitutions(personaId); if (query) { const searchLower = query.toLowerCase(); constitutions = constitutions.filter((c) => c.name.toLowerCase().includes(searchLower) || c.content.toLowerCase().includes(searchLower)); } return { content: [ { type: 'text', text: JSON.stringify({ query, persona: personaId || 'all', results: constitutions.length, constitutions: constitutions.map((c) => ({ id: c.id, name: c.name, persona: c.personaId, isSystem: c.isSystemConstitution, preview: c.content.substring(0, 200) + '...', })), }, null, 2), }, ], }; } case 'get_active_persona': { const persona = await this.client.getPersona(this.currentPersona); return { content: [ { type: 'text', text: JSON.stringify({ id: persona.id, name: persona.name, icon: persona.icon, description: persona.description, }, null, 2), }, ], }; } case 'clear_cache': { this.client.clearCache(); return { content: [ { type: 'text', text: 'Cache cleared successfully. Fresh data will be fetched on next request.', }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { // SECURITY: Log error with full context for security monitoring const errorContext = { toolName: name, args: JSON.stringify(args), persona: this.currentPersona, timestamp: new Date().toISOString(), stack: error instanceof Error ? error.stack : undefined }; console.error('[MCP_SERVER_ERROR]', JSON.stringify(errorContext)); // Re-throw with proper error chain to preserve original context if (error instanceof Error) { const enhancedError = new Error(`MCP tool '${name}' failed: ${error.message}`); enhancedError.cause = error; // Preserve error chain throw enhancedError; } // For non-Error objects, create specific actionable error throw new Error(`MCP tool '${name}' failed with unknown error type: ${String(error)}`); } }); } async start() { const transport = new stdio_js_1.StdioServerTransport(); await this.server.connect(transport); // Log to stderr so it doesn't interfere with stdio protocol console.error('Creed Space MCP Server started'); console.error(`Active persona: ${this.currentPersona}`); } } exports.CreedSpaceMCPServer = CreedSpaceMCPServer; //# sourceMappingURL=server.js.map