Profiles & Tool Routing
A profile is a named bundle of installed MCP servers, owned by a user, exposed as one streamable-HTTP MCP endpoint with its own bearer token (design decision #11):
/mcp/p/<slug>
Instead of configuring every MCP client with every server, you point each client (Claude Code, Cursor, ...) at one profile endpoint and manage the server bundle centrally — in the dashboard or via the REST API.
The model
| Property | Detail |
|---|---|
| Slug | ^[a-z0-9][a-z0-9-]{1,62}$, unique, forms the endpoint path |
| Name | 1–128 characters, display only |
| Owner | The creating user; admins can see and manage all profiles |
| Servers | Replace-all list; every name must be an installed server |
| Token | gig_-prefixed bearer token, plaintext shown exactly once |
Profiles are per-user, but credentials are shared across a user's profiles: when the egress proxy resolves a sandbox's identity to a profile, the credential lookup uses the profile owner's user ID — one stored key serves all of that user's profiles. See egress proxy and vault.
Connecting a client
Create a profile (dashboard or POST /api/profiles), copy the token from the one-time reveal, then:
claude mcp add --transport http my-profile http://localhost:8080/mcp/p/<slug> \
--header "Authorization: Bearer gig_..."
Authentication is per-request: the gateway hashes the presented bearer token and looks up (slug, token hash) in the database on every request. A rotated or deleted token is rejected immediately. Streamable HTTP uses POST, GET, and DELETE on the same URL, so the route is method-agnostic.
The legacy aggregated endpoint (/mcp, authenticated by GIG_BEARER_TOKEN) still works alongside profile endpoints — see authentication.
Runtime lifecycle: lazy spawn per profile
Profile runtimes are lazy. Nothing runs until the first authorized request hits /mcp/p/<slug>; the gateway then:
- Loads the profile's server bundle.
- Spawns one sandboxed instance of each server with the tenant set to the profile ID — shared code/image, never shared process (design decision #5). Each sandbox gets its own network namespace and
/30, so the egress proxy can attribute every outbound call to(server, user, profile). - Builds an aggregator over the spawned backends and caches the runtime; later requests reuse it.
Concurrent cold-start requests for the same profile coalesce into a single spawn; a slow spawn of one profile never blocks warm requests to another. If a spawn fails the error is not cached — the next request retries.
:::note Known limitation Idle reaping is not implemented yet: once spawned, a profile's sandboxes stay up until invalidated or the gateway shuts down. The sandbox count is bounded by profiles × servers. :::
Invalidation
Editing a profile's bundle or deleting the profile invalidates its runtime: the sandboxes are torn down and the next request respawns the bundle lazily. In-flight requests against a torn-down runtime fail with a transport error and must reconnect (re-auth + respawn) — invalidation is a rare, operator-triggered event, not a hot path.
Token rotation does not invalidate the runtime. New requests must present the new token (per-request check), but an already-established MCP stream continues until the client disconnects. Hard revocation is a bundle edit or profile deletion.
If a server is uninstalled but still referenced by a profile, the spawn skips it with a log line rather than failing the whole profile — the profile stays usable with its remaining servers.
Tool routing
The gateway aggregates every backend's tools into one MCP server, re-exposing each tool as:
<server>_<tool>
For example, the bundled echo server's echo tool appears as echo_echo. Because _ is the namespace separator, backend names must not contain _, and duplicate backend names are rejected.
Default tool subsets
Registry manifests mark each tool default: true/false. When a profile spawns a manifest-installed server, only the default: true tools are exposed — installs surface a curated subset rather than everything the server offers. Servers without a manifest (the legacy GIG_ECHO_BIN fallback) expose all tools.
A per-profile meta-tools mode (search/load tools on demand) is designed as an opt-in flag but is not implemented yet.
Managing profiles via the API
All endpoints require a control-plane session (see authentication); non-owners get 404 for foreign profiles unless they are admins.
| Operation | Endpoint | Notes |
|---|---|---|
| List | GET /api/profiles | Own profiles; admins see all |
| Create | POST /api/profiles {name, slug} | Response includes the token plaintext once |
| Detail | GET /api/profiles/{id} | Includes endpoint and servers; never the token |
| Rename | PATCH /api/profiles/{id} {name} | |
| Set bundle | PUT /api/profiles/{id}/servers {servers: []} | Replace-all; unknown names are 400; invalidates the runtime |
| Rotate token | POST /api/profiles/{id}/token | New plaintext returned once; runtime not invalidated |
| Delete | DELETE /api/profiles/{id} | Cascades the bundle and tears down the runtime |
Every mutation writes an audit event (profile_create, profile_rename, profile_servers, profile_token_rotate, profile_delete), visible in the dashboard audit page.