@brightbeamai/chap-coordinator-mcp
OfficialThis server exposes a CHAP coordinator for auditable collaboration between humans and agents across workspaces, tasks, reviews, decisions, and controls.
Create and describe workspaces, set enabled profiles, and manage mode ceilings.
Add or remove participants and rotate or revoke signing keys.
Create tasks, update task state, submit outputs, and use idempotent retries.
Request reviews and approve, reject, override with JSON Patch, or abstain.
Read the audit log and verify the hash chain or SCITT receipts.
Run quick questions with whispers and group decisions with deliberations.
Propose, accept, or decline handoffs between participants or groups.
Pause, resume, cancel, snapshot, roll back, supersede, and route tasks.
Evaluate review depth and auto-escalation from routing hints.
Collaborative Human-Agent Protocol (CHAP)
Work is moving towards teams in which hundreds of agents and hundreds of people take part. Agents draft, triage and recommend; people approve, correct, overrule and escalate. Those human decisions make the work accountable, yet they are usually recorded only in chat threads, ticket comments and memory.
CHAP gives those decisions a defined structure and an auditable record: what the agent produced, what the person decided and why, in one log that can be queried and verified.
Structured overrides. When a person edits an agent's work, the edit is recorded with a diff, a rationale and tags your team defines, so corrections can be analysed later.
A tamper-evident log. Every change is recorded in order. With the hash chain on, an altered entry is detected on verification, provided a recent chain head is kept out of the operator's reach (
SECURITY.md§5). The history survives key rotation, expired vendor logs and staff changes.Signatures and identity. Optional extensions, called profiles, require every call to be signed, bind signing keys to verified identities, and submit entries to a transparency log.
Alongside MCP and A2A. MCP connects agents to tools and A2A connects agents to each other; CHAP records the work agents share with people.
Concepts
CHAP is a protocol: a set of calls that agents, people and their tools send to a coordinator, the service that mediates a workspace. The coordinator checks each call against the workspace's rules, answers it, and records each accepted change in the workspace's audit log. The TypeScript and Python packages below are coordinators, to embed in your own process or run as a server.
Term | What it is |
Workspace | A named context for one body of work: its members, its tasks, and the profiles it uses |
Participant | A member, named by a URI such as |
Task | A unit of work, assigned to a participant |
Artefact | What a participant produces in a task: a draft, a decision, an override |
Envelope | One call, as a JSON-RPC 2.0 message; its |
Audit log | The ordered record of every change to the workspace; with the hash chain on, each entry carries the hash of the one before it |
The verbs read as their names. A task is created, completed and sent for review (task.create, task.complete, review.request). A reviewer approves, rejects or overrides it (decide.approve, decide.reject, decide.override). Work is handed off or escalated (handoff.propose, escalate.raise), and audit.read returns the record.
Core and profiles. Every coordinator implements Core (core/1.0): workspaces, participants, tasks and the audit log. Everything else is an optional profile, named with its version. review/1.0 adds review requests and decisions. Others add quick questions to a person (whisper/1.0), group votes (deliberation/1.0), handoffs (handoff/1.0), pausing and rollback (control/1.0), routing (routing/1.0), shadow and trial modes (modes/1.0), signed calls (security-signed/1.0), verified identities (identity-oidc/1.0, identity-vc/1.0) and a transparency log (audit-scitt/1.0). A workspace advertises the profiles it uses, and the coordinator refuses calls that belong to any other profile. core/SPEC.md fits Core on one page, profiles/PROFILES.md lists the profiles, and GLOSSARY.md defines every term.
Related MCP server: Cordum_io
Install
For | Run |
TypeScript |
|
Python |
|
A new project with a review desk |
|
Each package implements Core and every profile; a new workspace advertises core/1.0 and review/1.0 unless you name others. START_HERE.md is the full guide: a first recorded decision from a clone of this repository, with Python alone, and every other way in.
The override envelope
The override envelope records a reviewer's change to an agent's output. Each field is annotated below:
intent_preserved separates a refining override, which keeps the agent's decision and rewrites its wording, from a substituting one, which decides differently. tags are the small vocabulary a team agrees on and counts by later. ABOUT.md says what each points at.
Status
CHAP 0.3 is a public draft: a small Core and optional profiles (SPECIFICATION.md), with reference coordinators in TypeScript and Python that a differential fuzzer checks answer and log alike. Before 1.0 a minor release may break things, and its changelog lists each break with a migration. ABOUT.md says what is built so far, and ROADMAP.md sets out the milestones to 1.0.
Read this next
Read | For |
A first recorded decision, and every way in | |
The 90-second tour: a clip of one review, and the code for each step in TypeScript and Python | |
Scenarios, from a solo developer with Cursor to GMP-regulated manufacturing | |
How Core and the profiles are adopted, what the override envelope's fields point at, how CHAP sits with MCP and A2A, the standards it reuses, the status and the repository's layout | |
Core and every profile, in one document | |
Core, on one page | |
The optional profiles, and how they combine | |
Every term | |
The packages, the framework bridges and other implementations | |
The audit log as pandas tables and charts | |
The threat model, and how to report a vulnerability | |
What changed in each release | |
How to contribute; | |
The architecture, profile semantics and threat model, with the scenarios as JSON traces |
Cite
If you reference CHAP in academic or technical work, please cite the technical report.
@techreport{chap2026,
author = {Shahid, Arsalan and Suttie, Gordon and Black, Philip},
title = {Collaborative Human-Agent Protocol (CHAP): An open protocol for auditable, structured multi-human and multi-agent collaboration},
institution = {Brightbeam AI},
year = {2026},
type = {Technical Report},
number = {arXiv:2606.09751},
url = {https://arxiv.org/abs/2606.09751}
}CC BY 4.0 (specification text, see LICENSE-SPEC.md) · Apache 2.0 (everything else) · Any language, any deployment.
Available Tools
39 toolschap.abstain.declareAbstain from reviewA
Stand aside from a review this participant cannot decide, for example on a conflict of interest or for want of context. Use it in place of chap.decide.reject, which judges the work; abstaining judges nothing and leaves the artefact intact. It requires an open review addressed to the caller and answers -32010 otherwise. The task moves to abstained, which is not terminal: chap.escalate.raise or a fresh chap.review.request can still carry it forward.
Inputs: from (required), reason (required), task_id (required), workspace (required), +1 more.
chap.audit.readRead audit logARead-onlyIdempotent
Read entries from a workspace's audit log, optionally within a sequence range and filtered by method, sender, task or outcome. This is the query surface for everything the workspace has recorded, decisions and overrides included. An accepted call is under 'envelope'. A refused attempt the coordinator recorded, such as a decision by someone the review was not addressed to, is under 'request' with an 'outcome' giving the error code, and it did not take effect. Reads only: it records nothing, so it never appears in its own output. Entries come back as recorded, without aggregation, so grouping them, by tag for instance, is the reader's job.
Inputs: workspace (required), range, filter.
chap.audit.submit_to_scittAnchor audit log in SCITTARead-only
Build COSE_Sign1-shaped statements for a range of audit entries and submit them to the configured SCITT transparency service. Where none is configured the statements are returned for submission out of band.
Inputs: workspace (required), from, range, issuer.
chap.audit.verify_chainVerify audit chainARead-onlyIdempotent
Replay a workspace's prev-hash chain. Only status verified with ok true means the log was checked and is intact. Status not_evaluated with ok false means part of the log was never checked, so its integrity is unknown and must not be reported as verified; entries_unchecked says how much. An error means the chain is broken or absent.
Inputs: workspace (required), from.
chap.audit.verify_receiptVerify SCITT receiptARead-onlyIdempotent
Check a receipt returned by an external SCITT transparency service, confirming that service logged the statement. This is the external half of verification; chap.audit.verify_chain checks the local prev-hash chain instead, and the two answer different questions. Verification is delegated to a hook the deployment supplies and fails closed with -32082 when none is configured, so a missing verifier can never read as a pass. Reads only: nothing is recorded.
Inputs: receipt (required), workspace (required), from.
chap.control.cancelCancel taskADestructive
Cancel a task. Cancelled is terminal, and a task that has already settled cannot be cancelled.
Inputs: from (required), task_id (required), workspace (required), reason.
chap.control.pausePause workAIdempotent
Pause work. Scoped to a task it moves that task to paused; to a participant it stops new tasks being assigned to them; to the workspace it refuses every method except describing, reading the audit log, joining, leaving and resuming.
Inputs: from (required), workspace (required), scope, reason, +3 more.
chap.control.resumeResume workA
Resume work paused at the same scope: a task returns to the state it held at the pause, a participant can be assigned tasks again, a workspace returns to active.
Inputs: from (required), workspace (required), scope, task_id, +1 more.
chap.control.rollbackRoll back to snapshotADestructive
Restore workspace state from a snapshot. The mode ceiling and member roles are restored; the rollback is appended to the audit log rather than rewriting it.
Inputs: from (required), workspace (required), to_snapshot_artefact_id (required), reason, +1 more.
chap.control.set_mode_ceilingSet mode ceilingAIdempotent
Set the highest operating mode tasks in this workspace may request. A task above the ceiling is refused.
Inputs: from (required), workspace (required), new_ceiling (required), reason.
chap.control.snapshotSnapshot workspaceA
Capture the workspace state as an artefact before a change you may want to undo. Returns a snapshot artefact id, which is the only thing chap.control.rollback accepts as a target; the label is for a human reading the log and cannot be used to select it. Taking a snapshot changes nothing. Note that a rollback restores only the mode ceiling and member roles, so a snapshot is a narrow safety net rather than a general undo.
Inputs: from (required), workspace (required), label, include.
chap.control.supersedeSupersede taskADestructive
Replace a task with a successor in one call. The original moves to superseded and stays linked to its replacement. The successor requires review when the original did, whatever its own review_required says, and a paused assignee is refused with -32063.
Inputs: from (required), task_id (required), workspace (required), successor_task (required), +1 more.
chap.decide.approveApproveA
Approve the artefact under review as it stands. Only a reviewer the review was addressed to may decide, and anyone else is refused with -32011. The task completes once the review's rule is satisfied, so under all_approve or quorum:N an approval may leave the review open awaiting others. Use chap.decide.override instead to accept a corrected version.
Inputs: from (required), task_id (required), workspace (required), tags, +2 more.
chap.decide.overrideOverride with a correctionA
Correct the artefact under review with an RFC 6902 JSON Patch and accept the result. The patch, the rationale and any tags are recorded together, so the audit log holds what was changed and why, rather than only that the work was not accepted as written.
Inputs: diff (required), from (required), task_id (required), rationale (required), +6 more.
chap.decide.rejectRejectA
Reject the artefact under review. The task is declined, or returns to in_progress when request_revision is set, which is the choice between ending the work and sending it back. Only a reviewer the review was addressed to may decide. Use chap.decide.override instead to correct the artefact and accept it rather than refuse it.
Inputs: from (required), task_id (required), workspace (required), tags, +3 more.
chap.deliberate.closeClose deliberationAIdempotent
Close a deliberation and compute its outcome from the votes cast under its rule. Closing is final: further comments and votes are refused with -32032. Calling it again on a closed deliberation returns the same outcome and changes nothing, so a retry is safe. Votes never cast are simply absent from the tally rather than counted as abstentions.
Inputs: from (required), workspace (required), deliberation_id (required).
chap.deliberate.commentComment on deliberationA
Record a comment on an open deliberation, so the reasoning behind a vote survives alongside the tally. Comments carry no weight in the outcome and may be added by any participant at any point before chap.deliberate.close.
Inputs: from (required), comment (required), workspace (required), deliberation_id (required).
chap.deliberate.openOpen deliberationA
Open a group decision among several participants under a stated voting rule: any_one_approves, all_approve, quorum:N, weighted_vote:T or weighted_vote_with_veto:T. Use it where several people must weigh in on one question; chap.review.request is the tool for judging one participant's output. Returns a deliberation_id that the comment, vote and close tools all take. The participant list, rule, weights and veto map are fixed at open, so the terms are settled before anyone votes. No outcome exists until chap.deliberate.close computes one.
Inputs: to (required), from (required), rule (required), workspace (required), +5 more.
chap.deliberate.voteVote in deliberationA
Cast a yea, nay or abstain in an open deliberation. Each participant votes once and a second attempt answers -32031, so a vote cannot be revised. An abstention is recorded but counts as neither side, which under all_approve or quorum:N withholds the approval those rules need. The tally is computed later, by chap.deliberate.close.
Inputs: from (required), vote (required), workspace (required), deliberation_id (required), +3 more.
chap.escalate.autoEvaluate auto-escalationA
Evaluate a task's routing hints against the escalation policy and report whether it should be escalated, and to whom. It decides only: a route_decision artefact is recorded but the task does not move, so act on the answer with chap.escalate.raise. The default policy escalates on criticality critical, or high with confidence below 0.6.
Inputs: from (required), task_id (required), workspace (required), default_escalation_target.
chap.escalate.raiseEscalate taskA
Hand a task upwards, where the current holder can act but should not decide alone. The original moves to escalated and is linked to a new task for whoever takes it on. Use chap.handoff.propose to pass work sideways without that link, and chap.control.supersede to replace a task outright. The successor starts with an empty input unless one is supplied, so restate whatever the new assignee needs. A completed, cancelled or superseded task cannot be escalated. The successor takes the mode new_task.mode names, or else the original's, and requires review when the original did, or when it is a trial task under modes/1.0. A paused assignee is refused with -32063, and a mode above the workspace's current ceiling with -32040; after the ceiling is lowered, name a mode within it.
Inputs: from (required), new_task (required), workspace (required), original_task_id (required).
chap.handoff.acceptAccept handoffA
Accept a proposed handoff. The accepted tasks are reassigned to the accepting participant, which is the point at which responsibility actually moves. Omitting accepted_task_ids accepts all of them. Accepting resolves the proposal for good: a later accept or decline on the same handoff answers -32051. A paused participant cannot accept and is refused with -32063.
Inputs: from (required), workspace (required), handoff_id (required), comment, +1 more.
chap.handoff.declineDecline handoffA
Decline a proposed handoff. The tasks stay with the proposer and nothing is reassigned, which is the difference from chap.handoff.accept. Declining resolves the proposal for good: a later accept or decline on the same handoff answers -32051, and trying again means a fresh proposal. The reason is recorded so the proposer can route the work elsewhere.
Inputs: from (required), workspace (required), handoff_id (required), reason, +1 more.
chap.handoff.proposePropose handoffB
Propose handing one or more tasks to another participant or a group, with the context needed to pick them up. Every task must currently be assigned to the proposer.
Inputs: to (required), from (required), tasks (required), workspace (required), +2 more.
chap.participant.joinAdd participantBIdempotent
Add a participant to a workspace. The type given, human, agent, service, group or workspace, decides whether they are eligible to review work that requires it.
Inputs: from (required), type (required), workspace (required), role, +1 more.
chap.participant.leaveRemove participantADestructiveIdempotent
Remove a participant from a workspace. Entries they have already written stay in the audit log and their past signatures still verify, so leaving is not a way to withdraw a decision. They can no longer be assigned tasks or act as a reviewer, and returning means a fresh chap.participant.join.
Inputs: from (required), workspace (required).
chap.participant.revoke_keyRevoke signing keyADestructive
Revoke a signing key, for example after a device is lost. Signatures presented with it are refused from then on. Revoking another participant's key requires the admin role.
Inputs: kid (required), from (required), workspace (required), target_uri (required), +1 more.
chap.participant.rotate_keyRotate signing keyA
Retire a participant's signing key and register its replacement. The old key stays in the key history with a valid_until timestamp, so envelopes it signed still verify.
Inputs: from (required), new_jwk (required), old_kid (required), workspace (required).
chap.review.depthDecide review depthA
Decide how much review a task warrants, skip, spot_check or full, from its routing hints. Records a route_decision artefact giving the rule that produced the answer.
Inputs: from (required), task_id (required), workspace (required), artefact_routing_hints.
chap.review.requestRequest reviewA
Open a review on a task and address it to one or more reviewers. They then call chap.decide.approve, chap.decide.reject, chap.decide.override or chap.abstain.declare. Repeating the request with the same artefact adds reviewers to the open review.
Inputs: to (required), from (required), task_id (required), artefact (required), +3 more.
chap.task.completeSubmit task outputA
Submit a task's output. A task that requires review does not complete: the output is held as the artefact under review, the task moves to review_requested, and a reviewer decision completes it. Any other task completes immediately.
Inputs: from (required), task_id (required), workspace (required), output, +2 more.
chap.task.createCreate taskA
Create a task: a unit of work assigned to one participant. Set review_required to make completion depend on a reviewer decision rather than on the assignee. Returns a task_id that the rest of the lifecycle takes. Supplying idempotency_key is what makes a retry safe: a repeat carrying a key already seen returns the original task, while a repeat without one creates a second task. Assigning to a paused member is refused.
Inputs: from (required), kind (required), input (required), assignee (required), +6 more.
chap.task.routeRoute task to an assigneeA
Choose an assignee for a task from a list of candidates and reassign the task to the one selected. Use it where the choice itself belongs on the record: it writes a route_decision artefact naming the policy, the candidate chosen and the alternatives passed over, none of which is captured by setting assignee directly on chap.task.create. The default policy takes the first candidate that is a workspace member and is not paused. An operator-supplied policy may choose on any basis, and the call is refused with -32510 when its choice is not a member, or with -32063 when its choice is paused.
Inputs: from (required), task_id (required), workspace (required), candidates (required).
chap.task.updateChange task stateA
Move a task to a new state. Only the transitions in the specification's lifecycle table are accepted, and a task that requires review cannot be completed here.
Inputs: from (required), state (required), task_id (required), workspace (required), +1 more.
chap.whisper.answerAnswer a whisperA
Answer an open whisper. Where the question carried options, the answer must name one of their ids and any other value is refused with -32022. A whisper already answered is refused with -32020 and one past its deadline with -32021, in which case the default supplied at chap.whisper.ask has already been applied.
Inputs: from (required), workspace (required), whisper_id (required), answer, +2 more.
chap.whisper.askAsk a whisperA
Put one question to one or more participants, with a deadline and a default. If the deadline passes unanswered the default applies, so a task is never blocked waiting on a reply.
Inputs: to (required), from (required), task_id (required), question (required), +5 more.
chap.workspace.createCreate workspaceA
Create a workspace: the container for the participants, tasks and audit log of one piece of collaborative work. Returns the workspace id, which every later call carries.
Inputs: mode, profiles, workspace, mode_ceiling.
chap.workspace.describeDescribe workspaceARead-onlyIdempotent
Report the current state of a workspace: its members, enabled profiles, audit length, and task and override counts.
Inputs: workspace (required).
chap.workspace.set_profilesReplace workspace profilesADestructive
Replace the whole set of profiles enabled on a workspace. The list is not merged: any profile absent from it is dropped, so send the complete set you want. Enabling audit-scitt/1.0 on a workspace that already has entries starts the chain from that point and leaves the earlier entries permanently outside it, which chap.audit.verify_chain then reports as not_evaluated. Prefer setting profiles at chap.workspace.create, where no entries exist yet to strand.
Inputs: profiles (required), workspace (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 39 tools
The namespace.subdomain.verb convention combined with very precise descriptions keeps most tools distinct, and the tricky near-neighbors (decide.reject vs abstain.declare, escalate.raise vs handoff.propose vs control.supersede, verify_chain vs verify_receipt) are explicitly contrasted. The one soft spot is the trio of routing-hint tools (task.route, review.depth, escalate.auto), which all consume the same hints and differ mainly in whether they act or only decide; an agent could still reach for the wrong one.
Every tool follows the same chap.<domain>.<verb> pattern with snake_case multiword names (set_profiles, rotate_key, verify_chain, set_mode_ceiling, submit_to_scitt). The per-domain grouping is predictable and no competing convention appears anywhere in the set.
39 tools is heavy for any single agent to hold in context, and by the rubric's thresholds it sits in the 'too many' band. However, the server genuinely spans roughly a dozen sub-domains (workspace, participant, task, review, decision, escalation, whisper, deliberation, handoff, control, audit), each needing its own lifecycle calls, so the surface is broad rather than padded.
Coverage is unusually thorough: full lifecycles for workspaces, participants, tasks, reviews, deliberations, handoffs, control operations and cryptographic audit verification, with idempotency and error codes documented. Minor gaps remain around enumeration/discovery, e.g. no explicit list_workspaces or list_tasks, leaving audit.read to serve as the general query surface.
Maintenance
Related MCP Connectors
Human-in-the-loop review and approval for AI agents. Audit trail, approval policies, native MCP.
Human approvals, autonomy limits and a chained audit log for the AI agents of service firms.
Runtime AI governance: decision gates, human approval, hash-chained audit, compliance mapping.
The decision layer for AI agents: act, escalate or refuse, and every decision comes back signed.
Related MCP Servers
- AlicenseBqualityBmaintenanceGovernance Intelligence Architecture (GIA) is a governance-first MCP server that provides approval gates, auditable decision logs, compliance mapping, and operational safety controls for Claude agents. It connects Claude Desktop and Claude Code to a hosted governance control plane for secure, production-grade AI workflows.53293 npm5-
- FlicenseNot gradedqualityBmaintenanceThe Control Plane for Autonomous AI Enforce policy before execution, require human approvals where risk demands it, and keep a full audit trail — from first action to final result.510-
- AlicenseNot gradedqualityDmaintenanceA human-in-the-loop governance interlock for AI agents. Agents propose changes, a human countersigns the exact plan, and then it executes stage by stage with precondition checks, verification, and auditing.Apache 2.0
- FlicenseNot gradedqualityDmaintenanceEnables AI coding agents to evaluate actions against team-defined policies, record decisions, and obtain human approvals for potentially risky operations.63 npm1-