Skip to main content
Glama
models.ts29.8 kB
/** * HUMMBL Mental Models Tools * MCP tool registrations for Base120 framework operations * Hybrid architecture: Local data for fast lookups, REST API for recommendations */ /* eslint-disable no-undef */ // fetch is a global in Cloudflare Workers environment import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { TRANSFORMATIONS, getAllModels, getModelByCode, searchModels, getModelsByTransformation, } from "../framework/base120.js"; import { isOk } from "../types/domain.js"; import { MCP_CONFIG } from "../config/mcp.js"; // REST API Configuration (from centralized config) const API_CONFIG = { baseUrl: MCP_CONFIG.HUMMBL_API_URL, apiKey: MCP_CONFIG.HUMMBL_API_KEY, } as const; /** * Register all model-related tools with the MCP server. */ export function registerModelTools(server: McpServer): void { // Tool: Get specific model by code server.registerTool( "get_model", { title: "Get Mental Model by Code", description: "Retrieve detailed information about a specific HUMMBL mental model using its code (e.g., P1, IN3, CO5).", inputSchema: z.object({ code: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Model code (e.g., P1, IN3, CO5)"), }), outputSchema: z.object({ code: z.string(), name: z.string(), definition: z.string(), priority: z.number(), transformation: z.string().nullable(), }), }, async ({ code }) => { const normalizedCode = code.toUpperCase(); const result = getModelByCode(normalizedCode); if (!isOk(result)) { return { content: [ { type: "text", text: `Model code '${code}' not found in HUMMBL Base120 framework. Valid codes follow the pattern [P|IN|CO|DE|RE|SY][1-20].`, }, ], isError: true, } as const; } const model = result.value; const transformation = Object.values(TRANSFORMATIONS).find((t) => t.models.some((m) => m.code === model.code) ); const payload = { code: model.code, name: model.name, definition: model.definition, priority: model.priority, transformation: transformation?.key ?? null, }; return { content: [ { type: "text", text: JSON.stringify({ model: payload }, null, 2), }, ], structuredContent: payload, } as const; } ); // Tool: List all models server.registerTool( "list_all_models", { title: "List All Mental Models", description: "Retrieve complete list of all 120 HUMMBL mental models with basic information.", inputSchema: z.object({ transformation_filter: z .enum(["P", "IN", "CO", "DE", "RE", "SY"]) .optional() .describe("Optional filter by transformation type"), }), outputSchema: z.object({ total: z.number(), models: z.array( z.object({ code: z.string(), name: z.string(), definition: z.string(), priority: z.number(), transformation: z.string(), }) ), }), }, async ({ transformation_filter }) => { let models = getAllModels(); if (transformation_filter) { const result = getModelsByTransformation(transformation_filter); if (!isOk(result)) { return { content: [ { type: "text", text: `Unable to list models for transformation '${transformation_filter}'.`, }, ], isError: true, } as const; } models = result.value; } const enriched = models.map((m) => { const trans = Object.values(TRANSFORMATIONS).find((t) => t.models.some((model) => model.code === m.code) ); return { code: m.code, name: m.name, definition: m.definition, priority: m.priority, transformation: trans?.key ?? "UNKNOWN", }; }); const payload = { total: enriched.length, models: enriched, }; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, } as const; } ); // Tool: Search models server.registerTool( "search_models", { title: "Search Mental Models", description: "Search HUMMBL mental models by keyword across codes, names, and definitions.", inputSchema: z.object({ query: z.string().min(2).describe("Search query (minimum 2 characters)"), }), outputSchema: z.object({ query: z.string(), resultCount: z.number(), results: z.array( z.object({ code: z.string(), name: z.string(), definition: z.string(), priority: z.number(), transformation: z.string(), }) ), }), }, async ({ query }) => { const result = searchModels(query); if (!isOk(result)) { return { content: [ { type: "text", text: `Unable to search models: ${result.error.type}`, }, ], isError: true, } as const; } const enriched = result.value.map((m) => { const trans = Object.values(TRANSFORMATIONS).find((t) => t.models.some((model) => model.code === m.code) ); return { code: m.code, name: m.name, definition: m.definition, priority: m.priority, transformation: trans?.key ?? "UNKNOWN", }; }); const payload = { query, resultCount: enriched.length, results: enriched, }; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, } as const; } ); // Tool: Recommend models based on problem description (USES REST API) server.registerTool( "recommend_models", { title: "Recommend Models for Problem", description: "Get recommended mental models based on a natural language problem description using HUMMBL REST API.", inputSchema: z.object({ problem: z.string().min(10).describe("Detailed description of the problem or challenge"), }), outputSchema: z.object({ problem: z.string(), recommendationCount: z.number(), recommendations: z.array( z.object({ pattern: z.string(), transformations: z.array( z.object({ key: z.string(), name: z.string(), description: z.string(), }) ), topModels: z.array( z.object({ code: z.string(), name: z.string(), definition: z.string(), priority: z.number(), }) ), }) ), }), }, async ({ problem }) => { try { if (!API_CONFIG.apiKey) { return { content: [ { type: "text", text: "HUMMBL API key not configured. Please set HUMMBL_API_KEY environment variable.", }, ], isError: true, structuredContent: undefined, } as const; } const response = await fetch(`${API_CONFIG.baseUrl}/v1/recommend`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_CONFIG.apiKey}`, }, body: JSON.stringify({ problem }), }); if (!response.ok) { const errorText = await response.text(); return { content: [ { type: "text", text: `API request failed: ${response.status} ${response.statusText}\n${errorText}`, }, ], isError: true, structuredContent: undefined, } as const; } const payload = await response.json(); return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload as { problem: string; recommendationCount: number; recommendations: Array<{ pattern: string; transformations: Array<{ key: string; name: string; description: string; }>; topModels: Array<{ code: string; name: string; definition: string; priority: number; }>; }>; }, } as const; } catch (error) { return { content: [ { type: "text", text: `Failed to call HUMMBL API: ${error instanceof Error ? error.message : "Unknown error"}`, }, ], isError: true, structuredContent: undefined, } as const; } } ); // Tool: Get model relationships /* server.registerTool( "get_model_relationships", { title: "Get Model Relationships", description: "Get all relationships involving a specific mental model, showing how it connects to other models.", inputSchema: z.object({ code: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Model code (e.g., DE1, P5, IN2)"), }), outputSchema: z.object({ model: z.string(), relationships: z.array( z.object({ related_model: z.string(), type: z.string(), direction: z.string(), confidence: z.string(), logical_derivation: z.string(), relationship_id: z.string(), }) ), }), }, async ({ code }) => { try { const response = await fetch(`${API_CONFIG.baseUrl}/v1/models/${code.toUpperCase()}/relationships`, { headers: { "Authorization": `Bearer ${API_CONFIG.apiKey}`, }, }); if (!response.ok) { const errorText = await response.text(); return { content: [ { type: "text", text: `API request failed: ${response.status} ${response.statusText}\n${errorText}`, }, ], isError: true, }; } const payload = (await response.json()) as Record<string, unknown>; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, }; } catch (error) { return { content: [ { type: "text", text: `Failed to call HUMMBL API: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } ); */ // Tool: Find relationship path between models /* server.registerTool( "find_relationship_path", { title: "Find Relationship Path", description: "Find reasoning chains and relationships between two models.", inputSchema: z.object({ from: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Starting model code"), to: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Target model code"), max_depth: z.number().min(1).max(5).default(3).describe("Maximum relationship depth"), }), outputSchema: z.object({ from: z.string(), to: z.string(), paths: z.array( z.object({ path: z.array(z.string()), relationships: z.array( z.object({ from: z.string(), to: z.string(), type: z.string(), confidence: z.string(), logical_derivation: z.string(), }) ), confidence_score: z.number(), }) ), total_paths: z.number(), }), }, async ({ from, to, max_depth = 3 }) => { try { // Get relationships for both models const [fromResponse, toResponse] = await Promise.all([ fetch(`${API_CONFIG.baseUrl}/v1/models/${from.toUpperCase()}/relationships`, { headers: { "Authorization": `Bearer ${API_CONFIG.apiKey}` }, }), fetch(`${API_CONFIG.baseUrl}/v1/models/${to.toUpperCase()}/relationships`, { headers: { "Authorization": `Bearer ${API_CONFIG.apiKey}` }, }), ]); if (!fromResponse.ok || !toResponse.ok) { return { content: [ { type: "text", text: "Failed to retrieve relationship data for path finding", }, ], isError: true, } as const; } const fromData = await fromResponse.json() as { relationships?: any[] }; const toData = await toResponse.json() as { relationships?: any[] }; // Simple path finding - look for direct connections and 1-hop paths const paths: any[] = []; const fromRels = fromData.relationships || []; const toRels = toData.relationships || []; // Direct connections const directConnections = fromRels.filter((rel: any) => rel.related_model === to.toUpperCase() ); if (directConnections.length > 0) { paths.push({ path: [from.toUpperCase(), to.toUpperCase()], relationships: directConnections.map((rel: any) => ({ from: from.toUpperCase(), to: rel.related_model, type: rel.type, confidence: rel.confidence, logical_derivation: rel.logical_derivation, })), confidence_score: Math.max(...directConnections.map((r: any) => r.confidence === 'A' ? 3 : r.confidence === 'B' ? 2 : 1 )), }); } // 1-hop paths (common related models) const fromRelated = new Set(fromRels.map((r: any) => r.related_model)); const toRelated = new Set(toRels.map((r: any) => r.related_model)); const commonRelated = [...fromRelated].filter(model => toRelated.has(model)); for (const intermediate of commonRelated.slice(0, 5)) { // Limit to 5 paths const fromToIntermediate = fromRels.find((r: any) => r.related_model === intermediate); const intermediateToTo = toRels.find((r: any) => r.related_model === intermediate); if (fromToIntermediate && intermediateToTo) { paths.push({ path: [from.toUpperCase(), intermediate, to.toUpperCase()], relationships: [ { from: from.toUpperCase(), to: intermediate, type: fromToIntermediate.type, confidence: fromToIntermediate.confidence, logical_derivation: fromToIntermediate.logical_derivation, }, { from: intermediate, to: to.toUpperCase(), type: intermediateToTo.type, confidence: intermediateToTo.confidence, logical_derivation: intermediateToTo.logical_derivation, } ], confidence_score: Math.min( fromToIntermediate.confidence === 'A' ? 3 : fromToIntermediate.confidence === 'B' ? 2 : 1, intermediateToTo.confidence === 'A' ? 3 : intermediateToTo.confidence === 'B' ? 2 : 1 ), }); } } const payload = { from: from.toUpperCase(), to: to.toUpperCase(), paths: paths.slice(0, 10), // Limit to top 10 paths total_paths: paths.length, }; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, } as const; } catch (error) { return { content: [ { type: "text", text: `Failed to find relationship paths: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, } as const; } } ); */ // Tool: Get relationship neighborhood /* server.registerTool( "get_relationship_neighborhood", { title: "Get Relationship Neighborhood", description: "Get models within N relationships of a given model to understand its context in the knowledge graph.", inputSchema: z.object({ code: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Center model code"), depth: z.number().min(1).max(3).default(2).describe("Relationship depth"), confidence_min: z.enum(["C", "B", "A"]).default("C").describe("Minimum confidence level"), }), outputSchema: z.object({ center_model: z.string(), neighborhood: z.array( z.object({ model: z.string(), distance: z.number(), paths: z.array( z.object({ path: z.array(z.string()), relationship: z.object({ type: z.string(), confidence: z.string(), logical_derivation: z.string(), }), }) ), }) ), total_models: z.number(), depth: z.number(), }), }, async ({ code, depth = 2, confidence_min = "C" }) => { try { const response = await fetch( `${API_CONFIG.baseUrl}/v1/relationships?model=${code.toUpperCase()}&confidence=${confidence_min}&status=confirmed`, { headers: { "Authorization": `Bearer ${API_CONFIG.apiKey}` }, } ); if (!response.ok) { return { content: [ { type: "text", text: `API request failed: ${response.status} ${response.statusText}`, }, ], isError: true, } as const; } const data = await response.json() as { relationships?: any[] }; const relationships = data.relationships || []; // Build neighborhood const visited = new Set<string>([code.toUpperCase()]); const neighborhood: any[] = []; // Direct connections (depth 1) const directConnections = new Map<string, any[]>(); relationships.forEach((rel: any) => { const otherModel = rel.model_a === code.toUpperCase() ? rel.model_b : rel.model_a; if (!directConnections.has(otherModel)) { directConnections.set(otherModel, []); } directConnections.get(otherModel)!.push({ path: [code.toUpperCase(), otherModel], relationship: { type: rel.relationship_type, confidence: rel.confidence, logical_derivation: rel.logical_derivation, }, }); }); // Add direct connections to neighborhood for (const [model, paths] of directConnections) { neighborhood.push({ model, distance: 1, paths, }); visited.add(model); } // For depth > 1, we would need more complex graph traversal // For now, just return direct connections const payload = { center_model: code.toUpperCase(), neighborhood, total_models: neighborhood.length, depth, }; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, } as const; } catch (error) { return { content: [ { type: "text", text: `Failed to get relationship neighborhood: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, } as const; } } ); */ // Tool: Get related models server.registerTool( "get_related_models", { title: "Get Related Mental Models", description: "Get all models related to a specific model with relationship details", inputSchema: z.object({ code: z.string().describe("Model code (e.g., P1, DE7)"), }), }, async ({ code }) => { try { const response = await fetch( `${API_CONFIG.baseUrl}/v1/models/${code.toUpperCase()}/relationships`, { headers: { Authorization: `Bearer ${API_CONFIG.apiKey}`, }, } ); if (!response.ok) { return { content: [ { type: "text", text: `API request failed: ${response.status} ${response.statusText}`, }, ], isError: true, }; } const payload = (await response.json()) as Record<string, unknown>; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, }; } catch (error) { return { content: [ { type: "text", text: `Failed to get related models: ${error instanceof Error ? error.message : "Unknown error"}`, }, ], isError: true, }; } } ); // Tool: Find relationship path (BFS) /* server.registerTool( "find_relationship_path", { title: "Find Relationship Path", description: "Find the shortest path of relationships between two models", inputSchema: z.object({ from: z.string().describe("Source model code"), to: z.string().describe("Target model code"), maxDepth: z.number().optional().default(5) }) }, async ({ from, to, maxDepth = 5 }) => { try { // Get all relationships const response = await fetch(`${API_CONFIG.baseUrl}/v1/relationships?limit=1000`, { headers: { "Authorization": `Bearer ${API_CONFIG.apiKey}`, }, }); if (!response.ok) { return { content: [ { type: "text", text: `API request failed: ${response.status} ${response.statusText}`, }, ], isError: true, }; } const data = await response.json(); const relationships = data.relationships || []; // Build adjacency list const graph: Record<string, Array<{ target: string; type: string; confidence: string }>> = {}; relationships.forEach((rel: any) => { const source = rel.model_a; const target = rel.model_b; const type = rel.relationship_type; const confidence = rel.confidence; if (!graph[source]) graph[source] = []; if (!graph[target]) graph[target] = []; graph[source].push({ target, type, confidence }); // Add reverse for bidirectional search graph[target].push({ target: source, type: `${type} (reverse)`, confidence }); }); // BFS to find shortest path const queue: Array<{ node: string; path: string[]; relationships: Array<{ from: string; to: string; type: string; confidence: string }>; depth: number; }> = [{ node: from.toUpperCase(), path: [from.toUpperCase()], relationships: [], depth: 0 }]; const visited = new Set<string>(); visited.add(from.toUpperCase()); while (queue.length > 0) { const current = queue.shift()!; if (current.depth > maxDepth) continue; if (current.node === to.toUpperCase()) { // Found path const payload = { from: from.toUpperCase(), to: to.toUpperCase(), path: current.path, relationships: current.relationships, path_length: current.path.length - 1, confidence_score: Math.min(...current.relationships.map(r => r.confidence === 'A' ? 3 : r.confidence === 'B' ? 2 : 1 )), }; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, }; } if (graph[current.node]) { for (const neighbor of graph[current.node]) { if (!visited.has(neighbor.target)) { visited.add(neighbor.target); queue.push({ node: neighbor.target, path: [...current.path, neighbor.target], relationships: [...current.relationships, { from: current.node, to: neighbor.target, type: neighbor.type, confidence: neighbor.confidence, }], depth: current.depth + 1, }); } } } } // No path found return { content: [ { type: "text", text: `No relationship path found between ${from} and ${to} within ${maxDepth} steps`, }, ], structuredContent: { from: from.toUpperCase(), to: to.toUpperCase(), path: null, relationships: [], message: "No path found", }, }; } catch (error) { return { content: [ { type: "text", text: `Failed to find relationship path: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } } ); */ // Tool: Add relationship server.registerTool( "add_relationship", { title: "Add Model Relationship", description: "Add a relationship between two mental models with evidence.", inputSchema: z.object({ source_code: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Source model code"), target_code: z .string() .regex(/^(P|IN|CO|DE|RE|SY)\d{1,2}$/i) .describe("Target model code"), relationship_type: z .string() .describe("Type of relationship (e.g., 'enables', 'reinforces')"), confidence: z .enum(["A", "B", "C"]) .describe("Confidence level: A=High, B=Moderate, C=Hypothesis"), evidence: z.string().optional().describe("Evidence supporting this relationship"), }), outputSchema: z.object({ success: z.boolean(), relationship: z.object({ source_code: z.string(), target_code: z.string(), relationship_type: z.string(), confidence: z.string(), evidence: z.string().optional(), }), }), }, async ({ source_code, target_code, relationship_type, confidence, evidence }) => { try { const response = await fetch(`${API_CONFIG.baseUrl}/v1/relationships`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_CONFIG.apiKey}`, }, body: JSON.stringify({ source_code, target_code, relationship_type, confidence, evidence, }), }); if (!response.ok) { const errorText = await response.text(); return { content: [ { type: "text", text: `API request failed: ${response.status} ${response.statusText}\n${errorText}`, }, ], isError: true, }; } const payload = (await response.json()) as Record<string, unknown>; return { content: [ { type: "text", text: JSON.stringify(payload, null, 2), }, ], structuredContent: payload, }; } catch (error) { return { content: [ { type: "text", text: `Failed to add relationship: ${error instanceof Error ? error.message : "Unknown error"}`, }, ], isError: true, }; } } ); }

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/hummbl-dev/mcp-server'

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