@composer-app/mcp 0.0.1-beta.4 → 0.0.1-beta.7

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/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  logError,
5
5
  startMcpHttpServer,
6
6
  startMcpServer
7
- } from "./chunk-UVXQZ2TN.js";
7
+ } from "./chunk-EJUJPBX2.js";
8
8
 
9
9
  // src/setup.ts
10
10
  import * as fs from "fs/promises";
package/dist/mcp.js CHANGED
@@ -1,8 +1,36 @@
1
1
  import {
2
+ __test_clearRooms,
3
+ __test_dispatch,
4
+ __test_setRoom,
5
+ _clearRoomsForTest,
6
+ _registerRoomForTest,
7
+ dispatchTool,
8
+ performAddComment,
9
+ performAddSuggestion,
10
+ performAgentStatus,
11
+ performDone,
12
+ performReplyComment,
13
+ performReplySuggestion,
14
+ performResolveThread,
2
15
  startMcpHttpServer,
3
- startMcpServer
4
- } from "./chunk-UVXQZ2TN.js";
16
+ startMcpServer,
17
+ teardownAllRooms
18
+ } from "./chunk-EJUJPBX2.js";
5
19
  export {
20
+ __test_clearRooms,
21
+ __test_dispatch,
22
+ __test_setRoom,
23
+ _clearRoomsForTest,
24
+ _registerRoomForTest,
25
+ dispatchTool,
26
+ performAddComment,
27
+ performAddSuggestion,
28
+ performAgentStatus,
29
+ performDone,
30
+ performReplyComment,
31
+ performReplySuggestion,
32
+ performResolveThread,
6
33
  startMcpHttpServer,
7
- startMcpServer
34
+ startMcpServer,
35
+ teardownAllRooms
8
36
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@composer-app/mcp",
3
- "version": "0.0.1-beta.4",
3
+ "version": "0.0.1-beta.7",
4
4
  "description": "Composer MCP",
5
5
  "license": "MIT",
6
6
  "author": "Josh Philpott",
@@ -10,7 +10,8 @@
10
10
  },
11
11
  "files": [
12
12
  "dist",
13
- "skill"
13
+ "skill",
14
+ "CHANGELOG.md"
14
15
  ],
15
16
  "keywords": [
16
17
  "mcp",
package/skill/SKILL.md CHANGED
@@ -69,7 +69,7 @@ just joined), then execute `step2_callTool`.
69
69
  ### 3. Monitor
70
70
  Triggers: "watch this doc", or automatically after join/create.
71
71
  Action: call `composer_next_event({ roomId })` in a loop (default timeout
72
- is 10 minutes). **The loop is always-on.** Every return carries a
72
+ is 30 seconds). **The loop is always-on.** Every return carries a
73
73
  structured directive — follow it without waiting for user input.
74
74
 
75
75
  On `mention`: handle the event (reply / suggestion / resolve as needed),
@@ -95,11 +95,13 @@ On `mention`, the event contains everything you need to act in one turn:
95
95
  threadKind: "comment" | "suggestion",
96
96
  threadText: "...", // the exact message that triggered you
97
97
  replyId?: "...", // present when it's a reply on an existing thread
98
- reason: "direct_mention" | "active_thread",
98
+ reason: "direct_mention" | "active_thread" | "solo_room",
99
99
  anchoredText?: "...", // the doc text the thread is anchored to
100
100
  headingId?: "...", // the section's headingId (use with write tools)
101
101
  headingText?: "...",
102
- sectionMarkdown?: "..." // full containing section as markdown
102
+ sectionMarkdown?: "...", // full containing section as markdown
103
+ invokerUserId?: "...", // userId of whoever triggered you (use in mentions[])
104
+ invokerName?: "..." // their display name (use in @mention literal)
103
105
  }
104
106
  ```
105
107
 
@@ -108,6 +110,38 @@ or `composer_add_comment` — no extra `composer_get_section` call is needed in
108
110
  the common case. Reach for `sectionMarkdown` to understand surrounding context
109
111
  before replying or suggesting.
110
112
 
113
+ **Ack first, then do the work.** On every `mention` event you intend to act
114
+ on, post a brief ack reply FIRST — before reading the full doc, before
115
+ drafting a suggestion. This is the "I heard you, I'm on it" signal; without
116
+ it the user stares at a silent sidebar while you think.
117
+
118
+ - Call `composer_reply_comment` (or `composer_reply_suggestion`) with
119
+ `state: "thinking"` and `mentions: [event.invokerUserId]`. For a brand-new
120
+ thread response use `composer_add_comment` / `composer_add_suggestion`
121
+ with the same `state` + `mentions` shape.
122
+ - Body: `@<invokerName> — on it` or equivalent terse phrasing (≤ 24 chars).
123
+ "On it.", "Looking.", "Checking.". No preamble, no promise of structure.
124
+ - Read `invokerUserId` and `invokerName` directly from the event payload
125
+ above — do NOT try to reconstruct them from `threadText` or from an
126
+ awareness lookup. They're the authoritative values the server resolved.
127
+ - **Skip the ack** for empty thank-yous or conversational dead-ends you
128
+ wouldn't reply to at all (see `reason` gates below) — the ack is a
129
+ signal of intent to act, not a reflex.
130
+ - **Skip the ack** when this mention is a yes-variant confirmation inside
131
+ an ask-then-auto-suggest round-trip (see §"Auto-suggest when the user
132
+ confirms a concrete proposal"): if the mention fired as `active_thread`
133
+ AND your own prior reply on this thread was a concrete yes/no
134
+ counter-proposal AND the human's message is a yes-variant, drop the
135
+ suggestion directly via `composer_add_suggestion` with
136
+ `fromThreadId: event.threadId`. The "I heard you" signal was already
137
+ delivered in the prior ack; a second ack here is noise. Only the
138
+ initial substantive-work mention gets an ack.
139
+
140
+ Once the ack is posted, drive state with `composer_agent_status` as you
141
+ work (see §"Progress status" below). On completion, rewrite the ack to
142
+ its final form in the same call that transitions to `ready` — do not
143
+ post a duplicate pointer reply.
144
+
111
145
  **The event only carries the triggering message.** If the thread already has
112
146
  replies (from the user, or from another agent), call `composer_get_thread({
113
147
  roomId, threadId })` before replying. The return has every reply with author
@@ -133,6 +167,16 @@ need to catch up on what's already been said.
133
167
  drafts they're jotting down).
134
168
  When in doubt, reply — the user can always ignore you.
135
169
 
170
+ **When you skip a mention, call `composer_done({ roomId, threadId })`.**
171
+ The instant a mention is dequeued via `composer_next_event`, the server
172
+ publishes a `thinking…` indicator on that thread so the user sees you
173
+ picked it up — there's no flicker waiting for your first
174
+ `composer_agent_status` call. If you reply, the `state: "ready"`
175
+ transition clears the indicator on its own. If you skip without
176
+ replying, nothing else clears it and the user's avatar pulses forever.
177
+ `composer_done` is idempotent — safe to call even if the indicator was
178
+ already cleared.
179
+
136
180
  ### 4. Act
137
181
  Triggers: direct requests like "add a summary to section 2".
138
182
  Action: already attached; call the write tools and report back concisely.
@@ -149,13 +193,25 @@ Read tools:
149
193
 
150
194
  Write tools:
151
195
  - `composer_add_comment` — NEW comment on any span in the doc. Use when
152
- raising something outside the current thread's anchor.
196
+ raising something outside the current thread's anchor. Accepts an
197
+ optional `state` ("thinking" on an ack) so the first-ever reply on a
198
+ thread can be the ack.
153
199
  - `composer_add_suggestion` — propose a text replacement (lands as
154
200
  pending). Can target any span — `fromThreadId` inherits the source
155
201
  thread's anchor; `anchor` specifies a span elsewhere. Call it multiple
156
202
  times in a turn to suggest in several spots.
157
203
  - `composer_reply_comment` / `composer_reply_suggestion` — reply on an
158
- existing thread.
204
+ existing thread. Accept an optional `state` field for the ack-first
205
+ flow; post with `state: "thinking"` and the invoker in `mentions[]`.
206
+ - `composer_agent_status` — drive state transitions (`thinking →
207
+ working → replying → ready`) and rewrite the final ack body on the
208
+ reply/comment/suggestion you own. See §"Progress status".
209
+ - `composer_done` — clear the live indicator on a thread you decided
210
+ NOT to reply to. Required when you skip a mention (off-topic chatter,
211
+ self-mention, conversational dead-end), because the indicator is
212
+ published optimistically the moment a mention is dequeued. Replying
213
+ with `state: "ready"` clears it on its own — only call `composer_done`
214
+ for the skip case.
159
215
  - `composer_resolve_thread` — mark resolved.
160
216
 
161
217
  There is no "just edit" tool in v1. All text changes go through suggestions
@@ -169,11 +225,18 @@ a chat window. Long replies get unwieldy fast. Rules:
169
225
  - Answer in 1–3 sentences. Prefer one.
170
226
  - Reply directly to the question asked — no preamble ("Great question!"),
171
227
  no restating the ask, no trailing summary of what you just did.
172
- - **If you post a suggestion in response to the thread, the suggestion IS
173
- your reply. Do not also post a comment reply.** The suggestion renders
174
- as a Replace/With card in the sidebar already; a pointer comment ("see
175
- the suggestion") just duplicates what the user can already see.
176
- Silent suggestion-only responses are correct and expected.
228
+ - **When the substantive answer is a standalone artifact, that artifact
229
+ IS the reply.** A "standalone artifact" means a suggestion, a cross-span
230
+ comment on a different anchor, or a separate document link. In that
231
+ case do NOT post a duplicate pointer comment instead, rewrite your
232
+ existing ack in place to a thin pointer (`"Posted a suggestion below."`,
233
+ `"Added a comment in Section 3."`, `"See doc <link>."`) and set
234
+ `state: "ready"` on the same call. The suggestion / cross-span comment
235
+ renders as its own card; a pointer reply just duplicates what the user
236
+ can already see.
237
+ - **When the substantive answer IS a reply with text**, rewrite the ack
238
+ to that text and clear the state (or set `state: "ready"`) in the same
239
+ `composer_agent_status` call. Do not post a separate follow-up reply.
177
240
  - If the answer genuinely needs structure (a list of 4+ items, code, a
178
241
  table) and a suggestion isn't the right shape, post it as a suggestion
179
242
  in the doc body instead of as a comment reply.
@@ -298,8 +361,10 @@ Two turns, not three:
298
361
  2. **Turn 2 (commit on confirmation).** When the user replies with any
299
362
  variant of yes ("yes", "sure", "go for it", "perfect", a thumbs-up
300
363
  emoji), call `composer_add_suggestion` with `fromThreadId: event.threadId`
301
- and the concrete replacement. Do NOT also post a comment reply — the
302
- suggestion card IS your reply (see "Keep comment text terse" above).
364
+ and the concrete replacement. **Skip the ack in this case** — the
365
+ prior reply already delivered the "I heard you" signal, and the
366
+ suggestion card IS your answer. Do NOT also post a comment reply
367
+ (see §"Keep comment text terse" and the ack-skip rule in §"Monitor").
303
368
 
304
369
  If the user says no / picks a different value / redirects, follow their
305
370
  lead — do not post the original proposal anyway.
@@ -309,6 +374,108 @@ abstract to guess a number), ask a clarifying question instead. Don't
309
374
  propose something generic just to fill the slot — "Would you like me
310
375
  to shorten this?" is worthless without a target length.
311
376
 
377
+ ### Progress status
378
+
379
+ Once the ack is posted, drive the owning reply through a small state
380
+ machine using `composer_agent_status`. The UI animates state changes
381
+ on the reply card; the user sees progress instead of a silent pause.
382
+
383
+ ```
384
+ composer_agent_status({
385
+ roomId,
386
+ threadId,
387
+ replyId?, // identifies which reply you own; omit for thread-head
388
+ state, // "thinking" | "working" | "replying" | "ready"
389
+ text?, // rewrite the reply body (only meaningful on "ready")
390
+ note?, // short human-readable progress line
391
+ kind? // "comment" | "suggestion" — disambiguates head records
392
+ })
393
+ ```
394
+
395
+ State machine: **`thinking → working → replying → ready`**.
396
+
397
+ - `thinking` — initial ack, set by the reply/add tool that posted it.
398
+ - `working` — you're doing substantive work (reading the doc, computing,
399
+ drafting). Set this whenever you expect a gap.
400
+ - `replying` — you're about to write the final text. Optional, brief.
401
+ - `ready` — done. Call with `text: "<final ack body>"` to rewrite the
402
+ ack in place atomically; the awareness heartbeat for this entry is
403
+ pruned in the same call.
404
+
405
+ **Minimum-visible rule.** The UI collapses state changes that happen
406
+ faster than 400 ms, so don't worry about being too fast. DO worry about
407
+ being SILENT for more than ~2 seconds without calling
408
+ `composer_agent_status({ state: "working" })` — silence is the failure
409
+ mode. If you're about to do something slow (fetch the full doc, compute
410
+ a non-trivial diff, call another tool), transition to `working` first.
411
+
412
+ Use `note` to surface human-readable progress where it helps — e.g.
413
+ `note: "Reading section 3…"`, `note: "Drafting suggestion…"`,
414
+ `note: "Checking cross-references…"`. Short sentence fragments; the
415
+ user reads them at a glance.
416
+
417
+ On completion, one call does everything:
418
+
419
+ ```
420
+ composer_agent_status({
421
+ roomId, threadId, replyId,
422
+ state: "ready",
423
+ text: "Posted a suggestion below."
424
+ })
425
+ ```
426
+
427
+ This rewrites the ack body and prunes the awareness heartbeat
428
+ atomically. Do NOT post a separate reply to say "done" — the rewrite
429
+ IS the final reply.
430
+
431
+ ### Worked example — ack-then-suggestion flow
432
+
433
+ A mention arrives; the user asked you to tighten a sentence in Section 3.
434
+ The full round-trip is four tool calls:
435
+
436
+ ```
437
+ // 1. Mention arrives via composer_next_event:
438
+ // { kind: "mention", threadId: "t_abc", invokerUserId: "u_jess",
439
+ // invokerName: "Jess", reason: "direct_mention", ... }
440
+
441
+ // 2. Ack first — posts the "on it" reply with a thinking indicator.
442
+ const { replyId } = composer_reply_comment({
443
+ roomId,
444
+ threadId: "t_abc",
445
+ text: "@Jess — on it",
446
+ mentions: ["u_jess"],
447
+ state: "thinking",
448
+ });
449
+
450
+ // 3. Transition to working before any slow step.
451
+ composer_agent_status({
452
+ roomId,
453
+ threadId: "t_abc",
454
+ replyId,
455
+ state: "working",
456
+ note: "Reading section 3…",
457
+ });
458
+
459
+ // 4. Do the work and post the substantive artifact.
460
+ composer_add_suggestion({
461
+ roomId,
462
+ fromThreadId: "t_abc",
463
+ replacementText: "…",
464
+ });
465
+
466
+ // 5. Rewrite the ack to a thin pointer and mark ready atomically.
467
+ composer_agent_status({
468
+ roomId,
469
+ threadId: "t_abc",
470
+ replyId,
471
+ state: "ready",
472
+ text: "Posted a suggestion below.",
473
+ });
474
+ ```
475
+
476
+ No extra pointer reply. No "done" message. The rewrite + suggestion
477
+ card together are the complete response.
478
+
312
479
  ## Anchors
313
480
 
314
481
  Write tools take: