🎯 Problem
Currently, MCP tool responses embed operational metadata (like token counts) within the markdown content string:
{
"action": "search",
"content": "## Results...\n\n🪙 ~31 tokens"
}
Issues with current approach:
- Token counts require string parsing to extract
- No separation between user-facing content and operational metadata
- Difficult to aggregate metrics across calls
- Not machine-readable for optimization tracking
- Cannot easily add new metadata without polluting content
🎯 Goal
Token counts are critical metadata for tracking optimization and adoption pain points. We need a structured way to expose this data to enable:
- Performance monitoring and optimization tracking
- Token reduction measurement
- Client-side analytics and dashboards
- Data quality and freshness indicators
- Informed decision-making about search strategies
💡 Proposed Solution
Add a structured metadata field to all MCP tool responses with three tiers of implementation:
Tier 1: Initial Implementation (Core Metrics)
interface MCPMetadata {
// Cost tracking
tokens: number; // Approximate token count (documented)
// Performance
duration_ms: number; // Response time for optimization
timestamp: string; // ISO 8601 timestamp
// Data freshness
cached: boolean; // Was response from cache?
// Data quality
results_total?: number; // Total matches found
results_returned?: number; // Results in this response
results_truncated?: boolean; // Were results cut off?
}
Example response:
{
"action": "search",
"query": "logger integration",
"format": "compact",
"content": "## GitHub Search Results\n**Query:** \"logger integration\"\n**Found:** 1 results\n- 🔵 × #6: Implement CLI and Integration Examples [18%]",
"metadata": {
"tokens": 31,
"duration_ms": 142,
"timestamp": "2025-11-25T10:30:00Z",
"cached": false,
"results_total": 1,
"results_returned": 1,
"results_truncated": false
}
}
Tier 2: Near-Term Enhancements (Quality Indicators)
interface MCPMetadata {
// ... Tier 1 fields ...
// Search quality
confidence?: number; // 0-1 relevance score for semantic search
threshold?: number; // Search threshold applied
// Index information
index_size?: number; // Total items in searchable corpus
index_last_updated?: string; // When index was last refreshed
// Issues & warnings
warnings?: Array<{
code: string; // e.g., "INDEX_STALE"
message: string; // Human-readable message
severity: "info" | "warning" | "error";
}>;
}
Example with warnings:
{
"metadata": {
"tokens": 31,
"duration_ms": 142,
"cached": true,
"index_last_updated": "2025-11-23T08:00:00Z",
"warnings": [
{
"code": "INDEX_STALE",
"message": "Index is 2 days old, run 'dev-agent update'",
"severity": "warning"
}
]
}
}
Tier 3: Future Enhancements (Debugging & Advanced)
interface MCPMetadata {
// ... Tier 1 & 2 fields ...
// Debugging
request_id?: string; // For log correlation
tool_version?: string; // MCP server version
adapter?: string; // Which adapter handled request
// Resource usage
tokens_input?: number; // Tokens in query
tokens_output?: number; // Tokens in response
api_calls_made?: number; // External API calls
rate_limit_remaining?: number; // GitHub/external API limits
// Tool-specific context
tool_context?: Record<string, unknown>; // Flexible tool-specific data
}
📋 Implementation Tasks
Phase 1: Core Infrastructure (Tier 1)
1.1 Type Definitions
1.2 Update Formatters
Change all formatters to return structured data:
Return format:
return {
content: formattedString,
tokens: calculatedTokens // Extract from current string format
};
1.3 Update Adapters
All adapters construct metadata:
1.4 Remove Token Display from Content
1.5 Testing
Phase 2: Quality Enhancements (Tier 2)
Phase 3: Advanced Features (Tier 3)
Documentation
🔧 Tool-Specific Examples
dev_search
{
"action": "search",
"metadata": {
"tokens": 45,
"duration_ms": 230,
"cached": false,
"results_total": 12,
"results_returned": 10,
"confidence": 0.82,
"threshold": 0.7,
"index_size": 1250
}
}
dev_gh
{
"action": "context",
"metadata": {
"tokens": 113,
"duration_ms": 85,
"cached": true,
"index_last_updated": "2025-11-25T08:00:00Z",
"tool_context": {
"github_repo": "lytics/dev-agent",
"issue_number": 6,
"has_comments": true
}
}
}
dev_plan
{
"action": "plan",
"metadata": {
"tokens": 450,
"duration_ms": 1200,
"cached": false,
"tool_context": {
"issue_number": 29,
"related_files_analyzed": 15,
"tasks_generated": 8,
"detail_level": "detailed",
"used_explorer": true
}
}
}
dev_explore
{
"action": "pattern",
"metadata": {
"tokens": 280,
"duration_ms": 450,
"cached": false,
"results_total": 24,
"results_returned": 10,
"confidence": 0.75,
"tool_context": {
"action": "pattern",
"patterns_found": 12,
"files_analyzed": 45
}
}
}
dev_status
{
"action": "status",
"metadata": {
"tokens": 156,
"duration_ms": 45,
"cached": true,
"index_size": 1250,
"index_last_updated": "2025-11-25T08:00:00Z",
"warnings": [
{
"code": "INDEX_STALE",
"message": "Index is 2 days old",
"severity": "warning"
}
]
}
}
✅ Benefits
- Machine Readable: No string parsing needed
- Type Safe: Structured data with proper TypeScript types
- Extensible: Three-tier approach allows gradual enhancement
- Standard Pattern: Follows industry best practices (GitHub, Stripe, GraphQL)
- Optimization Tracking: Easy to build analytics and monitoring
- Client Flexibility: Clients choose how/if to display metadata
- Data Quality: Users understand freshness and completeness
- Performance Insights: Track and optimize slow operations
🚨 Breaking Change Notice
This is a breaking change to the MCP response format.
Impact:
- Clients parsing token counts from content string need updates
- Response structure changes (adds
metadata field)
Migration Path:
- Read from
metadata.tokens instead of parsing content
- Use
metadata.results_* fields for result information
- Content will be cleaner without token/result suffixes
- Clients can optionally display additional metadata
Timeline:
- Phase 1 (Tier 1): Next minor version (breaking)
- Phase 2 (Tier 2): Non-breaking additions to metadata
- Phase 3 (Tier 3): Non-breaking additions to metadata
📊 Success Metrics
After Phase 1 implementation:
- ✅ Track token reduction across tool improvements
- ✅ Identify which tools/queries are most expensive
- ✅ Build dashboards showing token usage trends
- ✅ Make data-driven decisions about optimization priorities
- ✅ Monitor response time performance
- ✅ Track cache hit rates
After Phase 2:
- ✅ Surface data freshness issues proactively
- ✅ Help users understand search quality
- ✅ Reduce support burden with better warnings
After Phase 3:
- ✅ Full observability for debugging
- ✅ Rate limit awareness and management
- ✅ Detailed cost attribution
🎨 Design Decisions
Why metadata vs top-level fields?
- Extensible for future fields without API changes
- Semantic clarity - metadata about the response
- Industry standard pattern
- Groups related operational data
Remove tokens from content?
Yes - cleaner separation, no duplication, client control
Token precision?
Whole numbers documented as approximate - simpler and sufficient
Tiered approach?
Yes - ship incrementally, validate each tier, avoid over-engineering
Tool-specific vs uniform?
Hybrid - common base fields + optional tool_context for specific data
🔗 Related
🎯 Problem
Currently, MCP tool responses embed operational metadata (like token counts) within the markdown content string:
{ "action": "search", "content": "## Results...\n\n🪙 ~31 tokens" }Issues with current approach:
🎯 Goal
Token counts are critical metadata for tracking optimization and adoption pain points. We need a structured way to expose this data to enable:
💡 Proposed Solution
Add a structured
metadatafield to all MCP tool responses with three tiers of implementation:Tier 1: Initial Implementation (Core Metrics)
Example response:
{ "action": "search", "query": "logger integration", "format": "compact", "content": "## GitHub Search Results\n**Query:** \"logger integration\"\n**Found:** 1 results\n- 🔵 × #6: Implement CLI and Integration Examples [18%]", "metadata": { "tokens": 31, "duration_ms": 142, "timestamp": "2025-11-25T10:30:00Z", "cached": false, "results_total": 1, "results_returned": 1, "results_truncated": false } }Tier 2: Near-Term Enhancements (Quality Indicators)
Example with warnings:
{ "metadata": { "tokens": 31, "duration_ms": 142, "cached": true, "index_last_updated": "2025-11-23T08:00:00Z", "warnings": [ { "code": "INDEX_STALE", "message": "Index is 2 days old, run 'dev-agent update'", "severity": "warning" } ] } }Tier 3: Future Enhancements (Debugging & Advanced)
📋 Implementation Tasks
Phase 1: Core Infrastructure (Tier 1)
1.1 Type Definitions
MCPMetadatainterface inpackages/mcp-server/src/server/types.tsMCPResponseinterfaces to includemetadatafield1.2 Update Formatters
Change all formatters to return structured data:
packages/mcp-server/src/formatters/search.tspackages/mcp-server/src/formatters/github.tspackages/mcp-server/src/formatters/plan.tspackages/mcp-server/src/formatters/status.tspackages/mcp-server/src/formatters/explore.tsReturn format:
1.3 Update Adapters
All adapters construct metadata:
github-adapter.ts- Add timing, result countssearch-adapter.ts- Add timing, result counts, confidenceplan-adapter.ts- Add timing, tasks generatedstatus-adapter.ts- Add timing, index infoexplore-adapter.ts- Add timing, files analyzed1.4 Remove Token Display from Content
🪙 ~XX tokenssuffix from all formatter outputs1.5 Testing
metadatafieldPhase 2: Quality Enhancements (Tier 2)
Phase 3: Advanced Features (Tier 3)
Documentation
🔧 Tool-Specific Examples
dev_search
{ "action": "search", "metadata": { "tokens": 45, "duration_ms": 230, "cached": false, "results_total": 12, "results_returned": 10, "confidence": 0.82, "threshold": 0.7, "index_size": 1250 } }dev_gh
{ "action": "context", "metadata": { "tokens": 113, "duration_ms": 85, "cached": true, "index_last_updated": "2025-11-25T08:00:00Z", "tool_context": { "github_repo": "lytics/dev-agent", "issue_number": 6, "has_comments": true } } }dev_plan
{ "action": "plan", "metadata": { "tokens": 450, "duration_ms": 1200, "cached": false, "tool_context": { "issue_number": 29, "related_files_analyzed": 15, "tasks_generated": 8, "detail_level": "detailed", "used_explorer": true } } }dev_explore
{ "action": "pattern", "metadata": { "tokens": 280, "duration_ms": 450, "cached": false, "results_total": 24, "results_returned": 10, "confidence": 0.75, "tool_context": { "action": "pattern", "patterns_found": 12, "files_analyzed": 45 } } }dev_status
{ "action": "status", "metadata": { "tokens": 156, "duration_ms": 45, "cached": true, "index_size": 1250, "index_last_updated": "2025-11-25T08:00:00Z", "warnings": [ { "code": "INDEX_STALE", "message": "Index is 2 days old", "severity": "warning" } ] } }✅ Benefits
🚨 Breaking Change Notice
This is a breaking change to the MCP response format.
Impact:
metadatafield)Migration Path:
metadata.tokensinstead of parsing contentmetadata.results_*fields for result informationTimeline:
📊 Success Metrics
After Phase 1 implementation:
After Phase 2:
After Phase 3:
🎨 Design Decisions
Why
metadatavs top-level fields?Remove tokens from content?
Yes - cleaner separation, no duplication, client control
Token precision?
Whole numbers documented as approximate - simpler and sufficient
Tiered approach?
Yes - ship incrementally, validate each tier, avoid over-engineering
Tool-specific vs uniform?
Hybrid - common base fields + optional
tool_contextfor specific data🔗 Related