MCP file uploads: how the files extension works and what to check before using it
MCP's draft files extension (Nov 10, 2026) moves uploads and downloads outside the model. How the flow works and the security risks.
On November 10, 2026, the maintainers of the Model Context Protocol (MCP) published a draft experimental extension, io.modelcontextprotocol/files, that lets an agent upload files to and download files from an MCP server without the bytes passing through the model. The file goes straight to its destination and the model only sees the result. The extension works with protocol revision 2026-07-28, is optional, and has no reference implementation yet.
In sintesi
- The
io.modelcontextprotocol/filesextension is an experimental draft for file input and output, built for MCP revision2026-07-28. The team aims to stabilize it before the end of the year. - Small files travel inline as base64, large ones through an out-of-band upload: the server returns
upload_requiredwith a temporary URL, and the client repeats the call after the upload. - The weak spot is the destination: user approval comes before the target is known, so a malicious server can name any public endpoint. The draft proposes an origin allowlist, not yet mandatory.
- On November 11, MCP set a six-month release cycle: the next spec is targeted for March 30, 2027 (
2027-03-30). - For now it suits prototypes and feedback: the draft warns to expect breaking changes.
The problem it solves
Every MCP server that handles files currently invents its own convention. The MCP blog post of November 10, 2026, written by Den Delimarsky (lead maintainer) and Wil St. Charles (Google), names two typical cases: saving a file from a conversation to cloud storage, and pulling a large CSV into a code sandbox. Without a standard, each developer builds their own tool conventions and transfer mechanisms.
The stated goal is that the model "doesn't need to read every byte or even handle it directly". The work comes from the File Uploads Working Group, which the MCP team formed after repeated customer requests.
Status: experimental draft, no implementation
The extension is a draft. The experimental-ext-file-uploads repository says it is not part of the MCP specification, has not been proposed as a SEP, and that you should "expect breaking changes while the working group iterates". The reference implementation will live in that repository, but it is not there yet. When I checked, the repo had 6 commits.
The blog post says the team expects to finalize and stabilize the extension before the end of the year. The draft specification says it builds on SEP-2356 and SEP-2631.
How the upload flow works
The flow has five steps and the model takes no part in the transfer. It relies on the stateless core of the 2026-07-28 spec, where, as the spec release post explains, the initialize handshake and the Mcp-Session-Id header are gone and every request carries its own version, client identity and capabilities.
- The server advertises the extension in its
server/discoverresponse:uploadif it accepts out-of-band files,maxInlineBytesif it accepts inline files. The client declares it in theio.modelcontextprotocol/clientCapabilitiesmetadata of every request that may need it,tools/listincluded. - The server marks the file argument in the input schema with
x-mcp-file, withaccept(allowed MIME types) andmaxSize. The file is an object withid,name,mimeType,size, an optional SHA-256digest, and a base64datafield for inline use. - The host asks the user for approval (with no user present, agent policy decides). The client calls the tool with the file's metadata only. The server checks permissions and quota right away, so a failed check costs zero uploaded bytes.
- If the checks pass, the server answers with
resultType: "upload_required", anuploadslist withfileId,url,headers,credentialsandexpiresAt, and an opaque, tamper-protectedrequestState. - The client sends the file with an HTTPS
PUT(or a multipartPOSTif the target asks for it) and, on a2xxresponse, repeats the sametools/callwith the same arguments plus the unchangedrequestState. The server verifies the state, confirms the bytes arrived, validates size and type, and only then runs the tool.
An abbreviated schema example, adapted from the official post (150 MiB is 157,286,400 bytes):
"file": {
"type": "object",
"x-mcp-file": { "accept": ["application/pdf"], "maxSize": 157286400 },
"required": ["id", "mimeType", "size"]
}
Retrying with the same requestState follows the Multi Round-Trip Requests (MRTR) pattern introduced by 2026-07-28, which replaced server-initiated requests such as elicitation/create and sampling/createMessage, according to the spec post. For tools that must not run twice, such as creating a duplicate file, the server marks the requestState as used before running, in storage shared by all instances.
Downloads and credentials
Downloads use the _meta field of the result, with no retry. The content field tells the model what was produced (for example "Exported q3-orders.csv."), while the target stays out of model context. The client fetches with an HTTPS GET, checks the byte count against size, and if a digest is present verifies the hash: on a mismatch the file is discarded. Whether downloads belong in _meta is an open question for the authors.
Every target carries a credentials field so the client does not send MCP credentials to arbitrary URLs:
| Value | What the client sends | When it fits |
|---|---|---|
omit | No MCP token, cookies or ambient credentials | External storage with a pre-signed URL |
include | The MCP credential, only to a path under the MCP address or a URL on the server's origin | Gateways and sandboxed clients that can only reach the gateway |
Clients must refuse redirects and reject targets that supply an Authorization header.
Security risks to know about
The draft's security section is the most useful part to read. Key points, all from the draft specification:
- Host approval happens before the target is known. A malicious server can name any public URL as an
omittarget and receive the file. Approval limits which files leave, not where they go. The proposed mitigation is an origin allowlist. - With
formtargets, the server controls most of the request body that the client sends from its own network position. - On an origin shared by several applications, the origin check for
includecannot tell them apart. The draft advises against hosting MCP servers on shared origins. - Whoever holds a writable target can replace the file with same-size bytes, and pre-signed URLs often cannot be revoked. Mitigations: short lifetimes, single-write targets, mandatory
digest. - Downloads need no user approval, and a downloaded file may contain instructions aimed at the model. A matching
digestonly shows the bytes match the hash, not that they are safe, so clients must treat files as untrusted. - Targets, headers,
formfields,requestStateand inline data must be redacted from logs, traces and error reports.
For accepted formats the draft points to OWASP ASVS 5.0 V5.
Open questions
The draft lists seven unresolved points. The most relevant for builders: whether the origin allowlist for omit should be mandatory (and whether wildcard hosts are allowed), whether digest should be required for every file, whether downloads should use resource_link, how to handle long-running file generation, and how gateways that expose a single generic tool can surface file arguments. Whether to extend the flow to resources is also open.
The new release cycle
The November 11, 2026 post by Delimarsky and David Soria Parra sets six-month releases, generally on the last Tuesday of March and September. The next spec is targeted for March 30, 2027 (2027-03-30). Soft freeze falls 2.5 months before, Tier 1 SDK betas 1.5 months before, and hard freeze one month before. A proposal that is not ready moves to the next release without delaying the spec. The post says only release preparation changes: messages and version negotiation stay the same, so clients and servers need no changes.
The draft does not say whether files will enter the core. Rayo's view is that while it stays experimental you should treat it as an optional extension and always keep a fallback, such as accepting the URL of an already-uploaded file.
What to do now
- If you run MCP servers whose tools take or produce files, list which ones use an ad hoc convention today and which handle files above a few MB. Those are your natural candidates.
- Keep inline base64 for small files. Base64 inflates data by about a third (3 bytes become 4 characters), and
maxInlineBytesexists to set the ceiling. - If you expose an
omittarget, use short-lived pre-signed URLs, single-write targets and a mandatorydigest. - Do not host MCP servers on origins shared with other applications.
- Try the draft in a test environment and send feedback to the working group through the repository issues or its Discord. Do not tie production flows to a format that will change.
- For downloads, treat every file as untrusted: no automatic execution, no
nameused as a raw path, no overwrites without approval.
Sources
- Exploring File Uploads and Downloads in MCP, MCP blog, November 10, 2026
- Draft specification file-uploads.md
- Repository experimental-ext-file-uploads
- The 2026-07-28 Specification, MCP blog, July 28, 2026
- A More Predictable Release Cycle for MCP, MCP blog, November 11, 2026
FAQ
What is the MCP files extension?
It is an experimental draft, io.modelcontextprotocol/files, that defines how MCP clients send files to tools and how tools return files. The bytes go directly to a storage destination or the server, without passing through the model's context. It works with revision 2026-07-28 and is optional.
Can I use it in production today?
Better not to. The repository warns to expect breaking changes and there is no reference implementation yet. The team aims to stabilize it before the end of the year. It is fine for prototypes and feedback, not for flows you cannot rewrite.
How does uploading a large file work?
The client calls the tool with metadata only. If permissions and quota are fine, the server answers upload_required with temporary URLs and a requestState. The client uploads the file with PUT and repeats the same call with the state unchanged. The server verifies the bytes arrived and then runs the tool.
What is the main security risk?
Exfiltration. The user approves the send before knowing the destination, so a malicious server can name a public URL as the target and receive the file. The draft proposes an origin allowlist for credential-free targets, but it is not mandatory yet.
When is the next MCP version due?
The MCP team announced a six-month cycle on November 11, 2026. The next spec is targeted for March 30, 2027, with version string 2027-03-30. Proposals that are not ready move to the following release.