> ## Documentation Index
> Fetch the complete documentation index at: https://mcp-use.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://mcp-use.com/docs/feedback

```json
{
  "path": "/typescript/server/widget-components/usefiles",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# useFiles()

> Hook for file upload and download in widgets

The `useFiles` hook provides file upload and download capabilities for widgets. It exposes an `isSupported` flag so you can gracefully handle environments where files are not available.

<Warning>
  **ChatGPT only.** File operations (`upload` and `getDownloadUrl`) are only available in the ChatGPT Apps SDK environment. The MCP Apps specification (SEP-1865) has [deferred file handling](https://github.com/modelcontextprotocol/ext-apps/issues/201), meaning MCP Apps clients such as Claude, Goose, and VS Code do **not** support file operations. Always check `isSupported` before calling `upload` or `getDownloadUrl`.
</Warning>

## Import

```typescript theme={null}
import { useFiles } from "mcp-use/react";
```

## Return values

| Property         | Type                                                             | Description                                                                                                                            |
| ---------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `isSupported`    | `boolean`                                                        | `true` only in the ChatGPT Apps SDK environment where `window.openai.uploadFile` and `window.openai.getFileDownloadUrl` are available. |
| `upload`         | `(file: File, options?: UploadOptions) => Promise<FileMetadata>` | Upload a file to the host. Returns `{ fileId }`. Throws if `isSupported` is `false`.                                                   |
| `getDownloadUrl` | `(file: FileMetadata) => Promise<{ downloadUrl: string }>`       | Get a temporary download URL for a previously uploaded file. Throws if `isSupported` is `false`.                                       |

### `UploadOptions`

| Option         | Type      | Default | Description                                                                                                                                                                                                                                                    |
| -------------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `modelVisible` | `boolean` | `true`  | Whether the uploaded file should be visible to the model. When `true`, the `fileId` is appended to `imageIds` in widget state so ChatGPT includes the file in the model's conversation context. When `false`, the file is uploaded but the model won't see it. |

## Basic usage

Always gate file operations behind `isSupported`:

```tsx theme={null}
import { useFiles } from "mcp-use/react";

function MyWidget() {
  const { upload, getDownloadUrl, isSupported } = useFiles();

  if (!isSupported) {
    return (
      <p>File operations are not available in this host.</p>
    );
  }

  async function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;

    // Model-visible by default — ChatGPT will include the file in context
    const { fileId } = await upload(file);

    // Upload privately — model won't see it
    const { fileId: privateFileId } = await upload(file, { modelVisible: false });
  }

  async function handleDownload(fileId: string) {
    const { downloadUrl } = await getDownloadUrl({ fileId });
    window.open(downloadUrl, "_blank");
  }

  return (
    <div>
      <input type="file" onChange={handleFileSelect} />
    </div>
  );
}
```

## Model visibility

By default, every uploaded file is tracked in widget state under `imageIds`. ChatGPT reads this field to include the file in the model's conversation context on future turns — the model can then reference the content of the uploaded image.

Pass `{ modelVisible: false }` when you want to upload a file for widget-only use without exposing it to the model:

```tsx theme={null}
// User uploads a private reference image — widget uses it, model doesn't see it
const { fileId } = await upload(referenceFile, { modelVisible: false });
const { downloadUrl } = await getDownloadUrl({ fileId });
// Use downloadUrl for <img src={...} /> in the widget only
```

<Note>
  `imageIds` state is preserved across `setWidgetState` calls — uploading a file won't wipe other widget state.
</Note>

````

## `FileMetadata` type

```typescript
type FileMetadata = { fileId: string };
````

`fileId` is an opaque string identifier assigned by the host. Store it in widget state to retrieve the download URL later.

## Full upload and download example

```tsx theme={null}
import { useFiles, useWidgetState } from "mcp-use/react";
import { useState } from "react";

interface FileState {
  uploadedFileId: string | null;
}

function FileWidget() {
  const { upload, getDownloadUrl, isSupported } = useFiles();
  const [state, setState] = useWidgetState<FileState>();
  const [isUploading, setIsUploading] = useState(false);
  const [downloadUrl, setDownloadUrl] = useState<string | null>(null);

  if (!isSupported) {
    return (
      <div className="notice">
        File operations require ChatGPT Apps SDK.
        This host does not support file uploads yet.
      </div>
    );
  }

  async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;

    setIsUploading(true);
    try {
      const { fileId } = await upload(file);
      // Persist the fileId in widget state so the model can reference it
      await setState({ uploadedFileId: fileId });
    } finally {
      setIsUploading(false);
    }
  }

  async function handleGetLink() {
    if (!state?.uploadedFileId) return;
    const { downloadUrl } = await getDownloadUrl({
      fileId: state.uploadedFileId,
    });
    setDownloadUrl(downloadUrl);
  }

  return (
    <div>
      <input type="file" onChange={handleUpload} disabled={isUploading} />

      {state?.uploadedFileId && !downloadUrl && (
        <button onClick={handleGetLink}>Get download link</button>
      )}

      {downloadUrl && (
        <a href={downloadUrl} target="_blank" rel="noreferrer">
          Download file
        </a>
      )}
    </div>
  );
}
```

<Note>
  Download URLs are **temporary** (typically valid for 5 minutes). Do not store the URL — call `getDownloadUrl` each time you need to display or fetch the file. Store the `fileId` in widget state instead.
</Note>

## Detection logic

`isSupported` is computed once on mount:

```typescript theme={null}
const isSupported =
  typeof window !== "undefined" &&
  typeof window.openai?.uploadFile === "function" &&
  typeof window.openai?.getFileDownloadUrl === "function";
```

This is `true` only in the ChatGPT Apps SDK host. In all other environments (MCP Apps clients, URL params fallback, SSR) it is `false`.

## Error handling

Calling `upload` or `getDownloadUrl` when `isSupported` is `false` throws a descriptive error:

```
[useFiles] File upload is not supported in this host.
Check `isSupported` before calling `upload`.
File operations are only available in the ChatGPT Apps SDK environment.
```

Wrap calls in try/catch to handle host-level errors (network failures, policy violations):

```tsx theme={null}
try {
  const { fileId } = await upload(file);
} catch (err) {
  console.error("Upload failed:", err);
}
```

## Related

* [`useWidget()`](./usewidget) — Access widget props and state
* [`<ModelContext />`](./modelcontext) — Annotate UI state for the model
