AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP unreviewed MIT Self-run

Gatewright

mcp-msathiyakeerthi-gatewright · by msathiyakeerthi

Self-hostable control plane for AI agents. One URL, diff/scan/deploy pipeline, per-agent allowlists, OpenAPI orchestrator.

No reviews yet
0 installs
40 views
0.0% view→install

Install

$ agentstack add mcp-msathiyakeerthi-gatewright

Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Possible prompt-injection directive.

What it can access

  • Network access Used
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Reliability & compatibility

Not yet reviewed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Gatewright? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

AgentGate

[](https://github.com/msathiyakeerthi/agentgate/actions/workflows/ci.yml) [](LICENSE) [](https://www.python.org/downloads/) [](.github/CONTRIBUTING.md)

A self-hostable control plane for MCP (Model Context Protocol) servers. One URL for every agent, a diff/scan/deploy pipeline for every change, per-agent allowlists, and an OpenAPI-to-MCP orchestrator. Open-source generic implementation of the architecture Uber presented at MCP Dev Summit 2026.

 agent clients (Claude Code, Cursor, …)  ─► one URL: /mcp/  ─►  10s of upstream MCPs
                                              │                    │
                                  per-agent allowlist          stdio / http / sse
                                  PII redaction
                                  OpenTelemetry spans

What's inside

  • Diff workflow. Every change goes through Create Diff → Security Scan → Deploy Diff. Four built-in scanners (mutating-verb, prompt-injection, secret-leakage, PII-schema) block bad changes before they reach agents.
  • Orchestrator. Point it at an OpenAPI spec; get a draft MCP definition with LLM-tuned descriptions, mutating-verb flags, and full provenance. Promote drafts through the same diff workflow.
  • Per-agent API keys. Each key carries a glob allowlist (jira__*, *). Hot-revocable. Open-mode when no keys exist for backwards compatibility.
  • Server-rendered admin UI. Starlette + Jinja2 + HTMX. No Node toolchain. Login, browse the registry, edit YAML, preview diff/scan, deploy, audit.
  • Tool-search "omni-MCP." Optional search_tools meta-tool that lets agents discover tools by query instead of loading the full catalog into context.
  • Observability. OpenTelemetry spans + metrics out of the box (no-op until you wire an exporter). sla_tier metadata per server.

Status: all four roadmap phases shipped. 181 tests, lint clean, ~150 MB Docker image. MIT licensed.

Read more

| Doc | Use it for | |---|---| | [docs/architecture-comparison.md](docs/architecture-comparison.md) | Side-by-side with Uber's architecture — what's the same, what we generalized, what we added, what we're behind on. | | [docs/quickstart.md](docs/quickstart.md) | 10-minute hands-on tour — boot, smoke-check, walk through each phase. | | [docs/use-cases.md](docs/use-cases.md) | What this platform is for — concrete operator scenarios. | | [Design Decisions](#design-decisions) (below) | Why each non-obvious call was made. |

Architecture

 OpenAPI specs ──► ┌──────────────┐
 (file/dir/URL)    │ Orchestrator │── LLM (litellm, optional) ──► tuned descriptions
                   └──────┬───────┘
                          │ writes drafts
                          ▼
                   ┌──────────────┐         (drafts NOT loaded by gateway —
                   │ Drafts store │          staged until operator promotes)
                   └──────┬───────┘
                          │ promote → standard diff/scan/deploy pipeline
                          ▼
                   ┌──────────────────────┐
                   │  Definitions store   │   versioned YAML files
                   │  (filesystem / S3)   │   on disk or in object storage
                   └──────────┬───────────┘
                              │ polled (Config Provider)
       ┌──────────────────────┼──────────────────────┐
       │ writes               │                      │
       ▼ (admin API)          ▼                      │
┌──────────────┐    ┌──────────────────────┐         │
│  Admin       │    │  Upstream Registry   │── stdio/http/sse ──► real MCPs
│  API + UI    │    └──────────┬───────────┘         │ (PII redaction
│  /admin/...  │               │ tool views          │  applied to
│              │               ▼                     │  responses)
│  diff →      │    ┌──────────────────────┐         │
│  scan →      │    │  Northbound MCP      │         │  spans/metrics
│  audit       │    │  Server + search_    │         │  via OpenTelemetry
│              │    │  tools meta-tool     │         │
└──────┬───────┘    └──────────┬───────────┘         │
       │ writes                ▲                     │
       ▼                       │ filter by allowlist │
┌──────────────┐    ┌──────────┴───────────┐         │
│  Audit log   │    │  NorthboundAuth      │         │
│  (SQLite)    │    │  (Bearer token)      │         │
└──────────────┘    └──────────┬───────────┘         │
                               │                     │
                               ▼                     │
                         agent clients ◄─────────────┘
                         (Claude Code, Cursor, …)

       ┌──────────────┐
       │  Keys store  │ ── allowlists ─► NorthboundAuth + list_tools/call_tool gating
       │  (SQLite)    │
       └──────────────┘

Quickstart

Requires Python 3.11+ and uv.

uv sync --all-packages

# Run the data plane only (no admin endpoints).
uv run mcpgw-gateway --port 8080

# Or run with the admin API + UI enabled. Pick any token you like.
MCPGW_ADMIN_TOKEN=change-me uv run mcpgw-gateway --port 8080

The repo ships with one sample definition at [definitions/echo/v1.yaml](definitions/echo/v1.yaml) pointing at [examples/echo-mcp/server.py](examples/echo-mcp/server.py). Once the gateway is up, point any MCP client at http://127.0.0.1:8080/mcp/ — you'll see two tools: echo__echo and echo__add.

Smoke check

uv run python scripts/smoke_check.py

Expected output:

tools advertised by gateway: ['echo__echo', 'echo__add']
echo__echo result: 'hello from smoke_check'
echo__add result: '42.0'
OK

Connecting Claude Code

Add a streamable-HTTP server pointing at http://127.0.0.1:8080/mcp/. Tools appear namespaced as __.

The diff workflow (Phase 2)

When MCPGW_ADMIN_TOKEN is set, the gateway exposes an admin surface for editing the registry. Every change is:

  1. Create Diff — proposed YAML compared against the current active version.
  2. Security Scan — pluggable scanners flag mutating verbs without mutates: true, prompt-injection patterns in tool descriptions, hard-coded secrets in static parameter overrides, and PII-shaped fields without redaction.
  3. Deploy Diff — only if scan didn't return any error findings. Bumps version, writes to the store. The Config Provider hot-reloads within a few seconds.

Every step is recorded in the SQLite audit log.

Web UI

http://127.0.0.1:8080/admin/ui/

Log in with the value of MCPGW_ADMIN_TOKEN. The dashboard lists registered servers; the server-detail page is a YAML editor with a "Preview diff & scan" button (HTMX, no full-page reload). When the preview is clean, hit Deploy to commit the new version.

The UI is deliberately minimal (Starlette + Jinja2 + HTMX, no Node toolchain). A richer Next.js admin app is on the roadmap.

REST API

All routes require Authorization: Bearer $MCPGW_ADMIN_TOKEN.

| Method | Path | Purpose | |---|---|---| | GET | /admin/api/servers | List registered servers + active versions. | | GET | /admin/api/servers/{name} | Active definition. | | GET | /admin/api/servers/{name}/versions | All versions. | | GET | /admin/api/servers/{name}/versions/{n} | Specific version (rollback / audit). | | POST | /admin/api/servers/proposals | Submit a definition. Body: {"definition_yaml": "...", "dry_run": bool}. Returns diff + scan + outcome. | | DELETE | /admin/api/servers/{name} | Remove a server entirely. | | GET | /admin/api/audit?server=&limit= | Recent audit entries. |

A dry_run: true proposal returns the diff and scan report without writing — for previewing in CI or in the UI. dry_run: false deploys if the scan passes; if the scan blocks, the rejection is itself audited.

Example:

curl -H "Authorization: Bearer $MCPGW_ADMIN_TOKEN" \
     -H "Content-Type: application/json" \
     -X POST http://127.0.0.1:8080/admin/api/servers/proposals \
     -d '{"definition_yaml":"name: jira\ntier: third-party\ntransport:\n  type: stdio\n  command: python\ntools:\n  - name: search","dry_run":true}'

Auto-generating from OpenAPI specs (Phase 3)

The orchestrator parses OpenAPI 3.x specs and writes draft definitions to a separate drafts store. Drafts are NOT loaded by the gateway — they sit in staging until an operator promotes them through the same diff/scan/deploy workflow.

The generated draft has:

  • one tool per HTTP operation, named from operationId (or synthesized from method+path)
  • policies.mutates: true for POST/PUT/PATCH/DELETE (so the mutating-verb scanner doesn't block)
  • description_for_llm from the spec's summary/description, optionally rewritten by an LLM
  • a placeholder transport the operator must replace before promoting (an OpenAPI URL is REST, not MCP)

CLI

uv run mcpgw-orchestrate \
    --specs examples/petstore-openapi/petstore.yaml \
    --drafts-dir ./drafts

Sources can be files, directories (picks up *.yaml/*.yml/*.json), or http(s):// URLs. Repeat --specs for multiple sources.

LLM descriptions (optional)

Without --model, descriptions come from the spec's own metadata — no network calls, works offline. To get LLM-tuned descriptions, install the litellm extra and set a model:

uv pip install 'mcpgw-llm[litellm]'
export ANTHROPIC_API_KEY=...    # or OPENAI_API_KEY, etc.
uv run mcpgw-orchestrate --specs ... --model anthropic/claude-3-5-haiku-latest

Any litellm-supported provider works (Anthropic, OpenAI, Bedrock, local Ollama, etc.). On any LLM failure the orchestrator silently falls back to heuristic descriptions for that batch — generation never blocks on a flaky API.

From the admin UI

Visit /admin/ui/drafts, paste one source per line into the form, hit Generate drafts. Each draft gets a row with provenance (source spec, generator version, timestamp). Click review & promote to load the draft YAML into the standard editor — fix the transport, hit Preview diff & scan, then Deploy v1 (consumes draft). On a successful deploy the draft is removed from the staging store automatically.

REST API

| Method | Path | Purpose | |---|---|---| | GET | /admin/api/drafts | List pending drafts. | | POST | /admin/api/drafts/generate | Body {"sources": ["..."]}. Crawls each source and writes drafts. | | GET | /admin/api/drafts/{name} | Get a single draft (YAML + provenance). | | DELETE | /admin/api/drafts/{name} | Discard. |

To "promote" via API: GET /admin/api/drafts/{name} to fetch the YAML, edit the transport, then POST /admin/api/servers/proposals with the fixed YAML — the standard diff/scan/deploy pipeline. Then DELETE /admin/api/drafts/{name} to clean up.

Per-agent API keys (Phase 4)

When at least one active agent key exists, the data plane (/mcp/) requires Authorization: Bearer . Each key carries an allowlist of glob patterns (e.g. jira__*, echo__echo, *). The gateway:

  • filters list_tools to only the patterns the key matches, and
  • rejects any call_tool with a tool outside the allowlist.

Keys are minted via the admin API (or the Keys tab in the UI) — the plaintext token is shown exactly once, only the SHA-256 hash is persisted. With zero keys, the data plane stays open (backwards compatible).

curl -H "Authorization: Bearer $MCPGW_ADMIN_TOKEN" \
     -H "Content-Type: application/json" \
     -X POST http://127.0.0.1:8080/admin/api/keys \
     -d '{"name":"claude-code-platform","allowlist":["jira__*","echo__echo"]}'
# => {"key": {...}, "token": "mcpgw_...."}    ← copy the token now

Then point the agent at the gateway:

// .claude/mcp.json
{
  "mcpServers": {
    "gateway": {
      "type": "http",
      "url": "http://127.0.0.1:8080/mcp/",
      "headers": { "Authorization": "Bearer mcpgw_..." }
    }
  }
}

Revoke a key from the UI (or POST /admin/api/keys/{name}/revoke); revocation is immediate.

PII redaction at runtime

Tools with policies.pii_redact: ["reporter.email", "users[*].ssn", ...] get the named paths scrubbed in their responses before the agent sees them. JSONPath-ish syntax (dotted, [*] list wildcards, trailing * for "every key one level under"). Plain-text content is left alone (prose-level PII detection is out of scope for v1; a Presidio-backed scanner is the natural follow-up).

OpenTelemetry

Each upstream tool call is wrapped in a span (mcpgw.call_tool) with attributes for server, tool, agent, and SLA tier, and contributes to three metrics: mcpgw.tool_calls (counter), mcpgw.tool_call_errors (counter), mcpgw.tool_call_latency_ms (histogram). All instrumentation is no-op unless opentelemetry-sdk is installed and an exporter is configured — typically by running with opentelemetry-instrument and setting OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_SERVICE_NAME.

Definitions can declare sla_tier: high|medium|low|unknown, surfaced both as a span attribute and as a tie-breaker boost in the tool-search ranking below.

Tool-search "omni-MCP"

Set MCPGW_OMNI_MCP=1 and the gateway exposes a single meta-tool, search_tools(query, limit), alongside the normal namespaced catalog. Agents can call it first to discover relevant tools by natural-language query — the pattern Uber's roadmap calls out for context-bloat reduction at high tool counts.

result = await session.call_tool("search_tools", {"query": "echo a message back"})
# result.structuredContent =>
# {"matches": [{"name": "echo__echo", "server": "echo", "description": "...", "score": 3.81}, ...],
#  "query": "echo a message back"}

Ranking is TF-IDF over each tool's corpus with a small SLA-tier boost (high floats up ~10% over low). Deterministic, no external deps, no LLM call. Results respect the agent's allowlist.

Adding a definition (manual / GitOps)

You can also drop YAML directly into the definitions store. The Config Provider picks it up within MCPGW_POLL_INTERVAL seconds (default 2s).

# definitions/jira/v1.yaml
name: jira
version: 1
tier: third-party
owner: platform-team@example.com
transport:
  type: http
  endpoint: https://mcp-jira.example.com
auth:
  type: bearer
  secret_ref: env:JIRA_TOKEN          # resolves to os.environ["JIRA_TOKEN"]
tools:
  - name: search_issues
    enabled: true
    description_for_llm: |
      Search Jira issues. Use JQL syntax.
    parameter_overrides:
      project: ENG                     # statically pinned — LLM cannot override
  - name: delete_issue
    enabled: false                     # soft-disabled, never exposed to agents

To deploy a change, write a new file at v2.yaml. Old versions are retained for rollback.

Repo layout

| Path | Purpose | |---|---| | [apps/gateway/](apps/gateway/) | The gateway service. Includes [admin/](apps/gateway/mcpgwgateway/admin/) (REST API + UI), [configprovider.py](apps/gateway/mcpgwgateway/configprovider.py), [upstream.py](apps/gateway/mcpgwgateway/upstream.py), [server.py](apps/gateway/mcpgwgateway/server.py). | | [packages/definitions/](packages/definitions/) | Pydantic schema for the YAML definition format. | | [packages/store/](packages/store/) | Definitions store abstraction + filesystem backend. | | [packages/diff/](packages/diff/) | Structured diff between two definitions. | | [packages/scanner/](packages/scanner/) | Pluggable scanner pipeline + 4 built-in scanners. | | [packages/audit/](packages/audit/) | Audit log abstraction + SQLite backend. | | [packages/llm-client/](packages/llm-client/) | Pluggable LLM client (heuristic + litellm) for the orchestrator. | | [packages/agent-keys/](packages/agent-keys/) | Per-agent API keys with allowlists, SQLite-backed. | | [apps/orchestrator/](apps/orchestrator/) | OpenAPI crawler + adapter + definition generator + mcpgw-orchestrate CLI. | | [apps/gateway/..

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.