starlink-enterprise-mcp
This server acts as an MCP proxy for the Starlink Enterprise API, enabling AI agents to manage Starlink services through auto-generated tools and a secure OAuth flow. Key capabilities include:
Account & Billing
Retrieve account details, available products, and query data usage
Get billing partial periods for proration details
Service Lines
List, create, update, and deactivate service lines
Set nicknames, change products/subscriptions, manage public IP settings
Opt in/out of priority data, manage data top-ups and recurring data blocks
Add/remove user terminals, set pool consumption behavior
User Terminals (Dishes)
List, add, remove, and reboot terminals remotely
Assign configurations and manage L2VPN VLANs
Routers
Get router details; create, update, and assign router configs
Manage TLS certificates, local content files, sandbox clients, and reboot routers remotely
Addresses & Contacts
Create, read, update addresses
Create, read, update, and delete contacts
Data Pools (Pre-Release)
View data pools and usage; enable/disable automatic top-up
Specialized Features
Post aviation flight events (departure, arrival, metadata) for aviation accounts
Create managed customer accounts for provider/reseller accounts
Infrastructure
Secure OAuth proxy β credentials never touch the AI model's context
Persistent sessions with automatic token refresh
Flexible deployment (local, Cloud Run, Fly.io, Kubernetes) and multiple auth modes (multi-tenant, pass-through, single-account)
Operator controls to disable specific tools or destructive actions via environment variables
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., "@starlink-enterprise-mcplist my Starlink terminals and their status"
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.
Starlink Enterprise MCP Server
π°οΈ 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_credentialsgrant; 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
HttpOnlycookies, 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
outputSchemaderived from the OpenAPI response, and returns matchingstructuredContent. 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 |
|
|
MFA | yes | none (service accounts skip MFA) |
Refresh | upstream refresh token | re-run |
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 stdioCreate 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 startConnect 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=passthroughIn 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
/mcpand 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 |
Firestore (native mode) | Persistent token store and DCR client registry |
Cloud Run SA β | Firestore access |
gcloud builds submit --config cloudbuild.yaml --project=<your-project>Required env vars on Cloud Run:
Var | Notes |
| enable the HTTP transport |
| public URL, e.g. |
| 32+ chars; signs login-state cookies & must be stable across instances |
| enable Firestore-backed tokens and clients |
| Firestore project ID (auto-set on Cloud Run) |
Transport and protocol options:
Var | Default | Notes |
|
|
|
|
| Return plain JSON instead of SSE, for intermediaries that break event streams |
| unset | Comma-separated allowlist; a non-matching |
|
|
|
|
| Page size for |
|
| Enable task augmentation (see above) |
|
| Firestore collection for task state |
| Starlink API docs |
|
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_URLnow does double duty: it still serves the favicon and login-page logo, and it is also advertised as the server's MCPiconsentry 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_SECRETand service-account secrets per Starlink's guidance if exposure is suspected.
π OAuth flow (detailed)
AI client hits
GET /.well-known/oauth-protected-resource/mcpand/.well-known/oauth-authorization-serverfor discovery.AI client POSTs
/register(RFC 7591 DCR). Public clients passtoken_endpoint_auth_method=noneand get back aclient_idonly; confidential clients also get aclient_secret. Registrations persist in Firestore.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.User submits their Service Account Client ID + Client Secret β server runs
POST {STARLINK_TOKEN_URL}withgrant_type=client_credentials. On success it stores the Starlink token + credentials and issues an authorization code.The server redirects back to the AI client; cookies are cleared.
AI exchanges the code at
/tokenfor the MCP-issued bearer + refresh token.On every
/mcprequest, the server verifies the bearer and transparently re-mints the upstream Starlink token if it's near expiry. On a401from 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. |
| 2025-06-18 | Unsupported version β 400 |
Structured output ( | 2025-06-18 | 52 of 55 tools; |
Tool | 2025-06-18 | All tools |
OAuth Resource Server + protected-resource metadata | 2025-06-18 | RFC 9728 discovery, |
No JSON-RPC batching | 2025-06-18 | Not accepted |
Icons on server and tools | 2025-11-25 (SEP-973) | From |
| 2025-11-25 | Sent at initialize |
Invalid | 2025-11-25 | Via |
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 |
| 2024-11-05 | Opt-in via |
| 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 instanceTask 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 |
|
Service Lines |
|
User Terminals |
|
Routers |
|
Addresses |
|
Contacts |
|
Data Pools |
|
Flights |
|
Managed |
|
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 testnpm run build runs generate automatically via the prebuild hook.
π§ͺ Tests
npm testThe Firestore-backed tests are emulator-gated and skip cleanly without one.
π What this server is
Two MCP transports.
stdiofor local CLI integrations andhttp(Streamable HTTP, stateless by default) for hosted deployments. Production useshttp.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
structuredContentresults.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
Maintenance
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
- Alicense-qualityDmaintenanceAn MCP server that enables AI agents to interact with the SpaceTraders API, managing agents, fleets, contracts, and trading operations in the SpaceTraders universe.Last updatedMIT
- Alicense-qualityAmaintenanceA 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 updatedMIT
- FlicenseAqualityFmaintenanceMCP 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 updated53
- Alicense-qualityDmaintenanceA production-grade MCP server designed for multi-tenant, authenticated, and observable AI agent systems, enabling secure tool execution across heterogeneous data sources.Last updated54MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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