A reference MCP server demonstrating safe, tenant-scoped, permission-aware, auditable agent access to CRM data.
Strong foundation with clear naming, comprehensive descriptions, and well-structured schemas. All 6 tools follow verb_noun convention (search_, get_, list_, update_). Descriptions are detailed (100-200 chars typical) and explain WHAT, WHEN, and tenant-scoping behavior. Input schemas use Zod with proper types and constraints. Enums used correctly (e.g., stage field in update_opportunity_stage). Audit trail implementation is exemplary. Main gaps: no output schemas documented, no pagination support despite list_opportunities potentially returning many records, and error handling lacks recovery guidance. Tool composition is clean (single responsibility), but missing batch variants and natural-identifier acceptance patterns.
Fetch a single account by id. Returns null if the account does not exist within the caller's tenant, even if the id is valid in another tenant.
List activities for an opportunity. Tenant-scoped: returns nothing for an opportunity id that belongs to another tenant.
Read the audit trail. Tenant-scoped and role-scoped: an admin sees the whole tenant's trail; everyone else sees only their own tool calls. Records contain arguments and result counts, never returned row contents.
List opportunities visible to the caller. Always tenant-scoped, then narrowed by role: a rep sees only their own, a manager sees their team's, an admin sees the whole tenant's.
Search accounts in the caller's tenant by name substring. Returns only accounts within the caller's tenant.
Update an opportunity's stage. Read-only unless confirmed: with confirmed=false (default) it returns a preview of the change without applying it; with confirmed=true it applies the change. Always scoped to the caller's tenant and ownership.
Output schemas not documented. LLMs cannot infer response structure for list_opportunities, get_activities, or get_audit_log. Breaks downstream tool chaining and forces agents to guess field names.
list_opportunities lacks pagination (limit, offset, cursor). No indication of result count or truncation. Large tenant datasets will blow context window; agents cannot iterate safely.
Error responses in dispatch.ts return generic text ('Unknown tool', 'error'). No recovery guidance. LLMs cannot self-correct or understand whether to retry, ask user, or abandon.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 61 | 2026-07-28+ | v2 |
update_opportunity_stage requires opaque opportunity IDs. No tool accepts human-friendly names (e.g., 'Acme Corp - Q4 Deal'). Agents must call get_activities or list_opportunities first to discover IDs, wasting round-trips.
No batch variants. If an agent needs to update 5 opportunity stages, it must call update_opportunity_stage 5 times sequentially. Batch tool would reduce latency and token waste.