Skip to main content
Glama
wontaeJeong

mock-mcp-server

by wontaeJeong

mock-mcp-server

A test MCP server that runs behind mcp-auth-gateway. It exists to validate the platform plumbing — not to be a real product.

Purpose

This server is used to verify:

  1. gateway → backend MCP server connectivity

  2. path-based /mock/mcp routing

  3. gateway-signed X-MCP-Identity verification

  4. propagation of the user loginid

  5. the Streamable HTTP MCP transport

  6. GitOps / Kubernetes deployment

How it fits in

client ──TLS──> mcp-auth-gateway ──/mock/mcp──> mock-mcp-server (this repo)
                (Keycloak / OAuth,              (verifies X-MCP-Identity,
                 mints X-MCP-Identity)           runs MCP tools)
  • It runs only behind the gateway. External clients reach it exclusively via https://gateway.mcp.aidev.samsungds.net/mock/mcp.

  • It does NOT do Keycloak / OAuth verification itself. All external auth is the gateway's job.

  • It DOES verify the gateway's internal X-MCP-Identity JWT on /mcp and every slash-delimited path below it, rejecting anything missing or invalid with 401.

  • Tools read the caller's identity — especially loginid — through a shared get_current_user() helper; they never parse HTTP headers directly.

Identity: X-MCP-Identity

The gateway injects a signed internal JWT:

X-MCP-Identity: <gateway-signed-internal-jwt>

Example payload:

{
  "iss": "mcp-auth-gateway",
  "aud": "mock-mcp-server",
  "sub": "keycloak-user-sub",
  "loginid": "user.loginid",
  "username": "user.name",
  "email": "user@example.com",
  "groups": ["engineering"],
  "scopes": ["mcp:mock:use"],
  "request_id": "01J...",
  "iat": 1730000000,
  "nbf": 1730000000,
  "exp": 1730000060
}

Verified on every /mcp request:

  • JWT signature

  • iss == mcp-auth-gateway

  • aud == mock-mcp-server

  • integer iat, nbf, and exp, with five seconds of clock skew

  • exp > iat, exp >= nbf, and a maximum lifetime of five minutes

  • canonical, non-empty sub, loginid, and optional scalar/list values

  • exactly one non-empty X-MCP-Identity header (duplicates and combined values are rejected)

The MVP uses shared-secret HS256 via MCP_IDENTITY_JWT_SECRET; the secret must contain at least 32 bytes. The verifier lives behind a small abstraction (identity.IdentityVerifier) so a production RS256 / JWKS implementation can be dropped in without touching tools or middleware.

loginid usage

loginid is the primary per-user key. Tools use it to build deterministic, user-scoped mock data (mock_profile, mock_search) and to echo the caller's identity (echo, whoami).

Endpoints

Method

Path

Auth

Purpose

GET

/healthz

none

liveness

GET

/readyz

none

readiness

ANY

/mcp[/...]

X-MCP-Identity (JWT)

Streamable HTTP MCP endpoint

Requests to /mcp or /mcp/... without a valid X-MCP-Identity are rejected at the HTTP layer with 401 Unauthorized before reaching any tool. Similar names such as /mcpish are not part of this authentication boundary.

MCP tools

Tool

Input

Returns

whoami

full identity asserted by the gateway

echo

message: str

{ loginid, message }

mock_profile

deterministic mock profile derived from loginid

mock_search

query: str

deterministic, user-scoped mock search results

Configuration

All configuration is via environment variables.

Variable

Default

Notes

APP_HOST

0.0.0.0

APP_PORT

8080

container port

MCP_ENDPOINT

/mcp

Streamable HTTP path

MCP_IDENTITY_ISSUER

mcp-auth-gateway

required iss

MCP_IDENTITY_AUDIENCE

mock-mcp-server

required aud

MCP_IDENTITY_ALGORITHM

HS256

verifier algorithm

MCP_IDENTITY_JWT_SECRET

required, at least 32 bytes unless dev mode is on

MCP_IDENTITY_DEV_MODE

false

if true, unauthenticated calls get a dummy user

LOG_LEVEL

info

  • MCP_IDENTITY_JWT_SECRET is mandatory in production; the server refuses to start if it is missing or shorter than 32 bytes.

  • MCP_IDENTITY_DEV_MODE=true is only for local development. It defaults to false and must never be enabled in the cluster.

Local development

Requirements: Python ≥ 3.11 and uv.

# 1. install
uv sync --locked --extra dev

# 2. run the server
export MCP_IDENTITY_JWT_SECRET=dev-only-secret-that-is-at-least-32-bytes
export MCP_IDENTITY_DEV_MODE=false
python -m mock_mcp_server.main

Generate a dev identity token

Without the gateway you must supply your own X-MCP-Identity JWT:

export MCP_IDENTITY_JWT_SECRET=dev-only-secret-that-is-at-least-32-bytes

python scripts/make-dev-identity-token.py \
  --loginid test.user \
  --subject test-sub \
  --aud mock-mcp-server
# ->  export X_MCP_IDENTITY='<jwt>'

eval "$(python scripts/make-dev-identity-token.py --loginid test.user --subject test-sub)"

Test with curl

/mcp speaks Streamable HTTP (JSON-RPC). In stateless JSON mode you can call a tool directly:

# health (no auth)
curl -s localhost:8080/healthz

# whoami (requires identity)
curl -s localhost:8080/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "X-MCP-Identity: ${X_MCP_IDENTITY}" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'

# missing identity -> 401
curl -i -s localhost:8080/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Test with MCP Inspector

Use the integrated gateway/Keycloak workflow from the sibling GitOps repository rather than connecting Inspector directly to this internal backend:

cd ../mcp-platform-gitops
scripts/local-build-push.sh
scripts/local-up.sh
scripts/local-inspector.sh

The launcher pins @modelcontextprotocol/inspector@0.22.0. In its OAuth settings use client ID mcp-inspector, no client secret, scope openid mcp:mock:use, and the preselected Streamable HTTP gateway URL http://gateway.localhost:8080/mock/mcp.

Run the tests

uv run pytest        # or: .venv/bin/python -m pytest

Docker

Image name (pushed to cr.aidev.samsungds.net/mcp-platform):

cr.aidev.samsungds.net/mcp-platform/mock-mcp-server:<commit-sha>

Build & push:

REGISTRY=cr.aidev.samsungds.net \
IMAGE_TAG="$(git rev-parse HEAD)" \
CA_CERT_FILE=/path/to/system-ca.pem \
PUSH=1 \
scripts/build-and-push.sh

The image installs production dependencies from the checked-in uv.lock with frozen semantics, runs as a non-root user, exposes 8080, and ships a HEALTHCHECK against /readyz. Tag images by commit SHA (no latest in the cluster) so GitOps pins an immutable deployment reference.

Deployment (mcp-platform-gitops)

Full Kubernetes / GitOps manifests live in mcp-platform-gitops, not here. This repo only ships the app and image. The gitops repo should deploy it as a ClusterIP Service with these values:

Setting

Value

Namespace

mcp-gateway

Service name

mock-mcp-server

Service type

ClusterIP

Container port

8080

Internal DNS

http://mock-mcp-server.mcp-gateway.svc.cluster.local:8080

Internal MCP URL

http://mock-mcp-server.mcp-gateway.svc.cluster.local:8080/mcp

External MCP URL

https://gateway.mcp.aidev.samsungds.net/mock/mcp (gateway)

Required Secret (mcp-internal-signing, key jwt-secret) holds the shared HS256 secret, which must match the gateway's signing secret and contain at least 32 bytes.

Container env:

env:
  - name: MCP_IDENTITY_JWT_SECRET
    valueFrom:
      secretKeyRef:
        name: mcp-internal-signing
        key: jwt-secret
  - name: MCP_IDENTITY_ISSUER
    value: mcp-auth-gateway
  - name: MCP_IDENTITY_AUDIENCE
    value: mock-mcp-server

Probes: GET /healthz (liveness), GET /readyz (readiness), both on 8080.

Do NOT create a public Ingress

This service must never be exposed with its own public Ingress / LoadBalancer.

  • It performs no external authentication (no Keycloak, no OAuth). It only trusts the gateway-signed X-MCP-Identity. A direct public route would let anyone reach /mcp while bypassing the gateway's real authentication.

  • All external access must go through the gateway, which authenticates the user, mints X-MCP-Identity, and routes /mock/mcp to this ClusterIP Service.

The only external entry point is:

https://gateway.mcp.aidev.samsungds.net/mock/mcp

Project layout

src/mock_mcp_server/
  __init__.py
  main.py          # ASGI app factory + uvicorn entrypoint
  settings.py      # env-based configuration (fails fast)
  identity.py      # X-MCP-Identity JWT verification (HS256; RS256-ready)
  middleware.py    # pure-ASGI identity enforcement (401) + scope injection
  context.py       # McpUserContext + get_current_user()
  tools.py         # whoami / echo / mock_profile / mock_search
scripts/
  make-dev-identity-token.py
tests/

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/wontaeJeong/mock-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server