Urumi MCP Gateway
Registers GitLab's hosted MCP server as an upstream, authenticated with a fine-grained personal access token that has "MCP tool: Execute", so GitLab MCP tools can be used through the gateway.
Connects to a WooCommerce store through the bundled woo-mcp upstream, using the WooCommerce REST API and an application password to expose store operations such as creating products and working with orders.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Urumi MCP Gatewaylist all the MCP tools I have access to"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Urumi MCP Gateway
A WooCommerce store and a team MCP gateway, both on Kubernetes. An admin registers MCP servers once. Team members then use every enabled tool through one authenticated URL, from an MCP client (Claude Code, Cursor, MCP Inspector) or from the dashboard's chat agent. Every tool call is attributed to a user and audited. One Helm chart runs on local k3d and, with a different values file, on a VPS.
New here? Start with docs/RUN_ON_YOUR_COMPUTER.md — a
plain-language, step-by-step guide to running it on your own laptop.
Design: docs/DESIGN.md · Threat model: docs/THREAT_MODEL.md
· Requirement IDs (K1…D8): docs/REQUIREMENTS.md · ADRs: docs/adr/
Architecture
flowchart LR
browser["Browser"]
client["External MCP client<br/>(Inspector, Claude Code)"]
subgraph k8s["Kubernetes namespace urumi"]
traefik["Traefik Ingress"]
dash["dashboard<br/>(nginx, React build)"]
gw["gateway<br/>/api/* · /mcp · chat loop<br/>callTool → audit_log"]
pg[("Postgres<br/>users, keys, upstreams, audit")]
woo["woo-mcp<br/>(ClusterIP only)"]
every["server-everything<br/>(ClusterIP only)"]
wp["WordPress + WooCommerce"]
db[("MariaDB")]
job["bootstrap Job<br/>(Helm hook)"]
end
gitlab["GitLab MCP<br/>gitlab.com/api/v4/mcp"]
claude["Claude API"]
browser -- "app.localtest.me /" --> traefik
browser -- "store.localtest.me" --> traefik
client -- "app.localtest.me/mcp + Bearer key" --> traefik
traefik --> dash
traefik -- "/api, /mcp" --> gw
traefik --> wp
gw --> pg
gw -- "x-woo-mcp-secret" --> woo
gw --> every
gw -- "Bearer PAT (encrypted at rest)" --> gitlab
gw -- "x-api-key" --> claude
woo -- "REST API + Application Password" --> wp
wp --> db
job -. "installs WP + Woo, writes Secret urumi-woo-api" .-> wpComponent | Source | Runs as | Reachable from |
gateway |
| Deployment | Ingress |
dashboard |
| Deployment | Ingress |
woo-mcp |
| Deployment | gateway pods only (NetworkPolicy + shared secret) |
server-everything |
| Deployment | gateway pods only (NetworkPolicy) |
WordPress + Woo | official | Deployment + PVC | Ingress |
MariaDB | official | StatefulSet + PVC | in-cluster |
Postgres 16 | official | StatefulSet + PVC | gateway pods only (NetworkPolicy) |
bootstrap |
| Helm | — |
GitLab MCP | hosted by GitLab | external | gateway, over the internet |
Related MCP server: MCPilot
Prerequisites
Tool | Version | Why |
Docker | Docker Desktop, ≥ 6 GB RAM | k3d nodes and image builds |
k3d | v5 | local cluster ( |
kubectl | any recent | scripts pin the |
helm | v3 | the one chart |
Node.js | ≥ 22.13 ( | workspaces, tests, e2e MCP client |
jq, curl, openssl | any | e2e scripts, |
scripts/dev-setup.sh installs them (Homebrew on macOS; apt + official installers on
Ubuntu/WSL2) and prints the versions it found. It does not pin versions.
You also need:
An Anthropic API key (chat: D2, D4).
A GitLab fine-grained personal access token with the permission "MCP tool: Execute" (third upstream; see below). A classic PAT is refused by GitLab with
403 insufficient_granular_scope.
Zero to running
git clone https://github.com/1012aaditya/urumi-mcp-gateway.git urumi && cd urumi
npm install
# 1. Secrets file (gitignored, mode 600). Generates every *_PASSWORD, *_SECRET and
# GATEWAY_ENC_KEY with openssl; refuses to overwrite an existing .env.local.
scripts/gen-env.sh
# 2. Fill in the two values a human must provide:
# ANTHROPIC_API_KEY=sk-ant-...
# GITLAB_MCP_TOKEN=glpat-... (fine-grained, "MCP tool: Execute")
$EDITOR .env.local
# 3. Cluster: k3d `urumi` (1 server + 1 agent), registry localhost:5005, Traefik on 80/443,
# namespace `urumi`, Secret `urumi-env` from .env.local. Safe to re-run.
scripts/cluster-up.sh
# 4. Build and push all images (gateway, dashboard, woo-mcp, everything) to the k3d registry.
scripts/build-images.sh
# 5. Install the chart. The bootstrap hook installs WordPress + WooCommerce and starts woo-mcp.
helm upgrade --install urumi charts/platform -n urumi -f charts/platform/values-local.yaml --wait
# 6. Run the eight demo flows.
tests/e2e/run.sh allIf you edit .env.local later (e.g. add a key), run scripts/secrets-apply.sh and
kubectl -n urumi rollout restart deploy/urumi-gateway.
What the two hand-filled values gate
Missing | Effect |
| Gateway starts and logs |
|
|
The flows themselves only call woo and everything tools; GitLab is required because the
seed (and D3's tool listing) checks that all three upstreams are up. The gateway itself does not
need GitLab: register a different third server (ADR 002's fallback is DeepWiki,
https://mcp.deepwiki.com/mcp, no credential) from the dashboard if you only want to click
through.
URLs (*.localtest.me is public DNS that resolves to 127.0.0.1, no /etc/hosts edits):
URL | What |
| storefront |
| WooCommerce admin: |
| dashboard: |
| MCP endpoint for external clients ( |
Read the seed admin login from your own .env.local (grep '^SEED_ADMIN_' .env.local).
Expected e2e output — one seed line, then one line per flow; diagnostics go to
tests/e2e/.out/<step>.log. The exit code is non-zero if any step fails.
PASS seed
PASS D1
PASS D2
PASS D3
PASS D4
PASS D5
PASS D6
PASS D7
PASS D8Single steps: tests/e2e/run.sh D3 D5 (seeds first if there is no current run).
Timing (approximate, not a benchmark): a fresh install from nothing — cluster, four image
builds, chart install with the WooCommerce download — takes on the order of 10 minutes on a
laptop, most of it image builds and the bootstrap Job. run.sh all takes a few minutes; D7 alone
waits for one health cycle down (≤ 25 s) and back up (≤ 60 s).
Demo walkthrough (manual D1–D8)
tests/e2e/run.sh all creates two members, usera@urumi.test (User A) and userb@urumi.test
(User B); their temporary passwords are kept in tests/e2e/.out/users.json. To click through by
hand, create them yourself under Members (admin), which shows each temporary password once.
D1 — store checkout. Open
http://store.localtest.me, add Demo Tee to the cart, check out with Cash on delivery. Inwp-admin→ WooCommerce → Orders, the order is there.D2 — User A chat. Sign in to the dashboard as User A → Chat: "Create a product called Urumi Mug priced 499". The reply lists
woo__create_productunder Tools used. Place an order for it on the storefront, then ask "What orders came in today?". "Today" is the Asia/Kolkata calendar day bydate_created.D3 — User B external client. As User B → My keys → create a key and copy it (shown once). The page also shows an MCP client configuration snippet. Then:
npx @modelcontextprotocol/inspectorTransport Streamable HTTP, URL
http://app.localtest.me/mcp, headerAuthorization: Bearer urk_…→ List Tools showswoo__*,everything__*,gitlab__*→ callwoo__list_orderswith{"date":"today"}.D4 — two servers in one prompt. As User A in Chat: "Call everything's echo tool with message 'urumi' and list today's store orders." Tools used shows an
everything__echoand awoo__list_orderscall. A woo + gitlab prompt ("summarise today's orders and open a GitLab issue with the summary") uses the same path but writes to GitLab, so the scripted D4 useseverythinginstead.D5 — revoke. Admin → Members → User B → keys → revoke (or User B revokes it under My keys). In the Inspector, List Tools again → 401.
D6 — disable a server. Admin → Upstreams → Disable
everything. Its tools vanish from Tools, from the chat tool set, and from the Inspector's next List Tools (the endpoint is stateless, so there is no push; re-list). Enable restores them.D7 — kill an upstream.
kubectl -n urumi scale deploy/urumi-everything --replicas=0(or delete its pod). Within one health cycle (15 s + 3 s) Upstreams showseverythingdown with its last error;wootools keep working in chat and in the Inspector. Scale back to 1 and it returns to up.D8 — audit. Admin → Audit: every call above, with user, source (
chat/mcp), server, tool, time, duration, outcome. Members see only their own rows.
Local → VPS
The same chart and templates; only the values file and the out-of-band Secrets change.
charts/platform/values-prod.yaml differs from values-local.yaml in:
Setting | Local | Prod ( |
|
| placeholders |
| off | off until a certificate Secret ( |
|
|
|
|
|
|
|
|
|
| empty |
|
|
| cluster default, 5 Gi |
|
|
|
Resource requests/limits are the same in both (set in values.yaml).
Steps on a single-node k3s VPS (Traefik is k3s's default ingress, matching ingress.className):
DNS:
Arecords for both hosts → the VPS IP.Images: push the four images to a registry the VPS can pull from; set every
*.image.repositoryandtagin a copy ofvalues-prod.yaml, plus the hosts andgateway.publicOrigin.Secrets, created out-of-band (the chart never renders a Secret;
tests/infra/chart-checks.shenforces this):kubectl -n urumi create secret generic urumi-env --from-env-file=<file>with the same keys as.env.local, generated on the server, never committed.TLS: either install cert-manager with a Let's Encrypt
ClusterIssuerand aCertificateper host writingstore-tls/app-tls, or configure Traefik's ACME resolver (k3sHelmChartConfig). Then setstore.tls.enabled/app.tls.enabled: true. Neither cert-manager nor ACME is part of this chart.helm upgrade --install urumi charts/platform -n urumi -f values-prod.yaml --wait, then register the three upstreams in the dashboard (Upstreams).tests/e2e/seed-upstreams.shis written for the local cluster: it reads the Secret through the pinnedk3d-urumikubectl context.Egress: the bootstrap Job downloads WooCommerce from wordpress.org; the gateway needs
gitlab.com:443andapi.anthropic.com:443. No egress NetworkPolicy exists today.
Honest status. The VPS deployment is optional and has not been run. What is tested: tests/infra/chart-checks.sh renders the chart with both
values-local.yaml and values-prod.yaml and checks every container has probes and
requests/limits and that no Secret is rendered. Not tested: TLS, cert-manager/ACME, a real
StorageClass, pulling from a remote registry, environmentType: production with
X-Forwarded-Proto, and CI does not build or push images.
Troubleshooting
Symptom | Cause and fix |
| Another server holds 80/443. |
Pods | Docker Desktop has too little memory. Give it ≥ 6 GB (Settings → Resources), restart, re-run |
| Expected for a moment: the chart renders woo-mcp with 0 replicas until the bootstrap Job has written Secret |
Bootstrap fails the | WordPress accepts Application Passwords only over HTTPS unless |
|
|
Chat says "Chat is not configured" (HTTP 503) |
|
Chat says "The chat model is unavailable, try again" | Anthropic returned an error (bad key, no credit, model id). The gateway logs only the error class and HTTP status: |
A rebuilt image does not show up | Tags are |
Start over |
|
Repo layout
apps/woo-mcp/ WooCommerce MCP server (TypeScript, Streamable HTTP, internal-only)
apps/gateway/ MCP gateway + admin API + chat agent backend (Fastify, Postgres)
apps/dashboard/ React + Vite admin/member UI and chat panel (served by nginx)
apps/everything/ Dockerfile for @modelcontextprotocol/server-everything (upstream 2)
packages/contracts/ Shared zod schemas: API, DB schema (sql/), tool naming, error envelope
charts/platform/ The one Helm chart; values.yaml, values-local.yaml, values-prod.yaml
tests/e2e/ D1–D8 scripted flows (run.sh <step|all>), seed scripts
tests/infra/ Cluster checks: chart, persistence, bootstrap, NetworkPolicies, auth
scripts/ dev-setup, gen-env, cluster-up/down, secrets-apply, build-images
docs/ REQUIREMENTS, PLAN, DESIGN, THREAT_MODEL, adr/Tests
npm test # unit + contract tests in every workspace (vitest)
npm run lint # eslint + prettier --check
npm run typecheck # tsc --noEmit in every workspaceGateway tests run against a real Postgres: a throwaway Docker container locally, or
TEST_DATABASE_URL(CI uses apostgres:16-alpineservice). Upstreams are fake MCP servers (apps/gateway/test/helpers/fake-upstream.ts); only external boundaries are faked.Cluster checks (need the local cluster):
tests/infra/chart-checks.sh(offline),persistence.sh,store-bootstrap.sh,gateway-db.sh,gateway-auth.sh,upstreams.sh,woo-mcp-netpol.sh, plusscripts/smoke-ingress.sh.End to end:
tests/e2e/run.sh all.
CI (.github/workflows/ci.yml, GitHub Actions): lint (eslint + prettier + typecheck) and
test (all workspaces against a postgres:16-alpine service). CI does not build images, run the
infra checks or run the e2e flows; those run against a local cluster.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
One MCP endpoint for your company's tools, with per-person sign-in, policy and audit.
One OAuth connector for your team's remote MCP servers, with Tool Packs, guardrails and logs.
Single-connection MCP gateway discovering and executing 490+ verified MCP & A2A tools.
- GentkeyOAuthcom.gentkey
One MCP URL for all your connectors — scoped writes, enforced constraints, and a full audit trail.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables centralized proxy and orchestration of MCP servers, providing unified tool discovery, authentication, and secure request routing. It adds a human-in-the-loop approval gate for high-risk actions and audit logging.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables an agent to search approved MCP integrations and expose only the tools a given task needs, signing the user in once via browser-based OAuth and enforcing scopes, effects and budgets in code. It also acts as a single gateway connection so existing hosts can reach many servers with per-tenant isolation and audit events.MIT
- FlicenseNot gradedqualityAmaintenanceEnables teams to onboard remote MCP servers, enforce function-level allow lists and credential modes, and let developers expose permitted tool bundles through a unified MCP endpoint with search and execute functions while keeping permissions and auditing enforced.-
- AlicenseNot gradedqualityAmaintenanceEnables teams to govern agent tool access with function-level allow-lists, credential modes, and audited execution, while exposing permitted MCP tools through a unified endpoint with search and execute functions.MIT