Skip to content

Adopt MCP file-transfer contract when SEP-2631 lands (proper file upload support) #384

Description

@kmonsoe

Background

We currently expose three asset-upload tools (cms_request_asset_upload, cms_upload_asset_from_url, cms_upload_asset_from_base64) and none of them are an ideal fit for ChatGPT's Apps SDK runtime:

  • Presigned URL flow (cms_request_asset_upload + cms_complete_asset_upload) — the Apps SDK runtime has no client-side PUT primitive, so the model literally can't complete step 2. Works only for SDK / CLI / server-to-server callers.
  • openai/fileParams-based file substitution — only fires for files the end-user attached to the conversation. Image-gen outputs in /mnt/data/* aren't in the file library, so ChatGPT's host clamps every such call with INVALID_PARAMETERS before it reaches Munin. We removed that tool in feat(cms): replace cms_upload_asset_from_file with cms_upload_asset_from_base64 #379 after confirming it never worked in practice.
  • Inline base64 (cms_upload_asset_from_base64, capped at 100 KB) — works today for AI-generated content the model can compress in-conversation, but burns conversation tokens and only fits small assets. Larger files have to round-trip through a publicly-reachable URL via cms_upload_asset_from_url.

None of this is a Munin-specific problem. It's the absence of a vendor-neutral, host-native file-upload primitive in MCP itself.

Upstream work

The MCP project has a File Uploads Working Group (chartered 2026-04-23, led by Anthropic with OpenAI participation) explicitly tackling this. Mission statement:

The File Uploads Working Group exists to define how MCP tools and elicitation requests declare file inputs so that hosts can present native file pickers and pass user-selected file content to servers. Today, servers that need a file from the user resort to prose instructions asking for base64 strings or local paths, which produces inconsistent UX and pushes encoding details onto end users.

The original deliverable, SEP-2356 (declarative x-mcp-file file inputs on tool/elicitation schemas), was closed without merging on 2026-06-26 in favor of a broader successor (sponsor: "Closing in favor of #2631 which will likely be the more durable solution").

The active deliverable is now SEP-2631 — File Objects and Transfer (open PR). It builds on SEP-2356 rather than discarding the model:

  • Keeps SEP-2356's x-mcp-file declaration on URI-string tool / elicitation inputs.
  • Adds out-of-band upload/download negotiation (files/authorizeUpload, plus x-mcp-file.transferModes = inline / upload-required / client-choice) so large files no longer have to travel as base64 inside the tool-call JSON.
  • Adds FileValue outputs (URI + optional name / MIME type / size) for tool-generated files.

This means the no-base64 / large-asset concern that earlier looked like a separate follow-on is now covered by the same SEP. (An earlier draft of this issue cited a follow-on "SEP-2361"; that number is unrelated — modelcontextprotocol#2361 is a Claude Desktop SSE crash bug — and the end-to-end-without-base64 goal it described is what SEP-2631 now delivers.)

What we should do here

Track + adopt. Concretely:

  1. Watch SEP-2631 through to acceptance. When it lands in the official MCP spec, the @McpTool decorator in packages/mcp-toolkit should grow first-class support for declaring x-mcp-file inputs (replacing our ad-hoc inline base64 + presigned URL convention).
  2. Migrate the CMS asset tools to expose file inputs via the new descriptor once SDK support lands. Likely deprecate cms_upload_asset_from_base64 in favor of a single canonical tool with proper host file-picker integration; keep cms_upload_asset_from_url for server-side fetch use cases.
  3. Conformance tests — once the schema keyword + transfer modes are part of the spec, add coverage in @getmunin/mcp-toolkit's test suite so we catch host regressions.
  4. Drop the 100 KB base64 cap once SEP-2631's out-of-band transfer (files/authorizeUpload / transferModes) ships in the SDKs — that removes the token-cost reason the cap exists, for both user-attached and AI-generated large assets. (Previously filed as a separate "if SEP-2361 lands" item; it's now part of the same SEP.)

Status

  • SEP-2631 accepted
  • TypeScript SDK (@modelcontextprotocol/sdk) ships the file-transfer types (x-mcp-file, transferModes, FileValue)
  • @getmunin/mcp-toolkit exposes a typed wrapper for declaring file params on @McpTool
  • CMS asset tools migrated; legacy base64 tool deprecated; 100 KB cap removed
  • ChatGPT Apps SDK runtime confirmed honoring the descriptor (close the loop on the bug that motivated this issue)

Context from recent work

This issue continues the investigation from #376 / #379. Background of why each existing upload path falls short for ChatGPT's runtime is in those PR descriptions and the linked OpenAI community threads:

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions