amp-mcp-server
Click on "Install 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., "@amp-mcp-serverlist all my game server instances"
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.
amp-mcp-server
An MCP server that wraps CubeCoders AMP so MCP clients (Claude Desktop, Claude Code, others) can list, inspect, and control AMP-managed game-server instances.
This project is a client of AMP's public REST API — no AMP source or binaries are redistributed. Bring your own licensed AMP install.
Tools exposed
Read-only (always enabled):
amp_list_instances— enumerate all AMP-managed instancesamp_get_instance_status— state, uptime, CPU/RAM/players for one instanceamp_get_active_users— connected users for one instanceamp_get_console_output— recent console lines for one instanceamp_get_host_status— state, uptime, CPU/RAM for the AMP controller host itselfamp_get_running_tasks— currently-running tasks on one instance (with progress %)amp_get_update_info— pending game-server updates for one instanceamp_list_backups— local backups for one instance
Write tools (gated by AMP_ALLOW_WRITES=true):
amp_start_instance/amp_stop_instance/amp_restart_instance— instance lifecycleamp_sleep_instance— soft shutdown (resumable faster than Stop; module-dependent)amp_send_console_command— send a command to one instance's consoleamp_take_backup— trigger a backup (poll completion viaamp_get_running_tasks)amp_update_application— apply a pending game-server update (long-running)amp_end_user_session— disconnect a user session (universal kick across modules)
Default-off prevents accidental destructive calls.
Environment
Var | Required | Default | Purpose |
| yes | — | Base URL of your AMP install, e.g. |
| yes | — | AMP admin username |
| yes | — | AMP password (or remembered-token) |
| no |
| Set |
| no |
|
|
| no |
| HTTP listen port (HTTP transport only) |
| no |
| HTTP bind host (HTTP transport only). Docker image overrides to |
| no | — | Comma-separated |
| no | — | Comma-separated |
| no | — | Forwarded to Express |
| no |
| Override the startup guard that refuses |
| no |
| Max requests per window on |
| no |
| Rate-limit window length in milliseconds. |
| no |
|
|
| when | — | Canonical external URL of this server (resource id + JWT audience) |
| when | — | Comma-separated list of accepted bearer tokens |
| when | — | OAuth 2.1 authorization server issuer URL |
| no |
| Override expected JWT |
| no | OIDC-discovered | Override JWKS URL (skips OIDC discovery) |
| no | — | Comma-separated scopes required on every request |
| no |
| pino log level: |
Copy .env.example to .env and fill in real values. Never commit .env.
Quick start — Docker (HTTP)
cp .env.example .env
# edit .env with your AMP credentials
docker compose up -d --build
docker compose logs -fThe server listens on http://127.0.0.1:3000/mcp (stateless Streamable HTTP transport). The compose file publishes the port to host loopback only; to expose it externally, set MCP_BIND=0.0.0.0 in .env and enable auth (MCP_AUTH_MODE=bearer/oauth) or set MCP_ALLOWED_HOSTS — the server refuses to start in 0.0.0.0 + no-auth + no-allowlist mode unless MCP_ALLOW_INSECURE=true.
Smoke check:
curl -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The HTTP transport also exposes GET /health (returns {"status":"ok"}) for Docker/k8s liveness probes — bypasses auth, rate limiting, and origin checks. The Dockerfile has a built-in HEALTHCHECK that hits this endpoint.
Quick start — local Node (stdio or HTTP)
Requires Node 20+.
npm install
npm run build
# stdio (for an MCP client to spawn as a subprocess)
AMP_URL=... AMP_USERNAME=... AMP_PASSWORD=... npm start
# HTTP (local)
MCP_TRANSPORT=http AMP_URL=... AMP_USERNAME=... AMP_PASSWORD=... npm startInspect tools interactively:
npx @modelcontextprotocol/inspector node dist/index.jsAuthentication
The HTTP transport supports three auth modes, selected by MCP_AUTH_MODE. stdio transport ignores all of these — its trust boundary is the OS process, and your MCP client passes credentials via the env block in its config.
Mode | When to use | What it does |
| stdio, or HTTP bound to | No auth at all. Network-layer trust is the only thing keeping callers out. |
| Exposing HTTP to one or two clients you control (e.g. a personal cloud VM) | Static |
| Public/multi-user deployments, or any client that expects spec-compliant MCP auth (e.g. Claude.ai connecting to a remote MCP server) | OAuth 2.1 resource server. Validates JWTs issued by your authorization server. Publishes RFC 9728 Protected Resource Metadata. |
These modes are mutually exclusive — pick one. None of them replace AMP_ALLOW_WRITES; that flag still controls whether the write tools are registered at all.
Bearer mode
MCP_AUTH_MODE=bearer
MCP_AUTH_TOKEN=$(openssl rand -hex 32)Clients call /mcp with Authorization: Bearer <token>. Multiple tokens are accepted as a comma-separated list (one per client, easy revocation by removing the entry and restarting). Missing/invalid tokens get 401 with WWW-Authenticate: Bearer realm="mcp".
curl -X POST http://localhost:3000/mcp \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'OAuth 2.1 mode
This server acts as an OAuth 2.1 resource server — it validates access tokens but does not issue them. You bring your own authorization server (Keycloak, Auth0, Authentik, Duende, Okta, etc.).
How the pieces fit together
OAuth involves three roles. amp-mcp-server is only one of them:
Resource server —
amp-mcp-serveritself. Validates incoming JWTs, serves tools. Has no callback URL and never participates in the redirect flow. Lives atMCP_PUBLIC_URL.Authorization server (AS) — something else you run (Keycloak, Duende, Auth0, Authentik, …). Issues tokens after a user logs in. Lives at
MCP_OAUTH_ISSUER.MCP client — Claude.ai, Claude Desktop, a custom CLI tool, etc. Drives the user-login flow against the AS, receives a token, sends it to the resource server. Each client owns its own redirect/callback URL.
The flow when a user adds your MCP server to a client like Claude.ai:
user
│
▼
MCP client ── (1) fetch PRM ─────► amp-mcp-server (resource server)
│ (says "use AS_X")
│
│ (2) Auth Code + PKCE ──────► AS (Keycloak / Duende / etc.)
│ user logs in + consents
│ AS redirects to the *client's* callback
│
└── (3) bearer JWT ─────────► amp-mcp-serverSo the redirect URI you configure at your AS is not https://your-mcp-server/callback — it's whatever URL the client needs. For Claude.ai it's something on claude.ai; for a desktop or CLI tool it's typically a loopback URL like http://127.0.0.1:8765/callback (RFC 8252).
This means a deployment decision:
A few known clients → pre-register each in your AS admin UI (one client entry per consumer, with that consumer's callback URL). Fine if it's just you adding one or two MCP clients.
Many or unknown clients → enable Dynamic Client Registration (RFC 7591) on your AS so clients register themselves at runtime. The MCP Authorization spec recommends DCR for public deployments. Keycloak, Duende, Auth0, and Authentik all support it as an opt-in feature.
amp-mcp-server itself doesn't care which path you pick — it only sees the resulting bearer JWT.
Required env
MCP_AUTH_MODE=oauth
MCP_PUBLIC_URL=https://amp-mcp.example.com # exact URL clients hit; used as JWT audience
MCP_OAUTH_ISSUER=https://auth.example.com/realms/amp
# Optional:
MCP_OAUTH_REQUIRED_SCOPES=mcp:read,mcp:writeThe exact shape of MCP_OAUTH_ISSUER depends on which authorization server you're using — it must match the iss claim that the AS puts in tokens it issues:
AS | Typical issuer URL |
Keycloak |
|
Duende IdentityServer |
|
Auth0 |
|
Authentik |
|
Okta |
|
When in doubt, fetch <issuer>/.well-known/openid-configuration and check the issuer field — that's the canonical value to use here.
The server publishes a Protected Resource Metadata document at:
GET /.well-known/oauth-protected-resourceso that compliant MCP clients can discover the authorization server automatically. On /mcp calls without a valid token, the server returns 401 with:
WWW-Authenticate: Bearer realm="mcp", resource_metadata="https://amp-mcp.example.com/.well-known/oauth-protected-resource"JWT validation requires:
valid signature (JWKS fetched from the AS)
issmatchesMCP_OAUTH_ISSUERaudincludesMCP_OAUTH_AUDIENCE(default:MCP_PUBLIC_URL)expis in the futureall
MCP_OAUTH_REQUIRED_SCOPES(if set) are present in thescopeorscpclaim
Important:
MCP_PUBLIC_URLmust match exactly what clients call. Audience-mismatch is the most common misconfig — if clients get 401s after appearing to authenticate successfully, check that the AS issued the token for this URL.
Quickstart with Keycloak
docker run -d --name kc -p 8080:8080 \
-e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:latest start-devIn the Keycloak admin UI:
Create a realm (e.g.
amp).Create a client
amp-mcp-test, client typeOpenID Connect, public, with PKCE; standard flow enabled.Create a user, set a password.
Add a client scope
mcp:read, mapped as a default scope.Set the client's "Valid post logout redirect URIs" / "Valid redirect URIs" to whatever your MCP client expects (e.g. Claude.ai's callback).
Run amp-mcp-server with:
MCP_TRANSPORT=http \
MCP_AUTH_MODE=oauth \
MCP_PUBLIC_URL=http://localhost:3000 \
MCP_OAUTH_ISSUER=http://localhost:8080/realms/amp \
MCP_OAUTH_AUDIENCE=http://localhost:3000 \
AMP_URL=... AMP_USERNAME=... AMP_PASSWORD=... \
npm startVerify the PRM endpoint:
curl http://localhost:3000/.well-known/oauth-protected-resourceVerify the 401 challenge:
curl -i -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# expect: 401 + WWW-Authenticate: Bearer realm="mcp", resource_metadata="..."For end-to-end testing with the real Auth Code + PKCE browser-login flow (the same flow Claude.ai and other compliant MCP clients use), this repo ships a one-shot helper at scripts/oauth-token.mjs:
# Configure a public client at your AS with redirect_uri http://127.0.0.1:8765/callback,
# PKCE required, and the scope(s) you want. Then:
TOKEN=$(node scripts/oauth-token.mjs \
--issuer http://localhost:8080/realms/amp \
--client-id amp-mcp-test \
--scope "openid mcp:read")
# Open the printed URL in your browser, log in, and the script captures the
# token and prints it to stdout.
curl -X POST http://localhost:3000/mcp \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'For quicker non-interactive checks against a Machine-to-Machine client, you can also use the client_credentials grant directly:
TOKEN=$(curl -s -X POST http://localhost:8080/realms/amp/protocol/openid-connect/token \
-d 'grant_type=client_credentials' \
-d 'client_id=<m2m-client-id>' \
-d 'client_secret=<secret>' \
-d 'scope=mcp:read' | jq -r .access_token)Wiring into Claude Desktop
stdio (local Node):
{
"mcpServers": {
"amp": {
"command": "node",
"args": ["/absolute/path/to/amp-mcp-server/dist/index.js"],
"env": {
"AMP_URL": "https://amp.example.local",
"AMP_USERNAME": "admin",
"AMP_PASSWORD": "..."
}
}
}
}HTTP (Docker / remote):
{
"mcpServers": {
"amp": { "url": "http://localhost:3000/mcp" }
}
}Production deployment
For exposure beyond your local machine:
Reverse proxy with TLS. Don't put plain HTTP on the public internet. Caddy is the easiest path:
amp-mcp.example.com { reverse_proxy 127.0.0.1:3000 }Caddy auto-provisions Let's Encrypt. nginx and Traefik work the same way.
Set
MCP_TRUST_PROXY. Without it, the rate limiter sees every request as coming from the proxy and locks legitimate clients out at the threshold:MCP_TRUST_PROXY=loopback # proxy on same host # or MCP_TRUST_PROXY=10.0.0.5/32 # CIDR for a specific upstreamPick an auth mode.
bearerfor one or two known clients;oauthfor multi-user or any client that expects spec-compliant MCP auth (e.g. Claude.ai connecting to a remote MCP server).MCP_PUBLIC_URLmust match exactly what clients call (the proxy's external URL with scheme, not the internal Docker URL). Audience-mismatch is the #1 OAuth misconfig.
The Docker image's default MCP_HOST=0.0.0.0 won't start unless MCP_AUTH_MODE is bearer/oauth, MCP_ALLOWED_HOSTS is set, or MCP_ALLOW_INSECURE=true is the explicit override. This is intentional — it prevents accidental public-no-auth deploys.
Troubleshooting
Symptom | Likely cause | Fix |
Server exits immediately with | Unsafe-binding safety guard | Set |
OAuth: | JWT | Decode the JWT and check the |
OAuth: | Token issuer mismatches | Verify |
All clients get | All traffic appearing as one IP because | Set |
Duende: | Scope is in client's allowed-scopes list but isn't defined as an | Add it under "API Scopes" + "API Resources", restart Duende to flush config cache |
Duende: post-consent redirect bounces back to login | Cookie/SameSite issue on the post-consent redirect | Disable |
| vitest cache flake during back-to-back | Re-run |
Architecture
MCP client (Claude Desktop / Code)
│ stdio ── or ── HTTP (stateless Streamable)
▼
amp-mcp-server ── REST/JSON ──▶ AMP install
│
└─▶ @neuralnexus/ampapi (typed AMP client; no transitive deps)The HTTP transport runs in stateless mode: each request gets a fresh StreamableHTTPServerTransport and McpServer so write-tool gating reflects the current AMP_ALLOW_WRITES value. Auth state on the AmpClient (the AMP session) is shared across requests as a singleton.
License
MIT — see LICENSE.
The bundled AMP client @neuralnexus/ampapi is dual-licensed GPL-3.0 / MIT and is used here under MIT.
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/eddinsw/amp-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server