mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
feat(mcp): support OAuth2 client_credentials for remote MCP servers
Remote MCP servers could only be authenticated with a static bearer
token. Deployments that put their MCP servers behind an OIDC provider
(authentik, Keycloak, Entra ID, ...) issue short-lived service tokens
instead, so LocalAI had to be given a long-lived key or could not
connect at all.
A remote server entry now accepts an optional "oauth2" block
(token_url, client_id/client_id_env, client_secret/client_secret_env,
scopes, endpoint_params). The token is fetched with the
client_credentials grant by an http.RoundTripper installed on the MCP
client's HTTP client, so streamable HTTP and SSE transports both use it.
The token is cached and refreshed at half of its lifetime; if a refresh
fails the current token is kept until 30s before expiry, after which
requests fail with an error instead of going out unauthenticated.
"token" and "oauth2" are mutually exclusive and inconsistent blocks are
rejected when the model config is loaded. Static tokens behave as
before. MCPRemoteServer now implements fmt.Stringer so the existing
debug logs that print whole server entries no longer write the token
or client secret in clear text.
Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
Assisted-by: Claude Code:claude-opus-5-5
(cherry picked from commit 954b9d25f7)
This commit is contained in:
1 parent
8dd91dde66
commit
beaeb05e52
6 files changed
+770
-5
No files matched your search
@@ -28,7 +28,7 @@ The Model Context Protocol is a standard for connecting AI models to external to
|
||||
- **Real-time Tool Access**: Connect to external MCP servers for live data
|
||||
- **Multiple Server Support**: Configure both remote HTTP and local stdio servers
|
||||
- **Cached Connections**: Efficient tool caching for better performance
|
||||
- **Secure Authentication**: Support for bearer token authentication
|
||||
- **Secure Authentication**: Static bearer tokens or OAuth2 `client_credentials` with automatic token refresh
|
||||
- **Multi-endpoint Support**: Works with OpenAI Chat, Anthropic Messages, and Open Responses APIs
|
||||
- **Selective Server Activation**: Use `metadata.mcp_servers` to enable only specific servers per request
|
||||
- **Server-side Tool Execution**: Tools are executed on the server and results fed back to the model automatically
|
||||
@@ -93,7 +93,8 @@ In the interactive model editor, **Remote MCP Servers** and **MCP STDIO Servers*
|
||||
Configure HTTP-based MCP servers:
|
||||
|
||||
- **`url`**: The MCP server endpoint URL
|
||||
- **`token`**: Bearer token for authentication (optional)
|
||||
- **`token`**: Static bearer token for authentication (optional)
|
||||
- **`oauth2`**: OAuth2 `client_credentials` settings (optional, mutually exclusive with `token`); see [OAuth2 client credentials](#oauth2-client-credentials-for-remote-servers)
|
||||
|
||||
LocalAI automatically selects the transport for remote model MCP servers. It tries Streamable HTTP first, including servers that do not assign session IDs. If the initial POST returns HTTP 400, 404, or 405, LocalAI retries with legacy SSE. Both attempts share the discovery timeout and use the configured bearer token. Authentication failures and redirects do not trigger fallback.
|
||||
|
||||
@@ -101,6 +102,47 @@ Use the endpoint URL published by your server: usually `/mcp` for Streamable HTT
|
||||
|
||||
Remote model MCP connections originate from the LocalAI process. If LocalAI runs in Docker, the URL must therefore resolve and be reachable **from the LocalAI container**, not only from the host browser. For another service in the same Compose project, use its Compose service name and container port. Host-only DNS names, VPN DNS, and private routes must also be made available inside the container.
|
||||
|
||||
#### OAuth2 client credentials for remote servers
|
||||
|
||||
When an MCP server sits behind an OpenID Connect / OAuth2 provider (for example authentik, Keycloak, Entra ID or Auth0), LocalAI can obtain short-lived access tokens with the `client_credentials` grant instead of using a long-lived static `token`:
|
||||
|
||||
```yaml
|
||||
mcp:
|
||||
remote: |
|
||||
{
|
||||
"mcpServers": {
|
||||
"internal-tools": {
|
||||
"url": "https://mcp.example.com/mcp",
|
||||
"oauth2": {
|
||||
"token_url": "https://auth.example.com/application/o/token/",
|
||||
"client_id": "localai",
|
||||
"client_secret_env": "MCP_INTERNAL_TOOLS_SECRET",
|
||||
"scopes": ["mcp:tools"],
|
||||
"endpoint_params": {"audience": "mcp-gateway"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Description |
|
||||
|---|---|
|
||||
| `token_url` | Token endpoint of the identity provider (required) |
|
||||
| `client_id` / `client_id_env` | Client ID, literally or as the name of an environment variable (exactly one is required) |
|
||||
| `client_secret` / `client_secret_env` | Client secret, literally or as the name of an environment variable (exactly one is required). Prefer `client_secret_env` so the secret is not stored in the model YAML |
|
||||
| `scopes` | Scopes to request (optional) |
|
||||
| `endpoint_params` | Extra form parameters for the token request, e.g. `audience` or `resource` (RFC 8707) (optional) |
|
||||
|
||||
Behaviour:
|
||||
|
||||
- The token is fetched on the first request to the server, cached, and sent as `Authorization: Bearer <token>` on every request, for both streamable HTTP and SSE transports.
|
||||
- It is refreshed proactively once **half** of its lifetime (`expires_in`) has elapsed. If the provider omits `expires_in`, a lifetime of 5 minutes is assumed.
|
||||
- If a refresh fails, the current token keeps being used until 30 seconds before it expires (refresh is retried at most every 5 seconds). After that, requests to the MCP server fail with an error; LocalAI never falls back to an unauthenticated request.
|
||||
- Configuring both `token` and `oauth2` for the same server is rejected when the model configuration is loaded, as are missing or duplicated `client_id`/`client_secret` sources. An environment variable named in `*_env` that is unset or empty makes the connection to that server fail, and the server is reported with an error in the server listing.
|
||||
- The environment variables are read by the process that connects to the MCP server. In distributed mode that is the agent worker, so set them there.
|
||||
|
||||
Servers configured with a static `token` behave exactly as before.
|
||||
|
||||
#### STDIO Servers (`stdio`)
|
||||
Configure local command-based MCP servers:
|
||||
|
||||
|
||||
Reference in new issue
Block a user