@creedspace/mcp-server
Version:
Universal MCP server for Creed Space - AI safety guardrails in 10 seconds
260 lines • 12.6 kB
JavaScript
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
;