Skip to content

[Bug]: directoryAgentsInjector crashes with TypeError when MCP tool response has non-string output (output.output.slice / += on undefined) #3800

Description

@PaoloC68

Summary

processFilePathForAgentsInjection in src/hooks/directory-agents-injector/injector.ts unconditionally appends to input.output.output without checking whether it is a string first. When an MCP tool (e.g. Serena via mcpm) returns a response where output.output is undefined or a non-string type, the += operator throws a TypeError, crashing the hook and breaking the session.

Version

oh-my-opencode 3.17.14 (latest as of 2026-05-05)

Steps to Reproduce

  1. Install any MCP server via mcpm (e.g. Serena)
  2. Trigger a Read-equivalent tool call through the MCP bridge
  3. The MCP response arrives with output.output as undefined (non-string) — this happens when the MCP SDK serialization format changed but the bridge wasn't updated
  4. directoryAgentsInjectorHook fires on tool.execute.after for the read tool
  5. processFilePathForAgentsInjection is called with output.output === undefined
  6. Line 51 of injector.ts executes: input.output.output += \...`TypeError: Cannot read properties of undefined (reading 'slice')` (or similar)

Root Cause

src/hooks/directory-agents-injector/injector.ts, line 51:

input.output.output += `\n\n[Directory Context: ${agentsPath}]\n${result}${truncationNotice}`;

There is no guard before this line. Other hooks in the codebase already use the correct pattern:

// src/hooks/tool-output-truncator.ts (line ~69686 in dist)
if (typeof output.output !== "string") return;

The ToolExecuteOutput interface in hook.ts declares output: string, but this is a TypeScript-only contract — at runtime, MCP tool responses can deliver output.output as undefined, an object, or any other type depending on the MCP server and bridge version.

Fix

Add a type guard at the top of the loop body in injector.ts, consistent with the pattern used in tool-output-truncator and other hooks:

// injector.ts — processFilePathForAgentsInjection
export async function processFilePathForAgentsInjection(input: { ... }): Promise<void> {
  // Guard: output.output may be non-string at runtime (MCP bridge format changes)
  if (typeof input.output.output !== "string") return;

  const resolved = resolveFilePath(input.ctx.directory, input.filePath);
  // ... rest of function

Alternatively, guard only the append site:

if (typeof input.output.output === "string") {
  input.output.output += `\n\n[Directory Context: ${agentsPath}]\n${result}${truncationNotice}`;
}

The same guard is also needed at line 69951 in dist/index.js (the directoryReadmeInjector equivalent, processReadmeInjection).

Impact

  • Severity: High — crashes the hook silently or visibly for any user running MCP servers through a bridge (mcpm, etc.) where the response format doesn't guarantee output.output is a string
  • Workaround: None without patching dist/index.js locally
  • Affected users: Anyone using Serena, or any MCP tool routed through mcpm's bridge, with oh-my-opencode 3.17.14

Additional Context

The crash was originally misattributed to mcpm's fastmcp version mismatch (see pathintegral-institute/mcpm.sh#331). After patching mcpm locally, the crash persisted — tracing it to this hook in oh-my-opencode's dist.

The directoryReadmeInjector hook likely has the same vulnerability at the equivalent append site.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions