…#495 #496)
Three follow-ups to the v0.0.200 MCP capstone (#490), bundled.
## subscribe (#494)
Streaming tool. Response switches to SSE (text/event-stream) and emits
MCP notifications as resources change. WAC-filtered per event so the
subscriber only sees resources they have Read access to. Wraps the
existing resourceEvents EventEmitter from src/notifications/events.js.
Path scope: trailing slash = subtree, exact = single resource, default
= whole pod (filtered by access). Cleanup on client disconnect.
## read_acl / write_acl (#496)
Structured ACL editing as first-class MCP tools. Eliminates hand-rolled
JSON-LD for the most common owner operation (delegating an agent
access). read_acl returns parsed authorizations (agents, agentClasses,
modes, isDefault). write_acl persists the same shape via
serializeAcl() in src/wac/parser.js.
Both require acl:Control on the resource.
## call_remote_pod (#495)
Federation primitive — invoke MCP tools on another pod by URL. Locked
in for v1 with conservative defaults:
- Federation gate path is derived from the agent's WebID:
<agent-pod>/private/federation/. Caller must have acl:Write there.
Foreign or anonymous identities can't federate.
- No pod-resident credential storage — every call carries its own
auth (bearer / custom header / anonymous).
- Depth cap via MCP-Federation-Depth header, hardcoded max 3.
Calls remote /mcp endpoint, forwards tools/call, returns the response.
30s timeout. Per-call audit via the existing log infrastructure.
## Tool count
12 → 17. Surface still graspable in one read. CRUD / Skills / Docs /
Introspect / ACL / Subscribe / Federation.
## Tests
26 passing (was 18). New coverage:
- read_acl returns owner authorizations on a fresh pod ACL
- write_acl + read_acl round-trip with 3 agent classes
- write_acl denied without Control
- call_remote_pod denied without federation gate (anonymous)
- call_remote_pod hits its own /mcp through the gate
- call_remote_pod depth cap enforcement (header at 3 → next hop fails)
- subscribe streams a 'subscribed' init event + resource_changed events
- subscribe filters by scope (out-of-scope changes are silent)
## Docs
docs/mcp.md expanded with sections for all three: structured ACL
ergonomics, subscribe SSE wire format with curl example, federation
worked example. 'What's not included' section pruned (these were the
items it deferred to).
Three follow-ups to the v0.0.200 MCP capstone (#490), bundled into one PR as discussed.
Closes #494 (subscribe), #495 (call_remote_pod), #496 (read_acl/write_acl).
What's in
subscribe (#494) — streaming change notifications
Streaming tool. Response switches to SSE; emits MCP notifications as resources change. WAC-filtered per event. Wraps the existing `resourceEvents` from `src/notifications/events.js`. Path scope: trailing slash = subtree, exact = single resource. Cleanup on client disconnect.
read_acl / write_acl (#496) — structured ACL editing
Structured form abstracts JSON-LD shape:
```json
{
"path": "/private/notes/",
"authorizations": [
{ "agents": ["did:nostr:abc..."], "modes": ["Read","Append"], "isDefault": true },
{ "agentClasses": ["acl:AuthenticatedAgent"], "modes": ["Read"] }
]
}
```
Round-trips via `src/wac/parser.js`. Requires `acl:Control` on the resource.
call_remote_pod (#495) — federation primitive
Design calls locked in as discussed:
Tool count
12 → 17. Still graspable in one read.
Tests
26 passing (was 18). New coverage:
```
ℹ pass 26
ℹ fail 0
```
Test plan