twingate-mcp
Use this read-only MCP server to answer questions about a Twingate network without creating, changing, or deleting anything.
List Twingate users and look up one user by exact email.
List groups with type, active status, and member count.
List resources with address, remote network name, and active status.
List remote networks with location, active status, and connector count.
List connectors with remote network, state, and last heartbeat to check health.
List devices with OS, owning user email, trust status, and last seen time.
Show which groups and service accounts can access a specific resource by resource id.
Show a user's groups and the resources reachable through those groups by email.
List tools accept an optional
limitand returntotal_returned/truncatedso you know whether results are incomplete.
Click on "Deploy 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., "@twingate-mcp list all resources and their remote network names"
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.
twingate-mcp
A read-only MCP server that lets Claude (or any MCP client) answer questions about a Twingate network: users, groups, resources, remote networks, connectors, devices, and who can access what. It never creates, changes, or deletes anything.
One server process talks to one Twingate network with one API token.
1. Create a read-only API token
In the Twingate Admin Console go to Settings → API, generate a token, and choose the Read-only permission. Treat it like a password.
Related MCP server: Google Ads MCP Server
2. Install
git clone <this repo> && cd twingate-mcp
uv sync
cp .env.example .env # then fill in TWINGATE_NETWORK and TWINGATE_API_KEY
uv run twingate-mcp versionTWINGATE_NETWORK is everything before .twingate.com: for acme.twingate.com use acme; for a regional tenant such as acme.us1.twingate.com use acme.us1.
Variable | Default | Notes |
| required | Host before |
| required | Read-only token recommended |
|
| 1–100 |
|
| Items per tool call when |
|
| Hard cap per tool call |
|
| Per request to the Twingate API |
|
| |
|
| Deadline for one tool call, including retries and pagination. Keep it below your client or load balancer timeout |
| unset | Required for non-loopback HTTP |
| unset | Extra comma-separated hostnames accepted in |
|
|
|
3. Use with Claude Desktop (stdio)
Add this to claude_desktop_config.json:
{
"mcpServers": {
"twingate": {
"command": "uv",
"args": ["run", "--directory", "/path/to/twingate-mcp", "twingate-mcp", "serve", "--transport", "stdio"],
"env": {
"TWINGATE_NETWORK": "acme",
"TWINGATE_API_KEY": "<your read-only token>"
}
}
}
}4. Streamable HTTP
uv run twingate-mcp serve --transport http # 127.0.0.1:8000, endpoint /mcpBinding to anything other than a loopback address fails unless TWINGATE_MCP_HTTP_TOKEN is set.
With it set, every request must send Authorization: Bearer <token>, except the /healthz and
/readyz probes and /metrics, which are unauthenticated. DNS-rebinding protection on /mcp
accepts only Host headers matching the bind address (plus loopback names on a 0.0.0.0 bind)
and TWINGATE_MCP_ALLOWED_HOSTS.
Security caveat: the bearer token is a single shared secret, not user authentication. Anyone holding it can read everything the Twingate API token can read. Use HTTPS in front of it (a reverse proxy) and don't expose it to the internet.
5. Docker
The image runs as a non-root user and contains only the runtime virtualenv. .env and the schema
are excluded from the build context.
docker build -t twingate-mcp:local .Or pull a published image and tag it for the examples below. Every main build is published as
sha-<full commit SHA> (tags are listed on the repo's GHCR package page). There is no latest tag.
docker pull ghcr.io/cblack34/twingate-mcp:sha-<commit>
docker tag ghcr.io/cblack34/twingate-mcp:sha-<commit> twingate-mcp:localRelease tags X.Y.Z are created from vX.Y.Z git tags: pushing one on a commit that main already
published copies the scanned sha-<commit> image to X.Y.Z without a rebuild.
stdio (Claude Desktop): use docker as the command:
"twingate": {
"command": "docker",
"args": ["run", "-i", "--rm", "--env-file", "/path/to/twingate-mcp/.env", "twingate-mcp:local"]
}HTTP (Compose): add TWINGATE_MCP_HTTP_TOKEN to .env (generate one with
openssl rand -hex 32), then:
docker compose up -d # http://localhost:8000/mcp, published on 127.0.0.1 onlyInside the container the server binds 0.0.0.0, so the bearer token is mandatory. It accepts
localhost and 127.0.0.1 Host headers; put a TLS reverse proxy in front before exposing it beyond
the local machine.
Kubernetes
The image defaults to stdio, which exits immediately in a pod. Override the container args:
args: ["serve", "--transport", "http", "--host", "0.0.0.0"].Set
TWINGATE_MCP_ALLOWED_HOSTSto every name clients use. Entries are bare hostnames, not URLs; they match theHostheader exactly (aname:*variant for any port is added automatically) and the matchinghttp(s)://Origins are generated. There are no wildcards or suffix matches. List e.g.twingate-mcp,twingate-mcp.<ns>,twingate-mcp.<ns>.svc,twingate-mcp.<ns>.svc.cluster.localplus any Ingress/LB host. A missing name makes/mcpreject requests while the probes still pass.Probe with
httpGeton port 8000:/healthzfor liveness and/readyzfor readiness. Both skip the bearer token and never call Twingate./metrics(Prometheus format, unauthenticated) exposes tool-call and Twingate API metrics; scrape it with Prometheus or Datadog's OpenMetrics check. Labels are fixed names only, never user data:twingate_mcp_tool_calls_total{tool,outcome},twingate_mcp_tool_call_duration_seconds{tool},twingate_api_requests_total{operation,outcome},twingate_api_request_duration_seconds{operation}, andstamina_retries_totalfor upstream retries.HTTP is stateless, so any number of replicas works without sticky sessions.
Route only
/mcpthrough any Ingress/LB./healthz,/readyzand/metricsare unauthenticated: keep them in-cluster by exposing only/mcpat the Ingress. A NetworkPolicy can't separate paths on port 8000; use it only to limit which peers reach the pod. The bearer token is one shared secret, so keep the Service internal.Settings are read once at startup: rotating
TWINGATE_API_KEYorTWINGATE_MCP_HTTP_TOKENneeds a pod restart.Shutdown: a tool call can run up to
TWINGATE_CALL_TIMEOUT_SECONDS(50s), but uvicorn waits only 20s after SIGTERM and/readyzkeeps returning 200 meanwhile. Add a ~5spreStopsleep so the pod leaves the Service endpoints first; in-flight calls longer than ~20s can still be cut on rollout.Starting resources: the Compose limits, 0.5 CPU and 256Mi memory.
The image runs as uid/gid 10001 (numeric), so
runAsNonRoot: trueworks. The app writes nothing to disk, soreadOnlyRootFilesystem: trueworks too.
Argo CD Image Updater
Deploy immutable tags only (
sha-<commit>orX.Y.Z); there is nolatest.For
mainbuilds use thenewest-buildupdate strategy with the tag filterregexp:^sha-[0-9a-f]{40}$. Once releases exist, switch tosemverwithregexp:^[0-9]+\.[0-9]+\.[0-9]+$.Images are multi-arch (
linux/amd64,linux/arm64): set the platform(s) of your nodes if your Image Updater version needs it.
Tools
All tools are read-only. List tools take an optional limit (default TWINGATE_DEFAULT_LIMIT,
capped at TWINGATE_MAX_LIMIT) and return {items, total_returned, truncated}.
Tool | Input | Returns |
|
| id, email, first/last name, role, state |
|
| One user or null |
|
| id, name, type, active, member count |
|
| id, name, address, remote network name, active |
|
| id, name, location, active, connector count |
|
| id, name, remote network, state, last heartbeat |
|
| id, name, OS, user email, trust status, last seen |
|
| Groups and service accounts with access |
|
| The user's groups and resources reachable through them |
From a terminal: uv run twingate-mcp call list_users --arg limit=5.
Contributing: data-safety rules
This repo must never contain company or tenant data.
Test data is synthetic: seeded polyfactory/Faker factories (
tests/factories.py) usingsafe_*providers, or small hand-written JSON intests/fixtures/with values likealice@example.comand theacme-testnetwork. Never use Faker's plainemail(),domain_name(), orhostname().Never record or paste real API responses. No VCR/cassette/record-mode libraries.
No real network names, emails, hostnames, or IDs anywhere: code, tests, docs, commit messages.
.envandschema/twingate.graphqlare gitignored. Pull the schema from a personal or trial tenant, never an employer tenant.Log operation names, durations, and status codes only, never bodies or headers.
gitleaksruns in pre-commit (uv run pre-commit install).tests/test_data_safety.pyfails on non-example.*domains or non-allowlisted network names.Only
queryoperations are allowed inqueries/; a test enforces it.
Quality gates: uv lock --check, uv run ruff check ., uv run ruff format --check .,
uv run pyright, uv run pytest --cov=twingate_mcp --cov-fail-under=85, the pip-licenses
command in the spec, and gitleaks detect --no-banner.
License
MIT, see LICENSE.
Available Tools
9 toolsget_resource_accessARead-onlyIdempotent
Show which groups and service accounts have access to one resource (by resource id
from list_resources). If truncated is true the principal list is incomplete.
Inputs: resource_id (required). Returns: resource, truncated, principals
get_userARead-onlyIdempotent
Look up one Twingate user by exact email. Returns null if no user has that email.
Inputs: email (required). Returns: result
get_user_accessARead-onlyIdempotent
Show a user's groups and the resources reachable through those groups.
Use to answer "what can this person reach?". If truncated is true lists are partial.
Inputs: email (required). Returns: email, groups, resources, truncated
list_connectorsARead-onlyIdempotent
List connectors: id, name, remote network, state (online/offline), last heartbeat.
Use to check connector health.
Results are capped by limit; if truncated is true, more existed than were
returned, so do not treat the list as complete.
Inputs: limit. Returns: items, truncated, total_returned
list_devicesARead-onlyIdempotent
List devices: id, name, OS, owning user's email, trust status, and last seen time.
Results are capped by limit; if truncated is true, more existed than were
returned, so do not treat the list as complete.
Inputs: limit. Returns: items, truncated, total_returned
list_groupsARead-onlyIdempotent
List Twingate groups: id, name, type, whether active, and member count.
Results are capped by limit; if truncated is true, more existed than were
returned, so do not treat the list as complete.
Inputs: limit. Returns: items, truncated, total_returned
list_remote_networksARead-onlyIdempotent
List remote networks: id, name, location, whether active, and connector count.
Results are capped by limit; if truncated is true, more existed than were
returned, so do not treat the list as complete.
Inputs: limit. Returns: items, truncated, total_returned
list_resourcesARead-onlyIdempotent
List Twingate resources: id, name, address, remote network name, and whether active.
Use get_resource_access with an id to see who can reach one.
Results are capped by limit; if truncated is true, more existed than were
returned, so do not treat the list as complete.
Inputs: limit. Returns: items, truncated, total_returned
list_usersARead-onlyIdempotent
List Twingate users: id, email, first/last name, role, and state.
Use to browse who is in the network; use get_user to look up one email.
Results are capped by limit; if truncated is true, more existed than were
returned, so do not treat the list as complete.
Inputs: limit. Returns: items, truncated, total_returned
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 9 tools
Each tool targets a distinct resource or a distinct direction of an access query: list_* tools each cover one entity type, get_user is the single-email lookup complement to list_users, and get_user_access (user→resources) is clearly the inverse of get_resource_access (resource→principals). Descriptions explicitly cross-reference each other to steer selection, leaving no realistic misselection risk.
Uniform verb_noun snake_case throughout: list_* for enumerations (groups, resources, users, remote_networks, connectors, devices) and get_* for targeted lookups (user, user_access, resource_access). No camelCase or vague verbs intrude.
Nine tools is well within the ideal 3-15 range and each earns its place by covering a distinct Twingate entity or access relationship. Nothing looks padded or redundant.
The read-only/audit surface is coherent and covers the main entities plus access relationships, but it is entirely queries: there are no create, update, or delete operations for users, groups, resources, or connectors, and no get_group/get_resource/get_connector single-item lookups. An agent can investigate but cannot remediate anything it discovers.
Maintenance
Related MCP Connectors
Query metrics, targets, entities, and team data in your Steep workspace via MCP.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
XFA's remote MCP server — query device posture, compliance, policies & CVEs. Read-only.
Read-only MCP for identity resolution and write guardrails.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceProvides read-only access to TrustLayer's public API, enabling users to query and retrieve data about parties, documents, projects, and other TrustLayer entities through MCP-compatible tools.MIT
- AlicenseNot gradedqualityCmaintenanceProvides read-only access to Google Ads account data including campaigns, ad groups, keywords, and performance reports. Enables querying via GAQL through an MCP interface.MIT
- FlicenseNot gradedqualityCmaintenanceProvides read-only access to Microsoft Intune through the Microsoft Graph API, enabling MCP clients to query managed devices, apps, compliance policies, and configuration profiles.-
- FlicenseNot gradedqualityBmaintenanceProvides read-only inspection of Cloudflare account resources, including zones, DNS records, Workers, Pages projects, R2 buckets, and Tunnels, via a secure MCP endpoint.-