# AI Agent Profiles > Module 14 · Plane: Data · Roadmap phase: 3 > Part of [MCP Nexus architecture](../ARCHITECTURE.md). ## Purpose An **AgentProfile** (Architecture §7) is the agent-facing binding of *identity → roles → visible tool set*. Where [Authentication](06-authentication.md) proves *who* is connecting and [RBAC](08-rbac.md) is the general role→tool engine, an AgentProfile is the first-class object that ties a specific AI agent — Claude, Cursor, VSCode, OpenWebUI, Codex, Aider, Continue.dev — to the roles it holds and therefore the exact tools it may see and call at the [Gateway](04-gateway.md). It is what makes "one endpoint, many agents" (tenet #1) concrete: every connecting agent resolves to a profile, and the profile decides its worldview. ## Responsibilities - Own the **AgentProfile** model: `{id, identity, allowed_roles}` (Architecture §7), plus display metadata (agent kind, description, owner) and an association to one or more credentials. - **Identify** a connecting agent: map the credential presented at the MCP `initialize` handshake (an API key, token, or client identifier resolved by [Authentication](06-authentication.md)) to exactly one AgentProfile. - **Resolve** the profile to a concrete visible tool set by handing its `allowed_roles` to the [RBAC](08-rbac.md) `Enforcer` — the profile does not compute tool visibility itself; it *binds* the identity to roles that RBAC expands. - Provide the Router with the profile's resolved `AllowedSet` so `tools/list` and `tools/call` are scoped per agent (enforcement stays in RBAC/Router, per tenet #5). - Maintain per-profile operational metadata: last-seen, connection count, and (optionally) per-profile rate-limit / quota hints for [Security](19-security.md). - Support **provisioning**: create a profile, mint/attach its agent credential (via [Authentication](06-authentication.md)), and assign roles — the operator workflow for onboarding a new agent. ## Non-goals - **Authenticating the credential.** Verifying the presented secret is [Authentication](06-authentication.md); the profile consumes the resolved `Principal`. - **Computing tool visibility.** The allow/deny expansion is [RBAC](08-rbac.md); the profile only supplies the roles. - **Being a human-user object.** Human operators are `Users` in the Identity store; profiles model *agents*. Both share the RBAC engine, but a profile is the agent binding specifically (this is how it differs from raw RBAC). - **Per-tool parameter policy.** Fine-grained call constraints, if any, belong to [RBAC](08-rbac.md)/[Security](19-security.md). - **Managing agent-side config.** Nexus does not push settings into Claude/Cursor; it presents an endpoint and scopes it. ## Interfaces ```go // The agent-facing binding of identity -> roles (Architecture §7). type AgentProfile struct { ID string // "profile:claude-primary" Identity string // stable agent identity key, matches Principal.ID for KindAgent Kind string // "claude" | "cursor" | "vscode" | "openwebui" | "codex" | "aider" | "continue" DisplayName string AllowedRoles []string // role names expanded by RBAC CredentialIDs []string // API keys / tokens that resolve to this profile CreatedAt time.Time LastSeen time.Time } type ProfileStore interface { Upsert(ctx context.Context, p AgentProfile) error Get(ctx context.Context, id string) (AgentProfile, error) // ByCredential maps an authenticated credential -> its profile (identify step). ByCredential(ctx context.Context, credentialID string) (AgentProfile, error) List(ctx context.Context) ([]AgentProfile, error) } // Resolver ties identify -> roles -> visible tool set via RBAC. type Resolver interface { // Identify: authenticated Principal (an agent) -> its profile. Identify(ctx context.Context, p Principal) (AgentProfile, error) // VisibleTools: profile's roles expanded by the RBAC Enforcer against the live catalog. VisibleTools(ctx context.Context, prof AgentProfile) (AllowedSet, error) } ``` HTTP/API surface (control-plane API, behind Auth, RBAC-gated; Admin/operator): - `GET /api/v1/agents` · `POST /api/v1/agents` — list / create profiles. - `GET /api/v1/agents/{id}` · `PUT /api/v1/agents/{id}` · `DELETE /api/v1/agents/{id}`. - `POST /api/v1/agents/{id}/roles` — set the profile's `allowed_roles`. - `POST /api/v1/agents/{id}/credentials` — mint/attach an agent credential (delegates to [Authentication](06-authentication.md)). - `GET /api/v1/agents/{id}/tools` — debug: the resolved visible tool set for this profile. MCP data-plane integration: on `initialize`, the Router calls `Identify(Principal)` to pin the profile to the session; `tools/list`/`tools/call` are then scoped by the profile's `VisibleTools` (enforced in RBAC/Router). Identify → resolve flow (per agent session): ``` 1. Agent (e.g. Cursor) opens an MCP session and sends `initialize` carrying its credential (API key / token on Streamable HTTP). 2. Authentication (Module 06) verifies it → Principal{Kind: agent, ID}. 3. Resolver.Identify(Principal) → ProfileStore.ByCredential → AgentProfile - no match → fail closed (empty set) unless a guest profile is set. 4. Resolver.VisibleTools(profile): hand profile.AllowedRoles to the RBAC Enforcer, which expands them against the live tool catalog (Module 05) → AllowedSet (the agent's worldview). 5. Profile + AllowedSet pinned to the MCP session. 6. tools/list → RBAC.Filter(AllowedSet); tools/call → RBAC.Can(...). 7. last_seen updated; provisioning/role changes audited (Module 19). ``` Example: profile `claude-primary` (identity `agent:claude-01`) holds roles `[Developer, Home Automation]`; RBAC expands these to `github.*`, `postgres.*`, `filesystem.*`, `homeassistant.*` — so this agent sees exactly those namespaces and nothing from Networking/Finance/Admin. ## Data - **Reads/writes** `AgentProfile` rows in the **Identity** store (Architecture §8), alongside `Users`, `Roles`, and API keys; a credential→profile index supports the identify step. - **Reads** the [RBAC](08-rbac.md) role definitions to expand `allowed_roles`, and the [Dynamic Tool Registry](05-dynamic-tool-registry.md) (via RBAC) for the live catalog. - **Writes** profile provisioning and role-binding changes to the **Audit** store. - Module-local: `last_seen`/connection counters updated on the hot path (cheap, async). ## Dependencies - Credential verification and the `Principal` come from [Authentication](06-authentication.md). - Role expansion and enforcement come from [RBAC](08-rbac.md); profiles supply the roles. - Visibility is scoped against the [Dynamic Tool Registry](05-dynamic-tool-registry.md). - Enforced at the [Gateway](04-gateway.md)/Router on the hot path. - Managed through the [Web Dashboard](12-web-dashboard.md) (agent onboarding). - Provisioning + access audited per [Security](19-security.md). ## Failure modes & handling | Failure | Behavior | |---|---| | Credential resolves to no profile | Treated as an unrecognized agent → no roles → empty visible set (**fail closed**); connection allowed only if a default/guest profile is explicitly configured. | | One credential maps to two profiles | Rejected at provisioning; the credential→profile index is unique. Ambiguity is a configuration error, not a runtime coin-flip. | | Profile references a deleted role | Missing role contributes nothing (no phantom access); resolution logs the dangling reference. | | Roles changed while agent connected | RBAC cache invalidated; next `tools/list`/`tools/call` reflects the new set without dropping the session. | | Profile deleted while agent connected | Session's pinned profile is invalidated; subsequent requests fail closed and the agent must reconnect. | | Identity store unavailable | Fail closed — unresolved profile means no tools; surfaced via [Notifications](13-notifications.md). | ## Security notes Honors Architecture §9 and tenets #1/#5. A profile is the point where an opaque agent connection becomes a **scoped, named identity** — everything downstream (visibility, invocation, audit) keys off it. Default is **fail closed**: an agent whose credential maps to no profile, or a profile with no roles, sees nothing. The profile never widens access on its own; it can only reference roles, and RBAC enforces them server-side, so a compromised/misconfigured agent cannot self-elevate. Every profile lifecycle event (create, role change, credential attach/detach) is audited with principal + target. Per-profile rate-limit hints feed the [Security](19-security.md) baseline so one noisy agent cannot starve others (tenet #1's "many agents" fairness). ## Open questions - Should a profile map to exactly one credential or many (e.g. Claude Desktop + Claude Code sharing one profile vs. distinct profiles)? - Do we ship a curated starter profile pack (Claude, Cursor, VSCode, …) with sensible default roles, or start every profile empty? - Is there value in profile-level tool *overrides* on top of roles, or does that erode the clean identity→roles→tools chain? - How do profiles interact with per-agent quotas/budgets (token usage) tracked in [Metrics](18-metrics.md)? - Guest/anonymous profile: supported at all, or is every agent required to be provisioned first? ## Milestone Delivered in **Phase 3** (Secure). Thin slice first: the `AgentProfile` model, credential→profile `Identify`, and role expansion via [RBAC](08-rbac.md) so a connecting agent resolves to a scoped visible tool set at the Router. Directly powers the Phase 3 exit proof: two agents (distinct profiles, distinct roles) connect and each sees a different tool set — the profile is what binds each agent to its slice.