Skip to content

Commit 3677cbe

Browse files
committed
docs: record the /mcp identity fixes
CAUCUS_PROJECT no longer influences the /mcp default; scope the README table row to the bridge and the native connector, which run one process per agent, and tell readers to name their agents explicitly when several share the hub.
1 parent 2293e91 commit 3677cbe

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,37 @@ and rename that heading to the version when you cut the release.
2222
now `mcp[cli]>=1.9,<2`, and a test asserts the ceiling stays in the published
2323
metadata. Lifting it means porting both modules to the 2.x server API first.
2424

25+
- **Two `/mcp` clients could merge into one identity.** The default `join` name
26+
was resolved once per *hub* process (from `CAUCUS_PROJECT`, else `mcp-client`),
27+
but one hub process serves every Streamable HTTP client, so every client that
28+
joined without an explicit `project` asked for that same name. A peer's liveness
29+
at the hub is its in-flight `/receive` long-poll, so between two polls the
30+
incumbent looked dead: `HubState.register` returned REPLACED rather than
31+
CONTESTED and handed the newcomer the *existing* `Client` record: same token,
32+
same inbox. Two agents, one identity, and no error raised anywhere.
33+
34+
The default name is now per session, taken from the MCP handshake's
35+
`clientInfo.name` (sanitized, falling back to `mcp-client`), and `join` refuses
36+
a name already held by another live session in the process with `name_in_use`.
37+
Note that `clientInfo.name` identifies the MCP *host*, not the agent: two
38+
sessions of the same host still collide, now explicitly. Pass
39+
`join(project=...)` whenever several agents share the hub.
40+
41+
`CAUCUS_PROJECT` no longer influences the `/mcp` default. It keeps naming the
42+
bridge and the native connector, which run one process per agent.
43+
44+
### Security
45+
46+
- **Reserved names were registrable over `/mcp`.** `join` bypasses `POST /register`
47+
to skip the per-host anti-flood bucket (it is the wrong brake for a trusted
48+
in-process caller), and in doing so it also skipped the `RegisterRequest`
49+
pydantic model that rejects the control-plane identities. An MCP client could
50+
`join(project="human")`, or `"hub"` / `"system"`, and fabricate operator
51+
authority in the `sender` field other agents read; the REST path answers 422 for
52+
exactly that reason. The two guards that model applied, the reserved-name
53+
rejection and the 1-64 character bound, are now re-applied on the `/mcp` path,
54+
returning `reserved_name` and `invalid_name`.
55+
2556
## [2.3.0](https://github.com/obeone/caucus-mcp/compare/v2.2.0...v2.3.0) (2026-07-25)
2657

2758
### Added

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ options, the security notes, and the manual route.
307307
| Variable | Default | Meaning |
308308
| --- | --- | --- |
309309
| `CAUCUS_HUB_URL` | `http://127.0.0.1:8765` | Hub the bridge and the native connector reach out to. Unused when the client speaks Streamable HTTP to `/mcp`, where the URL is the config. |
310-
| `CAUCUS_PROJECT` | working-dir basename | Name this agent registers under. Set it only when you want a name different from the directory, or when two checkouts share a basename. |
310+
| `CAUCUS_PROJECT` | working-dir basename | Name this agent registers under, for the bridge and the native connector (one process per agent). Set it only when you want a name different from the directory, or when two checkouts share a basename. Ignored over `/mcp`, where one hub process serves every client: there the name comes from the MCP handshake, or from `join(project=...)`. |
311311
| `CAUCUS_MCP_HTTP` | on for loopback | The Streamable HTTP MCP endpoint at `/mcp` is served by default on a loopback bind. Set to `0` to disable it, or to `1` to force it on a non-loopback bind (same as `--no-mcp-http` / `--mcp-http`). See [Connect over Streamable HTTP](#connect-over-streamable-http-no-bridge-subprocess). |
312312

313313
Hub flags: `caucus-hub --host <ip> --port <n>` (defaults `127.0.0.1:8765`). The
@@ -470,6 +470,12 @@ the hub, with a DNS-rebinding guard on the handshake.
470470
The session is keyed on the `Mcp-Session-Id` header, so many agents share the one
471471
hub process; a background sweep drops sessions that armed but never joined.
472472

473+
**Name your agents.** `join()` with no argument falls back to the name the MCP
474+
client announced at the handshake, which is the *host* ("claude-code", "codex"),
475+
not the agent. Two sessions of the same host therefore ask for the same name, and
476+
the second is refused with `name_in_use`. Pass `join(project="reviewer")`
477+
whenever more than one agent shares the hub.
478+
473479
### Which transport?
474480

475481
Both paths expose the **same tools, with the same schemas and the same

0 commit comments

Comments
 (0)