Install
$ agentstack add mcp-hewimetall-vmcp ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ 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.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
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 →About
vmcp
[](https://github.com/hewimetall/vmcp/actions/workflows/coverage.yml) [](https://github.com/hewimetall/vmcp/actions/workflows/coverage.yml)
A virtual MCP gateway that aggregates many upstream MCP servers behind a single GraphQL endpoint.
Rust workspace, 9 crates. Speaks the Model Context Protocol over streamable HTTP and exposes the union of all upstream tools as one GraphQL schema. An agent makes one query_graphql call instead of N+1 round-trips across individual MCP tools.
What it does
- One primary MCP tool,
query_graphql— clients send a GraphQL document,
vmcp fans out to upstreams in parallel via aliased selection sets. Read tools (Query) aggregate in parallel; write tools (Mutation) aggregate sequentially per the GraphQL spec — see [docs/mcp-aggregation-workshop.md](docs/mcp-aggregation-workshop.md).
- Optional
run_task(SEP-1686) — when[tasks]is enabled, long-running
upstream tools marked with taskSupport can be invoked as durable SQLite-backed tasks (tasks/get / tasks/result). GraphQL stays the sync path. See [docs/tasks.md](docs/tasks.md).
- Lazy discovery ladder —
prompts/list(skill playbooks) →
{ servers } → { search(q) } (includes taskSupport) → __type(name) → call. No deep __schema dumps.
- Dynamic schema built at boot from upstream
tools/list. Each upstream
foo gets FooRead under Query.foo and FooWrite under Mutation.foo, partitioned by readOnlyHint.
- Hot swap on drift — registered upstream tool set changes are detected
and the schema is replaced atomically via Arc>.
- OAuth 2.1 + PKCE + DCR — argon2id master-password consent, locally
rotated JWKS, JWT bearer on /mcp.
Documentation
Full operator guide: [docs/README.md](docs/README.md)
| Topic | Doc | | ----- | --- | | Docker / TLS / production (GHCR bootstrap) | [docs/deployment.md](docs/deployment.md) | | OAuth, master password, static tokens | [docs/authentication.md](docs/authentication.md) | | Cargo features, HTTP vs stdio | [docs/builds-and-modes.md](docs/builds-and-modes.md) | | Upstream registry (services, tools, prompts) | [docs/upstreams.md](docs/upstreams.md) | | Native MCP Tasks (run_task + SQLite) | [docs/tasks.md](docs/tasks.md) | | Admin sessions & recordings (JSON dirs) | [docs/sessions.md](docs/sessions.md) | | Skill playbooks (YAML → MCP prompts) | [docs/skills.md](docs/skills.md) | | Cursor, Claude / .mcpb, curl | [docs/clients.md](docs/clients.md) | | Aggregation bench (Python) | [docs/bench.md](docs/bench.md) |
Quick start
Prebuilt binaries and a Docker image ship on every v* tag (Releases · ghcr.io/hewimetall/vmcp). Latest: v0.6.3.
MCP Bundle (.mcpb) — one-click local install
Claude Desktop (and other MCPB hosts) can install a packaged stdio server:
- Download
vmcp-0.6.3-.mcpbfrom the Releases page (e.g.linux-x86_64,macos-aarch64). - Double-click / drag into Claude Desktop, or Settings → Extensions → Install Extension….
Needs Node on PATH for the demo npx upstream. Details: [docs/clients.md](docs/clients.md).
Binary (HTTP gateway, local try)
Needs Node only if you keep the demo npx upstream.
curl -fsSL -o vmcp.tgz \
"https://github.com/hewimetall/vmcp/releases/download/v0.6.3/vmcp-0.6.3-linux-x86_64.tar.gz"
tar -xzf vmcp.tgz
VMCP_REGISTRY_PATH=./demo/registry.json \
VMCP_SPEC_DIR=./demo/specs \
VMCP_SKILLS_DIR=./demo/skills \
./vmcp --config ./vmcp.toml
Other OS/arch assets (and vmcp-stdio-*) are on the same release page.
Docker / VPS
docker pull ghcr.io/hewimetall/vmcp:0.6.3
# TLS stack (Caddy + Let's Encrypt) from the published image:
./deploy/bootstrap.sh --domain gateway.example.com --tag 0.6.3
Details: [docs/deployment.md](docs/deployment.md). The runtime image has no Node — swap demo/registry.json for HTTP/binary upstreams in production.
From source
Requires Rust 1.80+ (and Node for the demo npx upstream).
cargo run -p vmcp
Either way, vmcp listens on http://127.0.0.1:8765:
/mcp— MCP streamable HTTP endpoint (bearer-authenticated)/health— liveness probe, returnsok/.well-known/oauth-authorization-server,/authorize,/consent,
/token, /register, /.well-known/jwks.json — OAuth surface
The default demo/registry.json spawns one upstream (@agentmemory/mcp via npx). Point your MCP client at http://127.0.0.1:8765/mcp and complete the OAuth flow; the default master password is demo-master (rotate before deploying anywhere real).
Build variants (cargo features)
The single vmcp binary is partitioned by compile-time features so you can ship a slim stdio client without the HTTP/admin surface:
| Feature | Default | Enables | | ------- | ------- | ------- | | web | yes | HTTP gateway ingress: Axum listener, OAuth surface, session recorder, transparent proxy. | | admin | yes | Admin SPA (/admin). Implies web. |
stdio mode and the utility subcommands (hash-password, pre-reg, print-config) are always available.
cargo build --release -p vmcp # full: web + admin (default)
cargo build --release -p vmcp --no-default-features --features web # web, no admin UI
cargo build --release -p vmcp --no-default-features # stdio-minimal (no HTTP, no admin)
Prebuilt binaries, .mcpb bundles (stdio + demo data), and ghcr.io/hewimetall/vmcp are published on every v* tag — see [Quick start](#quick-start). A local Docker build can also target the stdio-minimal variant: docker build --target runtime --build-arg FEATURES="--no-default-features" -t vmcp:stdio .
Configuration
Edit vmcp.toml (see inline comments). Every key is overridable via env vars with the VMCP_ prefix and __ as nested separator, e.g.
VMCP_AUTH__MASTER_PASSWORD_ARGON2='$argon2id$...' cargo run -p vmcp
Generate a password hash:
cargo run -p vmcp -- hash-password --password 'your-secret'
Print the resolved config and exit:
cargo run -p vmcp -- print-config
Pre-registered eternal tokens
The normal OAuth flow issues short-lived JWTs signed by a rotating, in-memory JWKS key — so every restart and key rotation invalidates outstanding tokens. That's correct for browser clients but painful for a CI job or a demo client that just wants one credential that keeps working across redeploys.
pre-reg mints an opaque, non-expiring bearer token (vmcp_) and appends it to a JSON file:
cargo run -p vmcp -- pre-reg --name ci --scope mcp:use --out ./tokens.json
# prints: vmcp_xK3v... (the token, on stdout)
Point the gateway at the file and the token is accepted on /mcp directly, bypassing OAuth:
[auth]
tokens_file = "./tokens.json"
curl -H "Authorization: Bearer vmcp_xK3v..." http://127.0.0.1:8765/mcp
The file is hot-reloaded — add or remove a token and the change takes effect without a restart. Revocation = delete the token's line from the file. These are god-keys with full gateway access and no expiry: treat the file as a secret (pre-reg writes it 0600 on unix; it's git-ignored by default). The OAuth flow keeps working alongside this — a vmcp_-prefixed bearer takes the static path, anything else is verified as a JWT.
> Not to be confused with the admin UI's pre_registered session state, which > refers to OAuth DCR clients (RFC 7591), a separate mechanism.
Stdio mode (Claude Desktop / Cursor)
For single-client hosts that speak MCP over stdin/stdout instead of HTTP:
cargo run -p vmcp -- stdio --config /path/vmcp.toml
Requires exactly one token in auth.tokens_file (create with pre-reg) or set VMCP_STDIO_CLIENT_ID for local dev. Example mcp.json:
{
"mcpServers": {
"vmcp": {
"command": "vmcp",
"args": ["stdio", "--config", "/path/vmcp.toml"],
"env": { "RUST_LOG": "info" }
}
}
}
Stdio mode skips OAuth, admin, and HTTP entirely. The pipe is the trust boundary — the static token identifies the client in logs, not as a per-request bearer check.
Disabling HTTP auth (local dev only)
Set auth.enabled = false (or VMCP_AUTH__ENABLED=false) to mount /mcp without bearer middleware and hide /admin. Never use on untrusted networks.
Workspace layout
| Crate | Purpose | | ---------------- | ------------------------------------------------------------------------ | | vmcp | Entry binary. Wires axum + rmcp + every library crate. | | vmcp-config | Config loading (figment + TOML + env override). | | vmcp-registry | registry.json, sidecar specs, tools.lock.json. | | vmcp-upstream | Upstream pool — stdio child-process MCP clients via rmcp. | | vmcp-graphql | Dynamic GraphQL schema generation from upstream tools/list. | | vmcp-auth | OAuth 2.1 + PKCE + DCR, argon2id, JWKS rotation, require_bearer. | | vmcp-server | MCP surface (query_graphql, optional run_task + SQLite TaskStore), skills, proxy. | | vmcp-notify | In-process notification ring buffer (tokio broadcast). | | vmcp-admin | Admin UI + recording/playback. |
License
MIT — see [LICENSE](LICENSE).
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: hewimetall
- Source: hewimetall/vmcp
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.