LocalAiMCP
OfficialREADME.md
# LocalAiMCP
A stateless, asynchronous FastMCP control plane for LocalAI. The bundled LocalAI Swagger contains **114 paths / 123 operations**, and all 123 remain usable through typed, validated callables. To avoid sending roughly 123 operation schemas to the model on every MCP request, only a curated set is advertised directly; everything else is discoverable and executable on demand.
Two Swagger WebSocket operations are implemented as bounded one-call exchanges, multipart routes support file uploads, and binary responses can be saved under `./data/output` and returned inline as base64 when small enough.
## Run
```bash
git clone https://github.com/twinlunarstarz-dev/LocalAiMCP.git
cd LocalAiMCP
cp .env.example .env
# Edit LOCALAI_BASE_URL / LOCALAI_API_KEY if needed.
docker compose up -d --build
```
The MCP endpoint is:
```text
http://localhost:8000/mcp
```
For VS Code/Zoo Code or another Streamable HTTP MCP client, use that URL as the remote MCP server endpoint. The container defaults to `host.docker.internal:8080` for LocalAI and includes the Linux `host-gateway` mapping.
## Curated tool surface
The server does **not** advertise all 123 LocalAI operations by default. The default preset advertises 20 commonly useful operation tools plus five fixed discovery/system helpers.
Default directly exposed operation tools:
```text
# System/model information
get_system_info
get_metrics
get_token_metrics
list_models
list_model_capabilities
get_backend_monitor
# Generation/media
chat
complete_text
generate_image
inpaint_image
generate_sound
generate_video
text_to_speech
text_to_speech_with_voice
# Voice
list_voice_profiles
create_voice_profile
analyze_voice
verify_speakers
# 3D
generate_3d_asset
remesh_3d_asset
```
The five fixed MCP helpers are:
```text
list_additional_tools
search_additional_tools
execute_additional_tool
server_health
schema_audit
```
Thus the default `tools/list` surface is **25 tools**, rather than about 128. The exact number is configurable.
### Configure which LocalAI operations are directly visible
Set `LOCALAI_MCP_EXPOSED_TOOLS` to a comma-separated list of semantic operation names:
```env
LOCALAI_MCP_EXPOSED_TOOLS=chat,list_models,generate_image,text_to_speech,generate_3d_asset
```
Special values:
```text
* expose all 123 Swagger operations directly
none expose no Swagger operations directly; use only the gateway/system helpers
gateway-only same as none
```
An empty or unset value uses the built-in 20-operation preset. Invalid names fail startup instead of silently disappearing.
Changing direct exposure affects only what MCP clients receive in `tools/list`; it does **not** remove the hidden operation from LocalAiMCP.
## Additional-tool gateway
Less common tools stay in an internal typed registry and are accessed through three small tools.
### `list_additional_tools`
Returns the complete sorted list of hidden tool names and nothing schema-heavy. It is intentionally compact so a model can inspect the whole hidden catalog on demand without permanently carrying those schemas in every request.
### `search_additional_tools`
Searches only hidden tools using a plain-language goal or an exact tool name. Each match returns:
- semantic tool name
- detailed purpose/input/output description
- tags
- complete input JSON schema
Examples:
```text
search_additional_tools(query="detokenize token ids")
search_additional_tools(query="transcribe audio")
search_additional_tools(query="install a backend")
search_additional_tools(query="inspect request traces")
```
### `execute_additional_tool`
Executes a hidden capability by semantic name:
```json
{
"tool_name": "detokenize",
"arguments": {
"request": {
"model": "my-model",
"tokens": [1, 42, 9001]
}
}
}
```
The `arguments` object is validated against the **same generated Pydantic schema** used by a directly exposed operation. Invalid or unknown fields return a validation error and the expected input schema before any LocalAI request is made. This is not a curl-style dispatcher: the model uses semantic tool names and typed arguments rather than HTTP methods/routes.
Directly exposed operations are intentionally rejected by `execute_additional_tool`; the client should call their normal MCP tool directly.
The previous advanced `raw_request` escape hatch and `probe_safe_endpoints` helper are retained as hidden additional tools, so reducing `tools/list` does not remove those capabilities.
## LLM-oriented descriptions
The registry is designed so a model does not need prior LocalAI API knowledge:
- Tool names describe tasks rather than mirroring HTTP routes or methods.
- Every typed HTTP operation states its purpose, expected inputs, and success output.
- JSON request schemas carry field-level descriptions, including conservative fallback guidance when Swagger only says things like `Request` or leaves a field undocumented.
- Referenced request objects surface useful top-level fields directly in descriptions.
- Response descriptions explain whether data appears under `data`, `text`, `events`, `base64`, or `saved_path`.
- Search returns the complete input schema only when the hidden tool is relevant.
- Wrapper plumbing such as custom headers and per-call timeouts stays off normal typed operations.
For example, hidden tool `detokenize` explains that its request contains:
- `tokens`: integer token IDs to convert back to text
- `model`: LocalAI model name or alias whose tokenizer should be used
and that the JSON response contains `content`, the detokenized text.
## Design
- **FastMCP 3.4.7**, pinned for reproducibility.
- **Streamable HTTP + stateless mode**. Multiple Uvicorn workers are safe because discovery and execution use a process-local immutable registry rather than conversational/session state.
- **Async LocalAI I/O** with `httpx`; independent calls can run concurrently.
- **123 typed Swagger operation callables** with semantic names and generated input validation; only the configured subset is registered directly with FastMCP.
- **On-demand gateway** for hidden operations, preserving full LocalAI functionality without advertising every schema on every request.
- **Multipart support** for audio, images, GLB files, branding assets, and voice profiles. File arguments accept `data:` URIs, `base64:<data>`, HTTP(S) URLs, or files under `/data`.
- **Binary support** for audio/images/GLB responses. Small payloads are returned as base64; binary payloads can also be saved to `/data/output`.
- **SSE-aware response handling** aggregates LocalAI SSE events into a structured result.
- **WebSocket support** for backend-log streaming and realtime audio transforms using bounded exchanges.
- **Bearer auth** via `LOCALAI_API_KEY`; no token is stored in code or returned to MCP clients.
## Response wrapper
Typed HTTP operations return a predictable wrapper:
- `ok`: whether LocalAI returned a successful HTTP status
- `status_code`: LocalAI HTTP status
- `elapsed_ms`: request duration
- `data`: parsed JSON response bodies
- `text`: text responses
- `events`: collected SSE `data:` payloads
- `base64`, `size_bytes`, `mime_type`, `saved_path`: binary response metadata/content when applicable
Always check `ok` before consuming the response body.
## File inputs
For multipart tools, a file argument can be any of:
- `data:<mime>;base64,<payload>`
- `base64:<payload>`
- an `http://` or `https://` URL that the MCP container can fetch
- a local path under `LOCALAI_MCP_FILE_ROOT` (`/data` in Compose)
The Compose file mounts `./data` to `/data`.
## LocalAI streaming behavior
LocalAI request bodies that set `stream=true` are forwarded unchanged. If LocalAI answers with `text/event-stream`, the MCP call collects the SSE `data:` events and returns them when the LocalAI stream ends.
The two Swagger WebSocket routes are mapped specially:
- `stream_backend_logs`: collect backend log messages for a model up to `max_messages`, then close.
- `stream_audio_transform`: send one session/config object plus base64 PCM frames, collect transformed messages up to `max_messages`, then close.
They may be direct or hidden depending on `LOCALAI_MCP_EXPOSED_TOOLS`; hidden WebSocket tools remain executable through `execute_additional_tool`.
## Verification
Repository tests verify:
- exact Swagger coverage: 114 paths / 123 operations
- 123 unique reviewed semantic names
- the default curated exposure count and MCP `tools/list` count
- the full hidden-name catalog
- hidden search returning real descriptions and generated input schemas
- hidden execution validating arguments before network access
- every non-WebSocket operation description explaining inputs and outputs
- referenced request/response schemas surfacing real fields
- `detokenize` exposing useful token/model/content guidance on demand
- WebSocket detection, response wrapping, and binary handling
- built wheels containing all four bundled Swagger payload parts
Run locally with dependencies installed:
```bash
python -m pip install -e '.[test]'
pytest
```
Container validation:
```bash
docker compose config
docker compose build
```
An MCP client should perform the normal MCP `initialize` handshake against `http://localhost:8000/mcp`.
## Configuration
| Variable | Default | Purpose |
|---|---|---|
| `LOCALAI_BASE_URL` | `http://host.docker.internal:8080` | LocalAI base URL visible to the container |
| `LOCALAI_API_KEY` | empty | Optional LocalAI bearer token |
| `LOCALAI_MCP_EXPOSED_TOOLS` | built-in 20-tool preset | Comma-separated directly exposed Swagger operation names; `*` for all, `none` for none |
| `LOCALAI_REQUEST_TIMEOUT` | `300` | Overall LocalAI request timeout seconds |
| `LOCALAI_CONNECT_TIMEOUT` | `10` | Connection timeout seconds |
| `LOCALAI_MCP_MAX_UPLOAD_BYTES` | `104857600` | Maximum fetched/uploaded file size |
| `LOCALAI_MCP_MAX_RESPONSE_BYTES` | `104857600` | Maximum buffered LocalAI response size |
| `LOCALAI_MCP_INLINE_BINARY_LIMIT` | `1048576` | Binary bytes allowed inline as base64 |
| `LOCALAI_MCP_SAVE_BINARY` | `true` | Save binary responses to output directory |
| `MCP_PORT` | `8000` | Published host port |
| `MCP_WORKERS` | `2` | Uvicorn worker count |
## Security note
The additional-tool gateway can still execute LocalAI administrative/destructive operations, including model/backend install/delete, task/job controls, trace/log clearing, branding, node budgets, and voice-profile administration. Hiding a tool from `tools/list` reduces context size; it is **not** an authorization boundary. Do not publish port 8000 to an untrusted network without authentication and network access controls in front of it.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues