Skip to main content
Glama
WiFiWithoutWalls

starlink-enterprise-mcp

Starlink Enterprise MCP Server

License: MIT TypeScript Starlink API MCP Protocol Cloud Run

πŸ›°οΈ Hosted, multi-account MCP for the Starlink Enterprise API Any AI agent β€” Claude, ChatGPT, anything that speaks MCP β€” connects with a real Starlink V2 Service Account, drives the full Enterprise API, and stays connected indefinitely. The Client Secret never touches the model.

⚑ Features

  • πŸ” Hosted OAuth proxy with API-key login β€” The server is the OAuth 2.1 authorization server. But Starlink has no interactive OAuth and no MFA, so the browser login page doesn't ask for a username and password β€” it asks for a Service Account Client ID + Client Secret. The server validates them with a client_credentials grant; credentials never enter the model's context.

  • πŸ” Transparent token re-minting β€” Starlink bearer tokens are short-lived (~15 min) and have no refresh token. The server stores the service-account credentials alongside the issued MCP token and silently re-mints a fresh bearer before expiry, and again on any 401. AI sessions stay alive across long conversations.

  • πŸͺ Stateless login state β€” OAuth pending state rides in HMAC-signed HttpOnly cookies, so logins survive container restarts and Cloud Run instance switches.

  • πŸ—„οΈ Firestore persistence β€” Issued tokens and DCR client registrations survive deploys and scaling events when MCP_PERSISTENCE=firestore.

  • 🀝 Claude and ChatGPT support β€” Public-client dynamic registration (token_endpoint_auth_method=none, PKCE only) means ChatGPT connects out of the box alongside confidential clients like Claude.

  • 🧬 55 auto-generated tools from the spec β€” The Starlink Enterprise v2 OpenAPI spec, regenerated on every build. Drop in a new spec and rebuild to pick up new endpoints.

  • 🎯 No curated layer needed β€” At 55 operations the full tool surface fits comfortably in a model's working memory, so every tool is exposed directly with read/write/destructive annotations.

  • ♾️ Stateless by default β€” No Mcp-Session-Id, no in-memory session map, no session affinity. Any instance can serve any request, so autoscaling and cold starts stop breaking mid-conversation.

  • 🧾 Typed results β€” Every tool declares an outputSchema derived from the OpenAPI response, and returns matching structuredContent. The model gets a typed object, not an opaque JSON blob.

  • πŸͺ› Operator-tunable β€” Disable globs (MCP_DISABLED_TOOLS=delete_*,*reboot*), a semantic destructive toggle (MCP_DISABLE_DESTRUCTIVE=true), branded login page (MCP_LOGIN_HEADER, MCP_ICON_URL). No code change for per-deployment policy.

  • πŸ§ͺ A real test suite β€” 108 tests, including a draft-2020-12 JSON Schema guard that compiles every tool's input and output schema on every run, and end-to-end JSON-RPC over the actual transport.

Related MCP server: m365-mcp-server

πŸ”‘ How auth differs from a username/password MCP

Username/password OAuth proxy

This server (Starlink)

Login page collects

username + password

Service Account Client ID + Client Secret

Upstream grant

password (+ MFA)

client_credentials

MFA

yes

none (service accounts skip MFA)

Refresh

upstream refresh token

re-run client_credentials (no refresh token)

Token TTL

hours

~15 min, re-minted on expiry / 401

The DCR + browser-redirect OAuth shell is identical β€” what changed is the login form and the upstream grant.

πŸ—οΈ Architecture

AI client (Claude/ChatGPT)
  β”‚  OAuth 2.1 DCR + browser login (PKCE)
  β–Ό
[ Starlink MCP HTTP server (this repo) ]   ← OAuth proxy, login page (Client ID + Secret), cookies, Firestore
  β”‚  per-account Starlink bearer (client_credentials)
  β–Ό
[ Starlink Enterprise API  https://web-api.starlink.com ]

Each issued MCP bearer maps to a stored upstream Starlink token plus the service-account credentials used to mint it, so the server can re-mint silently.

πŸ’» Running locally (stdio)

npm install
npm run build
export STARLINK_CLIENT_ID=<your-service-account-client-id>
export STARLINK_CLIENT_SECRET=<your-service-account-secret>
npm start                                      # MCP_TRANSPORT defaults to stdio

Create a V2 service account at Account Settings β†’ API V2 Service Accounts (requires the Admin or Service Account Management role).

Add this entry to your local MCP client config (Claude Desktop, etc.):

{
  "mcpServers": {
    "starlink": {
      "command": "node",
      "args": ["/path/to/starlink-enterprise-mcp/build/index.js"],
      "env": {
        "STARLINK_CLIENT_ID": "...",
        "STARLINK_CLIENT_SECRET": "..."
      }
    }
  }
}

You can also set STARLINK_ACCESS_TOKEN directly to skip the grant if you already hold a bearer.

🌐 Running as a hosted server (HTTP)

export MCP_TRANSPORT=http
export MCP_PORT=3000
export MCP_BASE_URL=https://mcp.example.com
export MCP_SESSION_SECRET=<32+ random hex>     # signs login-state cookies
npm start

Connect from Claude / ChatGPT by giving it the URL https://mcp.example.com/mcp. The client DCR-registers, redirects the user to /authorize, the user pastes their Service Account Client ID + Secret, and the bearer flows back to the AI automatically. No upstream operator credentials are needed in HTTP mode β€” each user brings their own service account.

Pass-through mode (credentials configured in the MCP client)

Set MCP_AUTH_MODE=passthrough and the connector supplies the Starlink Service Account as its OAuth client_id + client_secret (configured in Claude/ChatGPT, not on a login page). The server treats any presented client_id as a dynamic client, then at the /token exchange validates the client_secret against Starlink's client_credentials grant β€” a successful grant is the authentication. The credentials are then bound to that session and re-minted as usual. No login page, no server-side credentials, fully multi-tenant.

export MCP_AUTH_MODE=passthrough

In the client's connector setup, point it at https://…/mcp and enter your Starlink Service Account Client ID and Client Secret as the OAuth client credentials. Requirements: the client must use the authorization-code flow with PKCE and send the client_secret at the token endpoint (client_secret_post).

Single-account mode (skip the login page)

If you set STARLINK_CLIENT_ID + STARLINK_CLIENT_SECRET on the server, the /authorize step auto-logs-in with those and the credential-entry page is never shown β€” every user who connects shares that one Starlink account. Leave them unset for the multi-tenant login-page behavior above.

export STARLINK_CLIENT_ID=<service-account-id>
export STARLINK_CLIENT_SECRET=<service-account-secret>

Trade-off: in single-account mode the endpoint is only as private as its URL β€” DCR registration is open, so anyone who can reach /mcp and complete the (credential-free) OAuth flow uses that shared account. Put it behind access control, or accept that the URL is the secret.

☁️ Cloud Run deployment

Ships with a Cloud Run-friendly Dockerfile and cloudbuild.yaml.

Component

Purpose

Cloud Run service

Runs the HTTP server. No session affinity or min-instances needed in the default stateless mode

Firestore (native mode)

Persistent token store and DCR client registry

Cloud Run SA β†’ roles/datastore.user

Firestore access

gcloud builds submit --config cloudbuild.yaml --project=<your-project>

Required env vars on Cloud Run:

Var

Notes

MCP_TRANSPORT=http

enable the HTTP transport

MCP_BASE_URL

public URL, e.g. https://mcp.example.com

MCP_SESSION_SECRET

32+ chars; signs login-state cookies & must be stable across instances

MCP_PERSISTENCE=firestore

enable Firestore-backed tokens and clients

GOOGLE_CLOUD_PROJECT

Firestore project ID (auto-set on Cloud Run)

Transport and protocol options:

Var

Default

Notes

MCP_STATELESS

true

false restores Mcp-Session-Id sessions (single instance only)

MCP_JSON_RESPONSE

false

Return plain JSON instead of SSE, for intermediaries that break event streams

MCP_ALLOWED_ORIGINS

unset

Comma-separated allowlist; a non-matching Origin gets 403

MCP_STRUCTURED_OUTPUT

true

false drops outputSchema and structuredContent together

MCP_TOOLS_PAGE_SIZE

0 (one page)

Page size for tools/list cursor pagination

MCP_TASKS

false

Enable task augmentation (see above)

MCP_TASKS_COLLECTION

mcp_tasks

Firestore collection for task state

MCP_WEBSITE_URL

Starlink API docs

websiteUrl advertised at initialize

Also optional: STARLINK_API_URL, STARLINK_TOKEN_URL (defaults are correct for production), MCP_LOGIN_HEADER, MCP_ICON_URL, MCP_LOGIN_LOGO_URL, MCP_DISABLED_TOOLS, MCP_DISABLED_ACTIONS, MCP_DISABLE_DESTRUCTIVE, MCP_CORS_ORIGIN.

MCP_ICON_URL now does double duty: it still serves the favicon and login-page logo, and it is also advertised as the server's MCP icons entry so clients can render it in a connector list.

Other targets: fly.toml (Fly.io), render.yaml (Render), railway.toml (Railway), docker-compose.yml, and k8s/ manifests (apply with kubectl apply -k k8s/).

Security note on persistence. In HTTP mode the issued-token records hold each user's Starlink service-account Client ID + Secret so the server can re-mint bearers. Protect the token store accordingly β€” restrict the Firestore collection / file volume, and rotate MCP_SESSION_SECRET and service-account secrets per Starlink's guidance if exposure is suspected.

πŸ” OAuth flow (detailed)

  1. AI client hits GET /.well-known/oauth-protected-resource/mcp and /.well-known/oauth-authorization-server for discovery.

  2. AI client POSTs /register (RFC 7591 DCR). Public clients pass token_endpoint_auth_method=none and get back a client_id only; confidential clients also get a client_secret. Registrations persist in Firestore.

  3. AI redirects the user's browser to /authorize?... with PKCE parameters. The server stores the pending request in a signed cookie (mcp_pending_auth, 15 min TTL) and renders the login page.

  4. User submits their Service Account Client ID + Client Secret β†’ server runs POST {STARLINK_TOKEN_URL} with grant_type=client_credentials. On success it stores the Starlink token + credentials and issues an authorization code.

  5. The server redirects back to the AI client; cookies are cleared.

  6. AI exchanges the code at /token for the MCP-issued bearer + refresh token.

  7. On every /mcp request, the server verifies the bearer and transparently re-mints the upstream Starlink token if it's near expiry. On a 401 from the API, the client re-mints and retries once.

πŸ“ MCP spec conformance

Targets MCP 2025-11-25 and negotiates down to any revision the client asks for (2025-06-18, 2025-03-26, 2024-11-05).

Feature

Revision

Status

Streamable HTTP, stateless

2025-03-26

Default. MCP_STATELESS=false for sessions

MCP-Protocol-Version header validation

2025-06-18

Unsupported version β†’ 400

Structured output (outputSchema / structuredContent)

2025-06-18

52 of 55 tools; MCP_STRUCTURED_OUTPUT=false to disable

Tool title display names

2025-06-18

All tools

OAuth Resource Server + protected-resource metadata

2025-06-18

RFC 9728 discovery, WWW-Authenticate

No JSON-RPC batching

2025-06-18

Not accepted

Icons on server and tools

2025-11-25 (SEP-973)

From MCP_ICON_URL

Implementation.description, title, websiteUrl

2025-11-25

Sent at initialize

Invalid Origin β†’ 403

2025-11-25

Via MCP_ALLOWED_ORIGINS

Validation errors as tool errors, not protocol errors

2025-11-25 (SEP-1303)

Arguments validated, types coerced

Tool-name format guidance

2025-11-25 (SEP-986)

Validated at startup

JSON Schema 2020-12 as default dialect

2025-11-25 (SEP-1613)

Input and output schemas

Tasks (durable requests, polling, deferred results)

2025-11-25 (SEP-1686)

Opt-in via MCP_TASKS=true

tools/list cursor pagination

2024-11-05

Opt-in via MCP_TOOLS_PAGE_SIZE

logging capability + logging/setLevel

2024-11-05

Supported

Not implemented, and why: resources and prompts (this server exposes an API surface, not documents or templates), completions (nothing to complete without prompts or resource templates), sampling, elicitation, and roots (client-side features this server has no use for β€” every tool call is fully specified by its arguments).

Stateless vs. session mode

Stateless is the default. Each request gets a fresh Server and transport, and no Mcp-Session-Id is issued.

This matters on any autoscaled host. With sessions, initialize builds in-memory state on one instance, and the next tools/call gets load-balanced to an instance that has never heard of that session ID β€” the client sees Invalid or missing session ID and the conversation dies. Stateless has no affinity requirement, so min-instances=1 and session affinity stop being load-bearing.

Nothing is given up here: this server sends no server-initiated messages. The tool list is fixed at build time from the OpenAPI spec, and there are no resources or prompts to subscribe to, so the standalone GET /mcp SSE stream that sessions exist to support has nothing to carry. In stateless mode it answers 405 rather than opening a stream that can never produce anything.

Set MCP_STATELESS=false on a single-instance deployment to restore sessions.

Tasks

Off by default. A task-augmented tools/call returns a handle immediately and the client collects the result later via tasks/result, decoupling the tool's runtime from the HTTP request's lifetime.

Enabling it on Cloud Run needs two things that are not the default:

  • --no-cpu-throttling, or the container is frozen once the response is sent and the detached work never finishes.

  • MCP_PERSISTENCE=firestore, or the poll lands on an instance that has never heard of the task. With Firestore the store is shared and any instance can answer. Without it you get an in-memory store and a startup warning.

export MCP_TASKS=true
export MCP_PERSISTENCE=firestore     # required for more than one instance

Task documents live in mcp_tasks (override with MCP_TASKS_COLLECTION) and carry an expiresAt field β€” set a Firestore TTL policy on it to have Firestore reclaim them. Client-requested TTLs are clamped to 24 hours.

🧰 Tools

55 tools generated from spec/starlink-enterprise-v2.json, grouped by tag:

Group

Examples

Account

get_account, get_products, post_data_usage_query

Service Lines

get_service_lines, post_service_lines, put_service_line_nickname, post_service_line_data_top_up, patch_service_line_consume_from_pool

User Terminals

get_user_terminals, post_user_terminals, post_user_terminal_reboot, put_user_terminal_l2vpn

Routers

get_router, get_routers_configs, post_routers_configs, post_router_reboot, *_routers_configs_tls

Addresses

get_addresses, post_addresses, get_address, put_address

Contacts

get_contacts, post_contacts, put_contact, delete_contact

Data Pools

get_data_pools, get_data_pools_usage, post_data_pools_by_data_pool_id_set_automatic_top_up

Flights

post_flights_status (aviation accounts)

Managed

post_managed_customers (provider accounts)

Each tool carries a human-readable title, an inputSchema, an outputSchema, and the full annotation set: readOnlyHint, destructiveHint, idempotentHint (GET/PUT/DELETE), and openWorldHint. Reboots and deletes are flagged destructive β€” hide them all with MCP_DISABLE_DESTRUCTIVE=true, or selectively with e.g. MCP_DISABLED_TOOLS=delete_*,*reboot*.

Tool names map 1:1 to operations ({method}_{path}, with the /public/v2 prefix stripped). Two deep service-line paths are abbreviated to fit the MCP 64-character name limit.

Result shape

Results carry structuredContent matching the tool's outputSchema, shaped like the Starlink response envelope β€” payload under content, plus isValid:

{
  "content": [{ "type": "text", "text": "{ \"content\": { \"accountNumber\": \"ACC-…\" } }" }],
  "structuredContent": { "content": { "accountNumber": "ACC-…", "regionCode": "US" }, "isValid": true }
}

The schemas are deliberately permissive: no required, no additionalProperties: false, and nullable fields widened to a type union. A Starlink response that has drifted from the published spec still validates rather than being rejected by a strict client. If a client is still unhappy, MCP_STRUCTURED_OUTPUT=false drops both the schemas and the structured results in one move.

Errors come back in the result, not as protocol errors. A permission failure, a bad argument, or an operator-disabled tool returns isError: true with an explanatory message, so the model can read what went wrong and retry. Only an unknown tool name is a JSON-RPC error. Arguments are validated against the input schema before any call is made, and obvious type mismatches ("50" for a number) are coerced rather than rejected.

πŸ”„ Regenerating tools

The spec lives at spec/starlink-enterprise-v2.json (sourced from https://web-api.starlink.com/enterprise/swagger/v2/swagger.json). To refresh:

# drop a new spec into spec/starlink-enterprise-v2.json, then:
npm run generate      # rewrites src/generated/
npm run build
npm test

npm run build runs generate automatically via the prebuild hook.

πŸ§ͺ Tests

npm test

The Firestore-backed tests are emulator-gated and skip cleanly without one.

πŸ“‹ What this server is

  • Two MCP transports. stdio for local CLI integrations and http (Streamable HTTP, stateless by default) for hosted deployments. Production uses http.

  • MCP 2025-11-25, negotiating down to older revisions on request.

  • Auto-generated tools from the Starlink Enterprise v2 OpenAPI spec, regenerated on every build, with typed structuredContent results.

  • Hosted OAuth login where the login page collects Starlink Service Account credentials (Client ID + Secret), not a username/password. MFA does not apply to service accounts.

  • Transparent token re-minting via client_credentials (no refresh token).

  • Firestore persistence for tokens and DCR clients when MCP_PERSISTENCE=firestore.

License

MIT

Install Server
A
license - permissive license
A
quality
B
maintenance

Maintenance

–Maintainers
–Response time
–Release cycle
–Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    An MCP server that enables AI agents to interact with the SpaceTraders API, managing agents, fleets, contracts, and trading operations in the SpaceTraders universe.
    Last updated
    MIT
  • A
    license
    -
    quality
    A
    maintenance
    A production-ready MCP server that provides secure, delegated access to Microsoft 365 services including Email, SharePoint, OneDrive, and Calendar. It enables AI models to search messages, browse files, manage calendar events, and parse document contents using OAuth 2.1 authentication.
    Last updated
    MIT
  • F
    license
    A
    quality
    F
    maintenance
    MCP server that exposes 300+ AI agents as tools via a single API key. Supports listing agents, invoking any agent with chat-completion style messages, checking agent health, and retrieving platform statistics.
    Last updated
    5
    3

View all related MCP servers

Related MCP Connectors

  • Hosted MCP endpoint with realistic fake data for prototyping agents. 12 tools, no setup.

  • Hosted MCP with 91 agent tools: X, domains, SEO, Maps, Trends, Search, YouTube, TikTok, and more.

  • Hosted Google Calendar MCP server for AI agents. No self-hosting or Google Cloud setup.

View all MCP Connectors

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/WiFiWithoutWalls/starlink-enterprise-mcp'

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