claude-code - 💡(How to fix) Fix [FEATURE] Allow `mcp_tool` SessionStart hooks to inject their result into context

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

async function W7q(H, _, q, K, O, T=_3) { // ...connection guards: no MCP client context / server not connected... let Y = H.input ? interpolate(H.input, q) : {}, A = H.timeout ? H.timeout*1000 : T, { signal:w, cleanup:j } = makeSignal(O, { timeoutMs:A }); // calling ${H.server}/${H.tool} with N arg(s) let J = await server.client.callTool({ name:H.tool, arguments:Y }, ...); let M = Array.isArray(J.content) ? J.content.map(d => d.type==="text" ? d.text : [${d.type}]).join("\n") : ""; if (J.isError) return { ok:false, body:M, error:M || "MCP tool returned an error" }; return { ok:true, body:M }; // <-- body captured, never injected }

Code Example

{ "type": "mcp_tool", "server": "ccp", "tool": "status", "context": true }

---

async function W7q(H, _, q, K, O, T=_3) {
  // ...connection guards: no MCP client context / server not connected...
  let Y = H.input ? interpolate(H.input, q) : {},
      A = H.timeout ? H.timeout*1000 : T,
      { signal:w, cleanup:j } = makeSignal(O, { timeoutMs:A });
  // calling ${H.server}/${H.tool} with N arg(s)
  let J = await server.client.callTool({ name:H.tool, arguments:Y }, ...);
  let M = Array.isArray(J.content)
        ? J.content.map(d => d.type==="text" ? d.text : `[${d.type}]`).join("\n")
        : "";
  if (J.isError) return { ok:false, body:M, error:M || "MCP tool returned an error" };
  return { ok:true, body:M };          // <-- body captured, never injected
}

---

case "SessionStart":
  w.additionalContext = H.hookSpecificOutput.additionalContext;
  // ...initialUserMessage, sessionTitle, watchPaths, reloadSkills...
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing requests and this feature hasn't been requested yet
  • This is a single feature request (not multiple features)

Problem Statement

The mcp_tool hook type successfully calls a connected MCP server's tool, captures the tool's text result, and logs the hook as a success — but the captured result has no path into the model's context. For SessionStart (and similar context- injecting events), the result is silently discarded.

Proposed Solution

Please add a way for an mcp_tool hook to route its result into additionalContext, e.g. an opt-in field on the hook entry:

{ "type": "mcp_tool", "server": "ccp", "tool": "status", "context": true }

I maintain an MCP server whose tools return a body of state that should be present in context immediately after clear or compact, without depending on the model remembering to call the tools manually.

The natural fit is a SessionStart hook that calls those tools directly over the already-connected MCP RPC channel — which is exactly what mcp_tool does. The tools fire correctly; the data just never reaches context.

The decompiled mcp_tool hook executor in 2.1.158 reads exactly four fields from the hook entry (server, tool, input, timeout) and returns {ok, body}:

async function W7q(H, _, q, K, O, T=_3) {
  // ...connection guards: no MCP client context / server not connected...
  let Y = H.input ? interpolate(H.input, q) : {},
      A = H.timeout ? H.timeout*1000 : T,
      { signal:w, cleanup:j } = makeSignal(O, { timeoutMs:A });
  // calling ${H.server}/${H.tool} with N arg(s)
  let J = await server.client.callTool({ name:H.tool, arguments:Y }, ...);
  let M = Array.isArray(J.content)
        ? J.content.map(d => d.type==="text" ? d.text : `[${d.type}]`).join("\n")
        : "";
  if (J.isError) return { ok:false, body:M, error:M || "MCP tool returned an error" };
  return { ok:true, body:M };          // <-- body captured, never injected
}

The SessionStart result handler only injects from hookSpecificOutput.additionalContext:

case "SessionStart":
  w.additionalContext = H.hookSpecificOutput.additionalContext;
  // ...initialUserMessage, sessionTitle, watchPaths, reloadSkills...

There is no assignment of the mcp_tool executor's body into additionalContext in the hook-execution region, and no context-injection flag literal (injectContext / addToContext / contextInject) anywhere in the binary. So the body is a dead end for context.

An opt-in field on the hook entry could fix this issue easy, e.g. "context": true (or "injectResult": true), causing the executor's body to be assigned to additionalContext for context-injecting events. Backward compatible — absent field preserves current fire-and-forget behavior.

Alternative Solutions

No response

Priority

Medium - Would be very helpful

Feature Category

MCP server integration

Use Case Example

No response

Additional Context

No response

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [FEATURE] Allow `mcp_tool` SessionStart hooks to inject their result into context