b4909168
feat(mcp): advertise channel handling protocol via init instructions
a73x 2026-04-29 09:06
Commit message
internal/mcp/mcp.go
| Old | New | ||
|---|---|---|---|
| @@ -8,6 +8,18 @@ import ( | |||
| 8 | "sync" | 8 | "sync" |
| 9 | ) | 9 | ) |
| 10 | 10 | ||
| 11 | // channelInstructions is advertised in the initialize response so Claude | ||
| 12 | // Code adds it to the session prompt. Tells Claude how to handle filewatch | ||
| 13 | // channel messages. | ||
| 14 | const channelInstructions = `Channel messages arrive when a developer writes @claude in a code comment. ` + | ||
| 15 | `params.content is the marker text starting with "@claude". ` + | ||
| 16 | `params.meta has file (path), line (1-indexed line number, as a string), ` + | ||
| 17 | `and replyTo (e.g. "fw-a1b2c3d4") — the UUID already stamped into the comment as @claude[<replyTo>]. ` + | ||
| 18 | `To handle a message: read the file at meta.file around line meta.line, ` + | ||
| 19 | `do what the developer asked (answer in-session for questions, edit code for tasks), ` + | ||
| 20 | `then use Edit to remove the entire @claude[<replyTo>] marker line so the file returns to a clean state. ` + | ||
| 21 | `There is no reply tool — line removal is the ack.` | ||
| 22 | |||
| 11 | // ChannelMeta is the meta block of a notifications/claude/channel notification. | 23 | // ChannelMeta is the meta block of a notifications/claude/channel notification. |
| 12 | // All fields are strings: Claude Code's schema is Record<string, string> and | 24 | // All fields are strings: Claude Code's schema is Record<string, string> and |
| 13 | // silently drops non-string values. | 25 | // silently drops non-string values. |
| @@ -92,6 +104,7 @@ func (s *Server) Run(ctx context.Context) error { | |||
| 92 | "name": "filewatch-mcp", | 104 | "name": "filewatch-mcp", |
| 93 | "version": "0.1.0", | 105 | "version": "0.1.0", |
| 94 | }, | 106 | }, |
| 107 | "instructions": channelInstructions, | ||
| 95 | }) | 108 | }) |
| 96 | default: | 109 | default: |
| 97 | s.writeError(id, -32601, "method not found") | 110 | s.writeError(id, -32601, "method not found") |
internal/mcp/mcp_test.go
| Old | New | ||
|---|---|---|---|
| @@ -71,4 +71,11 @@ func TestInitializeAdvertisesChannelCapability(t *testing.T) { | |||
| 71 | if _, has := exp["claude/channel"]; !has { | 71 | if _, has := exp["claude/channel"]; !has { |
| 72 | t.Errorf("missing experimental.claude/channel capability: %#v", caps) | 72 | t.Errorf("missing experimental.claude/channel capability: %#v", caps) |
| 73 | } | 73 | } |
| 74 | instructions, _ := result["instructions"].(string) | ||
| 75 | if instructions == "" { | ||
| 76 | t.Error("missing instructions field") | ||
| 77 | } | ||
| 78 | if !strings.Contains(instructions, "remove") { | ||
| 79 | t.Errorf("instructions don't mention marker removal: %q", instructions) | ||
| 80 | } | ||
| 74 | } | 81 | } |