← Back to Documentation

MCP Integration

Model Context Protocol (MCP) lets VoiceRail call tools in your systems. This is completely optional - VoiceRail works great without it.

MCP is Optional

Most use cases don't need MCP. VoiceRail's built-in AI handles conversations well, and you can use webhooks for custom reasoning. MCP is best when you need to integrate with external systems (CRMs, databases, APIs) during calls.

What is MCP?

The Model Context Protocol is an open standard for connecting AI models to external tools and data sources. With VoiceRail + MCP:

  • Your MCP server defines available tools
  • VoiceRail decides when to call tools based on conversation
  • Tool results are woven naturally into the dialogue
  • Hold music plays automatically during long tool calls

Organization-Level Configuration

MCP servers are configured at the organization level, making tools available to all assistants in your organization:

Terminal
# Configure MCP servers at the organization level
curl -X PATCH https://api.voicerail.ai/v1/organizations/{org_id} \
  -H "Authorization: Bearer $VOICERAIL_KEY" \
  -H "X-Organization-Id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "mcpServers": [
      {
        "id": "crm-tools",
        "name": "CRM Tools",
        "transportType": "Http",
        "httpEndpoint": "https://your-mcp-server.com",
        "authentication": {
          "type": "Bearer",
          "bearerToken": "your-secret-token"
        },
        "isEnabled": true,
        "timeoutSeconds": 30
      }
    ]
  }'

Server Configuration Fields

  • id - Unique identifier for this server
  • name - Human-readable name
  • transportType - Http or Stdio
  • httpEndpoint - Server URL (for HTTP transport)
  • authentication - Auth config (Bearer or OAuth2)
  • isEnabled - Toggle server on/off
  • timeoutSeconds - Operation timeout (default: 30)

Transport Types

MCP supports two transport mechanisms:

TransportUse CaseConfiguration
HttpProduction - remote MCP serversSet httpEndpoint + authentication
StdioDevelopment - local processesSet command + arguments

Stdio Example (Local Development)

Stdio Config
{
  "mcpServers": [
    {
      "id": "local-tools",
      "name": "Local Development Tools",
      "transportType": "Stdio",
      "command": "npx",
      "arguments": ["-y", "@your-org/mcp-server"],
      "isEnabled": true,
      "timeoutSeconds": 30
    }
  ]
}

Authentication

HTTP transport supports multiple authentication methods:

TypeFields
NoneNo additional fields (local/trusted only)
BearerbearerToken
OAuth2tokenEndpoint, clientId, clientSecret, scopes

Building an MCP Server

Use the official MCP SDK to create a server that exposes your tools:

mcp-server.ts
import { Server } from "@modelcontextprotocol/sdk/server";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";

const server = new Server({
  name: "customer-service-tools",
  version: "1.0.0"
});

// Define available tools
server.setRequestHandler("tools/list", async () => ({
  tools: [
    {
      name: "lookup_customer",
      description: "Look up customer information by phone number or account ID",
      inputSchema: {
        type: "object",
        properties: {
          phone: { type: "string", description: "Customer phone number" },
          account_id: { type: "string", description: "Customer account ID" }
        }
      }
    },
    {
      name: "check_order_status",
      description: "Check the status of an order",
      inputSchema: {
        type: "object",
        properties: {
          order_id: { type: "string", description: "Order ID to look up" }
        },
        required: ["order_id"]
      }
    }
  ]
}));

// Handle tool calls
server.setRequestHandler("tools/call", async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "lookup_customer":
      const customer = await db.customers.findOne({
        $or: [{ phone: args.phone }, { accountId: args.account_id }]
      });
      return { content: [{ type: "text", text: JSON.stringify(customer) }] };

    case "check_order_status":
      const order = await db.orders.findOne({ orderId: args.order_id });
      return { content: [{ type: "text", text: JSON.stringify(order) }] };

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// Start server
const transport = new StdioServerTransport();
await server.connect(transport);

Tool Call Flow

Here's how a tool call flows through the system:

Flow Example
// 1. User says: "Where's my order 12345?"

// 2. VoiceRail recognizes tool need, calls your MCP server:
{
  "method": "tools/call",
  "params": {
    "name": "check_order_status",
    "arguments": { "order_id": "12345" }
  }
}

// 3. Your server returns:
{
  "content": [{
    "type": "text",
    "text": "{ \"status\": \"shipped\", \"carrier\": \"UPS\", \"tracking\": \"1Z999...\" }"
  }]
}

// 4. VoiceRail speaks: "Your order 12345 has shipped via UPS.
//    Would you like the tracking number?"

MCP vs Webhooks

Both are optional. Choose based on your needs:

AspectWebhookMCP
ControlYou decide what to do with each utteranceVoiceRail decides when to call tools
Best forComplex business logic, custom LLMsSimple tool calls, integrations
ScopePer-assistant configurationOrganization-wide configuration
Hold musicPlays during webhook calls >3sPlays during tool calls >3s

Tool Design Guidelines

Clear Names

Use descriptive names like check_order_status notcos. The model uses names to decide when to call tools.

Good Descriptions

Tool descriptions help the model understand when to use each tool. Be specific about what the tool does and what inputs it needs.

Simple Inputs

Keep input schemas simple. Prefer strings and numbers over complex nested objects. The model extracts these from conversation.

Fast Response

Keep tool calls under 3 seconds when possible. Longer calls trigger hold music, which is fine but feels slower to callers.

Next steps