claude-in-chrome file_upload non-functional: extension 1.0.77 requires files:[{name,data}], CLI 2.1.186 sends paths & strips injected props

Resolved 💬 2 comments Opened Jun 23, 2026 by tow Closed Jun 27, 2026

TL;DR

mcp__claude-in-chrome__file_upload is non-functional in every configuration. Root cause: a version skew between the Chrome extension and the CLI's MCP client. The extension (1.0.77) was changed to require file contents as base64 via a files array and to reject paths. The CLI (2.1.186, latest) still advertises the old {paths, ref, tabId} schema, does no path→files conversion, and strips any undeclared nested properties from forwarded arguments. No payload can satisfy both ends.

Environment

  • Claude Code: 2.1.186 (latest — npm view @anthropic-ai/claude-code version2.1.186)
  • Claude-in-Chrome extension: 1.0.77 (id fcoeoabgfenejglbffodgkkbkcdhcgfn)
  • Tool: mcp__claude-in-chrome__file_upload
  • OS: macOS (arm64), Chrome

Extension contract (1.0.77) — what it now requires

From …/Extensions/fcoeoabgfenejglbffodgkkbkcdhcgfn/1.0.77_0/assets/mcpPermissions-*.js, the file_upload execute handler:

if (!args?.files || args.files.length === 0) {
  if (args?.paths?.length > 0)
    return { error: "file_upload no longer accepts host filesystem paths. The MCP controller must read the file and pass its contents via the `files` parameter. …" };
  throw new Error("files parameter is required and must be a non-empty array");
}
for (const f of args.files)
  if (typeof f.data !== "string" || typeof f.name !== "string" || !f.name)
    throw new Error("each file must have `data` and `name`");
// …then: atob(f.data) -> Uint8Array -> new File([bytes], f.name, {type: f.mimeType || "application/octet-stream"}) -> DataTransfer -> input.files

Required payload: files: [{ name: string, data: <base64 string>, mimeType?: string }]. paths is explicitly rejected.

CLI tool schema (2.1.186) — what it actually sends

The file_upload tool definition the CLI exposes to the model:

inputSchema: {
  type: "object",
  properties: {
    paths: { type: "array", items: { type: "string" }, /* "Absolute paths …" */ },
    ref:   { type: "string" },
    tabId: { type: "number" }
  },
  required: ["paths", "ref", "tabId"]
}

No files property, and no logic that reads a path and emits files. (The tool description still claims paths from session/connected folders "can be uploaded" — stale; nothing converts them.)

Failure modes — both reproducible

  1. Call with paths (the only documented param):

Failed: file_upload no longer accepts host filesystem paths. The MCP controller must read the file and pass its contents via the files parameter.

  1. Inject files: [{name, data, mimeType}] directly (out-of-schema):

Failed to upload file(s): each file must have data and name`
This proves the extension received
files as a **non-empty array** but every element had data/name === undefined — i.e. the CLI forwarded the files array shell but **stripped the undeclared nested props**. Reproduced with real base64 *and* a data:"AAAA"` smoke test (both stripped).

Root cause

The extension migrated file_upload to a files(base64) contract; the CLI's MCP client was not updated in lockstep. The CLI (a) advertises the obsolete paths schema and (b) sanitizes arguments to that schema, dropping the files[].data / files[].name the extension needs. Neither channel reaches the extension intact.

Suggested fix (CLI side)

Preferred — in the file_upload MCP passthrough, when paths are supplied, read each file and forward the extension's expected shape, dropping paths:

files: paths.map(p => ({
  name: basename(p),
  data: readFileSync(p).toString("base64"),
  mimeType: lookupMime(p),            // optional; extension defaults to application/octet-stream
}))
// enforce the extension's combined-size limit (10 MB) before sending

Alternative/in addition — add files: [{name, data, mimeType}] to the exposed inputSchema and stop stripping it, so callers can pass contents directly.

Impact

Any Claude Code skill that drives a web <input type=file> (uploading receipts, PDFs, images to a web app) is blocked end-to-end. The only workaround is manual drag-drop.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗