mcpField
S
setup-tools

setup-tools

github.com/bolna-aiAgent workflows366 installsUpdated 3d ago
Repository

About setup-tools

A skill for configuring function tools in Bolna voice agents. It explains custom HTTP APIs, Cal.com tools, call transfers, and DTMF input so agents can invoke services based on conversation context.

Key features

  1. 01Configure custom HTTP GET and POST tools
  2. 02Define function descriptions and parameter mappings
  3. 03Use call transfer and Cal.com tools
  4. 04Enable DTMF keypad input
  5. 05Troubleshoot tool invocation

Use cases

  1. 01Look up order or customer data
  2. 02Create CRM tickets, leads, or bookings
  3. 03Transfer calls or collect a PIN during a call

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bolna-ai/skills --skill setup-tools

After installing, type/setup-toolsin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
cal-com.md
dtmf.md
tool-schemas.md
transfer-call.md
world-time.md

Details

From the project README

Bolna Function Tools

Tools turn a talking agent into a useful one. The LLM picks a tool based on description, collects parameters from the conversation, and Bolna fires the HTTP request — feeding the response back into the conversation.

When to add a tool

GoalTool
Hand the caller to a humantransfer_call (built-in)
Look up order / customer / account dataCustom HTTP GET
Create a ticket / lead / booking in CRMCustom HTTP POST
Show calendar availabilityfetch_calendar_slots (Cal.com)
Book a calendar slotbook_calendar_slot (Cal.com)
Get current time anywhereCustom HTTP GET to WorldTimeAPI
Capture PIN / OTP / phone number on keypadDTMF (dtmf_enabled: true)

Where tools live in the agent config

agent_config
  └── tasks[]
        └── tools_config
              └── api_tools
                    ├── tools[]          ← function schemas (OpenAI-style + Bolna extensions)
                    └── tools_params     ← per-tool API config (method, url, headers, param mapping)

Reference a tool from prompts with @function_name so the LLM knows when to use it. For graph agents, function_call: "<tool_name>" on a node forces the LLM's tool_choice.

The schema in one minute

{
  "name": "check_order_status",
  "description": "Use this function when the customer asks about their order status, delivery update, shipping progress, or tracking. The customer must provide their order ID.",
  "pre_call_message": "Let me check the status of your order.",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "The customer's order ID. Usually starts with ORD- followed by numbers, e.g., ORD-78234."
      }
    },
    "required": ["order_id"]
  },
  "key": "custom_task",
  "value": {
    "method": "GET",
    "param": { "order_id": "%(order_id)s" },
    "url": "https://api.yourstore.com/orders",
    "api_token": "Bearer sk_live_abc123",
    "headers": {}
  }
}

Two halves:

  • Function definition (name, description, parameters) — follows the OpenAI function calling spec. The LLM uses this to decide when to call and what to extract.
  • Bolna extensions (pre_call_message, key, value) — tells Bolna how to execute the API call.

key must be exactly "custom_task". Don't change it.

The non-negotiable rules

  1. The description is everything. The LLM reads it to decide whether to invoke the tool. Include synonyms ("status, shipping, delivery, tracking"), required preconditions ("only after the caller provides their order ID"), and exclusions ("not for billing questions — use lookup_invoice instead").
  2. Mark parameters required only if the call can't proceed without them. The LLM will keep asking until required fields are filled — annoying when the data isn't strictly necessary.
  3. Always add pre_call_message when the API takes more than ~500ms. "One moment please..." is much better than dead air.
  4. Match parameter names exactly between parameters.properties and value.param. Case-sensitive.
  5. Use format specifiers in value.param: %(name)s for string, %(name)i for int, %(name)f for float. Strings work for almost everything — only use %i/%f when the API needs a typed JSON value.
  6. Trust auto-injected context variables. {from_number}, {to_number}, {call_sid}, {agent_id} and any user_data keys get substituted automatically — don't make the LLM ask the caller for data you already have.

Quick examples

Transfer to a human

The built-in transfer_call tool. Config differs slightly from custom functions — it's wired into Bolna's telephony layer:

{
  "tools": [
    {
      "key": "transfer_call",
      "name": "transfer_to_sales",
      "description": "Transfer when the caller asks about pricing, demos, or purchasing, or explicitly asks to speak to sales.",
      "parameters": {
        "type": "object",
        "required": ["call_sid"],
        "properties": {
          "call_sid": { "type": "string", "description": "Unique call id" }
        }
      },
      "pre_call_message": "Sure, transferring you to sales — please hold a moment."
    }
  ],
  "tools_params": {
    "transfer_to_sales": {
      "url": null,
      "param": {
        "call_sid": "%(call_sid)s",
        "call_transfer_number": "+919876543210"
      },
      "method": "POST",
      "headers": {}
    }
  }
}

Add a separate tool per destination (transfer_to_sales, transfer_to_support, transfer_to_billing) — gives the LLM a sharper choice than one tool with a "which team" parameter.

Cal.com — check availability, then book

Two tools, used in sequence. The LLM is good at chaining them — first show options, then confirm, then book.

{
  "tools": [
    {
      "key": "calendar_slot_availability",
      "name": "fetch_calendar_slots",
      "description": "Fetch open Cal.com slots after the caller asks about scheduling a meeting or demo. Use the caller's preferred date if provided; otherwise use the next 3 business days.",
      "pre_call_message": "Just a moment, let me check our availability.",
      "parameters": {
        "type": "object",
        "properties": {
          "preferred_date": {
            "type": "string",
            "description": "Preferred date in YYYY-MM-DD format. Optional."
          }
        }
      }
    },
    {
      "key": "calendar_book",
      "name": "book_calendar_slot",
      "description": "Book a Cal.com slot ONLY after the caller has confirmed a specific time. Collect their full name and email first.",
      "pre_call_message": "Booking that for you now.",
      "parameters": {
        "type": "object",
        "properties": {
          "slot": { "type": "string", "description": "ISO 8601 slot start time, e.g. 2026-05-22T14:00:00+05:30" },
          "name": { "type": "string", "description": "Caller's full name" },
          "email": { "type": "string", "description": "Caller's email for the calendar invite" }
        },
        "required": ["slot", "name", "email"]
      }
    }
  ]
}

Cal.com API key, event ID, and timezone are configured per-tool in the dashboard once. Bolna handles the HTTP calls automatically — you don't need to write url/api_token for built-in Cal.com tools.

Custom HTTP POST — create a support ticket
{
  "name": "create_support_ticket",
  "description": "Use when the customer reports a problem, complaint, issue, or bug. Create a ticket with category, summary, and priority. Phone number is auto-injected.",
  "pre_call_message": "I'm creating a support ticket for this right away.",
  "parameters": {
    "type": "object",
    "properties": {
      "caller_phone":    { "type": "string", "description": "Auto-injected from {from_number}" },
      "issue_category":  { "type": "string", "description": "billing, technical, account, shipping, or other" },
      "description":     { "type": "string", "description": "1-2 sentence summary of the issue" },
      "priority":        { "type": "string", "description": "low, medium, or high. Set high if the customer is upset or the issue is time-sensitive." }
    },
    "required": ["caller_phone", "issue_category", "description"]
  },
  "key": "custom_task",
  "value": {
    "method": "POST",
    "param": {
      "caller_phone":   "%(caller_phone)s",
      "issue_category": "%(issue_category)s",
      "description":    "%(description)s",
      "priority":       "%(priority)s"
    },
    "url": "https://api.yourhelpdesk.com/tickets",
    "api_token": "Bearer helpdesk_key_789",
    "headers": { "Content-Type": "application/json" }
  }
}

To skip asking the caller for their phone, add {from_number} to the agent prompt and the LLM auto-fills caller_phone.

DTMF (keypad input)

For numeric input the caller would rather type than speak (OTP, account number, PIN). Enable on task_config:

{ "task_config": { "dtmf_enabled": true } }
TelephonySupported?
PlivoYes
TwilioYes
Exotel / Vobiz / SIP BYOTNo

In the prompt, tell the caller to press # after their digits and tell the agent what the input means:

When you need the caller's phone number, say:
"Please enter your phone number on your keypad and press the hash key when done."

When you receive a message starting with "dtmf_number:", the digits that follow are
what the caller pressed. Read them back to confirm and proceed.

What the agent receives:

dtmf_number: 9876543210

The agent treats this like any other turn — it can confirm, pass to a tool, or branch.

For IVR-style menu routing ("press 1 for sales, 2 for support"), use Bolna's IVR feature instead — it doesn't burn LLM tokens on every menu turn. DTMF here is for content (a phone number, an OTP), not navigation.

Going deeper

FileWhat's in it
references/tool-schemas.mdFull schema reference: every field, every type, format specifiers, auto-injection, common failure modes.
references/transfer-call.mdMulti-destination patterns, post-transfer handling, failure fallbacks.
references/cal-com.mdFetch + book end-to-end with worked prompts and example transcripts.
references/dtmf.mdDTMF prompt patterns, telephony support, fallback to speech.
references/world-time.mdWorldTimeAPI custom tool — no auth, useful template.

Debugging

SymptomLikely causeFix
Tool never calleddescription too vague or doesn't match the caller's vocabularyRewrite with synonyms ("status, tracking, shipping, delivery") and explicit trigger phrases
Tool called too eagerlydescription too broad, overlaps with other toolsTighten the "only when…" preconditions
Tool payload missing fieldsParam names mismatched between properties and paramMake them byte-identical, including case
API returns 4xxWrong URL / auth / headersTest the request with curl first; verify api_token format includes Bearer
Variable not substitutedVariable not in agent promptAdd {variable_name} to the prompt so it's in scope
Agent stalls during API callSlow backend with no pre_call_messageAdd a friendly "one moment please" message
Tool returns success but agent ignores resultNo instruction in prompt on what to do with the resultAdd: "After calling X, summarise the result and ask if they want anything else."

See also

  • create-agent — where tools_config.api_tools slots into the agent config.
  • bolna-graph-agents — using tools with function_call on a node for graph flows.
  • https://raw.githubusercontent.com/bolna-ai/skills/main/references/prompting-tips.md — when to ask the caller vs use auto-injected variables.
  • get-executions — inspect raw logs to see exactly what the tool received and returned.

Comments

Markdown supported