CedarCopilot wants to merge 6 commits into staging from fix/mcp-manufact-readiness
Live on prod, 1 of 12 surfaces degraded
Libra measured 1 of 12 production surfaces on prod. 11 surfaces had no requests at all. Libra checks hourly for 3 days after each deploy.
Fixes all 7 issues from Manufact's automated app-directory readiness check against Cedar's public MCP server (https://api.mail.cedarcopilot.com/mcp), ahead of submission to the Claude and OpenAI (Codex) app directories.
board) now declares a top-level title and annotations.{readOnlyHint,destructiveHint,openWorldHint} reflecting its real behavior, via one EXTERNAL_TOOL_METADATA table in server.ts that throws loudly if a tool is registered without an entry. The 5 individually-registered tools (load-skill, load-brain-skills, cedar-staff-debug, record-message, record-tool-call) each got their own title/annotations at their own registerTool call.document, table, config-read, debug, run-sql, conversation-read, mail) were trimmed. The reported byte counts turned out to be the source description plus the Armature telemetry hint Cedar's analytics wrapper appends when ANALYTICS_INGEST_API_KEY is set (171 bytes before this PR's telemetry patch, 156 after) , tool-description-length.test.ts was rewritten to measure real UTF-8 bytes (not .length, which undercounts every em dash/curly quote in this codebase's own house style) against budget minus that hint reserve, computed dynamically via appendTelemetryHint('') so it can't drift from the patched package. That stricter check caught two more real, previously-undetected bugs: board's description was also over budget (invisible to Manufact's live scan only because board has no scope mapping yet), and buildSessionInstructions's company-background truncation math was also .length-based and could land a few bytes over the real ceiling.@armature-tech/mcp-analytics@0.6.36 (via pnpm patch, same mechanism as the existing postgres/@superglue/client patches) so its telemetry.agent_thinking field description reads as genuinely optional rather than "include on every call... even when marked optional." The underlying Zod schema was already .optional(); only the coercive wording changed. Cedar's own first-party agents (including Cedar staff connecting their own Claude over external OAuth, which goes through this exact same code path) can keep sending it voluntarily.document into four risk-grouped tools: document-read (read/list/search), document-write, document-manage (delete/restore/move), document-share. The internal Mastra documentTool itself is untouched and stays in FAMILY_TOOLS unsplit, because that array is also consumed directly by the in-app chat harness (workflows/chat/harness/mcp-server.ts) and other first-party callers that must keep seeing one consolidated tool , the external registration loop in server.ts explicitly skips document there and registers the four split tools (document-external-tools.ts) in its place instead. scope-map.ts carries matching document-read/write/manage/share entries that preserve the exact per-action scope requirements the single document id used to resolve, plus a document entry retained solely for the in-app viewer-role gate.instructions text no longer echoes the caller's literal userId/orgId. Identity is already resolved server-side per request; no external tool's input schema asks the model to supply its own userId/orgId as an argument (no-bare-user-id-inputs.test.ts enforces this structurally , every surviving userId-shaped field is a deprecated targetUserId alias for cross-user delegation, never "tell us who you are"). One judgment call: run-sql's free-text SQL query genuinely needs the caller| Surface | Requests | Errors | p95 | Users | Verdict |
|---|---|---|---|---|---|
| db.getConversationIdForThread | 236 → 147 | 0 → 0 (0%) | 48 ms → 109 ms | 0 | Degraded Error rate is clean (0 of 147), but p95 latency more than doubled since the deploy. p95 109 ms, was 48 ms. |
| execute_tool update-conversation-fields | 3 → 0 | 0 → 0 (0%) | 5230 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| trpc.crm.getConversation | 5 → 0 | 0 → 0 (0%) | 1035 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| model_chunk Conversation Field Updater | 2 → 0 | 0 → 0 (0%) | 1 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| /api/trpc/crm.getConversation | 35 → 0 | 0 → 0 (0%) | 9532 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| POST /api/trpc/crm.getConversation | 23 → 0 | 0 → 0 (0%) | 9519 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| enrichment.enrichConversationContacts | 9 → 0 | 0 → 0 (0%) | 2443 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| /api/trpc/crm.searchConversationsMinimal | 3 → 0 | 0 → 0 (0%) | 9572 ms → not measured | 0 | No traffic No requests recorded since this deploy. |
| /api/trpc/crm.listConversationDocs | 0 → 0 | 0 → 0 (0%) | not measured | 0 | No traffic No requests recorded since this deploy. |
| /api/trpc/crm.getConversations | 0 → 0 | 0 → 0 (0%) | not measured | 0 | No traffic No requests recorded since this deploy. |
| /api/trpc/crm.getConversationFieldDefinitions | 0 → 0 | 0 → 0 (0%) | not measured | 0 | No traffic No requests recorded since this deploy. |
| crm.getConversation | 11 → 0 | 0 → 0 (0%) | 9392 ms → not measured | 0 | No traffic No requests recorded since this deploy. |