Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,18 @@ mcpServers:
'(Optional) Map of header names and values used only for OAuth flow requests, such as dynamic client registration or token exchange.',
'oauth_headers:\n Authorization: "Bearer ${DCR_API_KEY}"\n X-Custom-Header: "custom_value"',
],
[
'oauthPrompt',
'String',
"(Optional) When an OAuth server asks a user to sign in. `on_start` (default) asks when the conversation turn starts. `on_use` waits until the agent first calls one of the server's tools, when every tool the agent selected from the server is deferred and `oauthToolCatalog` is declared.",
'oauthPrompt: on_use',
Comment thread
berry-13 marked this conversation as resolved.
],
[
'oauthToolCatalog',
'Array of Objects',
'(Optional) Tools an `oauthPrompt: on_use` server offers to tool search before the user signs in. Each entry has a `name` and an optional `description` and `inputSchema`.',
'oauthToolCatalog:\n - name: read_sheet\n description: Read the values in a spreadsheet range',
],
[
'obo',
'Object',
Expand Down Expand Up @@ -641,6 +653,67 @@ Server-side OAuth discovery, registration, token, and revocation HTTP requests d
X-Custom-Header: "custom_value"
```

#### `oauthPrompt`

- **Type:** String (Optional)
- **Description:** Controls when a user is asked to sign in to an OAuth-protected server.
- `on_start`: the sign-in prompt appears when the conversation turn starts, before the agent runs.
- `on_use`: the turn starts without a prompt, and the agent finds the server's tools through tool search. The prompt appears when the agent first calls one of the server's tools, and the run waits for the user to sign in.
- **Default Value:** `on_start`
- **Notes:**
- `on_use` applies only when every tool the agent selected from the server is [deferred](/docs/features/agents#deferred-tools) ("Defer Loading" on). If any selected tool loads up front, or the agent selects the whole server rather than individual tools, the prompt stays at turn start.
- The `deferred_tools` [agent capability](/docs/configuration/librechat_yaml/object_structure/agents#capabilities) must be enabled. When the `run_in_background` capability is enabled, a selected tool that opts into background execution keeps the prompt at turn start, since a detached call cannot wait for a sign-in.
- `on_use` requires a declared [`oauthToolCatalog`](#oauthtoolcatalog). Without one, the server keeps the turn-start prompt, even for a user who has connected before.
- The tools offered before sign-in must include every selected tool, or the prompt stays at turn start. That is the declared catalog, or the user's own tool list when an earlier connection left one.
- Only operator-configured servers qualify: those defined in `librechat.yaml` or added through [admin configuration overrides](/docs/features/admin_panel#configuration-management). Servers users create in the UI keep the turn-start prompt. The prompt stays at turn start for a server that uses `obo`, `customUserVars`, `requestHeaders`, or per-user or per-request placeholders (`{{LIBRECHAT_USER_*}}`, `{{LIBRECHAT_OPENID_*}}`, `{{LIBRECHAT_GRAPH_*}}`, `{{LIBRECHAT_BODY_*}}`) in its connection settings.
- Applies whether OAuth is configured up front (`oauth`, `requiresOAuth: true`) or detected when the server refuses the connection.
- Not available on servers users create in the UI.
- **Example:**
```yaml
mcpServers:
google-sheets:
type: streamable-http
url: https://sheets.example.com/mcp
requiresOAuth: true
oauthPrompt: on_use
oauthToolCatalog:
- name: read_sheet
description: Read the values in a spreadsheet range
```

#### `oauthToolCatalog`

- **Type:** Array of Objects (Optional)
- **Description:** The tools an `oauthPrompt: on_use` server offers to tool search before a user signs in. LibreChat cannot list a protected server's tools for a user who has not signed in, so the operator declares them here. The catalog is the same for every user; no user's connection decides what other users' agents see. Once a user signs in, the server's own tool definitions are used, and the server validates every call.
- **Entry fields:**
- `name`: String (Required) - The tool's name on the server, as the agent selects it.
- `description`: String (Optional) - What the tool does, shown to tool search and the model.
- `inputSchema`: Object (Optional) - The tool's parameters:
- `type`: must be `object`
- `properties`: map of parameter name to a property schema
- `required`: array of required parameter names
- **Property schema:** a bounded subset of JSON Schema with these keys only:
- `type`: one of `string`, `number`, `integer`, `boolean`, `object`, `array`, `null`, or a non-empty list of them
- `description`: String
- `enum`: non-empty array of strings, numbers, booleans, or `null`
- `default`: a string, number, boolean, or `null`
- `items`: for arrays, an object with `type`, `description`, and `enum`
- **Notes:**
- Validation is strict. A misspelled entry key (such as `input_schema`) or a keyword outside the bounded schema (such as `minLength`, `pattern`, or `$ref`) fails config validation instead of being ignored.
- A user whose earlier connection left a tool list is offered that list instead; either way it must include every tool the agent selected from the server, or the prompt stays at turn start.
- Not available on servers users create in the UI.
- **Example:**
```yaml
oauthToolCatalog:
- name: read_sheet
description: Read the values in a spreadsheet range
inputSchema:
type: object
properties:
range: { type: string }
required: [range]
```

#### `startup`

- **Type:** Boolean (Optional)
Expand Down Expand Up @@ -804,6 +877,27 @@ oauth-api-server:
X-Custom-Header: 'custom_value'
```

### MCP Server with OAuth Sign-In on First Tool Use

```yaml filename="MCP Server with oauthPrompt: on_use"
google-sheets:
type: streamable-http
url: https://sheets.example.com/mcp
requiresOAuth: true
# Ask users to sign in when the agent first calls one of this server's tools.
# Every tool the agent selects from this server must be deferred.
oauthPrompt: on_use
# Offered to tool search until a user signs in; without it the turn-start prompt is kept.
oauthToolCatalog:
- name: read_sheet
description: Read the values in a spreadsheet range
inputSchema:
type: object
properties:
range: { type: string }
required: [range]
```

### MCP Server with Server Instructions

```yaml filename="MCP Server with Instructions"
Expand Down
59 changes: 59 additions & 0 deletions e2e/mcp-oauth-prompt-verify.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test, expect } from '@playwright/test'

const PAGE = '/docs/configuration/librechat_yaml/object_structure/mcp_servers'

test.describe('MCP OAuth sign-in timing docs', () => {
test('@scenario:mcp-oauth-prompt-keys-documented both keys have sections that state when each mode prompts', async ({
page,
}) => {
await page.goto(PAGE)
const main = page.locator('main')
await expect(main.locator('[id="oauthprompt"]')).toHaveCount(1)
await expect(main.locator('[id="oauthtoolcatalog"]')).toHaveCount(1)
await expect(main).toContainText('the sign-in prompt appears when the conversation turn starts')
await expect(main).toContainText('The prompt appears when the agent first calls one of the')
await expect(main).toContainText('Without one, the server keeps the turn-start prompt')
await expect(main).toContainText('fails config validation instead of being ignored')
const admin = main.getByRole('link', { name: 'admin configuration overrides', exact: true })
await expect(admin).toHaveAttribute(
'href',
/\/docs\/features\/admin_panel#configuration-management$/,
)
})

test('@scenario:mcp-oauth-prompt-links-resolve the deferred tools, capability and catalog links reach their targets', async ({
page,
}) => {
await page.goto(PAGE)
const section = page.locator('main')
const catalogLink = section.getByRole('link', { name: 'oauthToolCatalog', exact: true }).first()
await expect(catalogLink).toHaveAttribute('href', /#oauthtoolcatalog$/)

const deferred = section.getByRole('link', { name: 'deferred', exact: true }).first()
await expect(deferred).toHaveAttribute('href', /\/docs\/features\/agents#deferred-tools$/)
await deferred.click()
await expect(page.locator('[id="deferred-tools"]')).toHaveCount(1)

await page.goto(PAGE)
const capability = page
.locator('main')
.getByRole('link', { name: 'agent capability', exact: true })
.first()
await expect(capability).toHaveAttribute('href', /object_structure\/agents#capabilities$/)
await capability.click()
await expect(page.locator('[id="capabilities"]')).toHaveCount(1)
})

test('@scenario:mcp-oauth-prompt-example-shown the on_use example with a declared catalog is on the page', async ({
page,
}) => {
await page.goto(PAGE)
const main = page.locator('main')
await expect(
main.getByRole('heading', { name: 'MCP Server with OAuth Sign-In on First Tool Use' }),
).toBeVisible()
const example = main.locator('figure, pre').filter({ hasText: 'oauthPrompt: on_use' }).last()
await expect(example).toContainText('oauthToolCatalog:')
await expect(example).toContainText('required: [range]')
})
})
Loading