{
  "$schema": "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
  "name": "com.voxiora/assistant",
  "version": "1.0.0",
  "title": "Voxiora AI phone assistant",
  "description": "Remote MCP server of Voxiora, the AI phone assistant (AI receptionist): read calls with summaries and transcripts, appointments, contacts, tasks and SMS of one business account; create contacts, start outbound calls and send SMS (with consent). Authenticate with a Voxiora API key or an OAuth client_credentials access token.",
  "websiteUrl": "https://voxiora.com/en/developers#mcp-server",
  "icons": [
    {
      "src": "https://voxiora.com/icon.svg",
      "mimeType": "image/svg+xml"
    }
  ],
  "remotes": [
    {
      "type": "streamable-http",
      "url": "https://voxiora.com/api/mcp",
      "headers": [
        {
          "name": "Authorization",
          "description": "Bearer Voxiora API key (tf_live_…) or OAuth access token from https://voxiora.com/api/oauth/token",
          "isRequired": true,
          "isSecret": true
        }
      ],
      "supportedProtocolVersions": [
        "2025-11-25",
        "2025-06-18",
        "2025-03-26",
        "2024-11-05"
      ]
    }
  ],
  "serverInfo": {
    "name": "voxiora",
    "title": "Voxiora AI phone assistant",
    "version": "1.0.0"
  },
  "protocolVersion": "2025-11-25",
  "transport": {
    "type": "streamable-http",
    "url": "https://voxiora.com/api/mcp",
    "endpoint": "/api/mcp"
  },
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "authentication": {
    "required": true,
    "schemes": [
      "bearer",
      "oauth2"
    ],
    "protected_resource_metadata": "https://voxiora.com/.well-known/oauth-protected-resource/api/mcp",
    "authorization_server": "https://voxiora.com",
    "token_endpoint": "https://voxiora.com/api/oauth/token",
    "grant_types": [
      "client_credentials"
    ]
  },
  "documentation": "https://voxiora.com/en/developers#mcp-server",
  "tools": [
    {
      "name": "get_account",
      "title": "Get account",
      "description": "Returns the account the API key belongs to. Handy as a “test connection” call. REST equivalent: GET /api/v1/me."
    },
    {
      "name": "list_assistants",
      "title": "List assistants",
      "description": "Your assistants with their language, status and assigned phone numbers. Use an id as assistant_id when filtering calls. REST equivalent: GET /api/v1/assistants."
    },
    {
      "name": "list_phone_numbers",
      "title": "List phone numbers",
      "description": "Your account's phone numbers with country, type, status and the assistant that answers them. REST equivalent: GET /api/v1/phone-numbers."
    },
    {
      "name": "list_calls",
      "title": "List calls",
      "description": "Calls newest first, without transcripts. Demo calls are never included. Requires the read:calls scope. REST equivalent: GET /api/v1/calls."
    },
    {
      "name": "get_call",
      "title": "Get call with transcript",
      "description": "One call with the full transcript and the appointments booked during it. Requires the read:calls scope. REST equivalent: GET /api/v1/calls/{id}."
    },
    {
      "name": "list_contacts",
      "title": "List contacts",
      "description": "Your callers, newest first. The assistant remembers them across calls. Requires the read:contacts scope. REST equivalent: GET /api/v1/contacts."
    },
    {
      "name": "get_contact",
      "title": "Get contact",
      "description": "One contact with name, email, tags, notes and where the name and email came from (call, system or manual). Requires the read:contacts scope. REST equivalent: GET /api/v1/contacts/{id}."
    },
    {
      "name": "create_contact",
      "title": "Create contact",
      "description": "Adds a contact, for example from your CRM, so the assistant knows the caller's name. Name and email saved through the API are marked manual and the assistant never overwrites them. Returns 409 with contact_id if the phone number already exists. Sends the contact.created webhook. Requires the write:contacts scope. REST equivalent: POST /api/v1/contacts."
    },
    {
      "name": "list_appointments",
      "title": "List appointments",
      "description": "Upcoming appointments in start order (from now by default). Requires the read:appointments scope. REST equivalent: GET /api/v1/appointments."
    },
    {
      "name": "get_appointment",
      "title": "Get appointment",
      "description": "One appointment of your account. Requires the read:appointments scope. REST equivalent: GET /api/v1/appointments/{id}."
    },
    {
      "name": "list_tasks",
      "title": "List tasks",
      "description": "Callback requests, messages and other tasks from calls, newest first. Requires the read:tasks scope. REST equivalent: GET /api/v1/tasks."
    },
    {
      "name": "list_messages",
      "title": "List SMS messages",
      "description": "SMS your account sent and customer replies, newest first. phone is the customer's number (recipient or sender of a reply). generated_by: template, ai or manual; charged_from: plan or credits. Requires the read:messages scope. REST equivalent: GET /api/v1/messages."
    },
    {
      "name": "start_outbound_call",
      "title": "Start outbound call",
      "description": "Your assistant calls one person from your business number with the goal you give, like a step of an onboarding sequence. The call goes out now or at call_at, always within calling hours (Mon–Sat, 9:00–20:00 in your account's time zone; outside them it moves to the next allowed time and call_at shows when). If nobody answers, it tries up to 3 times, 2 hours apart. Returns 202 with the call in status scheduled. Errors: 400 consent_missing, invalid_phone, number_not_callable, international or invalid_call_at; 409 opted_out (the person asked not to be called), already_scheduled (with existing_id), assistant_not_live, over_quota or no_outbound_number; 429 daily_outbound_limit with limit (200 calls per 24 hours by default). The outcome arrives in the call.completed and outbound_call.finished webhooks with your external_id. Only for people who agreed to be called by this business: `consent` must be true. Confirm the number and goal with the user before calling. Requires the write:calls scope. REST equivalent: POST /api/v1/calls/outbound."
    },
    {
      "name": "get_outbound_call",
      "title": "Get outbound call status",
      "description": "Status of a scheduled outbound call: scheduled, dialing, done (with call_id of the conversation), failed (last_error, e.g. no_answer after all attempts) or canceled. Also works with the callback_id from webhooks for onboarding and callback calls. Requires the write:calls scope. REST equivalent: GET /api/v1/calls/outbound/{id}."
    },
    {
      "name": "send_message",
      "title": "Send SMS",
      "description": "Sends an SMS to one of your contacts or callers: your own text, or one of your account's templates with vars (date, time, name, service, link, amount). Counts toward your plan's SMS allowance and extra SMS. Errors: 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars); 409 conflict (opted_out — the customer replied STOP, over_quota, recipient_not_allowed — create the contact first, blocked_destination); 429 rate_limited. Only to existing contacts or callers who agreed to get SMS from this business: `consent` must be true. Confirm the text with the user before sending. Requires the write:messages scope. REST equivalent: POST /api/v1/messages."
    }
  ]
}