Skills API
Manage the skills library, its GitHub sources, and agent skill assignments. A skill is a whole package — a SKILL.md plus optional scripts/, templates, and resources — delivered into an agent's ~/.claude/skills/<name>/ directory. See Skills and Playbooks for the concepts.
Full interactive API docs are available at http://localhost:8000/docs when running locally. This page covers the most important endpoints.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /api/skills/library | GET | Merged skill listing across sources |
| /api/skills/library/status | GET | Sync state + per-source array |
| /api/skills/library/sync | POST | Sync every enabled source (admin, human-only) |
| /api/skills/sources | GET/POST | List / register a source (admin, human-only) |
| /api/skills/sources/{id} | PUT/DELETE | Edit / remove a source (admin, human-only) |
| /api/skills/sources/{id}/sync | POST | Sync one source (admin, human-only) |
| /api/settings/skills-library | GET/PUT | Auto-sync and fleet re-inject configuration (admin) |
| /api/skills/assignments | GET | Which agents hold each skill, batched, plus assignable_agents — the agents the caller may assign to. Human-only; admins see the fleet, others their accessible agents (the response says which via scope) |
| /api/agents/{name}/skills | GET/PUT | Read / set an agent's assignments (owner). The PUT response carries delivery and removal |
| /api/agents/{name}/skills/{skill} | POST/DELETE | Assign (and deliver) / unassign one skill (owner). The POST response carries delivery |
| /api/agents/{name}/skills/inject | POST | Force re-inject into this agent (owner); 409 while another injection holds the agent |
Source management is REST-only and human-only— there is no MCP tool for it, and agent-scoped keys are rejected. Registering or syncing a source decides which repository your fleet executes code from, so it is an operator action regardless of the caller's role.
Skills Library
/api/skills/library
The merged skill listing across every enabled source. Returns metadata only (no content) for performance. Each entry carries its source name and any shadowed_by sources — skill names share one flat namespace, and when two sources ship the same name, resolution is by priority (lower wins) then by age.
curl -H "Authorization: Bearer <token>" \
http://localhost:8000/api/skills/library
# Response
[
{
"name": "code-review",
"description": "Perform thorough code reviews with security checks",
"source": "My Team Skills",
"shadowed_by": []
},
{
"name": "research-web",
"description": "Research topics using web search and summarize findings",
"source": "Trinity Community Skills",
"shadowed_by": []
}
]/api/skills/library/:name
Get details for a specific skill including its full Markdown content.
curl -H "Authorization: Bearer <token>" \
http://localhost:8000/api/skills/library/code-review
# Response
{
"name": "code-review",
"description": "Perform thorough code reviews",
"path": "skills/code-review.md",
"content": "# Code Review Skill\n\nWhen reviewing code..."
}/api/skills/library/status
Library sync state, including the per-source array — each source's last sync status and last error. A sync contended by another worker reports busy rather than claiming failure, and a pinned tag that has moved reports moved_tag and leaves the fleet alone.
/api/skills/library/sync
Sync every enabled source from GitHub — clones or pulls each configured repository.Admin, human-only
curl -X POST -H "Authorization: Bearer <token>" \
http://localhost:8000/api/skills/library/sync
# Response
{
"success": true,
"skills_count": 12,
"message": "Library synced from GitHub"
}Skill Sources
The library is multi-source: a bundled public community catalog (github.com/abilityai/trinity-skills, shown as Trinity Community Skills) seeded on fresh installs, plus any custom repositories your admin adds. Manage them in Settings → Agents → Skills Library, or over REST.
| Endpoint | Method | Description |
|---|---|---|
| /api/skills/sources | GET | List sources in resolution order (admin, human-only) |
| /api/skills/sources | POST | Register a source: name, repository URL (github.com only), the ref to track (branch or tag), enabled flag, and priority (admin, human-only) |
| /api/skills/sources/{id} | PUT | Edit a source — including moving the bundled catalog to a newer tag, which the Settings panel cannot do in place (admin, human-only) |
| /api/skills/sources/{id} | DELETE | Remove a source (admin, human-only) |
| /api/skills/sources/{id}/sync | POST | Sync one source (admin, human-only) |
- Priority decides name clashes. Custom sources default to priority 100 and the bundled community source to 1000, so your own repository always wins a name clash and a source you add later needs no reordering.
- The community source is pinned to a tag — currently
v0.2.0oftrinity-skills— not a branch head. New upstream commits do not reach your fleet until the tag is bumped and you sync. The pin is a fresh-install seed: an existing instance keeps the source row it already has.TRINITY_DEFAULT_SKILL_SOURCE_REFin.envchanges the tag a fresh install is seeded with;TRINITY_DEFAULT_SKILL_SOURCE=""disables the seed entirely. - Custom sources track a branch by default, because you control who can write to them. A source can be pinned to a tag instead.
- A pinned tag that moves is refused, not adopted: the sync reports
moved_tagand leaves your fleet alone. Annotated and lightweight tags are both compared by the commit they point at, so a release tag that has not moved is never refused.
Library Automation
/api/settings/skills-library
Read or change the two automation settings. Both default OFF — a zero-config installation behaves exactly as it always did.Admin
- Scheduled auto-sync — Trinity pulls every enabled source on an interval (default 1 hour; 5 minutes to 24 hours). Changing the interval applies without a restart.
- Re-inject across the fleet after a sync — when a sync finds the library actually moved, running agents receive the updated packages automatically. A no-op pull never sweeps the fleet. Stopped agents are skipped and pick the change up on next start. The sweep runs at bounded concurrency and skips (rather than waits on) an agent that is mid-injection, reporting what it skipped. If one or more agents failed, an operator-queue alert is raised.
Agent Skill Assignments
/api/agents/:name/skills
Get skills assigned to an agent with assignment metadata — each skill's version short-SHA and the outcome of its last delivery. Assignments whose skill has since been removed upstream are kept and reported: the package stays on the agent until it is unassigned.
curl -H "Authorization: Bearer <token>" \
http://localhost:8000/api/agents/research-agent/skills
# Response
[
{
"skill_name": "code-review",
"assigned_by": "admin",
"assigned_at": "2026-03-15T10:00:00Z"
}
]/api/agents/:name/skills
Set an agent's full skill list — replaces all existing assignments with the provided list. Added names are delivered, dropped names are removed; the response's delivery and removal blocks report each half.Owner
| Name | Type | Required | Description |
|---|---|---|---|
| skills | string[] | Yes | Array of skill names to assign |
curl -X PUT -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"skills": ["code-review", "research-web"]}' \
http://localhost:8000/api/agents/research-agent/skills
# Response
{
"success": true,
"agent_name": "research-agent",
"skills_assigned": 2,
"skills": ["code-review", "research-web"],
"delivery": {"status": "injected"},
"removal": {"status": "removed"}
}/api/agents/:name/skills/:skill_name
Assign a single skill to an agent and deliver it. The response carries a delivery block (see below). Returns 404 if the skill is not found in the library.Owner
/api/agents/:name/skills/:skill_name
Remove a skill assignment from an agent. Unassigning also deletes the injected package, using the manifest the injection recorded — only paths a previous injection wrote are removed, empty directories are cleaned up, and the skill's .gitignore line is stripped. The unassignment itself always succeeds: if the agent is stopped, busy, or unreachable, removal is reported as deferred rather than failing the unassign, and the agent reconciles on next start.Owner
/api/agents/:name/skills/inject
Force an unconditional re-inject of every assigned skill into a running agent — the manual repair after a delivery that did not land. Copies each package to the agent's ~/.claude/skills/<name>/ directory. Agent must be running; returns 409 while another injection holds the agent.Owner
curl -X POST -H "Authorization: Bearer <token>" \
http://localhost:8000/api/agents/research-agent/skills/inject
# Response
{
"success": true,
"skills_injected": 2,
"skills_failed": 0
}/api/skills/assignments
Which agents hold each skill, batched — the fleet-wide read behind the Library's Assigned to N agents line. Also returns assignable_agents, the agents the caller may still assign to. Admins see the whole fleet, everyone else their own and shared agents; the response says which via scope.Human-only
Delivery Outcomes
Assigning delivers the package to a running agent immediately; a stopped agent receives it on its next start. An assign waits up to 20 seconds for delivery and then reports still installing while the work continues in the background. An assign that arrives while the agent is mid-sync is retried once; if the agent is still busy it is reported as not delivered and the assignment is kept either way.
| delivery.status | Meaning |
|---|---|
| injected | Every added skill is in the running agent — available now. |
| pending_start | The agent is stopped; the assignment is recorded and the files arrive on next start. |
| in_progress | A large package outlived the request's 20-second wait; delivery continues in the background. |
| not_delivered | The agent was mid-sync, still starting, its container state could not be read, or the install failed — a reason is included. The assignment is kept; retry with POST /api/agents/{name}/skills/inject. |
Injection itself is:
- Versioned and idempotent— each skill's version is its git tree SHA. A skill unchanged since the last inject is skipped on start and on assign; only changed skills transfer.
- Pruned by manifest diff — files removed from a skill upstream are removed from the agent on the next injection. Only paths the platform wrote are ever touched.
- Gitignored— injected skill directories are added to the agent's
.gitignoreand untracked, so git auto-sync never commits platform packages into your repository. - Bounded — 10 MiB per skill, 50 MiB per injection. An over-cap skill fails by name; the rest still inject.
- Honest about dependencies — a declaration-only check produces per-skill warnings (a missing binary, a missing environment variable) instead of failing. Environment checks report variable names only; values are never read.
MCP Tools
| Tool | Description |
|---|---|
| list_skills() | List library skills. Each entry carries its source name and any shadowed_by sources. |
| get_skill(name) | Skill details and contract |
| get_skills_library_status() | Library sync status, including the per-source array |
| assign_skill_to_agent(skill_name, agent_name) | Assign one skill and deliver it. The response's delivery block reports injected, pending_start, in_progress, or not_delivered with a reason |
| set_agent_skills(agent_name, skill_names) | Set the full skill list. Added names are delivered, dropped names are removed; delivery and removal report each half |
| sync_agent_skills(agent_name) | Force re-inject into a running agent — the manual retry after a not_delivered |
| get_agent_skills(agent_name) | List an agent's assigned skills |
| list_runnable_skills() | Skill Runner (entitled): the skills this agent is permitted to run — its permitted set, decided by an operator, not the whole library |
| run_skill(skill_name, input?) | Skill Runner (entitled): run a permitted skill in a separate workspace and return its result |
There is no MCP tool for source management. The Skill Runner is an entitled surface — in a community build, run_skill and list_runnable_skillsreturn a "disabled" result.
Error Responses
| Status | Meaning |
|---|---|
| 400 | Sync failed, invalid source (non-github.com URL, bad ref), or invalid request |
| 401 | Invalid or missing JWT token |
| 403 | Admin access required (library sync, source management), or an API key used where the endpoint is human-only |
| 404 | Skill, source, or agent not found |
| 409 | Another injection already holds the agent (POST /api/agents/{name}/skills/inject) |
See the Skills and Playbooks guide for the concepts behind sources, assignment, and injection, and the Abilities Marketplace for the plugin catalog used to build agents.