MCP Tool Errors Now Throw Typed Exceptions in Workflow Scripts #
New
This is the most impactful behavioral change in this release for workflow authors.
Previously, MCP tool call failures inside workflow scripts silently resolved to {error: string} objects, making them indistinguishable from a successful tool call that returned an object with an error field. Now, MCP tool failures throw a proper McpToolError exception that crosses the VM sandbox boundary with structured fields.
The thrown error has:
.name === "McpToolError".toolName — the name of the MCP tool that failed.error — the error message string.detail — parsed JSON of the response body, if the error response was JSON
In scripts, use try/catch:
try {
const result = await mcp__server__tool({ ... })
} catch (e) {
if (e.name === "McpToolError") {
console.log(e.toolName, e.error, e.detail)
}
}
The behavior is gated by tengu_repl_mcp_error_throw (default: true). The system prompt documentation for workflow scripts has been updated to reflect that MCP tools THROW on failure while built-in tools return {error: string}.
Evidence
McpToolError class (search for "McpToolError") and VM sandbox bridge (search for "_throwMcpVM")
Skills Discovery Migrated to skills/list Paginated Method #
New
MCP servers providing skills no longer need to expose a skill://index.json resource. The discovery protocol has changed to use a standard skills/list MCP method that supports cursor-based pagination.
Changes:
- Old: MCP server exposes
skill://index.json resource with a JSON index of skills (URL-based) - New: MCP server implements the
skills/list method, returning pages of skills with uri fields and optional digest and nextCursor - Pagination is handled automatically — Claude Code iterates until all pages are fetched or limits are reached
- Dropped entries are logged: "N skills/list entries skipped (malformed, missing, or oversized fields)"
- If a page fails mid-pagination, already-fetched entries are used and a warning is logged
If you operate an MCP server that provides skills, update it from the resource-based index to the skills/list method.
Evidence
New discovery function (search for "skills/list failed") and old discovery removed (search for "skill://index.json" — now gone)
Worker Agent System Prompt #
New
Worker agents launched in multi-agent workflows now receive a structured system prompt covering environment awareness, scope limits, resumed task handling, error escalation, and output format. Key guidelines:
- Don't modify code you don't understand; stop and report to the coordinator if confused
- Complete exactly what was asked; suggest follow-ups for unrelated issues found
- If you have the Task tool, you may use it to fan out — but workers at the depth cap don't receive it
- Output structured: "What you did or found" then "Summary: one sentence"
The subagent spawn depth ceiling is now configurable via tengu_hazel_trellis (feature flag) or CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH (environment variable).
Evidence
Worker prompt (search for "You are a worker agent executing a task assigned by the coordinator") and depth env var (search for "CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH")
Path Containment Security Overhaul #
New
The system that prevents worktree-isolated agents from writing outside their assigned worktree has been substantially rewritten to handle complex path forms that could previously bypass containment.
New protections:
- UNC shares (Windows
\\server\share paths) are detected and blocked when the session root is local /net/ automount paths (Linux/macOS NFS automounts) are recognized and normalized- Apple
/System/Volumes/Data/ paths are remapped to their real root - Paths with raw dot-segments that survive
path.normalize are blocked - Windows paths with trailing dots or spaces (a Windows security concern) are detected
- Device namespace paths (
\\?\) are unwrapped or blocked as appropriate - Symlink resolution now iterates up to 8 rounds to reach a stable canonical form
Error messages are more specific. For example, a network-shaped path against a local checkout says: > This write was blocked because the path is network-shaped (a UNC share or /net automount spelling) while this session's checkout is local.
A path that can't be safely resolved says: > This write was blocked because the path is spelled in a form that cannot be safely resolved (for example through a symlink storing a raw dot segment, a network-share or device-namespace shape, or an unreadable ancestor directory).
Commands run by worktree-isolated agents now get a more specific rejection if their working directory resolves outside the worktree due to letter-case mismatch: "(this path differs from the registered spelling only by letter case — respell it to match exactly)".
Evidence
Path resolution (search for "resolves-to-trailing-dot-or-space") and network-shaped block (search for "network-shaped (a UNC share")
Concurrency Slot Tracking for Subagents #
New
Subagents now properly claim and release a concurrency slot via takeConcurrencySlot() / onRunSettled(). The store now tracks runningSubagents as a counter. This enables accurate display of running agent count and proper enforcement of concurrent subagent limits (controlled by CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS).
Evidence
Concurrency tracking (search for "Concurrent subagent limit reached")
Import Fingerprinting for MCP Servers and Slash Commands #
New
Import proposals for MCP servers, slash commands, and GEMINI.md files now include a fingerprint field. The fingerprint is a stable hash of the entry's configuration, enabling detection of drift when an import item's config has changed since it was last imported.
Evidence
MCP server fingerprint (search for "fingerprint: JSON.stringify")
Brace Pattern Expansion Budget #
New
Glob patterns with brace expansion ({a,b,c}) now have a budget guard. If expanding a brace pattern would produce too many results or exceed the byte budget, it is used unexpanded with a warning:
> Brace pattern expansion exceeds the budget; using it unexpanded: {pattern}
This prevents accidental combinatorial explosion from deeply nested or wide brace patterns.
Evidence
Budget guard (search for "Brace pattern expansion exceeds the budget")
/auto-mode-setup Gets --apply-target, --request-id, and --expect-sha256 #
New
The /auto-mode-setup command now supports:
--request-id <uuid> — must come first; the UUID is echoed back in the JSON result as requestId so a host with multiple in-flight commands can match replies--apply-target <user|project> — specifies where to save settings; the apply step validates that the proposal's scope matches the target (user ↔ scope=all, project ↔ scope=project)--expect-sha256 <64-hex> — required before --apply-file; the apply refuses unless the file's bytes hash to the given SHA256
The proposal object returned by --propose now also includes the scope field.
Evidence
Command usage string (search for "--apply-target must be")
Workshop Artifact Skill: Page-Based Decision Resolution #
New
The /workshop skill (interactive decision artifact) has been updated to a page-self-publish model. Decisions are now resolved by readers clicking option rows directly on the published page, with the page republishing itself via window.claude.self.publish — no separate server-side interaction store is polled. The session loop now detects pending decisions by diffing the live page's DOM state (resolved vs. open call-items) rather than reading from an interactions API.
Additional clarity added to the skill prompt: when building or publishing a workshop, talk about the workshop at the product level — don't narrate internal machinery (publish declarations, renderer wiring, etc.) to the user.
Evidence
Workshop self-publish JS (search for "window.claude.self" or "data-decision-state")
Remote Workflow Launch via Server Events (CCR) #
New
Claude Code Remote sessions can now receive workflow_launch SSE events from the server, which trigger automatic workflow execution. This enables server-initiated multi-agent workflows in remote sessions without requiring a user to type a command.
The flow:
- Server pushes a
workflow_launch event with a signed workflow bundle (filestore path + SHA256) - Claude Code fetches the bundle, verifies the SHA256, and validates the format (version byte, script frame, args frame)
- If all checks pass, the script is executed via the workflow engine
- Results are posted back to the server as a
workflow_launch_result system event
Policy gates:
- Blocked entirely if
disableWorkflows is set in managed settings (separate message from org policy block) - Blocked if org policy
allow_workflows is false - At most one
workflow_launch event per session is permitted
The CLAUDE_CODE_REMOTE_SESSION_ORIGIN env var (set to "review") bypasses the org policy check for server-authored carriers.
Evidence
Launch handler (search for "workflow_launch received outside a remote (CCR) session") and bundle validation (search for "unsupported bundle format version")
Bridge Placeholder Sweep #
New
A new background cleanup task archives orphaned bridge placeholder session records. When a session is initiated via the bridge but then abandoned (never received any messages after creation), its placeholder entry is cleaned up. The sweep runs at startup (after a delay) and checks each placeholder: if the underlying session record shows created_at === updated_at (never used), the placeholder is archived. Records older than 30 days are removed unconditionally.
Evidence
Sweep (search for "[bridge:placeholder] archived orphaned placeholder")
Session File Update via Method Call #
New
The session file path is now set via a setSessionFile() method call rather than direct property assignment, preparing for more controlled session lifecycle management.
Evidence
Method call (search for "r.setSessionFile(o)")
Worktree Auto-Compaction Trigger #
New
When the remote hydration process (CCR delta sync) appends entries and the session file grows past a threshold, it now automatically requests compaction via requestCompact(). This keeps remote session transcripts from growing unboundedly.
Evidence
Auto-compact trigger (search for "requestCompact")