internal/mcp/mcp_test.go
Ref: Size: 2.6 KiB
package mcp
import (
"bytes"
"context"
"encoding/json"
"strings"
"testing"
)
func TestSendChannelEmitsCorrectFrame(t *testing.T) {
var buf bytes.Buffer
s := NewWithIO(strings.NewReader(""), &buf)
err := s.SendChannel(context.Background(), ChannelMeta{
Source: "filewatch",
File: "src/foo.ts",
Line: "42",
ReplyTo: "fw-a1b2c3d4",
}, "@claude write a test for foo")
if err != nil {
t.Fatal(err)
}
var frame map[string]any
if err := json.NewDecoder(&buf).Decode(&frame); err != nil {
t.Fatal(err)
}
if frame["jsonrpc"] != "2.0" {
t.Errorf("jsonrpc = %v, want 2.0", frame["jsonrpc"])
}
if frame["method"] != "notifications/claude/channel" {
t.Errorf("method = %v, want notifications/claude/channel", frame["method"])
}
params := frame["params"].(map[string]any)
if params["content"] != "@claude write a test for foo" {
t.Errorf("content = %v", params["content"])
}
meta := params["meta"].(map[string]any)
if meta["source"] != "filewatch" || meta["file"] != "src/foo.ts" || meta["replyTo"] != "fw-a1b2c3d4" {
t.Errorf("meta wrong: %#v", meta)
}
// line MUST be a string — Claude Code's schema is Record<string, string> and
// silently rejects non-string values.
if s, ok := meta["line"].(string); !ok || s != "42" {
t.Errorf("line = %v (%T), want \"42\" (string)", meta["line"], meta["line"])
}
}
func TestInitializeAdvertisesChannelCapability(t *testing.T) {
in := strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"0"}}}` + "\n")
var out bytes.Buffer
s := NewWithIO(in, &out)
if err := s.Run(context.Background()); err != nil {
t.Fatal(err)
}
var resp map[string]any
if err := json.NewDecoder(&out).Decode(&resp); err != nil {
t.Fatalf("decode: %v\nstdout was: %s", err, out.String())
}
if resp["id"] != float64(1) {
t.Errorf("id = %v", resp["id"])
}
result, ok := resp["result"].(map[string]any)
if !ok {
t.Fatalf("no result: %#v", resp)
}
if result["protocolVersion"] != "2025-11-25" {
t.Errorf("protocolVersion echo wrong: %v", result["protocolVersion"])
}
caps, _ := result["capabilities"].(map[string]any)
exp, _ := caps["experimental"].(map[string]any)
if _, has := exp["claude/channel"]; !has {
t.Errorf("missing experimental.claude/channel capability: %#v", caps)
}
instructions, _ := result["instructions"].(string)
if instructions == "" {
t.Error("missing instructions field")
}
if !strings.Contains(instructions, "remove") {
t.Errorf("instructions don't mention marker removal: %q", instructions)
}
}