pi-lemonade-link 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/mcp.md CHANGED
@@ -2,198 +2,302 @@
2
2
 
3
3
  Lemonade exposes its inference capabilities as a Model Context Protocol (MCP) server, so any MCP-compatible client (GitHub Copilot, Claude Desktop, MCP Inspector, Cursor, the `mcp` Python client, etc.) can call your locally running models as tools.
4
4
 
5
- The gateway implements the **MCP "Streamable HTTP" transport** (spec version `2025-06-18`) with the `tools` capability only. All traffic flows through a single endpoint:
5
+ The gateway implements the **MCP "Streamable HTTP" transport** (spec version `2025-06-18`) with the `tools` capability only, so `resources/*` and `prompts/*` are not implemented. All traffic flows through a single endpoint:
6
6
 
7
- | Endpoint | Status | Notes |
8
- |----------|--------|-------|
9
- | `POST /mcp` | Supported | JSON-RPC 2.0 envelope. Accepts a single message or a batch array. |
10
- | `GET /mcp` | `405 Method Not Allowed` | Server-initiated SSE channel is not supported. |
7
+ <!-- BEGIN GENERATED: mcp.summary -->
8
+ | Method | Endpoint | Status | Description |
9
+ |--------|----------|--------|-------------|
10
+ | `POST` | [`/mcp`](#post-mcp) | Supported | Call MCP methods over JSON-RPC 2.0 |
11
+ | `GET` | [`/mcp`](#get-mcp) | Not supported | Open a server-initiated event stream |
12
+ <!-- END GENERATED: mcp.summary -->
11
13
 
12
- > **Why a single path?** The MCP specification mandates one endpoint URL per server, so `/mcp` is an intentional exception to Lemonade's quad-prefix convention.
14
+ <!-- BEGIN GENERATED: mcp.rpc -->
15
+ ## `POST /mcp`
16
+ <sub>![Status](https://img.shields.io/badge/status-fully_available-green)</sub>
13
17
 
14
- ## Authentication
18
+ Serves the MCP methods listed under [Supported Methods](#supported-methods) over JSON-RPC 2.0.
15
19
 
16
- `/mcp` is treated as a regular API route, so it honors `LEMONADE_API_KEY` exactly like `/api/v1/chat/completions`:
20
+ Requires `LEMONADE_API_KEY` when it is set.
17
21
 
18
- ```bash
19
- curl -s http://localhost:13305/mcp \
20
- -H "Authorization: Bearer $LEMONADE_API_KEY" \
21
- -H "Content-Type: application/json" \
22
- -d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
23
- ```
22
+ ### Parameters
23
+
24
+ | Parameter | Required | Description | Status |
25
+ |-----------|----------|-------------|--------|
26
+ | `jsonrpc` | Yes | JSON-RPC version: `"2.0"`. | <sub>![Status](https://img.shields.io/badge/available-green)</sub> |
27
+ | `id` | No | Request id, echoed in the reply. Omit it to send a notification, which gets no reply. | <sub>![Status](https://img.shields.io/badge/available-green)</sub> |
28
+ | `method` | Yes | One of the [supported methods](#supported-methods), e.g. `initialize`, `tools/list` or `tools/call`. | <sub>![Status](https://img.shields.io/badge/available-green)</sub> |
29
+ | `params` | No | Method parameters; `tools/call` takes `name` and `arguments`. | <sub>![Status](https://img.shields.io/badge/available-green)</sub> |
24
30
 
25
- ## Supported methods
31
+ The body is one JSON-RPC message or a batch array of them. A JSON-RPC error is answered with `200` like any other JSON-RPC response; see [Error Model](#error-model). A request whose messages are all notifications answers `202` with no body.
32
+
33
+ ### Supported Methods
26
34
 
27
35
  | Method | Purpose |
28
36
  |--------|---------|
29
- | `initialize` | Negotiate protocol version, return server identity and capabilities. |
37
+ | `initialize` | Return the server's protocol version, identity and capabilities. |
30
38
  | `notifications/initialized` | Client acknowledgement; silently accepted. |
39
+ | `notifications/cancelled` | Client cancellation; silently accepted. |
31
40
  | `tools/list` | Return the catalogue of callable tools (with JSON Schemas). |
32
41
  | `tools/call` | Invoke one of the tools below. |
33
42
  | `ping` | Liveness probe; returns `{}`. |
34
43
 
35
- ## Tools
44
+ ### Error Model
36
45
 
37
- All tools auto-load (and download, if missing) the requested model on first call, exactly like `POST /v1/chat/completions`. Errors are returned as MCP results with `"isError": true` rather than JSON-RPC errors, matching the spec's guidance for tool failures.
46
+ | Code | Meaning |
47
+ |------|---------|
48
+ | `-32700` | Body was not valid JSON. |
49
+ | `-32600` | Request was not a JSON-RPC object (or batch was empty / missing `method`). |
50
+ | `-32601` | Unknown JSON-RPC method (e.g. `resources/list`). |
51
+ | `-32602` | Invalid `params` for a known method. |
52
+ | `-32603` | Internal server error (an exception escaped a handler). |
38
53
 
39
- ### `lemonade_list_models`
54
+ Tool-level failures (bad arguments, model load errors, backend exceptions) are returned as **successful** JSON-RPC results with `"isError": true` and a text content block describing the failure, so MCP-aware models can self-correct.
40
55
 
41
- Discover what's loaded, what's downloaded, and what's recommended. Call this first if you don't already know the exact model name to pass to the other tools — passing a wrong name may trigger a multi-GB download.
56
+ ### Limitations
57
+
58
+ - No session resumption (`Mcp-Session-Id` header is not issued).
59
+ - Tools return their whole result: `lemonade_chat` ignores `stream`. Use [`POST /v1/chat/completions`](./openai.md#post-v1chatcompletions) for streamed tokens.
60
+ - Embeddings and text-to-speech have no MCP tool; use [`/v1/embeddings`](./openai.md#post-v1embeddings) and [`/v1/audio/speech`](./openai.md#post-v1audiospeech).
61
+
62
+ ### Examples
63
+
64
+ #### Request
65
+
66
+ === "PowerShell"
67
+
68
+ ```powershell
69
+ Invoke-WebRequest `
70
+ -Uri "http://localhost:13305/mcp" `
71
+ -Method POST `
72
+ -Headers @{ "Content-Type" = "application/json" } `
73
+ -Body '{
74
+ "jsonrpc": "2.0",
75
+ "id": 1,
76
+ "method": "initialize",
77
+ "params": {"protocolVersion": "2025-06-18", "capabilities": {}}
78
+ }'
79
+ ```
80
+
81
+ === "Bash"
82
+
83
+ ```bash
84
+ curl http://localhost:13305/mcp \
85
+ -H "Content-Type: application/json" \
86
+ -d '{
87
+ "jsonrpc": "2.0",
88
+ "id": 1,
89
+ "method": "initialize",
90
+ "params": {"protocolVersion": "2025-06-18", "capabilities": {}}
91
+ }'
92
+ ```
93
+
94
+ **Response:** `200 OK`
42
95
 
43
96
  ```json
44
97
  {
45
- "name": "lemonade_list_models",
46
- "arguments": {
47
- "include_available": true,
48
- "include_suggested": true
98
+ "jsonrpc": "2.0",
99
+ "id": 1,
100
+ "result": {
101
+ "capabilities": {"tools": {}},
102
+ "protocolVersion": "2025-06-18",
103
+ "serverInfo": {"name": "lemonade-mcp", "version": "2026.43.0~1686.1553ede6"}
49
104
  }
50
105
  }
51
106
  ```
52
107
 
53
- Returns a summary text block plus a JSON-stringified text block with `{loaded, available, suggested_to_pull, recommended_chat_model}`.
108
+ #### Notification
54
109
 
55
- ### `lemonade_chat`
110
+ === "PowerShell"
56
111
 
57
- Chat completion against any LLM in the registry.
112
+ ```powershell
113
+ Invoke-WebRequest `
114
+ -Uri "http://localhost:13305/mcp" `
115
+ -Method POST `
116
+ -Headers @{ "Content-Type" = "application/json" } `
117
+ -Body '{"jsonrpc": "2.0", "method": "notifications/initialized"}'
118
+ ```
58
119
 
59
- ```json
60
- {
61
- "name": "lemonade_chat",
62
- "arguments": {
63
- "model": "Qwen3-1.7B-GGUF",
64
- "messages": [
65
- {"role": "system", "content": "You are concise."},
66
- {"role": "user", "content": "Summarize MCP in one line."}
67
- ],
68
- "max_tokens": 64,
69
- "temperature": 0.2
70
- }
71
- }
72
- ```
120
+ === "Bash"
73
121
 
74
- Returns one text block with the assistant content. If the model emits tool calls, a second text block containing `tool_calls: <json>` is appended.
122
+ ```bash
123
+ curl http://localhost:13305/mcp \
124
+ -H "Content-Type: application/json" \
125
+ -d '{"jsonrpc": "2.0", "method": "notifications/initialized"}'
126
+ ```
75
127
 
76
- Reasoning models (Qwen3, DeepSeek-R1, ...) have the `<think>` block disabled by default to keep small `max_tokens` budgets from being consumed by reasoning. Pass `"chat_template_kwargs": {"enable_thinking": true}` to opt back in.
128
+ **Response:** `202 Accepted`
77
129
 
78
- > **Picking a portable model.** The example above uses `Qwen3-1.7B-GGUF` because GGUF (llama.cpp) runs everywhere lemonade does — Windows, Linux/Docker, macOS, CPU and Vulkan/ROCm/Metal GPUs. Hybrid/NPU variants such as `*-Hybrid` (recipe `ryzenai-llm`, **Windows + AMD RyzenAI** only) or `*-FLM` (recipe `flm`, **AMD Ryzen AI NPU** only) are faster on supported hardware but unavailable on others. If your client picks one that isn't supported, the tool returns a structured error suggesting a portable alternative — prefer `lemonade_list_models` to discover what's actually available on the running server.
130
+ ### Response format
79
131
 
80
- ### `lemonade_transcribe_audio`
132
+ One JSON-RPC 2.0 response, or an array of them for a batch.
81
133
 
82
- Transcribe a local audio file with a Whisper-class model. Prefer `audio_path` (the server runs on the same machine as the caller); use `audio_base64` only when you genuinely have bytes in memory.
134
+ - `jsonrpc` - Always `2.0`.
135
+ - `id` - The `id` of the request this answers; `null` when it could not be read.
136
+ - `result` - The method's result.
137
+ - `error` - A JSON-RPC error, such as `-32601` for an unknown method.
138
+ - `code` - The JSON-RPC error code.
139
+ - `message` - What went wrong.
83
140
 
84
- ```json
85
- {
86
- "name": "lemonade_transcribe_audio",
87
- "arguments": {
88
- "model": "Whisper-Large-v3-Turbo",
89
- "audio_path": "C:/clips/meeting.wav",
90
- "response_format": "verbose_json"
91
- }
92
- }
93
- ```
141
+ ### Errors
94
142
 
95
- Returns two text blocks: the bare transcript, followed by the full OpenAI-shaped response (for callers that need timestamps or segments).
143
+ | Status | Code | When |
144
+ |--------|------|------|
145
+ | `500` | `-32603` | An exception escapes the JSON-RPC handling, such as a number too large for a double or invalid UTF-8 in the body |
146
+ <!-- END GENERATED: mcp.rpc -->
96
147
 
97
- ### `lemonade_generate_image`
148
+ <!-- BEGIN GENERATED: mcp.sse -->
149
+ ## `GET /mcp`
150
+ <sub>![Status](https://img.shields.io/badge/status-not_available-red)</sub>
98
151
 
99
- Generate one or more PNGs from a prompt. **Prefer writing to disk** via `output_path` (single image) or `output_dir` (one or more) — base64 image content blocks cost tens of thousands of tokens per image and some clients surface them as opaque resource URIs.
152
+ Lemonade opens no server-initiated event stream, so tools return their full result in the [`POST /mcp`](#post-mcp) reply.
100
153
 
101
- ```json
102
- {
103
- "name": "lemonade_generate_image",
104
- "arguments": {
105
- "model": "SDXL-Turbo",
106
- "prompt": "a lemon-shaped car driving across the moon",
107
- "size": "512x512",
108
- "output_path": "lemon-car.png"
109
- }
110
- }
111
- ```
154
+ Requires `LEMONADE_API_KEY` when it is set.
112
155
 
113
- When disk paths are provided, returns text block(s) with the absolute path(s). Otherwise, returns one inline image content block per image (`{"type":"image", "data":"<base64>", "mimeType":"image/png"}`).
156
+ ### Errors
114
157
 
115
- **Sandboxed disk writes.** To prevent a cross-origin or unauthenticated caller from overwriting arbitrary files, `output_path` and `output_dir` are confined to a sandbox directory:
158
+ | Status | Code | When |
159
+ |--------|------|------|
160
+ | `405` | `-32600` | Always, since MCP calls go to `POST /mcp`; the reply carries `Allow: POST` |
161
+ <!-- END GENERATED: mcp.sse -->
116
162
 
117
- - Default: `<cache_dir>/mcp-images`.
118
- - Override with the `LEMONADE_MCP_IMAGE_DIR` environment variable (absolute path).
119
- - Relative paths resolve against the sandbox root; absolute paths must stay within it. Paths that escape the sandbox (via `..` or symlinks) are rejected.
120
- - `output_dir` writes use auto-generated, unique filenames (`image_<token>_<i>.png`), so concurrent callers never clobber one another's images — the returned `paths` tell you the exact names. Use `output_path` when you need an exact, caller-chosen filename (it is written as named, replacing any existing file at that path).
163
+ ## Tools
121
164
 
122
- ### `lemonade_omni`
165
+ `lemonade_omni` runs the server's collection orchestrator; see the [Lemonade Omni documentation](../dev/lemonade-omni.md) for the collection model and component behavior.
123
166
 
124
- One-shot multimodal turn against a **Lemonade Omni collection** (a model bundle that pairs a planner LLM with an image model, an image-edit model, and a TTS voice under a single `collection.omni` recipe — see [the Omni docs](../dev/lemonade-omni.md)). The server runs the orchestrator's internal tool-calling loop, executes the collection's `generate_image` / `edit_image` / `text_to_speech` tools by routing to the bundled components, and returns the result as a text block plus native MCP `image` / `audio` content blocks — one per artifact, in the order they were produced.
167
+ <!--
168
+ The tool reference below is generated from a live Lemonade server's `tools/list`
169
+ response. Tool names, descriptions, input schemas, result contracts, and MCP
170
+ annotations therefore have a single source of truth in the server registry instead
171
+ of being copied into the docs.
172
+ -->
125
173
 
126
- `model` is **optional** and defaults to `LMX-Omni-5.5B-Lite` (smaller and faster). Pass `model` explicitly to opt into a larger collection (e.g. `LMX-Omni-52B-Halo` on capable hardware) or any other `collection.omni` model surfaced by `lemonade_list_models`. The collection is downloaded on first use and may be multi-GB.
174
+ <!-- BEGIN GENERATED: mcp-tools -->
175
+ <!-- Generated from live /mcp tools/list. Do not edit this region by hand. -->
127
176
 
128
- Use `lemonade_chat` instead when you only need plain-text LLM output and don't want the planner-loop overhead.
177
+ ### `lemonade_list_models`
129
178
 
130
- ```json
131
- {
132
- "name": "lemonade_omni",
133
- "arguments": {
134
- "messages": [
135
- {"role": "user", "content": "Generate an image of a lemon car, then read out a one-line description."}
136
- ],
137
- "output_dir": "omni"
138
- }
139
- }
140
- ```
179
+ List models known to the Lemonade server. ALWAYS call this first if you don't already know the exact model name to use for chat/transcribe/image — passing a wrong name may trigger a multi-GB download. Returns a summary text block plus a JSON block with `{loaded, available, suggested_to_pull, recommended_chat_model}`.
141
180
 
142
- **Disk vs. inline output.** A single Omni turn can produce both images and audio in arbitrary order. Pass an `output_dir` to write each artifact to disk under a unique auto-generated name (`omni_<token>_<i>.<ext>`) — the tool returns one text block per artifact with its absolute path, plus a JSON-stringified `paths` array. This is strongly preferred over inline base64 for the same reasons documented under `lemonade_generate_image` — and is the **only** way to get audio out on clients that don't render `audio` content blocks. Like `lemonade_generate_image`, `output_dir` is confined to the MCP image sandbox (see **Sandboxed disk writes** above): relative paths resolve against the sandbox root, paths escaping it are rejected, and unique filenames mean concurrent callers never clobber each other.
181
+ **Returns**
143
182
 
144
- When `output_dir` is omitted, artifacts are inlined as MCP content blocks: `{"type":"image", "data":"<base64>", "mimeType":"image/png"}` and `{"type":"audio", "data":"<base64>", "mimeType":"audio/mpeg"}`.
183
+ Success returns two `text` content blocks: a human-readable summary, then a JSON-stringified object with `loaded`, `available`, `suggested_to_pull`, and `recommended_chat_model`.
145
184
 
146
- If the planner emits app-defined tool calls (those you passed in via `tools`/`tool_choice`), an extra text block `tool_calls: <json>` is appended, matching `lemonade_chat`'s passthrough semantics.
185
+ | Argument | Required | Schema | Description |
186
+ |---|:---:|---|---|
187
+ | `include_available` | no | `{"type":"boolean"}` | Include downloaded models in the `available` list. |
188
+ | `include_suggested` | no | `{"type":"boolean"}` | Include suggested, not-yet-downloaded models in `suggested_to_pull`. |
147
189
 
148
- Passing a non-collection model (e.g. a plain LLM) returns `isError: true` with a hint to use `lemonade_chat`.
190
+ ### `lemonade_chat`
149
191
 
150
- ### `lemonade_docs`
192
+ Chat completion against a locally hosted LLM. Pass a `messages` array (OpenAI chat format). `model` is OPTIONAL: when omitted, the server reuses an already-loaded LLM, else an already-downloaded one; if neither exists it asks you to either pass a `model` or `allow_download: true` (which downloads the default, Qwen3.5-4B-MTP-GGUF). Call `lemonade_list_models` first if you want to choose explicitly — a wrong name may trigger a multi-GB download.
151
193
 
152
- Read the server's own API reference. Call with no arguments to list the pages this server ships, then pass `page` to read one as markdown. The pages are bundled with the server, so they match the running version and work offline.
194
+ **Returns**
153
195
 
154
- ```json
155
- {
156
- "name": "lemonade_docs",
157
- "arguments": {
158
- "page": "api/lemonade"
159
- }
160
- }
161
- ```
196
+ Success returns one `text` content block with the assistant output. If the model emits tool calls, a second `text` block prefixed with `tool_calls: ` contains the JSON tool-call array. If normal content is empty but reasoning content is present, the first block contains the reasoning fallback instead of an empty string.
162
197
 
163
- The listing returns a summary text block plus a JSON-stringified block with `{pages: [{id, title, bytes}]}`. `page` takes an `id` from that listing; unknown pages return `"isError": true`. The same content is available over HTTP at [`GET /v1/docs`](./lemonade.md#get-v1docs).
198
+ | Argument | Required | Schema | Description |
199
+ |---|:---:|---|---|
200
+ | `allow_download` | no | `{"type":"boolean"}` | Permit downloading the default model when none is loaded or downloaded. Defaults to false. |
201
+ | `chat_template_kwargs` | no | `{"type":"object"}` | e.g. {"enable_thinking": true} to enable reasoning blocks; disabled by default |
202
+ | `max_tokens` | no | `{"type":"integer"}` | Maximum number of output tokens. |
203
+ | `messages` | yes | `{"items":{"type":"object"},"type":"array"}` | Conversation messages in OpenAI chat format. |
204
+ | `model` | no | `{"type":"string"}` | Optional. Omit to auto-select a loaded/downloaded LLM; defaults to Qwen3.5-4B-MTP-GGUF only with allow_download=true. |
205
+ | `response_format` | no | `{"type":"object"}` | OpenAI-compatible response format configuration. |
206
+ | `seed` | no | `{"type":"integer"}` | Optional random seed for reproducible sampling. |
207
+ | `stop` | no | `{}` | stop sequences (string or array) |
208
+ | `temperature` | no | `{"type":"number"}` | Sampling temperature. |
209
+ | `tool_choice` | no | `{}` | auto \| none \| required \| {type: function, ...} |
210
+ | `tools` | no | `{"items":{"type":"object"},"type":"array"}` | OpenAI-compatible tool definitions available to the model. |
211
+ | `top_p` | no | `{"type":"number"}` | Nucleus sampling probability. |
164
212
 
165
- ## Error model
213
+ ### `lemonade_transcribe_audio`
166
214
 
167
- | Code | Meaning |
168
- |------|---------|
169
- | `-32700` | Body was not valid JSON. |
170
- | `-32600` | Request was not a JSON-RPC object (or batch was empty / missing `method`). |
171
- | `-32601` | Unknown JSON-RPC method (e.g. `resources/list`). |
172
- | `-32602` | Invalid `params` for a known method. |
173
- | `-32603` | Internal server error (an exception escaped a handler). |
215
+ Transcribe an audio clip with a Whisper-class model. The Lemonade MCP server always runs on the same machine as the caller, so prefer `audio_path` (an absolute path to a local audio file: wav, mp3, m4a, ogg, flac, webm). Use `audio_base64` only when you genuinely have audio bytes in memory. If both are provided, `audio_path` takes precedence. `model` is OPTIONAL: when omitted, the server reuses an already-loaded transcription model, else an already-downloaded one; if neither exists it asks you to pass a `model` or `allow_download: true` (which downloads the default, Whisper-Tiny).
174
216
 
175
- Tool-level failures (bad arguments, model load errors, backend exceptions) are returned as **successful** JSON-RPC results with `"isError": true` and a text content block describing the failure, so MCP-aware models can self-correct.
217
+ **Returns**
176
218
 
177
- ## Limitations (MVP)
219
+ Success returns two `text` content blocks: the transcript, then the JSON-stringified full transcription response, including timestamps or segments when provided by the backend.
178
220
 
179
- - No server-initiated SSE (GET /mcp returns 405). Tools return their full result in the POST response.
180
- - No session resumption (`Mcp-Session-Id` header is not issued).
181
- - `resources/*` and `prompts/*` capabilities are not implemented.
182
- - Streaming chat output is not exposed via MCP — `stream=true` is ignored. Use `POST /v1/chat/completions` directly for streamed tokens.
183
- - Embeddings and text-to-speech are not currently exposed as MCP tools; use the OpenAI-compatible endpoints (`/v1/embeddings`, `/v1/audio/speech`) for those.
221
+ | Argument | Required | Schema | Description |
222
+ |---|:---:|---|---|
223
+ | `allow_download` | no | `{"type":"boolean"}` | Permit downloading the default model when none is loaded or downloaded. Defaults to false. |
224
+ | `audio_base64` | no | `{"type":"string"}` | Base64-encoded audio data. Used when `audio_path` is not provided. |
225
+ | `audio_path` | no | `{"type":"string"}` | Path to a local audio file. Preferred over `audio_base64`; absolute paths avoid working-directory ambiguity. |
226
+ | `filename` | no | `{"type":"string"}` | Original audio filename, used as an input-format hint. |
227
+ | `language` | no | `{"type":"string"}` | Optional source language hint. |
228
+ | `model` | no | `{"type":"string"}` | Optional. Omit to auto-select a loaded/downloaded model; defaults to Whisper-Tiny only with allow_download=true. |
229
+ | `prompt` | no | `{"type":"string"}` | Optional text prompt to guide transcription. |
230
+ | `response_format` | no | `{"enum":["json","text","srt","verbose_json","vtt"],"type":"string"}` | Transcription response format. |
231
+ | `temperature` | no | `{"type":"number"}` | Sampling temperature for transcription. |
184
232
 
185
- ## Quick test with curl
233
+ ### `lemonade_generate_image`
186
234
 
187
- ```bash
188
- # 1. Initialize
189
- curl -s http://localhost:13305/mcp -H "Content-Type: application/json" \
190
- -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{}}}'
235
+ Generate one or more images from a text prompt. The Lemonade MCP server always runs on the same machine as the caller, so PREFER writing the result directly to disk by passing `output_path` (single image) or `output_dir` (one or more). When you do, the tool returns absolute file path(s) as text — no base64 round-trip and dramatically fewer tokens. Only omit both arguments when you genuinely need the image inline. For safety, disk writes are confined to a sandbox directory (&lt;cache_dir&gt;/mcp-images, or LEMONADE_MCP_IMAGE_DIR if set); paths outside it are rejected. `output_dir` writes get unique auto-generated filenames (so concurrent callers never clobber each other); use `output_path` when you need an exact name. `model` is OPTIONAL: when omitted, the server reuses an already-loaded image model, else an already-downloaded one; if neither exists it asks you to pass a `model` or `allow_download: true` (which downloads the default, SD-Turbo).
236
+
237
+ **Returns**
238
+
239
+ Without `output_path` or `output_dir`, success returns one native MCP `image` content block per generated PNG. With disk output, success returns a summary `text` block, optional per-image path blocks when multiple files are written, and a final JSON-stringified `paths` object in a `text` block.
240
+
241
+ | Argument | Required | Schema | Description |
242
+ |---|:---:|---|---|
243
+ | `allow_download` | no | `{"type":"boolean"}` | Permit downloading the default model when none is loaded or downloaded. Defaults to false. |
244
+ | `cfg_scale` | no | `{"type":"number"}` | Classifier-free guidance scale. |
245
+ | `model` | no | `{"type":"string"}` | Optional. Omit to auto-select a loaded/downloaded image model; defaults to SD-Turbo only with allow_download=true. |
246
+ | `n` | no | `{"minimum":1,"type":"integer"}` | Number of images to generate. |
247
+ | `negative_prompt` | no | `{"type":"string"}` | Text describing content to avoid in the generated image. |
248
+ | `output_dir` | no | `{"type":"string"}` | Directory to write generated images into, inside the MCP image sandbox. Filenames are auto-generated and unique (image_{token}_{index}.png); the returned paths tell you the exact names. Relative paths resolve against the sandbox root; absolute paths must stay within it. |
249
+ | `output_path` | no | `{"type":"string"}` | Exact path of the PNG file to write, inside the MCP image sandbox (&lt;cache_dir&gt;/mcp-images or LEMONADE_MCP_IMAGE_DIR). Relative paths resolve against the sandbox root; absolute paths must stay within it. Written as named (overwrites if it already exists). Only valid when n == 1. |
250
+ | `prompt` | yes | `{"type":"string"}` | Text prompt describing the image to generate. |
251
+ | `seed` | no | `{"type":"integer"}` | Optional random seed for reproducible generation. |
252
+ | `size` | no | `{"type":"string"}` | Requested image size as WIDTHxHEIGHT, for example 512x512. |
253
+ | `steps` | no | `{"type":"integer"}` | Number of diffusion sampling steps. |
254
+
255
+ ### `lemonade_omni`
256
+
257
+ Multimodal turn against a Lemonade Omni collection (one tool call -&gt; text + images + speech in the same response). The server runs an internal tool-calling loop against the collection's planner LLM and executes its image / image-edit / TTS tools by routing to the bundled component models; the result comes back as a text block plus native MCP `image` / `audio` content blocks (one per artifact). `model` is OPTIONAL: when omitted, the server reuses an already-downloaded Omni collection; if none is downloaded it asks you to pass a `model` or `allow_download: true` (which downloads the default, `LMX-Omni-5.5B-Lite`). Pass `model='LMX-Omni-52B-Halo'` (or any other recipe='collection.omni' model from `lemonade_list_models`) to opt into a larger collection; that model may be multi-GB. Same-machine deployment: PREFER `output_dir` to write artifacts to disk and avoid expensive inline base64 blobs. For plain text chat against a regular LLM, use `lemonade_chat` instead.
258
+
259
+ **Returns**
260
+
261
+ Success always starts with one `text` block containing the final text. Artifacts follow in the order they were produced. Inline mode then appends one native MCP `image` or `audio` block per artifact. Disk mode instead appends one `text` block per artifact path in the same order, plus a final JSON-stringified `paths` object. If application tool calls are emitted, a final `text` block prefixed with `tool_calls: ` is appended.
262
+
263
+ | Argument | Required | Schema | Description |
264
+ |---|:---:|---|---|
265
+ | `allow_download` | no | `{"type":"boolean"}` | Permit downloading the default collection when none is downloaded. Defaults to false. |
266
+ | `chat_template_kwargs` | no | `{"type":"object"}` | Additional chat-template arguments forwarded to the planner LLM. |
267
+ | `max_tokens` | no | `{"type":"integer"}` | Maximum number of planner output tokens. |
268
+ | `messages` | yes | `{"items":{"type":"object"},"type":"array"}` | Conversation messages in OpenAI chat format. |
269
+ | `model` | no | `{"type":"string"}` | Optional. Omni collection name (recipe='collection.omni'). Omit to reuse a downloaded collection; defaults to LMX-Omni-5.5B-Lite only with allow_download=true. |
270
+ | `output_dir` | no | `{"type":"string"}` | Directory to write produced artifacts into, inside the MCP image sandbox (&lt;cache_dir&gt;/mcp-images or LEMONADE_MCP_IMAGE_DIR). Filenames are auto-generated and unique (omni_{token}_{index}.{ext}); the returned paths tell you the exact names. Relative paths resolve against the sandbox root; absolute paths must stay within it. PREFER this to inline base64 when caller and server share a filesystem. Omit to receive artifacts inline as MCP content blocks. |
271
+ | `response_format` | no | `{"type":"object"}` | OpenAI-compatible response format configuration for the planner. |
272
+ | `seed` | no | `{"type":"integer"}` | Optional random seed forwarded to the planner LLM. |
273
+ | `stop` | no | `{}` | stop sequences (string or array) |
274
+ | `temperature` | no | `{"type":"number"}` | Sampling temperature for the planner LLM. |
275
+ | `tool_choice` | no | `{}` | auto \| none \| required \| {type: function, ...} |
276
+ | `tools` | no | `{"items":{"type":"object"},"type":"array"}` | OpenAI-compatible application tool definitions passed to the planner. |
277
+ | `top_p` | no | `{"type":"number"}` | Nucleus sampling probability for the planner LLM. |
191
278
 
192
- # 2. List tools
279
+ ### `lemonade_docs`
280
+
281
+ Read this server's own API reference. Call with no arguments to list the pages it ships, then pass `page` (an `id` from that list) to read one as markdown. The docs are bundled with the server, so they describe the version actually running and work offline. Use this before hand-writing requests against Lemonade endpoints.
282
+
283
+ **Returns**
284
+
285
+ With `page`, success returns one `text` block containing the raw bundled Markdown page. With no arguments, success normally returns a summary `text` block followed by a JSON-stringified `pages` object containing `id`, `title`, and `bytes` for each page; if no bundled documentation is installed, it returns one explanatory `text` block instead. Unknown pages return `isError: true` with one explanatory `text` block. The same bundled documentation is also available through `GET /v1/docs`.
286
+
287
+ | Argument | Required | Schema | Description |
288
+ |---|:---:|---|---|
289
+ | `page` | no | `{"type":"string"}` | Optional. Page id from the listing, e.g. 'api/lemonade'. Omit to list. |
290
+
291
+ <!-- END GENERATED: mcp-tools -->
292
+
293
+ ## Quick Test with curl
294
+
295
+ After the `initialize` request in [`POST /mcp`](#post-mcp), list the tools and call one:
296
+
297
+ ```bash
193
298
  curl -s http://localhost:13305/mcp -H "Content-Type: application/json" \
194
299
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
195
300
 
196
- # 3. Call lemonade_chat
197
301
  curl -s http://localhost:13305/mcp -H "Content-Type: application/json" \
198
302
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"lemonade_chat","arguments":{"model":"Qwen3-1.7B-GGUF","messages":[{"role":"user","content":"hi"}],"max_tokens":16}}}'
199
303
  ```