Skip to content

Tool Calling & the Catalog

Skills page — catalog of connector tools with descriptions, effect (read/write/destructive), and approval requirements.

Agentcy never dumps all connector tools into the LLM prompt. Instead, the agent sees four catalog meta-tools and uses them to discover real tools on demand. This page is how to work with the catalog from your own code and chats.

The theory is in Concept: Agent Loop and Concept: Skills (Tool Catalog). Here we're hands-on.

The four meta-tools

list_connectors()                         -> summary of enabled connectors
search_connector_tools(query, top_k)      -> ranked tools matching the query
execute_connector_tool(connector, tool, args)
                                          -> call it (runs policy+approval)
request_connector_access(connector, reason)
                                          -> ticket an admin for access

Every chat sees these whether or not connectors are enabled.

Typical flow (from the LLM's perspective)

User: "what pods are failing in prod?"

LLM → search_connector_tools({query:"pod status failing", top_k:5})
        ↘ Catalog returns ranked list; top hit: kubernetes.list_pods

LLM → execute_connector_tool({
        connector:"kubernetes", tool:"list_pods",
        args:{"cluster":"prod-us","status":"Failed"}
      })
        ↘ Policy allows read tool; executes.
        ↘ Returns 3 pods in CrashLoopBackOff.

LLM → composes a reply: "3 pods failing: checkout-6b…, auth-7c…, orders-2a…"

Driving it by hand via REST

Sometimes you want to call a tool without going through an LLM — e.g., from a script, a test, or a scheduled task's payload. Use:

bash
curl -X POST "http://…/chat/conversations/$CONV/tool-calls" \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{
    "connector":"kubernetes",
    "tool":"list_pods",
    "args":{"cluster":"prod-us","status":"Failed"}
  }'

Response:

json
{
  "tool_call_id": "tc_…",
  "status": "completed",
  "result": { "pods": [  ] },
  "usage_ms": 320,
  "policy": { "decision": "allow", "audit_id": "aud_…" }
}

If it would have required approval, the response is {"status":"pending_approval","approval_id":"apr_…"} — approve via POST .../approvals/:apr_id.

Listing connectors and their tools

bash
# Catalog for this conversation
curl "http://…/chat/conversations/$CONV/tools" -H "authorization: Bearer $TOKEN" | jq

# Org-wide (admin view, not filtered by realm)
curl "http://…/skills?kind=connector_tool&limit=500" -H "authorization: Bearer $TOKEN" | jq

Every ToolSpec has:

json
{
  "connector": "kubernetes",
  "name": "list_pods",
  "description": "List pods in a cluster, optionally filtered by namespace or status.",
  "args_schema": { "type":"object", "properties":{} },
  "effect": "read",
  "requires_approval": false,
  "realms": ["infrastructure"]
}

effect is one of read / write / destructive. The agent (and any policy) uses it to decide whether to gate.

Pinning connectors to a conversation

By default, a conversation's tool catalog is filtered by its realm. You can override that:

bash
# Pin: only these three sources' tools are available in this conv
curl -X PATCH "http://…/chat/conversations/$CONV" \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"enabled_source_ids":["src_gh_primary","src_k8s_prod","src_aws_prod"]}'

# Unpin: go back to realm-based selection
curl -X PATCH "http://…/chat/conversations/$CONV" \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"enabled_source_ids":null}'

Pinning is the right move for scripts and automations — it removes the ambiguity of "which of my three AWS sources did it pick?"

Custom skills

Beyond connector tools, you can register custom skills. A skill is a user-defined tool whose body is either:

  • a URL (HTTP webhook — the API POSTs args, receives result JSON),
  • a script (executed in a sandbox),
  • an MCP server (via the mcp connector).

Create:

bash
curl -X POST "http://…/skills" \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{
    "name":"triage.fetch_runbook",
    "description":"Fetch the runbook text for an alert by name.",
    "args_schema":{"type":"object","properties":{"alert":{"type":"string"}}},
    "effect":"read",
    "kind":"http",
    "http":{"url":"https://runbooks.internal/lookup","method":"POST","secret_header":"X-Token"}
  }'

After admin approval, the skill shows up in the catalog just like a connector tool.

See backend/crates/agentcy-api/src/routes/skills.rs.

Debugging a tool call

  • See args + result live: open the conversation in the UI, expand the tool-call card.
  • Replay: POST /chat/conversations/:id/tool-calls/:id/replay re-runs the call with the same args. Good for flaky upstream APIs.
  • Trace: every tool call carries a request_id that propagates into tracing spans. Grep logs for it.
  • Policy audit: if the call was denied, the audit entry has the exact rule and input that matched.

Effects and when things require approval

Defaults (override per-org in Settings → Security):

EffectDefault behavior
readauto-approve
writeapproval required
destructiveapproval required + policy must explicitly allow

A specific tool can override defaults (requires_approval: true/false in its ToolSpec) when a connector author knows better than the default.

Gotchas

  • Search is embedding-based. If the agent can't find your tool, improve the description. "Lists pods" is fine; "Show K8s workload health" is better.
  • Args come from the LLM. They must match the schema or the API returns args_schema_invalid. Err on the side of strict schemas — it teaches the model to pass correct args.
  • Don't ship write tools with requires_approval=false. Approvals are cheap; silent writes are expensive when they're wrong.

Next

Built by AgentcyLabs. For in-house deployment or Agentcy Cloud (PaaS) access, visit agentcylabs.com.