docs/features/profiles.md
Profiles are named, stateless subsets of upstream servers addressable as permanent URLs at /mcp/p/<slug>.
{
"profiles": [
{ "name": "research", "servers": ["arxiv", "wikipedia"] },
{ "name": "deploy", "servers": ["k8s", "github"] }
]
}
Each profile gets a permanent MCP endpoint:
| URL | Effective servers |
|---|---|
/mcp | All configured servers (unchanged) |
/mcp/p/research | arxiv, wikipedia only |
/mcp/p/deploy | k8s, github only |
^[a-z0-9][a-z0-9_-]{0,62}$ (max 63 chars, lowercase)all, code, call, p — cannot be used as profile namesretrieve_tools returns only tools from the profile's serverscall_tool_read/write/destructive into an out-of-profile server is rejected with:
server '<name>' is not in profile '<slug>'upstream_servers list at a profile URL excludes out-of-profile serverscode_execution at a profile URL runs with the profile-intersected server setenabled_tools/disabled_tools continue to apply inside a profile (no profile-level tool overrides)Profile filtering is independent of agent-token scope. An unauthenticated connection at /mcp/p/<slug> is still profile-filtered. When both a profile and an agent token are present, the effective server set is their intersection.
Error attribution:
server '<s>' is not in profile '<slug>'Server '<s>' is not in scope for this agent tokenTool-call activity records from profile URLs carry metadata["profile"] = "<slug>". Records from /mcp omit this field.
Profile changes take effect for new connections on the next config reload. In-flight sessions keep their snapshot.
| Condition | Body |
|---|---|
| No profiles configured | {"error":"no profiles configured"} |
| Unknown slug | {"error":"unknown profile '<slug>'","available":["research","deploy"]} |