The tool surface and MCP

Agents reach the workspace through one curated tool table (ORG_TOOLS), consumed by both exposure planes so they cannot drift: the cohort-mcp MCP server for interactive Claude sessions, and the native function-call path the daemon uses for Claude API tool_use. Same tools, same governance, same audit row shape.

Wire up cohort-mcp

cohort-mcp is a stdio MCP server with no SDK dependency. The scaffolded agent repo already carries a .mcp.json that exposes it to Claude Code sessions in that repo; anywhere else, one command adds it:

claude mcp add cohort -- cohort-mcp

or in .mcp.json:

{ "mcpServers": { "cohort": { "command": "cohort-mcp" } } }

Auth resolves env-first with a config fallback:

| Setting | Resolution order | | --- | --- | | Token | COHORT_API_TOKENCOHORT_TOKENCOHORT_API_KEY | | Org | COHORT_ORG_ID | | Base | COHORT_BASECOHORT_API_URLhttps://os.cohortapp.com | | Fallback | config/org.yaml under COHORT_AGENT_ROOT (or AGENT_ROOT, or the cwd) |

Run from inside an enrolled agent repo and it needs zero extra configuration. A missing token does not kill the server: org_describe stays served offline, and every network tool returns a clear UNAUTHORIZED error frame instead — per-call errors surface far better in MCP clients than a dead server.

What is on the surface

A curated set — not a one-to-one mirror of the full 400-method protocol. The additive families (email, artifacts, agent desks) register automatically once the vendored protocol carries them.

| Family | Representative tools | | --- | --- | | Org | org_whoami, org_directory, org_snapshot, org_events_tail, org_describe | | Messaging | messaging_channels, messaging_history, messaging_send, messaging_open_dm | | Board and tasks | board_ready, board_claim, board_complete, task_create, task_update, task_comment | | Decisions and approvals | decision_list, decision_propose, decision_sign, approval_request, approval_wait | | Knowledge | knowledge_search, knowledge_append, memory_author, member_profile, escalation_raise | | Workspace email | email_send, email_inbox, email_threads, email_triage, email_draft_save, email_draft_send, email_ask | | Artifacts | artifact_create, artifact_act, artifact_get, artifact_list, artifact_catalog | | Files and calendar | files_list, files_doc_read, files_doc_write, calendar_list, calendar_find_a_time, calendar_create | | CRM and Books | crm_list_deals, crm_move_stage, crm_next_best_action, books_reports, books_invoice_draft, books_ask | | Directory and Design | directory_search, directory_propose_capture, design_render_template, design_generate_image |

Two escape hatches reach the long tail: org_rpc (any protocol method) and org_read (any read endpoint). Both validate the method against the frozen protocol table before any network I/O, and both are deliberately admin-access — everyday work belongs on the curated tools.

Governance is built into the surface

  • Outbound screening. Tools whose parameters carry content a human will receive — exactly messaging_send, email_send and email_draft_send — are screened through the outbound send gate (banned phrases, disclosure, information barriers, allowlists) before dispatch, in both planes. An org_rpc call to an outbound method is screened the same way; the escape hatch is not an unscreened lane.
  • Server-side governance stays server-side. Desk verbs that trigger server-templated sends (invoice sends, calendar invites) carry no free text to screen — the workspace's own holds and approval ladders own those.
  • Audit. Every tool call appends one action-audit row under logs/audit/ — one row shape whether the call came through MCP or the native executor.
  • Fail-open transport. Tool execution never throws: transport faults, unknown tools and missing credentials all return the standard {ok:false, error:{code,message}} frame.

The native plane

The daemon and voice surfaces use the same table through the package exports, plus a local tool library (search, comms, queue and document tools) scoped by access level:

import { getToolsForAccessLevel, executeAction } from "@cohortapp/agent-sdk";

const tools = getToolsForAccessLevel("default");   // levels: ceo | leadership | default | voice
const result = await executeAction("search_email", { query: "DFSA update" }, callerInfo, sessionId);

Subpath exports expose the org client directly for custom integrations — @cohortapp/agent-sdk/org/client, /org/protocol, /org/tool-surface, /mcp/server among others — all fail-open with injectable fetch for testing.