claude-in-chrome file_upload non-functional: extension 1.0.77 requires files:[{name,data}], CLI 2.1.186 sends paths & strips injected props
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 version→2.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
- 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.
- Inject
files: [{name, data, mimeType}]directly (out-of-schema):
Failed to upload file(s): each file must have data and name`files
This proves the extension received 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.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗