About NV oOS Gateway
NV oOS Gateway is a streamable HTTP MCP gateway for fleets of NV oOS WordPress sites. It gives MCP clients one API key and a namespaced tool catalog, with fleet-level quotas and policy controls.
Key features
- 01Aggregate tools across bound sites
- 02Namespace tools by site
- 03API-key and optional OAuth 2.1 authentication
- 04Per-tool quotas and policy rules
- 05Health, catalog, and policy inspection endpoints
Use cases
- 01Connect an MCP client to multiple NV oOS sites
- 02Limit tool calls by key, site, or tool
- 03Block or require approval for calls under defined rules
MCP support
Exposes an MCP endpoint over streamable HTTP and proxies initialization, tool listing, and tool calls to bound sites' native MCP endpoints. Resources and prompts are routed only to the first bound site.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://mcp.nvoos.pro/mcpclaude mcp add --transport http nvdigitalsolutions-nvoos-mcp-gateway https://mcp.nvoos.pro/mcp{"mcpServers":{"nvdigitalsolutions-nvoos-mcp-gateway":{"type":"http","url":"https://mcp.nvoos.pro/mcp"}}}Open in Cursor{"servers":{"nvdigitalsolutions-nvoos-mcp-gateway":{"type":"http","url":"https://mcp.nvoos.pro/mcp"}}}Open in VS Code{"mcpServers":{"nvdigitalsolutions-nvoos-mcp-gateway":{"type":"http","url":"https://mcp.nvoos.pro/mcp"}}}{"mcpServers":{"nvdigitalsolutions-nvoos-mcp-gateway":{"serverUrl":"https://mcp.nvoos.pro/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
https://mcp.nvoos.pro/mcpAuth requiredTools
Details
From the project READMENV oOS MCP Gateway
==================
A public, fleet-scoped Model Context Protocol endpoint for the
NV oOS (Open Operator System)
WordPress platform. One API key, every bound site — tools appear namespaced
as <site-slug>.<tool>.
The gateway is an MCP aggregating proxy speaking streamable HTTP
(MCP 2026-07-28): it authenticates callers with a public API key, forwards
initialize / tools/list / tools/call to the bound NV oOS sites' native
/wp-json/mcp-ai/v1/mcp endpoints using Fleet Operator tokens, and keeps
the fleet surface collision-free by prefixing tool names per site.
Deployment target: Cloudways Velocity (managed Node hosting) at
https://mcp.nvoos.pro. See
docs/operations/deployment/mcp-gateway-velocity-setup.md.
One-way mirror: this addon is the canonical source and is synced to the standalone repo
nvdigitalsolutions/nvoos-mcp-gateway(branchmain) bysync-mcp-gateway.yml, which snapshots the subtree as one commit per sync and pushes without--force— the mirror main stays a linear fast-forward line, so hosting platforms (Cloudways Velocity) auto-deploy on every sync. Never commit to the standalone repo directly — the next sync overwrites it.
Endpoints
| Route | Auth | Purpose |
|---|---|---|
GET /mcp | public | Streamable-HTTP discovery JSON |
POST /mcp | Bearer key | JSON-RPC 2.0 (initialize, ping, notifications, tools/list, tools/call, resources/, prompts/) |
GET /health | public | Minimal status + per-site health registry |
GET /health/full | Bearer key | Full view (host-only URLs, bindings) |
GET /admin/policy | Operator token | Effective policy + quota table (read-only) |
POST /admin/policy-sim | Operator token | Replay a candidate policy against recorded calls |
GET /admin/catalog | Operator token | Fleet catalog with per-site lifecycle states |
GET / | public | Human landing page (directory reviewers) |
Configuration (env only)
| Variable | Required | Purpose |
|---|---|---|
GATEWAY_PUBLIC_KEYS | yes | Public keys → site slugs: key=slug-a,slug-b;key2=slug-c (or JSON object) |
GATEWAY_PUBLIC_KEYS_PREVIOUS | no | Rotation overlap — previous key map (warns once on use) |
PUBLIC_SITE_SLUGS | no | Privacy filter for the public /health endpoint: comma-separated slugs visible without auth (* or unset = all configured sites). /health/full always reports every site. |
NVOOS_SITE_<SLUG>_URL | per site | Upstream MCP endpoint, full URL incl. /wp-json/mcp-ai/v1/mcp |
NVOOS_SITE_<SLUG>_TOKEN | per site | Fleet Operator token (op_xxxx.SECRET); never logged or returned |
AUTH_MODE | no | strict (default) fails closed at boot when no keys are set |
UPSTREAM_TIMEOUT_MS | no | Per-site timeout for tools/list + notifications (default 20000) |
UPSTREAM_TOOL_TIMEOUT_MS | no | tools/call budget only — long-running tools get more room without slowing tools/list (default: UPSTREAM_TIMEOUT_MS) |
GATEWAY_OAUTH_ISSUER | no | OAuth 2.1 authorization server issuer (e.g. Auth0 tenant). Setting it turns the gateway into an RFC 9728 protected resource — see the OAuth section below |
GATEWAY_OAUTH_JWKS_URI | no | JWKS override (default: issuer's /.well-known/jwks.json) |
GATEWAY_OAUTH_RESOURCE | no | RFC 8707 resource identifier / token audience (default https://mcp.nvoos.pro) |
TRUST_PROXY | yes on Velocity | 1 — NGINX sets X-Forwarded-For |
ALLOWED_ORIGINS | no | Comma-separated CORS origins for browser-based clients |
MAX_JSON_BODY | no | Body limit (default 1mb) |
RATE_LIMIT_GLOBAL / _MCP / _HEALTH | no | Per-group budgets (defaults 600/5min, 120/min, 60/min) |
TOOL_QUOTAS | no | Per-tool quotas, compact form: <pattern>:<rate>/<window>:<burst> — e.g. delete_post:5/1h:2,woo_*:60/1m:*. Most-specific pattern wins. (Proposal 094) |
GATEWAY_POLICY | no | Policy ruleset (JSON string) — see the policy reference below. (Proposal 094) |
POLICY_FILE | no | Alternative: path to a JSON policy file (Git-versioned policy, read at boot). |
SHADOW_POLICY | no | Shadow ruleset (JSON string) — evaluated and logged, never enforced. |
GEO_MAP | no | JSON object mapping public key ids to ISO country codes (used when not behind a trusted proxy). |
GATEWAY_ADMIN_TOKEN | no | Operator token enabling the /admin/* surface (inert without it). |
AUDIT_SINK_URL / AUDIT_SINK_SECRET | no | HMAC-signed webhook for governance events (deny, quota_exceeded, call_ok…). Must be set together. |
SITE_LIFECYCLE | no | Per-site catalog states: slug-a:draft,slug-b:published (default published). |
Slug → env suffix mapping: site-a → NVOOS_SITE_SITE_A_* (uppercase,
hyphens → underscores).
Example
GATEWAY_PUBLIC_KEYS=demo-public-key-1234567890=demo-site
NVOOS_SITE_DEMO_SITE_URL=https://demo.example.com/wp-json/mcp-ai/v1/mcp
NVOOS_SITE_DEMO_SITE_TOKEN=op_xxxx.SECRET
AUTH_MODE=strict
TRUST_PROXY=1
Policy & quotas (Proposal 094)
Per-tool quotas and declarative policy ride on top of the group budgets. Sites remain the deep enforcement layer (per-tool RPM, token budgets, iteration caps, capability boundaries, nefarious-usage auto-shutdown) — the gateway adds the fleet layer and can only tighten, never weaken.
Quotas
TOOL_QUOTAS=delete_post:5/1h:2,woo_*:60/1m:*,*:600/5m:*
Breaches return HTTP 429 + RFC 9558 headers
(RateLimit-Limit/Remaining/Reset, RateLimit-Policy) with the JSON-RPC
error -32099. Buckets are scoped (key × site × tool) — one noisy key
cannot exhaust another's budget.
Policy grammar
{
"rules": [
{
"id": "no-night-deletes",
"effect": "deny",
"tool": { "match": "delete_*" },
"time": { "window": { "tz": "Europe/London", "days": [1,2,3,4,5], "start": "09:00", "end": "17:00" } }
}
]
}
First match wins. Effects: deny (403 + -32001), require_approval
(428 + -32002 with a redacted preview). Conditions: tool.match (glob),
site, key, time.window (IANA tz, ISO weekdays 1–7, midnight-crossing
windows), geo.country (ISO alpha-2). Denied tools are filtered out of
tools/list entirely (kept out of model context).
Inspection
dryRun: trueontools/callevaluates policy + quotas without executing or consuming — the result carrieswould_execute,policy, andquota.POST /admin/policy-simreplays a candidate policy against the last 200 recorded calls (argument keys only) and diffs it against the live policy.SHADOW_POLICYevaluates in parallel and logs — never enforces.
Tool namespacing
tools/listmerges every site bound to the key and prefixes names:site-a.create_post,site-b.woo_get_products.tools/callaccepts the prefixed name (always) or a bare name when the key is bound to exactly one site (passthrough mode).- A failing upstream degrades the list to a
_meta.gateway.errorsentry instead of failing the whole call. - v1 aggregation limitation:
resources/*andprompts/*use the first bound site.
Behavior & security
- Protocol negotiation:
initializeechoes the highest protocol version both sides support (2026-07-28→2024-11-05), defaulting to2024-11-05when the client sends no version — strict clients (Zed, Claude Desktop) abort with "Unsupported protocol version" when the server hardcodes a version they do not speak. - Stateless — no sessions, no SSE in v1 (the July 2026 spec revision makes stateless transport first-class).
- Fail-closed: no keys configured in strict mode → boot refuses; a key bound
to no configured site → 503; unknown key → 401 with
WWW-Authenticate. - Rate limits are per public key (one noisy key cannot exhaust another's budget); upstream tokens are header-only and never appear in responses, logs, or errors.
- Auth: static API keys in v1; OAuth 2.1 (RFC 8414 + dynamic client registration) is the documented upgrade path.
Claude Code plugin
The addon ships a Claude Code plugin (.claude-plugin/plugin.json,
commands/connect.md, skills/gateway/) that syncs to the mirror repo and
doubles as the marketplace source:
/plugin marketplace add nvdigitalsolutions/nvoos-mcp-gateway
Enabling the plugin prompts for the gateway API key (stored securely as a
sensitive userConfig option) and substitutes it into the MCP server's
Authorization header.
OAuth 2.1 resource server (opt-in)
With GATEWAY_OAUTH_ISSUER set, the gateway acts as an RFC 9728 protected
resource alongside static keys:
GET /.well-known/oauth-protected-resource(+ the/mcppath-insertion variant) serves the protected-resource metadata document (inert 404 when OAuth is unconfigured).- 401 responses carry
WWW-Authenticate: Bearer resource_metadata="…", scope="…"so MCP clients can run the OAuth 2.1 authorization-code + PKCE flow against the configured authorization server. - JWT bearer tokens validate against the AS JWKS (cached, fail-closed):
signature,
iss,aud(RFC 8707 resource binding),exp/nbf, and scope. - Site binding is scope-based: each
site:<slug>scope grants access to that bound site (the read-onlysite:readscope is advertised for future fine-grained enforcement). Tokens granting no configured site get 403insufficient_scope. - Upstream proxying is unchanged: the gateway always exchanges for the site's own Fleet Operator token — OAuth tokens are never forwarded upstream (no confused-deputy passthrough).
Authorization server: any OAuth 2.1 AS with RFC 8414 metadata + JWKS (Auth0 is the platform convention). Dynamic client registration (RFC 7591) belongs to the AS, not the gateway.
Development
npm install
npm test # node:test suites — fake upstreams, no infrastructure
npm run dev # node --watch src/index.js
Local smoke test against the docker compose dev site:
GATEWAY_PUBLIC_KEYS="dev-key-1234567890abcd=docker" \
NVOOS_SITE_DOCKER_URL="http://localhost:8000/wp-json/mcp-ai/v1/mcp" \
NVOOS_SITE_DOCKER_TOKEN="<assistant/operator token>" \
PORT=8899 npm start
# → curl http://127.0.0.1:8899/mcp
Ecosystem discovery
The public endpoint https://mcp.nvoos.pro/mcp is listed in the official
MCP Registry (registry.modelcontextprotocol.io) as
io.github.nvdigitalsolutions/nvoos-mcp-gateway — a streamable-HTTP remote
with a required Authorization header. The registry metadata lives in
server.json and is published with the mcp-publisher
CLI. Community directories (mcpservers.org, mcp.directory, PulseMCP, Glama)
carry entries that point back here; the GET / landing page is the
directory-reviewer surface.
License
GPL-3.0-or-later — see the repository LICENSE.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.