fast-gateway 0.0.1__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.
- fast_gateway-0.0.1/.gitignore +30 -0
- fast_gateway-0.0.1/LICENSE +21 -0
- fast_gateway-0.0.1/PKG-INFO +381 -0
- fast_gateway-0.0.1/README.md +350 -0
- fast_gateway-0.0.1/pyproject.toml +159 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/__init__.py +55 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/access.py +147 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/api/__init__.py +6 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/api/groups.py +74 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/api/servers.py +92 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/app.py +178 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/builder.py +82 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/catalog.py +92 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/connect.py +66 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/hooks.py +177 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/models.py +134 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/__init__.py +66 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/agentos/README.md +84 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/agentos/__init__.py +12 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/agentos/detectors.py +142 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/agentos/plugin.py +114 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/agentos/policy.py +40 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/plugins/agentos/settings.py +46 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/py.typed +0 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/routing.py +76 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/search.py +81 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/store/__init__.py +6 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/store/base.py +83 -0
- fast_gateway-0.0.1/src/fast_mcp_gateway/store/sqlite.py +421 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Tooling caches
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
.coverage.*
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# Gateway runtime artifacts
|
|
21
|
+
*.db
|
|
22
|
+
gateway.db
|
|
23
|
+
|
|
24
|
+
# Editor / OS
|
|
25
|
+
.DS_Store
|
|
26
|
+
.idea/
|
|
27
|
+
.vscode/
|
|
28
|
+
.claude
|
|
29
|
+
docs/superpowers
|
|
30
|
+
.code-review-graph
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nir Adler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fast-gateway
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A lean, FastMCP-based gateway that turns a registry of upstream MCP servers into one governed, namespaced MCP endpoint mounted on FastAPI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/niradler/fast-mcp-gateway
|
|
6
|
+
Project-URL: Repository, https://github.com/niradler/fast-mcp-gateway
|
|
7
|
+
Project-URL: Issues, https://github.com/niradler/fast-mcp-gateway/issues
|
|
8
|
+
Author: niradler
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: fastapi,fastmcp,gateway,llm,mcp,model-context-protocol,proxy
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: aiosqlite>=0.22.1
|
|
25
|
+
Requires-Dist: fastapi>=0.136.3
|
|
26
|
+
Requires-Dist: fastmcp>=3.3.1
|
|
27
|
+
Provides-Extra: agt
|
|
28
|
+
Requires-Dist: agent-governance-toolkit-core>=4.0.0; extra == 'agt'
|
|
29
|
+
Requires-Dist: agent-os-kernel>=4.0.0; extra == 'agt'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# fast-mcp-gateway
|
|
33
|
+
|
|
34
|
+
[](https://www.python.org)
|
|
35
|
+
[](https://gofastmcp.com)
|
|
36
|
+
[](https://fastapi.tiangolo.com)
|
|
37
|
+
[](https://mypy-lang.org)
|
|
38
|
+
[](https://docs.astral.sh/ruff/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
A lean Python package that mounts on **FastAPI** and turns a registry of upstream
|
|
42
|
+
**MCP servers** into one governed, namespaced MCP endpoint. The core stays thin;
|
|
43
|
+
everything cross-cutting — auth, policy, human-in-the-loop, redaction, audit, cost
|
|
44
|
+
limits — is a **hook function** you pass at mount time, or a **plugin** that bundles
|
|
45
|
+
several together.
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
many upstream MCP servers ──► fast-mcp-gateway ──► one governed /mcp endpoint
|
|
49
|
+
github, slack, jira… (namespaced + filtered + policy-checked)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> [!NOTE]
|
|
53
|
+
> **Status: 0.0.1, under active development.** This is the first public release and
|
|
54
|
+
> APIs may change. Server registry, the proxy builder, the full hook pipeline, groups
|
|
55
|
+
> with allow/deny, group-scoped endpoints, the plugin system, and the `search_tools` /
|
|
56
|
+
> `describe_tool` meta-tools are implemented and tested. The bundled reference hooks are
|
|
57
|
+
> still in progress — see the [roadmap](#roadmap).
|
|
58
|
+
|
|
59
|
+
## Why
|
|
60
|
+
|
|
61
|
+
Point an LLM at a dozen MCP servers directly and you get a dozen connections, a dozen
|
|
62
|
+
auth schemes, no namespacing, no central policy, and no way to hide a dangerous tool.
|
|
63
|
+
`fast-mcp-gateway` puts one endpoint in front of them all:
|
|
64
|
+
|
|
65
|
+
- **One connection, many servers** — register upstreams in a store; the gateway
|
|
66
|
+
proxies each under its own namespace (`github_*`, `slack_*`, …).
|
|
67
|
+
- **Governed** — filter which tools are exposed, gate calls behind policy or
|
|
68
|
+
human approval, redact results, audit everything — all as hooks.
|
|
69
|
+
- **Reuse, don't rebuild** — [FastMCP](https://gofastmcp.com) already does proxying,
|
|
70
|
+
transport bridging, composition/namespacing, and protocol middleware. This package
|
|
71
|
+
builds only what it lacks: the registry, groups, the builder, the hook runner, and
|
|
72
|
+
the plugin seam.
|
|
73
|
+
|
|
74
|
+
## Features
|
|
75
|
+
|
|
76
|
+
- **Server registry (Store)** — persistent CRUD over upstream MCP servers; ships with
|
|
77
|
+
a zero-setup `SqliteStore`, swappable for Postgres / Redis / in-memory via one
|
|
78
|
+
protocol.
|
|
79
|
+
- **Namespaced proxying** — each enabled server is mounted under its `name` as a
|
|
80
|
+
prefix; `reload()` rebuilds the mounts from the registry.
|
|
81
|
+
- **Five hook seams** — `pre_mcp_connect`, `pre_list_tools`, `pre_tool_call`,
|
|
82
|
+
`confirmation`, `post_tool_call`. Auth, policy, HIL, redaction, audit, and cost
|
|
83
|
+
limits are all plain async functions.
|
|
84
|
+
- **Access control** — per-server and per-group **allow/deny** glob lists, enforced on
|
|
85
|
+
both `list_tools` (hides) and `call_tool` (blocks).
|
|
86
|
+
- **Groups & group-scoped endpoints** — expose a curated subset of servers/tools at
|
|
87
|
+
`/mcp/g/{group}`, served by the same shared MCP app (no per-group duplication).
|
|
88
|
+
- **Plugins** — bundle hooks, FastMCP middleware, an admin router, ASGI mounts, and
|
|
89
|
+
meta-tools into one named extension with `setup` / `teardown`.
|
|
90
|
+
- **Optional policy engine** — an `agt` extra wires Microsoft's
|
|
91
|
+
[agent-governance-toolkit](https://github.com/microsoft/agent-governance-toolkit)
|
|
92
|
+
(agent-os) in as a policy plugin.
|
|
93
|
+
- **Typed throughout** — `mypy --strict`, `py.typed`, full type hints.
|
|
94
|
+
|
|
95
|
+
## Architecture
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
FastAPI app
|
|
99
|
+
├── /admin → APIRouter (CRUD: servers, groups, reload) [we build]
|
|
100
|
+
├── /mcp → FastMCP.http_app() (the gateway MCP server) [FastMCP]
|
|
101
|
+
│ ├── mount(proxy_github, namespace="github")
|
|
102
|
+
│ ├── mount(proxy_slack, namespace="slack") ← namespacing
|
|
103
|
+
│ └── HookMiddleware + search meta-tools
|
|
104
|
+
└── /mcp/g/{grp} → same MCP app, scoped to one group's servers/tools [we build]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The gateway is a **parent FastMCP server** that proxies each registered upstream and
|
|
108
|
+
mounts it under a namespace, exposed as an ASGI app you mount onto your own FastAPI app
|
|
109
|
+
alongside an admin router for CRUD. A `HookMiddleware` and an `AccessPolicy` wrap every
|
|
110
|
+
`list_tools` / `call_tool` request.
|
|
111
|
+
|
|
112
|
+
## Getting started
|
|
113
|
+
|
|
114
|
+
### Prerequisites
|
|
115
|
+
|
|
116
|
+
- Python **3.11+**
|
|
117
|
+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
118
|
+
|
|
119
|
+
### Install
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv add fast-mcp-gateway # or: pip install fast-mcp-gateway
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Quickstart
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
import os
|
|
129
|
+
from fastapi import FastAPI
|
|
130
|
+
from fast_mcp_gateway import ConnectContext, ConnectSettings, Hooks, SqliteStore, create_gateway
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
async def inject_auth(ctx: ConnectContext) -> ConnectSettings | None:
|
|
134
|
+
# Auth is just a hook: return headers merged over the server's static headers.
|
|
135
|
+
if ctx.server.name == "github":
|
|
136
|
+
return ConnectSettings(headers={"Authorization": f"Bearer {os.environ['GH_TOKEN']}"})
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
gateway = create_gateway(
|
|
141
|
+
store=SqliteStore("gateway.db"),
|
|
142
|
+
hooks=Hooks(pre_mcp_connect=[inject_auth]),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# The MCP server manages sessions via lifespan, so wire it on the host app:
|
|
146
|
+
app = FastAPI(lifespan=gateway.lifespan)
|
|
147
|
+
gateway.install(app) # mounts /admin (CRUD) and /mcp (MCP endpoint)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Register an upstream server through the admin API, then reload:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
curl -X POST http://127.0.0.1:8000/admin/servers \
|
|
154
|
+
-H 'content-type: application/json' \
|
|
155
|
+
-d '{"name": "math", "url": "http://127.0.0.1:9000/mcp/", "transport": "http"}'
|
|
156
|
+
|
|
157
|
+
curl -X POST http://127.0.0.1:8000/admin/reload
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Its tools now appear at the gateway endpoint under the `math_` namespace.
|
|
161
|
+
|
|
162
|
+
### Run the bundled example
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
make run # uv run uvicorn examples.basic_app:app --reload
|
|
166
|
+
# Admin + OpenAPI docs: http://127.0.0.1:8000/docs
|
|
167
|
+
# MCP endpoint: http://127.0.0.1:8000/mcp/
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Hooks
|
|
171
|
+
|
|
172
|
+
A hook is an async function, grouped in a `Hooks` container and passed at mount time.
|
|
173
|
+
Each binds to the layer where it belongs:
|
|
174
|
+
|
|
175
|
+
| Hook | Binds to | Runs |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| `pre_mcp_connect` | proxy client factory | before opening an upstream session |
|
|
178
|
+
| `pre_list_tools` | `HookMiddleware.on_list_tools` | on catalog requests |
|
|
179
|
+
| `pre_tool_call` | `HookMiddleware.on_call_tool` (pre) | before forwarding a call |
|
|
180
|
+
| `confirmation` | `on_call_tool` (when `REQUIRE_CONFIRMATION`) | human-in-the-loop approval |
|
|
181
|
+
| `post_tool_call` | `HookMiddleware.on_call_tool` (post) | after the upstream result |
|
|
182
|
+
|
|
183
|
+
Hooks chain in registration order. A `pre_tool_call` hook may **continue**, **mutate
|
|
184
|
+
args**, **deny**, or return **`REQUIRE_CONFIRMATION`** — which triggers the
|
|
185
|
+
`confirmation` hooks.
|
|
186
|
+
|
|
187
|
+
> [!IMPORTANT]
|
|
188
|
+
> Confirmation is **fail-safe**: if any confirmation hook rejects, or none is
|
|
189
|
+
> registered, the call is denied. Policy, guardrails, audit, and cost limits are all
|
|
190
|
+
> just hooks — nothing special in the core.
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from fast_mcp_gateway import Hooks, ToolCallResult, ToolDecision
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async def block_deletes(ctx) -> ToolCallResult | None:
|
|
197
|
+
if ctx.message.name.endswith("_delete_all"):
|
|
198
|
+
return ToolCallResult(decision=ToolDecision.REQUIRE_CONFIRMATION, reason="destructive")
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
async def approve(ctx) -> bool:
|
|
203
|
+
return await ask_a_human(ctx.tool_name, ctx.arguments) # your HIL channel
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
hooks = Hooks(pre_tool_call=[block_deletes], confirmation=[approve])
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Access control
|
|
210
|
+
|
|
211
|
+
Every server record carries `allow` / `deny` glob lists; groups carry their own on top.
|
|
212
|
+
`deny` wins over `allow`; an empty `allow` means "allow all". The policy is enforced in
|
|
213
|
+
two places: hidden from `list_tools` and blocked at `call_tool`.
|
|
214
|
+
|
|
215
|
+
```jsonc
|
|
216
|
+
// POST /admin/servers
|
|
217
|
+
{ "name": "fs", "url": "...", "deny": ["delete_*", "*_admin"] }
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Groups & group-scoped endpoints
|
|
221
|
+
|
|
222
|
+
Create a group, set its membership, and a curated view is served at
|
|
223
|
+
`/mcp/g/{group}` — showing only that group's member servers with the group's
|
|
224
|
+
allow/deny applied **on top of** each server's own rules. One shared parent server
|
|
225
|
+
backs every group view; there is no per-group proxy duplication.
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
/mcp → all enabled servers, every permitted tool
|
|
229
|
+
/mcp/g/analytics → only the 'analytics' group's servers & tools
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Plugins
|
|
233
|
+
|
|
234
|
+
A **plugin** is a named bundle of extensions applied at `create_gateway` time. Where a
|
|
235
|
+
single hook is one function, a plugin can contribute hooks **and** FastMCP middleware
|
|
236
|
+
(for around-the-call control like circuit breaking or retry), an admin `APIRouter`,
|
|
237
|
+
ASGI sub-app mounts, meta-tool registration, and async `setup` / `teardown` bound to
|
|
238
|
+
the gateway lifespan.
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from fast_mcp_gateway import create_gateway, SqliteStore
|
|
242
|
+
|
|
243
|
+
gateway = create_gateway(
|
|
244
|
+
store=SqliteStore("gateway.db"),
|
|
245
|
+
plugins=[MyAuditPlugin(), MyRateLimitPlugin()],
|
|
246
|
+
)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
A plugin implements the `Plugin` protocol: a `name`, a `contributions(context)` method
|
|
250
|
+
returning `PluginContributions`, and `setup` / `teardown` coroutines. The
|
|
251
|
+
`GatewayContext` it receives exposes the `store`, the parent `mcp`, and a `reload`
|
|
252
|
+
callable. These authoring types are top-level exports:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from fast_mcp_gateway import Plugin, PluginContributions, GatewayContext
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Optional: agent-os plugin (experimental)
|
|
259
|
+
|
|
260
|
+
> [!WARNING]
|
|
261
|
+
> The `agt` integration is **experimental**. Its upstream dependency is not yet on
|
|
262
|
+
> PyPI, so it installs only inside a uv project (see the install note below).
|
|
263
|
+
|
|
264
|
+
The `agt` extra wires Microsoft's
|
|
265
|
+
[agent-governance-toolkit](https://github.com/microsoft/agent-governance-toolkit)
|
|
266
|
+
(agent-os) in as `AgtAgentOsPlugin`. Its core capability is the **policy engine**: it
|
|
267
|
+
evaluates policy for every tool call — scoped to the active group — and denies calls the
|
|
268
|
+
policy rejects. Additional agent-os capabilities are opt-in toggles on `AgtAgentOsSettings`
|
|
269
|
+
(which reuses agent-os's own config types — `DetectionConfig`, `IntentCategory`, `EgressRule`):
|
|
270
|
+
|
|
271
|
+
| Toggle | Seam | Effect |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| `enable_prompt_injection` | `pre_tool_call` | deny calls whose arguments look like prompt injection |
|
|
274
|
+
| `enable_semantic_policy` (+ `semantic_deny`) | `pre_tool_call` | deny calls whose classified intent is dangerous |
|
|
275
|
+
| `enable_response_scan` | `post_tool_call` | block responses flagged unsafe (credential/PII/threat) |
|
|
276
|
+
| `enable_credential_redaction` | `post_tool_call` | redact secrets/PII out of responses |
|
|
277
|
+
| `enable_egress_policy` (+ `egress_rules`) | `pre_mcp_connect` | refuse upstreams whose URL is outside the allowlist |
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
uv add "fast-mcp-gateway[agt]" # from within a uv project — honors the git source
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
from fast_mcp_gateway import create_gateway, SqliteStore
|
|
285
|
+
from fast_mcp_gateway.plugins.agentos import AgtAgentOsPlugin, AgtAgentOsSettings
|
|
286
|
+
|
|
287
|
+
gateway = create_gateway(
|
|
288
|
+
store=SqliteStore("gateway.db"),
|
|
289
|
+
plugins=[
|
|
290
|
+
AgtAgentOsPlugin(
|
|
291
|
+
AgtAgentOsSettings(
|
|
292
|
+
policy_dir="./policies",
|
|
293
|
+
fail_closed=True,
|
|
294
|
+
enable_prompt_injection=True,
|
|
295
|
+
enable_response_scan=True,
|
|
296
|
+
enable_credential_redaction=True,
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
],
|
|
300
|
+
)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
> [!NOTE]
|
|
304
|
+
> The `agt` extra is sourced from the agent-governance-toolkit GitHub monorepo (via uv
|
|
305
|
+
> `[tool.uv.sources]`) until `agent-os-kernel` 4.x is published to PyPI. Because of that
|
|
306
|
+
> git source, install it from within a uv project (`uv add "fast-mcp-gateway[agt]"`),
|
|
307
|
+
> which honors the source; a plain `pip install "fast-mcp-gateway[agt]"` cannot resolve
|
|
308
|
+
> the dependency and will fail until it lands on PyPI. Upstream, `agent-os-kernel` is
|
|
309
|
+
> being renamed/consolidated to `agent-governance-toolkit-core`. The gateway and the
|
|
310
|
+
> plugin system work fully **without** the extra — only this one integration needs it.
|
|
311
|
+
|
|
312
|
+
## Admin API
|
|
313
|
+
|
|
314
|
+
| Method | Path | Purpose |
|
|
315
|
+
| --- | --- | --- |
|
|
316
|
+
| `GET` / `POST` | `/admin/servers` | list / register servers |
|
|
317
|
+
| `GET` / `PATCH` / `DELETE` | `/admin/servers/{id}` | read / update / remove |
|
|
318
|
+
| `GET` | `/admin/servers/{id}/tools` | live tool introspection |
|
|
319
|
+
| `POST` | `/admin/servers/{id}/test` | connect + handshake check |
|
|
320
|
+
| `GET` / `POST` | `/admin/groups` | list / create groups |
|
|
321
|
+
| `GET` / `PATCH` / `DELETE` | `/admin/groups/{id}` | read / update / remove |
|
|
322
|
+
| `PUT` | `/admin/groups/{id}/servers` | set membership |
|
|
323
|
+
| `POST` | `/admin/reload` | rebuild mounts from the store |
|
|
324
|
+
|
|
325
|
+
CRUD writes to the `Store`; `POST /admin/reload` (or `await gateway.reload()`) rebuilds
|
|
326
|
+
the proxy mounts. There is no live hot-swap in v1 — simple and lean.
|
|
327
|
+
|
|
328
|
+
> [!WARNING]
|
|
329
|
+
> The `/admin` API is **unauthenticated by default** and mutates the registry —
|
|
330
|
+
> registering upstreams, rewriting allow/deny lists, injecting connection headers, and
|
|
331
|
+
> triggering reload. The host app **must** protect it. Pass FastAPI dependencies via
|
|
332
|
+
> `Gateway.install(app, admin_dependencies=[Depends(require_admin)])` to guard the admin
|
|
333
|
+
> router, and/or place it behind reverse-proxy or network-level auth.
|
|
334
|
+
|
|
335
|
+
## Store
|
|
336
|
+
|
|
337
|
+
The gateway's only persistence dependency is the `Store` protocol. `SqliteStore`
|
|
338
|
+
(single file, zero setup) ships as the default; Postgres / Redis / in-memory are
|
|
339
|
+
drop-in via `store=` with no core changes.
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
class Store(Protocol):
|
|
343
|
+
async def initialize(self) -> None: ...
|
|
344
|
+
async def list_servers(self) -> list[ServerRecord]: ...
|
|
345
|
+
async def create_server(self, data: ServerCreate) -> ServerRecord: ...
|
|
346
|
+
# … plus get/patch/delete for servers and groups
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Development
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
make install # uv sync (venv + deps incl. dev group)
|
|
353
|
+
make check # lint + format-check + typecheck + tests (CI gate; run before done)
|
|
354
|
+
make test # pytest
|
|
355
|
+
make format # ruff format + safe lint fixes
|
|
356
|
+
make build # sdist + wheel
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Tooling: [uv](https://docs.astral.sh/uv/) (env + packaging), **ruff** (lint + format),
|
|
360
|
+
**mypy --strict** (types, the gate), **pytest** + pytest-asyncio. Run `make help` for
|
|
361
|
+
all targets.
|
|
362
|
+
|
|
363
|
+
> [!TIP]
|
|
364
|
+
> On Windows, `make` is not built in — use it from WSL/Git Bash, install GNU Make
|
|
365
|
+
> (`scoop install make`), or run the underlying `uv run ...` commands directly.
|
|
366
|
+
|
|
367
|
+
## Roadmap
|
|
368
|
+
|
|
369
|
+
| Phase | Deliverable | Status |
|
|
370
|
+
| --- | --- | --- |
|
|
371
|
+
| 0 | Package skeleton, `Store` protocol + `SqliteStore`, `create_gateway()` | done |
|
|
372
|
+
| 1 | Server CRUD + builder (registry → proxy mount) + `reload()` + `pre_mcp_connect` | done |
|
|
373
|
+
| 2 | `HookMiddleware`: `pre_tool_call` / `post_tool_call` / `pre_list_tools` | done |
|
|
374
|
+
| 3 | Groups + per-server/group allow-deny + group-scoped `/mcp/g/{group}` endpoints | done |
|
|
375
|
+
| — | Plugin system + agent-os policy integration | done |
|
|
376
|
+
| 4 | `search_tools` / `describe_tool` meta-tools + catalog cache | done |
|
|
377
|
+
| 5 | Reference hooks (audit, allow/deny, confirmation), docs, packaging | planned |
|
|
378
|
+
|
|
379
|
+
## License
|
|
380
|
+
|
|
381
|
+
[MIT](LICENSE)
|