@brightbeamai/chap-coordinator-mcp 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -19
- package/dist/cli.cjs +338 -41
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +341 -39
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +485 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -8
- package/dist/index.d.ts +92 -8
- package/dist/index.js +486 -21
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +40 -3
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +1 -1
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +40 -3
- package/dist/schemas.js.map +1 -1
- package/dist/tools.cjs +27 -6
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +17 -1
- package/dist/tools.d.ts +17 -1
- package/dist/tools.js +26 -6
- package/dist/tools.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,33 +9,54 @@ Spec target: **MCP 2026-07-28**, serving MCP 2025-11-25 clients as well · CHAP
|
|
|
9
9
|
|
|
10
10
|
## Run it as a server
|
|
11
11
|
|
|
12
|
-
The package ships a stdio server, so an MCP client can use
|
|
13
|
-
code.
|
|
12
|
+
The package ships a stdio server, `chap-mcp-server`, so an MCP client can use
|
|
13
|
+
CHAP without any code. It makes every call as one participant, by default the
|
|
14
|
+
agent `agent:assistant`, as a create-chap-app mcp-gate project names its
|
|
15
|
+
agent, and offers the tools an agent's own work needs.
|
|
16
|
+
A person decides in a desk, never through the assistant, so the server
|
|
17
|
+
refuses to act as a `human:` participant and refuses a call that names
|
|
18
|
+
anyone else.
|
|
19
|
+
|
|
20
|
+
The usual setup has a CHAP server with a desk, such as a project made with
|
|
21
|
+
`npx create-chap-app my-gate --template mcp-gate`, and points the stdio server
|
|
22
|
+
at that server's JSON-RPC endpoint. The assistant's work then waits for the
|
|
23
|
+
person in the desk:
|
|
14
24
|
|
|
15
25
|
```json
|
|
16
26
|
{
|
|
17
27
|
"mcpServers": {
|
|
18
28
|
"chap": {
|
|
19
29
|
"command": "npx",
|
|
20
|
-
"args": ["-y", "@brightbeamai/chap-coordinator-mcp"
|
|
21
|
-
|
|
30
|
+
"args": ["-y", "@brightbeamai/chap-coordinator-mcp",
|
|
31
|
+
"--url", "http://127.0.0.1:8792/chap", "--as", "agent:assistant"]
|
|
22
32
|
}
|
|
23
33
|
}
|
|
24
34
|
}
|
|
25
35
|
```
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
| Option | Environment | Effect |
|
|
38
|
+
|--------|-------------|--------|
|
|
39
|
+
| `--as <uri>` | `CHAP_PARTICIPANT` | The participant every call is made as. Default `agent:assistant`. `from` is filled in, a call naming anyone else is refused, and a `human:` URI is refused at start. |
|
|
40
|
+
| `--tools agent\|all` | | `agent`, the default, offers `AGENT_MCP_TOOLS` (below). `all` offers every method, still as the one participant. |
|
|
41
|
+
| `--url <endpoint>` | | Forward every call to a running CHAP server's JSON-RPC endpoint, such as a desk server's `http://127.0.0.1:8792/chap`. That server keeps the workspaces, its store and its profiles. A call waits 30 seconds for its answer. |
|
|
42
|
+
| `--db <path>` | `CHAP_DB_PATH` | Keep the workspaces in this SQLite file. A leading `~` is the home directory. |
|
|
43
|
+
| `--profiles <list>` | `CHAP_PROFILES` | Comma-separated profiles for new workspaces. Defaults to Core plus review, whisper, deliberation, handoff, control, routing, modes and audit-scitt, so the audit chain is on from the first entry. |
|
|
44
|
+
| `--help` | | Print the options and exit. |
|
|
45
|
+
|
|
46
|
+
An option takes its value as `--as agent:x` or `--as=agent:x`, and wins over
|
|
47
|
+
its environment variable; an empty variable counts as unset.
|
|
29
48
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
49
|
+
Without `--url` the server runs its own coordinator. With neither `--url` nor
|
|
50
|
+
`--db` it keeps everything in memory, says so on stderr, and every workspace
|
|
51
|
+
is lost when the client restarts. The agent tools create no workspace and
|
|
52
|
+
join no one, so a coordinator of its own suits a store that already holds the
|
|
53
|
+
workspace, or `--tools all`, where the assistant creates a workspace and
|
|
54
|
+
joins it as itself.
|
|
34
55
|
|
|
35
56
|
Persistence needs `better-sqlite3`, an optional native dependency of the
|
|
36
|
-
coordinator. If `CHAP_DB_PATH` is set and it cannot be loaded the
|
|
37
|
-
so and exits
|
|
38
|
-
was asked to keep.
|
|
57
|
+
coordinator. If `--db` or `CHAP_DB_PATH` is set and it cannot be loaded the
|
|
58
|
+
server says so and exits, so it never starts in memory and discards the
|
|
59
|
+
decisions it was asked to keep.
|
|
39
60
|
|
|
40
61
|
The server is also listed in the [MCP registry](https://registry.modelcontextprotocol.io)
|
|
41
62
|
as `io.github.BrightbeamAI/chap`.
|
|
@@ -46,21 +67,25 @@ as `io.github.BrightbeamAI/chap`.
|
|
|
46
67
|
npm install @brightbeamai/chap-coordinator-mcp
|
|
47
68
|
```
|
|
48
69
|
|
|
49
|
-
Node
|
|
70
|
+
Node 22+ required. Runtime dependencies: `@brightbeamai/chap-coordinator`,
|
|
50
71
|
`@modelcontextprotocol/sdk` and `zod`. The binary is `chap-mcp-server`.
|
|
51
72
|
|
|
52
73
|
## Quick start
|
|
53
74
|
|
|
54
75
|
```typescript
|
|
55
76
|
import { Coordinator } from "@brightbeamai/chap-coordinator";
|
|
56
|
-
import { makeChapMcpServer } from "@brightbeamai/chap-coordinator-mcp";
|
|
77
|
+
import { AGENT_MCP_TOOLS, makeChapMcpServer } from "@brightbeamai/chap-coordinator-mcp";
|
|
57
78
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
58
79
|
|
|
59
80
|
const coord = new Coordinator({
|
|
60
81
|
defaultProfiles: ["core/1.0", "review/1.0", "audit-scitt/1.0"],
|
|
61
82
|
});
|
|
62
83
|
|
|
63
|
-
|
|
84
|
+
// The assistant's own connection: every call as the agent, with its tools.
|
|
85
|
+
const server = makeChapMcpServer(coord, {
|
|
86
|
+
participant: "agent:assistant",
|
|
87
|
+
toolFilter: (name) => AGENT_MCP_TOOLS.has(name),
|
|
88
|
+
});
|
|
64
89
|
await server.connect(new StdioServerTransport());
|
|
65
90
|
```
|
|
66
91
|
|
|
@@ -68,14 +93,130 @@ The returned `Server` is the official MCP SDK's `Server` class with
|
|
|
68
93
|
`tools/list` and `tools/call` handlers pre-registered. Attach any MCP
|
|
69
94
|
transport (stdio, Streamable HTTP, etc.).
|
|
70
95
|
|
|
96
|
+
`makeChapMcpServer(coord, options)` takes:
|
|
97
|
+
|
|
98
|
+
| Option | Effect |
|
|
99
|
+
|--------|--------|
|
|
100
|
+
| `participant` | Make every call as this participant: `from` is filled in, and a call that names another `from` is refused. The tools list `from` as optional, with this URI in its description. A `human:` URI is refused, as the commands refuse it. |
|
|
101
|
+
| `toolFilter` | `(toolName) => boolean`. A tool the filter excludes is left out of the list and refused when called by name. |
|
|
102
|
+
| `name`, `version` | What the server reports to its client. |
|
|
103
|
+
| `envelopeIdFactory` | The id of each envelope a tool call sends. |
|
|
104
|
+
|
|
105
|
+
Without `participant` or `toolFilter`,
|
|
106
|
+
`makeChapMcpServer(coord, { name: "chap", version: "0.3.5" })` serves every
|
|
107
|
+
tool and each call names its own `from`. That suits a connection that stands
|
|
108
|
+
for no one member, such as a service of your own that authenticates its
|
|
109
|
+
callers and checks the `from` they send.
|
|
110
|
+
|
|
111
|
+
`coord` is a `Coordinator`, or any object with the same
|
|
112
|
+
`dispatch(envelope)`, returning the response or a promise of it.
|
|
113
|
+
|
|
114
|
+
`AGENT_MCP_TOOLS` is the agent's set: `chap.task.create`, `chap.task.update`,
|
|
115
|
+
`chap.task.complete`, `chap.whisper.ask`, `chap.decide.approve`,
|
|
116
|
+
`chap.decide.reject`, `chap.decide.override`, `chap.review.acknowledge`,
|
|
117
|
+
`chap.workspace.describe`, `chap.audit.read` and `chap.audit.verify_chain`.
|
|
118
|
+
The decision tools and `chap.review.acknowledge` stay for work the agent is
|
|
119
|
+
asked to review; on its own work the coordinator refuses them with `-32011`
|
|
120
|
+
and records the attempt. It leaves out the tools that choose who reviews the
|
|
121
|
+
agent's work or pass its review on: `chap.review.request`, whose `to` can
|
|
122
|
+
name the agent itself, `chap.escalate.raise`, which passes the open review
|
|
123
|
+
on to a successor task, and every workspace, membership and control method.
|
|
124
|
+
`chap.task.update` stays, so the agent can take a task under review back to
|
|
125
|
+
`in_progress`, which withdraws its draft; its next completion opens the
|
|
126
|
+
review again, so a task that holds a review completes only through a
|
|
127
|
+
decision. A task with no review open completes when the agent completes it,
|
|
128
|
+
so set `review_required` on any task the agent must not finish alone.
|
|
129
|
+
|
|
71
130
|
A runnable reference server is at
|
|
72
131
|
[`reference/mcp-server-ts/`](../../reference/mcp-server-ts/).
|
|
73
132
|
|
|
133
|
+
## Serve it over HTTP
|
|
134
|
+
|
|
135
|
+
`chapMcpHttpHandler` answers MCP on an HTTP endpoint such as `/mcp`, for a
|
|
136
|
+
Node `http` request and response, in both protocol eras:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
function chapMcpHttpHandler(
|
|
140
|
+
coord: ChapDispatcher,
|
|
141
|
+
options?: ChapMcpHttpOptions,
|
|
142
|
+
): (req: IncomingMessage, res: ServerResponse, parsedBody?: unknown) => Promise<void>;
|
|
143
|
+
|
|
144
|
+
interface ChapMcpHttpOptions extends ChapMcpOptions {
|
|
145
|
+
maxBodyBytes?: number; // default 1,114,112: 1 MiB, and 64 KiB of MCP around it
|
|
146
|
+
allowedHosts?: string[]; // Host values served besides the loopback names, such as "mcp.internal:8792"
|
|
147
|
+
allowedOrigins?: string[]; // origins a browser page may call from, such as "http://localhost:6274"
|
|
148
|
+
timeoutMs?: number; // default 45,000
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`options` are those of `makeChapMcpServer`, so `participant` and
|
|
153
|
+
`toolFilter` hold over HTTP as on stdio. Pass `parsedBody` when your server
|
|
154
|
+
has read and parsed the body already; otherwise the handler reads it, up to
|
|
155
|
+
`maxBodyBytes`.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { createServer } from "node:http";
|
|
159
|
+
import { AGENT_MCP_TOOLS, chapMcpHttpHandler } from "@brightbeamai/chap-coordinator-mcp";
|
|
160
|
+
|
|
161
|
+
const mcp = chapMcpHttpHandler(coord, {
|
|
162
|
+
participant: "agent:assistant",
|
|
163
|
+
toolFilter: (name) => AGENT_MCP_TOOLS.has(name),
|
|
164
|
+
});
|
|
165
|
+
createServer((req, res) => {
|
|
166
|
+
if (new URL(req.url ?? "/", "http://localhost").pathname === "/mcp") return mcp(req, res);
|
|
167
|
+
res.writeHead(404).end();
|
|
168
|
+
}).listen(8792, "127.0.0.1");
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The handler answers under the server's own names only, as the MCP
|
|
172
|
+
specification requires against DNS rebinding: a `Host` other than
|
|
173
|
+
127.0.0.1, localhost or [::1] on the port the request came in on is refused
|
|
174
|
+
with 421, and a browser's request from another origin with 403. Name the
|
|
175
|
+
host a proxy passes in `allowedHosts`, and the origin of a page that calls
|
|
176
|
+
the server, such as the MCP Inspector's, in `allowedOrigins`; such a page
|
|
177
|
+
also needs the CORS headers, which the server the handler is mounted in
|
|
178
|
+
sends. Anything but POST is refused with 405, in both eras.
|
|
179
|
+
|
|
180
|
+
A request with no `MCP-Protocol-Version` header, or a handshake-era version
|
|
181
|
+
(2025-11-25 and earlier), goes to the SDK's Streamable HTTP transport, with
|
|
182
|
+
a server and a transport for each request and no session. A batch is
|
|
183
|
+
refused with 400, as every revision since 2025-06-18 refuses one, and so is
|
|
184
|
+
a body whose envelope declares a protocol version the header does not name
|
|
185
|
+
(`-32020`). Any other version takes the 2026-07-28 binding: one POST
|
|
186
|
+
carrying one JSON-RPC request, answered with one JSON object. Its headers
|
|
187
|
+
are checked against its body, and an error carries that binding's status:
|
|
188
|
+
400 for a malformed request, a mismatched header (`-32020`) or an
|
|
189
|
+
unsupported version (`-32022`), 404 for a method it does not serve. The
|
|
190
|
+
SDK's transport alone refuses every 2026-07-28 request with 400.
|
|
191
|
+
|
|
192
|
+
A request with no answer within `timeoutMs` is answered with an internal
|
|
193
|
+
error, `-32603`, and one whose client has gone stops waiting at once.
|
|
194
|
+
|
|
195
|
+
## What an unsigned local server trusts
|
|
196
|
+
|
|
197
|
+
MCP calls carry no signature. `participant` keeps a connection to one name,
|
|
198
|
+
and that is all it does: the stdio server trusts the process that started it,
|
|
199
|
+
so whatever the client sends is made as the participant. The log records
|
|
200
|
+
that the call came through this connection; it does not show that the
|
|
201
|
+
participant signed it. Anything that can start the command, or reach a
|
|
202
|
+
server's MCP endpoint, acts as that participant.
|
|
203
|
+
|
|
204
|
+
With `--url`, the server at the other end applies its own rules to each call.
|
|
205
|
+
One that requires a signature on every call refuses these with `-32070`; one
|
|
206
|
+
that requires signatures from people only accepts the agent's unsigned calls.
|
|
207
|
+
The pin covers this connection only. A CHAP server without signatures
|
|
208
|
+
accepts a call in any member's name from any local process, so the record of
|
|
209
|
+
a person's decision holds only where the server requires the desk's
|
|
210
|
+
signature on it.
|
|
211
|
+
|
|
74
212
|
## What gets exposed
|
|
75
213
|
|
|
76
|
-
|
|
214
|
+
Every CHAP method becomes an MCP tool named `chap.<method>`. Each
|
|
77
215
|
tool's `inputSchema` is the JSON Schema for the corresponding method's
|
|
78
|
-
params. Tool descriptions are tuned for LLM consumption.
|
|
216
|
+
params. Tool descriptions are tuned for LLM consumption. A call naming no
|
|
217
|
+
CHAP tool is a protocol error, `-32602` "Unknown tool: <name>", as the MCP
|
|
218
|
+
tools specification has it; a CHAP tool the server leaves out is refused
|
|
219
|
+
as a tool result, as the coordinator's refusals are.
|
|
79
220
|
|
|
80
221
|
The `chap.` prefix avoids collisions with other MCP servers a client
|
|
81
222
|
might have loaded simultaneously.
|
|
@@ -103,7 +244,9 @@ npm test
|
|
|
103
244
|
|
|
104
245
|
The integration tests use the SDK's `InMemoryTransport.createLinkedPair()`
|
|
105
246
|
to drive a wrapped Coordinator end-to-end, exercising every major
|
|
106
|
-
profile.
|
|
247
|
+
profile. `cli.test.ts` starts `chap-mcp-server` the way an MCP client does,
|
|
248
|
+
with and without `--url`, and `http.test.ts` serves both protocol eras over
|
|
249
|
+
HTTP.
|
|
107
250
|
|
|
108
251
|
## See also
|
|
109
252
|
|