| name | customize |
| description | Create, configure, and maintain custom agent profiles and author new skills via the `repl` tool. Use when the user wants to create an agent profile, build a custom agent, modify agent capabilities, attach or detach skills/connectors on a profile, author a skill, or inspect which connectors and tools are available. Also use whenever you need the `host.agents.*` or `host.skills.*` Python SDK. |
| license | Apache-2.0 |
Customize
Build and maintain agent profiles and skills programmatically via the
repl tool using host.agents.* and host.skills.*.
A profile is a named bundle that shapes how an agent behaves:
system_prompt — the profile's identity. This is the opening of the
agent's system prompt; it REPLACES the generic "You are Claude Science" base identity.
Write it in second person, lead with You are {display_name}, ..., state what
the agent specializes in and what it does NOT do. Everything else (tool-usage
rules, working-style bullets, scope guardrail) is inherited automatically —
don't restate it.
display_name / description / icon_key / color_key — picker metadata.
skill_names (optional restriction) — by default a profile sees the
full live skill catalog via search_skills / skill(...), same as the
main agent. Pass an explicit list ONLY to deliberately restrict it; [] creates
a zero-skill specialist. Restricting skills also restricts connectors —
a single unrestricted flag governs both; passing skill_names flips the
profile to curated mode and starts it with zero connectors (see next).
- Connector access — an unrestricted profile (the default) reaches
every connector (bundled + custom + authorized directory), same as the
main agent; use
detach_connector to subtract specific ones. A curated
profile (one created with an explicit skill_names list, or flipped via
{"unrestricted": False}) starts with no connectors — reach is exactly
what you attach_connector.
excludedTools — per-tool blocklist applied after connectors resolve.
Use to strip specific high-risk or irrelevant tools from an otherwise-useful
connector. Per-connector, not a profile field — set via
attach_connector(..., include_tools_pattern=/exclude_tools_pattern=); the
profile's excludedTools in list() is the read-only aggregation across all
its attached connectors. Patterns match the connector's bare tool names
(e.g. '^list_marts$', as returned by list_connectors(name)['tools']); the
aggregated excludedTools entries are stored fully-qualified as
mcp_<connector>_<tool> since the list spans every attached connector.
Python SDK
All calls run via the repl tool (see "Runs via the repl tool"
below). Return values are plain dicts/lists; errors raise RuntimeError
with a host.agents.*: / host.skills.*: prefix.
host.agents
host.agents.list()
host.agents.create(name, display_name, description,
system_prompt="", skill_names=None)
host.agents.update(name, patch)
host.agents.switch(name)
host.agents.delete(name)
host.agents.attach_skill(name, skill)
host.agents.detach_skill(name, skill)
host.agents.attach_connector(name, connector,
include_tools_pattern=None,
exclude_tools_pattern=None)
host.agents.detach_connector(name, connector)
host.agents.list_connectors(connector_name=None)
host.skills
host.skills.list()
host.skills.read(name, path="SKILL.md")
host.skills.edit(name, path, content, old_string=None)
host.skills.publish(name, overwrite=False)
host.skills.delete(name)
Runs via the repl tool
host.agents.* / host.skills.* execute in the control-plane
kernel — a separate Python process from your python cells, reached via
the repl tool (not the python tool). It shares your workspace
directory (cwd) but not memory, so variables from python cells aren't
visible there and vice-versa. To hand results across, write to a file —
same pattern as Python↔R:
import json, os
os.makedirs("handoff", exist_ok=True)
profiles = host.agents.list()
json.dump(profiles, open("handoff/agents.json", "w"))
import json, pandas as pd
profiles = json.load(open("handoff/agents.json"))
pd.DataFrame(profiles)[["name", "source", "enabled"]]
Workflow: scope → draft → review → create
Do not call generate_plan for profile CRUD. This is a single
scope→draft→confirm loop; ask_user (step 4) is the review gate. A plan
adds a second approval that duplicates the ask_user confirmation and
drags in step-status bookkeeping that fights this workflow.
User approval. Most host.agents.* / host.skills.* calls apply
immediately — create/update (including unrestricted),
attach_*/detach_*, and skills.publish/skills.edit are pre-approved
at session start and the cell does NOT pause. An approval card (cell
pauses, resumes automatically on Allow — you don't retry) appears only
for the calls that hand off identity or free a granted name:
host.agents.switch(name) — per-target; Allow covers this name only
host.agents.update(name, {"name": ...}) (rename) — per-target
host.agents.delete(name) / host.skills.delete(name) — one card
per project: the first delete shows a card; "Allow for this project"
covers every subsequent delete in this project, including bulk
teardown. Do NOT tell the user they'll see N cards for N deletes.
The name is read from the runtime call for switch and rename, so both
literal and variable names work (host.agents.switch("FOO") or
host.agents.switch(name_var)); keep the ask_user review step
so the click is a quick confirm, not a surprise. After the call returns,
read back (host.agents.list()) to confirm the actual state — don't
narrate an expected card.
Reading existing profiles
Call host.agents.list() to see the user's current profiles so you don't
duplicate an existing agent. The main agent's bundled profile is protected —
it cannot be renamed or deleted.
1. Scope first
What is this agent for? A profile should have one job. Pick an UPPER_SNAKE
name (RNASEQ_REVIEWER, not RNA-seq review helper). Use ask_user if the
name or scope is unclear — profiles are user-visible in the picker.
2. Write the identity
system_prompt is the agent's opening paragraph — it replaces the base
identity, it's not an addendum. Lead with You are {display_name}. State the
specialization and the boundaries ("You handle X, Y, Z. You do not handle
..."). Keep it under ~200 words; the heavy how-to lives in skills, not the
prompt.
3. Ask: full access or a subset?
Before creating, ask_user whether this profile should have full access
(the live skill catalog and every connector — same reach as the main agent;
new skills and connectors appear automatically) or a restricted subset
(a fixed list you'll curate together). Don't infer this from the role
description — a narrowly-described specialist may still want full reach,
and a broadly-described one may want a tight loadout. Pair this with the
name/prompt review in step 4 so it's one round-trip.
4. Review with the user
Show the proposed name, display name, description, system_prompt, and the
user's full-vs-subset choice from step 3. ask_user to confirm before
writing. If they chose a subset, list the proposed skills/connectors here.
5. Create
Call host.agents.create(name, display_name, description, system_prompt=...).
If the user chose full access, leave skill_names unset. If they chose a
subset, pass skill_names=[...] with the agreed list — this flips the
profile to curated mode and starts it with zero connectors, so attach the
agreed connectors after create as in step 6. For edits to an existing profile,
use host.agents.update(name, {...}) with targeted fields; prefer
host.agents.attach_skill(...) / host.agents.detach_skill(...) and
host.agents.attach_connector(...) / host.agents.detach_connector(...)
over wholesale skill_names replacement so you don't clobber the user's own
edits.
After the profile exists, offer to switch to it:
host.agents.switch(name). The user sees an approval card; on Allow, this
conversation continues as the new specialist from their next message. If they
decline, tell them they can select it from the session config popover on any
new conversation.
6. Restricting the loadout (when the user chose a subset)
If the user chose a subset in step 3, curate after create:
- Skills:
host.agents.update(name, {"skill_names": [...]}) with the
exact list, or detach_skill one at a time. Check host.skills.list()
for available names. On update, skill_names is an exact replace — never
send a partial list to "add"; fetch the current skillNames (camelCase in
the return dict), modify, send back.
- Connectors: a curated profile (created with an explicit
skill_names
list, or flipped via {"unrestricted": False}) starts with NO
connectors — reach is exactly what you attach. Call
host.agents.list_connectors() to see every available connector (bundled +
directory + user-added MCP) with its auth state, then
host.agents.attach_connector(name, connector_name) for each connector
the user agreed to keep. Use include_tools_pattern=/exclude_tools_pattern=
on the attach call to scope which tools the profile gets (omit both to
expose every tool; re-attaching without patterns preserves the existing
exclusion list — pass include_tools_pattern='.*' to clear). A connector
with authState other than "authorized" or "not-required" must be
connected via the Connectors panel before it can be attached.
detach_connector only removes an explicit attachment; on a curated