Skip to main content
Glama

kpn-mcp

MCP server for KPN IRMA APIs hosted by Grexx (acceptatie: service-accept.grexx.today), aimed at MSP helpdesks. The default surface is realtime XML over OAuth 2.0 client credentials. The SDK sends HTTP Basic to the token endpoint. POST /realtime uses the Bearer token.

Built on the MCP 2026-07-28 spec via the split v2 SDK (@modelcontextprotocol/server / /node / /client ^2.0.0-beta.5) with dual-era serving: one shared McpServerFactory behind createMcpHandler({ legacy: 'stateless' }) answers both 2025-era initialize-handshake clients and modern 2026-07-28 envelope clients, with the same tool list for every caller. Ships as a GHCR container only (no MCPB bundle). The client is @wyre-ai/node-kpn (Grexx export). Contract: docs/GREXX.md.

Tools (15, flat)

  • kpn_grexx_test_connection: the SDK mints an OAuth client-credentials token and posts a ZipCodeCheckRequest_V6 probe for the public reference address 1012JS 1 (portfolio All). Success means the token endpoint and POST /realtime accepted the Bearer token.

  • kpn_grexx_zipcode_check: technology and speeds at a Dutch address (ZipCodeCheckRequest_V6 → ZipCodeCheckResponse_V5).

  • kpn_grexx_prequalification: address and product-type availability per supplier (PrequalificationRequest_V2 → PrequalificationResponse_V1). When hasBroadband is true, serviceId or referencePhoneNumber is required.

  • kpn_grexx_order_data: customer id, product code, and quantity for an order id (OrderDataRequest_V1 → OrderDataResponse_V1).

  • 11 more realtime tools built from the portal request XSDs (see docs/GREXX.md): kpn_grexx_carrier_info, kpn_grexx_radius_check, kpn_grexx_ras_check, kpn_grexx_start_line_diagnose, kpn_grexx_customer_data, kpn_grexx_order_summary, kpn_grexx_get_sim, kpn_grexx_mobile_settings, kpn_grexx_mobile_usage, kpn_grexx_mobile_orders, kpn_grexx_available_portings. start_line_diagnose starts a test at KPN, so it is not read-only and is never retried. SIM codes and the RadiusCheck PPP password are masked.

IRMA enforces XSD element order and rejects a bad request with an HTTP 200 NinaResponse; both are handled by the server. Queued writes and Proxymodule notifications are not exposed.

Set KPN_LEGACY_DEVELOPER_API=1 to also serve the previous 23 developer.kpn.com tools (disturbances, availability, SIM swap, MSM). They are absent from the default list. See docs/DESIGN.md for that catalog. Gated MSM writes still require confirmation.

Related MCP server: proofpoint-essentials-mcp

Credentials

Grexx username and password are the OAuth client_id and client_secret (scope=all). Copy the interface root from the partner portal (API Gegevens). There is no default base URL: acceptatie and production differ.

Env var (env mode)

Gateway header (AUTH_MODE=gateway)

Required

KPN_GREXX_USERNAME

X-KPN-Grexx-Username

yes

KPN_GREXX_PASSWORD

X-KPN-Grexx-Password

yes

KPN_GREXX_BASE_URL

X-Grexx-Api-Url (optional, per connection)

yes, unless the request sends X-Grexx-Api-Url + X-Grexx-Token-Url. No default. Interface root, without /realtime

KPN_GREXX_TOKEN_URL

X-Grexx-Token-Url (optional, per connection)

no. Default https://service-accept.grexx.today/oauth/access_token

Example (acceptatie shape — replace the interface id from API Gegevens):

export KPN_GREXX_USERNAME="..."
export KPN_GREXX_PASSWORD="..."
export KPN_GREXX_BASE_URL="https://service-accept.grexx.today/interfaces/kpn/kpn_partners_acceptatieomgeving/<interface-id>/"
# export KPN_GREXX_TOKEN_URL="https://service-accept.grexx.today/oauth/access_token"

See env.example. Token mint, cache, and Bearer retry live in @wyre-ai/node-kpn. Base or token URLs under any other header name (X-KPN-Grexx-Base-Url, ...) are rejected (HTTP 400).

In gateway mode a request missing X-KPN-Grexx-Username or X-KPN-Grexx-Password is answered 401 (JSON-RPC error -32001) before the MCP handler runs. It never falls through to env credentials.

Per-connection Grexx environment (WYREAI-525)

In gateway mode Conduit may send two headers on every call to point that connection at another Grexx environment, such as production:

Header

Value

Example

X-Grexx-Api-Url

Interface root from the portal (API Gegevens), without /realtime

https://service.grexx.today/interfaces/kpn/<interface-name>/<interface-id>

X-Grexx-Token-Url

OAuth token endpoint

https://service.grexx.today/oauth/access_token

  • Both or neither. When both are absent or empty, the sidecar uses KPN_GREXX_BASE_URL and KPN_GREXX_TOKEN_URL, also when the credential headers are sent without the URL headers. KPN_GREXX_BASE_URL has no default and is then required; KPN_GREXX_TOKEN_URL is optional and defaults to the acceptatie token endpoint (https://service-accept.grexx.today/oauth/access_token). Exactly one URL header is HTTP 400.

  • Honoured only with X-KPN-Grexx-Username / X-KPN-Grexx-Password; without them the request is 401.

  • Scheme and authority: https: only, port 443 only, no userinfo, no query string, no fragment.

  • Exact-host allowlist (GREXX_ALLOWED_HOSTS in @wyre-ai/node-kpn): service.grexx.today and service-accept.grexx.today. No *.grexx.today wildcard; IP literals never match.

  • Paths: token URL path exactly /oauth/access_token; API URL path must start with /interfaces/kpn/ and is the interface root without /realtime (the SDK appends it).

  • Any violation is HTTP 400 (JSON-RPC -32600), and the message never echoes the URL.

  • Private-IP blocking: with header URLs the SDK resolves the host before every token mint and /realtime post, and again at socket connect (undici fetch with a guarded Agent), and refuses private, loopback, link-local (incl. 169.254.169.254), CGNAT, reserved/benchmark, ULA and metadata addresses.

  • Redirects are refused: any 3xx fails the call.

  • Every other URL header name (X-KPN-Grexx-Base-Url, ...) is still rejected with HTTP 400.

HTTP authentication

CONDUIT_S2S_SECRET is required when MCP_TRANSPORT=http. If it is empty, the process logs an error and exits non-zero. It never prints the secret. Known placeholders (changeme, change-me, secret, placeholder, example, your-secret, your-s2s-secret, s2s-secret, replace-me, replace-with-a-real-secret) are rejected the same way, including when MCP_ALLOW_INSECURE_DEV=1 is set.

MCP_ALLOW_INSECURE_DEV=1 is local development only. The process logs a warning, does not check X-Gateway-S2S, and listens on 127.0.0.1 only. A non-loopback MCP_HTTP_HOST (including 0.0.0.0) is ignored in that mode and the override is logged.

MCP_HTTP_HOST is honored when a real secret is set. Unset, that bind stays 0.0.0.0. Set 127.0.0.1 to keep a secret-backed process on loopback. The container image sets 0.0.0.0 and AUTH_MODE=gateway.

With a real secret, every request except /health and /healthz must include:

X-Gateway-S2S: t=<unix seconds>,v1=<hex hmac-sha256>

v1 is HMAC-SHA256 of the exact string t=<unix seconds> (the same t= prefix that starts the header), keyed with CONDUIT_S2S_SECRET, hex-encoded. The timestamp must be within 300 seconds of the server clock. A missing or invalid header is 401. /health and /healthz stay unauthenticated and do not read credentials. The stdio transport does not use CONDUIT_S2S_SECRET.

# sign "t=$NOW" with the gateway-provisioned secret
NOW=$(date +%s)
SIG=$(printf 't=%s' "$NOW" | openssl dgst -sha256 -hmac "$CONDUIT_S2S_SECRET" | awk '{print $NF}')
curl -sS -D- http://127.0.0.1:8080/mcp \
  -H "X-Gateway-S2S: t=${NOW},v1=${SIG}" \
  -H "X-KPN-Grexx-Username: ..." \
  -H "X-KPN-Grexx-Password: ..." \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

SDK dependency

Import the Grexx client from @wyre-ai/node-kpn (package root), not /legacy.

The dependency is the published range ^2.2.0 (per-connection URLs + SSRF guard from node-kpn#6; Prequalification and OrderData from node-kpn#5, tag v2.1.0). Token minting is the HTTP Basic behavior from node-kpn#4. /legacy remains the developer.kpn.com client for KPN_LEGACY_DEVELOPER_API=1 only. This server does not mint the Grexx token.

Running

export NODE_AUTH_TOKEN=$(gh auth token)   # GitHub Packages auth for @wyre-ai/*
npm install
npm run build
node dist/index.js                        # stdio (default)
# HTTP refuses to start until CONDUIT_S2S_SECRET is a real secret
CONDUIT_S2S_SECRET="$CONDUIT_S2S_SECRET" MCP_TRANSPORT=http node dist/index.js
# Local HTTP only, without a service-to-service secret (always binds 127.0.0.1)
MCP_ALLOW_INSECURE_DEV=1 MCP_TRANSPORT=http node dist/index.js
npm run smoke                             # both protocol eras serve the same Grexx tools

Docker (linux/amd64 per fleet law). The image listens on 0.0.0.0 inside the container and defaults to AUTH_MODE=gateway. Publish the port on loopback and pass the S2S secret. Grexx username and password arrive on each request as headers. KPN_GREXX_BASE_URL stays in the environment because a caller-supplied URL is rejected.

docker build --platform linux/amd64 --build-arg GITHUB_TOKEN=$(gh auth token) -t kpn-mcp .
docker run \
  -e CONDUIT_S2S_SECRET="$CONDUIT_S2S_SECRET" \
  -e KPN_GREXX_BASE_URL="https://service-accept.grexx.today/interfaces/kpn/kpn_partners_acceptatieomgeving/<interface-id>/" \
  -p 127.0.0.1:8080:8080 \
  kpn-mcp

Compose refuses to start when CONDUIT_S2S_SECRET is unset, publishes 127.0.0.1:8080:8080, and does not inject KPN_GREXX_USERNAME or KPN_GREXX_PASSWORD:

CONDUIT_S2S_SECRET="$CONDUIT_S2S_SECRET" \
KPN_GREXX_BASE_URL="https://service-accept.grexx.today/interfaces/kpn/kpn_partners_acceptatieomgeving/<interface-id>/" \
GITHUB_TOKEN=$(gh auth token) \
docker compose up --build

Single-tenant HTTP (AUTH_MODE=env) still requires the S2S secret, then uses KPN_GREXX_USERNAME and KPN_GREXX_PASSWORD:

docker run \
  -e CONDUIT_S2S_SECRET="$CONDUIT_S2S_SECRET" \
  -e AUTH_MODE=env \
  -e KPN_GREXX_USERNAME=... \
  -e KPN_GREXX_PASSWORD=... \
  -e KPN_GREXX_BASE_URL=... \
  -p 127.0.0.1:8080:8080 \
  kpn-mcp

/health is a shallow liveness probe. It does not call Grexx and does not read credentials.

Legacy developer.kpn.com tools

With KPN_LEGACY_DEVELOPER_API=1 the process also registers kpn_test_connection, kpn_disturbances_check, kpn_availability_check, kpn_sim_swap_get_date, and the MSM read/write tools. Credentials are KPN_CLIENT_ID / KPN_CLIENT_SECRET (gateway: X-KPN-Client-Id / X-KPN-Client-Secret), plus an optional MSM pair. KPN_BASE_URL stays env-only. A half MSM pair is rejected. MSM writes still create orders, are not retried, and require confirmation (confirm_destructive_action when the client cannot be prompted).

Vendor quirks encoded here

  • OAuth client_credentials, scope=all. The SDK sends HTTP Basic to the token endpoint, then Bearer on POST /realtime (Content-Type: text/xml). Plain XML, no SOAP envelope. Basic Auth is not sent on POST /realtime.

  • This server hands token minting to GrexxClient, which sends HTTP Basic first and may fall back to form-body client_id / client_secret after HTTP 400/401 invalid_client.

  • The token endpoint is not retried into a Bearer-less call. HTTP 401 remints once inside the SDK.

  • IRMA code 108 and HTTP 429 are rate limits. Code 102 is an IP allowlist rejection.

  • Success codes include Success (what acceptatie returned for ZipCodeCheck).

License

Apache-2.0 © WYRE Technology

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for Kaseya BMS PSA — tickets, accounts, time entries, and contracts. Enables AI assistants to manage service desk operations via the Kaseya BMS API.
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to manage MSP-multi-tenant email security: organizations, domains, users, licensing, features, subscription packages, and email flow reporting.
    Apache 2.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables IT helpdesk operations such as retrieving user and device information, checking device status, and creating, retrieving, and updating support tickets through MCP tools.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables a telecom support assistant to look up customers, check balances and orders, suspend lost SIMs, open tickets, and analyze SIP traces to diagnose call failures.
    MIT