agent-mcp-gateway 0.2.1__py3-none-any.whl

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.
@@ -0,0 +1,1330 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-mcp-gateway
3
+ Version: 0.2.1
4
+ Summary: An MCP gateway that aggregates your existing MCP servers and lets you define which servers and individual tools each agent or subagent can access. Solves Claude Code's MCP context window waste where all tool definitions load upfront instead of being discovered when actually needed.
5
+ Project-URL: Homepage, https://github.com/roddutra/agent-mcp-gateway
6
+ Project-URL: Documentation, https://github.com/roddutra/agent-mcp-gateway#readme
7
+ Project-URL: Repository, https://github.com/roddutra/agent-mcp-gateway
8
+ Project-URL: Issues, https://github.com/roddutra/agent-mcp-gateway/issues
9
+ Author: Rodrigo Franken Dutra
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,fastmcp,gateway,mcp,model-context-protocol,proxy
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: fastmcp<3.0,>=2.13.0.1
21
+ Requires-Dist: watchdog<7.0,>=6.0.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Agent MCP Gateway
25
+
26
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) gateway that aggregates multiple MCP servers and provides policy-based access control for agents and subagents. Solves Claude Code's MCP context window waste by enabling on-demand tool discovery instead of loading all tool definitions upfront.
27
+
28
+ ## Status
29
+
30
+ - ✅ **M0: Foundation** - Configuration, policy engine, audit logging, `list_servers` tool
31
+ - ✅ **M1: Core** - Proxy infrastructure, `get_server_tools`, `execute_tool`, middleware, metrics, hot reload, OAuth support
32
+ - 🚧 **M2: Production** - HTTP transport, health checks (planned)
33
+ - 🚧 **M3: DX** - Single-agent mode, config validation CLI, Docker (planned)
34
+
35
+ **Current Version:** M1-Core Complete (with OAuth)
36
+
37
+ ## Table of Contents
38
+
39
+ - [Overview](#overview)
40
+ - [Installation](#installation)
41
+ - [Quick Start](#quick-start)
42
+ - [Command-Line Options](#command-line-options)
43
+ - [Configuration File Discovery](#configuration-file-discovery)
44
+ - [Configuration](#configuration)
45
+ - [Usage](#usage)
46
+ - [Gateway Tools](#gateway-tools)
47
+ - [Security Considerations](#security-considerations)
48
+ - [Troubleshooting](#troubleshooting)
49
+ - [Testing](#testing)
50
+ - [Development](#development)
51
+ - [Architecture](#architecture)
52
+ - [Future Features](#future-features)
53
+ - [Documentation](#documentation)
54
+ - [Contributing](#contributing)
55
+ - [License](#license)
56
+ - [Support](#support)
57
+ - [Acknowledgments](#acknowledgments)
58
+
59
+ ## Overview
60
+
61
+ ### The Problem
62
+
63
+ When multiple MCP servers are configured in development environments (Claude Code, Cursor, VS Code), all tool definitions from all servers load into every agent's and subagent's context window at startup:
64
+
65
+ - 5,000-50,000+ tokens consumed upfront
66
+ - 80-95% of loaded tools never used by individual agents
67
+ - Context needed for actual work gets wasted on unused tool definitions
68
+
69
+ ### The Solution
70
+
71
+ The Agent MCP Gateway acts as a single MCP server that proxies to multiple downstream MCP servers based on configurable per-agent rules:
72
+
73
+ - **3 gateway tools** load at startup (~400 tokens)
74
+ - Agents discover and request specific tools on-demand
75
+ - **90%+ context reduction**
76
+ - Policy-based access control per agent/subagent
77
+
78
+ ### How It Works
79
+
80
+ ![Agent MCP Gateway Architecture](https://raw.githubusercontent.com/roddutra/agent-mcp-gateway/main/docs/diagram-snippet.png)
81
+
82
+ The gateway sits between agents and downstream MCP servers, exposing only 3 lightweight tools. When an agent needs specific functionality, it discovers available servers and tools through the gateway, which filters visibility based on policy rules - agents only see servers and tools they have access to. This reduces each agent's context window to only relevant tools, while the gateway handles proxying authorized requests to downstream servers.
83
+
84
+ **[View detailed diagram with examples →](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/diagram-full.png)** (includes downstream servers, tools, and gateway rules examples)
85
+
86
+ ### Key Features
87
+
88
+ - ✅ **On-Demand Tool Discovery** - Load tool definitions only when needed
89
+ - ✅ **Per-Agent Access Control** - Configure which servers/tools each agent can access
90
+ - ✅ **Easy Agent Integration** - Simple template to add gateway support to any agent ([see guide](#3-configure-your-agents))
91
+ - ✅ **Deny-Before-Allow Policies** - Explicit deny rules take precedence
92
+ - ✅ **Wildcard Support** - Pattern matching for tool names (`get_*`, `*_user`)
93
+ - ✅ **Session Isolation** - Concurrent requests don't interfere
94
+ - ✅ **Transparent Proxying** - Downstream servers unaware of gateway
95
+ - ✅ **Audit Logging** - All operations logged for monitoring
96
+ - ✅ **Performance Metrics** - Track latency and error rates per agent/operation
97
+ - ✅ **Hot Configuration Reload** - Update rules/servers without restart
98
+ - ✅ **Thread-Safe Operations** - Safe concurrent access during reloads
99
+ - ✅ **Diagnostic Tools** - Health monitoring via `get_gateway_status` (debug mode only)
100
+
101
+ ## Installation
102
+
103
+ ```bash
104
+ # Creates ~/.config/agent-mcp-gateway/ with template configuration files
105
+ uvx agent-mcp-gateway --init
106
+ ```
107
+
108
+ This generates two template files ready to customize:
109
+ - `mcp.json` - Your downstream MCP servers (Brave, Postgres, etc.)
110
+ - `mcp-gateway-rules.json` - Per-agent access policies (who can use which servers/tools)
111
+
112
+ **For local development:** See [Development](#development) section.
113
+
114
+ ## Quick Start
115
+
116
+ ### 1. Configure Gateway Files
117
+
118
+ After running `uvx agent-mcp-gateway --init` (see [Installation](#installation)), edit the generated template files:
119
+
120
+ ```bash
121
+ # Define your downstream MCP servers
122
+ nano ~/.config/agent-mcp-gateway/.mcp.json
123
+
124
+ # Define agent access policies
125
+ nano ~/.config/agent-mcp-gateway/.mcp-gateway-rules.json
126
+ ```
127
+
128
+ See [Configuration](#configuration) section for detailed examples and [Configuration File Discovery](#configuration-file-discovery) for alternative file locations.
129
+
130
+ ### 2. Add Gateway to Your MCP Client
131
+
132
+ **Claude Code CLI:**
133
+
134
+ ```bash
135
+ claude mcp add agent-mcp-gateway uvx agent-mcp-gateway
136
+ ```
137
+
138
+ **Manual configuration:**
139
+
140
+ ```json
141
+ {
142
+ "mcpServers": {
143
+ "agent-mcp-gateway": {
144
+ "command": "uvx",
145
+ "args": ["agent-mcp-gateway"],
146
+ "env": {
147
+ "GATEWAY_MCP_CONFIG": "~/.config/agent-mcp-gateway/.mcp.json",
148
+ "GATEWAY_RULES": "~/.config/agent-mcp-gateway/.mcp-gateway-rules.json",
149
+ "GATEWAY_DEFAULT_AGENT": "developer"
150
+ }
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ **Note:** The `env` variables are optional if using default config locations. See [Environment Variables Reference](#4-environment-variables-reference) for all options.
157
+
158
+ ### 3. Configure Your Agents
159
+
160
+ The gateway's tool descriptions are self-documenting, but for proper access control you should configure how your agents identify themselves. Choose the approach that fits your use case:
161
+
162
+ #### Approach 1: Multi-Agent Mode (Recommended)
163
+
164
+ For different agents with different permissions, configure each agent to pass its identity.
165
+
166
+ **Add this to your agent's system prompt** (e.g., `CLAUDE.md`, `.claude/agents/agent-name.md`):
167
+
168
+ ```markdown
169
+ ## MCP Gateway Access
170
+
171
+ **Available Tools (via agent-mcp-gateway):**
172
+
173
+ You have access to MCP servers through the agent-mcp-gateway. The specific servers and tools available to you are determined by the gateway's access control rules.
174
+
175
+ **Tool Discovery Process:**
176
+
177
+ When you need to use tools from downstream MCP servers:
178
+ 1. Use `agent_id: "YOUR_AGENT_NAME"` in ALL gateway tool calls for proper access control
179
+ 2. Call `list_servers` to discover which servers you have access to
180
+ 3. Call `get_server_tools` with the specific server name to discover available tools
181
+ 4. Use `execute_tool` to invoke tools with appropriate parameters
182
+ 5. If you cannot access a tool you need, immediately notify the user
183
+
184
+ **Important:** Always include `agent_id: "YOUR_AGENT_NAME"` in your gateway tool calls. This ensures proper access control and audit logging.
185
+ ```
186
+
187
+ Replace `YOUR_AGENT_NAME` with your agent's identifier (e.g., "researcher", "backend", "admin").
188
+
189
+ **Examples:** See [`.claude/agents/researcher.md`](https://github.com/roddutra/agent-mcp-gateway/blob/main/.claude/agents/researcher.md) and [`.claude/agents/mcp-developer.md`](https://github.com/roddutra/agent-mcp-gateway/blob/main/.claude/agents/mcp-developer.md) for complete configuration examples.
190
+
191
+ #### Approach 2: Single-Agent Mode
192
+
193
+ For simpler setups where all agents should have the same permissions, or when using MCP clients without system prompt configuration (e.g., Claude Desktop), configure a default agent using either method:
194
+
195
+ **Option A: Environment Variable**
196
+ ```bash
197
+ # Set in your MCP client configuration
198
+ export GATEWAY_DEFAULT_AGENT=developer
199
+ ```
200
+ **Note:** The agent specified (e.g., "developer") must exist in your `.mcp-gateway-rules.json` file with appropriate permissions.
201
+
202
+ **Option B: "default" Agent in Rules**
203
+ ```json
204
+ {
205
+ "agents": {
206
+ "default": {
207
+ "allow": {
208
+ "servers": ["*"]
209
+ }
210
+ }
211
+ },
212
+ "defaults": {
213
+ "deny_on_missing_agent": false
214
+ }
215
+ }
216
+ ```
217
+ **Note:** Allowing all servers (`"servers": ["*"]`) without specifying tool restrictions grants access to all tools on all servers.
218
+
219
+ With either approach, agents can omit `agent_id` in tool calls - the gateway uses your configured default agent automatically.
220
+
221
+ ## Command-Line Options
222
+
223
+ ```bash
224
+ # Show version
225
+ agent-mcp-gateway --version
226
+
227
+ # Initialize config directory (first-time setup)
228
+ agent-mcp-gateway --init
229
+
230
+ # Enable debug mode (exposes get_gateway_status diagnostic tool)
231
+ agent-mcp-gateway --debug
232
+
233
+ # Show help
234
+ agent-mcp-gateway --help
235
+ ```
236
+
237
+ ## Configuration File Discovery
238
+
239
+ The gateway searches for configuration files in this order:
240
+
241
+ ### MCP Server Config (.mcp.json)
242
+ 1. `GATEWAY_MCP_CONFIG` environment variable (if set)
243
+ 2. `.mcp.json` in current directory
244
+ 3. `~/.config/agent-mcp-gateway/.mcp.json` (home directory)
245
+ 4. `./config/.mcp.json` (fallback)
246
+
247
+ ### Gateway Rules (.mcp-gateway-rules.json)
248
+ 1. `GATEWAY_RULES` environment variable (if set)
249
+ 2. `.mcp-gateway-rules.json` in current directory
250
+ 3. `~/.config/agent-mcp-gateway/.mcp-gateway-rules.json` (home directory)
251
+ 4. `./config/.mcp-gateway-rules.json` (fallback)
252
+
253
+ **Tip:** Use `agent-mcp-gateway --init` to create the home directory configs on first run.
254
+
255
+ ## Configuration
256
+
257
+ The gateway requires two configuration files:
258
+
259
+ ### 1. MCP Servers Configuration
260
+
261
+ **File:** `mcp.json` (searched in [multiple locations](#configuration-file-discovery))
262
+
263
+ Defines the downstream MCP servers the gateway will proxy to. Uses the standard MCP config format compatible with Claude Code and other coding agents:
264
+
265
+ ```json
266
+ {
267
+ "mcpServers": {
268
+ "brave-search": {
269
+ "description": "Web search via Brave Search API",
270
+ "command": "npx",
271
+ "args": ["-y", "@modelcontextprotocol/server-brave-search"],
272
+ "env": {
273
+ "BRAVE_API_KEY": "${BRAVE_API_KEY}"
274
+ }
275
+ },
276
+ "postgres": {
277
+ "description": "PostgreSQL database access and query execution",
278
+ "command": "uvx",
279
+ "args": ["mcp-server-postgres"],
280
+ "env": {
281
+ "DATABASE_URL": "${DATABASE_URL}"
282
+ }
283
+ },
284
+ "remote-server": {
285
+ "description": "Custom remote API integration",
286
+ "url": "https://example.com/mcp",
287
+ "transport": "http",
288
+ "headers": {
289
+ "Authorization": "Bearer ${API_TOKEN}"
290
+ }
291
+ }
292
+ }
293
+ }
294
+ ```
295
+
296
+ **Server Descriptions (Recommended):**
297
+ Adding a `description` field to each server helps AI agents understand what each server provides and when to use it. Descriptions are always returned by `list_servers`, enabling agents to make informed decisions about which servers to query for tools. While optional, descriptions significantly improve agent tool discovery and decision-making.
298
+
299
+ **Supported Transports:**
300
+ - `stdio` - Local servers via npx/uvx (specified with `command` + `args`)
301
+ - `http` - Remote HTTP servers (specified with `url`)
302
+
303
+ **Environment Variables:**
304
+ - Use `${VAR_NAME}` syntax for environment variable substitution
305
+ - Set variables before running: `export BRAVE_API_KEY=your-key`
306
+
307
+ **Important - GUI Applications (Claude Desktop, etc.):**
308
+ If you use `${VAR_NAME}` syntax in `.mcp.json`, note that macOS GUI applications run in isolated environments without access to your shell's environment variables. For Claude Desktop and similar apps, add API keys to the gateway's `env` object in your MCP client configuration:
309
+
310
+ ```json
311
+ {
312
+ "mcpServers": {
313
+ "agent-mcp-gateway": {
314
+ "command": "uvx",
315
+ "args": ["agent-mcp-gateway"],
316
+ "env": {
317
+ "BRAVE_API_KEY": "your-actual-key-here",
318
+ "DATABASE_URL": "postgresql://...",
319
+ "GATEWAY_DEFAULT_AGENT": "claude-desktop"
320
+ }
321
+ }
322
+ }
323
+ }
324
+ ```
325
+
326
+ (If you hardcode values directly in `.mcp.json` without `${VAR_NAME}` syntax, this is not necessary.)
327
+
328
+ ### 2. Gateway Rules Configuration
329
+
330
+ **File:** `mcp-gateway-rules.json` (searched in [multiple locations](#configuration-file-discovery))
331
+
332
+ Defines per-agent access policies using deny-before-allow precedence:
333
+
334
+ ```json
335
+ {
336
+ "agents": {
337
+ "researcher": {
338
+ "allow": {
339
+ "servers": ["brave-search", "context7"],
340
+ "tools": {
341
+ "brave-search": ["brave_web_search"]
342
+ }
343
+ }
344
+ },
345
+ "backend": {
346
+ "allow": {
347
+ "servers": ["postgres", "laravel-boost"],
348
+ "tools": {
349
+ "postgres": ["query", "list_tables", "list_schemas"],
350
+ "laravel-boost": ["get_*", "list_*", "read_*", "database_*", "search_*"]
351
+ }
352
+ },
353
+ "deny": {
354
+ "tools": {
355
+ "postgres": ["drop_*", "delete_*"],
356
+ "laravel-boost": ["database_query", "tinker"]
357
+ }
358
+ }
359
+ },
360
+ "admin": {
361
+ "allow": {
362
+ "servers": ["*"],
363
+ "tools": {
364
+ "brave-search": ["brave_web_search"]
365
+ }
366
+ },
367
+ "deny": {
368
+ "servers": ["notion"],
369
+ "tools": {
370
+ "playwright": ["browser_type"]
371
+ }
372
+ }
373
+ },
374
+ "claude-desktop": {
375
+ "allow": {
376
+ "servers": ["context7", "brave-search", "notion", "playwright"]
377
+ },
378
+ "deny": {
379
+ "tools": {
380
+ "playwright": ["browser_type", "browser_close_all", "launch_*"]
381
+ }
382
+ }
383
+ },
384
+ "default": {
385
+ "deny": {
386
+ "servers": ["*"]
387
+ }
388
+ }
389
+ },
390
+ "defaults": {
391
+ "deny_on_missing_agent": false
392
+ }
393
+ }
394
+ ```
395
+
396
+ **Agent Examples Explained:**
397
+
398
+ **researcher** - Demonstrates implicit grant + explicit allow:
399
+ - `brave-search`: ONLY `brave_web_search` tool (explicit allow narrows access)
400
+ - `context7`: ALL tools (implicit grant - server allowed, no tool rules specified)
401
+
402
+ **backend** - Demonstrates wildcard allows with deny-before-allow precedence:
403
+ - `postgres`: ONLY `query`, `list_tables`, `list_schemas` (explicit allows); deny rules serve as safety net
404
+ - `laravel-boost`: Wildcard allows (`get_*`, `list_*`, `read_*`, `database_*`, `search_*`) grant broad access, BUT `database_query` explicitly denied despite matching `database_*` wildcard (deny wins), and `tinker` blocked as safety measure
405
+
406
+ **admin** - Demonstrates server wildcard + mixed access patterns:
407
+ - `notion`: DENIED (server-level deny overrides wildcard server allow)
408
+ - `brave-search`: ONLY `brave_web_search` (explicit restriction on one server)
409
+ - `playwright`: ALL tools EXCEPT `browser_type` (implicit grant with explicit deny)
410
+ - All other servers: ALL tools (implicit grant - no tool rules specified)
411
+
412
+ **claude-desktop** - Demonstrates implicit grant with multiple deny types:
413
+ - `context7`, `brave-search`, `notion`: ALL tools (implicit grant)
414
+ - `playwright`: ALL tools EXCEPT `browser_type`, `browser_close_all`, and tools matching `launch_*` (implicit grant with explicit + wildcard denies)
415
+
416
+ **default** - Principle of least privilege:
417
+ - Used as fallback when `agent_id` not provided and `deny_on_missing_agent` is `false`
418
+ - Denies all servers by default; use `GATEWAY_DEFAULT_AGENT` environment variable to specify a different default agent
419
+
420
+ **Policy Precedence Order:**
421
+ 1. Explicit deny rules (highest priority)
422
+ 2. Wildcard deny rules
423
+ 3. Explicit allow rules
424
+ 4. Wildcard allow rules
425
+ 5. Implicit grant (if server allowed but no tool rules specified)
426
+ 6. Default policy (deny)
427
+
428
+ **Implicit Grant Behavior:**
429
+ - If agent has server access and no `allow.tools.{server}` entry, all tools from that server are implicitly granted
430
+ - `allow.tools.{server}` entries narrow access to specified tools only
431
+ - `deny.tools.{server}` entries filter out specific tools (evaluated in steps 1-2)
432
+ - Rules are server-specific and don't affect other servers
433
+
434
+ **Configuration Flexibility:**
435
+ - Rules can reference servers not currently in `.mcp.json`
436
+ - Undefined server references treated as warnings (not errors)
437
+ - Allows keeping rules for temporarily removed servers
438
+ - Hot reload applies changes immediately without restart
439
+
440
+ **Wildcard Patterns:**
441
+ - `*` - Matches everything
442
+ - `get_*` - Matches tools starting with "get_"
443
+ - `*_user` - Matches tools ending with "_user"
444
+
445
+ **Agent Naming:**
446
+ - Use hierarchical names: `team.role` (e.g., `backend.database`, `frontend.ui`)
447
+ - Alphanumeric characters, hyphens, underscores, and dots allowed
448
+ - **Configure your agents** to pass their identity: See [Configure Your Agents](#3-configure-your-agents)
449
+
450
+ ### Configuration Validation
451
+
452
+ The gateway validates configurations at startup and during hot reload. Example output:
453
+
454
+ ```
455
+ ✓ Configuration loaded from .mcp.json
456
+ ⚠ Warning: Agent 'researcher' references undefined server 'unknown-server'
457
+ ℹ These rules will be ignored until the server is added
458
+ ```
459
+
460
+ **Validation Behavior:**
461
+ - Structural errors (invalid JSON, missing required fields) → Fail startup/reload
462
+ - Undefined server references → Log warnings, continue with valid rules
463
+ - Policy conflicts → Deny-before-allow precedence resolves automatically
464
+
465
+ ### 3. OAuth Support for Downstream Servers
466
+
467
+ OAuth-protected downstream servers (Notion, GitHub) are automatically supported via auto-detection when servers return HTTP 401. The gateway uses FastMCP's OAuth support to handle authentication flows transparently - browser opens once for initial authentication, then tokens are cached for future use. See [OAuth User Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/oauth-user-guide.md) for detailed setup and troubleshooting.
468
+
469
+ **OAuth Limitations:**
470
+
471
+ The gateway supports OAuth servers that implement **Dynamic Client Registration (RFC 7591)**.
472
+
473
+ - ✅ **Supported:** OAuth with auto-detection (e.g., Notion MCP)
474
+ - ❌ **Not Supported:** OAuth with pre-registered apps (e.g., GitHub OAuth flow)
475
+ - 💡 **For GitHub MCP:** Use Personal Access Token instead
476
+
477
+ **GitHub MCP with PAT Example:**
478
+ ```json
479
+ {
480
+ "mcpServers": {
481
+ "github": {
482
+ "url": "https://api.githubcopilot.com/mcp/",
483
+ "headers": {
484
+ "Authorization": "Bearer ${GITHUB_PAT}"
485
+ }
486
+ }
487
+ }
488
+ }
489
+ ```
490
+
491
+ For detailed OAuth setup and troubleshooting, see [OAuth User Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/oauth-user-guide.md).
492
+
493
+ ### 4. Environment Variables Reference
494
+
495
+ | Variable | Description | Default | Example |
496
+ |----------|-------------|---------|---------|
497
+ | `GATEWAY_MCP_CONFIG` | Path to MCP servers configuration file | `.mcp.json`, fallback: `./config/.mcp.json` | `export GATEWAY_MCP_CONFIG=./custom.json` |
498
+ | `GATEWAY_RULES` | Path to gateway rules configuration file | `.mcp-gateway-rules.json`, fallback: `./config/.mcp-gateway-rules.json` | `export GATEWAY_RULES=~/.claude/rules.json` |
499
+ | `GATEWAY_DEFAULT_AGENT` | Default agent identity when `agent_id` not provided (optional) | None | `export GATEWAY_DEFAULT_AGENT=developer` |
500
+ | `GATEWAY_DEBUG` | Enable debug mode to expose `get_gateway_status` tool | `false` | `export GATEWAY_DEBUG=true` |
501
+ | `GATEWAY_AUDIT_LOG` | Path to audit log file | `~/.cache/agent-mcp-gateway/logs/audit.jsonl` | `export GATEWAY_AUDIT_LOG=./audit.jsonl` |
502
+ | `GATEWAY_TRANSPORT` | Transport protocol (stdio or http) | `stdio` | `export GATEWAY_TRANSPORT=stdio` |
503
+ | `GATEWAY_INIT_STRATEGY` | Initialization strategy (eager or lazy) | `eager` | `export GATEWAY_INIT_STRATEGY=eager` |
504
+
505
+ **Note on GUI Applications:** macOS GUI applications (Claude Desktop, etc.) run in isolated environments without access to shell environment variables. If using `${VAR_NAME}` syntax in `.mcp.json`, add required API keys to the gateway's `env` object in your MCP client configuration.
506
+
507
+ ## Usage
508
+
509
+ The gateway runs automatically when your MCP client starts. See [Quick Start](#quick-start) for adding it to your MCP client configuration.
510
+
511
+ **Custom configuration paths** can be specified via environment variables in your MCP client config:
512
+
513
+ ```json
514
+ {
515
+ "mcpServers": {
516
+ "agent-mcp-gateway": {
517
+ "command": "uvx",
518
+ "args": ["agent-mcp-gateway"],
519
+ "env": {
520
+ "GATEWAY_MCP_CONFIG": "/path/to/custom-mcp.json",
521
+ "GATEWAY_RULES": "/path/to/custom-rules.json"
522
+ }
523
+ }
524
+ }
525
+ }
526
+ ```
527
+
528
+ See [Environment Variables Reference](#4-environment-variables-reference) for all available options.
529
+
530
+ ### Startup Output
531
+
532
+ ```
533
+ Loading MCP server configuration from: .mcp.json
534
+ Loading gateway rules from: .mcp-gateway-rules.json
535
+ Audit log will be written to: ~/.cache/agent-mcp-gateway/logs/audit.jsonl
536
+
537
+ Initializing proxy connections to downstream servers...
538
+ - 2 proxy client(s) initialized
539
+ * brave-search: ready
540
+ * postgres: ready
541
+ - Metrics collector initialized
542
+ - Access control middleware registered
543
+
544
+ Agent MCP Gateway initialized successfully
545
+ - 2 MCP server(s) configured
546
+ - 3 agent(s) configured
547
+ - Default policy: deny unknown agents
548
+ - 3 gateway tools available: list_servers, get_server_tools, execute_tool
549
+ (4 tools if GATEWAY_DEBUG=true: includes get_gateway_status)
550
+
551
+ Gateway is ready. Running with stdio transport...
552
+ ```
553
+
554
+ ## Gateway Tools
555
+
556
+ The gateway exposes exactly 3 tools to agents. All tools accept an optional `agent_id` parameter for access control. When `agent_id` is not provided, the gateway uses a fallback chain to determine agent identity (see [Agent Identity Modes](#agent-identity-modes)).
557
+
558
+ **For Agent Developers:** To configure your agents to properly use these gateway tools with access control, see [Configure Your Agents](#3-configure-your-agents).
559
+
560
+ ### 1. `list_servers`
561
+
562
+ Lists MCP servers available to the calling agent based on policy rules.
563
+
564
+ **Parameters:**
565
+ - `agent_id` (string, optional) - Identifier of the agent making the request (see [Agent Identity Modes](#agent-identity-modes))
566
+ - `include_metadata` (boolean, optional) - Include technical details like transport, command, and url (default: false)
567
+
568
+ **Returns:**
569
+ ```json
570
+ [
571
+ {
572
+ "name": "brave-search",
573
+ "description": "Web search via Brave Search API"
574
+ },
575
+ {
576
+ "name": "postgres",
577
+ "description": "PostgreSQL database access and query execution"
578
+ }
579
+ ]
580
+ ```
581
+
582
+ **With `include_metadata=true`:**
583
+ ```json
584
+ [
585
+ {
586
+ "name": "brave-search",
587
+ "description": "Web search via Brave Search API",
588
+ "transport": "stdio",
589
+ "command": "npx"
590
+ },
591
+ {
592
+ "name": "postgres",
593
+ "description": "PostgreSQL database access and query execution",
594
+ "transport": "stdio",
595
+ "command": "uvx"
596
+ }
597
+ ]
598
+ ```
599
+
600
+ **Note:** Server descriptions are always included (when configured in `.mcp.json`) to help agents understand what each server provides. The `include_metadata` flag only controls whether technical details (transport, command, url) are included.
601
+
602
+ **Example:**
603
+ ```python
604
+ # Basic usage - returns names and descriptions
605
+ result = await client.call_tool("list_servers", {
606
+ "agent_id": "researcher"
607
+ })
608
+
609
+ # With technical metadata
610
+ result = await client.call_tool("list_servers", {
611
+ "agent_id": "researcher",
612
+ "include_metadata": True
613
+ })
614
+ ```
615
+
616
+ ### 2. `get_server_tools`
617
+
618
+ Retrieves tool definitions from a specific MCP server, filtered by agent permissions.
619
+
620
+ **Parameters:**
621
+ - `agent_id` (string, optional) - Identifier of the agent (see [Agent Identity Modes](#agent-identity-modes))
622
+ - `server` (string, required) - Name of the downstream MCP server
623
+ - `names` (string, optional) - Comma-separated list of tool names (e.g., `"tool1,tool2,tool3"`) or single tool name
624
+ - `pattern` (string, optional) - Wildcard pattern for tool names (e.g., `"get_*"`)
625
+ - `max_schema_tokens` (integer, optional) - Token budget limit for schemas
626
+
627
+ **Returns:**
628
+ ```json
629
+ {
630
+ "tools": [
631
+ {
632
+ "name": "brave_web_search",
633
+ "description": "Search the web using Brave Search",
634
+ "inputSchema": {
635
+ "type": "object",
636
+ "properties": {
637
+ "query": {"type": "string"}
638
+ },
639
+ "required": ["query"]
640
+ }
641
+ }
642
+ ],
643
+ "server": "brave-search",
644
+ "total_available": 5,
645
+ "returned": 1,
646
+ "tokens_used": 150
647
+ }
648
+ ```
649
+
650
+ **Example:**
651
+ ```python
652
+ # Get all allowed tools
653
+ tools = await client.call_tool("get_server_tools", {
654
+ "agent_id": "researcher",
655
+ "server": "brave-search"
656
+ })
657
+
658
+ # Get specific tools by name (comma-separated)
659
+ tools = await client.call_tool("get_server_tools", {
660
+ "agent_id": "researcher",
661
+ "server": "brave-search",
662
+ "names": "brave_web_search,brave_local_search"
663
+ })
664
+
665
+ # Get specific tools by pattern
666
+ tools = await client.call_tool("get_server_tools", {
667
+ "agent_id": "backend",
668
+ "server": "postgres",
669
+ "pattern": "get_*"
670
+ })
671
+
672
+ # Limit token usage
673
+ tools = await client.call_tool("get_server_tools", {
674
+ "agent_id": "researcher",
675
+ "server": "brave-search",
676
+ "max_schema_tokens": 1000
677
+ })
678
+ ```
679
+
680
+ ### 3. `execute_tool`
681
+
682
+ Executes a tool on a downstream MCP server with transparent result forwarding.
683
+
684
+ **Parameters:**
685
+ - `agent_id` (string, optional) - Identifier of the agent (see [Agent Identity Modes](#agent-identity-modes))
686
+ - `server` (string, required) - Name of the downstream MCP server
687
+ - `tool` (string, required) - Name of the tool to execute
688
+ - `args` (object, required) - Arguments to pass to the tool
689
+ - `timeout_ms` (integer, optional) - Timeout in milliseconds
690
+
691
+ **Returns:**
692
+ ```json
693
+ {
694
+ "content": [
695
+ {
696
+ "type": "text",
697
+ "text": "Search results: ..."
698
+ }
699
+ ],
700
+ "isError": false
701
+ }
702
+ ```
703
+
704
+ **Example:**
705
+ ```python
706
+ # Execute a tool
707
+ result = await client.call_tool("execute_tool", {
708
+ "agent_id": "researcher",
709
+ "server": "brave-search",
710
+ "tool": "brave_web_search",
711
+ "args": {
712
+ "query": "FastMCP documentation"
713
+ }
714
+ })
715
+
716
+ # With timeout
717
+ result = await client.call_tool("execute_tool", {
718
+ "agent_id": "backend",
719
+ "server": "postgres",
720
+ "tool": "query",
721
+ "args": {
722
+ "sql": "SELECT * FROM users LIMIT 10"
723
+ },
724
+ "timeout_ms": 5000
725
+ })
726
+ ```
727
+
728
+ ### 4. `get_gateway_status` (Debug Mode Only)
729
+
730
+ Returns comprehensive gateway health and diagnostics information.
731
+
732
+ **Important:** This tool is only available when debug mode is enabled (via `GATEWAY_DEBUG=true` environment variable or `--debug` CLI flag). See [Security Considerations](#security-considerations) for details.
733
+
734
+ **Parameters:**
735
+ - `agent_id` (string, optional) - Identifier of the agent (see [Agent Identity Modes](#agent-identity-modes))
736
+
737
+ **Returns:**
738
+ ```json
739
+ {
740
+ "reload_status": {
741
+ "mcp_config": {
742
+ "last_attempt": "2025-10-30T10:30:00Z",
743
+ "last_success": "2025-10-30T10:30:00Z",
744
+ "last_error": null,
745
+ "attempt_count": 1,
746
+ "success_count": 1
747
+ },
748
+ "gateway_rules": {
749
+ "last_attempt": "2025-10-30T10:35:00Z",
750
+ "last_success": "2025-10-30T10:35:00Z",
751
+ "last_error": null,
752
+ "attempt_count": 2,
753
+ "success_count": 2,
754
+ "last_warnings": []
755
+ }
756
+ },
757
+ "policy_state": {
758
+ "total_agents": 3,
759
+ "agent_ids": ["researcher", "backend", "admin"],
760
+ "defaults": {"deny_on_missing_agent": true}
761
+ },
762
+ "available_servers": ["brave-search", "postgres"],
763
+ "config_paths": {
764
+ "mcp_config": "/path/to/.mcp.json",
765
+ "gateway_rules": "/path/to/.mcp-gateway-rules.json"
766
+ },
767
+ "message": "Gateway is operational. Check reload_status for hot reload health."
768
+ }
769
+ ```
770
+
771
+ **Example:**
772
+ ```python
773
+ # Check gateway health and reload status (requires GATEWAY_DEBUG=true)
774
+ status = await client.call_tool("get_gateway_status", {
775
+ "agent_id": "admin"
776
+ })
777
+
778
+ # Verify last reload was successful
779
+ if status["reload_status"]["gateway_rules"]["last_error"]:
780
+ print("Warning: Last rule reload failed!")
781
+ ```
782
+
783
+ ### Error Handling
784
+
785
+ All tools return structured errors with clear messages:
786
+
787
+ ```json
788
+ {
789
+ "error": {
790
+ "code": "DENIED_BY_POLICY",
791
+ "message": "Agent 'frontend' denied access to tool 'drop_table'",
792
+ "rule": "agents.frontend.deny.tools.postgres[0]"
793
+ }
794
+ }
795
+ ```
796
+
797
+ **Error Codes:**
798
+ - `DENIED_BY_POLICY` - Agent lacks permission
799
+ - `SERVER_UNAVAILABLE` - Downstream server unreachable
800
+ - `TOOL_NOT_FOUND` - Requested tool doesn't exist
801
+ - `TIMEOUT` - Operation exceeded time limit
802
+ - `INVALID_AGENT_ID` - Missing or unknown agent identifier
803
+ - `FALLBACK_AGENT_NOT_IN_RULES` - Configured fallback agent not found in gateway rules
804
+ - `NO_FALLBACK_CONFIGURED` - No agent_id provided and no fallback agent configured
805
+
806
+ ### Complete Workflow Example
807
+
808
+ Here's a minimal working example showing the typical gateway workflow:
809
+
810
+ ```python
811
+ from fastmcp import Client
812
+
813
+ async def gateway_workflow():
814
+ async with Client('agent-mcp-gateway') as client:
815
+ # 1. Discover available servers
816
+ servers = await client.call_tool('list_servers', {
817
+ 'agent_id': 'researcher'
818
+ })
819
+ # Response: [{"name": "brave-search", "description": "Web search..."}]
820
+
821
+ # 2. Get tools from specific server
822
+ tools = await client.call_tool('get_server_tools', {
823
+ 'agent_id': 'researcher',
824
+ 'server': 'brave-search'
825
+ })
826
+ # Response: {"tools": [...], "server": "brave-search", ...}
827
+
828
+ # 3. Execute a tool
829
+ result = await client.call_tool('execute_tool', {
830
+ 'agent_id': 'researcher',
831
+ 'server': 'brave-search',
832
+ 'tool': 'brave_web_search',
833
+ 'args': {'query': 'MCP protocol documentation'}
834
+ })
835
+ # Response: {"content": [...search results...], "isError": false}
836
+ ```
837
+
838
+ This workflow demonstrates on-demand tool discovery - load definitions only when needed, not upfront.
839
+
840
+ ### Agent Identity Modes
841
+
842
+ The gateway supports two deployment modes for handling agent identity:
843
+
844
+ #### Multi-Agent Mode (Recommended)
845
+
846
+ Use when different agents need different permissions (production, multi-agent systems):
847
+
848
+ ```json
849
+ {
850
+ "agents": {
851
+ "researcher": {"allow": {"servers": ["brave-search"]}},
852
+ "backend": {"allow": {"servers": ["postgres"]}}
853
+ },
854
+ "defaults": {
855
+ "deny_on_missing_agent": true // Require explicit agent_id
856
+ }
857
+ }
858
+ ```
859
+
860
+ Configure each agent to pass their identity (see [Configure Your Agents](#3-configure-your-agents)).
861
+
862
+ #### Single-Agent Mode
863
+
864
+ Use when all agents should have the same permissions (development, personal use, single-agent deployments):
865
+
866
+ ```bash
867
+ # Set default agent via environment variable
868
+ export GATEWAY_DEFAULT_AGENT=developer
869
+ ```
870
+
871
+ Or define a "default" agent in rules:
872
+ ```json
873
+ {
874
+ "agents": {
875
+ "default": {
876
+ "allow": {"servers": ["brave-search", "postgres"]}
877
+ }
878
+ },
879
+ "defaults": {
880
+ "deny_on_missing_agent": false
881
+ }
882
+ }
883
+ ```
884
+
885
+ Agents can omit `agent_id` in tool calls - the gateway automatically uses the configured default.
886
+
887
+ <details>
888
+ <summary><strong>Technical Details: Agent Identity Resolution</strong></summary>
889
+
890
+ When `agent_id` is not provided, the gateway uses this fallback chain:
891
+
892
+ 1. `GATEWAY_DEFAULT_AGENT` environment variable (highest priority)
893
+ 2. Agent named "default" in `.mcp-gateway-rules.json`
894
+ 3. Error if neither configured
895
+
896
+ The `deny_on_missing_agent` setting controls this behavior:
897
+ - `true`: Require explicit `agent_id` (bypass fallback chain)
898
+ - `false`: Use fallback chain when `agent_id` omitted
899
+
900
+ **Security Note:** The fallback mechanism follows the principle of least privilege - it never grants implicit "allow all" access, only the explicitly configured agent's permissions.
901
+
902
+ </details>
903
+
904
+ ## Security Considerations
905
+
906
+ **Rules File Location:** Store `.mcp-gateway-rules.json` in-project for context optimization only. For production access control, store outside project directory (e.g., `~/.claude/mcp-gateway-rules.json`) to prevent agents from reading/modifying permissions.
907
+
908
+ **Debug Mode:** The `get_gateway_status` tool exposes gateway internals and is only available when `GATEWAY_DEBUG=true`. Disable in production environments.
909
+
910
+ **For comprehensive security guidance:** See [Security Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/security-guide.md) for detailed information on rules file security, debug mode considerations, agent impersonation risks, and production best practices.
911
+
912
+ ## Troubleshooting
913
+
914
+ ### Gateway Won't Start
915
+
916
+ **Symptom:** Error on startup or gateway fails to initialize
917
+
918
+ **Solutions:**
919
+ - **Check configuration files exist:** Verify `.mcp.json` and `.mcp-gateway-rules.json` are in the expected location
920
+ - **Validate JSON syntax:** Use `python -m json.tool < .mcp.json` to check for syntax errors
921
+ - **Check Python version:** Ensure Python 3.12+ is installed (`python --version`)
922
+ - **Verify dependencies:** Run `uv sync` to ensure all packages are installed
923
+
924
+ ### Can't Connect to Downstream Server
925
+
926
+ **Symptom:** `SERVER_UNAVAILABLE` error when calling tools
927
+
928
+ **Solutions:**
929
+ - **Verify server configuration:** Check server is properly defined in `.mcp.json`
930
+ - **Test stdio servers:** Ensure command is available (`npx --version`, `uvx --version`)
931
+ - **Check environment variables:** Verify API keys and credentials are set
932
+ - **Test HTTP servers:** Try accessing server URL directly in browser
933
+ - **Review startup logs:** Look for server initialization errors in gateway output
934
+
935
+ ### Permission Denied Errors
936
+
937
+ **Symptom:** `DENIED_BY_POLICY` when agent tries to use a tool
938
+
939
+ **Solutions:**
940
+ - **Verify agent_id:** Ensure agent is passing correct identity (check audit logs)
941
+ - **Check agent rules:** Confirm agent exists in `.mcp-gateway-rules.json`
942
+ - **Review policy precedence:** Remember deny rules take precedence over allow rules
943
+ - **Test with wildcard:** Try `"tools": {"server-name": ["*"]}` to grant broad access temporarily
944
+ - **Enable debug mode:** Use `GATEWAY_DEBUG=true` and call `get_gateway_status` to inspect policy state
945
+
946
+ ### OAuth Authentication Issues
947
+
948
+ **Symptom:** Browser doesn't open or OAuth flow fails
949
+
950
+ See detailed troubleshooting in [OAuth User Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/oauth-user-guide.md).
951
+
952
+ Quick fixes:
953
+ - **Clear token cache:** `rm -rf ~/.fastmcp/oauth-mcp-client-cache/`
954
+ - **Test browser:** `python -m webbrowser https://example.com`
955
+ - **Check server URL:** Verify correct OAuth server URL in `.mcp.json`
956
+
957
+ ### Hot Reload Not Working
958
+
959
+ **Symptom:** Changes to config files don't take effect
960
+
961
+ **Solutions:**
962
+ - **Check file watch:** Ensure config files are in expected locations
963
+ - **Review logs:** Look for reload errors in gateway output
964
+ - **Manual reload:** Send SIGHUP signal or restart gateway
965
+ - **Debug mode:** Use `get_gateway_status` to check last reload timestamps
966
+
967
+ **For additional help:** See [Security Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/security-guide.md), [OAuth User Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/oauth-user-guide.md), or open a GitHub issue.
968
+
969
+ ## Testing
970
+
971
+ ### Testing with MCP Inspector
972
+
973
+ The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive tool for testing MCP servers.
974
+
975
+ ```bash
976
+ # Basic usage
977
+ npx @modelcontextprotocol/inspector uvx agent-mcp-gateway
978
+
979
+ # With custom config paths
980
+ GATEWAY_MCP_CONFIG=~/.config/agent-mcp-gateway/.mcp.json \
981
+ GATEWAY_RULES=~/.config/agent-mcp-gateway/.mcp-gateway-rules.json \
982
+ GATEWAY_DEFAULT_AGENT=researcher \
983
+ npx @modelcontextprotocol/inspector uvx agent-mcp-gateway
984
+
985
+ # With debug mode
986
+ GATEWAY_DEBUG=true npx @modelcontextprotocol/inspector uvx agent-mcp-gateway
987
+ ```
988
+
989
+ **Inspector features:**
990
+ - View all gateway tools with schemas
991
+ - Test tools with custom inputs
992
+ - Inspect request/response messages
993
+ - Monitor logs and notifications
994
+
995
+ #### Testing Gateway Tools in Inspector
996
+
997
+ **1. Test `list_servers`:**
998
+ ```json
999
+ {
1000
+ "agent_id": "researcher"
1001
+ }
1002
+ ```
1003
+
1004
+ Expected: List of servers the "researcher" agent can access.
1005
+
1006
+ **2. Test `get_server_tools`:**
1007
+ ```json
1008
+ {
1009
+ "agent_id": "researcher",
1010
+ "server": "brave-search"
1011
+ }
1012
+ ```
1013
+
1014
+ Expected: Tool definitions from brave-search server.
1015
+
1016
+ **3. Test `execute_tool`:**
1017
+ ```json
1018
+ {
1019
+ "agent_id": "researcher",
1020
+ "server": "brave-search",
1021
+ "tool": "brave_web_search",
1022
+ "args": {
1023
+ "query": "test query"
1024
+ }
1025
+ }
1026
+ ```
1027
+
1028
+ Expected: Search results from Brave (if server configured and running).
1029
+
1030
+ **Troubleshooting:**
1031
+ - Check **Logs pane** for errors
1032
+ - Verify `agent_id` exists in rules file
1033
+ - Confirm downstream servers configured
1034
+ - Review **Message pane** for policy denials
1035
+
1036
+ ## Development
1037
+
1038
+ ### Local Installation
1039
+
1040
+ Clone and install in development mode:
1041
+
1042
+ ```bash
1043
+ # Clone repository
1044
+ git clone https://github.com/roddutra/agent-mcp-gateway.git
1045
+ cd agent-mcp-gateway
1046
+
1047
+ # Install dependencies
1048
+ uv sync
1049
+
1050
+ # Create local config files from examples
1051
+ cp config/.mcp.json.example .mcp.json
1052
+ cp config/.mcp-gateway-rules.json.example .mcp-gateway-rules.json
1053
+
1054
+ # Run locally
1055
+ uv run python main.py --help
1056
+ ```
1057
+
1058
+ ### Add Local Gateway to MCP Client
1059
+
1060
+ ```bash
1061
+ # Claude Code CLI
1062
+ claude mcp add agent-mcp-gateway \
1063
+ uv run --directory /path/to/agent-mcp-gateway python main.py
1064
+
1065
+ # Or manual configuration
1066
+ {
1067
+ "mcpServers": {
1068
+ "agent-mcp-gateway": {
1069
+ "command": "uv",
1070
+ "args": ["run", "--directory", "/path/to/agent-mcp-gateway", "python", "main.py"],
1071
+ "env": {
1072
+ "GATEWAY_DEFAULT_AGENT": "developer"
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ ```
1078
+
1079
+ **Note:** The `--directory` flag tells `uv run` to change to the project directory before running, ensuring it finds `pyproject.toml` and the gateway configuration files.
1080
+
1081
+ ### Project Structure
1082
+
1083
+ ```
1084
+ agent-mcp-gateway/
1085
+ ├── src/ # Core gateway implementation
1086
+ ├── tests/ # Test suite
1087
+ ├── config/ # Configuration examples
1088
+ ├── docs/ # Documentation and specifications
1089
+ ├── main.py # Entry point
1090
+ └── pyproject.toml # Python dependencies
1091
+ ```
1092
+
1093
+ ### Running in Development
1094
+
1095
+ ```bash
1096
+ # Run locally
1097
+ uv run python main.py
1098
+
1099
+ # With debug mode
1100
+ uv run python main.py --debug
1101
+ ```
1102
+
1103
+ ### Testing
1104
+
1105
+ ```bash
1106
+ # Run all tests
1107
+ uv run pytest
1108
+
1109
+ # Run with coverage
1110
+ uv run pytest --cov=src --cov-report=term
1111
+
1112
+ # Run specific test file
1113
+ uv run pytest tests/test_gateway.py -v
1114
+
1115
+ # Run tests in watch mode
1116
+ uv run pytest-watch
1117
+
1118
+ # Generate HTML coverage report
1119
+ uv run pytest --cov=src --cov-report=html
1120
+ open htmlcov/index.html
1121
+ ```
1122
+
1123
+ **Testing with MCP Inspector:**
1124
+
1125
+ ```bash
1126
+ # Basic usage (uses local config files)
1127
+ npx @modelcontextprotocol/inspector uv run python main.py
1128
+
1129
+ # With debug mode
1130
+ npx @modelcontextprotocol/inspector uv run python main.py --debug
1131
+
1132
+ # With custom config paths
1133
+ GATEWAY_MCP_CONFIG=.mcp.json \
1134
+ GATEWAY_RULES=.mcp-gateway-rules.json \
1135
+ GATEWAY_DEFAULT_AGENT=researcher \
1136
+ npx @modelcontextprotocol/inspector uv run python main.py
1137
+ ```
1138
+
1139
+ **Manual testing with FastMCP Client:**
1140
+
1141
+ ```bash
1142
+ uv run python -c "
1143
+ import asyncio
1144
+ from fastmcp import Client
1145
+
1146
+ async def test():
1147
+ async with Client('main.py') as client:
1148
+ result = await client.call_tool('list_servers', {'agent_id': 'researcher'})
1149
+ print(result)
1150
+
1151
+ asyncio.run(test())
1152
+ "
1153
+ ```
1154
+
1155
+ ### Adding a New Feature
1156
+
1157
+ 1. **Update specs**: Document in relevant milestone file
1158
+ 2. **Write tests first**: Create test file in `tests/`
1159
+ 3. **Implement feature**: Add code in `src/`
1160
+ 4. **Run tests**: `uv run pytest`
1161
+ 5. **Check coverage**: `uv run pytest --cov=src`
1162
+ 6. **Update docs**: Document in README and relevant files
1163
+ 7. **Commit**: Follow commit message format
1164
+
1165
+ ### Code Style
1166
+
1167
+ - Follow existing patterns in M0/M1 code
1168
+ - Use type hints throughout
1169
+ - Write docstrings for all public functions
1170
+ - Keep functions focused and testable
1171
+ - Add tests for all new functionality
1172
+
1173
+ ## Architecture
1174
+
1175
+ ### Component Diagram
1176
+
1177
+ ```
1178
+ ┌─────────────────────────────────────────────────────────┐
1179
+ │ Agent / Client │
1180
+ └─────────────────────┬───────────────────────────────────┘
1181
+
1182
+
1183
+ ┌─────────────────────────────────────────────────────────┐
1184
+ │ Agent MCP Gateway │
1185
+ │ ┌───────────────────────────────────────────────────┐ │
1186
+ │ │ Gateway Tools (3 tools, ~400 tokens) │ │
1187
+ │ │ • list_servers │ │
1188
+ │ │ • get_server_tools │ │
1189
+ │ │ • execute_tool │ │
1190
+ │ └───────────────────────────────────────────────────┘ │
1191
+ │ │ │
1192
+ │ ┌─────────────────────────────────────────────────┐ │
1193
+ │ │ AgentAccessControl Middleware │ │
1194
+ │ │ • Extract agent_id │ │
1195
+ │ │ • Validate permissions │ │
1196
+ │ └─────────────────────────────────────────────────┘ │
1197
+ │ │ │
1198
+ │ ┌─────────────────────────────────────────────────┐ │
1199
+ │ │ PolicyEngine │ │
1200
+ │ │ • Deny-before-allow precedence │ │
1201
+ │ │ • Wildcard matching │ │
1202
+ │ └─────────────────────────────────────────────────┘ │
1203
+ │ │ │
1204
+ │ ┌─────────────────────────────────────────────────┐ │
1205
+ │ │ ProxyManager │ │
1206
+ │ │ • Session isolation │ │
1207
+ │ │ • Connection pooling │ │
1208
+ │ └─────────────────────────────────────────────────┘ │
1209
+ │ │ │
1210
+ │ ┌─────────────────────────────────────────────────┐ │
1211
+ │ │ AuditLogger & MetricsCollector │ │
1212
+ │ └─────────────────────────────────────────────────┘ │
1213
+ └──────────────────────┬──────────────────────────────────┘
1214
+
1215
+ ┌─────────────┼─────────────┐
1216
+ ▼ ▼ ▼
1217
+ ┌─────────┐ ┌─────────┐ ┌─────────┐
1218
+ │ Server │ │ Server │ │ Server │
1219
+ │ A │ │ B │ │ C │
1220
+ │ (stdio) │ │ (stdio) │ │ (HTTP) │
1221
+ └─────────┘ └─────────┘ └─────────┘
1222
+ ```
1223
+
1224
+ ### Request Flow
1225
+
1226
+ 1. **Agent sends request** to gateway tool with `agent_id`
1227
+ 2. **Middleware intercepts**: Extracts and validates `agent_id`
1228
+ 3. **Tool validates**: Checks PolicyEngine for server/tool access
1229
+ 4. **Proxy forwards**: ProxyManager routes to downstream server
1230
+ 5. **Session isolated**: Each request gets fresh connection
1231
+ 6. **Result returns**: Transparently forwarded to agent
1232
+ 7. **Audit logged**: Operation recorded with metrics
1233
+
1234
+ ### Performance Characteristics
1235
+
1236
+ - **Context reduction**: 90%+ (400 tokens vs 5,000-50,000+)
1237
+ - **Added latency**: <100ms (P95)
1238
+ - **Gateway overhead**: <30ms per operation
1239
+ - **Session isolation**: Automatic per-request
1240
+ - **Concurrent requests**: Fully supported
1241
+
1242
+ ## Future Features
1243
+
1244
+ ### M2: Production (Planned)
1245
+
1246
+ 🚧 **Status:** Not yet implemented
1247
+
1248
+ **Features:**
1249
+ - [ ] HTTP transport for gateway server
1250
+ - [ ] Health check endpoints
1251
+ - [ ] Enhanced error handling
1252
+ - [ ] Metrics export API
1253
+ - [ ] Connection pooling optimization
1254
+ - [ ] Rate limiting
1255
+
1256
+ **When available:**
1257
+ ```bash
1258
+ # Run with HTTP transport
1259
+ export GATEWAY_TRANSPORT=http
1260
+ export GATEWAY_PORT=8080
1261
+ uv run python main.py
1262
+
1263
+ # Health check endpoint
1264
+ curl http://localhost:8080/health
1265
+
1266
+ # Metrics endpoint
1267
+ curl http://localhost:8080/metrics
1268
+ ```
1269
+
1270
+ ### M3: Developer Experience (Planned)
1271
+
1272
+ 🚧 **Status:** Not yet implemented
1273
+
1274
+ **Features:**
1275
+ - [ ] Single-agent mode (bypass agent_id requirement)
1276
+ - [ ] Config validation CLI tool
1277
+ - [ ] Docker container with examples
1278
+ - [ ] Interactive setup wizard
1279
+ - [ ] VS Code extension
1280
+
1281
+ **When available:**
1282
+ ```bash
1283
+ # Single-agent mode (no agent_id required)
1284
+ export GATEWAY_DEFAULT_AGENT=developer
1285
+ uv run python main.py
1286
+
1287
+ # Validate configs
1288
+ uv run python -m src.cli validate
1289
+
1290
+ # Run with Docker
1291
+ docker run -v ./config:/config agent-mcp-gateway
1292
+ ```
1293
+
1294
+ ## Documentation
1295
+
1296
+ - [Product Requirements Document](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/specs/PRD.md)
1297
+ - [M0: Foundation Spec](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/specs/m0-foundation.md)
1298
+ - [M0: Success Report](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/milestones/m0-success-report.md)
1299
+ - [M1: Core Spec](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/specs/m1-core.md)
1300
+ - [M1: Success Report](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/milestones/m1-success-report.md)
1301
+ - [FastMCP Implementation Guide](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/fastmcp-implementation-guide.md)
1302
+ - [Claude Code Subagent Limitations](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/claude-code-subagent-mcp-limitations.md)
1303
+
1304
+ ## Contributing
1305
+
1306
+ Contributions welcome! Please:
1307
+
1308
+ 1. Read the [PRD](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/specs/PRD.md) and relevant milestone specs
1309
+ 2. Follow the existing code style and patterns
1310
+ 3. Write tests for all new functionality
1311
+ 4. Ensure all tests pass: `uv run pytest`
1312
+ 5. Update documentation as needed
1313
+ 6. Submit a pull request with clear description
1314
+
1315
+ ## License
1316
+
1317
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/roddutra/agent-mcp-gateway/blob/main/LICENSE) file for details.
1318
+
1319
+ ## Support
1320
+
1321
+ For issues and questions:
1322
+ - GitHub Issues: [Create an issue](https://github.com/roddutra/agent-mcp-gateway/issues)
1323
+ - Documentation: [docs/](https://github.com/roddutra/agent-mcp-gateway/blob/main/docs/)
1324
+ - MCP Specification: https://modelcontextprotocol.io
1325
+
1326
+ ## Acknowledgments
1327
+
1328
+ Built with:
1329
+ - [FastMCP](https://gofastmcp.com/) - MCP server framework
1330
+ - [Model Context Protocol](https://modelcontextprotocol.io/) - Protocol specification