Read-only MCP server for FoxTrove Voice — connect Claude Desktop, Cursor, ChatGPT, or any MCP client to your voice call data, customer context, follow-ups, and analytics.
Strong foundation with 13 well-named, read-only tools covering calls, customers, assistants, and analytics. All tools have descriptions (avg 180 chars, within baseline 194) and explicit input schemas with typed parameters. Naming follows verb_noun convention consistently (list_*, get_*, search_*, compare_*). However, output schemas are not documented in the source, responses are JSON-stringified but field structures are not declared. Parameter descriptions are present but some lack format/constraint details (e.g., 'ISO 8601' dates mentioned but no validation guidance). Error handling is basic (InternalApiError wrapper) without recovery guidance or categorization. No tool annotations (readOnlyHint, etc.) despite all being read-only. Pagination is well-designed (cursor-based, limits enforced) but not all tools document their response structure.
Period-over-period delta for a single metric — current value, prior value, absolute change, percent change. The single most useful tool for briefs and trend callouts: 'calls up 23% vs last week, qualified rate down 4 points, booked flat'. Pick a metric (`calls`, `qualified`, `avg_duration`, `booked`) and a period granularity (`day`, `week`, `month`); the tool handles the windowing automatically. Prefer this over running `get_call_metrics` twice and diffing manually. Requires `read:analytics` scope.
Aggregate performance metrics for one assistant over a time window — call counts by outcome, average duration, and the most common topics it encountered. Use this when the user wants to zoom in on a single assistant ('how is the receptionist performing this week?') rather than the tenant overall. For tenant-wide numbers prefer `get_call_metrics` or `get_outcome_breakdown`. Requires `read:assistants` scope.
Fetch the full record for one call by ID — metadata, outcome, summary, and action items. Opt into the heavier fields (`include_transcript`, `include_recording_url`) only when the user actually wants to read the exact words or listen back; transcripts can run thousands of tokens, so keep them off by default when you're just confirming what a call was about. Use after `list_calls` or `search_calls` surfaces an interesting ID. Requires `read:calls` scope.
Aggregate call counts, total duration, and average duration over a window — with optional grouping by day / week / month / assistant / outcome. This is the workhorse analytics tool: 'calls per day this month', 'total minutes by assistant last week', 'breakdown by outcome for Q1'. Returns either a time series (when `group_by` is a time bucket) or grouped totals. Pair with `compare_periods` for week-over-week deltas, or with `get_outcome_breakdown` for conversion-style questions. Requires `read:analytics` scope.
Output schemas not documented. Tool descriptions explain what is returned (e.g., 'call summaries with matching snippets', 'aggregate metrics') but the actual JSON field structure is not declared. LLMs cannot reliably extract fields or plan downstream calls without knowing response shape.
No tool annotations despite all 13 tools being read-only. The MCP spec supports readOnlyHint to signal safe tools. Adding this would help agents reason about which tools are safe to call without user confirmation.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 68 | 2026-07-28+ | v2 |
Fetch a single, unified snapshot of one customer across every connected CRM — identity, deal stage, recent calls, open follow-ups, and last interaction. This is the tool to reach for whenever the user asks 'what's the story with <person>?' or 'did we talk to <phone> before?'. It stitches together data that otherwise lives in different dashboards (HubSpot, JobNimbus, Roofr, SumoQuote, etc.) and returns one clean object. Provide exactly one of `phone`, `email`, or `customer_id`. Prefer phone for inbound-call contexts, email for inbound-form/CRM contexts. Requires `read:customers` scope.
Distribution of call outcomes over a window — each outcome label with its count and percentage of total calls. The right tool for conversion-style questions: 'what's my qualified-lead rate this month?', 'how often are calls going to voicemail?', 'did my booking rate change after the prompt update?'. For trend-over-time questions use `compare_periods`; for raw counts use `get_call_metrics`. Requires `read:analytics` scope.
Most-discussed topics and objections across calls in a window — ranked, with a count and a few example call IDs per topic. Powered by topic tags extracted from call summaries. The best tool for 'what are people calling about this week?', 'which objections keep coming up?', or 'are we hearing about the new pricing yet?'. Each result gives you example call IDs so you can pivot to `get_call` for the actual exchange. Requires `read:analytics` scope.
List appointments and bookings that were scheduled during voice calls — estimates, site visits, follow-up consultations. Useful for 'what's on the calendar this week from inbound calls?' or to audit booking volume. Each appointment includes the linked call ID so you can inspect how the booking conversation went. Requires `read:appointments` scope.
List the voice assistants configured on the tenant — id, name, a one-line purpose, and a recent call count per assistant. This is the discovery tool: use it when the user says 'how is the booking agent doing?' or 'which assistants are live?' and you need the canonical assistant_id to pass into `get_assistant_stats`, `list_calls`, or the analytics tools. Returns all assistants in one shot (no pagination — tenants typically have <20). Requires `read:assistants` scope.
List recent FoxTrove Voice calls for the authenticated tenant, newest first. Use this to browse what happened over a window of time — for example 'show me today's calls', 'how many calls did the booking assistant handle this week?', or as a starting point before drilling into a specific call with `get_call`. Returns a compact summary per call (id, started_at, assistant, customer identifier, outcome, duration, short summary) plus a `next_cursor` for pagination. Defaults to the last 7 days and 25 results — pass `since`/`until` or `limit` to widen, and pass `cursor` to page. Prefer `search_calls` when the user is asking about a topic or phrase rather than a time range. Requires `read:calls` scope.
List open action items that voice agents flagged during calls — 'send warranty paperwork', 'call back Thursday', 'confirm roof measurements'. Perfect for briefs and to-do prompts: 'what do I owe people today?', 'anything open from yesterday's calls?'. Defaults to open items only; pass `status` to include closed ones. Each item links back to the call and customer it came from, so you can drill in with `get_call` or `get_customer_context`. Requires `read:followups` scope.
Semantically search voice-call transcripts and summaries by a natural-language query — powered by embeddings, not keyword match. Reach for this when the user asks about topics, themes, objections, or phrases that may have come up across multiple calls: 'any calls about warranty issues?', 'who asked about pricing this week?', 'did anyone push back on the install timeline?'. Returns ranked call summaries with matching snippets. Narrower than `list_calls` (max 25 results) and more relevant — prefer it whenever the question is topical rather than chronological. Combine with `get_call` to pull the full transcript of a match. Requires `read:calls` scope.
Find customers by a fragment of name, phone, or email. Use this to resolve ambiguous references like 'the Andersons' or '555-0182' into a concrete customer ID you can then pass to `get_customer_context`. Returns up to 10 matches by default with just enough identifying info to disambiguate; don't use it to bulk-enumerate the customer database. Requires `read:customers` scope.
Error handling lacks recovery guidance. InternalApiError is caught and surfaced as 'FoxTrove Voice API error {status}: {body}' but does not categorize errors (retryable vs user-fixable) or suggest next steps. E.g., 'Invalid assistant_id. Call list_assistants() to discover valid IDs.'
Parameter constraints not fully specified. Date parameters mention 'ISO 8601' in descriptions but lack format declarations or validation examples. Numeric limits (e.g., 'hard max 100' for limit) are stated in prose but not in schema constraints (min/max).
Scope declarations present in descriptions ('Requires read:calls scope') but not machine-readable. No explicit scope metadata in tool registration. This prevents automated least-privilege configuration and audit trail generation.