AgentStack
SKILL verified MIT Self-run

Add Tools And Handoffs

skill-patterai-skills-add-tools-and-handoffs · by PatterAI

>

No reviews yet
0 installs
10 views
0.0% view→install

Install

$ agentstack add skill-patterai-skills-add-tools-and-handoffs

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Add Tools And Handoffs? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Add tools and handoffs to a Patter agent

Patter agents have two built-in tools (transfer_call, end_call) plus unlimited custom tools. Custom tools can be handler-based (your Python/TS function runs in-process) or webhook-based (the agent POSTs JSON arguments to your URL). Both shapes use the same Tool model. Output guardrails block or rephrase agent responses before they reach TTS.

Built-in tools (always available)

Every agent automatically has:

  • transfer_call(target: str) — bridge to another phone number. Caller

hears the new party, your Patter session ends.

  • end_call() — hang up. Use for "goodbye" flows or abusive callers.

You don't have to register these. The LLM picks them when the system prompt authorizes it.

agent = phone.agent(
    engine=OpenAIRealtime2(),
    system_prompt=(
        "You are Mia, the receptionist for Acme. "
        "If the caller asks for a human, call `transfer_call` with target='+14155559999'. "
        "If the caller wants to hang up, call `end_call`."
    ),
    first_message="Hi, how can I help?",
)

Custom tools — handler form

The @tool decorator (Python) and defineTool (TypeScript) auto-extract the JSON schema from your function signature and docstring. Best for simple synchronous lookups.

Python

from getpatter import Patter, Twilio, OpenAIRealtime2, tool

@tool
async def lookup_customer(phone: str) -> dict:
    """Look up customer details by phone number.

    Args:
        phone: E.164 phone number, e.g. +14155551234.

    Returns:
        Customer name, tier, and last order date.
    """
    # your CRM call here
    return {"name": "Jane Doe", "tier": "gold", "last_order": "2026-04-12"}

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
    engine=OpenAIRealtime2(),
    system_prompt="You are a customer-service agent. Use `lookup_customer` when you need account info.",
    first_message="Hi, can I help with your account?",
    tools=[lookup_customer],
)

TypeScript

import { Patter, Twilio, OpenAIRealtime2, defineTool } from "getpatter";

const lookupCustomer = defineTool({
  name: "lookup_customer",
  description: "Look up customer details by phone number.",
  parameters: {
    type: "object",
    properties: {
      phone: { type: "string", description: "E.164 phone number, e.g. +14155551234." },
    },
    required: ["phone"],
  },
  handler: async ({ phone }) => {
    // your CRM call here
    return { name: "Jane Doe", tier: "gold", lastOrder: "2026-04-12" };
  },
});

const phone = new Patter({ carrier: new Twilio(), phoneNumber: "+15550001234" });
const agent = phone.agent({
  engine: new OpenAIRealtime2(),
  systemPrompt: "You are a customer-service agent. Use lookup_customer when you need account info.",
  firstMessage: "Hi, can I help with your account?",
  tools: [lookupCustomer],
});

Custom tools — webhook form

Better for cross-language deployments or when the tool lives in a separate service. Patter POSTs the tool args as JSON to your URL and uses the JSON response as the tool result.

from getpatter import Tool

crm_lookup = Tool(
    name="lookup_customer",
    description="Look up customer details by phone number.",
    parameters={
        "type": "object",
        "properties": {"phone": {"type": "string"}},
        "required": ["phone"],
    },
    webhook_url="https://api.acme.com/crm/lookup",
)

agent = phone.agent(
    engine=OpenAIRealtime2(),
    system_prompt="…",
    tools=[crm_lookup],
)

The endpoint receives POST with body {"phone": "+14155551234"} and must respond with JSON. Authentication: add your own header via reverse proxy, or use Patter's Tool(webhook_headers={...}) (omitted here for brevity).

Reassurance messages (Realtime mode)

In Realtime mode, tool calls happen during the call — the LLM has to decide whether to keep talking while the tool runs. Set reassurance to play a "hold on" message while the handler executes:

@tool(reassurance="Let me check that for you, one moment.")
async def lookup_customer(phone: str) -> dict:
    await asyncio.sleep(2)  # imagine a slow CRM
    return {"name": "Jane"}

Without it, there's silence while the tool runs — which sounds broken to the caller.

Output guardrails

Block specific phrases or run a custom check before the agent's response reaches the TTS.

from getpatter import guardrail, Patter, Twilio, OpenAIRealtime2

@guardrail
def no_competitors(text: str) -> bool:
    """Block any response that mentions a competitor."""
    return "patter" not in text.lower() or "alternative" not in text.lower()

phone = Patter(carrier=Twilio(), phone_number="+15550001234")
agent = phone.agent(
    engine=OpenAIRealtime2(),
    system_prompt="You are a sales agent for Acme.",
    first_message="Hi, how can I help?",
    guardrails=[no_competitors],
)

When a guardrail blocks, the agent speaks the guardrail's replacement text instead (default: "I'm sorry, I can't respond to that.").

For a simple keyword list:

from getpatter import Guardrail

agent = phone.agent(
    ...,
    guardrails=[Guardrail(
        name="no-pii",
        blocked_terms=["social security", "credit card"],
        replacement="I can't help with sensitive information. Let me transfer you.",
    )],
)

Pipeline hooks (advanced)

For more control than guardrails, pipeline hooks intercept every leg of the pipeline (only available in Pipeline mode):

| Hook | Fires when | Use case | |---|---|---| | before_send_to_stt | Audio in | Echo cancellation, noise gate. | | after_transcribe | After STT | PII redaction, command parsing. | | before_llm | Before LLM | RAG injection, context rewrites. | | after_llm | After LLM | Output filter, persona enforcement. | | before_synthesize | Before TTS | Sentence chunking, voice-tag insertion. | | after_synthesize | After TTS | Audio post-processing. |

Hooks are async functions returning None (no change) or the modified value. See .

Gotchas

  • transfer_call requires target in E.164. Tell the agent in the

system prompt: "Use transfer_call with the target number +1...."

  • Webhook tools time out at 30 s. For longer ops, return a job ID and

expose a second tool to poll.

  • JSON Schema strict mode: pass strict=True (Python) /

strict: true (TS) to force OpenAI's strict-mode schema validation. Catches bad LLM args at the boundary.

  • Guardrails fire post-LLM, pre-TTS — the caller never hears the

filtered text. The agent's memory still has the original though.

  • Reassurance only works in Realtime mode. In Pipeline mode the LLM

generates the reassurance text itself; ConvAI mode doesn't expose this.

Common errors

| Symptom | Fix | |---|---| | LLM never calls the tool | System prompt isn't authorizing it. Add "Use the X tool when ..." explicitly. | | ValueError: parameters must be a JSON Schema object | You passed parameters={"phone": "string"} instead of {"type": "object", "properties": {...}}. | | Tool returns None and LLM gets stuck | Always return something — a status string, a dict, an empty {}. Never None. | | Transfer rings out | The target number isn't a Twilio-owned or verified caller ID (Twilio trial). |

Related skills

  • [build-voice-agent](../build-voice-agent/) — define the agent the tools attach to.
  • [inspect-calls-and-metrics](../inspect-calls-and-metrics/) — see tool-call latency in the dashboard.

References

  • Patter Tools doc: ·
  • Patter Guardrails doc: ·
  • Pipeline hooks:

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.