Progressive disclosure for MCP - Minimal context bloat with on-demand tool discovery and dynamic server provisioning. When Claude Code connects directly to multiple MCP servers (GitHub, Jira, DB, etc.), it loads all tool schemas into context. This causes: - Context bloat: Dozens of tool definitions consume tokens before you even ask a question - Static configuration: Requires Claude Code restart t
Add this skill
npx mdskills install ViperJuice/mcp-gatewaySophisticated MCP gateway enabling progressive tool discovery and dynamic server provisioning to reduce context bloat
1# PMCP - Progressive MCP23<!-- mcp-name: io.github.ViperJuice/pmcp -->45[](https://pypi.org/project/pmcp/)6[](LICENSE)78**Progressive disclosure for MCP** - Minimal context bloat with on-demand tool discovery and dynamic server provisioning.910## The Problem1112When Claude Code connects directly to multiple MCP servers (GitHub, Jira, DB, etc.), it loads **all** tool schemas into context. This causes:13- **Context bloat**: Dozens of tool definitions consume tokens before you even ask a question14- **Static configuration**: Requires Claude Code restart to see new servers15- **No progressive disclosure**: Full schemas shown even when not needed1617Anthropic has [highlighted context bloat](https://www.anthropic.com/news) as a key challenge with MCP tooling.1819## The Solution2021**PMCP** acts as a single MCP server that Claude Code connects to. Instead of exposing all downstream tools, it provides:2223- **11 stable meta-tools** (not the 50+ underlying tools)24- **Auto-starts** essential servers (Playwright, Context7) with no configuration25- **Dynamically provisions** new servers on-demand from a manifest of 25+26- **Progressive disclosure**: Compact capability cards first, detailed schemas only on request27- **Policy enforcement**: Output size caps and optional secret redaction2829## Quick Start3031### Installation3233```bash34# With uv (recommended)35uv pip install pmcp3637# Or run directly without installing38uvx pmcp3940# With pip41pip install pmcp4243# With LLM-enhanced features (optional, see below)44uv pip install pmcp[llm]45```4647### Advanced LLM Features (Optional)4849PMCP can use an LLM for smarter capability matching and summarization. Without an API key, it falls back to keyword matching and templates.5051**Features enabled with LLM:**5253| Feature | Without API Key | With API Key |54|---------|-----------------|--------------|55| Capability matching | Keyword-based | Semantic understanding |56| Tool summaries | Static templates | LLM-generated descriptions |57| Code snippets | Static examples | Dynamic, context-aware examples |5859**Setup:**60611. Get a free API key from [Groq Console](https://console.groq.com/keys)622. Add to your `.env` file:6364```bash65# In your project's .env file (or ~/.env)66GROQ_API_KEY=gsk_your_groq_api_key_here67```68693. Install with LLM support:7071```bash72uv pip install pmcp[llm]73```7475PMCP uses [BAML](https://docs.boundaryml.com/) with Groq's fast inference API for sub-second LLM responses. The LLM features are entirely optional - PMCP works fully without them.7677### Configure Claude Code7879Create/update `~/.claude/mcp.json`:8081```json82{83 "mcpServers": {84 "gateway": {85 "command": "pmcp",86 "args": []87 }88 }89}90```9192That's it! PMCP auto-starts with Playwright and Context7 servers ready to use.9394### Other MCP Clients9596PMCP works with any MCP-compatible client. Below are configuration examples for popular clients.9798#### Codex CLI99100Create `~/.codex/mcp.json` (verify path in Codex documentation):101102```json103{104 "mcpServers": {105 "gateway": {106 "command": "pmcp",107 "args": []108 }109 }110}111```112113#### Gemini CLI114115Create the appropriate config file (verify path in Gemini CLI documentation):116117```json118{119 "mcpServers": {120 "gateway": {121 "command": "pmcp",122 "args": []123 }124 }125}126```127128> **Note**: Configuration paths and formats vary by client. Verify the exact location and format in each client's official documentation.129130### Your First Interaction131132```133You: "Take a screenshot of google.com"134135Claude uses: gateway.invoke {136 tool_id: "playwright::browser_navigate",137 arguments: { url: "https://google.com" }138}139// Then: gateway.invoke { tool_id: "playwright::browser_screenshot" }140141Returns: Screenshot of google.com142```143144## Architecture145146```147┌─────────────────────────────────────────────────────────────┐148│ Claude Code │149│ Only connects to PMCP (single server in config) │150└────────────────────────────┬────────────────────────────────┘151 │152 ▼153┌─────────────────────────────────────────────────────────────┐154│ PMCP │155│ • 11 meta-tools (catalog, invoke, provision, etc.) │156│ • Progressive disclosure (compact cards → full schemas) │157│ • Policy enforcement (allow/deny lists) │158└────────────────────────────┬────────────────────────────────┘159 │160 ┌────────────────────┼────────────────────┐161 ▼ ▼ ▼162┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐163│ Auto-Start │ │ Manifest │ │ Custom Servers │164│ (Playwright, │ │ (25+ servers │ │ (your own MCP │165│ Context7) │ │ on-demand) │ │ servers) │166└───────────────┘ └─────────────────┘ └─────────────────┘167```168169**Key principle**: Users configure ONLY `pmcp` in Claude Code.170The gateway discovers and manages all other servers.171172### Why Single-Gateway?1731741. **No context bloat** - Claude sees 11 tools, not 50+1752. **No restarts** - Provision new servers without restarting Claude Code1763. **Consistent interface** - All tools accessed via `gateway.invoke`1774. **Policy control** - Centralized allow/deny rules178179## Gateway Tools180181The gateway exposes **11 meta-tools** organized into three categories:182183### Core Tools184185| Tool | Purpose |186|------|---------|187| `gateway.catalog_search` | Search available tools, returns compact capability cards |188| `gateway.describe` | Get detailed schema for a specific tool |189| `gateway.invoke` | Call a downstream tool with argument validation |190| `gateway.refresh` | Reload backend configs and reconnect |191| `gateway.health` | Get gateway and server health status |192193### Capability Discovery Tools194195| Tool | Purpose |196|------|---------|197| `gateway.request_capability` | Natural language capability matching with CLI preference |198| `gateway.sync_environment` | Detect platform and available CLIs |199| `gateway.provision` | Install and start MCP servers on-demand |200| `gateway.provision_status` | Check installation progress |201202### Monitoring Tools203204| Tool | Purpose |205|------|---------|206| `gateway.list_pending` | List pending tool invocations with health status |207| `gateway.cancel` | Cancel a pending tool invocation |208209## Progressive Disclosure Workflow210211PMCP follows a progressive disclosure pattern - start with natural language, get recommendations, drill down as needed.212213### Step 1: Request a Capability214215```216You: "I need to look up library documentation"217218gateway.request_capability({ query: "library documentation" })219```220221Returns:222```json223{224 "status": "candidates",225 "candidates": [{226 "name": "context7",227 "candidate_type": "server",228 "relevance_score": 0.95,229 "is_running": true,230 "reasoning": "Context7 provides up-to-date documentation for any package"231 }],232 "recommendation": "Use context7 - already running"233}234```235236### Step 2: Search Available Tools237238```239gateway.catalog_search({ query: "documentation" })240```241242### Step 3: Get Tool Details243244```245gateway.describe({ tool_id: "context7::get-library-docs" })246```247248### Step 4: Invoke the Tool249250```251gateway.invoke({252 tool_id: "context7::get-library-docs",253 arguments: { libraryId: "/npm/react/19.0.0" }254})255```256257### Offline Tool Discovery258259When using `gateway.catalog_search`, you can discover tools from servers that haven't started yet:260261```json262// Search all tools including offline/lazy servers263gateway.catalog_search({264 "query": "browser",265 "include_offline": true266})267```268269This uses pre-cached tool descriptions from `.mcp-gateway/descriptions.yaml`. To refresh the cache:270271```bash272pmcp refresh273```274275**Note**: Cached tools show metadata only. Full schemas are available after the server starts (use `gateway.describe` to trigger lazy start).276277## Dynamic Server Provisioning278279PMCP can install and start MCP servers on-demand from a curated manifest of 25+ servers.280281### Example: Adding GitHub Support282283```284You: "I need to manage GitHub issues"285286gateway.request_capability({ query: "github issues" })287```288289Returns (if not already configured):290```json291{292 "status": "candidates",293 "candidates": [{294 "name": "github",295 "candidate_type": "server",296 "is_running": false,297 "requires_api_key": true,298 "env_var": "GITHUB_PERSONAL_ACCESS_TOKEN",299 "env_instructions": "Create at https://github.com/settings/tokens with repo scope"300 }]301}302```303304### Provisioning305306```bash307# 1. Set API key (if required)308export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...309310# 2. Provision via gateway311gateway.provision({ server_name: "github" })312```313314## Auto-Start Servers315316These servers start automatically (no configuration required):317318| Server | Description | API Key |319|--------|-------------|---------|320| `playwright` | Browser automation - navigation, screenshots, DOM inspection | Not required |321| `context7` | Library documentation lookup - up-to-date docs for any package | Optional (for higher rate limits) |322323## Available Servers324325The manifest includes 25+ servers that can be provisioned on-demand:326327### No API Key Required328329| Server | Description |330|--------|-------------|331| `filesystem` | File operations - read, write, search |332| `memory` | Persistent knowledge graph |333| `fetch` | HTTP requests with robots.txt compliance |334| `sequential-thinking` | Problem solving through thought sequences |335| `git` | Git operations via MCP |336| `sqlite` | SQLite database operations |337| `time` | Timezone operations |338| `puppeteer` | Headless Chrome automation |339340### Requires API Key341342| Server | Description | Environment Variable |343|--------|-------------|---------------------|344| `github` | GitHub API - issues, PRs, repos | `GITHUB_PERSONAL_ACCESS_TOKEN` |345| `gitlab` | GitLab API - projects, MRs | `GITLAB_PERSONAL_ACCESS_TOKEN` |346| `slack` | Slack messaging | `SLACK_BOT_TOKEN` |347| `notion` | Notion workspace | `NOTION_TOKEN` |348| `linear` | Linear issue tracking | `LINEAR_API_KEY` |349| `postgres` | PostgreSQL database | `POSTGRES_URL` |350| `brave-search` | Web search | `BRAVE_API_KEY` |351| `google-drive` | Google Drive files | `GDRIVE_CREDENTIALS` |352| `sentry` | Error tracking | `SENTRY_AUTH_TOKEN` |353354See `.env.example` for all supported environment variables.355356## Code Execution Guidance357358PMCP includes built-in guidance to encourage models to use code execution patterns, reducing context bloat and improving workflow efficiency.359360### Guidance Layers361362**L0 (MCP Instructions)**: Brief philosophy in server instructions (~30 tokens)363- "Write code to orchestrate tools - use loops, filters, conditionals"364365**L1 (Code Hints)**: Ultra-terse hints in search results (~8-12 tokens/card)366- Single-word hints: "loop", "filter", "try/catch", "poll"367368**L2 (Code Snippets)**: Minimal examples in describe output (~40-80 tokens, opt-in)369- 3-4 line code examples showing practical usage370371**L3 (Methodology Resource)**: Full guide (lazy-loaded, 0 tokens)372- Accessible via `pmcp://guidance/code-execution` resource373374### Guidance Configuration375376Create `~/.claude/gateway-guidance.yaml`:377378```yaml379guidance:380 level: "minimal" # Options: "off", "minimal", "standard"381382 layers:383 mcp_instructions: true # L0 philosophy384 code_hints: true # L1 hints385 code_snippets: false # L2 examples (default: off)386 methodology_resource: true # L3 guide387```388389**Levels**:390- `minimal` (default): L0 + L1 (~200 tokens overhead)391- `standard`: L0 + L1 + L2 (~320 tokens overhead)392- `off`: No guidance393394### View Guidance Status395396```bash397pmcp guidance # Show configuration398pmcp guidance --show-budget # Show token estimates399```400401### Token Budget402403- **Minimal mode**: ~200 tokens typical workflow (L0 + search)404- **Standard mode**: ~320 tokens (L0 + search + 1 describe)405- **80% reduction** vs loading all tool schemas upfront!406407## Configuration408409### Config Discovery410411PMCP discovers MCP servers from:4124131. **Project config**: `.mcp.json` in project root (highest priority)4142. **User config**: `~/.mcp.json` or `~/.claude/.mcp.json`4153. **Custom config**: Via `--config` flag or `PMCP_CONFIG` env var416417### Adding Custom Servers418419For MCP servers not in the manifest, add them to `~/.mcp.json`:420421```json422{423 "mcpServers": {424 "my-custom-server": {425 "command": "node",426 "args": ["./my-server.js"],427 "env": {428 "API_KEY": "..."429 }430 }431 }432}433```434435**Important**: Don't add `pmcp` itself to this file. PMCP is configured436in Claude Code's config (`~/.claude/mcp.json`), not in the downstream server list.437438### Policy File439440Create a policy file to control access and limits:441442**~/.claude/gateway-policy.yaml**:443```yaml444servers:445 allowlist: [] # Empty = allow all446 denylist:447 - dangerous-server448449tools:450 denylist:451 - "*::delete_*"452 - "*::drop_*"453454limits:455 max_tools_per_server: 100456 max_output_bytes: 50000457 max_output_tokens: 4000458459redaction:460 patterns:461 - "(api[_-]?key)[\\s]*[:=][\\s]*[\"']?([^\\s\"']+)"462 - "(password|secret)[\\s]*[:=][\\s]*[\"']?([^\\s\"']+)"463```464465### CLI Commands466467```bash468# Start the gateway server (default)469pmcp470471# Check server status472pmcp status473pmcp status --json # JSON output474pmcp status --server playwright # Filter by server475476# View logs477pmcp logs478pmcp logs --follow # Live tail479pmcp logs --tail 100 # Last 100 lines480481# Refresh server connections482pmcp refresh483pmcp refresh --server github # Refresh specific server484pmcp refresh --force # Force reconnect all485486# Initialize config (interactive)487pmcp init488```489490### Singleton Lock491492By default, PMCP uses a global lock at `~/.pmcp/gateway.lock` to ensure only one gateway runs per user. This prevents multiple gateway instances from spawning duplicate downstream servers.493494**Override the lock directory:**495496```bash497# CLI flag498pmcp --lock-dir /custom/path499500# Environment variable501export PMCP_LOCK_DIR=/custom/path502pmcp503```504505**Per-project lock (not recommended):**506507```bash508pmcp --lock-dir ./.mcp-gateway509```510511## Docker512513```bash514# Using Docker515docker run -it --rm \516 -v ~/.mcp.json:/home/appuser/.mcp.json:ro \517 -v ~/.env:/app/.env:ro \518 ghcr.io/viperjuice/pmcp:latest519520# Using Docker Compose521docker-compose up -d522```523524## Development525526```bash527# Clone the repo528git clone https://github.com/ViperJuice/pmcp529cd pmcp530531# Install with uv (recommended)532uv sync --all-extras533534# Run tests535uv run pytest536537# Run with debug logging538uv run pmcp --debug539```540541### Running Tests542543```bash544# Run all tests545uv run pytest546547# Run with coverage548uv run pytest --cov=pmcp549550# Run specific test file551uv run pytest tests/test_policy.py -v552```553554### Project Structure555556```557pmcp/558├── src/pmcp/559│ ├── __init__.py560│ ├── __main__.py # python -m pmcp entry561│ ├── cli.py # CLI commands (status, logs, init, refresh)562│ ├── server.py # MCP server implementation563│ ├── config/564│ │ └── loader.py # Config discovery (.mcp.json)565│ ├── client/566│ │ └── manager.py # Downstream server connections567│ ├── policy/568│ │ └── policy.py # Allow/deny lists569│ ├── tools/570│ │ └── handlers.py # Gateway tool implementations571│ ├── manifest/572│ │ ├── manifest.yaml # Server manifest (25+ servers)573│ │ ├── loader.py # Manifest loading574│ │ ├── installer.py # Server provisioning575│ │ └── environment.py # Platform/CLI detection576│ └── baml_client/ # BAML-generated LLM client (optional)577├── tests/ # 310+ tests578├── Dockerfile579├── docker-compose.yml580├── .env.example581├── pyproject.toml582└── README.md583```584585## Troubleshooting586587### Server Won't Connect588589```bash590pmcp status591pmcp logs --level debug592pmcp refresh --force593```594595### Missing API Key596597```bash598# Check which key is needed599pmcp status --server github600601# Set the key602export GITHUB_PERSONAL_ACCESS_TOKEN=ghp_...603```604605### Tool Invocation Fails606607```608gateway.catalog_search({ query: "tool-name" })609gateway.describe({ tool_id: "server::tool-name" })610gateway.list_pending()611```612613## License614615MIT616
Full transparency — inspect the skill content before installing.