Skip to main content
Glama
mental-models.test.ts8.71 kB
/** * Behavior tests for mental_models toolhost * * Run with: npx tsx tests/mental-models.test.ts */ import { MentalModelsServer, MENTAL_MODELS_TOOL } from "../src/mental-models/index.js"; import { getModelNames, getTagNames, MENTAL_MODELS, TAG_DEFINITIONS } from "../src/mental-models/operations.js"; const server = new MentalModelsServer(); async function test(name: string, fn: () => Promise<void> | void) { try { await fn(); console.log(`✅ ${name}`); } catch (error) { console.error(`❌ ${name}`); console.error(` ${error instanceof Error ? error.message : error}`); process.exitCode = 1; } } function assert(condition: boolean, message: string) { if (!condition) throw new Error(message); } async function runTests() { console.log("\n🧪 Mental Models Toolhost Tests\n"); // Tool definition tests await test("Tool has correct name", () => { assert(MENTAL_MODELS_TOOL.name === "mental_models", "Expected name 'mental_models'"); }); await test("Tool description includes model count", () => { assert( MENTAL_MODELS_TOOL.description?.includes("15 mental models"), "Description should mention 15 models" ); }); await test("Tool description includes all tags", () => { const desc = MENTAL_MODELS_TOOL.description || ""; for (const tag of getTagNames()) { assert(desc.includes(tag), `Description missing tag: ${tag}`); } }); await test("Tool schema has dynamic model enum", () => { const schema = MENTAL_MODELS_TOOL.inputSchema as any; const modelEnum = schema.properties.args.properties.model.enum; assert(Array.isArray(modelEnum), "Model enum should be array"); assert(modelEnum.length === 15, `Expected 15 models, got ${modelEnum.length}`); assert(modelEnum.includes("five-whys"), "Should include five-whys"); }); await test("Tool schema has dynamic tag enum", () => { const schema = MENTAL_MODELS_TOOL.inputSchema as any; const tagEnum = schema.properties.args.properties.tag.enum; assert(Array.isArray(tagEnum), "Tag enum should be array"); assert(tagEnum.length === 9, `Expected 9 tags, got ${tagEnum.length}`); assert(tagEnum.includes("debugging"), "Should include debugging"); }); // list_tags operation await test("list_tags returns all tags", async () => { const result = await server.processTool("list_tags", {}); const response = JSON.parse(result.content[0].text); assert(response.count === 9, `Expected 9 tags, got ${response.count}`); assert(response.tags.length === 9, "Tags array should have 9 items"); }); await test("list_tags includes descriptions", async () => { const result = await server.processTool("list_tags", {}); const response = JSON.parse(result.content[0].text); const debugTag = response.tags.find((t: any) => t.name === "debugging"); assert(debugTag, "Should have debugging tag"); assert(debugTag.description.length > 10, "Description should be meaningful"); }); // list_models operation await test("list_models returns all models without filter", async () => { const result = await server.processTool("list_models", {}); const response = JSON.parse(result.content[0].text); assert(response.count === 15, `Expected 15 models, got ${response.count}`); assert(!response.filter, "Should not have filter when unfiltered"); }); await test("list_models filters by tag", async () => { const result = await server.processTool("list_models", { tag: "debugging" }); const response = JSON.parse(result.content[0].text); assert(response.count === 2, `Expected 2 debugging models, got ${response.count}`); assert(response.filter === "debugging", "Should show filter"); // All returned models should have debugging tag for (const model of response.models) { assert(model.tags.includes("debugging"), `${model.name} should have debugging tag`); } }); await test("list_models rejects invalid tag", async () => { const result = await server.processTool("list_models", { tag: "invalid-tag" }); assert(result.isError === true, "Should return error"); const response = JSON.parse(result.content[0].text); assert(response.error.includes("Unknown tag"), "Should mention unknown tag"); }); // get_model operation await test("get_model returns full content", async () => { const result = await server.processTool("get_model", { model: "five-whys" }); const response = JSON.parse(result.content[0].text); assert(response.name === "five-whys", "Name should match"); assert(response.title === "Five Whys", "Title should match"); assert(response.tags.includes("debugging"), "Should have debugging tag"); assert(response.content.includes("# Five Whys"), "Content should have heading"); assert(response.content.includes("## Process"), "Content should have Process section"); assert(response.content.length > 1000, "Content should be substantial"); }); await test("get_model requires model name", async () => { const result = await server.processTool("get_model", {}); assert(result.isError === true, "Should return error"); const response = JSON.parse(result.content[0].text); assert(response.error.includes("required"), "Should mention required"); }); await test("get_model rejects invalid model", async () => { const result = await server.processTool("get_model", { model: "invalid-model" }); assert(result.isError === true, "Should return error"); const response = JSON.parse(result.content[0].text); assert(response.error.includes("not found"), "Should mention not found"); }); // get_capability_graph operation await test("get_capability_graph returns entities and relations", async () => { const result = await server.processTool("get_capability_graph", {}); const response = JSON.parse(result.content[0].text); assert(Array.isArray(response.entities), "Should have entities array"); assert(Array.isArray(response.relations), "Should have relations array"); assert(response.entities.length > 20, "Should have many entities"); assert(response.relations.length > 20, "Should have many relations"); }); await test("get_capability_graph includes all models as entities", async () => { const result = await server.processTool("get_capability_graph", {}); const response = JSON.parse(result.content[0].text); const modelEntities = response.entities.filter((e: any) => e.entityType === "mental_model"); assert(modelEntities.length === 15, `Expected 15 model entities, got ${modelEntities.length}`); }); await test("get_capability_graph includes usage instructions", async () => { const result = await server.processTool("get_capability_graph", {}); const response = JSON.parse(result.content[0].text); assert(response.usage, "Should have usage instructions"); assert(response.usage.step1.includes("memory_create_entities"), "Should reference memory tools"); }); // Content quality tests await test("All models have required content sections", async () => { for (const modelName of getModelNames()) { const result = await server.processTool("get_model", { model: modelName }); const response = JSON.parse(result.content[0].text); const content = response.content; assert(content.includes("# "), `${modelName}: Missing title heading`); assert(content.includes("## When to Use"), `${modelName}: Missing When to Use section`); assert(content.includes("## Process"), `${modelName}: Missing Process section`); assert(content.includes("## "), `${modelName}: Should have multiple sections`); } }); await test("All models have meaningful descriptions", () => { for (const model of MENTAL_MODELS) { assert(model.description.length > 20, `${model.name}: Description too short`); assert(model.description.length < 200, `${model.name}: Description too long`); } }); await test("All tags are used by at least one model", () => { for (const tag of TAG_DEFINITIONS) { const modelsWithTag = MENTAL_MODELS.filter(m => m.tags.includes(tag.name)); assert(modelsWithTag.length > 0, `Tag ${tag.name} has no models`); } }); // Error handling await test("Unknown operation returns error with available operations", async () => { const result = await server.processTool("invalid_operation", {}); assert(result.isError === true, "Should return error"); const response = JSON.parse(result.content[0].text); assert(response.availableOperations, "Should list available operations"); }); console.log("\n✨ All tests completed\n"); } runTests().catch(console.error);

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/glassBead-tc/Thoughtbox'

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