{"id":"f0aa3c9d-d5c4-40c0-8b53-c858e414541c","entityType":"agent","slug":"clawhub-hellojixian-agent4-io","name":"agent4.io","canonicalUrl":"https://www.xpersona.co/agent/clawhub-hellojixian-agent4-io","canonicalPath":"/agent/clawhub-hellojixian-agent4-io","generatedAt":"2026-10-10T06:43:37.890Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":null},"description":"Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks. Skill: agent4.io Owner: hellojixian Summary: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks. Tags: latest:1.1.11 Version history: v1.1.11 | 2026-08-08T14:20:52.319Z | user Knowledge base routing: a base's description now decides whether a question searches it at all — new Cookbook section on writing one that routes,","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.8K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s172sz43hyj8hrb9dmr6bat0gx83kgrd:agent4-io","sourceUrl":"https://clawhub.ai/hellojixian/agent4-io","homepage":"https://clawhub.ai/hellojixian/skills/agent4-io","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/hellojixian/agent4-io","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/hellojixian/skills/agent4-io","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":65,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks. Skill: ag"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":null},"stars":null,"forks":null,"downloads":1797,"packageName":null,"latestVersion":"1.1.11","tractionLabel":"1.8K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T01:45:41.177Z","lastCrawledAt":"2026-10-10T01:45:41.177Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T01:45:41.177Z","lastVerifiedAt":null,"highlights":[{"version":"1.1.11","createdAt":"2026-08-08T14:20:52.319Z","changelog":"Knowledge base routing: a base's description now decides whether a question searches it at all — new Cookbook section on writing one that routes, measured against a nine-base tenant.","fileCount":3,"zipByteSize":44578},{"version":"1.1.10","createdAt":"2026-08-07T23:58:47.212Z","changelog":"Cookbook sync: two output corollaries in the design principles (a prompt rule is a request, a post-check is a rule; parse for the load-bearing field instead of demanding strict JSON), and knowledge v9 — write the retrieval sentence next to the value, the structured header index, and reading 'dropped'.","fileCount":3,"zipByteSize":43940},{"version":"1.1.9","createdAt":"2026-08-04T10:09:06.527Z","changelog":"Optional highlight_color: a second brand colour for the second chart series and citation markers, with foregrounds derived like the primary.","fileCount":3,"zipByteSize":40946},{"version":"1.1.8","createdAt":"2026-08-03T22:36:26.938Z","changelog":"Rename a share with configure_share(label=…); agent names are lower-cased on save; forms and charts on by default for new agents.","fileCount":3,"zipByteSize":40347},{"version":"1.1.7","createdAt":"2026-08-03T19:41:32.838Z","changelog":"Charts in answers: agents draw comparable numbers automatically, with a containment gate that drops any chart whose figures are not in the retrieved material. New compute_chart built-in works out shares, growth, running totals, projections and metered bills in code rather than in the model.","fileCount":3,"zipByteSize":39689},{"version":"1.1.6","createdAt":"2026-08-01T12:50:20.518Z","changelog":"Per-step icebreaker questions (node.questions chips), step summaries in the read-only map (click a taken step to see what it collected), whole-key blackboard packing for long flows, New-chat dropdown fix.","fileCount":3,"zipByteSize":37665},{"version":"1.1.5","createdAt":"2026-08-01T08:47:26.101Z","changelog":"Storyline automation upgrade: node checklists (turn-by-turn check-off + deterministic checklist rule), gated choice buttons (choices_offer=on_ready), files>=N exit rules, on_enter/on_complete deterministic tool actions, judge now sees conversation window + collected state.","fileCount":3,"zipByteSize":37414},{"version":"1.1.4","createdAt":"2026-08-01T08:21:49.971Z","changelog":"Records now require contact.email or contact.phone (rejection message instructs collection); storyline user_choice labels must be precise verb phrases; storyline completion signal + explicit re-entry semantics.","fileCount":3,"zipByteSize":36502}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s172sz43hyj8hrb9dmr6bat0gx83kgrd:agent4-io","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T06:43:37.885Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-hellojixian-agent4-io/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":null},"readme":"Skill: agent4.io\n\nOwner: hellojixian\n\nSummary: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\n\nTags: latest:1.1.11\n\nVersion history:\n\nv1.1.11 | 2026-08-08T14:20:52.319Z | user\n\nKnowledge base routing: a base's description now decides whether a question searches it at all — new Cookbook section on writing one that routes, measured against a nine-base tenant.\n\nv1.1.10 | 2026-08-07T23:58:47.212Z | user\n\nCookbook sync: two output corollaries in the design principles (a prompt rule is a request, a post-check is a rule; parse for the load-bearing field instead of demanding strict JSON), and knowledge v9 — write the retrieval sentence next to the value, the structured header index, and reading 'dropped'.\n\nv1.1.9 | 2026-08-04T10:09:06.527Z | user\n\nOptional highlight_color: a second brand colour for the second chart series and citation markers, with foregrounds derived like the primary.\n\nv1.1.8 | 2026-08-03T22:36:26.938Z | user\n\nRename a share with configure_share(label=…); agent names are lower-cased on save; forms and charts on by default for new agents.\n\nv1.1.7 | 2026-08-03T19:41:32.838Z | user\n\nCharts in answers: agents draw comparable numbers automatically, with a containment gate that drops any chart whose figures are not in the retrieved material. New compute_chart built-in works out shares, growth, running totals, projections and metered bills in code rather than in the model.\n\nv1.1.6 | 2026-08-01T12:50:20.518Z | user\n\nPer-step icebreaker questions (node.questions chips), step summaries in the read-only map (click a taken step to see what it collected), whole-key blackboard packing for long flows, New-chat dropdown fix.\n\nv1.1.5 | 2026-08-01T08:47:26.101Z | user\n\nStoryline automation upgrade: node checklists (turn-by-turn check-off + deterministic checklist rule), gated choice buttons (choices_offer=on_ready), files>=N exit rules, on_enter/on_complete deterministic tool actions, judge now sees conversation window + collected state.\n\nv1.1.4 | 2026-08-01T08:21:49.971Z | user\n\nRecords now require contact.email or contact.phone (rejection message instructs collection); storyline user_choice labels must be precise verb phrases; storyline completion signal + explicit re-entry semantics.\n\nv1.1.3 | 2026-07-31T21:39:15.309Z | user\n\nStorylines gain a user-facing layer: user_visibility four tiers (server-side redacted read-only map), user-started flows (entry=user, one-case-per-conversation), allow_exit, show_profile, per-node rewind (reset|keep), IM /storyline command. learner_visibility is deprecated (still mapped).\n\nv1.1.2 | 2026-07-31T18:29:20.761Z | user\n\nPWA install branding is now per-agent: set_pwa_branding takes a required agent param — each agent's /s/ page installs as its own app with its own icon.\n\nv1.1.1 | 2026-07-31T10:59:47.743Z | user\n\nDeterministic intake filing: [[inbox:*]] playbook markers make the platform file form submissions itself — the relayed reference always matches the inbox record\n\nv1.1.0 | 2026-07-31T09:39:03.891Z | user\n\nPlaybook openings can carry an ask form when the context demands greeting-time collection (book-a-callback / leave-a-message intents); starter questions step aside\n\nv1.0.34 | 2026-07-31T08:36:35.877Z | user\n\nDesign principles: 'Never hand determinism to probability' — the platform's core agent-design rule, with a table of real-failure-turned-feature examples (reference codes, run_at, derived colours, rule exits, trigger testing) and the skill-writing corollary.\n\nv1.0.33 | 2026-07-31T08:13:26.498Z | user\n\nschedule_followup now accepts run_at (local ISO datetime, server-side timezone resolution) for absolute times - stops models from hand-computing delay seconds wrong; booking window extended to 7 days.\n\nv1.0.32 | 2026-07-31T07:53:04.347Z | user\n\nsave_contact / schedule_followup now return REAL reference codes (AB2C-D3EF style, phone-safe alphabet) stored server-side and visible on the end user's detail page - instruct agents to relay the returned code verbatim, never invent one.\n\nv1.0.31 | 2026-07-31T00:24:55.218Z | user\n\nPlatform now auto-nudges save_contact / schedule_followup when a contact or callback signal is detected in any language (zero false saves in benchmarks); built-in tool descriptions rewritten bilingual + trigger-first for reliable calls in EN/ES/RU/JA conversations.\n\nv1.0.30 | 2026-07-30T23:58:44.382Z | user\n\nNew test_skill_trigger MCP tool: dry-run your trigger prompts against production prompt assembly + model routing before shipping; per-sample hit/lie report plus actionable advice (few-shot block, trigger placement, edge-case examples) on every failure.\n\nv1.0.29 | 2026-07-30T23:39:53.154Z | user\n\nEvidence-based few-shot guidance for skill instructions: worked-example blocks raise tool-call compliance from 3-4/6 to 6/6 in production benchmarks; includes the four entry types (positive, counter-example, correction, format edge case).\n\nv1.0.28 | 2026-07-30T22:19:00.458Z | user\n\nSkill-authoring paradigm: mandatory tool-call triggers must live in description (instructions are gated behind load_skill); never script tool return values that don't exist. create_skill/update_skill now return lint warnings.\n\nv1.0.27 | 2026-07-30T21:52:17.590Z | user\n\nBranding recipe v9: desktop calendar is platform-drawn and themeable (.ca-dp selectors); native picker stays on touch. New CSS lint broad_element_selector — bare input/textarea selectors leak into system dialogs; scope them.\n\nv1.0.26 | 2026-07-30T21:38:52.618Z | user\n\nBranding recipe v8: form controls are token-styled end to end; the native date pop-up is browser UI and not brandable — color-scheme pinning, the input field and the indicator icon are the whole reachable surface (documented so agents stop chasing the rest).\n\nv1.0.25 | 2026-07-30T19:53:27.979Z | user\n\nBranding recipe v7: the ⌘K conversation-search modal — it follows theme_color automatically; stable selectors (.ca-ss*) documented for strict brand guides, and custom_css now genuinely outranks the component's own styles.\n\nv1.0.24 | 2026-07-30T19:24:58.861Z | user\n\nStoryline concurrency: progress follows the user (shared run) or the conversation (one case per chat) — create/update_storyline concurrency param; guidance on blackboard vs profile state\n\nv1.0.23 | 2026-07-30T18:51:20.345Z | user\n\nClawHub variant no longer recommends the curl|sh installer (flagged by the security audit, reasonably). ClawHub readers already have the skill — connecting is one claude mcp add / one HTTP endpoint with an X-API-Key header; updates via clawhub update. Also: AGENT4_API_KEY requirement now documents where to obtain the key.\n\nv1.0.22 | 2026-07-30T17:16:49.857Z | user\n\nRequirements: AGENT4_API_KEY carries where-to-get-it guidance (dashboard → Tenant & API / console → Settings → Security; free signup link). Re-publish of 1.0.21 whose latest tag never moved.\n\nv1.0.21 | 2026-07-30T16:45:09.192Z | user\n\nRequirements: AGENT4_API_KEY now carries where-to-get-it guidance (dashboard → Tenant & API, or console → Settings → Security; free signup link).\n\nv1.0.20 | 2026-07-30T16:08:36.362Z | user\n\nSkill authoring: binding a tool is availability, not policy — instructions must name the tool, its trigger step and arguments; where each behaviour spec belongs (description vs instructions vs soul)\n\nv1.0.19 | 2026-07-30T15:39:16.490Z | user\n\nFine-grained updates: add_/remove_ params on update_agent & update_skill (whole-list replace footgun documented); skill-bound tools explained; PATCH /knowledge-bases\n\nv1.0.18 | 2026-07-30T09:46:02.347Z | user\n\nBranding v6: hold-to-talk recording glow derives from theme_color (was hardcoded blue); .ca-talk.rec hook + override example documented.\n\nv1.0.17 | 2026-07-30T09:44:02.307Z | user\n\nCustom domains: bind a customer's own domain to the hosted chat page (set_custom_domain MCP tool) — CNAME to endpoint.agent4.io, automatic certificates, async verification with self-diagnosing errors (Cloudflare grey-cloud case included).\n\nv1.0.16 | 2026-07-30T09:20:43.877Z | user\n\nBranding v4: ask-form selection is styled from theme_color out of the box (accent border + tint + accent-color); custom_css hooks for option/checked rows documented with a two-theme override example.\n\nv1.0.15 | 2026-07-30T08:59:44.389Z | user\n\nBranding v3: PWA install branding (set_pwa_branding), the --acc/--acc-fg contract for themed controls incl. ask-form buttons, alias pretty_url as the human link. (Re-publish of 1.0.14.)\n\nv1.0.14 | 2026-07-30T08:43:23.057Z | user\n\nBranding v3: PWA install branding (set_pwa_branding — one master icon to full set, install prompt banner|card|off), the --acc/--acc-fg contract for every themed control incl. ask-form buttons, and alias pretty_url as the link to hand humans (create_share/list_shares now return it).\n\nv1.0.13 | 2026-07-30T01:25:11.793Z | user\n\nBranding recipe v2: state that a single :root block silently kills the light/dark toggle, label the LIGHT/DARK example blocks, explain which variables need the dark branch, and note that !important is no longer required.\n\nv1.0.12 | 2026-07-30T00:51:33.956Z | user\n\nAdd the branding recipe: one brand colour with server-derived readable foregrounds, logo aspect-ratio guidance (header vs collapsed bubble), and the CSS variable list for strict brand guides. New MCP tool configure_share.\n\nv1.0.11 | 2026-07-29T21:32:15.948Z | user\n\nKnowledge module v5: write KB 'instructions' from expected usage at creation (scope / authority / usage-rules template) with per-KB-type worked examples; MCP create_knowledge_base guidance rewritten fill-forward.\n\nv1.0.10 | 2026-07-29T14:08:27.603Z | user\n\nAdd an explicit data/safety disclosure to the skill header (what's transmitted to agent4.io, that only the provided API key is used, no local-file reads, no elevated commands) — addresses the SkillSpector 'Missing User Warnings' finding.\n\nv1.0.9 | 2026-07-29T13:57:43.323Z | user\n\nREST fallback is now searchable: search_agent4_docs indexes the REST API per endpoint, so find the exact endpoint (params+response) without loading all of api.md.\n\nv1.0.8 | 2026-07-29T13:09:53.393Z | user\n\nCreate flow: system MCP tools (time/geo/weather) on by default; set a public alias on create; knowledge-base names become url-safe slugs (use the returned name).\n\nv1.0.7 | 2026-07-29T12:59:16.798Z | user\n\nPublish guide: ask the channel, then create_share and hand back a real link — hosted chat page + printable QR for tenants with no website (anonymous, browser-remembered). Add REST API (api.md) as the fallback for anything MCP doesn't cover.\n\nv1.0.6 | 2026-07-29T11:38:17.433Z | user\n\nStrengthen the deliverable principle: after every creation, hand back what+link+how-to-use AND propose the next step and offer to do it (attach KB, add skill, create share link, set Storyline default...) — a setup is a chain, not a single action.\n\nv1.0.5 | 2026-07-29T11:07:05.512Z | user\n\nOn a Telegram bot: send your own messages with link previews disabled (link_preview_options.is_disabled) so frequent agent4.io links don't spam preview cards; user-sent links still preview.\n\nv1.0.4 | 2026-07-29T11:00:48.453Z | user\n\nAdd a read-first 'Discover before you build' principle: interview the tenant, plan the whole setup (agent + KB + skills + MCP + Storyline), confirm, then build — no more empty-shell agents.\n\nv1.0.3 | 2026-07-29T10:55:36.188Z | user\n\nAdd self-serve guidance: an agent deployed on a Telegram/WhatsApp builder bot registers its command menu via setMyCommands (hyphens→underscores).\n\nv1.0.2 | 2026-07-29T10:48:52.774Z | user\n\nClarify the /agent4-* slash commands are Claude Code/Cursor builder commands, not Telegram/WhatsApp bot menus; OpenClaw folds them into the agent4-io skill.\n\nv1.0.1 | 2026-07-28T22:08:29.396Z | user\n\nDocument slash-command usage (/agent4-io <task>) and set user-invocable.\n\nv1.0.0 | 2026-07-28T22:04:35.360Z | user\n\nInitial release: connect the agent4-io MCP; recipes for agents, knowledge bases, skills, Storylines, page playbooks, usage and user/session lookup.\n\nArchive index:\n\nArchive v1.1.11: 3 files, 44578 bytes\n\nFiles: skill-card.md (2299b), SKILL.md (106390b), _meta.json (129b)\n\nFile v1.1.11:SKILL.md\n\n---\nname: agent4-io\ndescription: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\nhomepage: https://agent4.io/cookbook\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🤖\",\n        \"requires\": { \"env\": [\"AGENT4_API_KEY\"] },\n        \"primaryEnv\": \"AGENT4_API_KEY\",\n        \"envVars\":\n          [\n            {\n              \"name\": \"AGENT4_API_KEY\",\n              \"required\": true,\n              \"description\": \"Your agent4.io tenant API key (tk_…). Get it from the agent4.io dashboard → Tenant & API, or console.agent4.io → Settings → Security — the plaintext is shown once, at creation. No account yet? Sign up free at https://agent4.io (5M tokens/month, no card).\"\n            }\n          ]\n      }\n  }\n---\n\n# agent4.io skills — build agents over MCP\n\nThis skill drives the **agent4.io** platform through its remote MCP server: create grounded agents,\nknowledge bases, load-on-demand skills, stateful Storylines and page playbooks — all from your agent.\n\n## Connect the MCP server (once)\n\nThe tools live on a remote MCP endpoint. Add it to your agent, authenticating with your tenant API key\n(set `AGENT4_API_KEY`; get a key from **console → Settings → Security**, shown once at creation):\n\n- **URL:** `https://api.agent4.io/v1/mcp`\n- **Header:** `X-API-Key: $AGENT4_API_KEY`\n- **Transport:** streamable-http\n\nFor Claude Code / any MCP client that takes a shell command:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOnce connected, call `tenant_info()` to confirm you are on the right tenant, then follow the recipes\nbelow. This skill is a mirror of the always-current Cookbook at https://agent4.io/cookbook.\n\n## Use it as a slash command\n\nThis skill is invocable directly — `/agent4-io <what you want>` — and routes to the right recipe below:\n\n- `/agent4-io create a support agent grounded in these docs` → build a grounded agent\n- `/agent4-io build a knowledge base from this site + these PDFs` → create & populate a KB\n- `/agent4-io author a load-on-demand skill for booking` → write a Skill\n- `/agent4-io compile this intake flow into a Storyline` → design & publish a Storyline\n- `/agent4-io set a page-aware opener for /pricing` → configure a page playbook\n- `/agent4-io how much quota have I used?` → usage & quota\n- `/agent4-io who are my heaviest users this week?` → look up users and their sessions\n- `/agent4-io what is a Storyline?` → look up an agent4.io concept\n\n(On Claude Code and Cursor, the installer also lays down finer-grained shortcuts —\n`/agent4-agent`, `/agent4-kb`, `/agent4-skill`, `/agent4-storyline`, `/agent4-docs` — but they all just\nhand off to this same skill; here a single `/agent4-io` covers them.)\n\n---\n\n# agent4.io — build agents over MCP\n\nThis guide is assembled from the agent4.io Cookbook (https://agent4.io/cookbook). Modules:\n\n- **agent4-io-principles** (v5) — Read-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\n- **agent4-io-recipes** (v8) — Connect to agent4.io over MCP, install the skills, and run end-to-end recipes.\n- **agent4-io-admin** (v9) — Create and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\n- **agent4-io-knowledge** (v9) — Build, populate, verify and query grounded knowledge bases.\n- **agent4-io-storyline** (v8) — Compile multi-step processes into stateful, guided Storyline graphs.\n\n---\nname: agent4-io-principles\ndescription: Read-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\nversion: 5\n---\n\nRead-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\n\n## Design principles — read this first\n\nMost \"the agent gives bad answers\" problems are configuration, not the model. Follow these before you\nbuild, and the results hold up. Ignore them and it is easy to assemble something that looks plausible and\nbehaves badly — then mistake that for a platform limit.\n\n## Discover before you build — interview, don't just execute\n\n\"Create a support agent\" is the **start of the conversation, not the spec.** Never answer it with a single\n`create_agent` and hand back a blank shell. Work like the setup wizard: **interview first, plan the whole\nsetup, play it back, then build.** Ask in plain language — a few questions at a time, about their real\nsituation, not tool parameters — until you can picture the finished thing:\n\n- **The job & who it serves.** What is this agent for, who talks to it, what does a *good* answer look\n  like? One job per agent — if they describe three, that's three agents.\n- **What it must answer from → a knowledge base.** Do they have documents, a site, policies, a price\n  list? If facts have to be right, those become a [knowledge base](/cookbook/build-a-knowledge-base) you\n  build and attach — not prompt text. If they have nothing yet, tell them what to gather.\n- **Procedures it runs → skills.** Any \"when X, do these steps\" behaviours (book, screen, quote)? Each\n  becomes a load-on-demand [skill](/cookbook/create-a-skill) with a sharp \"use this when…\".\n- **Things it must *do* in other systems → MCP.** Check a calendar, look up an order, open a ticket?\n  Those are [MCP tools](/cookbook/connect-your-mcp-server) to bind — ask which system and whether they can\n  connect it.\n- **A guided, stateful flow → a Storyline.** Is it a process with memory (intake → qualify → follow-up)\n  rather than one-shot Q&A? That's a [Storyline](/cookbook/build-a-storyline), not just an agent.\n- **How it opens and its hard limits.** The first line visitors see (→ a [page playbook](/cookbook/configure-page-playbooks))\n  and the boundaries that go in `task` (\"never quote a price\", \"never give legal advice\").\n\nThen **play the plan back before touching a tool** — \"so I'll build: agent *Support*, a knowledge base\nfrom your policy PDFs, a *booking* skill, and bind your calendar over MCP — right?\" — and build only once\nthey confirm. Skipping this is exactly how you end up with an empty agent nobody wanted. A vague request\nis a cue to ask, never a cue to guess.\n\n## One agent, one job\n\nGive each agent a **single, clearly bounded task**. A \"does everything\" agent — support *and* sales\n*and* scheduling — has a diffuse `task`, competes with itself for attention, and answers each thing\nworse. If you have three jobs, build three agents.\n\n## Keep the skill count small\n\nAttach **only the skills this agent's job needs — roughly five or fewer.** Skills are chosen by the model\nfrom their one-line `description`; the more you attach, the harder that choice, and the more often it\nloads the wrong one or none. A focused set with sharp \"use this when…\" descriptions beats a big pile.\n\n- Each skill's `description` says **when** to reach for it, in one line.\n- Procedures live in `instructions` (loaded on demand), never in `soul` (paid for every turn).\n- If two skills overlap in *when*, merge them — overlapping triggers make the choice a coin-flip.\n\n## Ground facts; put boundaries in the task\n\n- Facts (rates, policy, catalogue) belong in a **knowledge base**, which is retrieved every turn — not\n  in the prompt, where they go stale and un-cited. See [Build a knowledge base](/cookbook/build-a-knowledge-base).\n- Constraints belong in **`task`**, as negatives: \"never promise a date\", \"for a quote, call a tool\".\n  Negative boundaries stop drift better than positive description. Don't put safety rules in `soul` —\n  the platform appends global moderation for you.\n- Turn on `grounding_required` when answers must come from the material, not the model's priors.\n\n## Never hand determinism to probability\n\nThe single most useful design rule on this platform: **if something can be computed, generated, or\nverified by the system, never leave it to the model.** A model asked to produce a deterministic\nartifact doesn't fail loudly — it produces a plausible one. The user gets a ticket number that\ndoesn't exist, a callback booked seven hours off, a colour palette that fails contrast. Nothing\nerrors; it's just quietly wrong.\n\nEvery one of these started as a real production failure and became a platform feature:\n\n| Deterministic thing | Wrong way (probability) | Right way (system) |\n|---|---|---|\n| Reference / ticket numbers | Instructions say \"tell the user the ticket number\" → model invents one | `save_contact` / `schedule_followup` **return a real stored code** — instruct the model to relay it verbatim |\n| Absolute times | Model hand-computes `delay_seconds` for \"tomorrow 10am\" → off by hours | Pass **`run_at`** (local ISO time); the server resolves the timezone |\n| Text colours on a brand colour | Model picks \"matching\" colours → unreadable in one theme | Send `theme_color` only; WCAG-contrast foregrounds are **derived server-side** |\n| Routing in a flow | An `ai` exit for \"if the user agreed\" | `rule` / `user_choice` exits; reserve `ai` exits for genuine judgment. Loops get an explicit counter and cap — never \"the LLM will stop eventually\" |\n| \"Did the trigger fire?\" | Assume the prompt works | `test_skill_trigger` measures it; the platform also injects a deterministic hint when a phone/email is detected |\n| The wording that reaches a tool | Hope the model turns \"any others like that?\" into the right call | The platform works out what was asked, then **composes the instruction from the tool's own definition** and shows that turn one tool. Measured 80% → 97%; letting the *model* rewrite its own request did nothing (82%) |\n| A phrase that must never appear | Add another \"do not say X\" line to the prompt | **Delete it after the fact.** A rule you can check on the finished answer is a rule you can enforce; a rule in the prompt is a request |\n| Machine-readable output | \"Reply with valid JSON only\" and parse strictly | Ask for the shape, then **parse for the one field that matters**. Strictness throws away answers that were right |\n\nThe corollary for writing skills: your `instructions` should tell the model **which system facility\nto use** (\"pass run_at, relay the returned code\"), not teach it to imitate the facility (\"compute\nthe seconds, format a ticket number\"). If you find yourself scripting the *output* of a\ndeterministic process, look for the tool that produces it — or ask for one.\n\n### Two corollaries about output\n\n**A prompt rule is a request; a post-check is a rule.** \"Never say X\" belongs in the prompt — it\nlowers the rate — but it is not enforcement. A model that agrees with the instruction can still\nreach the same forbidden idea by a phrasing your wording didn't anticipate, and each rewrite of the\nrule tends to catch only the phrasings you already saw. Wording cannot police wording. So ask a\ndifferent question: **is the unwanted sentence recognisable in the finished answer?** If it is,\nremove it there, and keep the prompt line as well.\n\n**Get the content right first; do not let syntax cost you the content.** A smaller model will\noften make the correct choice and then write it in a shape your parser rejects — one object per\nline instead of an array, a trailing comma, prose wrapped around the block. Parsing strictly means\na correct answer is discarded for a misplaced brace, and the symptom looks like a model that isn't\ncapable enough. Decide which field in the response is **load-bearing** — usually exactly one: an\nid, a reference, a choice — and extract that field however it arrives. The rest of the payload is\ntypically data you were going to replace with your own anyway, so strictness about it protects\nnothing.\n\n## Show the agent good patterns only\n\nWhen you hand an agent examples, make them **correct** examples. Don't paste a \"here's the wrong way\"\nsnippet next to the right one — the model may imitate the nearest example rather than read the caveat.\nDescribe what to avoid in words; keep runnable examples exemplary.\n\n## Verify — writing is not working\n\nAfter every change, check it did what you intended. Creating an agent doesn't mean it's configured the\nway you think; adding to a knowledge base doesn't mean the question retrieves.\n\n```text\nget_agent(name=\"Support\")                                   # confirm the config that landed\nsearch_knowledge_base(kb_name=\"Company policy\", query=\"…\")  # confirm the answer is retrievable\n```\n\nAn empty `search` result means that question will be answered as \"not covered\" — find that now, not from\na customer.\n\n## Hand back a deliverable — and the next step\n\nAfter every action, don't just report that you finished. Hand back **four things**: what you produced, a\nclickable console link to view it, one line on how to use it, and **the natural next step — proposed, and\noffered to do.** They will ask \"where do I see it?\", \"how do I use it?\" and \"what now?\" anyway; answer all\nthree up front. URL-encode names that contain spaces.\n\n| After you… | Hand back |\n|---|---|\n| Create or import into a **knowledge base** | Its page — which includes the **knowledge starmap** (a 3D view of what was ingested): `https://console.agent4.io/#/knowledge-bases/<name>` |\n| Create an **agent** | Its page to review/test: `https://console.agent4.io/#/agents/<name>` — and note that to let end users reach it, they create a **share link** in the console |\n| Create a **skill** | `https://console.agent4.io/#/skills/<name>` |\n| Publish a **Storyline** | `https://console.agent4.io/#/storylines/<id>` (the id from `create_storyline`) |\n| Register an **MCP server** | `https://console.agent4.io/#/mcp/<id>` |\n\nFor example, after importing documents into a knowledge base, reply with how many chunks landed, the link\nabove so they can open its **knowledge starmap** and see exactly what was ingested, and confirm whether\nit's now attached to an agent (or how to attach it). A bare \"done\" just makes them ask.\n\n**Always end on the next step, and offer to do it** — a setup is a chain, not a single action:\n\n- Built a **knowledge base**? → offer to attach it to an agent (ask which).\n- Created an **agent**? → offer to attach a knowledge base, add a skill, or create a **share link** so\n  end users can reach it (an empty space = off).\n- Authored a **skill**? → offer to attach it to the agent that needs it.\n- Published a **Storyline**? → offer to set it as the agent's default, or wire its enrollment trigger.\n- Registered an **MCP server**? → offer to grant an agent its tools.\n\n\"Here's what I made, here's the link, here's how to use it, and here's what I'd do next — want me to?\"\nkeeps the build moving; a bare \"done\" leaves the tenant guessing.\n\n---\nname: agent4-io-recipes\ndescription: Connect to agent4.io over MCP, install the skills, and run end-to-end recipes.\nversion: 8\n---\n\nConnect to agent4.io over MCP, install the skills, and run end-to-end recipes.\n\n## Get started with the agent4.io agent skills\n\nagent4.io ships a remote **MCP server** (the tools) plus a set of **skills** (how to use the tools).\nPoint your agent at the MCP endpoint, install the skills once, and every recipe below becomes an\nexecutable, step-by-step procedure.\n\n## What this skill does with your data — tell the user\n\nThis skill connects to **agent4.io's remote API** (`api.agent4.io`). Be explicit with the user before you\nsend their material:\n\n- **It transmits to agent4.io.** The agent configuration, knowledge-base content and queries you send go\n  to agent4.io's servers to build and run agents there — that is the platform's purpose, not a side effect.\n- **It uses exactly one credential — your agent4.io API key** (`tk_…`), which the user provides. It does\n  **not** read other environment variables, and it does **not** read or enumerate your local files:\n  `add_knowledge_file` deliberately cannot access your machine's paths; you only ever send content you\n  explicitly pass to a knowledge-base tool.\n- **Nothing runs with elevated privileges.** The installer only writes skill files into your agent's own\n  skills folder — no `sudo`, and it fetches only from agent4.io.\n- **On a chat channel**, a bot token the user provides is used **solely** to call that channel's own API at\n  the user's instruction (e.g. Telegram `setMyCommands`) — never sent to agent4.io.\n\nIf any of this isn't acceptable to the user, stop — don't send their data.\n\n## Connect — one step\n\nYou already have this skill (installed via ClawHub). The only thing left is the remote MCP server:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOr add it to any MCP client's config — URL `https://api.agent4.io/v1/mcp`, header\n`X-API-Key`, transport streamable-http. No installer, no shell script: one HTTP endpoint.\nGet a key from **console → Settings → Security** (the plaintext is shown once, at creation).\n\n## Verify\n\n```text\ntenant_info()   # → confirms you are connected to the right tenant\n```\n\n## Keep it current\n\n```bash\nclawhub update agent4-io\n```\n\nThe MCP tools are remote, so they are always current; only this skill document is versioned.\n\n## Slash commands (for your coding CLI — not for the deployed bot)\n\nThese are **builder shortcuts for coding CLIs that read a `commands/` folder** — Claude Code\n(`~/.claude/commands/`) and Cursor (`.cursor/commands/`). The installer drops them so *you*, building on\nagent4.io, can type them in your CLI:\n\n- `/agent4-agent <what>` — create a grounded agent\n- `/agent4-kb <name + source>` — build a knowledge base, import, verify, attach\n- `/agent4-skill <when + what>` — author a load-on-demand skill\n- `/agent4-storyline <process>` — compile a process into a Storyline and publish\n- `/agent4-docs <question>` — look up an agent4.io concept or how-to\n\nEach carries no logic of its own — it points at the matching `agent4-io-*` skill and passes what you type.\n\n**They are not the deployed bot's commands.** They will not — and should not — show up in a Telegram or\nWhatsApp bot's slash menu. Those menus are shown to your **end users** and are set separately by the bot's\noperator (e.g. Telegram's `setMyCommands`); they list end-user actions, never builder operations like\n\"create an agent\". Different audience, different mechanism — if you're deploying on a chat channel, ignore\nthese and configure that channel's own command menu.\n\n**On OpenClaw / Codex** (no `commands/` folder) there are no separate command files: the agent uses the\n`agent4-io` skill directly — the model invokes it, or you call the single `/agent4-io <task>` command — and\nthe five shortcuts above fold into that.\n\n### Running on a Telegram / WhatsApp bot? Register the menu yourself\n\nIf **you (the agent) are deployed on a chat channel**, the channel's command menu is *not* synced from\nskills — you set it through the channel's own API. Do this **only when this bot is your operator / builder\nassistant** (the person using it builds on agent4.io); **skip it for a customer-facing bot**, whose menu is\nfor end users, not builder operations.\n\nTelegram command names may contain only `a-z 0-9 _` — **no hyphens** — so rename `agent4-agent` →\n`agent4_agent`. When you have the bot token, register them once with `setMyCommands`:\n\n```bash\ncurl -s \"https://api.telegram.org/bot$BOT_TOKEN/setMyCommands\" -H \"Content-Type: application/json\" -d '{\n  \"commands\":[\n    {\"command\":\"agent4_agent\",\"description\":\"Create a grounded agent\"},\n    {\"command\":\"agent4_kb\",\"description\":\"Build a knowledge base\"},\n    {\"command\":\"agent4_skill\",\"description\":\"Author a load-on-demand skill\"},\n    {\"command\":\"agent4_storyline\",\"description\":\"Compile a process into a Storyline\"},\n    {\"command\":\"agent4_docs\",\"description\":\"Look up an agent4.io doc\"}\n  ]}'\n```\n\nWhen a user taps one, the channel sends that text to you as an ordinary message — handle it with this\nskill (`agent4_agent` = the `/agent4-agent` shortcut, and so on). No bot token / shell access? Set the\nsame list by hand in **@BotFather → `/setcommands`**. WhatsApp has no slash menu; expose the same actions\nas an interactive list/quick-reply instead.\n\n**Silence your own link previews.** You cite `agent4.io` links constantly, so Telegram will attach a\npreview card to nearly every reply — noisy fast. Send **your** messages with previews off:\n`link_preview_options: {\"is_disabled\": true}` on `sendMessage` (legacy API: `disable_web_page_preview: true`).\nThis affects only your messages; a link the **user** sends still previews as normal.\n\n## When there's no MCP tool for it — the REST API is the full surface\n\nThe MCP tools cover the common build-and-run operations, but they are not the whole platform. For\nanything they don't expose — a field, an endpoint, a bulk job — the **complete tenant REST API** covers\neverything a tenant can do. Call it directly with the same key: `X-API-Key: tk_...`.\n\nTwo ways to reach it, cheapest first:\n\n- **`search_agent4_docs(\"… rest api …\")`** — the docs search now indexes the REST API per endpoint. A\n  REST/HTTP-worded query returns the **exact endpoint** with its parameters and response, without loading\n  the whole reference. Use this first.\n- **[https://agent4.io/api.md](https://agent4.io/api.md)** — the full machine-readable reference (every\n  endpoint, params, request/response, examples). Read the whole file only when you need the broad picture.\n\nMCP is the fast path; the REST API is the fallback.\n\n## What to do next\n\n- [Create a grounded support agent](/cookbook/create-support-agent)\n- [Build and populate a knowledge base](/cookbook/build-a-knowledge-base)\n- [Author a load-on-demand skill](/cookbook/create-a-skill)\n- [Configure a page-aware chat opener](/cookbook/configure-page-playbooks)\n- [Compile a flow into a Storyline](/cookbook/build-a-storyline)\n\nEvery recipe names the exact MCP tool and arguments — never \"open this page and click\".\n\n---\nname: agent4-io-admin\ndescription: Create and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\nversion: 9\n---\n\nCreate and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\n\n## Create a grounded support agent\n\n> **Before you build — discover, don't run this cold.** Interview the tenant about their job, their\n> material, the procedures it runs and the systems it must touch; plan the whole setup (agent + knowledge\n> base + skills + MCP as needed) and play it back before creating anything. The steps below are what you\n> run *after* that — see [Discover before you build](/cookbook/design-principles).\n\n> **Principle — one agent, one job.** Give this agent a single, clearly bounded task. If the business\n> has three jobs (support *and* sales *and* scheduling), build three agents — a \"does everything\" agent\n> answers each thing worse. More in [Design principles](/cookbook/design-principles).\n\nAn agent is **soul + task + tools + skills + knowledge_bases**. `soul` is identity and voice; `task` is\nthe job and its boundaries — both go into the fixed prefix of the system prompt.\n\n## 1. See what you can attach\n\n```text\nlist_tools()             # tools available to this tenant (including connected MCP tools)\nlist_knowledge_bases()   # knowledge bases you can mount\n```\n\n## 2. Create it\n\n> **On by default, so you don't have to ask for them.** A new agent can already reply with a\n> tappable single/multi-choice **form** (`ask_forms`) when it needs two or three facts before it can\n> answer, and can already draw a **chart** (`compute_chart` is in the default tool list). Both were\n> off by default until 2026-08-03, and the create call had no parameter for the first — so agents\n> built before then have neither, and `update_agent(ask_forms=True, add_tools=[\"compute_chart\"])`\n> is how you bring one up to date.\n>\n> Passing `tools=[...]` **replaces** the default list rather than adding to it, so include\n> `compute_chart` yourself whenever you pass tools at all.\n\n> **Names are lower-case.** The name is not only what you see — eleven tables reference an agent by\n> it (shares, sessions, storylines, channel bindings, usage), so `Pip` and `pip` are two different\n> agents to all of them and the same one to you. New names are lower-cased on save; write them that\n> way and there is nothing to reconcile. It is also not the public URL — that is `alias`.\n\n```text\ncreate_agent(\n  name=\"support\",                  # lower-case; the platform lower-cases it anyway\n  alias=\"support\",                 # public human-readable URL slug — set one (url-safe, lowercase)\n  soul=\"You are the support assistant for Acme Loans. Professional and warm.\",\n  task=\"Answer questions about mortgage products and the application process. \"\n       \"Never promise a disbursement date; never give legal or tax advice; \"\n       \"for any specific quote, call a tool — do not answer from memory.\",\n  tools=[\"web_search\"],\n  knowledge_bases=[\"company-policy\"],   # use the KB's returned slug name (see below)\n  published=True,\n)\n```\n\n- **`alias`** is the agent's public human-readable address segment (`{public_base}/t/<tenant>/<alias>`) —\n  set it so you can hand people a memorable link. It's normalised to a url-safe slug; a clash comes back\n  in `alias_result`.\n- **System tools are on by default.** New agents automatically get `current_time`, `ip_geo` and `weather`\n  (the built-in `system` MCP) — you don't list them; `tools` is for the *extra* ones.\n- **Names in URLs are slugged.** A **knowledge base** name becomes a url-safe slug on creation\n  (`\"Company Policy\"` → `company-policy`); attach it by the **returned** name, not what you typed.\n\n## 3. Confirm what landed\n\n```text\nget_agent(name=\"Support\")   # writing it doesn't mean it looks the way you intended\n```\n\n<Callout>\n`published=True` means **visible**, not **reachable**. End users can't get to the agent until you create\na **share** (step 4). Don't stop at \"published\".\n</Callout>\n\nBoundaries belong in `task` — negative constraints (\"never promise…\") stop drift better than positive\ndescription. Don't put safety rules in `soul`; the platform appends global moderation automatically. To\nchange one field later, use `update_agent(name, field=…)` — it merges, so it won't blank the rest.\n\n## 4. Publish it — ask how, then make it reachable\n\nBefore you say \"done\", ask the tenant **how their customers should reach it**, then wire that channel with\n`create_share` (it returns a real, openable link — hand that back, not \"it's published\"):\n\n```text\ncreate_share(agent_name=\"Support\", label=\"Website widget\")\n# → { token, chat_url, qr_url, embed_snippet, pretty_url, … }\n```\n\nIf the response carries `pretty_url` (`…/t/<tenant-alias>/<agent-alias>`), **that's the link for\nhumans** — readable and stable across token rotation. If it's null, set the missing alias\n(`create_agent(alias=…)` / `PUT /agents/{name}/alias`; tenant alias in console → Settings) rather\nthan shipping the token link. `chat_url` remains right for embeds and QR codes.\n\n- **No website — just a link or a QR** (a freelancer, a shop, a flyer, a business card): hand back\n  `chat_url` (a full-screen **hosted chat page** — no site needed) and `qr_url` (a QR they can print).\n  It's anonymous: no login, and each visitor is remembered by their browser, so regulars are recognised.\n- **Their own website**: give them `embed_snippet` (one line before `</body>` for a floating widget), or\n  `chat_url` to link / iframe.\n- **Telegram / WhatsApp**: set up per-channel in the console (Agent → **Integration** →\n  Telegram / WhatsApp); point them there.\n\n> **Report back.** Hand back the **actual link they can open and test right now** (`chat_url`, and\n> `qr_url` if they have no site), plus the agent's console page to review it —\n> `https://console.agent4.io/#/agents/Support`. Not \"it's published\" — the link.\n\n## Author a load-on-demand skill\n\n> **Principle — keep the skill count small.** Attach only what this agent's job needs (≈5 or fewer).\n> The model picks skills from their one-line `description`; the more you attach, the more often it loads\n> the wrong one or none. If two skills overlap in *when*, merge them. More in\n> [Design principles](/cookbook/design-principles).\n\nA **skill** is a capability pack loaded on demand: the system prompt carries only its `description`\n(\"when to use\"); the model pulls the full `instructions` only when it judges the skill relevant. So the\n`description` must be short and say *when*, or the model won't know to reach for it.\n\n```text\nlist_skills()                                # what already exists\n\ncreate_skill(\n  name=\"refund-policy\",\n  description=\"Use when the user asks about refunds, cancellations or chargebacks.\",\n  instructions=\"The full procedure: eligibility windows, how to word the outcome, when to escalate …\",\n)\n\nupdate_agent(name=\"Support\", add_skills=[\"refund-policy\"])   # attach it (incremental — keeps existing skills)\n```\n\nKeep procedures in `instructions` (fetched on load), not in the agent's `soul` (which is in the prompt\nevery turn). A one-line `description` that says *when* is what makes the skill discoverable — a vague one\nmeans the model never pulls it.\n\n## Tools can ride on the skill\n\nA skill can carry its own tool bindings — `create_skill(tools=[…])` or\n`update_skill(name, add_tools=[…])`. Tools bound to an attached skill **join the agent's toolset\nautomatically at chat time**, so a skill can ship as a self-contained capability: procedure + the\ntools it needs, attached in one move.\n\nTwo things to know before you rely on it:\n\n- **They don't appear in the agent's own `tools` list.** `get_agent` shows only the agent's directly\n  attached tools; the console's Tools tab shows skill-bound ones as a separate read-only \"via skills\"\n  line. When verifying \"is tool X enabled\", check both places — or just call the tool in a test chat.\n- Prefer binding a tool to the **skill** when it only makes sense inside that procedure (a refund\n  lookup inside the refund skill); bind to the **agent** when it's generally useful across turns\n  (`save_contact`, `web_search`).\n\n## Binding a tool is availability, not policy — the instructions must say when to call it\n\nChecking a tool on a skill (or agent) only makes it *callable*. The model sees the tool's schema and\nits one-line description every turn, so it may use it opportunistically — a visitor volunteers an\nemail and `save_contact` fires. But **any behaviour that must happen reliably needs to be written\ndown**, and each part of it has one right home:\n\n| What you're specifying | Where it goes |\n|---|---|\n| *When to load this skill* | the skill's `description` (in the prompt every turn) |\n| *The procedure: at which step to call which tool, with what arguments* | the skill's `instructions` — **name the tool explicitly** (\"after the caller confirms interest, call `save_contact` with email and phone; then call `schedule_followup` for 1 business day later\") |\n| *A behaviour that must drive the whole conversation* (always capture leads, never quote rates) | the agent's `soul` / `task` |\n\nThe failure mode when instructions don't name the tool: everything *looks* configured — the tool is\nchecked, the skill loads — and the agent still never calls it, or calls it with guessed arguments.\nNothing errors. Write the procedure as if briefing a new employee: the step, the tool name, the\narguments, and what \"done\" looks like.\n\n## Mandatory tool calls: the trigger must live in `description`, not only in `instructions`\n\n`instructions` are visible to the model **only after it calls `load_skill`** — and models frequently\nanswer directly without loading the skill. So a rule like *\"when the user provides a phone number,\nyou must call `save_contact`\"* written only in `instructions` is invisible exactly when it matters:\nthe tool is silently never called, and the model may even claim it saved the contact. This happened\nin production — an agent answered several contact-bearing messages in a row, \"confirmed\" the details\nwere recorded, and no record existed.\n\nThe fix is one sentence in the `description` (which *is* in the system prompt every turn):\n\n```text\nupdate_skill(\n  name=\"consultative-sales\",\n  description=\"Consultative sales: understand the case, recommend products, arrange expert callbacks. \"\n              \"When the user provides a phone number or email, call save_contact BEFORE answering anything else.\",\n)\n```\n\nKeep the detailed procedure in `instructions`; put the *trigger* of any mandatory call in\n`description`.\n\nTwo related rules:\n\n- **Never script return values the tool doesn't produce — relay real ones verbatim.** Built-in\n  `save_contact` and `schedule_followup` return a **real reference code** on success (format\n  `AB2C-D3EF`, stored server-side and visible to the tenant on the end user's detail page).\n  Instruct the model to relay *the code from the tool result* verbatim — never to invent one or\n  reformat it as \"#12345\". For any other tool, verify it actually returns an ID before scripting\n  one: a promised-but-absent ID will be fabricated.\n- **`create_skill` / `update_skill` / `get_skill` now return a `warnings` list** that flags exactly\n  these two patterns (`trigger_hidden_in_instructions`, `promised_tool_return_id`). Warnings are\n  advisory — the save succeeds — but treat them like a linter: fix, don't ignore.\n\n## Worked examples in `instructions` measurably raise tool-call compliance\n\nWe benchmarked this on production backends (6 lead-capture messages of increasing difficulty —\nnumbers buried in long questions, digit groups with spaces, corrections — sampled per condition).\nWith the skill loaded, a plain \"you must call X\" mandate hit **3–4/6**; adding a short block of\nworked examples brought both tested models to **6/6**. Moving the mandate section around did\nnothing — *position doesn't matter, examples do*.\n\nAn effective example block has four kinds of entries, each one line:\n\n```text\n### Worked examples (follow exactly)\n\n1. User: \"I'd like to know about X, my phone is 13800138000\"\n   → call save_contact(phone=\"13800138000\") first, then answer about X.\n2. User: \"email me the offer: li@example.com\"\n   → call save_contact(email=\"li@example.com\").\n3. COUNTER-EXAMPLE (forbidden): user gives a phone number and you reply\n   \"I've noted it down\" WITHOUT calling the tool — claiming success without\n   the call is the worst failure.\n4. User: \"sorry, wrong number — it's 13633334444\"\n   → call save_contact again with the corrected value.\n5. Numbers may contain spaces (\"138 0013 9000\") — still a phone number;\n   strip the spaces and call save_contact(phone=\"13800139000\").\n```\n\nThe counter-example (3) and the format edge case (5) close most of the remaining misses — models\nfail on *recognition* (\"is this a phone number?\") and on *honesty under pressure* (answering a rich\ndomain question first and claiming the save happened) more than on willingness. Keep it to ~5\nentries; use the exact call syntax with realistic arguments.\n\n## Test the trigger before shipping — don't count corpses in production\n\nWhether a skill *actually* fires is measurable, so measure it. `test_skill_trigger` dry-runs your\nmessages against the **production** prompt assembly, tool schemas and model routing, and reports\nwhat the model decided — tools are never executed, nothing is stored, tokens count toward your\nquota (caps: 5 messages × 5 samples).\n\n```text\ntest_skill_trigger(\n  agent=\"advisor\",\n  messages=[\n    \"My phone is 555 0123, call me back\",          # easy\n    \"long question about the product … oh and my number is 555 0123\",  # buried\n    \"555 0123 — that's me\",                        # implicit\n    \"sorry, wrong number, it's 555 9999\",          # correction\n  ],\n  expect_tool=\"save_contact\",\n  samples=3,\n  loaded=true,        # simulate post-load_skill → tests instructions quality\n)                     # loaded=false (default) → first turn, tests the description trigger\n```\n\nRead the result like this:\n\n- **`hit_rate`** below ~90% on realistic messages → strengthen the trigger (description) or add\n  worked examples (instructions), then re-test.\n- **`claimed_without_call` > 0** is the worst failure — the model told the user \"noted!\" without\n  calling the tool. Add the counter-example from the block above.\n- Test **both modes**: `loaded=false` proves the description alone triggers on turn one;\n  `loaded=true` proves the loaded instructions don't dilute it (long instructions measurably do —\n  that's what the worked examples compensate for).\n\nThe result also carries an **`advice` list**: when samples miss or lie, it tells you exactly which\nfix to apply (trigger into the description, add the worked-example block, add a counter-example or\nformat edge case) with the benchmark numbers behind each recommendation — apply it and re-test.\n\nThe full loop: `create_skill` → fix any `warnings` (static lint) → `test_skill_trigger` (dynamic\nreality check) → apply its `advice` → re-test until the hit rate holds.\n\n## Configure a page playbook (page-aware chat opener)\n\n> **Principle — the opener is the only sentence guaranteed to be read.** A generic \"How can I help?\"\n> converts a page visit into nothing. A playbook lets the *same* agent open differently on each page,\n> already knowing where the visitor is. More in [Page playbooks](/concepts/playbooks).\n\nA **page playbook** is a small briefing attached to a URL pattern. It has three parts:\n\n- **`context`** — private background for the agent, **not shown** to the visitor (who lands on this\n  page, what they're deciding, what they usually worry about). Write background, not facts: prices,\n  quotas and policy belong in a [knowledge base](/cookbook/build-a-knowledge-base), which outranks it.\n- **`greeting`** — the opening line the visitor actually sees.\n- **`questions`** — up to four suggested questions, so a visitor who hasn't formed one can just pick.\n\nThe right playbook is chosen from the URL: resolution order is **explicit key → `url_pattern` →\ndefault**. A single default catches every unmatched page, so nothing ever falls back to a blank box.\n\n## 1. See what's already there\n\n```text\nlist_page_contexts()   # existing playbooks: match rules, greeting mode, position, which is default\n```\n\n## 2. Create or replace one\n\n```text\nupsert_page_context(\n  key=\"pricing\",\n  label=\"Pricing page\",\n  url_pattern=\"*/pricing\",           # glob, path-only; ignores query string and trailing slash\n  context=\"Visitors here are comparing plans and worrying about overage. \"\n          \"They are usually the person who will sign off on the cost. \"\n          \"Do not quote custom pricing in chat.\",\n  greeting=\"You're looking at our plans — want me to work out where overage would start for your volume?\",\n  questions=[\"What's included in the free plan?\", \"How is overage billed?\", \"Can I change plans later?\"],\n  greeting_mode=\"generated\",         # generate opener + questions in the visitor's language (recommended)\n  is_default=False,\n)\n```\n\n`greeting_mode=\"generated\"` writes the opener and questions per visitor language on the fly — so a\nSpanish visitor gets a Spanish greeting without you authoring five versions. Use `\"static\"` only when\nyou want your exact `greeting`/`questions` verbatim.\n\n## 3. Verify the match — always\n\n```text\nresolve_page_context(url=\"https://acme.com/pricing?ref=x\")   # → which playbook this URL hits, and how\n```\n\nGlobs are easy to get subtly wrong (a missing `*`, one path level too many). A mismatch has **no error\nmessage** — the visitor just silently gets the default playbook. So after writing any `url_pattern`,\nresolve a real URL and confirm `matched_by` is `url_pattern`, not `default`.\n\n## 4. Set a catch-all default\n\n```text\nupsert_page_context(\n  key=\"default\",\n  label=\"Everywhere else\",\n  context=\"General visitor to the site; intent unknown. Ask what brought them in before assuming.\",\n  greeting_mode=\"generated\",\n  is_default=True,                    # at most one default per tenant; setting this unsets any other\n)\n```\n\n<Callout>\n`upsert_page_context` is a **full replace**, not a patch: fields you omit fall back to defaults rather\nthan keeping their old value. To change one field, `list_page_contexts()` first, merge, then upsert.\n</Callout>\n\nOnce playbooks have been live for a while, `page_context_stats()` shows which ones get opened and which\nsuggested questions get clicked — so you tune copy from data, not guesses.\n\n> **Report back.** Give the user the console link to review and edit these —\n> `https://console.agent4.io/#/page-contexts` — and confirm each `url_pattern` resolves the way they\n> expect (step 3). The chat widget needs no code change for a static site; single-page apps that have\n> no distinct URL per view can name a playbook by its `key` instead.\n\n## Openings that collect on the spot\n\nIf a playbook's `context` explicitly instructs presenting a form at first contact — e.g.\n`\"PRESENT THE BOOKING FORM IN YOUR GREETING/OPENING: (1) preferred channel (phone / email — single),\n(2) phone or email (text)\"` — the generated opening will include that interactive form, and the\nstarter questions are suppressed (the form is the guidance). Submitting counts as a normal visitor\nmessage, so `save_contact` / `schedule_followup` fire as they would mid-conversation.\n\nUse it for intents where asking first is the whole point — \"book a callback\", \"leave a message\" —\nand keep it to 2–3 fields. For intents where the visitor wants answers first (support,\ntroubleshooting), let the conversation start normally and collect in the first reply instead.\n\n## Records require a contact — by design\n\nEvery record-creating tool (`submit_lead`, `escalate`, `request_booking`, `open_checklist`)\n**rejects the call unless `contact.email` or `contact.phone` is filled in** — a record nobody can\nfollow up on is noise, not a lead. The rejection message tells the agent what to do (ask for a\ncontact first, never invent one), so make your playbooks collect email/phone up front — the\ngreeting-time forms above exist precisely for this.\n\n## File form submissions deterministically\n\nFor intake-style playbooks (bookings, leads, complaints), append a machine marker to the\n`context`: `[[inbox:submit_lead]]`, `[[inbox:escalate]]` or `[[inbox:request_booking]]`. Form\nsubmissions on that playbook are then **filed by the platform before the model replies** — the\nreply's reference is guaranteed to be the filed record's reference, and the contact is saved\nalongside. Keep the prose instructions too: they cover the free-conversation path (details given\nwithout the form), where the model still does the filing.\n\n## Deleting things — point the user to the console\n\nDeletions are deliberately **not** exposed as tools — they are irreversible, and from a conversation you\nusually can't tell whether an agent or knowledge base is still used by something else. So when the user\nasks you to delete something, **don't attempt it** — tell them exactly where to do it in the console at\n[console.agent4.io](https://console.agent4.io), and offer to help with the safe parts (finding the item,\nlisting what depends on it) first.\n\n| To delete… | Where in the console |\n|---|---|\n| An agent | Agents → open the agent → top-right **⋯ / Delete** |\n| A knowledge base (and all its documents) | Knowledge → open the base → **Delete** |\n| One document in a knowledge base | Knowledge → the base → **Documents** tab → the row → **Delete** |\n| A skill | Skills → the skill → **Delete** (agents referencing it stop loading its guidance) |\n| A user, space, or session | Users → the row → **Delete** |\n| Revoke / re-issue an API key | Settings → **Security** → **Revoke** (then create a new one) |\n| Change privacy mode / BYOK | Settings (this shifts compliance responsibility — confirm before changing) |\n\nBefore you send them off, it helps to **surface what would be affected**: e.g. list the agents that\nmount a knowledge base (`list_agents` + `get_agent`) so they can see whether deleting it breaks anything.\nThat check is exactly why deletion is a human action in the console, not a tool call.\n\n## Connect your own MCP server\n\nBring your own tools: register a **remote** MCP server and its tools become available to attach to your\nagents. There is no \"create MCP server\" tool on purpose — registering a server means trusting it to run\nagainst your tenant, so it is a deliberate action in the console (or a REST call), not something an agent\ndoes on its own.\n\n## 1. Register the server\n\nIn the console: **Tools / MCP → Add MCP server** — give it a name, choose the transport\n(**Streamable HTTP** or **SSE**), and the URL plus any auth headers.\n\nOr by REST, with your tenant key (`$BASE` = `https://api.agent4.io/v1`):\n\n```bash\ncurl -X POST $BASE/mcp-servers -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' \\\n  -d '{\"name\":\"my-tools\",\"transport\":\"streamable_http\",\n       \"config\":{\"url\":\"https://tools.example.com/mcp\",\"headers\":{\"Authorization\":\"Bearer …\"}}}'\n```\n\nOnly **remote** transports are accepted. `stdio` (local commands) is rejected — the server runs on the\nplatform, not your machine, so a local command would be both useless to you and a code-execution risk.\n\n## 2. See its tools\n\n```text\nlist_mcp_servers()   # the MCP servers registered on your tenant\nlist_tools()         # every tool you can attach to an agent, including the ones from your servers\n```\n\n## 3. Attach the tools to an agent\n\n```text\nupdate_agent(name=\"Support\", add_tools=[\"…\"])   # incremental; use tool names exactly as list_tools() reports them\n```\n\nThe agent can now call those tools during a conversation. Keep the set focused — a handful of relevant\ntools beats a big pile (see [Design principles](/cookbook/design-principles)).\n\n## Built-in tools (and the system MCP)\n\nYou don't have to build the common tools — the platform ships them. Attach the ones this agent's job\nneeds with `update_agent(name, add_tools=[…])`; keep the set small (see [Design principles](/cookbook/design-principles)).\nAlways confirm the exact names on your tenant with `list_tools()` — that is the source of truth.\n\n> **`tools=[…]` replaces the whole list; `add_tools` / `remove_tools` change one item.** Passing\n> `tools=` with only the tools you're thinking about silently drops every tool you didn't read first —\n> this has happened in production and nobody noticed until a feature stopped working. Reach for\n> `tools=` only when you deliberately mean \"exactly this set\", and read the returned agent to confirm\n> the final list either way. The same pairs exist for skills (`add_skills`/`remove_skills`) and\n> knowledge bases (`add_knowledge_bases`/`remove_knowledge_bases`).\n\n## The built-in tools\n\n| Tool | What it does |\n|---|---|\n| `web_search` | Look something up on the open web. |\n| `save_contact` | Capture the end user's contact details into their profile — the platform's lead-capture / records path. Returns a **real reference code** (`AB2C-D3EF` style) the agent relays to the user; look codes up on the user's detail page (`recent_refs`). |\n| `export_document` | Generate a structured document (a branded summary / report) for the conversation. |\n| `schedule_followup` | Proactively follow up later (\"check back in 2 days\") or book a user-requested callback — the message is sent for you. Returns a **real booking reference** traceable to the scheduled task. For absolute times (\"tomorrow 10am\") instruct the model to pass **`run_at`** (local ISO datetime + optional `tz`) — the server resolves the user's timezone; hand-computed `delay_seconds` is for relative times only, and models get the arithmetic wrong. Window: up to 7 days. |\n| `schedule_reminder` | Set a reminder for the end user; `list_reminders` / `cancel_reminder` manage them. |\n| `compute_chart` | Work out a derived series **in code** — share of total, growth %, running total, a projection at a fixed rate, or a metered bill (base fee + allowance + per-unit overage). Attached automatically to any agent that already has tools, so you rarely add it by hand. See [Charts in answers](/cookbook/charts-in-answers). |\n\n`load_skill` is also built in, but you don't attach it — the model pulls a skill on its own when a\nskill's `description` matches.\n\n```text\nlist_tools()                                              # exact names available on your tenant\nupdate_agent(name=\"Support\", add_tools=[\"save_contact\", \"schedule_followup\"])   # incremental — keeps what's there\n```\n\nCapturing a contact with `save_contact` populates the user's record; combined with `ask_forms` on the\nagent, structured intake lands as records you can review in the console.\n\n**The platform backs these two up automatically.** When the end user's message contains a clear\nsignal — a phone number / email (any language, incl. spelled-out digits and spaced formats), or a\n\"call me tomorrow morning\"-style callback request — and `save_contact` / `schedule_followup` is\nenabled, the platform injects a per-turn hint reminding the model to call the tool before answering.\nBenchmarked on production backends, this lifted lead-capture compliance on the weakest model from\n~85% to 100% with **zero** false saves (the hint carries an \"ignore if wrong\" escape hatch, so an\nover-eager detector cannot cause bad data). You get this for free — no configuration; your skill\nshould still state the trigger in its `description` (see [Author a skill](/cookbook/create-a-skill)),\nthe hint is a safety net, not a replacement.\n\n## The system MCP — already installed\n\nEvery tenant gets a built-in **`system`** MCP server, enabled by default, with keyless tools for\n**local time, weather, and IP geolocation** (city / region / timezone). You don't register it — it's\nthere. Its tools show up in `list_tools()` alongside the built-ins above, ready to attach.\n\nSo \"what's the weather where the user is?\" or \"what's their local time?\" work out of the box — the\nplatform injects the end user's client IP, so location-aware answers need\n\nFile v1.1.11:_meta.json\n\n{\n  \"ownerId\": \"kn715vygmbtxev9229v6dqrc71838kjn\",\n  \"slug\": \"agent4-io\",\n  \"version\": \"1.1.11\",\n  \"publishedAt\": 1786198852319\n}\n\nFile v1.1.11:skill-card.md\n\n## Description:\n\nBuild and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[hellojixian](https://clawhub.ai/user/hellojixian)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and tenant operators use this skill to connect an agent to agent4.io, then create and configure agents, knowledge bases, load-on-demand skills, Storylines, page playbooks, usage checks, and related tenant workflows.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Tenant configuration, knowledge-base material, and queries may be sent to agent4.io when the skill is used.\n\nMitigation: Tell the user before sending data and send only tenant material they explicitly provide.\n\nRisk: The AGENT4_API_KEY grants access to administer the connected agent4.io tenant.\n\nMitigation: Keep the key restricted, avoid exposing it in logs or shared output, and revoke it when no longer needed.\n\nRisk: MCP or REST actions can change agents, knowledge bases, Storylines, page playbooks, or other tenant state.\n\nMitigation: Confirm the intended tenant and user intent before state-changing actions, and use tenant_info() to verify the connection.\n\n## Reference(s):\n\n- [agent4.io Cookbook](https://agent4.io/cookbook)\n- [agent4.io](https://agent4.io)\n- [agent4.io MCP endpoint](https://api.agent4.io/v1/mcp)\n- [ClawHub skill page](https://clawhub.ai/hellojixian/skills/agent4-io)\n- [ClawHub publisher profile](https://clawhub.ai/user/hellojixian)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, API calls, Markdown, Code]\n\n**Output Format:** [Markdown guidance with inline shell, text, JSON, and API examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires AGENT4_API_KEY and may guide state-changing tenant administration actions.]\n\n## Skill Version(s):\n\n1.1.11 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.10: 3 files, 43940 bytes\n\nFiles: skill-card.md (2674b), SKILL.md (104349b), _meta.json (129b)\n\nFile v1.1.10:SKILL.md\n\n---\nname: agent4-io\ndescription: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\nhomepage: https://agent4.io/cookbook\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🤖\",\n        \"requires\": { \"env\": [\"AGENT4_API_KEY\"] },\n        \"primaryEnv\": \"AGENT4_API_KEY\",\n        \"envVars\":\n          [\n            {\n              \"name\": \"AGENT4_API_KEY\",\n              \"required\": true,\n              \"description\": \"Your agent4.io tenant API key (tk_…). Get it from the agent4.io dashboard → Tenant & API, or console.agent4.io → Settings → Security — the plaintext is shown once, at creation. No account yet? Sign up free at https://agent4.io (5M tokens/month, no card).\"\n            }\n          ]\n      }\n  }\n---\n\n# agent4.io skills — build agents over MCP\n\nThis skill drives the **agent4.io** platform through its remote MCP server: create grounded agents,\nknowledge bases, load-on-demand skills, stateful Storylines and page playbooks — all from your agent.\n\n## Connect the MCP server (once)\n\nThe tools live on a remote MCP endpoint. Add it to your agent, authenticating with your tenant API key\n(set `AGENT4_API_KEY`; get a key from **console → Settings → Security**, shown once at creation):\n\n- **URL:** `https://api.agent4.io/v1/mcp`\n- **Header:** `X-API-Key: $AGENT4_API_KEY`\n- **Transport:** streamable-http\n\nFor Claude Code / any MCP client that takes a shell command:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOnce connected, call `tenant_info()` to confirm you are on the right tenant, then follow the recipes\nbelow. This skill is a mirror of the always-current Cookbook at https://agent4.io/cookbook.\n\n## Use it as a slash command\n\nThis skill is invocable directly — `/agent4-io <what you want>` — and routes to the right recipe below:\n\n- `/agent4-io create a support agent grounded in these docs` → build a grounded agent\n- `/agent4-io build a knowledge base from this site + these PDFs` → create & populate a KB\n- `/agent4-io author a load-on-demand skill for booking` → write a Skill\n- `/agent4-io compile this intake flow into a Storyline` → design & publish a Storyline\n- `/agent4-io set a page-aware opener for /pricing` → configure a page playbook\n- `/agent4-io how much quota have I used?` → usage & quota\n- `/agent4-io who are my heaviest users this week?` → look up users and their sessions\n- `/agent4-io what is a Storyline?` → look up an agent4.io concept\n\n(On Claude Code and Cursor, the installer also lays down finer-grained shortcuts —\n`/agent4-agent`, `/agent4-kb`, `/agent4-skill`, `/agent4-storyline`, `/agent4-docs` — but they all just\nhand off to this same skill; here a single `/agent4-io` covers them.)\n\n---\n\n# agent4.io — build agents over MCP\n\nThis guide is assembled from the agent4.io Cookbook (https://agent4.io/cookbook). Modules:\n\n- **agent4-io-principles** (v5) — Read-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\n- **agent4-io-recipes** (v8) — Connect to agent4.io over MCP, install the skills, and run end-to-end recipes.\n- **agent4-io-admin** (v9) — Create and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\n- **agent4-io-knowledge** (v9) — Build, populate, verify and query grounded knowledge bases.\n- **agent4-io-storyline** (v8) — Compile multi-step processes into stateful, guided Storyline graphs.\n\n---\nname: agent4-io-principles\ndescription: Read-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\nversion: 5\n---\n\nRead-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\n\n## Design principles — read this first\n\nMost \"the agent gives bad answers\" problems are configuration, not the model. Follow these before you\nbuild, and the results hold up. Ignore them and it is easy to assemble something that looks plausible and\nbehaves badly — then mistake that for a platform limit.\n\n## Discover before you build — interview, don't just execute\n\n\"Create a support agent\" is the **start of the conversation, not the spec.** Never answer it with a single\n`create_agent` and hand back a blank shell. Work like the setup wizard: **interview first, plan the whole\nsetup, play it back, then build.** Ask in plain language — a few questions at a time, about their real\nsituation, not tool parameters — until you can picture the finished thing:\n\n- **The job & who it serves.** What is this agent for, who talks to it, what does a *good* answer look\n  like? One job per agent — if they describe three, that's three agents.\n- **What it must answer from → a knowledge base.** Do they have documents, a site, policies, a price\n  list? If facts have to be right, those become a [knowledge base](/cookbook/build-a-knowledge-base) you\n  build and attach — not prompt text. If they have nothing yet, tell them what to gather.\n- **Procedures it runs → skills.** Any \"when X, do these steps\" behaviours (book, screen, quote)? Each\n  becomes a load-on-demand [skill](/cookbook/create-a-skill) with a sharp \"use this when…\".\n- **Things it must *do* in other systems → MCP.** Check a calendar, look up an order, open a ticket?\n  Those are [MCP tools](/cookbook/connect-your-mcp-server) to bind — ask which system and whether they can\n  connect it.\n- **A guided, stateful flow → a Storyline.** Is it a process with memory (intake → qualify → follow-up)\n  rather than one-shot Q&A? That's a [Storyline](/cookbook/build-a-storyline), not just an agent.\n- **How it opens and its hard limits.** The first line visitors see (→ a [page playbook](/cookbook/configure-page-playbooks))\n  and the boundaries that go in `task` (\"never quote a price\", \"never give legal advice\").\n\nThen **play the plan back before touching a tool** — \"so I'll build: agent *Support*, a knowledge base\nfrom your policy PDFs, a *booking* skill, and bind your calendar over MCP — right?\" — and build only once\nthey confirm. Skipping this is exactly how you end up with an empty agent nobody wanted. A vague request\nis a cue to ask, never a cue to guess.\n\n## One agent, one job\n\nGive each agent a **single, clearly bounded task**. A \"does everything\" agent — support *and* sales\n*and* scheduling — has a diffuse `task`, competes with itself for attention, and answers each thing\nworse. If you have three jobs, build three agents.\n\n## Keep the skill count small\n\nAttach **only the skills this agent's job needs — roughly five or fewer.** Skills are chosen by the model\nfrom their one-line `description`; the more you attach, the harder that choice, and the more often it\nloads the wrong one or none. A focused set with sharp \"use this when…\" descriptions beats a big pile.\n\n- Each skill's `description` says **when** to reach for it, in one line.\n- Procedures live in `instructions` (loaded on demand), never in `soul` (paid for every turn).\n- If two skills overlap in *when*, merge them — overlapping triggers make the choice a coin-flip.\n\n## Ground facts; put boundaries in the task\n\n- Facts (rates, policy, catalogue) belong in a **knowledge base**, which is retrieved every turn — not\n  in the prompt, where they go stale and un-cited. See [Build a knowledge base](/cookbook/build-a-knowledge-base).\n- Constraints belong in **`task`**, as negatives: \"never promise a date\", \"for a quote, call a tool\".\n  Negative boundaries stop drift better than positive description. Don't put safety rules in `soul` —\n  the platform appends global moderation for you.\n- Turn on `grounding_required` when answers must come from the material, not the model's priors.\n\n## Never hand determinism to probability\n\nThe single most useful design rule on this platform: **if something can be computed, generated, or\nverified by the system, never leave it to the model.** A model asked to produce a deterministic\nartifact doesn't fail loudly — it produces a plausible one. The user gets a ticket number that\ndoesn't exist, a callback booked seven hours off, a colour palette that fails contrast. Nothing\nerrors; it's just quietly wrong.\n\nEvery one of these started as a real production failure and became a platform feature:\n\n| Deterministic thing | Wrong way (probability) | Right way (system) |\n|---|---|---|\n| Reference / ticket numbers | Instructions say \"tell the user the ticket number\" → model invents one | `save_contact` / `schedule_followup` **return a real stored code** — instruct the model to relay it verbatim |\n| Absolute times | Model hand-computes `delay_seconds` for \"tomorrow 10am\" → off by hours | Pass **`run_at`** (local ISO time); the server resolves the timezone |\n| Text colours on a brand colour | Model picks \"matching\" colours → unreadable in one theme | Send `theme_color` only; WCAG-contrast foregrounds are **derived server-side** |\n| Routing in a flow | An `ai` exit for \"if the user agreed\" | `rule` / `user_choice` exits; reserve `ai` exits for genuine judgment. Loops get an explicit counter and cap — never \"the LLM will stop eventually\" |\n| \"Did the trigger fire?\" | Assume the prompt works | `test_skill_trigger` measures it; the platform also injects a deterministic hint when a phone/email is detected |\n| The wording that reaches a tool | Hope the model turns \"any others like that?\" into the right call | The platform works out what was asked, then **composes the instruction from the tool's own definition** and shows that turn one tool. Measured 80% → 97%; letting the *model* rewrite its own request did nothing (82%) |\n| A phrase that must never appear | Add another \"do not say X\" line to the prompt | **Delete it after the fact.** A rule you can check on the finished answer is a rule you can enforce; a rule in the prompt is a request |\n| Machine-readable output | \"Reply with valid JSON only\" and parse strictly | Ask for the shape, then **parse for the one field that matters**. Strictness throws away answers that were right |\n\nThe corollary for writing skills: your `instructions` should tell the model **which system facility\nto use** (\"pass run_at, relay the returned code\"), not teach it to imitate the facility (\"compute\nthe seconds, format a ticket number\"). If you find yourself scripting the *output* of a\ndeterministic process, look for the tool that produces it — or ask for one.\n\n### Two corollaries about output\n\n**A prompt rule is a request; a post-check is a rule.** \"Never say X\" belongs in the prompt — it\nlowers the rate — but it is not enforcement. A model that agrees with the instruction can still\nreach the same forbidden idea by a phrasing your wording didn't anticipate, and each rewrite of the\nrule tends to catch only the phrasings you already saw. Wording cannot police wording. So ask a\ndifferent question: **is the unwanted sentence recognisable in the finished answer?** If it is,\nremove it there, and keep the prompt line as well.\n\n**Get the content right first; do not let syntax cost you the content.** A smaller model will\noften make the correct choice and then write it in a shape your parser rejects — one object per\nline instead of an array, a trailing comma, prose wrapped around the block. Parsing strictly means\na correct answer is discarded for a misplaced brace, and the symptom looks like a model that isn't\ncapable enough. Decide which field in the response is **load-bearing** — usually exactly one: an\nid, a reference, a choice — and extract that field however it arrives. The rest of the payload is\ntypically data you were going to replace with your own anyway, so strictness about it protects\nnothing.\n\n## Show the agent good patterns only\n\nWhen you hand an agent examples, make them **correct** examples. Don't paste a \"here's the wrong way\"\nsnippet next to the right one — the model may imitate the nearest example rather than read the caveat.\nDescribe what to avoid in words; keep runnable examples exemplary.\n\n## Verify — writing is not working\n\nAfter every change, check it did what you intended. Creating an agent doesn't mean it's configured the\nway you think; adding to a knowledge base doesn't mean the question retrieves.\n\n```text\nget_agent(name=\"Support\")                                   # confirm the config that landed\nsearch_knowledge_base(kb_name=\"Company policy\", query=\"…\")  # confirm the answer is retrievable\n```\n\nAn empty `search` result means that question will be answered as \"not covered\" — find that now, not from\na customer.\n\n## Hand back a deliverable — and the next step\n\nAfter every action, don't just report that you finished. Hand back **four things**: what you produced, a\nclickable console link to view it, one line on how to use it, and **the natural next step — proposed, and\noffered to do.** They will ask \"where do I see it?\", \"how do I use it?\" and \"what now?\" anyway; answer all\nthree up front. URL-encode names that contain spaces.\n\n| After you… | Hand back |\n|---|---|\n| Create or import into a **knowledge base** | Its page — which includes the **knowledge starmap** (a 3D view of what was ingested): `https://console.agent4.io/#/knowledge-bases/<name>` |\n| Create an **agent** | Its page to review/test: `https://console.agent4.io/#/agents/<name>` — and note that to let end users reach it, they create a **share link** in the console |\n| Create a **skill** | `https://console.agent4.io/#/skills/<name>` |\n| Publish a **Storyline** | `https://console.agent4.io/#/storylines/<id>` (the id from `create_storyline`) |\n| Register an **MCP server** | `https://console.agent4.io/#/mcp/<id>` |\n\nFor example, after importing documents into a knowledge base, reply with how many chunks landed, the link\nabove so they can open its **knowledge starmap** and see exactly what was ingested, and confirm whether\nit's now attached to an agent (or how to attach it). A bare \"done\" just makes them ask.\n\n**Always end on the next step, and offer to do it** — a setup is a chain, not a single action:\n\n- Built a **knowledge base**? → offer to attach it to an agent (ask which).\n- Created an **agent**? → offer to attach a knowledge base, add a skill, or create a **share link** so\n  end users can reach it (an empty space = off).\n- Authored a **skill**? → offer to attach it to the agent that needs it.\n- Published a **Storyline**? → offer to set it as the agent's default, or wire its enrollment trigger.\n- Registered an **MCP server**? → offer to grant an agent its tools.\n\n\"Here's what I made, here's the link, here's how to use it, and here's what I'd do next — want me to?\"\nkeeps the build moving; a bare \"done\" leaves the tenant guessing.\n\n---\nname: agent4-io-recipes\ndescription: Connect to agent4.io over MCP, install the skills, and run end-to-end recipes.\nversion: 8\n---\n\nConnect to agent4.io over MCP, install the skills, and run end-to-end recipes.\n\n## Get started with the agent4.io agent skills\n\nagent4.io ships a remote **MCP server** (the tools) plus a set of **skills** (how to use the tools).\nPoint your agent at the MCP endpoint, install the skills once, and every recipe below becomes an\nexecutable, step-by-step procedure.\n\n## What this skill does with your data — tell the user\n\nThis skill connects to **agent4.io's remote API** (`api.agent4.io`). Be explicit with the user before you\nsend their material:\n\n- **It transmits to agent4.io.** The agent configuration, knowledge-base content and queries you send go\n  to agent4.io's servers to build and run agents there — that is the platform's purpose, not a side effect.\n- **It uses exactly one credential — your agent4.io API key** (`tk_…`), which the user provides. It does\n  **not** read other environment variables, and it does **not** read or enumerate your local files:\n  `add_knowledge_file` deliberately cannot access your machine's paths; you only ever send content you\n  explicitly pass to a knowledge-base tool.\n- **Nothing runs with elevated privileges.** The installer only writes skill files into your agent's own\n  skills folder — no `sudo`, and it fetches only from agent4.io.\n- **On a chat channel**, a bot token the user provides is used **solely** to call that channel's own API at\n  the user's instruction (e.g. Telegram `setMyCommands`) — never sent to agent4.io.\n\nIf any of this isn't acceptable to the user, stop — don't send their data.\n\n## Connect — one step\n\nYou already have this skill (installed via ClawHub). The only thing left is the remote MCP server:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOr add it to any MCP client's config — URL `https://api.agent4.io/v1/mcp`, header\n`X-API-Key`, transport streamable-http. No installer, no shell script: one HTTP endpoint.\nGet a key from **console → Settings → Security** (the plaintext is shown once, at creation).\n\n## Verify\n\n```text\ntenant_info()   # → confirms you are connected to the right tenant\n```\n\n## Keep it current\n\n```bash\nclawhub update agent4-io\n```\n\nThe MCP tools are remote, so they are always current; only this skill document is versioned.\n\n## Slash commands (for your coding CLI — not for the deployed bot)\n\nThese are **builder shortcuts for coding CLIs that read a `commands/` folder** — Claude Code\n(`~/.claude/commands/`) and Cursor (`.cursor/commands/`). The installer drops them so *you*, building on\nagent4.io, can type them in your CLI:\n\n- `/agent4-agent <what>` — create a grounded agent\n- `/agent4-kb <name + source>` — build a knowledge base, import, verify, attach\n- `/agent4-skill <when + what>` — author a load-on-demand skill\n- `/agent4-storyline <process>` — compile a process into a Storyline and publish\n- `/agent4-docs <question>` — look up an agent4.io concept or how-to\n\nEach carries no logic of its own — it points at the matching `agent4-io-*` skill and passes what you type.\n\n**They are not the deployed bot's commands.** They will not — and should not — show up in a Telegram or\nWhatsApp bot's slash menu. Those menus are shown to your **end users** and are set separately by the bot's\noperator (e.g. Telegram's `setMyCommands`); they list end-user actions, never builder operations like\n\"create an agent\". Different audience, different mechanism — if you're deploying on a chat channel, ignore\nthese and configure that channel's own command menu.\n\n**On OpenClaw / Codex** (no `commands/` folder) there are no separate command files: the agent uses the\n`agent4-io` skill directly — the model invokes it, or you call the single `/agent4-io <task>` command — and\nthe five shortcuts above fold into that.\n\n### Running on a Telegram / WhatsApp bot? Register the menu yourself\n\nIf **you (the agent) are deployed on a chat channel**, the channel's command menu is *not* synced from\nskills — you set it through the channel's own API. Do this **only when this bot is your operator / builder\nassistant** (the person using it builds on agent4.io); **skip it for a customer-facing bot**, whose menu is\nfor end users, not builder operations.\n\nTelegram command names may contain only `a-z 0-9 _` — **no hyphens** — so rename `agent4-agent` →\n`agent4_agent`. When you have the bot token, register them once with `setMyCommands`:\n\n```bash\ncurl -s \"https://api.telegram.org/bot$BOT_TOKEN/setMyCommands\" -H \"Content-Type: application/json\" -d '{\n  \"commands\":[\n    {\"command\":\"agent4_agent\",\"description\":\"Create a grounded agent\"},\n    {\"command\":\"agent4_kb\",\"description\":\"Build a knowledge base\"},\n    {\"command\":\"agent4_skill\",\"description\":\"Author a load-on-demand skill\"},\n    {\"command\":\"agent4_storyline\",\"description\":\"Compile a process into a Storyline\"},\n    {\"command\":\"agent4_docs\",\"description\":\"Look up an agent4.io doc\"}\n  ]}'\n```\n\nWhen a user taps one, the channel sends that text to you as an ordinary message — handle it with this\nskill (`agent4_agent` = the `/agent4-agent` shortcut, and so on). No bot token / shell access? Set the\nsame list by hand in **@BotFather → `/setcommands`**. WhatsApp has no slash menu; expose the same actions\nas an interactive list/quick-reply instead.\n\n**Silence your own link previews.** You cite `agent4.io` links constantly, so Telegram will attach a\npreview card to nearly every reply — noisy fast. Send **your** messages with previews off:\n`link_preview_options: {\"is_disabled\": true}` on `sendMessage` (legacy API: `disable_web_page_preview: true`).\nThis affects only your messages; a link the **user** sends still previews as normal.\n\n## When there's no MCP tool for it — the REST API is the full surface\n\nThe MCP tools cover the common build-and-run operations, but they are not the whole platform. For\nanything they don't expose — a field, an endpoint, a bulk job — the **complete tenant REST API** covers\neverything a tenant can do. Call it directly with the same key: `X-API-Key: tk_...`.\n\nTwo ways to reach it, cheapest first:\n\n- **`search_agent4_docs(\"… rest api …\")`** — the docs search now indexes the REST API per endpoint. A\n  REST/HTTP-worded query returns the **exact endpoint** with its parameters and response, without loading\n  the whole reference. Use this first.\n- **[https://agent4.io/api.md](https://agent4.io/api.md)** — the full machine-readable reference (every\n  endpoint, params, request/response, examples). Read the whole file only when you need the broad picture.\n\nMCP is the fast path; the REST API is the fallback.\n\n## What to do next\n\n- [Create a grounded support agent](/cookbook/create-support-agent)\n- [Build and populate a knowledge base](/cookbook/build-a-knowledge-base)\n- [Author a load-on-demand skill](/cookbook/create-a-skill)\n- [Configure a page-aware chat opener](/cookbook/configure-page-playbooks)\n- [Compile a flow into a Storyline](/cookbook/build-a-storyline)\n\nEvery recipe names the exact MCP tool and arguments — never \"open this page and click\".\n\n---\nname: agent4-io-admin\ndescription: Create and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\nversion: 9\n---\n\nCreate and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\n\n## Create a grounded support agent\n\n> **Before you build — discover, don't run this cold.** Interview the tenant about their job, their\n> material, the procedures it runs and the systems it must touch; plan the whole setup (agent + knowledge\n> base + skills + MCP as needed) and play it back before creating anything. The steps below are what you\n> run *after* that — see [Discover before you build](/cookbook/design-principles).\n\n> **Principle — one agent, one job.** Give this agent a single, clearly bounded task. If the business\n> has three jobs (support *and* sales *and* scheduling), build three agents — a \"does everything\" agent\n> answers each thing worse. More in [Design principles](/cookbook/design-principles).\n\nAn agent is **soul + task + tools + skills + knowledge_bases**. `soul` is identity and voice; `task` is\nthe job and its boundaries — both go into the fixed prefix of the system prompt.\n\n## 1. See what you can attach\n\n```text\nlist_tools()             # tools available to this tenant (including connected MCP tools)\nlist_knowledge_bases()   # knowledge bases you can mount\n```\n\n## 2. Create it\n\n> **On by default, so you don't have to ask for them.** A new agent can already reply with a\n> tappable single/multi-choice **form** (`ask_forms`) when it needs two or three facts before it can\n> answer, and can already draw a **chart** (`compute_chart` is in the default tool list). Both were\n> off by default until 2026-08-03, and the create call had no parameter for the first — so agents\n> built before then have neither, and `update_agent(ask_forms=True, add_tools=[\"compute_chart\"])`\n> is how you bring one up to date.\n>\n> Passing `tools=[...]` **replaces** the default list rather than adding to it, so include\n> `compute_chart` yourself whenever you pass tools at all.\n\n> **Names are lower-case.** The name is not only what you see — eleven tables reference an agent by\n> it (shares, sessions, storylines, channel bindings, usage), so `Pip` and `pip` are two different\n> agents to all of them and the same one to you. New names are lower-cased on save; write them that\n> way and there is nothing to reconcile. It is also not the public URL — that is `alias`.\n\n```text\ncreate_agent(\n  name=\"support\",                  # lower-case; the platform lower-cases it anyway\n  alias=\"support\",                 # public human-readable URL slug — set one (url-safe, lowercase)\n  soul=\"You are the support assistant for Acme Loans. Professional and warm.\",\n  task=\"Answer questions about mortgage products and the application process. \"\n       \"Never promise a disbursement date; never give legal or tax advice; \"\n       \"for any specific quote, call a tool — do not answer from memory.\",\n  tools=[\"web_search\"],\n  knowledge_bases=[\"company-policy\"],   # use the KB's returned slug name (see below)\n  published=True,\n)\n```\n\n- **`alias`** is the agent's public human-readable address segment (`{public_base}/t/<tenant>/<alias>`) —\n  set it so you can hand people a memorable link. It's normalised to a url-safe slug; a clash comes back\n  in `alias_result`.\n- **System tools are on by default.** New agents automatically get `current_time`, `ip_geo` and `weather`\n  (the built-in `system` MCP) — you don't list them; `tools` is for the *extra* ones.\n- **Names in URLs are slugged.** A **knowledge base** name becomes a url-safe slug on creation\n  (`\"Company Policy\"` → `company-policy`); attach it by the **returned** name, not what you typed.\n\n## 3. Confirm what landed\n\n```text\nget_agent(name=\"Support\")   # writing it doesn't mean it looks the way you intended\n```\n\n<Callout>\n`published=True` means **visible**, not **reachable**. End users can't get to the agent until you create\na **share** (step 4). Don't stop at \"published\".\n</Callout>\n\nBoundaries belong in `task` — negative constraints (\"never promise…\") stop drift better than positive\ndescription. Don't put safety rules in `soul`; the platform appends global moderation automatically. To\nchange one field later, use `update_agent(name, field=…)` — it merges, so it won't blank the rest.\n\n## 4. Publish it — ask how, then make it reachable\n\nBefore you say \"done\", ask the tenant **how their customers should reach it**, then wire that channel with\n`create_share` (it returns a real, openable link — hand that back, not \"it's published\"):\n\n```text\ncreate_share(agent_name=\"Support\", label=\"Website widget\")\n# → { token, chat_url, qr_url, embed_snippet, pretty_url, … }\n```\n\nIf the response carries `pretty_url` (`…/t/<tenant-alias>/<agent-alias>`), **that's the link for\nhumans** — readable and stable across token rotation. If it's null, set the missing alias\n(`create_agent(alias=…)` / `PUT /agents/{name}/alias`; tenant alias in console → Settings) rather\nthan shipping the token link. `chat_url` remains right for embeds and QR codes.\n\n- **No website — just a link or a QR** (a freelancer, a shop, a flyer, a business card): hand back\n  `chat_url` (a full-screen **hosted chat page** — no site needed) and `qr_url` (a QR they can print).\n  It's anonymous: no login, and each visitor is remembered by their browser, so regulars are recognised.\n- **Their own website**: give them `embed_snippet` (one line before `</body>` for a floating widget), or\n  `chat_url` to link / iframe.\n- **Telegram / WhatsApp**: set up per-channel in the console (Agent → **Integration** →\n  Telegram / WhatsApp); point them there.\n\n> **Report back.** Hand back the **actual link they can open and test right now** (`chat_url`, and\n> `qr_url` if they have no site), plus the agent's console page to review it —\n> `https://console.agent4.io/#/agents/Support`. Not \"it's published\" — the link.\n\n## Author a load-on-demand skill\n\n> **Principle — keep the skill count small.** Attach only what this agent's job needs (≈5 or fewer).\n> The model picks skills from their one-line `description`; the more you attach, the more often it loads\n> the wrong one or none. If two skills overlap in *when*, merge them. More in\n> [Design principles](/cookbook/design-principles).\n\nA **skill** is a capability pack loaded on demand: the system prompt carries only its `description`\n(\"when to use\"); the model pulls the full `instructions` only when it judges the skill relevant. So the\n`description` must be short and say *when*, or the model won't know to reach for it.\n\n```text\nlist_skills()                                # what already exists\n\ncreate_skill(\n  name=\"refund-policy\",\n  description=\"Use when the user asks about refunds, cancellations or chargebacks.\",\n  instructions=\"The full procedure: eligibility windows, how to word the outcome, when to escalate …\",\n)\n\nupdate_agent(name=\"Support\", add_skills=[\"refund-policy\"])   # attach it (incremental — keeps existing skills)\n```\n\nKeep procedures in `instructions` (fetched on load), not in the agent's `soul` (which is in the prompt\nevery turn). A one-line `description` that says *when* is what makes the skill discoverable — a vague one\nmeans the model never pulls it.\n\n## Tools can ride on the skill\n\nA skill can carry its own tool bindings — `create_skill(tools=[…])` or\n`update_skill(name, add_tools=[…])`. Tools bound to an attached skill **join the agent's toolset\nautomatically at chat time**, so a skill can ship as a self-contained capability: procedure + the\ntools it needs, attached in one move.\n\nTwo things to know before you rely on it:\n\n- **They don't appear in the agent's own `tools` list.** `get_agent` shows only the agent's directly\n  attached tools; the console's Tools tab shows skill-bound ones as a separate read-only \"via skills\"\n  line. When verifying \"is tool X enabled\", check both places — or just call the tool in a test chat.\n- Prefer binding a tool to the **skill** when it only makes sense inside that procedure (a refund\n  lookup inside the refund skill); bind to the **agent** when it's generally useful across turns\n  (`save_contact`, `web_search`).\n\n## Binding a tool is availability, not policy — the instructions must say when to call it\n\nChecking a tool on a skill (or agent) only makes it *callable*. The model sees the tool's schema and\nits one-line description every turn, so it may use it opportunistically — a visitor volunteers an\nemail and `save_contact` fires. But **any behaviour that must happen reliably needs to be written\ndown**, and each part of it has one right home:\n\n| What you're specifying | Where it goes |\n|---|---|\n| *When to load this skill* | the skill's `description` (in the prompt every turn) |\n| *The procedure: at which step to call which tool, with what arguments* | the skill's `instructions` — **name the tool explicitly** (\"after the caller confirms interest, call `save_contact` with email and phone; then call `schedule_followup` for 1 business day later\") |\n| *A behaviour that must drive the whole conversation* (always capture leads, never quote rates) | the agent's `soul` / `task` |\n\nThe failure mode when instructions don't name the tool: everything *looks* configured — the tool is\nchecked, the skill loads — and the agent still never calls it, or calls it with guessed arguments.\nNothing errors. Write the procedure as if briefing a new employee: the step, the tool name, the\narguments, and what \"done\" looks like.\n\n## Mandatory tool calls: the trigger must live in `description`, not only in `instructions`\n\n`instructions` are visible to the model **only after it calls `load_skill`** — and models frequently\nanswer directly without loading the skill. So a rule like *\"when the user provides a phone number,\nyou must call `save_contact`\"* written only in `instructions` is invisible exactly when it matters:\nthe tool is silently never called, and the model may even claim it saved the contact. This happened\nin production — an agent answered several contact-bearing messages in a row, \"confirmed\" the details\nwere recorded, and no record existed.\n\nThe fix is one sentence in the `description` (which *is* in the system prompt every turn):\n\n```text\nupdate_skill(\n  name=\"consultative-sales\",\n  description=\"Consultative sales: understand the case, recommend products, arrange expert callbacks. \"\n              \"When the user provides a phone number or email, call save_contact BEFORE answering anything else.\",\n)\n```\n\nKeep the detailed procedure in `instructions`; put the *trigger* of any mandatory call in\n`description`.\n\nTwo related rules:\n\n- **Never script return values the tool doesn't produce — relay real ones verbatim.** Built-in\n  `save_contact` and `schedule_followup` return a **real reference code** on success (format\n  `AB2C-D3EF`, stored server-side and visible to the tenant on the end user's detail page).\n  Instruct the model to relay *the code from the tool result* verbatim — never to invent one or\n  reformat it as \"#12345\". For any other tool, verify it actually returns an ID before scripting\n  one: a promised-but-absent ID will be fabricated.\n- **`create_skill` / `update_skill` / `get_skill` now return a `warnings` list** that flags exactly\n  these two patterns (`trigger_hidden_in_instructions`, `promised_tool_return_id`). Warnings are\n  advisory — the save succeeds — but treat them like a linter: fix, don't ignore.\n\n## Worked examples in `instructions` measurably raise tool-call compliance\n\nWe benchmarked this on production backends (6 lead-capture messages of increasing difficulty —\nnumbers buried in long questions, digit groups with spaces, corrections — sampled per condition).\nWith the skill loaded, a plain \"you must call X\" mandate hit **3–4/6**; adding a short block of\nworked examples brought both tested models to **6/6**. Moving the mandate section around did\nnothing — *position doesn't matter, examples do*.\n\nAn effective example block has four kinds of entries, each one line:\n\n```text\n### Worked examples (follow exactly)\n\n1. User: \"I'd like to know about X, my phone is 13800138000\"\n   → call save_contact(phone=\"13800138000\") first, then answer about X.\n2. User: \"email me the offer: li@example.com\"\n   → call save_contact(email=\"li@example.com\").\n3. COUNTER-EXAMPLE (forbidden): user gives a phone number and you reply\n   \"I've noted it down\" WITHOUT calling the tool — claiming success without\n   the call is the worst failure.\n4. User: \"sorry, wrong number — it's 13633334444\"\n   → call save_contact again with the corrected value.\n5. Numbers may contain spaces (\"138 0013 9000\") — still a phone number;\n   strip the spaces and call save_contact(phone=\"13800139000\").\n```\n\nThe counter-example (3) and the format edge case (5) close most of the remaining misses — models\nfail on *recognition* (\"is this a phone number?\") and on *honesty under pressure* (answering a rich\ndomain question first and claiming the save happened) more than on willingness. Keep it to ~5\nentries; use the exact call syntax with realistic arguments.\n\n## Test the trigger before shipping — don't count corpses in production\n\nWhether a skill *actually* fires is measurable, so measure it. `test_skill_trigger` dry-runs your\nmessages against the **production** prompt assembly, tool schemas and model routing, and reports\nwhat the model decided — tools are never executed, nothing is stored, tokens count toward your\nquota (caps: 5 messages × 5 samples).\n\n```text\ntest_skill_trigger(\n  agent=\"advisor\",\n  messages=[\n    \"My phone is 555 0123, call me back\",          # easy\n    \"long question about the product … oh and my number is 555 0123\",  # buried\n    \"555 0123 — that's me\",                        # implicit\n    \"sorry, wrong number, it's 555 9999\",          # correction\n  ],\n  expect_tool=\"save_contact\",\n  samples=3,\n  loaded=true,        # simulate post-load_skill → tests instructions quality\n)                     # loaded=false (default) → first turn, tests the description trigger\n```\n\nRead the result like this:\n\n- **`hit_rate`** below ~90% on realistic messages → strengthen the trigger (description) or add\n  worked examples (instructions), then re-test.\n- **`claimed_without_call` > 0** is the worst failure — the model told the user \"noted!\" without\n  calling the tool. Add the counter-example from the block above.\n- Test **both modes**: `loaded=false` proves the description alone triggers on turn one;\n  `loaded=true` proves the loaded instructions don't dilute it (long instructions measurably do —\n  that's what the worked examples compensate for).\n\nThe result also carries an **`advice` list**: when samples miss or lie, it tells you exactly which\nfix to apply (trigger into the description, add the worked-example block, add a counter-example or\nformat edge case) with the benchmark numbers behind each recommendation — apply it and re-test.\n\nThe full loop: `create_skill` → fix any `warnings` (static lint) → `test_skill_trigger` (dynamic\nreality check) → apply its `advice` → re-test until the hit rate holds.\n\n## Configure a page playbook (page-aware chat opener)\n\n> **Principle — the opener is the only sentence guaranteed to be read.** A generic \"How can I help?\"\n> converts a page visit into nothing. A playbook lets the *same* agent open differently on each page,\n> already knowing where the visitor is. More in [Page playbooks](/concepts/playbooks).\n\nA **page playbook** is a small briefing attached to a URL pattern. It has three parts:\n\n- **`context`** — private background for the agent, **not shown** to the visitor (who lands on this\n  page, what they're deciding, what they usually worry about). Write background, not facts: prices,\n  quotas and policy belong in a [knowledge base](/cookbook/build-a-knowledge-base), which outranks it.\n- **`greeting`** — the opening line the visitor actually sees.\n- **`questions`** — up to four suggested questions, so a visitor who hasn't formed one can just pick.\n\nThe right playbook is chosen from the URL: resolution order is **explicit key → `url_pattern` →\ndefault**. A single default catches every unmatched page, so nothing ever falls back to a blank box.\n\n## 1. See what's already there\n\n```text\nlist_page_contexts()   # existing playbooks: match rules, greeting mode, position, which is default\n```\n\n## 2. Create or replace one\n\n```text\nupsert_page_context(\n  key=\"pricing\",\n  label=\"Pricing page\",\n  url_pattern=\"*/pricing\",           # glob, path-only; ignores query string and trailing slash\n  context=\"Visitors here are comparing plans and worrying about overage. \"\n          \"They are usually the person who will sign off on the cost. \"\n          \"Do not quote custom pricing in chat.\",\n  greeting=\"You're looking at our plans — want me to work out where overage would start for your volume?\",\n  questions=[\"What's included in the free plan?\", \"How is overage billed?\", \"Can I change plans later?\"],\n  greeting_mode=\"generated\",         # generate opener + questions in the visitor's language (recommended)\n  is_default=False,\n)\n```\n\n`greeting_mode=\"generated\"` writes the opener and questions per visitor language on the fly — so a\nSpanish visitor gets a Spanish greeting without you authoring five versions. Use `\"static\"` only when\nyou want your exact `greeting`/`questions` verbatim.\n\n## 3. Verify the match — always\n\n```text\nresolve_page_context(url=\"https://acme.com/pricing?ref=x\")   # → which playbook this URL hits, and how\n```\n\nGlobs are easy to get subtly wrong (a missing `*`, one path level too many). A mismatch has **no error\nmessage** — the visitor just silently gets the default playbook. So after writing any `url_pattern`,\nresolve a real URL and confirm `matched_by` is `url_pattern`, not `default`.\n\n## 4. Set a catch-all default\n\n```text\nupsert_page_context(\n  key=\"default\",\n  label=\"Everywhere else\",\n  context=\"General visitor to the site; intent unknown. Ask what brought them in before assuming.\",\n  greeting_mode=\"generated\",\n  is_default=True,                    # at most one default per tenant; setting this unsets any other\n)\n```\n\n<Callout>\n`upsert_page_context` is a **full replace**, not a patch: fields you omit fall back to defaults rather\nthan keeping their old value. To change one field, `list_page_contexts()` first, merge, then upsert.\n</Callout>\n\nOnce playbooks have been live for a while, `page_context_stats()` shows which ones get opened and which\nsuggested questions get clicked — so you tune copy from data, not guesses.\n\n> **Report back.** Give the user the console link to review and edit these —\n> `https://console.agent4.io/#/page-contexts` — and confirm each `url_pattern` resolves the way they\n> expect (step 3). The chat widget needs no code change for a static site; single-page apps that have\n> no distinct URL per view can name a playbook by its `key` instead.\n\n## Openings that collect on the spot\n\nIf a playbook's `context` explicitly instructs presenting a form at first contact — e.g.\n`\"PRESENT THE BOOKING FORM IN YOUR GREETING/OPENING: (1) preferred channel (phone / email — single),\n(2) phone or email (text)\"` — the generated opening will include that interactive form, and the\nstarter questions are suppressed (the form is the guidance). Submitting counts as a normal visitor\nmessage, so `save_contact` / `schedule_followup` fire as they would mid-conversation.\n\nUse it for intents where asking first is the whole point — \"book a callback\", \"leave a message\" —\nand keep it to 2–3 fields. For intents where the visitor wants answers first (support,\ntroubleshooting), let the conversation start normally and collect in the first reply instead.\n\n## Records require a contact — by design\n\nEvery record-creating tool (`submit_lead`, `escalate`, `request_booking`, `open_checklist`)\n**rejects the call unless `contact.email` or `contact.phone` is filled in** — a record nobody can\nfollow up on is noise, not a lead. The rejection message tells the agent what to do (ask for a\ncontact first, never invent one), so make your playbooks collect email/phone up front — the\ngreeting-time forms above exist precisely for this.\n\n## File form submissions deterministically\n\nFor intake-style playbooks (bookings, leads, complaints), append a machine marker to the\n`context`: `[[inbox:submit_lead]]`, `[[inbox:escalate]]` or `[[inbox:request_booking]]`. Form\nsubmissions on that playbook are then **filed by the platform before the model replies** — the\nreply's reference is guaranteed to be the filed record's reference, and the contact is saved\nalongside. Keep the prose instructions too: they cover the free-conversation path (details given\nwithout the form), where the model still does the filing.\n\n## Deleting things — point the user to the console\n\nDeletions are deliberately **not** exposed as tools — they are irreversible, and from a conversation you\nusually can't tell whether an agent or knowledge base is still used by something else. So when the user\nasks you to delete something, **don't attempt it** — tell them exactly where to do it in the console at\n[console.agent4.io](https://console.agent4.io), and offer to help with the safe parts (finding the item,\nlisting what depends on it) first.\n\n| To delete… | Where in the console |\n|---|---|\n| An agent | Agents → open the agent → top-right **⋯ / Delete** |\n| A knowledge base (and all its documents) | Knowledge → open the base → **Delete** |\n| One document in a knowledge base | Knowledge → the base → **Documents** tab → the row → **Delete** |\n| A skill | Skills → the skill → **Delete** (agents referencing it stop loading its guidance) |\n| A user, space, or session | Users → the row → **Delete** |\n| Revoke / re-issue an API key | Settings → **Security** → **Revoke** (then create a new one) |\n| Change privacy mode / BYOK | Settings (this shifts compliance responsibility — confirm before changing) |\n\nBefore you send them off, it helps to **surface what would be affected**: e.g. list the agents that\nmount a knowledge base (`list_agents` + `get_agent`) so they can see whether deleting it breaks anything.\nThat check is exactly why deletion is a human action in the console, not a tool call.\n\n## Connect your own MCP server\n\nBring your own tools: register a **remote** MCP server and its tools become available to attach to your\nagents. There is no \"create MCP server\" tool on purpose — registering a server means trusting it to run\nagainst your tenant, so it is a deliberate action in the console (or a REST call), not something an agent\ndoes on its own.\n\n## 1. Register the server\n\nIn the console: **Tools / MCP → Add MCP server** — give it a name, choose the transport\n(**Streamable HTTP** or **SSE**), and the URL plus any auth headers.\n\nOr by REST, with your tenant key (`$BASE` = `https://api.agent4.io/v1`):\n\n```bash\ncurl -X POST $BASE/mcp-servers -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' \\\n  -d '{\"name\":\"my-tools\",\"transport\":\"streamable_http\",\n       \"config\":{\"url\":\"https://tools.example.com/mcp\",\"headers\":{\"Authorization\":\"Bearer …\"}}}'\n```\n\nOnly **remote** transports are accepted. `stdio` (local commands) is rejected — the server runs on the\nplatform, not your machine, so a local command would be both useless to you and a code-execution risk.\n\n## 2. See its tools\n\n```text\nlist_mcp_servers()   # the MCP servers registered on your tenant\nlist_tools()         # every tool you can attach to an agent, including the ones from your servers\n```\n\n## 3. Attach the tools to an agent\n\n```text\nupdate_agent(name=\"Support\", add_tools=[\"…\"])   # incremental; use tool names exactly as list_tools() reports them\n```\n\nThe agent can now call those tools during a conversation. Keep the set focused — a handful of relevant\ntools beats a big pile (see [Design principles](/cookbook/design-principles)).\n\n## Built-in tools (and the system MCP)\n\nYou don't have to build the common tools — the platform ships them. Attach the ones this agent's job\nneeds with `update_agent(name, add_tools=[…])`; keep the set small (see [Design principles](/cookbook/design-principles)).\nAlways confirm the exact names on your tenant with `list_tools()` — that is the source of truth.\n\n> **`tools=[…]` replaces the whole list; `add_tools` / `remove_tools` change one item.** Passing\n> `tools=` with only the tools you're thinking about silently drops every tool you didn't read first —\n> this has happened in production and nobody noticed until a feature stopped working. Reach for\n> `tools=` only when you deliberately mean \"exactly this set\", and read the returned agent to confirm\n> the final list either way. The same pairs exist for skills (`add_skills`/`remove_skills`) and\n> knowledge bases (`add_knowledge_bases`/`remove_knowledge_bases`).\n\n## The built-in tools\n\n| Tool | What it does |\n|---|---|\n| `web_search` | Look something up on the open web. |\n| `save_contact` | Capture the end user's contact details into their profile — the platform's lead-capture / records path. Returns a **real reference code** (`AB2C-D3EF` style) the agent relays to the user; look codes up on the user's detail page (`recent_refs`). |\n| `export_document` | Generate a structured document (a branded summary / report) for the conversation. |\n| `schedule_followup` | Proactively follow up later (\"check back in 2 days\") or book a user-requested callback — the message is sent for you. Returns a **real booking reference** traceable to the scheduled task. For absolute times (\"tomorrow 10am\") instruct the model to pass **`run_at`** (local ISO datetime + optional `tz`) — the server resolves the user's timezone; hand-computed `delay_seconds` is for relative times only, and models get the arithmetic wrong. Window: up to 7 days. |\n| `schedule_reminder` | Set a reminder for the end user; `list_reminders` / `cancel_reminder` manage them. |\n| `compute_chart` | Work out a derived series **in code** — share of total, growth %, running total, a projection at a fixed rate, or a metered bill (base fee + allowance + per-unit overage). Attached automatically to any agent that already has tools, so you rarely add it by hand. See [Charts in answers](/cookbook/charts-in-answers). |\n\n`load_skill` is also built in, but you don't attach it — the model pulls a skill on its own when a\nskill's `description` matches.\n\n```text\nlist_tools()                                              # exact names available on your tenant\nupdate_agent(name=\"Support\", add_tools=[\"save_contact\", \"schedule_followup\"])   # incremental — keeps what's there\n```\n\nCapturing a contact with `save_contact` populates the user's record; combined with `ask_forms` on the\nagent, structured intake lands as records you can review in the console.\n\n**The platform backs these two up automatically.** When the end user's message contains a clear\nsignal — a phone number / email (any language, incl. spelled-out digits and spaced formats), or a\n\"call me tomorrow morning\"-style callback request — and `save_contact` / `schedule_followup` is\nenabled, the platform injects a per-turn hint reminding the model to call the tool before answering.\nBenchmarked on production backends, this lifted lead-capture compliance on the weakest model from\n~85% to 100% with **zero** false saves (the hint carries an \"ignore if wrong\" escape hatch, so an\nover-eager detector cannot cause bad data). You get this for free — no configuration; your skill\nshould still state the trigger in its `description` (see [Author a skill](/cookbook/create-a-skill)),\nthe hint is a safety net, not a replacement.\n\n## The system MCP — already installed\n\nEvery tenant gets a built-in **`system`** MCP server, enabled by default, with keyless tools for\n**local time, weather, and IP geolocation** (city / region / timezone). You don't register it — it's\nthere. Its tools show up in `list_tools()` alongside the built-ins above, ready to attach.\n\nSo \"what's the weather where the user is?\" or \"what's their local time?\" work out of the box — the\nplatform injects the end user's client IP, so location-aware answers need\n\nFile v1.1.10:_meta.json\n\n{\n  \"ownerId\": \"kn715vygmbtxev9229v6dqrc71838kjn\",\n  \"slug\": \"agent4-io\",\n  \"version\": \"1.1.10\",\n  \"publishedAt\": 1786147127212\n}\n\nFile v1.1.10:skill-card.md\n\n## Description:\n\nBuild and run grounded business agents on agent4.io over MCP - agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[hellojixian](https://clawhub.ai/user/hellojixian)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and business operators use this skill to configure, publish, and operate agent4.io agents through a remote MCP server. It guides creation of grounded agents, knowledge bases, load-on-demand skills, page playbooks, Storylines, shares, usage checks, and related platform administration.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill sends selected agent configuration, knowledge-base material, and queries to the hosted agent4.io service.\n\nMitigation: Confirm the user intends to use agent4.io and only transmit documents, prompts, and business data they are allowed to process there.\n\nRisk: The required AGENT4_API_KEY grants tenant access and is sensitive.\n\nMitigation: Store the key as a secret, avoid exposing it in logs or shared transcripts, and verify the connected tenant with tenant_info before making changes.\n\nRisk: Broad REST API actions, privacy or BYOK changes, channel bot-token actions, public shares, and follow-ups can affect deployed agents or external users.\n\nMitigation: Use explicit user confirmation before these actions and return concrete console links or generated URLs so the user can review the result.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/hellojixian/skills/agent4-io)\n- [Publisher profile](https://clawhub.ai/user/hellojixian)\n- [agent4.io cookbook](https://agent4.io/cookbook)\n- [agent4.io API reference](https://agent4.io/api.md)\n- [agent4.io MCP endpoint](https://api.agent4.io/v1/mcp)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Markdown, Shell commands, Configuration, API calls]\n\n**Output Format:** [Markdown with inline shell commands, MCP tool-call recipes, configuration snippets, and concise operational guidance.]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires AGENT4_API_KEY and a configured agent4.io remote MCP connection; outputs may direct the agent to send user-selected configuration, prompts, and knowledge-base content to agent4.io.]\n\n## Skill Version(s):\n\n1.1.10 (source: server release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.9: 3 files, 40946 bytes\n\nFiles: skill-card.md (2614b), SKILL.md (97617b), _meta.json (128b)\n\nFile v1.1.9:SKILL.md\n\n---\nname: agent4-io\ndescription: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\nhomepage: https://agent4.io/cookbook\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🤖\",\n        \"requires\": { \"env\": [\"AGENT4_API_KEY\"] },\n        \"primaryEnv\": \"AGENT4_API_KEY\",\n        \"envVars\":\n          [\n            {\n              \"name\": \"AGENT4_API_KEY\",\n              \"required\": true,\n              \"description\": \"Your agent4.io tenant API key (tk_…). Get it from the agent4.io dashboard → Tenant & API, or console.agent4.io → Settings → Security — the plaintext is shown once, at creation. No account yet? Sign up free at https://agent4.io (5M tokens/month, no card).\"\n            }\n          ]\n      }\n  }\n---\n\n# agent4.io skills — build agents over MCP\n\nThis skill drives the **agent4.io** platform through its remote MCP server: create grounded agents,\nknowledge bases, load-on-demand skills, stateful Storylines and page playbooks — all from your agent.\n\n## Connect the MCP server (once)\n\nThe tools live on a remote MCP endpoint. Add it to your agent, authenticating with your tenant API key\n(set `AGENT4_API_KEY`; get a key from **console → Settings → Security**, shown once at creation):\n\n- **URL:** `https://api.agent4.io/v1/mcp`\n- **Header:** `X-API-Key: $AGENT4_API_KEY`\n- **Transport:** streamable-http\n\nFor Claude Code / any MCP client that takes a shell command:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOnce connected, call `tenant_info()` to confirm you are on the right tenant, then follow the recipes\nbelow. This skill is a mirror of the always-current Cookbook at https://agent4.io/cookbook.\n\n## Use it as a slash command\n\nThis skill is invocable directly — `/agent4-io <what you want>` — and routes to the right recipe below:\n\n- `/agent4-io create a support agent grounded in these docs` → build a grounded agent\n- `/agent4-io build a knowledge base from this site + these PDFs` → create & populate a KB\n- `/agent4-io author a load-on-demand skill for booking` → write a Skill\n- `/agent4-io compile this intake flow into a Storyline` → design & publish a Storyline\n- `/agent4-io set a page-aware opener for /pricing` → configure a page playbook\n- `/agent4-io how much quota have I used?` → usage & quota\n- `/agent4-io who are my heaviest users this week?` → look up users and their sessions\n- `/agent4-io what is a Storyline?` → look up an agent4.io concept\n\n(On Claude Code and Cursor, the installer also lays down finer-grained shortcuts —\n`/agent4-agent`, `/agent4-kb`, `/agent4-skill`, `/agent4-storyline`, `/agent4-docs` — but they all just\nhand off to this same skill; here a single `/agent4-io` covers them.)\n\n---\n\n# agent4.io — build agents over MCP\n\nThis guide is assembled from the agent4.io Cookbook (https://agent4.io/cookbook). Modules:\n\n- **agent4-io-principles** (v5) — Read-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\n- **agent4-io-recipes** (v8) — Connect to agent4.io over MCP, install the skills, and run end-to-end recipes.\n- **agent4-io-admin** (v9) — Create and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\n- **agent4-io-knowledge** (v6) — Build, populate, verify and query grounded knowledge bases.\n- **agent4-io-storyline** (v8) — Compile multi-step processes into stateful, guided Storyline graphs.\n\n---\nname: agent4-io-principles\ndescription: Read-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\nversion: 5\n---\n\nRead-first design principles: one job per agent, few skills, grounded, verified — how to configure agents that work.\n\n## Design principles — read this first\n\nMost \"the agent gives bad answers\" problems are configuration, not the model. Follow these before you\nbuild, and the results hold up. Ignore them and it is easy to assemble something that looks plausible and\nbehaves badly — then mistake that for a platform limit.\n\n## Discover before you build — interview, don't just execute\n\n\"Create a support agent\" is the **start of the conversation, not the spec.** Never answer it with a single\n`create_agent` and hand back a blank shell. Work like the setup wizard: **interview first, plan the whole\nsetup, play it back, then build.** Ask in plain language — a few questions at a time, about their real\nsituation, not tool parameters — until you can picture the finished thing:\n\n- **The job & who it serves.** What is this agent for, who talks to it, what does a *good* answer look\n  like? One job per agent — if they describe three, that's three agents.\n- **What it must answer from → a knowledge base.** Do they have documents, a site, policies, a price\n  list? If facts have to be right, those become a [knowledge base](/cookbook/build-a-knowledge-base) you\n  build and attach — not prompt text. If they have nothing yet, tell them what to gather.\n- **Procedures it runs → skills.** Any \"when X, do these steps\" behaviours (book, screen, quote)? Each\n  becomes a load-on-demand [skill](/cookbook/create-a-skill) with a sharp \"use this when…\".\n- **Things it must *do* in other systems → MCP.** Check a calendar, look up an order, open a ticket?\n  Those are [MCP tools](/cookbook/connect-your-mcp-server) to bind — ask which system and whether they can\n  connect it.\n- **A guided, stateful flow → a Storyline.** Is it a process with memory (intake → qualify → follow-up)\n  rather than one-shot Q&A? That's a [Storyline](/cookbook/build-a-storyline), not just an agent.\n- **How it opens and its hard limits.** The first line visitors see (→ a [page playbook](/cookbook/configure-page-playbooks))\n  and the boundaries that go in `task` (\"never quote a price\", \"never give legal advice\").\n\nThen **play the plan back before touching a tool** — \"so I'll build: agent *Support*, a knowledge base\nfrom your policy PDFs, a *booking* skill, and bind your calendar over MCP — right?\" — and build only once\nthey confirm. Skipping this is exactly how you end up with an empty agent nobody wanted. A vague request\nis a cue to ask, never a cue to guess.\n\n## One agent, one job\n\nGive each agent a **single, clearly bounded task**. A \"does everything\" agent — support *and* sales\n*and* scheduling — has a diffuse `task`, competes with itself for attention, and answers each thing\nworse. If you have three jobs, build three agents.\n\n## Keep the skill count small\n\nAttach **only the skills this agent's job needs — roughly five or fewer.** Skills are chosen by the model\nfrom their one-line `description`; the more you attach, the harder that choice, and the more often it\nloads the wrong one or none. A focused set with sharp \"use this when…\" descriptions beats a big pile.\n\n- Each skill's `description` says **when** to reach for it, in one line.\n- Procedures live in `instructions` (loaded on demand), never in `soul` (paid for every turn).\n- If two skills overlap in *when*, merge them — overlapping triggers make the choice a coin-flip.\n\n## Ground facts; put boundaries in the task\n\n- Facts (rates, policy, catalogue) belong in a **knowledge base**, which is retrieved every turn — not\n  in the prompt, where they go stale and un-cited. See [Build a knowledge base](/cookbook/build-a-knowledge-base).\n- Constraints belong in **`task`**, as negatives: \"never promise a date\", \"for a quote, call a tool\".\n  Negative boundaries stop drift better than positive description. Don't put safety rules in `soul` —\n  the platform appends global moderation for you.\n- Turn on `grounding_required` when answers must come from the material, not the model's priors.\n\n## Never hand determinism to probability\n\nThe single most useful design rule on this platform: **if something can be computed, generated, or\nverified by the system, never leave it to the model.** A model asked to produce a deterministic\nartifact doesn't fail loudly — it produces a plausible one. The user gets a ticket number that\ndoesn't exist, a callback booked seven hours off, a colour palette that fails contrast. Nothing\nerrors; it's just quietly wrong.\n\nEvery one of these started as a real production failure and became a platform feature:\n\n| Deterministic thing | Wrong way (probability) | Right way (system) |\n|---|---|---|\n| Reference / ticket numbers | Instructions say \"tell the user the ticket number\" → model invents one | `save_contact` / `schedule_followup` **return a real stored code** — instruct the model to relay it verbatim |\n| Absolute times | Model hand-computes `delay_seconds` for \"tomorrow 10am\" → off by hours | Pass **`run_at`** (local ISO time); the server resolves the timezone |\n| Text colours on a brand colour | Model picks \"matching\" colours → unreadable in one theme | Send `theme_color` only; WCAG-contrast foregrounds are **derived server-side** |\n| Routing in a flow | An `ai` exit for \"if the user agreed\" | `rule` / `user_choice` exits; reserve `ai` exits for genuine judgment. Loops get an explicit counter and cap — never \"the LLM will stop eventually\" |\n| \"Did the trigger fire?\" | Assume the prompt works | `test_skill_trigger` measures it; the platform also injects a deterministic hint when a phone/email is detected |\n\nThe corollary for writing skills: your `instructions` should tell the model **which system facility\nto use** (\"pass run_at, relay the returned code\"), not teach it to imitate the facility (\"compute\nthe seconds, format a ticket number\"). If you find yourself scripting the *output* of a\ndeterministic process, look for the tool that produces it — or ask for one.\n\n## Show the agent good patterns only\n\nWhen you hand an agent examples, make them **correct** examples. Don't paste a \"here's the wrong way\"\nsnippet next to the right one — the model may imitate the nearest example rather than read the caveat.\nDescribe what to avoid in words; keep runnable examples exemplary.\n\n## Verify — writing is not working\n\nAfter every change, check it did what you intended. Creating an agent doesn't mean it's configured the\nway you think; adding to a knowledge base doesn't mean the question retrieves.\n\n```text\nget_agent(name=\"Support\")                                   # confirm the config that landed\nsearch_knowledge_base(kb_name=\"Company policy\", query=\"…\")  # confirm the answer is retrievable\n```\n\nAn empty `search` result means that question will be answered as \"not covered\" — find that now, not from\na customer.\n\n## Hand back a deliverable — and the next step\n\nAfter every action, don't just report that you finished. Hand back **four things**: what you produced, a\nclickable console link to view it, one line on how to use it, and **the natural next step — proposed, and\noffered to do.** They will ask \"where do I see it?\", \"how do I use it?\" and \"what now?\" anyway; answer all\nthree up front. URL-encode names that contain spaces.\n\n| After you… | Hand back |\n|---|---|\n| Create or import into a **knowledge base** | Its page — which includes the **knowledge starmap** (a 3D view of what was ingested): `https://console.agent4.io/#/knowledge-bases/<name>` |\n| Create an **agent** | Its page to review/test: `https://console.agent4.io/#/agents/<name>` — and note that to let end users reach it, they create a **share link** in the console |\n| Create a **skill** | `https://console.agent4.io/#/skills/<name>` |\n| Publish a **Storyline** | `https://console.agent4.io/#/storylines/<id>` (the id from `create_storyline`) |\n| Register an **MCP server** | `https://console.agent4.io/#/mcp/<id>` |\n\nFor example, after importing documents into a knowledge base, reply with how many chunks landed, the link\nabove so they can open its **knowledge starmap** and see exactly what was ingested, and confirm whether\nit's now attached to an agent (or how to attach it). A bare \"done\" just makes them ask.\n\n**Always end on the next step, and offer to do it** — a setup is a chain, not a single action:\n\n- Built a **knowledge base**? → offer to attach it to an agent (ask which).\n- Created an **agent**? → offer to attach a knowledge base, add a skill, or create a **share link** so\n  end users can reach it (an empty space = off).\n- Authored a **skill**? → offer to attach it to the agent that needs it.\n- Published a **Storyline**? → offer to set it as the agent's default, or wire its enrollment trigger.\n- Registered an **MCP server**? → offer to grant an agent its tools.\n\n\"Here's what I made, here's the link, here's how to use it, and here's what I'd do next — want me to?\"\nkeeps the build moving; a bare \"done\" leaves the tenant guessing.\n\n---\nname: agent4-io-recipes\ndescription: Connect to agent4.io over MCP, install the skills, and run end-to-end recipes.\nversion: 8\n---\n\nConnect to agent4.io over MCP, install the skills, and run end-to-end recipes.\n\n## Get started with the agent4.io agent skills\n\nagent4.io ships a remote **MCP server** (the tools) plus a set of **skills** (how to use the tools).\nPoint your agent at the MCP endpoint, install the skills once, and every recipe below becomes an\nexecutable, step-by-step procedure.\n\n## What this skill does with your data — tell the user\n\nThis skill connects to **agent4.io's remote API** (`api.agent4.io`). Be explicit with the user before you\nsend their material:\n\n- **It transmits to agent4.io.** The agent configuration, knowledge-base content and queries you send go\n  to agent4.io's servers to build and run agents there — that is the platform's purpose, not a side effect.\n- **It uses exactly one credential — your agent4.io API key** (`tk_…`), which the user provides. It does\n  **not** read other environment variables, and it does **not** read or enumerate your local files:\n  `add_knowledge_file` deliberately cannot access your machine's paths; you only ever send content you\n  explicitly pass to a knowledge-base tool.\n- **Nothing runs with elevated privileges.** The installer only writes skill files into your agent's own\n  skills folder — no `sudo`, and it fetches only from agent4.io.\n- **On a chat channel**, a bot token the user provides is used **solely** to call that channel's own API at\n  the user's instruction (e.g. Telegram `setMyCommands`) — never sent to agent4.io.\n\nIf any of this isn't acceptable to the user, stop — don't send their data.\n\n## Connect — one step\n\nYou already have this skill (installed via ClawHub). The only thing left is the remote MCP server:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOr add it to any MCP client's config — URL `https://api.agent4.io/v1/mcp`, header\n`X-API-Key`, transport streamable-http. No installer, no shell script: one HTTP endpoint.\nGet a key from **console → Settings → Security** (the plaintext is shown once, at creation).\n\n## Verify\n\n```text\ntenant_info()   # → confirms you are connected to the right tenant\n```\n\n## Keep it current\n\n```bash\nclawhub update agent4-io\n```\n\nThe MCP tools are remote, so they are always current; only this skill document is versioned.\n\n## Slash commands (for your coding CLI — not for the deployed bot)\n\nThese are **builder shortcuts for coding CLIs that read a `commands/` folder** — Claude Code\n(`~/.claude/commands/`) and Cursor (`.cursor/commands/`). The installer drops them so *you*, building on\nagent4.io, can type them in your CLI:\n\n- `/agent4-agent <what>` — create a grounded agent\n- `/agent4-kb <name + source>` — build a knowledge base, import, verify, attach\n- `/agent4-skill <when + what>` — author a load-on-demand skill\n- `/agent4-storyline <process>` — compile a process into a Storyline and publish\n- `/agent4-docs <question>` — look up an agent4.io concept or how-to\n\nEach carries no logic of its own — it points at the matching `agent4-io-*` skill and passes what you type.\n\n**They are not the deployed bot's commands.** They will not — and should not — show up in a Telegram or\nWhatsApp bot's slash menu. Those menus are shown to your **end users** and are set separately by the bot's\noperator (e.g. Telegram's `setMyCommands`); they list end-user actions, never builder operations like\n\"create an agent\". Different audience, different mechanism — if you're deploying on a chat channel, ignore\nthese and configure that channel's own command menu.\n\n**On OpenClaw / Codex** (no `commands/` folder) there are no separate command files: the agent uses the\n`agent4-io` skill directly — the model invokes it, or you call the single `/agent4-io <task>` command — and\nthe five shortcuts above fold into that.\n\n### Running on a Telegram / WhatsApp bot? Register the menu yourself\n\nIf **you (the agent) are deployed on a chat channel**, the channel's command menu is *not* synced from\nskills — you set it through the channel's own API. Do this **only when this bot is your operator / builder\nassistant** (the person using it builds on agent4.io); **skip it for a customer-facing bot**, whose menu is\nfor end users, not builder operations.\n\nTelegram command names may contain only `a-z 0-9 _` — **no hyphens** — so rename `agent4-agent` →\n`agent4_agent`. When you have the bot token, register them once with `setMyCommands`:\n\n```bash\ncurl -s \"https://api.telegram.org/bot$BOT_TOKEN/setMyCommands\" -H \"Content-Type: application/json\" -d '{\n  \"commands\":[\n    {\"command\":\"agent4_agent\",\"description\":\"Create a grounded agent\"},\n    {\"command\":\"agent4_kb\",\"description\":\"Build a knowledge base\"},\n    {\"command\":\"agent4_skill\",\"description\":\"Author a load-on-demand skill\"},\n    {\"command\":\"agent4_storyline\",\"description\":\"Compile a process into a Storyline\"},\n    {\"command\":\"agent4_docs\",\"description\":\"Look up an agent4.io doc\"}\n  ]}'\n```\n\nWhen a user taps one, the channel sends that text to you as an ordinary message — handle it with this\nskill (`agent4_agent` = the `/agent4-agent` shortcut, and so on). No bot token / shell access? Set the\nsame list by hand in **@BotFather → `/setcommands`**. WhatsApp has no slash menu; expose the same actions\nas an interactive list/quick-reply instead.\n\n**Silence your own link previews.** You cite `agent4.io` links constantly, so Telegram will attach a\npreview card to nearly every reply — noisy fast. Send **your** messages with previews off:\n`link_preview_options: {\"is_disabled\": true}` on `sendMessage` (legacy API: `disable_web_page_preview: true`).\nThis affects only your messages; a link the **user** sends still previews as normal.\n\n## When there's no MCP tool for it — the REST API is the full surface\n\nThe MCP tools cover the common build-and-run operations, but they are not the whole platform. For\nanything they don't expose — a field, an endpoint, a bulk job — the **complete tenant REST API** covers\neverything a tenant can do. Call it directly with the same key: `X-API-Key: tk_...`.\n\nTwo ways to reach it, cheapest first:\n\n- **`search_agent4_docs(\"… rest api …\")`** — the docs search now indexes the REST API per endpoint. A\n  REST/HTTP-worded query returns the **exact endpoint** with its parameters and response, without loading\n  the whole reference. Use this first.\n- **[https://agent4.io/api.md](https://agent4.io/api.md)** — the full machine-readable reference (every\n  endpoint, params, request/response, examples). Read the whole file only when you need the broad picture.\n\nMCP is the fast path; the REST API is the fallback.\n\n## What to do next\n\n- [Create a grounded support agent](/cookbook/create-support-agent)\n- [Build and populate a knowledge base](/cookbook/build-a-knowledge-base)\n- [Author a load-on-demand skill](/cookbook/create-a-skill)\n- [Configure a page-aware chat opener](/cookbook/configure-page-playbooks)\n- [Compile a flow into a Storyline](/cookbook/build-a-storyline)\n\nEvery recipe names the exact MCP tool and arguments — never \"open this page and click\".\n\n---\nname: agent4-io-admin\ndescription: Create and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\nversion: 9\n---\n\nCreate and configure agents (soul/task/tools/skills), load-on-demand skills, page playbooks (how the chat opens per page); check usage/quota and look up end users and their sessions.\n\n## Create a grounded support agent\n\n> **Before you build — discover, don't run this cold.** Interview the tenant about their job, their\n> material, the procedures it runs and the systems it must touch; plan the whole setup (agent + knowledge\n> base + skills + MCP as needed) and play it back before creating anything. The steps below are what you\n> run *after* that — see [Discover before you build](/cookbook/design-principles).\n\n> **Principle — one agent, one job.** Give this agent a single, clearly bounded task. If the business\n> has three jobs (support *and* sales *and* scheduling), build three agents — a \"does everything\" agent\n> answers each thing worse. More in [Design principles](/cookbook/design-principles).\n\nAn agent is **soul + task + tools + skills + knowledge_bases**. `soul` is identity and voice; `task` is\nthe job and its boundaries — both go into the fixed prefix of the system prompt.\n\n## 1. See what you can attach\n\n```text\nlist_tools()             # tools available to this tenant (including connected MCP tools)\nlist_knowledge_bases()   # knowledge bases you can mount\n```\n\n## 2. Create it\n\n> **On by default, so you don't have to ask for them.** A new agent can already reply with a\n> tappable single/multi-choice **form** (`ask_forms`) when it needs two or three facts before it can\n> answer, and can already draw a **chart** (`compute_chart` is in the default tool list). Both were\n> off by default until 2026-08-03, and the create call had no parameter for the first — so agents\n> built before then have neither, and `update_agent(ask_forms=True, add_tools=[\"compute_chart\"])`\n> is how you bring one up to date.\n>\n> Passing `tools=[...]` **replaces** the default list rather than adding to it, so include\n> `compute_chart` yourself whenever you pass tools at all.\n\n> **Names are lower-case.** The name is not only what you see — eleven tables reference an agent by\n> it (shares, sessions, storylines, channel bindings, usage), so `Pip` and `pip` are two different\n> agents to all of them and the same one to you. New names are lower-cased on save; write them that\n> way and there is nothing to reconcile. It is also not the public URL — that is `alias`.\n\n```text\ncreate_agent(\n  name=\"support\",                  # lower-case; the platform lower-cases it anyway\n  alias=\"support\",                 # public human-readable URL slug — set one (url-safe, lowercase)\n  soul=\"You are the support assistant for Acme Loans. Professional and warm.\",\n  task=\"Answer questions about mortgage products and the application process. \"\n       \"Never promise a disbursement date; never give legal or tax advice; \"\n       \"for any specific quote, call a tool — do not answer from memory.\",\n  tools=[\"web_search\"],\n  knowledge_bases=[\"company-policy\"],   # use the KB's returned slug name (see below)\n  published=True,\n)\n```\n\n- **`alias`** is the agent's public human-readable address segment (`{public_base}/t/<tenant>/<alias>`) —\n  set it so you can hand people a memorable link. It's normalised to a url-safe slug; a clash comes back\n  in `alias_result`.\n- **System tools are on by default.** New agents automatically get `current_time`, `ip_geo` and `weather`\n  (the built-in `system` MCP) — you don't list them; `tools` is for the *extra* ones.\n- **Names in URLs are slugged.** A **knowledge base** name becomes a url-safe slug on creation\n  (`\"Company Policy\"` → `company-policy`); attach it by the **returned** name, not what you typed.\n\n## 3. Confirm what landed\n\n```text\nget_agent(name=\"Support\")   # writing it doesn't mean it looks the way you intended\n```\n\n<Callout>\n`published=True` means **visible**, not **reachable**. End users can't get to the agent until you create\na **share** (step 4). Don't stop at \"published\".\n</Callout>\n\nBoundaries belong in `task` — negative constraints (\"never promise…\") stop drift better than positive\ndescription. Don't put safety rules in `soul`; the platform appends global moderation automatically. To\nchange one field later, use `update_agent(name, field=…)` — it merges, so it won't blank the rest.\n\n## 4. Publish it — ask how, then make it reachable\n\nBefore you say \"done\", ask the tenant **how their customers should reach it**, then wire that channel with\n`create_share` (it returns a real, openable link — hand that back, not \"it's published\"):\n\n```text\ncreate_share(agent_name=\"Support\", label=\"Website widget\")\n# → { token, chat_url, qr_url, embed_snippet, pretty_url, … }\n```\n\nIf the response carries `pretty_url` (`…/t/<tenant-alias>/<agent-alias>`), **that's the link for\nhumans** — readable and stable across token rotation. If it's null, set the missing alias\n(`create_agent(alias=…)` / `PUT /agents/{name}/alias`; tenant alias in console → Settings) rather\nthan shipping the token link. `chat_url` remains right for embeds and QR codes.\n\n- **No website — just a link or a QR** (a freelancer, a shop, a flyer, a business card): hand back\n  `chat_url` (a full-screen **hosted chat page** — no site needed) and `qr_url` (a QR they can print).\n  It's anonymous: no login, and each visitor is remembered by their browser, so regulars are recognised.\n- **Their own website**: give them `embed_snippet` (one line before `</body>` for a floating widget), or\n  `chat_url` to link / iframe.\n- **Telegram / WhatsApp**: set up per-channel in the console (Agent → **Integration** →\n  Telegram / WhatsApp); point them there.\n\n> **Report back.** Hand back the **actual link they can open and test right now** (`chat_url`, and\n> `qr_url` if they have no site), plus the agent's console page to review it —\n> `https://console.agent4.io/#/agents/Support`. Not \"it's published\" — the link.\n\n## Author a load-on-demand skill\n\n> **Principle — keep the skill count small.** Attach only what this agent's job needs (≈5 or fewer).\n> The model picks skills from their one-line `description`; the more you attach, the more often it loads\n> the wrong one or none. If two skills overlap in *when*, merge them. More in\n> [Design principles](/cookbook/design-principles).\n\nA **skill** is a capability pack loaded on demand: the system prompt carries only its `description`\n(\"when to use\"); the model pulls the full `instructions` only when it judges the skill relevant. So the\n`description` must be short and say *when*, or the model won't know to reach for it.\n\n```text\nlist_skills()                                # what already exists\n\ncreate_skill(\n  name=\"refund-policy\",\n  description=\"Use when the user asks about refunds, cancellations or chargebacks.\",\n  instructions=\"The full procedure: eligibility windows, how to word the outcome, when to escalate …\",\n)\n\nupdate_agent(name=\"Support\", add_skills=[\"refund-policy\"])   # attach it (incremental — keeps existing skills)\n```\n\nKeep procedures in `instructions` (fetched on load), not in the agent's `soul` (which is in the prompt\nevery turn). A one-line `description` that says *when* is what makes the skill discoverable — a vague one\nmeans the model never pulls it.\n\n## Tools can ride on the skill\n\nA skill can carry its own tool bindings — `create_skill(tools=[…])` or\n`update_skill(name, add_tools=[…])`. Tools bound to an attached skill **join the agent's toolset\nautomatically at chat time**, so a skill can ship as a self-contained capability: procedure + the\ntools it needs, attached in one move.\n\nTwo things to know before you rely on it:\n\n- **They don't appear in the agent's own `tools` list.** `get_agent` shows only the agent's directly\n  attached tools; the console's Tools tab shows skill-bound ones as a separate read-only \"via skills\"\n  line. When verifying \"is tool X enabled\", check both places — or just call the tool in a test chat.\n- Prefer binding a tool to the **skill** when it only makes sense inside that procedure (a refund\n  lookup inside the refund skill); bind to the **agent** when it's generally useful across turns\n  (`save_contact`, `web_search`).\n\n## Binding a tool is availability, not policy — the instructions must say when to call it\n\nChecking a tool on a skill (or agent) only makes it *callable*. The model sees the tool's schema and\nits one-line description every turn, so it may use it opportunistically — a visitor volunteers an\nemail and `save_contact` fires. But **any behaviour that must happen reliably needs to be written\ndown**, and each part of it has one right home:\n\n| What you're specifying | Where it goes |\n|---|---|\n| *When to load this skill* | the skill's `description` (in the prompt every turn) |\n| *The procedure: at which step to call which tool, with what arguments* | the skill's `instructions` — **name the tool explicitly** (\"after the caller confirms interest, call `save_contact` with email and phone; then call `schedule_followup` for 1 business day later\") |\n| *A behaviour that must drive the whole conversation* (always capture leads, never quote rates) | the agent's `soul` / `task` |\n\nThe failure mode when instructions don't name the tool: everything *looks* configured — the tool is\nchecked, the skill loads — and the agent still never calls it, or calls it with guessed arguments.\nNothing errors. Write the procedure as if briefing a new employee: the step, the tool name, the\narguments, and what \"done\" looks like.\n\n## Mandatory tool calls: the trigger must live in `description`, not only in `instructions`\n\n`instructions` are visible to the model **only after it calls `load_skill`** — and models frequently\nanswer directly without loading the skill. So a rule like *\"when the user provides a phone number,\nyou must call `save_contact`\"* written only in `instructions` is invisible exactly when it matters:\nthe tool is silently never called, and the model may even claim it saved the contact. This happened\nin production — an agent answered several contact-bearing messages in a row, \"confirmed\" the details\nwere recorded, and no record existed.\n\nThe fix is one sentence in the `description` (which *is* in the system prompt every turn):\n\n```text\nupdate_skill(\n  name=\"consultative-sales\",\n  description=\"Consultative sales: understand the case, recommend products, arrange expert callbacks. \"\n              \"When the user provides a phone number or email, call save_contact BEFORE answering anything else.\",\n)\n```\n\nKeep the detailed procedure in `instructions`; put the *trigger* of any mandatory call in\n`description`.\n\nTwo related rules:\n\n- **Never script return values the tool doesn't produce — relay real ones verbatim.** Built-in\n  `save_contact` and `schedule_followup` return a **real reference code** on success (format\n  `AB2C-D3EF`, stored server-side and visible to the tenant on the end user's detail page).\n  Instruct the model to relay *the code from the tool result* verbatim — never to invent one or\n  reformat it as \"#12345\". For any other tool, verify it actually returns an ID before scripting\n  one: a promised-but-absent ID will be fabricated.\n- **`create_skill` / `update_skill` / `get_skill` now return a `warnings` list** that flags exactly\n  these two patterns (`trigger_hidden_in_instructions`, `promised_tool_return_id`). Warnings are\n  advisory — the save succeeds — but treat them like a linter: fix, don't ignore.\n\n## Worked examples in `instructions` measurably raise tool-call compliance\n\nWe benchmarked this on production backends (6 lead-capture messages of increasing difficulty —\nnumbers buried in long questions, digit groups with spaces, corrections — sampled per condition).\nWith the skill loaded, a plain \"you must call X\" mandate hit **3–4/6**; adding a short block of\nworked examples brought both tested models to **6/6**. Moving the mandate section around did\nnothing — *position doesn't matter, examples do*.\n\nAn effective example block has four kinds of entries, each one line:\n\n```text\n### Worked examples (follow exactly)\n\n1. User: \"I'd like to know about X, my phone is 13800138000\"\n   → call save_contact(phone=\"13800138000\") first, then answer about X.\n2. User: \"email me the offer: li@example.com\"\n   → call save_contact(email=\"li@example.com\").\n3. COUNTER-EXAMPLE (forbidden): user gives a phone number and you reply\n   \"I've noted it down\" WITHOUT calling the tool — claiming success without\n   the call is the worst failure.\n4. User: \"sorry, wrong number — it's 13633334444\"\n   → call save_contact again with the corrected value.\n5. Numbers may contain spaces (\"138 0013 9000\") — still a phone number;\n   strip the spaces and call save_contact(phone=\"13800139000\").\n```\n\nThe counter-example (3) and the format edge case (5) close most of the remaining misses — models\nfail on *recognition* (\"is this a phone number?\") and on *honesty under pressure* (answering a rich\ndomain question first and claiming the save happened) more than on willingness. Keep it to ~5\nentries; use the exact call syntax with realistic arguments.\n\n## Test the trigger before shipping — don't count corpses in production\n\nWhether a skill *actually* fires is measurable, so measure it. `test_skill_trigger` dry-runs your\nmessages against the **production** prompt assembly, tool schemas and model routing, and reports\nwhat the model decided — tools are never executed, nothing is stored, tokens count toward your\nquota (caps: 5 messages × 5 samples).\n\n```text\ntest_skill_trigger(\n  agent=\"advisor\",\n  messages=[\n    \"My phone is 555 0123, call me back\",          # easy\n    \"long question about the product … oh and my number is 555 0123\",  # buried\n    \"555 0123 — that's me\",                        # implicit\n    \"sorry, wrong number, it's 555 9999\",          # correction\n  ],\n  expect_tool=\"save_contact\",\n  samples=3,\n  loaded=true,        # simulate post-load_skill → tests instructions quality\n)                     # loaded=false (default) → first turn, tests the description trigger\n```\n\nRead the result like this:\n\n- **`hit_rate`** below ~90% on realistic messages → strengthen the trigger (description) or add\n  worked examples (instructions), then re-test.\n- **`claimed_without_call` > 0** is the worst failure — the model told the user \"noted!\" without\n  calling the tool. Add the counter-example from the block above.\n- Test **both modes**: `loaded=false` proves the description alone triggers on turn one;\n  `loaded=true` proves the loaded instructions don't dilute it (long instructions measurably do —\n  that's what the worked examples compensate for).\n\nThe result also carries an **`advice` list**: when samples miss or lie, it tells you exactly which\nfix to apply (trigger into the descript\n\nArchive v1.1.8: 3 files, 40347 bytes\n\nFiles: skill-card.md (2384b), SKILL.md (96548b), _meta.json (128b)\n\nArchive v1.1.7: 3 files, 39689 bytes\n\nFiles: skill-card.md (2539b), SKILL.md (94852b), _meta.json (128b)\n\nArchive v1.1.6: 3 files, 37665 bytes\n\nFiles: skill-card.md (2247b), SKILL.md (90131b), _meta.json (128b)\n\nArchive v1.1.5: 3 files, 37414 bytes\n\nFiles: skill-card.md (2323b), SKILL.md (89338b), _meta.json (128b)\n\nArchive v1.1.4: 3 files, 36502 bytes\n\nFiles: skill-card.md (2130b), SKILL.md (87440b), _meta.json (128b)\n\nArchive v1.1.3: 3 files, 35256 bytes\n\nFiles: skill-card.md (2182b), SKILL.md (84587b), _meta.json (128b)\n\nArchive v1.1.2: 3 files, 34473 bytes\n\nFiles: skill-card.md (2544b), SKILL.md (82469b), _meta.json (128b)","readmeExcerpt":"Skill: agent4.io Owner: hellojixian Summary: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks. Tags: latest:1.1.11 Version history: v1.1.11 | 2026-08-08T14:20:52.319Z | user Knowledge base routing: a base's description now decides whether a question searches it at all — new Cookbook section on writing one that routes,","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"claude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\""},{"language":"text","snippet":"get_agent(name=\"Support\")                                   # confirm the config that landed\nsearch_knowledge_base(kb_name=\"Company policy\", query=\"…\")  # confirm the answer is retrievable"},{"language":"bash","snippet":"claude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\""},{"language":"text","snippet":"tenant_info()   # → confirms you are connected to the right tenant"},{"language":"bash","snippet":"clawhub update agent4-io"},{"language":"bash","snippet":"curl -s \"https://api.telegram.org/bot$BOT_TOKEN/setMyCommands\" -H \"Content-Type: application/json\" -d '{"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: agent4-io\ndescription: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\nhomepage: https://agent4.io/cookbook\nuser-invocable: true\nmetadata:\n  {\n    \"openclaw\":\n      {\n        \"emoji\": \"🤖\",\n        \"requires\": { \"env\": [\"AGENT4_API_KEY\"] },\n        \"primaryEnv\": \"AGENT4_API_KEY\",\n        \"envVars\":\n          [\n            {\n              \"name\": \"AGENT4_API_KEY\",\n              \"required\": true,\n              \"description\": \"Your agent4.io tenant API key (tk_…). Get it from the agent4.io dashboard → Tenant & API, or console.agent4.io → Settings → Security — the plaintext is shown once, at creation. No account yet? Sign up free at https://agent4.io (5M tokens/month, no card).\"\n            }\n          ]\n      }\n  }\n---\n\n# agent4.io skills — build agents over MCP\n\nThis skill drives the **agent4.io** platform through its remote MCP server: create grounded agents,\nknowledge bases, load-on-demand skills, stateful Storylines and page playbooks — all from your agent.\n\n## Connect the MCP server (once)\n\nThe tools live on a remote MCP endpoint. Add it to your agent, authenticating with your tenant API key\n(set `AGENT4_API_KEY`; get a key from **console → Settings → Security**, shown once at creation):\n\n- **URL:** `https://api.agent4.io/v1/mcp`\n- **Header:** `X-API-Key: $AGENT4_API_KEY`\n- **Transport:** streamable-http\n\nFor Claude Code / any MCP client that takes a shell command:\n\n```bash\nclaude mcp add --transport http agent4-io https://api.agent4.io/v1/mcp --header \"X-API-Key: $AGENT4_API_KEY\"\n```\n\nOnce connected, call `tenant_info()` to confirm you are on the right tenant, then follow the recipes\nbelow. This skill is a mirror of the always-current Cookbook at https://agent4.io/cookbook.\n\n## Use it as a slash command\n\nThis skill is invocable directly — `/agent4-io <what you want>` — and routes to the right recipe below:\n\n- `/agent4-io create a support agent grounded in these docs` → build a grounded agent\n- `/agent4-io build a knowledge base from this site + these PDFs` → create & populate a KB\n- `/agent4-io author a load-on-demand skill for booking` → write a Skill\n- `/agent4-io compile this intake flow into a Storyline` → design & publish a Storyline\n- `/agent4-io set a page-aware opener for /pricing` → configure a page playbook\n- `/agent4-io how much quota have I used?` → usage & quota\n- `/agent4-io who are my heaviest users this week?` → look up users and their sessions\n- `/agent4-io what is a Storyline?` → look up an agent4.io concept\n\n(On Claude Code and Cursor, the installer also lays down finer-grained shortcuts —\n`/agent4-agent`, `/agent4-kb`, `/agent4-skill`, `/agent4-storyline`, `/agent4-docs` — but they all just\nhand off to this same skill; here a single `/agent4-io` covers them.)\n\n---\n\n# agent4.io — build agents over MCP\n\nThis guide is assembled from the agent4.io Cookbook (https://agent4.io/cookbook). Modules:\n\n- **agent4-io-princip"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn715vygmbtxev9229v6dqrc71838kjn\",\n  \"slug\": \"agent4-io\",\n  \"version\": \"1.1.11\",\n  \"publishedAt\": 1786198852319\n}"},{"path":"skill-card.md","content":"## Description:\n\nBuild and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[hellojixian](https://clawhub.ai/user/hellojixian)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and tenant operators use this skill to connect an agent to agent4.io, then create and configure agents, knowledge bases, load-on-demand skills, Storylines, page playbooks, usage checks, and related tenant workflows.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Tenant configuration, knowledge-base material, and queries may be sent to agent4.io when the skill is used.\n\nMitigation: Tell the user before sending data and send only tenant material they explicitly provide.\n\nRisk: The AGENT4_API_KEY grants access to administer the connected agent4.io tenant.\n\nMitigation: Keep the key restricted, avoid exposing it in logs or shared output, and revoke it when no longer needed.\n\nRisk: MCP or REST actions can change agents, knowledge bases, Storylines, page playbooks, or other tenant state.\n\nMitigation: Confirm the intended tenant and user intent before state-changing actions, and use tenant_info() to verify the connection.\n\n## Reference(s):\n\n- [agent4.io Cookbook](https://agent4.io/cookbook)\n- [agent4.io](https://agent4.io)\n- [agent4.io MCP endpoint](https://api.agent4.io/v1/mcp)\n- [ClawHub skill page](https://clawhub.ai/hellojixian/skills/agent4-io)\n- [ClawHub publisher profile](https://clawhub.ai/user/hellojixian)\n\n## Skill Output:\n\n**Output Type(s):** [Guidance, Shell commands, Configuration, API calls, Markdown, Code]\n\n**Output Format:** [Markdown guidance with inline shell, text, JSON, and API examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires AGENT4_API_KEY and may guide state-changing tenant administration actions.]\n\n## Skill Version(s):\n\n1.1.11 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks. Skill: agent4.io Owner: hellojixian Summary: Build and run grounded business agents on agent4.io over MCP — agents, knowledge bases, load-on-demand skills, stateful Storylines and page playbooks. Tags: latest:1.1.11 Version history: v1.1.11 | 2026-08-08T14:20:52.319Z | user Knowledge base routing: a base's description now decides whether a question searches it at all — new Cookbook section on writing one that routes,","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1366,"uniquenessScore":50,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T01:45:41.177Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:43:37.890Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}