Skip to main content
DeployStack Satellite implements a hierarchical router pattern to solve the MCP context window consumption problem. Instead of exposing hundreds of tools directly to MCP clients, the satellite exposes only 4 meta-tools that enable dynamic tool discovery, execution, and resource access.
Problem Solved: Traditional MCP implementations expose all tools directly, consuming 40-80% of the context window before any actual work begins. The hierarchical router reduces this to <1% while maintaining full functionality.
Alternative Access Method: For direct MCP client integration without OAuth complexity, see Instance Router which provides token-based access to individual instances.
For information about how tools are discovered and cached internally, see Tool Discovery Implementation.

The Context Window Problem

Traditional MCP Architecture

When MCP clients connect to servers with many tools, the context window gets consumed by tool definitions:
Real-world impact:
  • Claude Code: 82,000 tokens consumed by MCP tools (41% of context)
  • Industry consensus: 20-40 tools before performance degrades
  • Cursor hard limit: 40 tools maximum
  • Critical failures: 80+ tools cause suboptimal responses

Root Cause

MCP clients call tools/list to discover available tools, and servers respond with complete tool definitions including:
  • Tool name
  • Description (often 100+ characters)
  • Input schema (JSON Schema objects)
  • Parameter descriptions
  • Examples and constraints
This metadata is valuable but consumes massive context space when multiplied across many tools.

The Hierarchical Router Solution

Architecture Overview

Instead of exposing all tools directly, the satellite exposes only 4 meta-tools:

Token Reduction

Before (Traditional):
  • 150 tools × 500 tokens = 75,000 tokens
  • 37.5% of 200k context consumed
After (Hierarchical):
  • 4 meta-tools × ~500 tokens = ~2000 tokens consumed
  • Result: ~1.0% of context window used
  • Token Reduction: 97.3%

Meta-Tool Specifications

Tool 1: discover_mcp_tools

Purpose: Search for available tools across all connected MCP servers using natural language queries. Definition:
Example Request:
Example Response:
Implementation Details:
  • Uses Fuse.js for fuzzy full-text search
  • Searches across tool names, descriptions, and server names
  • Returns results ranked by relevance score
  • Search time: 2-5ms for 100+ tools
  • Queries UnifiedToolDiscoveryManager for tool cache

Tool 2: execute_mcp_tool

Purpose: Execute a discovered tool by its path returned from discover_mcp_tools. Definition:
Example Request:
Example Response:
Implementation Details:
  • Parses tool_path from external format (server:tool) to internal format (server-tool)
  • Looks up tool in unified cache to determine transport type
  • Routes to appropriate handler (stdio or HTTP/SSE)
  • Returns result in MCP content format

Tool 3: list_mcp_resources

Purpose: List all available resources across all connected MCP servers. Definition:
Example Request:
Example Response:
Implementation Details:
  • Returns all resources from connected MCP servers with namespaced URIs
  • Resource metadata is cached during tool discovery (no extra round-trip)
  • _meta fields are preserved for MCP Apps support
  • _meta.ui.resourceUri is rewritten to use the namespaced URI format
  • Per-user filtering: only shows resources from the authenticated user’s installations

Tool 4: read_mcp_resource

Purpose: Read the content of a specific resource by its namespaced URI. Definition:
Example Request:
Example Response:
Implementation Details:
  • Parses the namespaced URI (serverSlug|originalUri) to identify origin server
  • Content is never cached — always proxied on-demand to the origin MCP server
  • Routes to the correct transport (stdio subprocess or HTTP/SSE endpoint)
  • Per-user routing: resolves the authenticated user’s specific installation
  • Supports both text and binary (base64) resource content

Complete User Flow

Step 1: Client Connects

Client knows: “I connected to a server named deploystack-satellite”

Step 2: Client Discovers Available Tools

Client knows: “This server has 4 tools available”

Step 3: User Asks for GitHub Tools

The AI agent (Claude) decides it needs GitHub tools:
Client knows: “The discovery tool returned some tool paths as strings”

Step 4: User Executes Discovered Tool

The AI agent decides to use github:create_issue:
Client knows: “The execute tool returned a result”

Step 5: User Lists Resources

The AI agent discovers available resources:
Client knows: “There is 1 resource available from the excalidraw server”

Step 6: User Reads a Resource

The AI agent reads a specific resource:
Client knows: “The resource content was returned”

Format Conversion

The satellite converts between user-facing format (serverName:toolName) and internal routing format (serverName-toolName) transparently during tool discovery and execution. See Tool Discovery - Namespacing Strategy for complete details on naming conventions and format conversion logic.

Search Implementation

Fuse.js Configuration

Search Performance

  • Index Build: On-demand per search (no stale cache)
  • Search Time: 2-5ms for 100+ tools
  • Memory: Zero duplication (queries UnifiedToolDiscoveryManager directly)
  • Accuracy: Handles typos, partial matches, fuzzy matching

Single Source of Truth

ToolSearchService queries UnifiedToolDiscoveryManager.getAllTools() directly for every search:
Benefits:
  • Always up-to-date (no cache invalidation needed)
  • No memory duplication
  • Automatic reflection of tool changes

Why Tool and Resource Notifications Are Irrelevant

The Question

“Should we implement notifications/tools/list_changed or notifications/resources/list_changed when tools or resources are added or removed?”

The Answer

NO. The MCP client only sees 4 static meta-tools that NEVER change:
  • discover_mcp_tools ✅ Always available
  • execute_mcp_tool ✅ Always available
  • list_mcp_resources ✅ Always available
  • read_mcp_resource ✅ Always available
What DOES change behind the scenes:
  • New MCP servers installed → Search index and resource cache update automatically
  • MCP servers removed → Search index and resource cache update automatically
  • Tools/resources discovered from new servers → Caches update automatically
But the client never needs to know! When a new MCP server is installed:
  1. ✅ Backend sends configuration update to satellite
  2. ✅ Satellite spawns new MCP server process (if stdio)
  3. ✅ Tool and resource discovery runs automatically
  4. ✅ Search index and resource cache refreshed
  5. Client is NOT notified (doesn’t need to be)
Next time the client calls discover_mcp_tools or list_mcp_resources, the new tools and resources are automatically included. No notification needed.

Performance Characteristics

Token Consumption

Search Performance

  • Discovery Latency: 2-5ms (including Fuse.js index build)
  • Execution Latency: <1ms (routing overhead only)
  • Memory Overhead: ~1KB per cached tool
  • Scalability: No degradation up to 500+ tools

Real-World Impact

Claude Code Example:
  • Before: 82,000 tokens (41%) consumed by MCP tools
  • After: ~2000 tokens (~1.0%) consumed by 4 meta-tools
  • Result: ~80,000 tokens freed for actual work

Implementation Status

Status: ✅ Fully OperationalAll 4 meta-tools are implemented and production-ready:
  • discover_mcp_tools: Full-text search with Fuse.js
  • execute_mcp_tool: Complete routing for stdio and HTTP/SSE transports
  • list_mcp_resources: Resource listing with per-user filtering and _meta preservation
  • read_mcp_resource: On-demand resource proxying to origin servers

Code Locations

Core Implementation:
  • /services/satellite/src/core/mcp-server-wrapper.ts - Meta-tool handlers
  • /services/satellite/src/services/tool-search-service.ts - Search implementation
  • /services/satellite/src/services/unified-tool-discovery-manager.ts - Tool cache
  • /services/satellite/src/services/unified-resource-discovery-manager.ts - Resource cache
  • /services/satellite/src/lib/mcp-resource-executor.ts - Resource read proxying
Key Methods:
  • setupMcpServer() - Registers 4 meta-tools instead of all tools
  • handleDiscoverTools() - Implements tool discovery logic
  • handleExecuteTool() - Implements tool execution routing
  • handleListResources() - Implements resource listing with per-user filtering
  • handleReadResource() - Implements on-demand resource proxying

Resource Support & _meta Preservation

Resource Discovery

Resources are discovered alongside tools during the same connection — no extra round-trip per server. When the satellite connects to an MCP server (stdio or HTTP/SSE), it calls both tools/list and resources/list in sequence. Resource metadata (URI, name, description, mimeType) is cached, but resource content is never cached — it’s always proxied on-demand via McpResourceExecutor.

URI Namespacing

Resources use a pipe separator (|) for URI namespacing, different from the colon (:) used for tools:
The pipe separator was chosen because resource URIs frequently contain colons (e.g., ui://, file://, https://), which would conflict with the tool namespacing format.

_meta Preservation

The _meta field is preserved through the entire cache and response chain. This is important for MCP Apps support, where _meta carries UI rendering hints (e.g., _meta.ui.resourceUri tells the client which resource to load for an app-like experience). Preservation chain:
  1. Origin MCP server returns _meta on tools and resources
  2. Discovery managers preserve _meta when caching (...(item._meta ? { _meta: item._meta } : {}))
  3. discover_mcp_tools includes _meta in tool search results
  4. list_mcp_resources includes _meta in resource listings
  5. _meta.ui.resourceUri is rewritten to use the namespaced URI format so clients can read the resource through the hierarchical router

Resource vs Tool Flow

Benefits Summary

For Users:
  • 95%+ more context available for actual work
  • Natural language tool discovery
  • No performance degradation with many tools
  • Transparent - works like any MCP server
For Developers:
  • Simple 4-tool interface
  • Automatic tool discovery
  • No client modifications needed
  • Standard MCP protocol compliance
For Operations:
  • Scales to hundreds of tools
  • No memory explosion
  • Fast search performance
  • Easy to monitor and debug

Status-Based Tool Filtering

The hierarchical router integrates with status tracking to hide tools from the user’s unavailable instances and provide clear error messages when unavailable tools are executed.
Per-User Filtering: Each user sees only tools from their OWN instances that are online. Other team members’ instance status doesn’t affect your tool availability.
See Status Tracking - Tool Filtering for complete filtering logic, execution blocking rules, and status values.