From zipchat-custom-tool
Build a Zipchat (chatlive) custom tool — the prompt/instructions, variables, and (for gallery templates) the merchant note — that lets the AI support agent carry out a real action by running shell commands (curl plus jq, ripgrep, fd, unzip) against one or more external HTTP APIs. Trigger whenever someone wants to "create/build/draft a custom tool", "connect Zipchat to <API>", "make the bot look up orders / create discounts / fetch tracking / escalate tickets", write the instructions/prompt for a custom tool, or debug/fix a custom tool that isn't working (it can read the debug transcript from the dashboard's Debug modal). Produces all the inputs needed to install the tool; the author does NOT need access to the chatlive codebase.
How this skill is triggered — by the user, by Claude, or both
Slash command
/zipchat-custom-tool:zipchat-custom-toolThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
A **custom tool** lets a brand's Zipchat AI agent perform a real action by running shell
A custom tool lets a brand's Zipchat AI agent perform a real action by running shell commands in a sandbox against one or more external HTTP APIs. It can be a single call, a sequence of calls against the same API, or a multi-system flow that opens several APIs one after another — order lookup, discount creation, shipment tracking, ticket escalation, and richer multi-step workflows are all in scope. Your job with this skill is to interview the user and produce every input needed to install the tool, with a well-formed prompt as the centerpiece.
You do not need the chatlive codebase. Everything you need to know about how tools run is below.
instructions), and a set of variables (secrets/config).#### <tool name>
<custom_tool_instructions>
…your prompt…
</custom_tool_instructions>
curl, optionally piped through jq to pull or
reshape fields, and across multiple steps when the tool needs them. Each command's
output (usually JSON) comes back to the agent, which reads it and either runs the next
step or replies to the customer in the brand's voice.WOO_CONSUMER_KEY is referenced in the curl as
$WOO_CONSUMER_KEY. Values are encrypted at rest and never shown to the customer.There is no server-side code per tool — the effect is produced by the real HTTP call(s) the prompt makes. If a desired action has no HTTP call behind it, the tool can't do it.
The shell is a real Unix shell preloaded with a small toolbox — use it to process what a call returns:
curl — every HTTP call (the only thing that produces an effect).jq — parse, filter, and reshape JSON; e.g. pipe a response through jq -r '.id' to
feed the next step.ripgrep (rg) and fd — search text/files the tool fetched.unzip — expand an archive a call returns.The actual effect must always come from the HTTP call itself. Never use the shell to fake a result, stand in for an API call, or mutate sandbox files to simulate an effect.
$VAR vs {PLACEHOLDER}This distinction is the heart of a good tool prompt.
| Form | Meaning | Example | Who fills it |
|---|---|---|---|
$VAR_NAME | A stored variable (secret/config) injected as an env var | $WOO_STORE_URL, $ZENDESK_API_TOKEN | The platform, at runtime |
{PLACEHOLDER} | A runtime value the agent substitutes from the conversation | {ORDER_ID}, {EMAIL}, {PERCENT} | The agent, when writing the curl |
$VAR. Never hardcode a secret in the prompt.{PLACEHOLDER} for values that come from the customer or the agent's judgment.
Tell the agent in <inputs_resolution> how to obtain each one.^[A-Z_][A-Z0-9_]*$.WOO_…, ZENDESK_…, LOYALTY_…. Never a bare API_KEY.Zipchat injects shared "Custom Tool Mode" rules into every tool-enabled agent. Your prompt must not restate them (it wastes tokens and drifts out of sync):
jq/awk/etc. to post-process it is fine; running
the real command (not an echo/python -c that fakes a result) is required.rest_no_route, bad path/param),
the agent re-reads, fixes, and retries up to 3 times. Safe even for write tools.So your prompt should focus on what's specific to this tool: which endpoint, how to resolve inputs, business rules, and how to present the result. Only add error handling that is tool-specific (e.g. "if the code already exists, generate a new one before retrying") — never the generic loop.
When you finish, hand the user a filled-in version of all of these:
NAME → what to paste pairs (the secrets/config), each
with a note on where the merchant gets the value. These become $VARs.instructions) — the core deliverable, ≤10,000 chars. Structure below.Write the prompt as Markdown with these XML-ish sections, in order. See
reference/prompt-structure.md for the full section-by-section guide, and
examples/ for two complete, real prompts (a read tool and a write tool).
<task> What the tool does + when to use it. If it writes/creates data, say so loudly.
<inputs_resolution> Every {PLACEHOLDER} and how the agent obtains it (ask customer, read system prompt, decide).
<execution_protocol> The shell command(s): one network call per step (number multi-step / multi-system flows), plus any jq post-processing, using $VARS and {PLACEHOLDERS}. State the expected success status.
<tool_persistence_rules> Anti-hallucination: the agent must observe the real success response before claiming the action happened.
<output_contract> How to present the result to the customer; what to never reveal (secrets, raw JSON, variable names, the API's name).
Add <composition_rules> or a tool-specific <…_rules> block only when the tool has
real business logic (e.g. a discount cap, a multi-step order). Keep it tight.
If the user says the tool misbehaves (never fires, fails, or the agent replies wrong), ask them to fetch the debug transcript for a real example before you change anything:
The transcript is a plain-text dump of everything the agent saw and did for that one reply:
## System prompt — the full prompt the agent received. An installed tool appears
inside it as a #### <tool name> section wrapped in <custom_tool_instructions>.## User / ## Assistant — the conversation turns sent to the model.[Tool call] Shell command — each shell invocation with its arguments, including
the exact command line the agent composed ($VAR references are shown unexpanded;
the values never appear).## Tool result — what came back from each command: its stdout/stderr, i.e. the raw
API response body or error.It deliberately contains no model, token, or other platform-internal data — everything in it is relevant to your tool.
Work through these checks in order; each maps a symptom to the input you should fix:
<custom_tool_instructions> block with your tool's name ⇒ the tool is inactive,
not installed, or not enabled for that conversation's channel. Fix activation —
the prompt itself isn't the problem yet.[Tool call] where one
was expected ⇒ the <task> trigger description didn't match how the customer
actually asked. Sharpen the when-to-use wording (add the phrasings customers use).$VAR names — a typo'd variable name
expands to an empty string, which typically shows up as a 401 or a malformed URL.
Check each {PLACEHOLDER} was filled with a sensible value from the conversation;
if not, tighten <inputs_resolution>.rest_no_route ⇒ wrong path or store URL;
400/422 ⇒ wrong params; a successful response the agent then misreported ⇒ the
response shape doesn't match your jq filter or <output_contract>.$VAR, never hardcoded; names are prefixed and
match ^[A-Z_][A-Z0-9_]*$.{PLACEHOLDER} has a resolution rule in <inputs_resolution>.jq to post-process is fine; no faking
results and no mutating sandbox files.<tool_persistence_rules> forbids claiming success before the real success
response is observed (critical for write tools — discounts, escalations, etc.).<output_contract> lists what to never reveal: env var values, raw JSON,
variable names, and the underlying API/vendor name (speak in the brand's voice).<task> states it creates/modifies data; any tool-specific retry
nuance (e.g. regenerate a value on conflict) is inline.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub zipchat-ai/zipchat-custom-tool-skill --plugin zipchat-custom-tool