pmcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pmcp-0.1.0/.claude/plans/ticklish-inventing-stearns.md +677 -0
- pmcp-0.1.0/.dockerignore +62 -0
- pmcp-0.1.0/.env.example +129 -0
- pmcp-0.1.0/.github/dependabot.yml +27 -0
- pmcp-0.1.0/.github/workflows/docker.yml +57 -0
- pmcp-0.1.0/.github/workflows/release.yml +54 -0
- pmcp-0.1.0/.github/workflows/test.yml +79 -0
- pmcp-0.1.0/.gitignore +56 -0
- pmcp-0.1.0/.mcp-gateway/descriptions.yaml +147 -0
- pmcp-0.1.0/CHANGELOG.md +37 -0
- pmcp-0.1.0/CONTRIBUTING.md +136 -0
- pmcp-0.1.0/Dockerfile +38 -0
- pmcp-0.1.0/LICENSE +21 -0
- pmcp-0.1.0/PKG-INFO +493 -0
- pmcp-0.1.0/README.md +450 -0
- pmcp-0.1.0/baml_src/capability_match.baml +109 -0
- pmcp-0.1.0/baml_src/clients.baml +11 -0
- pmcp-0.1.0/baml_src/generators.baml +9 -0
- pmcp-0.1.0/baml_src/summarize.baml +71 -0
- pmcp-0.1.0/config/.mcp.json.example +15 -0
- pmcp-0.1.0/docker-compose.yml +24 -0
- pmcp-0.1.0/examples/gateway-only.mcp.json +11 -0
- pmcp-0.1.0/examples/gateway-policy.yaml +52 -0
- pmcp-0.1.0/examples/sample-backends.mcp.json +20 -0
- pmcp-0.1.0/pyproject.toml +94 -0
- pmcp-0.1.0/server.json +16 -0
- pmcp-0.1.0/src/pmcp/__init__.py +3 -0
- pmcp-0.1.0/src/pmcp/__main__.py +6 -0
- pmcp-0.1.0/src/pmcp/baml_client/__init__.py +60 -0
- pmcp-0.1.0/src/pmcp/baml_client/async_client.py +191 -0
- pmcp-0.1.0/src/pmcp/baml_client/config.py +102 -0
- pmcp-0.1.0/src/pmcp/baml_client/globals.py +35 -0
- pmcp-0.1.0/src/pmcp/baml_client/inlinedbaml.py +22 -0
- pmcp-0.1.0/src/pmcp/baml_client/parser.py +59 -0
- pmcp-0.1.0/src/pmcp/baml_client/runtime.py +352 -0
- pmcp-0.1.0/src/pmcp/baml_client/stream_types.py +72 -0
- pmcp-0.1.0/src/pmcp/baml_client/sync_client.py +201 -0
- pmcp-0.1.0/src/pmcp/baml_client/tracing.py +22 -0
- pmcp-0.1.0/src/pmcp/baml_client/type_builder.py +458 -0
- pmcp-0.1.0/src/pmcp/baml_client/type_map.py +44 -0
- pmcp-0.1.0/src/pmcp/baml_client/types.py +90 -0
- pmcp-0.1.0/src/pmcp/baml_client/watchers.py +45 -0
- pmcp-0.1.0/src/pmcp/cli.py +693 -0
- pmcp-0.1.0/src/pmcp/client/__init__.py +5 -0
- pmcp-0.1.0/src/pmcp/client/manager.py +954 -0
- pmcp-0.1.0/src/pmcp/config/__init__.py +5 -0
- pmcp-0.1.0/src/pmcp/config/loader.py +205 -0
- pmcp-0.1.0/src/pmcp/errors.py +190 -0
- pmcp-0.1.0/src/pmcp/manifest/__init__.py +20 -0
- pmcp-0.1.0/src/pmcp/manifest/environment.py +149 -0
- pmcp-0.1.0/src/pmcp/manifest/installer.py +612 -0
- pmcp-0.1.0/src/pmcp/manifest/loader.py +157 -0
- pmcp-0.1.0/src/pmcp/manifest/manifest.yaml +452 -0
- pmcp-0.1.0/src/pmcp/manifest/matcher.py +225 -0
- pmcp-0.1.0/src/pmcp/manifest/refresher.py +440 -0
- pmcp-0.1.0/src/pmcp/manifest/version_checker.py +205 -0
- pmcp-0.1.0/src/pmcp/policy/__init__.py +5 -0
- pmcp-0.1.0/src/pmcp/policy/policy.py +258 -0
- pmcp-0.1.0/src/pmcp/server.py +381 -0
- pmcp-0.1.0/src/pmcp/summary/__init__.py +5 -0
- pmcp-0.1.0/src/pmcp/summary/generator.py +109 -0
- pmcp-0.1.0/src/pmcp/summary/llm_summarizer.py +74 -0
- pmcp-0.1.0/src/pmcp/summary/template_fallback.py +105 -0
- pmcp-0.1.0/src/pmcp/tools/__init__.py +5 -0
- pmcp-0.1.0/src/pmcp/tools/handlers.py +1304 -0
- pmcp-0.1.0/src/pmcp/types.py +550 -0
- pmcp-0.1.0/tests/__init__.py +1 -0
- pmcp-0.1.0/tests/conftest.py +369 -0
- pmcp-0.1.0/tests/test_cli.py +660 -0
- pmcp-0.1.0/tests/test_client_manager.py +565 -0
- pmcp-0.1.0/tests/test_config_loader.py +134 -0
- pmcp-0.1.0/tests/test_errors.py +202 -0
- pmcp-0.1.0/tests/test_integration.py +236 -0
- pmcp-0.1.0/tests/test_manifest.py +998 -0
- pmcp-0.1.0/tests/test_policy.py +228 -0
- pmcp-0.1.0/tests/test_refresher.py +588 -0
- pmcp-0.1.0/tests/test_server.py +230 -0
- pmcp-0.1.0/tests/test_server_lifecycle.py +135 -0
- pmcp-0.1.0/tests/test_summary.py +159 -0
- pmcp-0.1.0/tests/test_tools.py +252 -0
- pmcp-0.1.0/tests/test_version_checker.py +446 -0
- pmcp-0.1.0/uv.lock +1935 -0
|
@@ -0,0 +1,677 @@
|
|
|
1
|
+
# Plan: Dynamic Capability Discovery & Provisioning
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
Add environment-aware capability resolution with on-demand MCP server provisioning, CLI detection, and LLM-powered request matching.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
### 1. Environment Detection
|
|
9
|
+
- Probe inherited environment on startup (PATH, installed CLIs)
|
|
10
|
+
- Cache detected CLIs for fast lookup
|
|
11
|
+
- Optional `gateway.sync_environment` for explicit environment info
|
|
12
|
+
|
|
13
|
+
### 2. Capability Request Tool
|
|
14
|
+
- New `gateway.request_capability` tool
|
|
15
|
+
- LLM-powered matching via BAML/Groq
|
|
16
|
+
- Priority: CLI → Active MCP → Dormant MCP → Not Available
|
|
17
|
+
|
|
18
|
+
### 3. MCP Server Manifest
|
|
19
|
+
- YAML manifest of installable MCP servers
|
|
20
|
+
- Platform-specific install commands (Mac, WSL/Linux)
|
|
21
|
+
- Keyword matching + LLM semantic matching
|
|
22
|
+
- API key requirements with .env.example
|
|
23
|
+
|
|
24
|
+
### 4. On-Demand Provisioning
|
|
25
|
+
- Install MCP server from manifest when requested
|
|
26
|
+
- Spin up and index tools
|
|
27
|
+
- Return new tools to Claude
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## New Files
|
|
32
|
+
|
|
33
|
+
### `src/mcp_gateway/manifest/`
|
|
34
|
+
```
|
|
35
|
+
manifest/
|
|
36
|
+
├── __init__.py
|
|
37
|
+
├── loader.py # Load and parse manifest.yaml
|
|
38
|
+
├── matcher.py # BAML-powered request matching
|
|
39
|
+
├── installer.py # Platform-specific installation
|
|
40
|
+
├── environment.py # CLI detection and env probing
|
|
41
|
+
└── manifest.yaml # Server definitions
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### `baml_src/capability_match.baml`
|
|
45
|
+
```baml
|
|
46
|
+
class ManifestEntry {
|
|
47
|
+
name string
|
|
48
|
+
keywords string[]
|
|
49
|
+
description string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class MatchResult {
|
|
53
|
+
matched bool
|
|
54
|
+
entry_name string @description("Name of matched manifest entry or empty")
|
|
55
|
+
confidence float @description("0.0 to 1.0 confidence score")
|
|
56
|
+
reasoning string @description("Why this matches or doesn't")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function MatchCapabilityRequest(
|
|
60
|
+
query: string,
|
|
61
|
+
manifest_entries: ManifestEntry[]
|
|
62
|
+
) -> MatchResult {
|
|
63
|
+
client Groq
|
|
64
|
+
prompt #"
|
|
65
|
+
Match the user's capability request to the best manifest entry.
|
|
66
|
+
|
|
67
|
+
User request: {{ query }}
|
|
68
|
+
|
|
69
|
+
Available entries:
|
|
70
|
+
{% for entry in manifest_entries %}
|
|
71
|
+
- {{ entry.name }}: {{ entry.description }}
|
|
72
|
+
Keywords: {{ entry.keywords | join(", ") }}
|
|
73
|
+
{% endfor %}
|
|
74
|
+
|
|
75
|
+
If no entry matches well, set matched=false.
|
|
76
|
+
Confidence should reflect how well the request matches.
|
|
77
|
+
|
|
78
|
+
{{ ctx.output_format }}
|
|
79
|
+
"#
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Manifest Schema
|
|
86
|
+
|
|
87
|
+
### `src/mcp_gateway/manifest/manifest.yaml`
|
|
88
|
+
|
|
89
|
+
```yaml
|
|
90
|
+
version: "1.0"
|
|
91
|
+
|
|
92
|
+
# Environment detection
|
|
93
|
+
cli_alternatives:
|
|
94
|
+
git:
|
|
95
|
+
keywords: [git, version control, commits, branches, repository, clone]
|
|
96
|
+
check_command: ["git", "--version"]
|
|
97
|
+
help_command: ["git", "--help"]
|
|
98
|
+
description: "Git version control CLI"
|
|
99
|
+
prefer_mcp_for: [github issues, pull requests, github actions, github api]
|
|
100
|
+
|
|
101
|
+
docker:
|
|
102
|
+
keywords: [docker, container, image, dockerfile, compose]
|
|
103
|
+
check_command: ["docker", "--version"]
|
|
104
|
+
help_command: ["docker", "--help"]
|
|
105
|
+
description: "Docker container CLI"
|
|
106
|
+
|
|
107
|
+
kubectl:
|
|
108
|
+
keywords: [kubernetes, k8s, pods, deployments, services, helm]
|
|
109
|
+
check_command: ["kubectl", "version", "--client"]
|
|
110
|
+
help_command: ["kubectl", "--help"]
|
|
111
|
+
description: "Kubernetes CLI"
|
|
112
|
+
|
|
113
|
+
node:
|
|
114
|
+
keywords: [node, nodejs, npm, javascript, js]
|
|
115
|
+
check_command: ["node", "--version"]
|
|
116
|
+
help_command: ["node", "--help"]
|
|
117
|
+
description: "Node.js runtime"
|
|
118
|
+
|
|
119
|
+
python:
|
|
120
|
+
keywords: [python, pip, python3]
|
|
121
|
+
check_command: ["python3", "--version"]
|
|
122
|
+
help_command: ["python3", "--help"]
|
|
123
|
+
description: "Python interpreter"
|
|
124
|
+
|
|
125
|
+
aws:
|
|
126
|
+
keywords: [aws, amazon, s3, ec2, lambda, cloudformation]
|
|
127
|
+
check_command: ["aws", "--version"]
|
|
128
|
+
help_command: ["aws", "help"]
|
|
129
|
+
description: "AWS CLI"
|
|
130
|
+
|
|
131
|
+
gcloud:
|
|
132
|
+
keywords: [gcp, google cloud, gcloud, bigquery]
|
|
133
|
+
check_command: ["gcloud", "--version"]
|
|
134
|
+
help_command: ["gcloud", "--help"]
|
|
135
|
+
description: "Google Cloud CLI"
|
|
136
|
+
|
|
137
|
+
az:
|
|
138
|
+
keywords: [azure, az, microsoft cloud]
|
|
139
|
+
check_command: ["az", "--version"]
|
|
140
|
+
help_command: ["az", "--help"]
|
|
141
|
+
description: "Azure CLI"
|
|
142
|
+
|
|
143
|
+
# MCP Servers (dormant until requested)
|
|
144
|
+
servers:
|
|
145
|
+
# === No API Key Required ===
|
|
146
|
+
|
|
147
|
+
playwright:
|
|
148
|
+
description: "Browser automation - navigation, clicks, screenshots, DOM inspection"
|
|
149
|
+
keywords: [browser, web, automation, playwright, screenshot, click, navigate, scrape]
|
|
150
|
+
install:
|
|
151
|
+
mac: ["npx", "-y", "@anthropic/mcp-playwright"]
|
|
152
|
+
wsl: ["npx", "-y", "@anthropic/mcp-playwright"]
|
|
153
|
+
command: "npx"
|
|
154
|
+
args: ["-y", "@anthropic/mcp-playwright"]
|
|
155
|
+
requires_api_key: false
|
|
156
|
+
auto_start: true # Start by default
|
|
157
|
+
|
|
158
|
+
claude-in-chrome:
|
|
159
|
+
description: "Chrome browser control - read pages, click, type, navigate, screenshots"
|
|
160
|
+
keywords: [chrome, browser, web, automation, screenshot, click, navigate, tabs]
|
|
161
|
+
install:
|
|
162
|
+
mac: ["npx", "-y", "@anthropic/mcp-claude-in-chrome"]
|
|
163
|
+
wsl: ["npx", "-y", "@anthropic/mcp-claude-in-chrome"]
|
|
164
|
+
command: "npx"
|
|
165
|
+
args: ["-y", "@anthropic/mcp-claude-in-chrome"]
|
|
166
|
+
requires_api_key: false
|
|
167
|
+
auto_start: true # Start by default
|
|
168
|
+
|
|
169
|
+
# === Bright Data MCPs (Web Scraping & Data) ===
|
|
170
|
+
|
|
171
|
+
brightdata-scraper:
|
|
172
|
+
description: "Web scraping at scale - scrape any website with rotating proxies and CAPTCHA solving"
|
|
173
|
+
keywords: [scrape, scraping, web data, extract, crawl, brightdata, bright data, proxy]
|
|
174
|
+
install:
|
|
175
|
+
mac: ["npx", "-y", "@brightdata/mcp-scraper"]
|
|
176
|
+
wsl: ["npx", "-y", "@brightdata/mcp-scraper"]
|
|
177
|
+
command: "npx"
|
|
178
|
+
args: ["-y", "@brightdata/mcp-scraper"]
|
|
179
|
+
requires_api_key: true
|
|
180
|
+
env_var: "BRIGHTDATA_API_KEY"
|
|
181
|
+
env_instructions: "Get API key at https://brightdata.com/cp/api_tokens"
|
|
182
|
+
auto_start: true # Start by default
|
|
183
|
+
|
|
184
|
+
brightdata-serp:
|
|
185
|
+
description: "Search engine results - Google, Bing, DuckDuckGo SERP data"
|
|
186
|
+
keywords: [search, serp, google, bing, search results, seo, brightdata]
|
|
187
|
+
install:
|
|
188
|
+
mac: ["npx", "-y", "@brightdata/mcp-serp"]
|
|
189
|
+
wsl: ["npx", "-y", "@brightdata/mcp-serp"]
|
|
190
|
+
command: "npx"
|
|
191
|
+
args: ["-y", "@brightdata/mcp-serp"]
|
|
192
|
+
requires_api_key: true
|
|
193
|
+
env_var: "BRIGHTDATA_API_KEY"
|
|
194
|
+
env_instructions: "Get API key at https://brightdata.com/cp/api_tokens"
|
|
195
|
+
auto_start: true # Start by default
|
|
196
|
+
|
|
197
|
+
brightdata-unlocker:
|
|
198
|
+
description: "Web Unlocker - access any website bypassing blocks and CAPTCHAs"
|
|
199
|
+
keywords: [unblock, captcha, bypass, proxy, brightdata, anti-bot]
|
|
200
|
+
install:
|
|
201
|
+
mac: ["npx", "-y", "@brightdata/mcp-unlocker"]
|
|
202
|
+
wsl: ["npx", "-y", "@brightdata/mcp-unlocker"]
|
|
203
|
+
command: "npx"
|
|
204
|
+
args: ["-y", "@brightdata/mcp-unlocker"]
|
|
205
|
+
requires_api_key: true
|
|
206
|
+
env_var: "BRIGHTDATA_API_KEY"
|
|
207
|
+
env_instructions: "Get API key at https://brightdata.com/cp/api_tokens"
|
|
208
|
+
|
|
209
|
+
brightdata-datasets:
|
|
210
|
+
description: "Pre-built datasets - Amazon, LinkedIn, Google Maps, etc."
|
|
211
|
+
keywords: [dataset, data, amazon, linkedin, google maps, ecommerce, brightdata]
|
|
212
|
+
install:
|
|
213
|
+
mac: ["npx", "-y", "@brightdata/mcp-datasets"]
|
|
214
|
+
wsl: ["npx", "-y", "@brightdata/mcp-datasets"]
|
|
215
|
+
command: "npx"
|
|
216
|
+
args: ["-y", "@brightdata/mcp-datasets"]
|
|
217
|
+
requires_api_key: true
|
|
218
|
+
env_var: "BRIGHTDATA_API_KEY"
|
|
219
|
+
env_instructions: "Get API key at https://brightdata.com/cp/api_tokens"
|
|
220
|
+
|
|
221
|
+
filesystem:
|
|
222
|
+
description: "File system operations - read, write, search files"
|
|
223
|
+
keywords: [file, filesystem, read, write, directory, folder, fs]
|
|
224
|
+
install:
|
|
225
|
+
mac: ["npx", "-y", "@anthropic/mcp-filesystem"]
|
|
226
|
+
wsl: ["npx", "-y", "@anthropic/mcp-filesystem"]
|
|
227
|
+
command: "npx"
|
|
228
|
+
args: ["-y", "@anthropic/mcp-filesystem", "/"]
|
|
229
|
+
requires_api_key: false
|
|
230
|
+
|
|
231
|
+
memory:
|
|
232
|
+
description: "Persistent memory - store and retrieve information across sessions"
|
|
233
|
+
keywords: [memory, remember, store, persist, recall, notes]
|
|
234
|
+
install:
|
|
235
|
+
mac: ["npx", "-y", "@anthropic/mcp-memory"]
|
|
236
|
+
wsl: ["npx", "-y", "@anthropic/mcp-memory"]
|
|
237
|
+
command: "npx"
|
|
238
|
+
args: ["-y", "@anthropic/mcp-memory"]
|
|
239
|
+
requires_api_key: false
|
|
240
|
+
|
|
241
|
+
fetch:
|
|
242
|
+
description: "HTTP requests - fetch web pages, APIs, download content"
|
|
243
|
+
keywords: [http, fetch, request, api, web, download, curl]
|
|
244
|
+
install:
|
|
245
|
+
mac: ["npx", "-y", "@anthropic/mcp-fetch"]
|
|
246
|
+
wsl: ["npx", "-y", "@anthropic/mcp-fetch"]
|
|
247
|
+
command: "npx"
|
|
248
|
+
args: ["-y", "@anthropic/mcp-fetch"]
|
|
249
|
+
requires_api_key: false
|
|
250
|
+
|
|
251
|
+
sqlite:
|
|
252
|
+
description: "SQLite database operations"
|
|
253
|
+
keywords: [sqlite, database, sql, query, db]
|
|
254
|
+
install:
|
|
255
|
+
mac: ["npx", "-y", "@anthropic/mcp-sqlite"]
|
|
256
|
+
wsl: ["npx", "-y", "@anthropic/mcp-sqlite"]
|
|
257
|
+
command: "npx"
|
|
258
|
+
args: ["-y", "@anthropic/mcp-sqlite"]
|
|
259
|
+
requires_api_key: false
|
|
260
|
+
|
|
261
|
+
puppeteer:
|
|
262
|
+
description: "Headless Chrome automation"
|
|
263
|
+
keywords: [puppeteer, chrome, headless, browser, scraping]
|
|
264
|
+
install:
|
|
265
|
+
mac: ["npx", "-y", "@anthropic/mcp-puppeteer"]
|
|
266
|
+
wsl: ["npx", "-y", "@anthropic/mcp-puppeteer"]
|
|
267
|
+
command: "npx"
|
|
268
|
+
args: ["-y", "@anthropic/mcp-puppeteer"]
|
|
269
|
+
requires_api_key: false
|
|
270
|
+
|
|
271
|
+
# === API Key Required ===
|
|
272
|
+
|
|
273
|
+
github:
|
|
274
|
+
description: "GitHub API - issues, PRs, repos, actions, code search"
|
|
275
|
+
keywords: [github, issues, pull request, pr, repository, actions, workflows, code review]
|
|
276
|
+
install:
|
|
277
|
+
mac: ["npx", "-y", "@anthropic/mcp-github"]
|
|
278
|
+
wsl: ["npx", "-y", "@anthropic/mcp-github"]
|
|
279
|
+
command: "npx"
|
|
280
|
+
args: ["-y", "@anthropic/mcp-github"]
|
|
281
|
+
requires_api_key: true
|
|
282
|
+
env_var: "GITHUB_TOKEN"
|
|
283
|
+
env_instructions: "Create at https://github.com/settings/tokens with repo scope"
|
|
284
|
+
|
|
285
|
+
slack:
|
|
286
|
+
description: "Slack messaging - channels, DMs, search, notifications"
|
|
287
|
+
keywords: [slack, messaging, chat, channels, dm, notifications, team]
|
|
288
|
+
install:
|
|
289
|
+
mac: ["npx", "-y", "@anthropic/mcp-slack"]
|
|
290
|
+
wsl: ["npx", "-y", "@anthropic/mcp-slack"]
|
|
291
|
+
command: "npx"
|
|
292
|
+
args: ["-y", "@anthropic/mcp-slack"]
|
|
293
|
+
requires_api_key: true
|
|
294
|
+
env_var: "SLACK_TOKEN"
|
|
295
|
+
env_instructions: "Create Slack app at https://api.slack.com/apps and get Bot Token"
|
|
296
|
+
|
|
297
|
+
linear:
|
|
298
|
+
description: "Linear issue tracking - issues, projects, cycles"
|
|
299
|
+
keywords: [linear, issues, project management, tickets, bugs, tasks]
|
|
300
|
+
install:
|
|
301
|
+
mac: ["npx", "-y", "@anthropic/mcp-linear"]
|
|
302
|
+
wsl: ["npx", "-y", "@anthropic/mcp-linear"]
|
|
303
|
+
command: "npx"
|
|
304
|
+
args: ["-y", "@anthropic/mcp-linear"]
|
|
305
|
+
requires_api_key: true
|
|
306
|
+
env_var: "LINEAR_API_KEY"
|
|
307
|
+
env_instructions: "Create at https://linear.app/settings/api"
|
|
308
|
+
|
|
309
|
+
notion:
|
|
310
|
+
description: "Notion workspace - pages, databases, search"
|
|
311
|
+
keywords: [notion, wiki, documentation, pages, databases, notes]
|
|
312
|
+
install:
|
|
313
|
+
mac: ["npx", "-y", "@anthropic/mcp-notion"]
|
|
314
|
+
wsl: ["npx", "-y", "@anthropic/mcp-notion"]
|
|
315
|
+
command: "npx"
|
|
316
|
+
args: ["-y", "@anthropic/mcp-notion"]
|
|
317
|
+
requires_api_key: true
|
|
318
|
+
env_var: "NOTION_TOKEN"
|
|
319
|
+
env_instructions: "Create integration at https://www.notion.so/my-integrations"
|
|
320
|
+
|
|
321
|
+
postgres:
|
|
322
|
+
description: "PostgreSQL database operations"
|
|
323
|
+
keywords: [postgres, postgresql, database, sql, query]
|
|
324
|
+
install:
|
|
325
|
+
mac: ["npx", "-y", "@anthropic/mcp-postgres"]
|
|
326
|
+
wsl: ["npx", "-y", "@anthropic/mcp-postgres"]
|
|
327
|
+
command: "npx"
|
|
328
|
+
args: ["-y", "@anthropic/mcp-postgres"]
|
|
329
|
+
requires_api_key: true
|
|
330
|
+
env_var: "POSTGRES_URL"
|
|
331
|
+
env_instructions: "PostgreSQL connection string: postgresql://user:pass@host:5432/db"
|
|
332
|
+
|
|
333
|
+
sentry:
|
|
334
|
+
description: "Sentry error tracking - issues, events, releases"
|
|
335
|
+
keywords: [sentry, errors, monitoring, debugging, exceptions, crashes]
|
|
336
|
+
install:
|
|
337
|
+
mac: ["npx", "-y", "@anthropic/mcp-sentry"]
|
|
338
|
+
wsl: ["npx", "-y", "@anthropic/mcp-sentry"]
|
|
339
|
+
command: "npx"
|
|
340
|
+
args: ["-y", "@anthropic/mcp-sentry"]
|
|
341
|
+
requires_api_key: true
|
|
342
|
+
env_var: "SENTRY_AUTH_TOKEN"
|
|
343
|
+
env_instructions: "Create at https://sentry.io/settings/account/api/auth-tokens/"
|
|
344
|
+
|
|
345
|
+
google-drive:
|
|
346
|
+
description: "Google Drive - files, folders, search, sharing"
|
|
347
|
+
keywords: [google drive, gdrive, docs, sheets, files, cloud storage]
|
|
348
|
+
install:
|
|
349
|
+
mac: ["npx", "-y", "@anthropic/mcp-google-drive"]
|
|
350
|
+
wsl: ["npx", "-y", "@anthropic/mcp-google-drive"]
|
|
351
|
+
command: "npx"
|
|
352
|
+
args: ["-y", "@anthropic/mcp-google-drive"]
|
|
353
|
+
requires_api_key: true
|
|
354
|
+
env_var: "GOOGLE_CREDENTIALS"
|
|
355
|
+
env_instructions: "Create OAuth credentials at https://console.cloud.google.com/apis/credentials"
|
|
356
|
+
|
|
357
|
+
context7:
|
|
358
|
+
description: "Library documentation lookup - up-to-date docs for any package"
|
|
359
|
+
keywords: [documentation, docs, library, package, api reference, context7]
|
|
360
|
+
install:
|
|
361
|
+
mac: ["npx", "-y", "@upstash/context7-mcp"]
|
|
362
|
+
wsl: ["npx", "-y", "@upstash/context7-mcp"]
|
|
363
|
+
command: "npx"
|
|
364
|
+
args: ["-y", "@upstash/context7-mcp"]
|
|
365
|
+
requires_api_key: false
|
|
366
|
+
auto_start: true
|
|
367
|
+
|
|
368
|
+
# Discovery queue for unmatched requests
|
|
369
|
+
discovery_queue_path: ".mcp-gateway/discovery_queue.json"
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## .env.example
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# MCP Gateway Environment Variables
|
|
378
|
+
# Copy to .env and fill in your API keys
|
|
379
|
+
|
|
380
|
+
# === Required for LLM-powered matching ===
|
|
381
|
+
GROQ_API_KEY=gsk_your_groq_api_key_here
|
|
382
|
+
|
|
383
|
+
# === Default Auto-Start Servers (API keys required) ===
|
|
384
|
+
|
|
385
|
+
# Bright Data - https://brightdata.com/cp/api_tokens
|
|
386
|
+
# Required for: brightdata-scraper, brightdata-serp, brightdata-unlocker, brightdata-datasets
|
|
387
|
+
BRIGHTDATA_API_KEY=your_brightdata_api_key_here
|
|
388
|
+
|
|
389
|
+
# === Optional MCP Server API Keys ===
|
|
390
|
+
# Uncomment and fill in as needed
|
|
391
|
+
|
|
392
|
+
# GitHub - https://github.com/settings/tokens (repo scope)
|
|
393
|
+
#GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
394
|
+
|
|
395
|
+
# Slack - https://api.slack.com/apps (Bot Token)
|
|
396
|
+
#SLACK_TOKEN=xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
|
|
397
|
+
|
|
398
|
+
# Linear - https://linear.app/settings/api
|
|
399
|
+
#LINEAR_API_KEY=lin_api_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
400
|
+
|
|
401
|
+
# Notion - https://www.notion.so/my-integrations
|
|
402
|
+
#NOTION_TOKEN=secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
403
|
+
|
|
404
|
+
# PostgreSQL - Connection string
|
|
405
|
+
#POSTGRES_URL=postgresql://user:password@localhost:5432/database
|
|
406
|
+
|
|
407
|
+
# Sentry - https://sentry.io/settings/account/api/auth-tokens/
|
|
408
|
+
#SENTRY_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
409
|
+
|
|
410
|
+
# Google Drive - OAuth credentials JSON path
|
|
411
|
+
#GOOGLE_CREDENTIALS=/path/to/credentials.json
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## New Types
|
|
417
|
+
|
|
418
|
+
### `src/mcp_gateway/types.py` additions
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
class CapabilityRequestInput(BaseModel):
|
|
422
|
+
"""Input for gateway.request_capability."""
|
|
423
|
+
query: str = Field(min_length=1, description="Natural language capability request")
|
|
424
|
+
available_clis: list[str] | None = Field(default=None, description="CLIs Claude knows are available")
|
|
425
|
+
|
|
426
|
+
class CapabilityResolution(BaseModel):
|
|
427
|
+
"""Result of capability resolution."""
|
|
428
|
+
status: Literal["use_cli", "available", "provisioned", "needs_api_key", "not_available"]
|
|
429
|
+
|
|
430
|
+
# For use_cli
|
|
431
|
+
cli: str | None = None
|
|
432
|
+
cli_path: str | None = None
|
|
433
|
+
cli_help: str | None = None
|
|
434
|
+
cli_examples: list[str] | None = None
|
|
435
|
+
|
|
436
|
+
# For available/provisioned
|
|
437
|
+
server: str | None = None
|
|
438
|
+
new_tools: list[CapabilityCard] | None = None
|
|
439
|
+
|
|
440
|
+
# For needs_api_key
|
|
441
|
+
env_var: str | None = None
|
|
442
|
+
env_path: str | None = None
|
|
443
|
+
env_instructions: str | None = None
|
|
444
|
+
|
|
445
|
+
# For not_available
|
|
446
|
+
logged_for_discovery: bool = False
|
|
447
|
+
|
|
448
|
+
message: str
|
|
449
|
+
|
|
450
|
+
class EnvironmentInfo(BaseModel):
|
|
451
|
+
"""Environment information from Claude or detected."""
|
|
452
|
+
path: str | None = None
|
|
453
|
+
cwd: str | None = None
|
|
454
|
+
platform: Literal["mac", "wsl", "linux", "windows"] | None = None
|
|
455
|
+
detected_clis: list[str] = Field(default_factory=list)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
## New Gateway Tools
|
|
461
|
+
|
|
462
|
+
### `gateway.request_capability`
|
|
463
|
+
```python
|
|
464
|
+
Tool(
|
|
465
|
+
name="gateway.request_capability",
|
|
466
|
+
description=(
|
|
467
|
+
"Request a capability that may not be currently available. "
|
|
468
|
+
"The gateway will check installed CLIs, active servers, and dormant servers. "
|
|
469
|
+
"May install and start a new MCP server if available in manifest."
|
|
470
|
+
),
|
|
471
|
+
inputSchema={
|
|
472
|
+
"type": "object",
|
|
473
|
+
"properties": {
|
|
474
|
+
"query": {
|
|
475
|
+
"type": "string",
|
|
476
|
+
"description": "Natural language description of needed capability"
|
|
477
|
+
},
|
|
478
|
+
"available_clis": {
|
|
479
|
+
"type": "array",
|
|
480
|
+
"items": {"type": "string"},
|
|
481
|
+
"description": "Optional: CLIs you know are available in the environment"
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
"required": ["query"]
|
|
485
|
+
}
|
|
486
|
+
)
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### `gateway.sync_environment`
|
|
490
|
+
```python
|
|
491
|
+
Tool(
|
|
492
|
+
name="gateway.sync_environment",
|
|
493
|
+
description=(
|
|
494
|
+
"Sync environment information with the gateway. "
|
|
495
|
+
"Call this if the gateway's environment detection seems incorrect."
|
|
496
|
+
),
|
|
497
|
+
inputSchema={
|
|
498
|
+
"type": "object",
|
|
499
|
+
"properties": {
|
|
500
|
+
"platform": {
|
|
501
|
+
"type": "string",
|
|
502
|
+
"enum": ["mac", "wsl", "linux", "windows"]
|
|
503
|
+
},
|
|
504
|
+
"detected_clis": {
|
|
505
|
+
"type": "array",
|
|
506
|
+
"items": {"type": "string"},
|
|
507
|
+
"description": "CLIs confirmed to be installed"
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
)
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Implementation Flow
|
|
517
|
+
|
|
518
|
+
### Startup
|
|
519
|
+
```python
|
|
520
|
+
async def initialize(self):
|
|
521
|
+
# 1. Detect platform
|
|
522
|
+
self._platform = detect_platform() # mac, wsl, linux
|
|
523
|
+
|
|
524
|
+
# 2. Load manifest
|
|
525
|
+
self._manifest = load_manifest()
|
|
526
|
+
|
|
527
|
+
# 3. Probe environment for CLIs
|
|
528
|
+
self._detected_clis = await probe_clis(self._manifest.cli_alternatives)
|
|
529
|
+
|
|
530
|
+
# 4. Connect to auto_start servers (playwright, context7)
|
|
531
|
+
await self._connect_auto_start_servers()
|
|
532
|
+
|
|
533
|
+
# 5. Generate capability summary (includes detected CLIs)
|
|
534
|
+
self._capability_summary = await generate_capability_summary(
|
|
535
|
+
tools=self._client_manager.get_all_tools(),
|
|
536
|
+
detected_clis=self._detected_clis,
|
|
537
|
+
)
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### request_capability Flow
|
|
541
|
+
```python
|
|
542
|
+
async def request_capability(self, input_data: dict) -> CapabilityResolution:
|
|
543
|
+
parsed = CapabilityRequestInput.model_validate(input_data)
|
|
544
|
+
query = parsed.query
|
|
545
|
+
|
|
546
|
+
# 1. Check if active MCP server already has matching tools
|
|
547
|
+
if tools := self._search_active_tools(query):
|
|
548
|
+
return CapabilityResolution(
|
|
549
|
+
status="available",
|
|
550
|
+
message=f"Tools already available",
|
|
551
|
+
new_tools=tools
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
# 2. Check for matching CLI
|
|
555
|
+
if cli := self._match_cli(query):
|
|
556
|
+
if cli.name in self._detected_clis:
|
|
557
|
+
help_output = await self._get_cli_help(cli)
|
|
558
|
+
return CapabilityResolution(
|
|
559
|
+
status="use_cli",
|
|
560
|
+
cli=cli.name,
|
|
561
|
+
cli_path=self._cli_paths.get(cli.name),
|
|
562
|
+
cli_help=help_output,
|
|
563
|
+
cli_examples=cli.examples,
|
|
564
|
+
message=f"Use {cli.name} CLI via Bash tool"
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
# 3. Use BAML/Groq to match against manifest
|
|
568
|
+
match = await self._match_manifest(query)
|
|
569
|
+
|
|
570
|
+
if not match.matched:
|
|
571
|
+
# Log for discovery
|
|
572
|
+
await self._log_discovery_request(query)
|
|
573
|
+
return CapabilityResolution(
|
|
574
|
+
status="not_available",
|
|
575
|
+
logged_for_discovery=True,
|
|
576
|
+
message="No matching capability found. Request logged for future discovery."
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
server_config = self._manifest.servers[match.entry_name]
|
|
580
|
+
|
|
581
|
+
# 4. Check API key if required
|
|
582
|
+
if server_config.requires_api_key:
|
|
583
|
+
env_var = server_config.env_var
|
|
584
|
+
if not os.environ.get(env_var):
|
|
585
|
+
return CapabilityResolution(
|
|
586
|
+
status="needs_api_key",
|
|
587
|
+
server=match.entry_name,
|
|
588
|
+
env_var=env_var,
|
|
589
|
+
env_path=str(Path.cwd() / ".env"),
|
|
590
|
+
env_instructions=server_config.env_instructions,
|
|
591
|
+
message=f"Capability available but requires {env_var} to be set"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# 5. Install and start server
|
|
595
|
+
try:
|
|
596
|
+
await self._install_server(match.entry_name)
|
|
597
|
+
await self._start_server(match.entry_name)
|
|
598
|
+
|
|
599
|
+
new_tools = self._client_manager.get_tools_for_server(match.entry_name)
|
|
600
|
+
|
|
601
|
+
return CapabilityResolution(
|
|
602
|
+
status="provisioned",
|
|
603
|
+
server=match.entry_name,
|
|
604
|
+
new_tools=[self._to_capability_card(t) for t in new_tools],
|
|
605
|
+
message=f"Started {match.entry_name} MCP server with {len(new_tools)} tools"
|
|
606
|
+
)
|
|
607
|
+
except Exception as e:
|
|
608
|
+
return CapabilityResolution(
|
|
609
|
+
status="not_available",
|
|
610
|
+
message=f"Failed to provision {match.entry_name}: {e}"
|
|
611
|
+
)
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## Files to Create/Modify
|
|
617
|
+
|
|
618
|
+
| File | Action |
|
|
619
|
+
|------|--------|
|
|
620
|
+
| `src/mcp_gateway/manifest/__init__.py` | Create |
|
|
621
|
+
| `src/mcp_gateway/manifest/loader.py` | Create |
|
|
622
|
+
| `src/mcp_gateway/manifest/matcher.py` | Create |
|
|
623
|
+
| `src/mcp_gateway/manifest/installer.py` | Create |
|
|
624
|
+
| `src/mcp_gateway/manifest/environment.py` | Create |
|
|
625
|
+
| `src/mcp_gateway/manifest/manifest.yaml` | Create |
|
|
626
|
+
| `baml_src/capability_match.baml` | Create |
|
|
627
|
+
| `.env.example` | Create |
|
|
628
|
+
| `src/mcp_gateway/types.py` | Add new types |
|
|
629
|
+
| `src/mcp_gateway/tools/handlers.py` | Add new tools |
|
|
630
|
+
| `src/mcp_gateway/server.py` | Add manifest loading, env detection |
|
|
631
|
+
| `tests/test_manifest.py` | Create |
|
|
632
|
+
| `tests/test_capability_request.py` | Create |
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
## Updated Capability Summary
|
|
637
|
+
|
|
638
|
+
The L1 handshake summary will now include detected CLIs:
|
|
639
|
+
|
|
640
|
+
```
|
|
641
|
+
MCP Gateway capabilities:
|
|
642
|
+
• Browser Automation (playwright): Navigate, click, screenshot
|
|
643
|
+
• Documentation (context7): Library docs lookup
|
|
644
|
+
|
|
645
|
+
Detected CLIs (use via Bash): git, docker, node, python, aws
|
|
646
|
+
|
|
647
|
+
Use gateway.request_capability to discover more capabilities.
|
|
648
|
+
Use gateway.catalog_search to explore available tools.
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## Testing Plan
|
|
654
|
+
|
|
655
|
+
1. **Unit tests**: CLI detection, manifest loading, BAML matching
|
|
656
|
+
2. **Integration tests**:
|
|
657
|
+
- Request capability → CLI resolution
|
|
658
|
+
- Request capability → Server provisioning
|
|
659
|
+
- Request capability → API key needed response
|
|
660
|
+
3. **E2E test**: Full flow with Groq API
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## Execution Order
|
|
665
|
+
|
|
666
|
+
1. Create manifest module structure
|
|
667
|
+
2. Implement environment.py (CLI detection)
|
|
668
|
+
3. Implement loader.py (manifest parsing)
|
|
669
|
+
4. Add BAML capability_match.baml + regenerate client
|
|
670
|
+
5. Implement matcher.py (BAML-powered matching)
|
|
671
|
+
6. Implement installer.py (platform-specific install)
|
|
672
|
+
7. Add new types to types.py
|
|
673
|
+
8. Add new tools to handlers.py
|
|
674
|
+
9. Update server.py initialization
|
|
675
|
+
10. Create .env.example
|
|
676
|
+
11. Add tests
|
|
677
|
+
12. Run full test suite
|