* feat(config): let model aliases carry an opt-in profile block An alias can now act as a profile of its target through an explicit `profile:` block carrying system_prompt, mcp (remote/stdio servers, plus the new mcp.auto) and the new client_tools. ResolveAlias applies only that block on top of a copy of the target, while Name, Model, Backend and the persisted revision stay the target's. The loader key is therefore unchanged and every profile runs on the target's backend process; today a persona needs a separate model config, which loads the weights a second time. The block is opt-in so existing aliases never change: top-level fields on an alias stay ignored, and an alias without a profile block resolves to exactly the target. A profile block on a non-alias config is rejected. A profile with its own MCP servers caches its sessions under the alias name (MCPSessionKey), because the cache slot keyed on the target's name is invalidated whenever its config hash changes and two server sets would tear each other's sessions down. mcp.auto enables all configured servers on /v1/chat/completions when the request does not name any in metadata.mcp_servers, which plain OpenAI clients cannot send. client_tools: drop discards client tools and tool_choice before the request is merged (chat completions, responses, Anthropic messages), so only the model's own MCP tools are offered. Both keys work on any model and inside an alias's profile block. Refs #12548 Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de> * fix(openai): only auto-enable model MCP when MCP is enabled and permitted mcp.auto turns a plain /v1/chat/completions request into a server-side MCP request. It now applies the same controls as the dedicated MCP routes before any server is selected: the instance switch (LOCALAI_DISABLE_MCP) and, with auth on, the caller's "mcp" feature, checked with auth.HasFeatureAccess against the auth database that the chat route now passes to ChatEndpoint. A denied request is answered as a plain chat, without contacting a server or adding tools, rather than rejected: the client did not ask for MCP, it only reached a model that offers it. Endpoint specs drive ChatEndpoint against a counting MCP fixture server: MCP disabled and a user with chat but without mcp see no tools and no connection; MCP enabled and a user with mcp get the fixture tool. The user specs need the auth build tag (SQLite auth database). Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de> * fix(mcp): resolve profile aliases in the MCP discovery endpoints GET /v1/mcp/servers/:model, /v1/mcp/prompts/:model and /v1/mcp/resources/:model looked the model up with GetModelConfig, which returns the alias stub. A profile alias carries its servers in its profile block, so the stub has none of its own and the endpoints reported an empty list for an alias whose chat requests do use tools. Resolve the alias the way the request middleware does before reading the MCP config, and open the sessions under MCPSessionKey so discovery shares the profile's session cache slot instead of tearing down the target's. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de> * feat(config): opt-in evidence check for profile aliases A profile with server-side MCP tools (profile.mcp, mcp.auto) runs its tools on the server and returns only the answer text. On the next turn the client sends the history back without those tool calls and results, and the model then tends to skip the tool and answer from nothing. In a measurement on a follow-up question, 0 of 12 turns called the tool and the answers carried invented numbers (e.g. "0" where the true value was 9); with the tool trail kept in the history the tool was called 12 of 12 times. A prompt instruction does not reliably prevent this, so the check sits at the output. New profile keys: profile: evidence_check: numbers: true # enables the check note: "..." # optional, defaults to an English note Rule, per request and stateless: when the check is on, no MCP tool call succeeded on the server during this request (a call that returned an error delivers no evidence and does not count), and the final answer contains a number (\b\d+([.,]\d+)?\b) that occurs in none of the request's input (the profile's system prompt and every message as received, before compression), the note is appended after a blank line. "3,5" and "3.5" count as the same number. In a streamed response the note is one extra content delta before the finish chunk. Choices that hand tool calls to the client are not checked. The setting is only read from a profile block, so plain models and aliases behave as before; a note without numbers: true is rejected at validation. Tests (Ginkgo): the number rule (invented number, number from the input, no numbers, comma/dot decimals, system prompt as evidence, tool ran), the overlay into the resolved config with plain alias, target and a top-level key as counter-checks, validation, and ChatEndpoint specs on a stubbed backend for both paths: note on an invented number, none for a number from the request, none for a profile without the check, none after a successful MCP tool call against an in-process MCP server, and the note again after a failed tool call (arguments that are not JSON). Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de> --------- Signed-off-by: Stefan Walcz <stefan.walcz@walcz.de>
26 KiB
+++ title = "Model Context Protocol (MCP)" weight = 22 toc = true description = "Agentic capabilities with Model Context Protocol integration" tags = ["MCP", "Agents", "Tools", "Advanced"] categories = ["Features"] +++
LocalAI now supports the Model Context Protocol (MCP), enabling powerful agentic capabilities by connecting AI models to external tools and services. This feature allows your LocalAI models to interact with various MCP servers, providing access to real-time data, APIs, and specialized tools.
{{< agentic-routing current="mcp" >}}
What is MCP?
The Model Context Protocol is a standard for connecting AI models to external tools and data sources. It enables AI agents to:
- Access real-time information from external APIs
- Execute commands and interact with external systems
- Use specialized tools for specific tasks
- Maintain context across multiple tool interactions
Key Features
- 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: Static bearer tokens or OAuth2
client_credentialswith automatic token refresh - Multi-endpoint Support: Works with OpenAI Chat, Anthropic Messages, and Open Responses APIs
- Selective Server Activation: Use
metadata.mcp_serversto enable only specific servers per request - Server-side Tool Execution: Tools are executed on the server and results fed back to the model automatically
- Agent Configuration: Customizable execution limits and retry behavior
- MCP Prompts: Discover and expand reusable prompt templates from MCP servers
- MCP Resources: Browse and inject resource content (files, data) from MCP servers into conversations
Configuration
MCP support is configured in your model's YAML configuration file using the mcp section:
name: my-mcp-model
backend: llama-cpp
parameters:
model: qwen3-4b.gguf
mcp:
remote: |
{
"mcpServers": {
"weather-api": {
"url": "https://api.weather.com/v1",
"token": "your-api-token"
},
"search-engine": {
"url": "https://search.example.com/mcp",
"token": "your-search-token"
}
}
}
stdio: |
{
"mcpServers": {
"file-manager": {
"command": "python",
"args": ["-m", "mcp_file_manager"],
"env": {
"API_KEY": "your-key"
}
},
"database-tools": {
"command": "node",
"args": ["database-mcp-server.js"],
"env": {
"DB_URL": "postgresql://localhost/mydb"
}
}
}
}
agent:
max_iterations: 10 # Maximum MCP tool execution loop iterations
Configuration Options
In the interactive model editor, Remote MCP Servers and MCP STDIO Servers are YAML editors. Enter only the embedded mcpServers document shown inside the remote: | or stdio: | block above; the editor supplies the outer model configuration keys.
Remote Servers (remote)
Configure HTTP-based MCP servers:
url: The MCP server endpoint URLtoken: Static bearer token for authentication (optional)oauth2: OAuth2client_credentialssettings (optional, mutually exclusive withtoken); see OAuth2 client credentials
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.
Use the endpoint URL published by your server: usually /mcp for Streamable HTTP or /sse for legacy SSE. Legacy SSE servers must advertise a message endpoint on the same origin (scheme, host, and port). No transport setting is required.
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:
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). Must use https; plain http is accepted only for localhost, 127.0.0.1 and ::1 |
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). Use client_secret_env instead of a literal client_secret so the secret is not stored in the model YAML or the server configuration |
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 omitsexpires_in, a lifetime of 5 minutes is assumed. - If a refresh fails, the current token keeps being used until 30 seconds before it expires (retries wait at least 5 seconds after a failed fetch, including the first fetch). After that, requests to the MCP server fail with an error; LocalAI never falls back to an unauthenticated request.
- Configuring both
tokenandoauth2for the same server is rejected when the model configuration is loaded, as are missing or duplicatedclient_id/client_secretsources. An environment variable named in*_envthat is unset or empty makes the connection to that server fail, and the server is reported with an error in the server listing. - Debug logs of remote server entries redact static tokens and OAuth2 client secrets in both text and JSON formats.
- Token caches and retry timers are local to each connection-owning process. Workers obtain and refresh their own tokens; this cache is not shared across the cluster.
- 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.
- Sessions and cached tokens are keyed by the server configuration, not by the value of an environment variable. After you rotate a secret that is referenced by
client_secret_env(orclient_id_env), restart the process that connects to the MCP server so it picks up the new value.
Servers configured with a static token behave exactly as before.
STDIO Servers (stdio)
Configure local command-based MCP servers:
command: The executable command to runargs: Array of command-line argumentsenv: Environment variables (optional)
Automatic activation (auto)
/v1/chat/completions only runs a model's MCP servers when the request selects
them through metadata.mcp_servers. Plain OpenAI clients cannot send that field.
Set auto: true in the mcp: block to enable every configured server when the
request names none:
mcp:
auto: true
remote: |
{"mcpServers": {"weather-api": {"url": "https://weather.example.com/mcp"}}}
A request that does send metadata.mcp_servers still gets exactly the servers it
names. /v1/responses already enables all servers when the request sends no
tools, and /mcp/v1/chat/completions always does; auto does not change them.
auto only applies when MCP is enabled on the instance (not disabled with
LOCALAI_DISABLE_MCP) and, with authentication on, the caller has the mcp
permission. Otherwise the request is answered as a plain chat: no server is
contacted and no tools are added. It is not rejected, because the client did not
ask for MCP.
Client tools (client_tools)
client_tools: drop (a top-level model config key) discards the tools,
functions and tool_choice a client sends, so the model is offered only its own
MCP tools. Use it when a front end attaches tools of its own that should not
reach this model. Empty (the default) keeps client tools.
On a [model alias]({{%relref "features/model-aliases" %}}#profile-aliases) these
keys go inside the alias's profile block, which lets several assistants with
different tools share one loaded model.
Agent Configuration (agent)
max_iterations: Maximum number of MCP tool execution loop iterations (default: 10). Each iteration allows the model to call tools and receive results before generating the next response.
Agent-scoped MCP servers
The mcp: block above attaches MCP servers to a model: every request that uses that model config can reach those tools. LocalAI's [agents]({{% relref "features/agents" %}}) can also attach MCP servers to a single agent, independent of the model it runs on. This is the mechanism the Agents feature advertises as "MCP Servers".
Use model-scoped MCP (the mcp: block in a model YAML) when the tools belong to the model itself and every caller of that model should get them. Use agent-scoped MCP when only one agent should have a given tool, or when different agents sharing the same model need different tools.
Agent-scoped MCP servers are configured per agent, not in a model YAML:
- In the web UI: open the Agents page, edit an agent, and fill in its MCP Servers (remote HTTP servers) and MCP STDIO Servers (local command servers) fields. The JSON shape is the same
mcpServersmap used above. - In an agent config (JSON): the agent stores remote servers under
mcp_serversand local command servers undermcp_stdio_servers, so an exported/imported agent carries its MCP servers with it.
An agent-scoped MCP server that is unreachable is a common reason an imported agent's tool "times out or is unavailable"; see the checklist in {{% relref "getting-started/first-agent" %}}.
Usage
Selecting MCP Servers via metadata
All API endpoints support MCP server selection through the standard metadata field. Pass a comma-separated list of server names in metadata.mcp_servers:
- When present: Only the named MCP servers are activated for this request. Server names must match the keys in the model's MCP config YAML (e.g.,
"weather-api","search-engine"). - When absent: Behavior depends on the endpoint:
- OpenAI Chat Completions and Anthropic Messages: No MCP tools are injected (standard behavior).
- Open Responses: If the model has MCP config and no user-provided tools, all MCP servers are auto-activated (backward compatible).
The mcp_servers metadata key is consumed by the MCP engine and stripped before reaching the backend. Clients that support the standard metadata field can use this without custom schema extensions.
API Endpoints
MCP tools work across all three API endpoints:
OpenAI Chat Completions (/v1/chat/completions)
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "my-mcp-model",
"messages": [{"role": "user", "content": "What is the weather in New York?"}],
"metadata": {"mcp_servers": "weather-api"},
"stream": true
}'
Anthropic Messages (/v1/messages)
curl http://localhost:8080/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "my-mcp-model",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "What is the weather in New York?"}],
"metadata": {"mcp_servers": "weather-api"}
}'
Open Responses (/v1/responses)
curl http://localhost:8080/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "my-mcp-model",
"input": "What is the weather in New York?",
"metadata": {"mcp_servers": "weather-api"}
}'
Server Listing Endpoint
You can list available MCP servers and their tools for a given model:
curl http://localhost:8080/v1/mcp/servers/my-mcp-model
Returns a model wrapper and one status entry for every configured server:
{
"model": "my-mcp-model",
"servers": [
{
"name": "weather-api",
"type": "remote",
"tools": ["get_weather", "get_forecast"]
},
{
"name": "search-engine",
"type": "remote",
"tools": [],
"error": "connection failed: ..."
}
]
}
The Chat MCP → Servers tab uses this endpoint. A configured server remains listed when it is unavailable, shows the discovery error, and cannot be selected until a later discovery attempt succeeds. Reopening the dropdown or returning to the Servers tab retries discovery.
MCP Prompts
MCP servers can provide reusable prompt templates. LocalAI supports discovering and expanding prompts from MCP servers.
List Prompts
curl http://localhost:8080/v1/mcp/prompts/my-mcp-model
Returns:
[
{
"name": "code-review",
"description": "Review code for best practices",
"title": "Code Review",
"arguments": [
{"name": "language", "description": "Programming language", "required": true}
],
"server": "dev-tools"
}
]
Expand a Prompt
curl -X POST http://localhost:8080/v1/mcp/prompts/my-mcp-model/code-review \
-H "Content-Type: application/json" \
-d '{"arguments": {"language": "go"}}'
Returns:
{
"messages": [
{"role": "user", "content": "Please review the following Go code for best practices..."}
]
}
Inject Prompts via Metadata
You can inject MCP prompts into any chat request using metadata.mcp_prompt and metadata.mcp_prompt_args:
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "my-mcp-model",
"messages": [{"role": "user", "content": "Review this function: func add(a, b int) int { return a + b }"}],
"metadata": {
"mcp_servers": "dev-tools",
"mcp_prompt": "code-review",
"mcp_prompt_args": "{\"language\": \"go\"}"
}
}'
The prompt messages are prepended to the conversation before inference.
MCP Resources
MCP servers can expose data/content (files, database records, etc.) as resources identified by URI.
List Resources
curl http://localhost:8080/v1/mcp/resources/my-mcp-model
Returns:
[
{
"name": "project-readme",
"uri": "file:///README.md",
"description": "Project documentation",
"mimeType": "text/markdown",
"server": "file-manager"
}
]
Read a Resource
curl -X POST http://localhost:8080/v1/mcp/resources/my-mcp-model/read \
-H "Content-Type: application/json" \
-d '{"uri": "file:///README.md"}'
Returns:
{
"uri": "file:///README.md",
"content": "# My Project\n...",
"mimeType": "text/markdown"
}
Inject Resources via Metadata
You can inject MCP resources into chat requests using metadata.mcp_resources (comma-separated URIs):
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "my-mcp-model",
"messages": [{"role": "user", "content": "Summarize this project"}],
"metadata": {
"mcp_servers": "file-manager",
"mcp_resources": "file:///README.md,file:///CHANGELOG.md"
}
}'
Resource contents are appended to the last user message as text blocks (following the same approach as llama.cpp's WebUI).
Legacy Endpoint
The /mcp/v1/chat/completions endpoint is still supported for backward compatibility. It automatically enables all configured MCP servers (equivalent to not specifying mcp_servers).
curl http://localhost:8080/mcp/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "my-mcp-model",
"messages": [
{"role": "user", "content": "What is the current weather in New York?"}
]
}'
Example Response
{
"id": "chatcmpl-123",
"created": 1699123456,
"model": "my-mcp-model",
"choices": [
{
"text": "The current weather in New York is 72°F (22°C) with partly cloudy skies."
}
],
"object": "text_completion"
}
Example Configurations
Docker-based Tools
name: docker-agent
backend: llama-cpp
parameters:
model: qwen3-4b.gguf
mcp:
stdio: |
{
"mcpServers": {
"searxng": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"quay.io/mudler/tests:duckduckgo-localai"
]
}
}
}
agent:
max_iterations: 10
How It Works
- Tool Discovery: LocalAI connects to configured MCP servers and discovers available tools
- Tool Injection: Discovered tools are injected into the model's tool/function list alongside any user-provided tools
- Inference Loop: The model generates a response. If it calls MCP tools, LocalAI executes them server-side, appends results to the conversation, and re-runs inference
- Response Generation: When the model produces a final response (no more MCP tool calls), it is returned to the client
The execution loop is bounded by agent.max_iterations (default 10) to prevent infinite loops.
Session Lifecycle
MCP sessions are automatically managed by LocalAI:
- Lazy initialization: Sessions are created the first time a model's MCP tools are used
- Cached per model: Sessions are reused across requests for the same model
- Cleanup on model unload: When a model is unloaded (idle watchdog eviction, manual stop, or shutdown), all associated MCP sessions are closed and resources freed
- Graceful shutdown: All MCP sessions are closed when LocalAI shuts down
This means you don't need to manually manage MCP connections - they follow the model's lifecycle automatically.
Supported MCP Servers
LocalAI is compatible with any MCP-compliant server.
Best Practices
Security
- Use environment variables for sensitive tokens
- Validate MCP server endpoints before deployment
- Implement proper authentication for remote servers
Performance
- Cache frequently used tools
- Use appropriate timeout values for external APIs
- Monitor resource usage for stdio servers
Error Handling
- Implement fallback mechanisms for tool failures
- Log tool execution for debugging
- Handle network timeouts gracefully
With External Applications
Use MCP-enabled models in your applications:
import openai
client = openai.OpenAI(
base_url="http://localhost:8080/v1",
api_key="your-api-key"
)
response = client.chat.completions.create(
model="my-mcp-model",
messages=[
{"role": "user", "content": "Analyze the latest research papers on AI"}
],
extra_body={"metadata": {"mcp_servers": "search-engine"}}
)
MCP and adding packages
It might be handy to install packages before starting the container to setup the environment. This is an example on how you can do that with docker-compose (installing and configuring docker)
services:
local-ai:
image: localai/localai:latest
#image: localai/localai:latest-gpu-nvidia-cuda-13
#image: localai/localai:latest-gpu-nvidia-cuda-12
container_name: local-ai
restart: always
entrypoint: [ "/bin/bash" ]
command: >
-c "apt-get update &&
apt-get install -y docker.io &&
/entrypoint.sh"
environment:
- DEBUG=true
- LOCALAI_WATCHDOG_IDLE=true
- LOCALAI_WATCHDOG_BUSY=true
- LOCALAI_WATCHDOG_IDLE_TIMEOUT=15m
- LOCALAI_WATCHDOG_BUSY_TIMEOUT=15m
- LOCALAI_API_KEY=my-beautiful-api-key
- DOCKER_HOST=tcp://docker:2376
- DOCKER_TLS_VERIFY=1
- DOCKER_CERT_PATH=/certs/client
ports:
- "8080:8080"
volumes:
- /data/models:/models
- /data/backends:/backends
- certs:/certs:ro
# uncomment for nvidia
# deploy:
# resources:
# reservations:
# devices:
# - capabilities: [gpu]
# device_ids: ['7']
# runtime: nvidia
docker:
image: docker:dind
privileged: true
container_name: docker
volumes:
- certs:/certs
healthcheck:
test: ["CMD", "docker", "info"]
interval: 10s
timeout: 5s
volumes:
certs:
An example model config (to append to any existing model you have) can be:
mcp:
stdio: |
{
"mcpServers": {
"weather": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"ghcr.io/mudler/mcps/weather:master"
]
},
"memory": {
"command": "docker",
"env": {
"MEMORY_INDEX_PATH": "/data/memory.bleve"
},
"args": [
"run", "-i", "--rm", "-v", "/host/data:/data",
"ghcr.io/mudler/mcps/memory:master"
]
},
"ddg": {
"command": "docker",
"env": {
"MAX_RESULTS": "10"
},
"args": [
"run", "-i", "--rm", "-e", "MAX_RESULTS",
"ghcr.io/mudler/mcps/duckduckgo:master"
]
}
}
}
Links
Client-Side MCP (Browser)
In addition to server-side MCP (where the backend connects to MCP servers), LocalAI supports client-side MCP where the browser connects directly to MCP servers. This is inspired by llama.cpp's WebUI and works alongside server-side MCP.
How It Works
- Add servers in the UI: Click the MCP chip above the message box, open the Client tab, and add MCP server URLs
- Browser connects directly: The browser uses the MCP TypeScript SDK (
StreamableHTTPClientTransportorSSEClientTransport) to connect to MCP servers - Tool discovery: Connected servers' tools are sent as
toolsin the chat request body - Browser-side execution: When the LLM calls a client-side tool, the browser executes it against the MCP server and sends the result back in a follow-up request
- Agentic loop: This continues (up to 10 turns) until the LLM produces a final response
CORS Proxy
Since browsers enforce CORS restrictions, LocalAI provides a built-in proxy at /api/cors-proxy. When "Use CORS proxy" is enabled (default), requests to external MCP servers are routed through:
/api/cors-proxy?url=https://remote-mcp-server.example.com/sse
The proxy forwards the request method, headers, and body to the target URL and streams the response back with appropriate CORS headers.
MCP Apps (Interactive Tool UIs)
LocalAI supports the MCP Apps extension, which allows MCP tools to declare interactive HTML UIs. When a tool has _meta.ui.resourceUri in its definition, calling that tool renders the app's HTML inline in the chat as a sandboxed iframe.
How it works:
- When the LLM calls a tool with
_meta.ui.resourceUri, the browser fetches the HTML resource from the MCP server and renders it in an iframe - The iframe is sandboxed (
allow-scripts allow-forms, noallow-same-origin) for security - The app can call server tools, send messages, and update context via the
AppBridgeprotocol (JSON-RPC overpostMessage) - Tools marked as app-only (
_meta.ui.visibility: "app-only") are hidden from the LLM and only callable by the app iframe - On page reload, apps render statically until the MCP connection is re-established
Requirements:
- Only works with client-side MCP connections (the browser must be connected to the MCP server)
- The MCP server must implement the Apps extension (
_meta.ui.resourceUrion tools, resource serving)
Coexistence with Server-Side MCP
Both modes work simultaneously in the same chat:
- Server-side MCP tools are configured in model YAML files and executed by the backend. The backend handles these in its own agentic loop.
- Client-side MCP tools are configured per-user in the browser and sent as
toolsin the request. When the LLM calls them, the browser executes them.
If both sides have a tool with the same name, the server-side tool takes priority.
Security Considerations
- The CORS proxy can forward requests to any HTTP/HTTPS URL. It is only available when MCP is enabled (
LOCALAI_DISABLE_MCPis not set). - Client-side MCP server configurations are stored in the browser's localStorage and are not shared with the server.
- Custom headers (e.g., API keys) for MCP servers are stored in localStorage. Use with caution on shared machines.
Disabling MCP Support
You can completely disable MCP functionality in LocalAI by setting the LOCALAI_DISABLE_MCP environment variable to true, 1, or yes:
export LOCALAI_DISABLE_MCP=true
When this environment variable is set, all MCP-related features will be disabled, including:
- MCP server connections (both remote and stdio)
- Agent tool execution
- The
/mcp/v1/chat/completionsendpoint
This is useful when you want to:
- Run LocalAI without MCP capabilities for security reasons
- Reduce the attack surface by disabling unnecessary features
- Troubleshoot MCP-related issues
Example
# Disable MCP completely
LOCALAI_DISABLE_MCP=true localai run
# Or in Docker
docker run -e LOCALAI_DISABLE_MCP=true localai/localai:latest
When MCP is disabled, any model configuration with mcp sections will be ignored, and attempts to use the MCP endpoint will return an error indicating that MCP support is disabled.
