MCP Access
Connect AI clients to your PodWarden infrastructure via the Hub MCP server
What you see
URL: /dashboard/mcp (requires login)
The MCP access page is where you see every Core serving MCP, every client connected to one, and where you get the configuration to add another — Claude Desktop, Claude Code, Cursor, or Windsurf. Hub acts as a tunnel, routing MCP tool calls from your AI client to your connected PodWarden instances.
The page is organised around two credentials and one session type, split by who holds them:
| Credential | Who holds it | What it is |
|---|---|---|
pwh_ instance key | a PodWarden Core | one per machine, TOFU-pinned to the UUID that Core mints and persists |
pwp_ personal access token | an AI client | scoped, at mint, to exactly one instance |
| OAuth session | a human at a browser | no key at all — identity is the session |
Summary strip
Five figures across the top:
| Tile | Shows |
|---|---|
| Instances | Cores currently connected |
| Active keys | Active pwh_ tunnel keys |
| Live sessions | OAuth sessions still inside their TTL and seen in the last 24 hours — not a count of sessions active this second |
| Tools exposed | Sum of the tools exposed by online instances |
| Needs attention | Rotations in flight plus offline instances, spelled out (e.g. "1 rotating · 1 offline") |
Below it, a strip showing recent MCP call volume over the reporting window.
Tabs
Two tabs carry the three planes, split by what serves MCP and what connects to it.
PodWarden Core instances (pwh_)
One card per Core. Each card carries:
| Field | Description |
|---|---|
| Name and version | The instance's display name and the PodWarden version it reported on its last tunnel hello |
| Address | https://mcp.podwarden.com/<instance-uuid> — the address an OAuth client points at, with a copy button |
| Status | Connected or disconnected, with uptime |
| Tools | Tool count while online, — while not, with a meter splitting it into read and write. The split is fail-closed: a tool with no recorded tier counts as write |
| Your clients | How many of your pwp_ tokens are scoped to this instance. Other members' tokens are not counted |
| Tunnel key | The pwh_ key this Core holds, its status, its pin state, and a Minted by line — which is empty for a tunnel key, deliberately, because tunnel keys record no minter |
The pin state is the important one. A key reads as pinned when its pin names this
instance — whether it was pinned explicitly at mint or imprinted on first hello, with
a TOFU imprinted <date> pill in the second case. It reads TOFU armed · awaiting first hello while armed but never presented, unpinned org key if it was never
armed, and pinned elsewhere if its pin names a different instance. A
N keys pin this instance warning appears if more than one key claims it.
Card actions: Connect an AI client, Rotate, Revoke, and — on a disconnected instance — Archive.
There is no re-pin and no un-pin, deliberately. A key pinned to the wrong Core is revoked and replaced. Note the operational caveat: after revoking, the next key is imprinted by whichever Core connects first — so deal with the wrong Core before you deploy the replacement key.
Below the cards, an Unused keys section lists active keys no Core has ever
presented — these carry their own Rotate and Revoke controls — and an Add a Core
card mints a new pwh_ key.
Add a Core always arms the new key for trust-on-first-use, and a tunnel key has
no expiry to choose (the field is shown read-only). It is deliberately not the same
as Create Key on the API Keys page, which offers the three-way pinning choice.
Set the key it hands you as PODWARDEN_HUB_TOKEN on the Core and restart.
MCP clients (pwp_ · OAuth)
Two sections, because two different things connect.
Access tokens lists the pwp_ personal access tokens you have minted:
| Column | Description |
|---|---|
| Token | Token name and its non-secret prefix |
| Scoped to | The one instance this token may reach |
| Minted by | The user the token acts as |
| Last used | When it last made a call |
| Expires | Expiry date, if it has one |
| Status | active, expired, or revoked — or rotating with a countdown while a rotation is open |
| Actions | Rotate or revoke — replaced by Purge now / Cancel during a rotation |
This list is yours, not the organization's — it shows the tokens you minted. Other members' tokens exist and are not shown here; there is no organization-wide token view. Note the asymmetry in the actions: you can rotate only a token you minted, but an organization admin can revoke anyone's.
Signed-in people lists one row per person across the organization — not one per session; a single person can hold several, and the Sessions column says how many. Columns are Who, Role, Client, Sessions, Last seen, and an End session action. The list is capped at 25 rows.
End session is not a revocation. It deletes that person's stored sessions in your organization, which closes every path that has to reload one — but it does not touch their Keycloak sign-in or their access token. They can reconnect immediately into a brand-new session. It also does not tear down a session already seated in a running MCP server process, which can continue until it ends on its own. Use it to clear a stale connection, not to cut off access — for that, remove the person from the organization. The control is disabled for a session carrying no Keycloak subject, because addressing it would mean publishing its session id, which is itself a bearer handle.
At the foot of the page, a Legend panel records what the page shows that the
backend cannot supply — the row cap, which figures are scoped to you rather than the
organization, and why an offline instance shows — for its tool count instead of
zero.
Connect an AI client
This dialog is the point of the page. It hands over a working configuration rather than a description of one — the instance's real UUID, and the real token if you mint one, are already substituted into the block you copy.
Pick one of two methods:
- OAuth (interactive) — the client opens a browser and you sign in. The URL is
https://mcp.podwarden.com/<instance-uuid>; there is no header and no secret to leak. Calls are attributed to you and the session expires. - Access token (permanent) — a
pwp_bearer token scoped to this instance at mint. The URL is the barehttps://mcp.podwarden.com/mcpwithAuthorization: Bearer pwp_..., because the token carries its own instance. No browser, no re-login.
Each method shows a config block with a copy button; OAuth also shows the one-line
claude mcp add equivalent. To mint a token, give it a name and an expiry (90 days,
30 days, 1 year, or no expiry — 90 days is preselected) and click Mint token.
The instance is fixed to the card you opened the dialog from and is not editable.
The secret is shown once and dropped straight into the config below it — you never
paste a secret into a template by hand.
The token form deliberately shows the URL without a UUID. Hub refuses a path that
disagrees with a token's scope rather than reconciling it, so pasting a /<uuid>
URL for a token connection works only by coincidence.
Rotation
Rotating a key or a token mints a replacement and opens a grace window during which both are accepted. The presets are 0, 2, 12 and 24 hours; 2 is the default, and 0 means no grace at all — the old credential stops working immediately. When the window closes the old credential is refused on every plane at once; its row is tombstoned on a later console read. Deploy the replacement before then.
While a rotation is in flight the modal offers Purge now, which closes the window early, and Cancel, which restores the original and revokes the undeployed replacement.
Archiving an instance
An instance row is created the first time a Core connects and, until it is archived, it stays on this page forever. A Core that has been decommissioned, rebuilt, or re-identified therefore leaves a permanently disconnected card behind, advertising an address that reaches nothing.
Archive (on the instance card, next to Rotate and Revoke) retires one. It is offered only while the instance is disconnected — a connected Core cannot be archived, so you cannot take a live machine off the air by accident. If a Core has crashed without closing its tunnel cleanly, wait for its heartbeat to go stale (about 90 seconds) and the button becomes available.
Archiving does four things, in one transaction:
| What happens | |
|---|---|
| The instance | Leaves this page, the list_instances tool, the fleet view and the Warden Agent's reach picker. Its mcp.podwarden.com/<uuid> address stops resolving, and no new access token can be scoped to it. Platform administrators still see it — the platform inventory is a record of what exists. |
pwh_ tunnel keys pinned to it | Revoked. There is no re-pin, so a key pinned to a retired instance can never be aimed anywhere else. An unpinned org key that other Cores also use is not touched. |
pwp_ access tokens scoped to it | Revoked — including tokens minted by other members, which the confirm dialog cannot list because the token listing only ever shows your own. |
| History | Kept, in full. Pin events, key rotations and the MCP call log all survive and still name the instance. |
Nothing is deleted. The database row is retained deliberately: genuinely deleting it would erase the access-token records, the enrollment history and the announced tool list, and would strip the instance identifier off every MCP call ever audited against that Core. Retiring the instance must not destroy the evidence of what it did, so the row stays as the anchor that history points at.
If the Core comes back, so does the instance. A Core that reconnects asserting the same UUID un-archives its own row on its next tunnel hello and reappears on this page with the same address. Archive means "forget until it returns", not "prevent".
So stop the Core before you archive it. Archiving revokes the keys pinned to the instance, but a machine still running on an unpinned org key can reconnect within seconds and bring the card straight back. If the Core's tunnel happens to still be open when you archive — its heartbeat had gone stale, but the socket had not closed — the Hub closes that tunnel on the next heartbeat rather than letting a retired instance keep serving.
There is no un-archive button, for the same reason: it would hand back a card with no working credential. If you want a Core back, mint it a key and start it.
How it works
- Connect your instance — Configure Hub URL and
pwh_instance key in PodWarden Settings > Hub, then enable Hub Tunnel in Settings > MCP - Choose access level — Set the MCP access level to read-only or full
- Connect your AI client — Use Connect an AI client on the instance card
- Manage via AI — List instances, browse tools, execute commands through your client
Available actions
| Action | Where | What it does |
|---|---|---|
| Set up a new Core | Page header | Jumps to the Add a Core card and focuses the name field |
| Refresh | Page header | Re-reads instances, keys, tokens, and sessions |
| Copy address | Instance card | Copies https://mcp.podwarden.com/<instance-uuid> |
| Connect an AI client | Instance card | Opens the config dialog for OAuth or an access token |
| Mint token | Connect dialog | Creates a pwp_ token scoped to that one instance |
| Rotate | Instance card, unused key, token row | Mints a replacement and opens a grace window |
| Revoke | Instance card, unused key, token row | Permanently deactivates the credential |
| Archive | Instance card | Retire a disconnected instance: it leaves this page and the credentials pointing at it are revoked. History is kept, and a returning Core brings it back |
| End session | Signed-in people | Deletes that person's stored sessions, and the row disappears on reload. Not a revocation — see the note above |
Status badges
| Badge | Meaning |
|---|---|
| connected | Instance is online and reachable via Hub |
| disconnected | Instance is not currently connected. The key stays valid and stays pinned, so the Core reclaims the same UUID and the same address when it returns |
| pinned | The key's pin names this instance, whether set explicitly at mint or imprinted on first hello |
| TOFU armed · awaiting first hello | The key is armed and will imprint on the first Core that connects |
| unpinned org key | The key was never armed and any Core in the organization may use it |
| pinned elsewhere | The key's pin names a different instance |
| active | Credential is valid and will be accepted right now |
| expired | A token past its expiry. It authenticates nothing |
| revoked | Credential has been revoked |
| rotating | A replacement exists and the grace window is still open, with a countdown |
The pin is asserted by the Core, not proven. It is not a cryptographic binding — it establishes that the same identity keeps coming back on the same key, which is what it is for.
Related docs
- API Keys — Instance keys and personal access tokens
- Client Configuration — The same configurations written out per client
- Settings: MCP — Configure MCP on your PodWarden instance
- Settings: Hub — Connect your instance to Hub
- Hub MCP Proxy — Detailed setup guide