Skip to main content
Glama

DollhouseMCP

by DollhouseMCP
CAPABILITY_INDEX_RESOURCE_ANALYSIS.md26.4 kB
# CapabilityIndexResource Implementation Analysis **Date**: October 16, 2025 **Branch**: feature/capability-index-mcp-resources **Status**: Implementation complete, not yet integrated into server --- ## Executive Summary The `CapabilityIndexResource` class implements MCP (Model Context Protocol) Resources to expose DollhouseMCP's capability index as injectable LLM context. This implementation is unique in the codebase - it's the **first and only MCP Resources implementation**, whereas the rest of the server focuses on MCP Tools. **Key Distinction**: - **Tools** (47 in codebase): Require explicit LLM invocation, consume tokens only when called - **Resources** (this implementation): Designed for automatic context injection, consume tokens continuously when enabled --- ## Critical Implementation Details ### Location - **TypeScript Source**: `/Users/mick/Developer/Organizations/DollhouseMCP/active/mcp-server/src/server/resources/CapabilityIndexResource.ts` - **Compiled Output**: `/Users/mick/Developer/Organizations/DollhouseMCP/active/mcp-server/dist/server/resources/CapabilityIndexResource.js` ### Current Status **DORMANT** - Code exists but is not integrated: - ❌ Not imported in ServerSetup.ts - ❌ No resource handlers registered - ❌ Resources capability not advertised to clients - ❌ No configuration system implemented yet ### Token Impact (Measured, Not Estimated) | Variant | Actual Tokens | Context % (200K) | Context % (1M) | |---------|---------------|------------------|----------------| | Summary | 1,254 | 0.63% | 0.125% | | Full | 48,306 | 24.15% | 4.83% | | Stats | ~50 | <0.01% | <0.01% | **Original estimates were too high**: Summary is 1,254 tokens, not 2,500-3,500 as estimated. --- ## How CapabilityIndexResource Differs from Tools ### 1. Protocol Layer: Resources vs Tools **MCP Tools Implementation** (existing, 47 tools): ```typescript // In ServerSetup.ts server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = this.toolRegistry.getAllTools(); return { tools }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = this.toolRegistry.getHandler(name); return await handler(args); }); ``` **MCP Resources Implementation** (CapabilityIndexResource): ```typescript // Would need to be added to ServerSetup.ts server.setRequestHandler(ListResourcesRequestSchema, async () => { const resourceHandler = new CapabilityIndexResource(); return await resourceHandler.listResources(); }); server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; const resourceHandler = new CapabilityIndexResource(); return await resourceHandler.readResource(uri); }); ``` **Key Difference**: - Tools: Pull model (LLM requests → server responds) - Resources: Push model (server advertises → client decides what to inject) ### 2. Capability Advertisement **Tools** (from index.ts): ```typescript const server = new Server({ name: 'dollhousemcp-capability-index', version: '1.9.16' }, { capabilities: { tools: {} // Advertises tool support } }); ``` **Resources** (would need to be added): ```typescript const server = new Server({ name: 'dollhousemcp-capability-index', version: '1.9.16' }, { capabilities: { tools: {}, resources: {} // Would advertise resource support } }); ``` ### 3. Registration Pattern **Tools Pattern**: ```typescript // ToolRegistry maintains a Map<string, ToolHandler> export class ToolRegistry { private tools: Map<string, ToolHandler> = new Map(); register(tool: ToolDefinition, handler: ToolHandler): void { this.tools.set(tool.name, handler); } } ``` **Resources Pattern** (no registry, direct instantiation): ```typescript // No ResourceRegistry - handlers create instances directly export class CapabilityIndexResource { async listResources(): Promise<MCPResourceListResponse> { ... } async readResource(uri: string): Promise<MCPResourceReadResponse> { ... } } ``` **Why Different**: - Tools: Many handlers (47), need registry for management - Resources: Single handler (for now), instantiated per request ### 4. URI Scheme **Tools** (identified by name): ```typescript { name: "list_elements", description: "...", inputSchema: { ... } } ``` **Resources** (identified by URI): ```typescript { uri: "dollhouse://capability-index/summary", name: "Capability Index Summary", description: "...", mimeType: "text/yaml" } ``` **Custom URI Scheme**: `dollhouse://capability-index/{variant}` - `summary` - Lightweight index (1,254 tokens) - `full` - Complete index (48,306 tokens) - `stats` - Metrics (JSON, ~50 tokens) ### 5. Caching Strategy **Tools** (cache discovery only): ```typescript // ServerSetup.ts - caches tool list, not execution results server.setRequestHandler(ListToolsRequestSchema, async () => { let tools = this.toolCache.getToolList(); if (!tools) { tools = this.toolRegistry.getAllTools(); this.toolCache.setToolList(tools); } return { tools }; }); ``` **Resources** (cache file content): ```typescript // CapabilityIndexResource.ts - caches parsed YAML private cachedIndex: CapabilityIndex | null = null; private cacheTimestamp: number = 0; private readonly CACHE_TTL = 60000; // 60 seconds async loadCapabilityIndex() { const now = Date.now(); if (this.cachedIndex && (now - this.cacheTimestamp) < this.CACHE_TTL) { return this.cachedIndex; // Return cached } // Otherwise, read and parse file } ``` **Why Different**: - Tools: Execution varies by arguments, can't cache results - Resources: Content changes infrequently, benefits from caching --- ## Three Resource Variants ### 1. Summary (`dollhouse://capability-index/summary`) **Content**: Metadata + action_triggers only ```yaml # Capability Index Summary # This is a lightweight summary of the capability index for LLM context injection. # Contains action verb → element mappings for quick tool selection guidance. # Full index available at: dollhouse://capability-index/full # Total elements: 42 metadata: version: "1.0.0" created: "2025-10-15" last_updated: "2025-10-16" total_elements: 42 action_triggers: analyze: [analyzer-persona, code-reviewer] create: [creative-writer, code-generator] debug: [debugger-persona, troubleshooter] # ... more verb mappings ``` **Use Case**: Automatic injection for models with 200K+ context **Token Cost**: 1,254 tokens (0.6% of 200K context) **Benefit**: Helps LLM select appropriate tools based on action verbs ### 2. Full (`dollhouse://capability-index/full`) **Content**: Complete index with all element details ```yaml # Capability Index (Full) # Complete capability index including all element details, relationships, and semantic data. # This is a large resource (~35-45K tokens) - use only with large context models. # Summary version available at: dollhouse://capability-index/summary # Total elements: 42 metadata: { ... } action_triggers: { ... } elements: personas: - name: "Creative Writer" description: "..." triggers: [...] relationships: [...] semantic_data: { ... } skills: { ... } # ... complete element data ``` **Use Case**: Large context models (500K-4M tokens) **Token Cost**: 48,306 tokens (24% of 200K, 5% of 1M, 1.2% of 4M) **Benefit**: Complete context about all available elements and relationships ### 3. Stats (`dollhouse://capability-index/stats`) **Content**: JSON metrics ```json { "summarySize": 6675, "summaryWords": 646, "summaryLines": 265, "fullSize": 280832, "fullWords": 20311, "fullLines": 9363, "estimatedSummaryTokens": 1254, "estimatedFullTokens": 48306 } ``` **Use Case**: Debugging, monitoring, decision-making **Token Cost**: ~50 tokens **Benefit**: Helps LLM decide which variant to request --- ## MCP Resources Protocol Implementation ### Standard MCP Resources Interface ```typescript // From @modelcontextprotocol/sdk interface Resource { uri: string; // Unique identifier (custom scheme allowed) name: string; // Human-readable name description: string; // What this resource contains mimeType: string; // Content type (text/yaml, application/json, etc) } interface ListResourcesResponse { resources: Resource[]; } interface ResourceContent { uri: string; // Must match requested URI mimeType: string; // Content type text: string; // Actual content } interface ReadResourceResponse { contents: ResourceContent[]; } ``` ### CapabilityIndexResource Implementation ```typescript export class CapabilityIndexResource { // List all available resources (called during client connection) async listResources(): Promise<MCPResourceListResponse> { return { resources: [ { uri: 'dollhouse://capability-index/summary', name: 'Capability Index Summary', description: 'Lightweight capability index with action verb → element mappings. Estimated ~2.5-3.5K tokens. Recommended for models with 200K+ context.', mimeType: 'text/yaml' }, { uri: 'dollhouse://capability-index/full', name: 'Capability Index (Full)', description: 'Complete capability index with all element details, relationships, and semantic data. Estimated ~35-45K tokens. Recommended for models with 500K+ context.', mimeType: 'text/yaml' }, { uri: 'dollhouse://capability-index/stats', name: 'Capability Index Statistics', description: 'Measurement data about capability index size and token estimates.', mimeType: 'application/json' } ] }; } // Read a specific resource (called when client/LLM requests content) async readResource(uri: string): Promise<MCPResourceReadResponse> { let content: string; let mimeType: string; switch (uri) { case 'dollhouse://capability-index/summary': content = await this.generateSummary(); mimeType = 'text/yaml'; break; case 'dollhouse://capability-index/full': content = await this.generateFull(); mimeType = 'text/yaml'; break; case 'dollhouse://capability-index/stats': const stats = await this.getStatistics(); content = JSON.stringify(stats, null, 2); mimeType = 'application/json'; break; default: throw new Error(`Unknown capability index resource: ${uri}`); } return { contents: [ { uri, mimeType, text: content } ] }; } } ``` **Protocol Compliance**: Verified via logs from October 3, 2025 testing: ``` Message from server: {"capabilities":{"tools":{},"resources":{}}} Message from client: {"method":"resources/list","params":{}} Message from server: {"result":{"resources":[...]}} # All 3 resources returned ``` --- ## Token Cost Implications ### Comparison with Existing Tool Overhead **Current DollhouseMCP Token Usage** (measured October 15, 2025): - Base Claude Code: 36,695 tokens - Claude Code + DollhouseMCP: ~60,000 tokens - **DollhouseMCP tool overhead**: ~24,000 tokens (47 tools) **If Resources Were Enabled**: | Configuration | Additional Tokens | Total Overhead | % of 200K Context | |---------------|-------------------|----------------|-------------------| | None (default) | 0 | 24,000 | 12% | | Summary only | 1,254 | 25,254 | 12.6% | | Full only | 48,306 | 72,306 | 36.2% | | Both (naive) | 49,560 | 73,560 | 36.8% | **Context Window Economics**: - 200K models: Summary viable (0.6%), Full heavy (24%) - 500K models: Summary negligible, Full acceptable (9.7%) - 1M models: Both lightweight (Summary 0.1%, Full 4.8%) - 4M models (Gemini): Both essentially free (Summary 0.03%, Full 1.2%) ### Risk Assessment **Current Risk**: ZERO - Resources code exists but dormant (not hooked up) - Even if hooked up, MCP clients don't support `resources/read` yet **Future Risk**: HIGH (if naively implemented) - Client updates to support resources - Naively auto-injects ALL discovered resources - Users experience 49K token overhead without opting in **Mitigation Strategy**: Configuration-controlled resource advertising (see Integration Requirements below) --- ## Client Support Status (as of October 2025) ### Tested Clients **Claude Desktop** (tested October 3, 2025): - ✅ Calls `resources/list` successfully - ❌ Never calls `resources/read` - ❌ No UI to browse or enable resources - **Status**: Resources discovered but not used **Claude Code 2.0.5** (tested October 3, 2025): - ✅ Calls `resources/list` successfully - ❌ Never calls `resources/read` - ❌ Manually calls multiple tools instead - **Status**: Resources discovered but not used **Gemini CLI** (GitHub Issue #3816, filed July 2025): - ❌ Resources not supported - ✅ Only tools supported - **Status**: "Acknowledged as feature request, no timeline" ### Current MCP Ecosystem State **Phase 1: Tools** (Mature) - Widely supported by all MCP clients - Standard implementation pattern - Proven at scale **Phase 2: Resources** (Specification exists, adoption lagging) - Servers implementing (including this one) - Clients discovering but not using - Gap between protocol support and client implementation **Phase 3: Prompts** (Similar state to Resources) - Part of MCP spec - Not yet adopted by clients **Pattern Observed**: ``` Client connects → Server advertises resources Client calls resources/list → Server returns resources [CLIENTS STOP HERE] Client should call resources/read → NEVER HAPPENS Client should inject into context → NEVER HAPPENS ``` **Why This Matters**: This implementation is **future-proof** but not immediately useful until clients add support. --- ## Integration Requirements ### 1. Server Capability Advertisement **File**: `src/index.ts` **Current**: ```typescript const server = new Server({ name: 'dollhousemcp-capability-index', version: VERSION }, { capabilities: { tools: {} } }); ``` **Needs**: ```typescript const server = new Server({ name: 'dollhousemcp-capability-index', version: VERSION }, { capabilities: { tools: {}, resources: {} // Add this conditionally } }); ``` **Conditional Logic** (based on configuration): ```typescript import { ConfigManager } from './config/ConfigManager.js'; const config = ConfigManager.getInstance().getConfig(); const capabilities: any = { tools: {} }; // Only advertise resources if explicitly enabled if (config.resources?.advertise_resources) { capabilities.resources = {}; } const server = new Server( { name: 'dollhousemcp-capability-index', version: VERSION }, { capabilities } ); ``` ### 2. Request Handler Registration **File**: `src/server/ServerSetup.ts` **Add after tool handlers**: ```typescript import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { CapabilityIndexResource } from './resources/CapabilityIndexResource.js'; import { ConfigManager } from '../config/ConfigManager.js'; export class ServerSetup { // ... existing code ... setupServer(server: Server, instance: IToolHandler): void { // Register all tools this.registerTools(instance); // Setup request handlers this.setupListToolsHandler(server); this.setupCallToolHandler(server); // Setup resource handlers (conditionally) this.setupResourceHandlers(server); } /** * Setup MCP resource handlers (conditional on configuration) */ private setupResourceHandlers(server: Server): void { const config = ConfigManager.getInstance().getConfig(); // Only register handlers if resources are enabled if (!config.resources?.advertise_resources) { logger.debug('MCP resources disabled by configuration'); return; } const resourceHandler = new CapabilityIndexResource(); // List available resources server.setRequestHandler(ListResourcesRequestSchema, async () => { return await resourceHandler.listResources(); }); // Read specific resource server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; return await resourceHandler.readResource(uri); }); logger.info('MCP resource handlers registered'); } } ``` ### 3. Configuration Schema **File**: `src/config/ConfigManager.ts` **Already exists** (from October 15 investigation): ```typescript export interface CapabilityIndexResourcesConfig { advertise_resources: boolean; // Default: false - safe, don't advertise variants?: { summary?: boolean; // Enable summary variant full?: boolean; // Enable full variant }; } export interface DollhouseConfig { // ... other config ... resources?: CapabilityIndexResourcesConfig; } // Default configuration const DEFAULT_CONFIG: DollhouseConfig = { // ... other defaults ... resources: { advertise_resources: false, // Default: safe, disabled variants: { summary: false, full: false } } }; ``` **Enhancement Needed**: Conditional resource list based on enabled variants ```typescript // In CapabilityIndexResource.ts async listResources(): Promise<MCPResourceListResponse> { const config = ConfigManager.getInstance().getConfig(); const resources: MCPResource[] = []; // Always include stats (minimal overhead) resources.push({ uri: 'dollhouse://capability-index/stats', name: 'Capability Index Statistics', description: 'Measurement data about capability index size and token estimates.', mimeType: 'application/json' }); // Conditionally add summary if (config.resources?.variants?.summary !== false) { resources.push({ uri: 'dollhouse://capability-index/summary', name: 'Capability Index Summary', description: 'Lightweight capability index with action verb → element mappings. Measured 1,254 tokens. Recommended for models with 200K+ context.', mimeType: 'text/yaml' }); } // Conditionally add full if (config.resources?.variants?.full) { resources.push({ uri: 'dollhouse://capability-index/full', name: 'Capability Index (Full)', description: 'Complete capability index with all element details, relationships, and semantic data. Measured 48,306 tokens. Recommended for models with 500K+ context.', mimeType: 'text/yaml' }); } return { resources }; } ``` ### 4. Testing Requirements **Unit Tests** (`test/__tests__/unit/server/resources/CapabilityIndexResource.test.ts`): ```typescript describe('CapabilityIndexResource', () => { it('should list resources based on configuration'); it('should cache capability index for 60 seconds'); it('should generate summary with correct structure'); it('should generate full index with correct structure'); it('should generate statistics with accurate measurements'); it('should handle missing capability-index.yaml gracefully'); it('should throw error for unknown resource URI'); }); ``` **Integration Tests** (`test/__tests__/integration/resources/CapabilityIndexResource.test.ts`): ```typescript describe('CapabilityIndexResource Integration', () => { it('should not advertise resources when disabled'); it('should advertise resources when enabled'); it('should respect variant configuration'); it('should handle resource read requests'); it('should return proper MCP protocol responses'); }); ``` **Protocol Compliance Tests**: ```typescript describe('MCP Resources Protocol Compliance', () => { it('should implement ListResourcesRequestSchema correctly'); it('should implement ReadResourceRequestSchema correctly'); it('should return valid resource URIs'); it('should return valid MIME types'); it('should handle errors per MCP spec'); }); ``` ### 5. Documentation Requirements **Configuration Guide** (`docs/CONFIGURATION.md`): ```markdown ### Capability Index Resources Control whether DollhouseMCP advertises its capability index as MCP resources. **Default**: Disabled (safe, no token overhead) **Configuration**: ```json { "resources": { "advertise_resources": false, // Master switch "variants": { "summary": false, // 1,254 token summary "full": false // 48,306 token full index } } } ``` **Use Cases**: - **Summary**: Enable for 200K+ context models to improve tool selection - **Full**: Enable for 500K+ context models (or 1M+ for negligible overhead) - **Both disabled**: Default, safe for all contexts **Token Impact**: See token cost tables in CAPABILITY_INDEX_RESOURCE_ANALYSIS.md ``` **User Guide** (`docs/guides/MCP_RESOURCES_GUIDE.md`): - What are MCP resources vs tools - When to enable capability index resources - How to configure based on model context size - How to verify resources are working - Client support status --- ## Alternative Approaches (Historical Context) From October 3, 2025 session notes, four alternatives were considered: ### Option A: Tool-Based Injection Create a tool that returns the capability index: ```typescript { name: "get_capability_index", description: "Returns the complete capability index for context", handler: async () => { const resource = new CapabilityIndexResource(); return await resource.generateFull(); } } ``` **Pros**: Works with all current clients today **Cons**: Requires explicit tool call, not automatic **Status**: Could be implemented alongside resources ### Option B: Memory-Based Injection Create a DollhouseMCP memory containing the capability index summary: - Mark as "always active" or "auto-load" - Point to it in CLAUDE.md for automatic loading **Pros**: Works today, integrates with existing memory system **Cons**: Another memory to manage, may not be as discoverable **Status**: Could be implemented alongside resources ### Option C: Enhanced Tool Descriptions Implement Serena-style guidance in tool descriptions: - Add "when to use" / "when NOT to use" sections - Include implicit scenario handling - ~10K token overhead **Pros**: Works with all clients, improves tool selection directly **Cons**: Requires updating all 47 tool descriptions **Status**: Separate initiative ### Option D: Wait for Client Support (Chosen) Keep the resource implementation as-is and wait for clients to catch up. **Pros**: Future-proof, already implemented **Cons**: Unknown timeline for client support **Status**: **CURRENT APPROACH** with configuration safety --- ## Security Considerations ### Input Validation ```typescript async readResource(uri: string): Promise<MCPResourceReadResponse> { // Validate URI matches known resources switch (uri) { case 'dollhouse://capability-index/summary': case 'dollhouse://capability-index/full': case 'dollhouse://capability-index/stats': // Valid URIs break; default: throw new Error(`Unknown capability index resource: ${uri}`); } } ``` **No user input in resource generation**: - File path is hardcoded: `~/.dollhouse/portfolio/capability-index.yaml` - No user-controlled paths or parameters - YAML parsing uses `js-yaml` (already in use throughout codebase) ### DoS Protection **60-second cache** prevents excessive file reads: ```typescript private cachedIndex: CapabilityIndex | null = null; private cacheTimestamp: number = 0; private readonly CACHE_TTL = 60000; // 60 seconds ``` **Why this matters**: Malicious client could repeatedly call `resources/read`, but cache limits filesystem impact. ### Configuration Protection **Default to disabled**: ```typescript resources: { advertise_resources: false, // Explicit opt-in required variants: { summary: false, full: false } } ``` **Why this matters**: Prevents accidental token consumption if clients update to support resources. --- ## Recommendations ### 1. Immediate: Complete Integration - Add conditional resource handlers to ServerSetup.ts - Add conditional capability advertisement to index.ts - Respect existing configuration schema (already in ConfigManager) - Write comprehensive tests ### 2. Short-term: Alternative Approaches - Implement Option A (tool-based) as fallback for current clients - Document both approaches in user guides - Let users choose based on their needs ### 3. Long-term: Monitor Client Adoption - Track MCP client updates for resource support - Update documentation when clients add support - Consider submitting feedback to Claude/Gemini teams ### 4. Documentation - Complete configuration guide - Write MCP resources user guide - Update README with resource information - Document token costs clearly --- ## Related Session Notes - **October 3, 2025**: `SESSION_NOTES_2025-10-03-CAPABILITY-INDEX-RESOURCE-EXPERIMENT.md` - Initial implementation and testing - Token measurements - Client support investigation - **October 15, 2025**: `SESSION_NOTES_2025-10-15-EVENING-CAPABILITY-INDEX-RESOURCES-INVESTIGATION.md` - Discovery that code was never committed - Configuration design - Risk assessment --- ## Conclusion The CapabilityIndexResource implementation is **architecturally sound** and **protocol-compliant**, but represents a significant departure from the rest of the DollhouseMCP codebase: **What Makes It Unique**: 1. First and only MCP Resources implementation (vs 47 Tools) 2. Content caching (60s TTL) vs tool discovery caching 3. Custom URI scheme (`dollhouse://`) vs name-based identification 4. Multiple content variants (3) from single handler 5. Designed for automatic context injection vs explicit invocation **Integration Requirements**: 1. Conditional handler registration based on configuration 2. Conditional capability advertisement 3. Variant-aware resource listing 4. Comprehensive testing 5. Clear documentation about token costs **Current Status**: - Implementation complete ✅ - Configuration schema exists ✅ - Integration pending ⏸️ - Client support pending ⏸️ **Recommendation**: Complete integration with **strict configuration controls** to prevent accidental token overhead, then wait for client support while offering alternative approaches (tool-based, memory-based) for immediate use. --- **Document Version**: 1.0 **Last Updated**: October 16, 2025 **Author**: Claude (Sonnet 4.5) via DollhouseMCP Analysis

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/DollhouseMCP/DollhouseMCP'

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