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

# useFiles()

> React hook for file upload and download in widgets API Documentation

<Callout type="info" title="Source Code">
  View the source code for this module on GitHub: <a href="https://github.com/mcp-use/mcp-use/blob/main/libraries/typescript/packages/mcp-use/src/react/useFiles.ts" target="_blank" rel="noopener noreferrer">[https://github.com/mcp-use/mcp-use/blob/main/libraries/typescript/packages/mcp-use/src/react/useFiles.ts](https://github.com/mcp-use/mcp-use/blob/main/libraries/typescript/packages/mcp-use/src/react/useFiles.ts)</a>
</Callout>

`useFiles` is a React hook for file upload and download inside widgets. It exposes an `isSupported` flag plus an `upload` and a `getDownloadUrl` function.

<Warning>
  File operations are only available through the OpenAI `window.openai` extension. The MCP Apps specification (SEP-1865) has [deferred standard file handling](https://github.com/modelcontextprotocol/ext-apps/issues/201), so MCP Apps clients such as Claude, Goose, and VS Code do not support file operations. Always check `isSupported` before calling `upload` or `getDownloadUrl`. Calling either when `isSupported` is `false` throws.
</Warning>

## useFiles

Hook for file upload and download operations in widgets.

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

Returns the file operations API for the current host. `isSupported` is computed once on mount via `useMemo` and is `true` only when the OpenAI `window.openai` extension exposes both `window.openai.uploadFile` and `window.openai.getFileDownloadUrl` as functions. In all other environments (MCP Apps clients, URL params fallback, server-side rendering) it is `false`.

The hook takes no arguments.

**Returns**

> <ResponseField name="returns" type="UseFilesResult">   An object with `isSupported`, `upload`, and `getDownloadUrl`. See [`UseFilesResult`](/typescript/api-reference/react/usefiles#usefilesresult). </ResponseField>

**Signature**

```ts wrap theme={null}
function useFiles(): UseFilesResult
```

### Basic usage

Always gate file operations behind `isSupported`. The following server registers a widget that opens a file manager, and the widget component uses the hook.

```ts wrap theme={null}
import { MCPServer, widget } from "mcp-use/server"
import { z } from "zod"

const server = new MCPServer({
  name: "files-example",
  version: "1.0.0",
  description:
    "Demonstrates useFiles() hook for file upload and download with isSupported detection",
})

server.tool(
  {
    name: "open-file-manager",
    description:
      "Open an interactive file manager widget. Supports uploading files and retrieving download URLs. " +
      "File operations are only available in ChatGPT. The widget will show a notice in other clients.",
    schema: z.object({}),
    widget: {
      name: "file-manager",
      invoking: "Opening file manager...",
      invoked: "File manager ready",
    },
  },
  async () => {
    return widget({
      props: {},
      message:
        "File manager opened. You can upload files and retrieve download links. " +
        "Note: file operations are only available in ChatGPT.",
    })
  }
)

await server.listen()
```

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

function FileManager() {
  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 includes the file in context
    const { fileId } = await upload(file)

    // Upload privately: the model will not 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 <input type="file" onChange={handleFileSelect} />
}
```

## Returns

The object returned by `useFiles`.

### UseFilesResult

The shape of the value returned by [`useFiles`](/typescript/api-reference/react/usefiles#usefiles). Every field is required and present on every call.

**Returns**

> <ResponseField name="isSupported" type="boolean" required="True">   Whether the host supports file operations. `true` only when the OpenAI `window.openai` extension exposes `window.openai.uploadFile` and `window.openai.getFileDownloadUrl`. Always check this flag before calling `upload` or `getDownloadUrl`. </ResponseField>
> <ResponseField name="upload" type="(file: File, options?: UploadOptions) => Promise<FileMetadata>" required="True">   Upload a file to the host. Returns a [`FileMetadata`](/typescript/api-reference/react/usefiles#filemetadata) reference (`{ fileId }`) that can be passed to `getDownloadUrl` or stored in widget state for later retrieval. By default the file is tracked in widget state (`imageIds`) so the model can see it; pass `{ modelVisible: false }` in [`UploadOptions`](/typescript/api-reference/react/usefiles#uploadoptions) to upload privately. Throws if called when `isSupported` is `false`. </ResponseField>
> <ResponseField name="getDownloadUrl" type="(file: FileMetadata) => Promise<{ downloadUrl: string }>" required="True">   Get a temporary download URL for a previously uploaded file. The returned URL is valid for a limited time (typically 5 minutes). Do not store the URL; call `getDownloadUrl` again when you need to display or fetch the file. Throws if called when `isSupported` is `false`. </ResponseField>

**Signature**

```ts wrap theme={null}
interface UseFilesResult {
  isSupported: boolean
  upload: (file: File, options?: UploadOptions) => Promise<FileMetadata>
  getDownloadUrl: (file: FileMetadata) => Promise<{ downloadUrl: string }>
}
```

## Types

### UploadOptions

Options passed as the optional second argument to `upload`.

Configures a single `upload` call. All fields are optional.

**Parameters**

> <ParamField body="modelVisible" type="boolean" default="true">   Whether the uploaded file should be visible to the model. When `true` (the default), the `fileId` is appended to `imageIds` in widget state so the ChatGPT host includes the file in the model's conversation context on future turns. When `false`, the file is uploaded but not tracked in `imageIds`, so the model will not see it. Useful for files used only by the widget (for example a user-provided config file or a privately processed image). </ParamField>

**Signature**

```ts wrap theme={null}
interface UploadOptions {
  modelVisible?: boolean
}
```

### FileMetadata

The opaque file reference returned by `upload`.

Opaque file reference returned by `useFiles().upload()`. Pass it to `getDownloadUrl()` to retrieve a temporary download URL. Defined in `mcp-use/react` (re-exported from the widget types module).

**Returns**

> <ResponseField name="fileId" type="string" required="True">   Opaque string identifier assigned by the host. Store it in widget state to retrieve the download URL later. </ResponseField>

**Signature**

```ts wrap theme={null}
type FileMetadata = { fileId: string }
```

## 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, so the model can reference the content of the uploaded image. Internally, `upload` preserves existing `imageIds` (and other widget state) when appending, so uploading a file does not wipe other state.

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

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

<Note>
  `imageIds` state is preserved across `setWidgetState` calls, so uploading a file does not wipe other widget state. If `setWidgetState` fails while tracking the `imageId`, the error is logged with `console.warn` and the upload still resolves with its `FileMetadata`.
</Note>

## Full upload and download example

```tsx wrap 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 the OpenAI window.openai extension.
        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>

## Error handling

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

```
[useFiles] File upload is not supported in this host.
Check `isSupported` before calling `upload`.
File operations are only available through the OpenAI window.openai extension.
```

And for `getDownloadUrl`:

```
[useFiles] File download is not supported in this host.
Check `isSupported` before calling `getDownloadUrl`.
File operations are only available through the OpenAI window.openai extension.
```

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

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

## Related

* [`useWidget()`](/typescript/api-reference/react/usewidget) for accessing widget props and state.
* [`ModelContext`](/typescript/api-reference/react/modelcontext) for annotating UI state for the model.
