diff options
| author | Claude <noreply@anthropic.com> | 2026-05-24 09:38:06 +0000 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-05-24 09:38:06 +0000 |
| commit | 54f6631c28a8b85f6f874e17822549faba916a38 (patch) | |
| tree | bfd2a45e77cdbe60ca95362caf90721da6c8ea22 /internal/executor/local_test.go | |
| parent | c7d95f3992d24f86ff71e5f3e18260a8ef8a09f0 (diff) | |
feat(executor): per-task agent MCP server + token registry (Phase 2)
Adds the agent-facing MCP transport foundation: a Registry that mints a
per-task bearer token bound to a freshly built MCP server exposing the four
agent tools (ask_user, report_summary, spawn_subtask, record_progress), and
an HTTP handler (StreamableHTTP) that resolves the token to that server. The
server never trusts an agent-supplied task ID — context comes from the token.
The default storeChannel now buffers summary and question signals under a
mutex (an MCP tool call lands on an HTTP-handler goroutine mid-run), exposing
ReportedSummary/PendingQuestion. The pool flushes the buffered summary onto
the execution after the run, replacing the runner's direct exec.Summary write
and keeping the read race-free.
ask_user follows the record-and-resume model: it buffers the question, returns
ErrAgentBlocked, and the tool tells the agent to end its turn; the run blocks
and resumes later via claude --resume (no live slot held).
Tests cover registry lifecycle, in-memory tool dispatch, and HTTP end-to-end
with bearer auth (valid token dispatches; invalid token rejected).
Not yet wired into the runners or mounted on the API server — next increment.
https://claude.ai/code/session_01SESwn7kQ7oP62trWw6pc39
Diffstat (limited to 'internal/executor/local_test.go')
| -rw-r--r-- | internal/executor/local_test.go | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/internal/executor/local_test.go b/internal/executor/local_test.go index 8aa8791..ffe87f9 100644 --- a/internal/executor/local_test.go +++ b/internal/executor/local_test.go @@ -72,7 +72,7 @@ func TestLocalRunner_Run_WritesStreamJSON(t *testing.T) { } exec := &storage.Execution{ID: uuid.New().String(), TaskID: tt.ID} - if err := r.Run(context.Background(), tt, exec, newStoreChannel(nil, tt.ID, exec)); err != nil { + if err := r.Run(context.Background(), tt, exec, newStoreChannel(nil, tt.ID)); err != nil { t.Fatalf("Run: %v", err) } @@ -121,7 +121,7 @@ func TestLocalRunner_Run_NoClient_Errors(t *testing.T) { r := &LocalRunner{LogDir: t.TempDir()} tt := &task.Task{ID: "x", Agent: task.AgentConfig{Instructions: "hi"}} exec := &storage.Execution{ID: "exec-x"} - err := r.Run(context.Background(), tt, exec, newStoreChannel(nil, tt.ID, exec)) + err := r.Run(context.Background(), tt, exec, newStoreChannel(nil, tt.ID)) if err == nil || !strings.Contains(err.Error(), "no LLM client") { t.Errorf("expected 'no LLM client' error, got %v", err) } @@ -134,7 +134,7 @@ func TestLocalRunner_Run_EmptyInstructions_Errors(t *testing.T) { } tt := &task.Task{ID: "x", Agent: task.AgentConfig{}} exec := &storage.Execution{ID: "exec-x"} - err := r.Run(context.Background(), tt, exec, newStoreChannel(nil, tt.ID, exec)) + err := r.Run(context.Background(), tt, exec, newStoreChannel(nil, tt.ID)) if err == nil || !strings.Contains(err.Error(), "empty instructions") { t.Errorf("expected empty-instructions error, got %v", err) } |
