amp-mcp-server

amp-mcp-server

MCP server that wraps CubeCoders AMP to list, inspect, and control game-server instances.

Category
Visit Server

README

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 instances
  • amp_get_instance_status — state, uptime, CPU/RAM/players for one instance
  • amp_get_active_users — connected users for one instance
  • amp_get_console_output — recent console lines for one instance
  • amp_get_host_status — state, uptime, CPU/RAM for the AMP controller host itself
  • amp_get_running_tasks — currently-running tasks on one instance (with progress %)
  • amp_get_update_info — pending game-server updates for one instance
  • amp_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 lifecycle
  • amp_sleep_instance — soft shutdown (resumable faster than Stop; module-dependent)
  • amp_send_console_command — send a command to one instance's console
  • amp_take_backup — trigger a backup (poll completion via amp_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
AMP_URL yes Base URL of your AMP install, e.g. https://amp.example.local
AMP_USERNAME yes AMP admin username
AMP_PASSWORD yes AMP password (or remembered-token)
AMP_ALLOW_WRITES no false Set true to enable mutating tools
MCP_TRANSPORT no stdio stdio (subprocess use) or http (Docker / remote)
MCP_PORT no 3000 HTTP listen port (HTTP transport only)
MCP_HOST no 127.0.0.1 HTTP bind host (HTTP transport only). Docker image overrides to 0.0.0.0.
MCP_ALLOWED_HOSTS no Comma-separated Host header allow-list (DNS rebinding protection). Required when MCP_HOST is 0.0.0.0/:: and MCP_AUTH_MODE=none.
MCP_ALLOWED_ORIGINS no Comma-separated Origin allow-list. Requests with a mismatching Origin get 403; requests with no Origin (server-to-server) are allowed.
MCP_TRUST_PROXY no Forwarded to Express trust proxy. Set when behind nginx/Caddy so req.ip is the real client.
MCP_ALLOW_INSECURE no false Override the startup guard that refuses 0.0.0.0 + none auth + no host allow-list.
MCP_RATE_LIMIT no 120 Max requests per window on /mcp. Set 0 to disable.
MCP_RATE_WINDOW_MS no 60000 Rate-limit window length in milliseconds.
MCP_AUTH_MODE no none none / bearer / oauth — see Authentication
MCP_PUBLIC_URL when oauth Canonical external URL of this server (resource id + JWT audience)
MCP_AUTH_TOKEN when bearer Comma-separated list of accepted bearer tokens
MCP_OAUTH_ISSUER when oauth OAuth 2.1 authorization server issuer URL
MCP_OAUTH_AUDIENCE no MCP_PUBLIC_URL Override expected JWT aud claim
MCP_OAUTH_JWKS_URL no OIDC-discovered Override JWKS URL (skips OIDC discovery)
MCP_OAUTH_REQUIRED_SCOPES no Comma-separated scopes required on every request
LOG_LEVEL no info pino log level: trace / debug / info / warn / error / fatal. All logs go to stderr.

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 -f

The 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 start

Inspect tools interactively:

npx @modelcontextprotocol/inspector node dist/index.js

Authentication

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
none (default) stdio, or HTTP bound to 127.0.0.1 / private network only (Tailscale, WireGuard, LAN) No auth at all. Network-layer trust is the only thing keeping callers out.
bearer Exposing HTTP to one or two clients you control (e.g. a personal cloud VM) Static Authorization: Bearer <token> check. Constant-time compare.
oauth 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 serveramp-mcp-server itself. Validates incoming JWTs, serves tools. Has no callback URL and never participates in the redirect flow. Lives at MCP_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 clientClaude.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-server

So 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:write

The 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 https://auth.example.com/realms/<realm> (a realm is a Keycloak tenant — its own users/clients/roles)
Duende IdentityServer https://auth.example.com (bare, no path)
Auth0 https://<tenant>.auth0.com/
Authentik https://authentik.example.com/application/o/<slug>/
Okta https://<org>.okta.com/oauth2/<server-id>

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-resource

so 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)
  • iss matches MCP_OAUTH_ISSUER
  • aud includes MCP_OAUTH_AUDIENCE (default: MCP_PUBLIC_URL)
  • exp is in the future
  • all MCP_OAUTH_REQUIRED_SCOPES (if set) are present in the scope or scp claim

Important: MCP_PUBLIC_URL must 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-dev

In the Keycloak admin UI:

  1. Create a realm (e.g. amp).
  2. Create a client amp-mcp-test, client type OpenID Connect, public, with PKCE; standard flow enabled.
  3. Create a user, set a password.
  4. Add a client scope mcp:read, mapped as a default scope.
  5. 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 start

Verify the PRM endpoint:

curl http://localhost:3000/.well-known/oauth-protected-resource

Verify 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" }
  }
}

Install as a CubeCoders AMP instance

CubeCoders AMP custom application templates for amp-mcp-server live in a dedicated repo: eddinsw/amp-templates. Two variants are available — host-process (any AMP tier) and Docker (AMP Enterprise + Docker-instances).

Quick install: in the AMP web UI go to Configuration → Instance Deployment → Configuration Repositories, add eddinsw/amp-templates:main, click Fetch Latest. Both amp-mcp-server and amp-mcp-server (Docker) then appear in the New Instance wizard.

The Docker variant pulls ghcr.io/eddinsw/amp-mcp-server:latest, published from this repo by .github/workflows/publish-image.yml on every tag and main push.

Full walkthrough, variant comparison, configuration reference, and troubleshooting: see the amp-templates README.

Production deployment

For exposure beyond your local machine:

  1. 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.

  2. 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 upstream
    
  3. Pick an auth mode. bearer for one or two known clients; oauth for multi-user or any client that expects spec-compliant MCP auth (e.g. Claude.ai connecting to a remote MCP server).

  4. MCP_PUBLIC_URL must 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 refusing to start: MCP_HOST binds to all interfaces... Unsafe-binding safety guard Set MCP_AUTH_MODE=bearer/oauth, set MCP_ALLOWED_HOSTS, or override with MCP_ALLOW_INSECURE=true
OAuth: 401 with token that looks valid JWT aud doesn't match MCP_OAUTH_AUDIENCE (or MCP_PUBLIC_URL if audience override is unset) Decode the JWT and check the aud array contains the configured audience exactly
OAuth: 401 invalid_token after a successful login Token issuer mismatches MCP_OAUTH_ISSUER, or AS rotated signing keys and JWKS cache is stale Verify MCP_OAUTH_ISSUER matches the token's iss; restart server to flush JWKS cache
All clients get 429 after one client misbehaves All traffic appearing as one IP because MCP_TRUST_PROXY isn't set Set MCP_TRUST_PROXY to the proxy CIDR or loopback
Duende: invalid_scope even though the scope shows on the client Scope is in client's allowed-scopes list but isn't defined as an ApiScope in IdentityServer 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 RequireConsent on the client as a workaround, or fix Duende's cookie config
npm test fails with "no tests" but no errors vitest cache flake during back-to-back build + test invocations Re-run npm test

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.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured