Agent Profiles Specification¶
Overview¶
See [[design/profiles]] for the hybrid markdown profile format and vault storage.
Agent Profiles are capability bundles that configure agents with specific tools, MCP servers, model overrides, and system prompt additions at task execution time. They allow task-level specialization (e.g., a code reviewer vs. a web developer) without changing the scheduler or agent pool.
Key invariant: The scheduler is completely profile-unaware. Profile resolution happens downstream in _execute_task(), after the scheduler has already assigned a task to an agent. Zero LLM calls for orchestration are added.
Data Model¶
AgentProfile¶
| Field | Type | Default | Description |
|---|---|---|---|
id |
str | (required) | Slug identifier: "reviewer", "web-developer" |
name |
str | (required) | Human-readable display name |
description |
str | "" | What this profile is for |
model |
str | "" | Model override (empty = use adapter default) |
permission_mode |
str | "" | Permission mode override (empty = use adapter default) |
allowed_tools |
list[str] | [] | Tool whitelist (empty = use adapter default). Stored as bare tool names — see Tool Naming below. |
mcp_servers |
list[str] | [] | Names of MCP servers from the registry. Resolved at task launch (project scope first, system fallback). |
system_prompt_suffix |
str | "" | Appended to agent's context as "Agent Role Instructions" |
install |
dict | {} | Auto-install manifest (reserved for future use) |
Task Extension¶
Tasks gain an optional profile_id field (nullable foreign key to agent_profiles.id). When set, the agent executing this task is configured with the referenced profile's capabilities.
Project Extension¶
Projects gain an optional default_profile_id field (nullable foreign key to agent_profiles.id). This serves as the fallback when a task has no explicit profile_id.
Profile Resolution Cascade¶
When _execute_task() runs, it resolves the profile in this order:
- Task-level:
task.profile_id— if set, use this profile - Project-level:
project.default_profile_id— if set, use this profile - System default: Neither set → use
ClaudeAdapterConfigdefaults (current behavior)
This is a pure dict lookup. No LLM calls. No scheduler changes.
Platform Integration¶
When a profile is resolved:
- Config merging:
PlatformRegistry._config_for_profile()creates aClaudeAdapterConfigby overlaying profile fields onto the base config. Empty profile fields (model="", permission_mode="", allowed_tools=[]) fall through to base config defaults. - TaskContext population:
TaskContext.mcp_serversis populated from the profile. (Tools flow throughClaudeAdapterConfig.allowed_tools.) - System prompt injection: If
profile.system_prompt_suffixis non-empty, it's injected as an "Agent Role Instructions" section in the task context. - Runtime logging: After profile resolution, the orchestrator logs which profile/tools/MCP servers were applied to the task.
Database¶
Table: agent_profiles¶
CREATE TABLE IF NOT EXISTS agent_profiles (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT NOT NULL DEFAULT '',
model TEXT NOT NULL DEFAULT '',
permission_mode TEXT NOT NULL DEFAULT '',
allowed_tools TEXT NOT NULL DEFAULT '[]', -- JSON array of bare tool names
mcp_servers TEXT NOT NULL DEFAULT '{}', -- JSON; written as a list[str] of registry names. The literal default is '{}' for legacy reasons; the application coerces on read.
system_prompt_suffix TEXT NOT NULL DEFAULT '',
install TEXT NOT NULL DEFAULT '{}', -- JSON dict
created_at REAL NOT NULL,
updated_at REAL NOT NULL
);
Migrations¶
ALTER TABLE tasks ADD COLUMN profile_id TEXT REFERENCES agent_profiles(id)ALTER TABLE projects ADD COLUMN default_profile_id TEXT REFERENCES agent_profiles(id)ALTER TABLE archived_tasks ADD COLUMN profile_id TEXT
CRUD Methods¶
create_profile(profile)— insert a new profileget_profile(profile_id)— fetch by ID, returns None if not foundlist_profiles()— all profiles sorted by nameupdate_profile(profile_id, **kwargs)— partial update; JSON fields auto-serializeddelete_profile(profile_id)— cascades: clears profile_id from tasks and default_profile_id from projects before deleting
Source of Truth: Vault Markdown¶
The runtime source of truth for profiles is vault markdown at
vault/agent-types/<id>/profile.md (system) and
vault/projects/<pid>/agent-types/<id>/profile.md (project override). See
[[design/profiles]] for the markdown format. The vault watcher syncs changes
into the agent_profiles table at startup and on file change.
config.yaml profiles are still accepted for backward compatibility — a
startup migration extracts any agent_profiles: section into vault markdown
files (idempotent; never clobbers a hand-authored entry). New work should
edit the markdown directly.
Tool Naming¶
allowed_tools is stored as bare tool names (get_weather,
create_task, send_message). The supervisor matches the LLM's
tool_use.name against this list directly. The Claude adapter rewrites
embedded agent-queue tools to their MCP-prefixed form
(mcp__agent-queue__<name>) when building --allowed-tools for the CLI
subprocess. Profile authors must not write the mcp__agent-queue__
prefix.
Exceptions:
- Claude built-ins (Read, Edit, Bash, Glob, Grep, WebSearch,
WebFetch, Agent, etc.) keep their bare names.
- Third-party MCP servers use the full mcp__<server>__<tool> form (no
unambiguous strip rule across servers).
The profile parser strips the mcp__agent-queue__ prefix on read, so
legacy entries auto-canonicalize on the next vault → DB sync.
MCP Server Registry¶
Profiles do not store inline MCP server configs anymore. mcp_servers
is a list[str] of names from the registry sourced at:
vault/mcp-servers/*.md— system-scope server definitionsvault/projects/<pid>/mcp-servers/*.md— project-scope (shadows system by name)
The orchestrator resolves names at task launch (project scope first,
system fallback). A startup migration moves legacy inline configs from
config.yaml profiles and old profile.md ## MCP Servers blocks into
registry files. The embedded agent-queue server is a builtin registry
entry computed in-process from CommandHandler tool definitions plus
plugin tools. See [[mcp-server]] for the registry API and CRUD commands.
Discovery & Validation¶
Known Tools Registry (src/known_tools.py)¶
A registry of Claude Code built-in tools (CLAUDE_CODE_TOOLS) and well-known MCP servers (KNOWN_MCP_SERVERS). Provides:
validate_tool_names(tools)— returns unrecognized tool names (soft warning, not error)KNOWN_TOOL_NAMES/KNOWN_MCP_NAMES— frozensets for fast lookup
Tool Validation on Create/Edit¶
When creating or editing a profile with allowed_tools, unrecognized tool names produce a warnings field in the response. The profile is still created/edited — this catches typos without blocking custom MCP-provided tools.
Discovery Command¶
list_available_tools returns all known tools (name + description) and MCP servers (name + description + npm_package) so users can discover what's available before configuring profiles.
Install Manifest¶
InstallManifest Dataclass¶
Parsed from AgentProfile.install dict:
InstallManifest.from_dict(d) / .to_dict() for serialization. .is_empty property.
check_profile Command¶
Validates a profile's install manifest:
- Commands: checked via shutil.which()
- npm packages: checked via npm list -g <pkg> --depth=0
- pip packages: checked via importlib.metadata.version()
Returns {profile_id, valid: bool, issues: [...], manifest: {...}}.
install_profile Command¶
Installs missing dependencies from the manifest:
- npm: npm install -g <pkg>
- pip: pip install <pkg>
- Commands: reported as manual-install items (can't auto-install system binaries)
Returns {profile_id, installed, already_present, manual, ready}.
No restart required. Each task spawns a fresh Claude Code subprocess, so newly-installed packages are immediately available.
install Field in Create/Edit¶
create_profile and edit_profile accept an optional install dict. The install field is persisted as JSON in the agent_profiles table.
Export / Import & Sharing¶
YAML Export Format¶
Self-contained, shareable format under agent_profile: key:
# Agent Profile: Code Reviewer
agent_profile:
id: reviewer
name: "Code Reviewer"
description: "Read-only code review agent"
model: "claude-sonnet-4-5-20250514"
allowed_tools: [Read, Glob, Grep, Bash]
mcp_servers: ["linter"] # name of an entry in vault/mcp-servers/
system_prompt_suffix: "You are a code reviewer."
install:
npm: ["eslint-mcp"]
commands: ["npx"]
export_profile Command¶
Serializes a profile to YAML. Optionally creates a public GitHub gist via gh gist create.
import_profile Command¶
Imports from YAML text or gist URL:
- URL: extracts gist ID, fetches via gh gist view <id> --raw
- YAML: parses agent_profile key
- Optional id override and overwrite flag
- Auto-installs dependencies: if profile has non-empty install manifest, runs check → install for missing npm/pip packages
- Returns {imported, name, installed, already_present, manual, ready}
Commands (Complete)¶
Profile CRUD (system scope)¶
list_profiles— show all profiles with summary infocreate_profile— create new profile (id, name required; tools/MCP/prompt/install optional)get_profile— full details of one profile (includes install manifest)edit_profile— partial update of profile fields (including install)delete_profile— remove profile, clear references
Project-Scoped Profile CRUD¶
list_project_profiles— list per-agent-type rows for a project (override / inherit / no-default)create_project_profile— create a project override (optionally seeded from the global default)edit_project_profile— partial update of a project overridedelete_project_profile— remove the override (resets the agent-type to global). Cleans up nested and flat vault paths so a stray scoped file can't resurrect during the next watcher pass.show_effective_profile— resolveproject + agent_typeto the effective profile (override → inherit → no-default)
Discovery & Validation¶
list_available_tools— discover tools and MCP servers for profile configuration
Install Manifest¶
check_profile— validate install dependencies (commands, npm, pip)install_profile— install missing npm/pip dependencies
Export / Import¶
export_profile— serialize to YAML, optionally create public gistimport_profile— import from gist URL or YAML text, auto-install dependencies
MCP Server Registry¶
See [[mcp-server]] for full details. Commands: list_mcp_servers,
get_mcp_server, create_mcp_server, edit_mcp_server,
delete_mcp_server, probe_mcp_server, list_mcp_tool_catalog.
delete_mcp_server refuses if any profile still references the name.
Modified Commands¶
create_task— accepts optionalprofile_idedit_task— accepts optionalprofile_id(null to clear)edit_project— accepts optionaldefault_profile_id(null to clear)get_task— returnsprofile_idin output
Backward Compatibility¶
- Tasks without
profile_id→ cascade to project default or system default. Identical to current behavior. - Projects without
default_profile_id→ tasks use system defaults. Identical to current behavior. - No YAML
agent_profilessection → no profiles synced, everything works as before. - Database migrations are additive (
ALTER TABLE ADD COLUMN). - Agents are unchanged — no migration needed.
- Scheduler is unchanged — same algorithm, same inputs, same outputs.