Cookbook
Agents & skills · for AI agents

Create a grounded support agent

Create an agent with a soul, a task with boundaries, tools and a knowledge base — over MCP.

MCP tools:list_toolslist_knowledge_basescreate_agentget_agentcreate_share

Before you build — discover, don't run this cold. Interview the tenant about their job, their material, the procedures it runs and the systems it must touch; plan the whole setup (agent + knowledge base + skills + MCP as needed) and play it back before creating anything. The steps below are what you run after that — see Discover before you build.

Principle — one agent, one job. Give this agent a single, clearly bounded task. If the business has three jobs (support and sales and scheduling), build three agents — a "does everything" agent answers each thing worse. More in Design principles.

An agent is soul + task + tools + skills + knowledge_bases. soul is identity and voice; task is the job and its boundaries — both go into the fixed prefix of the system prompt.

1. See what you can attach

list_tools()             # tools available to this tenant (including connected MCP tools)
list_knowledge_bases()   # knowledge bases you can mount

2. Create it

On by default, so you don't have to ask for them. A new agent can already reply with a tappable single/multi-choice form (ask_forms) when it needs two or three facts before it can answer, and can already draw a chart (compute_chart is in the default tool list). Both were off by default until 2026-08-03, and the create call had no parameter for the first — so agents built before then have neither, and update_agent(ask_forms=True, add_tools=["compute_chart"]) is how you bring one up to date.

Passing tools=[...] replaces the default list rather than adding to it, so include compute_chart yourself whenever you pass tools at all.

Names are lower-case. The name is not only what you see — eleven tables reference an agent by it (shares, sessions, storylines, channel bindings, usage), so Pip and pip are two different agents to all of them and the same one to you. New names are lower-cased on save; write them that way and there is nothing to reconcile. It is also not the public URL — that is alias.

create_agent(
  name="support",                  # lower-case; the platform lower-cases it anyway
  alias="support",                 # public human-readable URL slug — set one (url-safe, lowercase)
  soul="You are the support assistant for Acme Loans. Professional and warm.",
  task="Answer questions about mortgage products and the application process. "
       "Never promise a disbursement date; never give legal or tax advice; "
       "for any specific quote, call a tool — do not answer from memory.",
  tools=["web_search"],
  knowledge_bases=["company-policy"],   # use the KB's returned slug name (see below)
  published=True,
)
  • alias is the agent's public human-readable address segment ({public_base}/t/<tenant>/<alias>) — set it so you can hand people a memorable link. It's normalised to a url-safe slug; a clash comes back in alias_result.
  • System tools are on by default. New agents automatically get current_time, ip_geo and weather (the built-in system MCP) — you don't list them; tools is for the extra ones.
  • Names in URLs are slugged. A knowledge base name becomes a url-safe slug on creation ("Company Policy" → company-policy); attach it by the returned name, not what you typed.

3. Confirm what landed

get_agent(name="Support")   # writing it doesn't mean it looks the way you intended

published=True means visible, not reachable. End users can't get to the agent until you create a share (step 4). Don't stop at "published".

Boundaries belong in task — negative constraints ("never promise…") stop drift better than positive description. Don't put safety rules in soul; the platform appends global moderation automatically. To change one field later, use update_agent(name, field=…) — it merges, so it won't blank the rest.

4. Publish it — ask how, then make it reachable

Before you say "done", ask the tenant how their customers should reach it, then wire that channel with create_share (it returns a real, openable link — hand that back, not "it's published"):

create_share(agent_name="Support", label="Website widget")
# → { token, chat_url, qr_url, embed_snippet, pretty_url, … }

If the response carries pretty_url (…/t/<tenant-alias>/<agent-alias>), that's the link for humans — readable and stable across token rotation. If it's null, set the missing alias (create_agent(alias=…) / PUT /agents/{name}/alias; tenant alias in console → Settings) rather than shipping the token link. chat_url remains right for embeds and QR codes.

  • No website — just a link or a QR (a freelancer, a shop, a flyer, a business card): hand back chat_url (a full-screen hosted chat page — no site needed) and qr_url (a QR they can print). It's anonymous: no login, and each visitor is remembered by their browser, so regulars are recognised.
  • Their own website: give them embed_snippet (one line before </body> for a floating widget), or chat_url to link / iframe.
  • Telegram / WhatsApp: set up per-channel in the console (Agent → Integration → Telegram / WhatsApp); point them there.

Report back. Hand back the actual link they can open and test right now (chat_url, and qr_url if they have no site), plus the agent's console page to review it — https://console.agent4.io/#/agents/Support. Not "it's published" — the link.