@composer-app/mcp 0.0.1-beta.5 → 0.0.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,145 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@composer-app/mcp` are documented in this file.
4
+
5
+ This project uses pre-release semver under the `0.0.x-beta.N` track; each
6
+ release bumps the `beta.N` counter regardless of additive / breaking
7
+ distinction until the package graduates to `0.1.0`.
8
+
9
+ ## 0.0.1-beta.7 — 2026-04-29
10
+
11
+ Polish on the COM-26 ack/state work shipped in `0.0.1-beta.6` (which was
12
+ never published to npm). Adds an explicit "skip" signal and fixes the
13
+ top-bar avatar pulsing forever after a reply finished.
14
+
15
+ ### Added
16
+
17
+ - **New tool: `composer_done`.** Explicit signal for the skip path —
18
+ off-topic chatter, self-mention, or any case where the agent decides
19
+ not to reply. Accepts `{ roomId, threadId, replyId? }`. Clears the
20
+ thread-head `agentWork` placeholder published by
21
+ `RoomState.onEventDelivered` when `composer_next_event` dequeued the
22
+ mention; optionally also clears a per-reply entry. Idempotent.
23
+ SKILL.md tells the agent to call it whenever it skips.
24
+
25
+ ### Fixed
26
+
27
+ - **Top-bar PresenceBar avatar kept pulsing forever after a reply.**
28
+ `composer_next_event` publishes a thread-head
29
+ `agentWork(threadId, undefined, "thinking")` placeholder when it
30
+ dequeues a mention; the agent's ack reply then publishes a
31
+ reply-scoped entry. The `composer_agent_status({state: "ready"})`
32
+ handler cleared the reply-scoped entry but not the thread-head
33
+ placeholder, and `useAgentActivityByUser`'s max-severity reduction
34
+ kept the avatar pulsing.
35
+ - `performAgentStatus` ready branch now clears both
36
+ `(threadId, replyId)` and `(threadId, undefined)`.
37
+ - `performReplyComment` and `performReplySuggestion` also clear the
38
+ thread-head placeholder when posted with `state: "ready"` directly,
39
+ as defense in depth for callers that skip the state machine.
40
+
41
+ ## 0.0.1-beta.6 — 2026-04-28
42
+
43
+ Agent acknowledgment + live state indicators (COM-26). Layered onto the
44
+ socket-driven presence model from 0.0.1-beta.5 (PR #24); the awareness
45
+ heartbeat is no longer used — `agentWork` updates ride on the natural
46
+ tool-call cadence instead.
47
+
48
+ ### Added
49
+
50
+ - **New tool: `composer_agent_status`.** Lets the agent advance an
51
+ in-flight ack's state without re-sending text, and atomically rewrite
52
+ the ack at completion. Accepts `{ roomId, threadId, replyId?, state,
53
+ text?, note?, kind? }`. `kind` defaults to `"comment"`; set
54
+ `"suggestion"` to target a thread-head agent-authored suggestion. Omit
55
+ `replyId` to update a thread-head record directly; pass it to update a
56
+ reply inside the parent thread. Returns `{ id }` for thread-head
57
+ updates and `{ replyId }` for reply updates.
58
+ - **Optional `state` argument on `composer_reply_comment`,
59
+ `composer_reply_suggestion`, `composer_add_comment`, and
60
+ `composer_add_suggestion`.** Allowed values: `"thinking" | "working" |
61
+ "replying" | "ready"`. Absent = existing behavior (legacy callers
62
+ unaffected).
63
+ - **`invokerUserId` and `invokerName` on the `mention` event payload.**
64
+ Resolved server-side from the triggering reply's or thread's
65
+ `authorUserId` / `authorName`. For `solo_room`, this is the sole human
66
+ in awareness. Lets the model post an ack that `@-mentions` the invoker
67
+ back without a round-trip lookup.
68
+ - **Decision 9 filter on the mention observer.** `attachMentionObserver`
69
+ now skips `active_thread` and `solo_room` emissions when the
70
+ triggering reply has `authorIsAgent === true`. Direct `direct_mention`
71
+ triggers still fire cross-agent (explicit `@<otherAgent>` is
72
+ legitimate). Prevents multi-agent ping-pong amplification on every
73
+ state transition.
74
+ - **Truth-ordering + self-heal in `composer_agent_status`.** On `ready`
75
+ transitions the awareness `agentWork` entry is pruned BEFORE the CRDT
76
+ write, so a mid-op crash leaves at worst an ephemeral anomaly rather
77
+ than a stuck persisted state. Every `composer_agent_status` handler
78
+ call self-heals stale `agentWork` entries whose target record is
79
+ already `ready`.
80
+
81
+ ### Host-side SKILL.md
82
+
83
+ - **Ack-first behavior** — the model is now instructed to post a brief
84
+ ack reply (`state: "thinking"` with the invoker in the `mentions[]`
85
+ sidecar) before any substantive work on every mention it decides to
86
+ act on. The ack body is ≤ 24 chars — e.g., `@<invokerName> — on it`.
87
+ - **Generalized "standalone artifact IS the reply" rule.** The old
88
+ "suggestion IS the reply; do not also post a comment reply" guidance
89
+ is superseded by a rule covering any standalone artifact (suggestion,
90
+ cross-span comment on a different anchor, separate document). The ack
91
+ gets rewritten to a thin pointer on completion; no duplicate pointer
92
+ comment is posted.
93
+ - **Progress status section** documents `composer_agent_status`, the
94
+ `thinking → working → replying → ready` state machine, the UI's 400 ms
95
+ minimum-visible collapse window, and the 2-second silence ceiling
96
+ before a progress call is expected.
97
+ - **Worked example** added showing the full ack-then-suggestion flow
98
+ end to end.
99
+ - **Ask-then-auto-suggest reconciliation.** On a yes-variant
100
+ confirmation inside an active ask-then-suggest round-trip, the agent
101
+ skips the ack — the prior reply already delivered the "I heard you"
102
+ signal, and the suggestion is the immediate answer.
103
+
104
+ ### Changed
105
+
106
+ - **`addReply` and `addSuggestionReply` on the web side** now accept an
107
+ optional `{ silent?: boolean }` opts bag (propagated to
108
+ `emitActivity`). The MCP does not call these directly, but the
109
+ `composer_agent_status` handler passes `silent: true` internally for
110
+ every non-`ready` transition so the activity feed doesn't spam on
111
+ every `thinking → working → replying` flip. Default is
112
+ `silent: false` — existing callers unaffected.
113
+
114
+ ### Infrastructure
115
+
116
+ - **New `mcp/vitest.config.ts`** so `npm -w @composer-app/mcp run test`
117
+ no longer picks up the root `vitest.config.ts`'s `projects` list and
118
+ crashes. Fixes followup #3 for the mcp workspace.
119
+
120
+ ### Backward compatibility
121
+
122
+ All additions are strictly additive:
123
+
124
+ - Older clients that don't know the `state` field ignore it on read; the
125
+ ack reply's literal text still renders legibly with no indicator.
126
+ - Older models running a pre-ack-first SKILL.md continue to work — they
127
+ just don't call `composer_agent_status` and go straight from `thinking`
128
+ (if they use the new `state` arg) or a plain reply (if they don't) to
129
+ the final answer.
130
+ - Existing tool handlers preserve their v1 return shapes (`{id}` /
131
+ `{replyId}`); new fields are additive.
132
+ - The `mention` event payload's new `invokerUserId` / `invokerName`
133
+ fields are optional — older consumers that don't read them are
134
+ unaffected.
135
+
136
+ ### Rollback
137
+
138
+ Revert `0.0.1-beta.4` to `0.0.1-beta.3`. Browser clients rendering
139
+ `state`-bearing records posted before rollback fall through the
140
+ unknown-state branch and render the record's literal text — no crash, no
141
+ broken layout.
142
+
143
+ ## 0.0.1-beta.3 and earlier
144
+
145
+ Unreleased baseline. See git log for commit-level history.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @composer-app/mcp
2
+
3
+ MCP server for [Composer](https://usecomposer.app) — a realtime
4
+ collaborative markdown editor with first-class agent support.
5
+
6
+ The MCP server runs locally under an MCP-capable coding CLI (Claude Code
7
+ today; Codex / OpenCode adapters are follow-up work) and connects to a
8
+ Composer room as a standard Yjs peer. It exposes a small, typed tool
9
+ surface the model uses to read the doc, react to mentions, post
10
+ comments, and suggest edits — all through the same CRDT path humans use.
11
+
12
+ ## Install / setup
13
+
14
+ ```bash
15
+ npx @composer-app/mcp@latest setup
16
+ ```
17
+
18
+ Writes `~/.composer/user.json` (persistent machine identity +
19
+ self-chosen display name), registers `composer-mcp` with your coding
20
+ CLI, and installs the host-side SKILL prompt at
21
+ `~/.claude/skills/composer/SKILL.md`.
22
+
23
+ Restart your CLI once setup completes.
24
+
25
+ ## Tools
26
+
27
+ ### Read
28
+
29
+ - **`composer_attach_room`** — attach to an existing room by ID and
30
+ return a snapshot (full doc markdown, outline, version).
31
+ - **`composer_get_full_doc`** — fetch the current doc as markdown.
32
+ - **`composer_get_section`** — fetch a single section by `headingId`.
33
+ - **`composer_get_thread`** — fetch a thread's body + every reply +
34
+ anchored text + containing section markdown (for catch-up on
35
+ conversations the model was tagged into mid-flight).
36
+ - **`composer_next_event`** — block for up to `timeoutSec` (default
37
+ 600 s) waiting for a remote event. Returns `mention` or `timeout`.
38
+
39
+ The `mention` event carries the triggering text, the anchored doc
40
+ range, the containing section as markdown, and (new in 0.0.1-beta.4)
41
+ **`invokerUserId` + `invokerName`** — so the model can `@mention` the
42
+ invoker back without a client-side lookup. The `reason` field is one
43
+ of: `"direct_mention"`, `"active_thread"`, or `"solo_room"`.
44
+
45
+ ### Write
46
+
47
+ - **`composer_add_comment`** — comment anchored to text.
48
+ - **`composer_add_suggestion`** — propose a text replacement (lands as
49
+ pending).
50
+ - **`composer_reply_comment`** / **`composer_reply_suggestion`** — reply
51
+ on a thread.
52
+ - **`composer_resolve_thread`** — mark a thread resolved.
53
+ - **`composer_agent_status`** (new in 0.0.1-beta.4) — advance an
54
+ in-flight ack's state, and atomically rewrite the ack's text on
55
+ completion. See [Progress status](#progress-status) below.
56
+
57
+ ### Create / join rooms
58
+
59
+ - **`composer_create_room`** — create a new doc, optionally seeded from
60
+ a markdown file or inline string. Returns the browser URL so the
61
+ invoking human can open it.
62
+ - **`composer_join_room`** — attach to an existing room by browser URL.
63
+
64
+ ## Agent reply states
65
+
66
+ Every agent-authored reply, comment, or suggestion can carry an
67
+ optional `state` field (new in 0.0.1-beta.4):
68
+
69
+ | State | Meaning | UI |
70
+ |---|---|---|
71
+ | `thinking` | Ack posted, no tool use yet | Violet status dot + name-row shimmer |
72
+ | `working` | Agent is using tools | Blue dot + indeterminate bar under row |
73
+ | `replying` | Final response is being assembled / streamed | Teal dot + blinking caret |
74
+ | `ready` | Terminal; record holds the final content (or a pointer to a standalone artifact) | Neutral grey dot (no indicator) |
75
+
76
+ The UI also derives a `stalled` view when the agent's awareness
77
+ presence drops while a non-`ready` state persists for > 60 s (4× the
78
+ MCP's 15 s heartbeat) — amber dot with a slower pulse and an inline
79
+ "Retry" affordance.
80
+
81
+ Legacy records and older browsers ignore the `state` field and render
82
+ the literal reply text — no crash, no broken layout.
83
+
84
+ ## Progress status
85
+
86
+ The model drives state transitions through the `composer_agent_status`
87
+ tool. The canonical lifecycle:
88
+
89
+ ```text
90
+ # Mention arrives
91
+ composer_next_event() → { kind: "mention", threadId, invokerUserId, invokerName, ... }
92
+
93
+ # 1. Acknowledge first
94
+ composer_reply_comment({
95
+ roomId, threadId,
96
+ text: "@<invokerName> — on it",
97
+ mentions: [invokerUserId],
98
+ state: "thinking",
99
+ }) // → { replyId }
100
+
101
+ # 2. Advance state as you work (optional but recommended for >2 s spans)
102
+ composer_agent_status({
103
+ roomId, threadId, replyId,
104
+ state: "working",
105
+ note: "reading section 3…",
106
+ })
107
+
108
+ # 3. Land the substantive answer
109
+ composer_add_suggestion({ roomId, fromThreadId: threadId, replacementText: "…" })
110
+
111
+ # 4. Atomically rewrite the ack to a pointer + mark ready
112
+ composer_agent_status({
113
+ roomId, threadId, replyId,
114
+ state: "ready",
115
+ text: "Posted a suggestion below.",
116
+ })
117
+ ```
118
+
119
+ Key invariants:
120
+
121
+ - **Prune awareness before writing `ready` to the CRDT.** The handler
122
+ enforces this internally; a mid-op crash leaves at worst an ephemeral
123
+ anomaly, never a stuck persisted state masked by a stale heartbeat.
124
+ - **Self-heal on handler entry.** Every call prunes awareness entries
125
+ whose target record is already `ready`. Idempotent; converges on
126
+ retries.
127
+ - **Silent intermediate transitions.** Non-`ready` state flips pass
128
+ `silent: true` to the activity-feed path, so the feed only sees the
129
+ initial ack and the final `ready` rewrite.
130
+
131
+ See [`skill/SKILL.md`](./skill/SKILL.md) — shipped with the package and
132
+ installed into your CLI's skill directory by `setup` — for the full
133
+ host-side behavior contract (when to ack vs skip, when suggestion IS
134
+ the reply, ask-then-auto-suggest reconciliation, multi-agent etiquette).
135
+
136
+ ## Environment
137
+
138
+ The MCP reads two env vars (set by `setup` when registering with your
139
+ CLI):
140
+
141
+ - `COMPOSER_SERVER_HOST` — the Composer server host (e.g.,
142
+ `usecomposer.app` or `localhost:5173` for local dev).
143
+ - `COMPOSER_APP_BASE` — the base URL of the Composer web app (e.g.,
144
+ `https://usecomposer.app` or `http://localhost:5173`).
145
+
146
+ Identity is stored at `~/.composer/user.json` with mode `0600`.
147
+ Delete the file to reset; setup will prompt for a new name on next run.
148
+
149
+ ## Development
150
+
151
+ ```bash
152
+ cd mcp
153
+ npm run typecheck # tsc --noEmit -p .
154
+ npm run build # tsup → dist/cli.js + dist/mcp.js
155
+ npm run test # vitest run (uses mcp/vitest.config.ts; 131 tests as of 0.0.1-beta.4)
156
+ ```
157
+
158
+ ## License
159
+
160
+ MIT