@gtrabanco/pi-nan-provider 1.2.0 → 1.2.1
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/AGENTS.md +14 -10
- package/README.es.md +43 -26
- package/README.md +44 -27
- package/package.json +2 -2
- package/scripts/models.generated.ts +2 -2
- package/src/commands.ts +62 -149
- package/src/index.ts +7 -32
- package/src/mcp/api-key.ts +1 -26
- package/src/mcp/state.ts +0 -3
package/AGENTS.md
CHANGED
|
@@ -270,29 +270,33 @@ Since 1.0.0 the package version **aligns its MAJOR.MINOR with the pi release lin
|
|
|
270
270
|
the `SessionEntry` union, expanded `TurnEndEvent`, removed `shouldStopAfterTurn`,
|
|
271
271
|
`SessionManager` canonical for provider context. The package touches none of them.
|
|
272
272
|
- Since 0.10.0, this package uses native MCP via `pi.registerMcpServer()`.
|
|
273
|
-
|
|
273
|
+
The server is session-scoped, visible in `/mcp` with source "extension".
|
|
274
274
|
Server names and tool IDs:
|
|
275
|
-
- `nan-search` → tools: `mcp__nan_search__web_search`
|
|
276
|
-
(official remote, HTTP, `Authorization: Bearer <key>` header)
|
|
277
275
|
- `nan-media` → tools: `mcp__nan_media__generate_image/edit_image/text_to_speech/list_voices/speech_to_text/list_models/embed_text/rerank_documents/get_quota`
|
|
278
276
|
(community stdio, `npx -y nan-mcp-server@1.3.0`, 120s timeout; 9 tools as of 1.3.0)
|
|
279
|
-
- `exposure: "direct"`
|
|
277
|
+
- `exposure: "direct"` — tools are declared to the model.
|
|
280
278
|
- User `mcp.json` entries with the same name take precedence.
|
|
281
279
|
- `/nan-mcp enable/disable` calls `pi.registerMcpServer()` / `pi.unregisterMcpServer()` directly.
|
|
282
|
-
- Missing `NAN_API_KEY` at load:
|
|
280
|
+
- Missing `NAN_API_KEY` at load: media server NOT registered (console.warn).
|
|
281
|
+
- **`nan-search` was REMOVED in 1.2.1.** NaN's official remote MCP server
|
|
282
|
+
(`https://api.nan.builders/mcp`) now exposes ZERO tools: `initialize` returns an
|
|
283
|
+
empty `capabilities.tools` and `tools/list` returns `{"tools":[]}` (verified with a
|
|
284
|
+
valid key on 2026-10-15), and NaN deleted every MCP mention from its docs (the old
|
|
285
|
+
`/docs/mcp` redirects to `/docs/agent-setup`). The `web_search` tool no longer
|
|
286
|
+
exists anywhere in NaN's surface. Users must bring their own `web_search`/fetch MCP
|
|
287
|
+
server in `mcp.json` (README documents this). `NAN_MCP_TOOLS` is gone with it.
|
|
283
288
|
NaN integration facts remain:
|
|
284
289
|
- pi >=0.99.2 sanitizes MCP tool/namespace names by replacing every character except
|
|
285
|
-
`[A-Za-z0-9_]` with `"_"` (0.99.0–0.99.1 kept `-`). So `mcp__nan-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
(`nan-search` and `nan-media` do not collide, they differ in more than one character).
|
|
290
|
+
`[A-Za-z0-9_]` with `"_"` (0.99.0–0.99.1 kept `-`). So `mcp__nan-media__*` keeps its
|
|
291
|
+
underscore form from 0.99.2 onward. (The removed `nan-search` server used to
|
|
292
|
+
demonstrate the hyphen→underscore rename; do not rely on it.)
|
|
289
293
|
- pi 0.99.2+/1.0 accept `McpServerConfig.description` (system-prompt listing plus
|
|
290
294
|
tool-search ranking); `validateMcpServerConfig` in 0.99.0–0.99.1 ignores unknown
|
|
291
295
|
keys, so passing `description` stays compatible.
|
|
292
296
|
- NaN's official remote MCP server: `https://api.nan.builders/mcp` (host
|
|
293
297
|
root, NOT /v1; JSON-RPC 2.0 over streamable HTTP, stateless; same `sk-`
|
|
294
298
|
key, shared rate limit/quota). Spec: https://nan.builders/openapi.json
|
|
295
|
-
(tag "MCP"). Currently exposes
|
|
299
|
+
(tag "MCP"). **Currently exposes no tools** (see the removal note above).
|
|
296
300
|
- Community `nan-mcp-server` (https://github.com/luciferfran/nan-mcp-server):
|
|
297
301
|
stdio MCP server, version-pinned via NAN_MEDIA_MCP_VERSION (default 1.3.0)
|
|
298
302
|
or full command override via NAN_MEDIA_MCP_COMMAND. Tools (1.3.0): generate_image,
|
package/README.es.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# @gtrabanco/pi-nan-provider
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://github.com/gtrabanco/pi-nan-provider/releases)
|
|
5
|
+
[](https://cloud.nan.builders/r/7GK06FX8)
|
|
5
6
|
|
|
6
|
-
[NaN Builders](https://nan.builders) model provider + MCP nativo para [pi](https://github.com/earendil-works/pi).
|
|
7
|
+
[NaN Builders](https://cloud.nan.builders/r/7GK06FX8) model provider + MCP nativo para [pi](https://github.com/earendil-works/pi).
|
|
7
8
|
|
|
8
|
-
Registra el proveedor `nan` vía `pi.registerProvider()` usando la API OpenAI-compatible de NaN (`https://api.nan.builders/v1`), y registra
|
|
9
|
+
Registra el proveedor `nan` vía `pi.registerProvider()` usando la API OpenAI-compatible de NaN (`https://api.nan.builders/v1`), y registra el servidor MCP de media de la comunidad con `pi.registerMcpServer()`.
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
@@ -101,22 +102,48 @@ Consigue una clave en la [plataforma NaN](https://cloud.nan.builders/r/7GK06FX8)
|
|
|
101
102
|
|
|
102
103
|
## 🔌 MCP nativo
|
|
103
104
|
|
|
104
|
-
Dado que [pi 0.99.0 incluye un cliente MCP integrado](https://github.com/earendil-works/pi/blob/main/docs/usage.md), este paquete
|
|
105
|
+
Dado que [pi 0.99.0 incluye un cliente MCP integrado](https://github.com/earendil-works/pi/blob/main/docs/usage.md), este paquete usa MCP nativo (requiere `pi >=0.99, <2` — pi 0.99 hasta 1.2 son compatibles; el rango peer se amplió a `<2` en 0.11.0).
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
El puente de media está **activado por defecto** (por sesión, visible en `/mcp`). Usa `/nan-mcp` para gestionarlo.
|
|
107
108
|
|
|
108
|
-
> [!
|
|
109
|
-
>
|
|
109
|
+
> [!WARNING]
|
|
110
|
+
> **El servidor MCP remoto oficial de NaN ya no expone ninguna herramienta.** Hasta 1.2.0 este paquete
|
|
111
|
+
> incluía un puente `nan-search` apuntando a `https://api.nan.builders/mcp`, que exponía
|
|
112
|
+
> `web_search`. Ese servidor ahora responde a `initialize` con `capabilities.tools` vacío y a
|
|
113
|
+
> `tools/list` con `{"tools":[]}` — verificado con una clave válida el 2026-10-15 — y NaN eliminó toda
|
|
114
|
+
> mención a MCP de su documentación (la antigua página `/docs/mcp` ahora redirige a `/docs/agent-setup`).
|
|
115
|
+
> El puente se eliminó en **1.2.1** porque registrar un servidor que no expone nada solo produce un
|
|
116
|
+
> servidor sin herramientas en `/mcp`.
|
|
117
|
+
>
|
|
118
|
+
> **Trae tu propio `web_search` y fetch.** Instala un servidor MCP que los proporcione (p. ej. el par
|
|
119
|
+
> `fetch` + `brave-search` de los servidores MCP oficiales, o cualquier otro servidor MCP de
|
|
120
|
+
> búsqueda/fetch) y decláralo en tu `mcp.json` global:
|
|
121
|
+
>
|
|
122
|
+
> ```json
|
|
123
|
+
> {
|
|
124
|
+
> "mcpServers": {
|
|
125
|
+
> "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] },
|
|
126
|
+
> "web-search": {
|
|
127
|
+
> "command": "npx",
|
|
128
|
+
> "args": ["-y", "@modelcontextprotocol/server-brave-search"],
|
|
129
|
+
> "env": { "BRAVE_API_KEY": "tu-clave" }
|
|
130
|
+
> }
|
|
131
|
+
> }
|
|
132
|
+
> }
|
|
133
|
+
> ```
|
|
134
|
+
>
|
|
135
|
+
> Vuelve a comprobar `https://api.nan.builders/mcp` periódicamente: si NaN publica herramientas otra
|
|
136
|
+
> vez, este paquete puede restaurar el puente.
|
|
110
137
|
|
|
111
138
|
### 🛠️ Comando de Gestión: `/nan-mcp`
|
|
112
139
|
|
|
113
140
|
| Comando | Efecto |
|
|
114
141
|
| :--- | :--- |
|
|
115
|
-
| `/nan-mcp status` | Muestra el estado
|
|
116
|
-
| `/nan-mcp enable [target]` | Activa `
|
|
117
|
-
| `/nan-mcp disable [target]` |
|
|
142
|
+
| `/nan-mcp status` | Muestra el estado del puente de media y la resolución de la clave API. |
|
|
143
|
+
| `/nan-mcp enable [target]` | Activa `nan-mcp-server` (persiste). |
|
|
144
|
+
| `/nan-mcp disable [target]` | Lo desactiva de forma persistente. |
|
|
118
145
|
|
|
119
|
-
|
|
146
|
+
El puente resuelve la credencial almacenada de `/login nan` (almacenada → env). Si ninguna se resuelve, el servidor no se registra y se muestra un aviso en la salida de estado.
|
|
120
147
|
|
|
121
148
|
> [!NOTE]
|
|
122
149
|
> El registro ocurre cuando se carga la extensión. Si ejecutas `/login nan` **después** de que la sesión haya empezado, la factoría ya se ejecutó — usa `/reload` (o `/nan-mcp enable`) para registrar los puentes sin reiniciar pi.
|
|
@@ -171,15 +198,7 @@ conector con `pi config` → Built-in extensions → `mcp`.
|
|
|
171
198
|
|
|
172
199
|
---
|
|
173
200
|
|
|
174
|
-
###
|
|
175
|
-
*Puente oficial para herramientas remotas vía [https://api.nan.builders/mcp](https://nan.builders/docs/api).*
|
|
176
|
-
|
|
177
|
-
- **`mcp__nan_search__web_search(query, ...)`**: Realiza búsquedas web a través del gateway de NaN.
|
|
178
|
-
> pi 0.99.0–0.99.1 usaba la forma con guiones (`mcp__nan-search__web_search`); pi 0.99.2+ sanitiza
|
|
179
|
-
> los nombres de herramientas a solo `[A-Za-z0-9_]`, así que los guiones se convirtieron en
|
|
180
|
-
> guiones bajos. La forma con guiones bajos aplica desde 0.99.2 hasta pi 1.0.
|
|
181
|
-
|
|
182
|
-
### 2. Servidor MCP de Media (Comunidad)
|
|
201
|
+
### Servidor MCP de Media (Comunidad)
|
|
183
202
|
*Conecta [`nan-mcp-server`](https://github.com/luciferfran/nan-mcp-server) mediante un cliente stdio local mínimo.*
|
|
184
203
|
|
|
185
204
|
- **Por sesión**: El servidor se conecta al registrarse (eager). Aparece en `/mcp` con fuente "extension".
|
|
@@ -197,10 +216,9 @@ conector con `pi config` → Built-in extensions → `mcp`.
|
|
|
197
216
|
| `mcp__nan_media__rerank_documents` | Reordenación de documentos (Qwen3-Reranker) |
|
|
198
217
|
| `mcp__nan_media__get_quota` | Cuota de tokens por modelo (usado / tope / restante / reinicio) |
|
|
199
218
|
|
|
200
|
-
> pi 0.99.0–0.99.1 usaba
|
|
201
|
-
> `
|
|
202
|
-
>
|
|
203
|
-
> con guiones bajos aplica desde 0.99.2 hasta pi 1.0.
|
|
219
|
+
> pi 0.99.0–0.99.1 usaba la forma con guiones (`mcp__nan-media__*`); pi 0.99.2+ sanitiza los
|
|
220
|
+
> nombres de herramientas a solo `[A-Za-z0-9_]`, así que los guiones se convirtieron en guiones
|
|
221
|
+
> bajos. La forma con guiones bajos aplica desde 0.99.2 hasta pi 1.2.
|
|
204
222
|
|
|
205
223
|
#### 🔧 Configuración del Puente de Media
|
|
206
224
|
|
|
@@ -210,7 +228,6 @@ conector con `pi config` → Built-in extensions → `mcp`.
|
|
|
210
228
|
| `NAN_MEDIA_MCP_VERSION` | `1.3.0` | Versión del servidor fijada (recomendado). |
|
|
211
229
|
| `NAN_MEDIA_MCP_COMMAND` | — | Override del comando personalizado. |
|
|
212
230
|
| `NAN_MEDIA_MCP_TIMEOUT_MS` | `120000` | Timeout por llamada. |
|
|
213
|
-
| `NAN_MCP_TOOLS` | — | Override para el puente oficial (`0` para desactivar). |
|
|
214
231
|
|
|
215
232
|
#### 🤖 Detección Automática de Actualizaciones
|
|
216
233
|
|
|
@@ -384,4 +401,4 @@ bun test # Ejecutar todos los tests
|
|
|
384
401
|
bun run typecheck # Ejecutar typechecking
|
|
385
402
|
```
|
|
386
403
|
|
|
387
|
-
*Las versiones siguen semver estricto. CI publica automáticamente al hacer merge a `main`.*
|
|
404
|
+
*Las versiones siguen semver estricto. CI publica automáticamente al hacer merge a `main`.*
|
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# @gtrabanco/pi-nan-provider
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](https://github.com/gtrabanco/pi-nan-provider/releases)
|
|
5
|
+
[](https://cloud.nan.builders/r/7GK06FX8)
|
|
5
6
|
|
|
6
|
-
[NaN Builders](https://nan.builders) model provider + native MCP
|
|
7
|
+
[NaN Builders](https://cloud.nan.builders/r/7GK06FX8) model provider + a native MCP server for [pi](https://github.com/earendil-works/pi).
|
|
7
8
|
|
|
8
|
-
Registers the `nan` provider via `pi.registerProvider()` using NaN's OpenAI-compatible API (`https://api.nan.builders/v1`), and registers MCP
|
|
9
|
+
Registers the `nan` provider via `pi.registerProvider()` using NaN's OpenAI-compatible API (`https://api.nan.builders/v1`), and registers the community media MCP server via pi's native `pi.registerMcpServer()`.
|
|
9
10
|
|
|
10
11
|
---
|
|
11
12
|
|
|
@@ -99,24 +100,50 @@ You can override any model's `compat` per-model in `~/.pi/agent/models.json` (pi
|
|
|
99
100
|
|
|
100
101
|
Get a key from the [NaN platform](https://cloud.nan.builders/r/7GK06FX8) (user settings → API Keys; referral link).
|
|
101
102
|
|
|
102
|
-
## 🔌 Native MCP
|
|
103
|
+
## 🔌 Native MCP Server
|
|
103
104
|
|
|
104
|
-
Since [pi 0.99.0 ships a built-in MCP client](https://github.com/earendil-works/pi/blob/main/docs/usage.md), this package
|
|
105
|
+
Since [pi 0.99.0 ships a built-in MCP client](https://github.com/earendil-works/pi/blob/main/docs/usage.md), this package bridges to native MCP (requires `pi >=0.99, <2` — pi 0.99 through 1.2 are supported; the peer range was widened to `<2` in 0.11.0).
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
The media bridge is **enabled by default** (session-scoped, visible in `/mcp`). Use `/nan-mcp` to manage it.
|
|
107
108
|
|
|
108
|
-
> [!
|
|
109
|
-
>
|
|
109
|
+
> [!WARNING]
|
|
110
|
+
> **NaN's official remote MCP server no longer exposes any tool.** Until 1.2.0 this package shipped a
|
|
111
|
+
> `nan-search` bridge pointing at `https://api.nan.builders/mcp`, which exposed
|
|
112
|
+
> `web_search`. That server now answers `initialize` with an empty `capabilities.tools` and
|
|
113
|
+
> `tools/list` with `{"tools":[]}` — verified with a valid key on 2026-10-15 — and NaN removed every
|
|
114
|
+
> MCP mention from its documentation (the old `/docs/mcp` page now redirects to `/docs/agent-setup`).
|
|
115
|
+
> The bridge was removed in **1.2.1** because registering a server that exposes nothing only produces
|
|
116
|
+
> a tool-less server in `/mcp`.
|
|
117
|
+
>
|
|
118
|
+
> **Bring your own `web_search` and fetch.** Install an MCP server that provides them (e.g. the
|
|
119
|
+
> `fetch` + `brave-search` pair for the official MCP servers, or any other search/fetch MCP server)
|
|
120
|
+
> and declare it in your global `mcp.json`:
|
|
121
|
+
>
|
|
122
|
+
> ```json
|
|
123
|
+
> {
|
|
124
|
+
> "mcpServers": {
|
|
125
|
+
> "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] },
|
|
126
|
+
> "web-search": {
|
|
127
|
+
> "command": "npx",
|
|
128
|
+
> "args": ["-y", "@modelcontextprotocol/server-brave-search"],
|
|
129
|
+
> "env": { "BRAVE_API_KEY": "your-key" }
|
|
130
|
+
> }
|
|
131
|
+
> }
|
|
132
|
+
> }
|
|
133
|
+
> ```
|
|
134
|
+
>
|
|
135
|
+
> Re-check `https://api.nan.builders/mcp` periodically: if NaN publishes tools again, this package can
|
|
136
|
+
> restore the bridge.
|
|
110
137
|
|
|
111
138
|
### 🛠️ Management Command: `/nan-mcp`
|
|
112
139
|
|
|
113
140
|
| Command | Effect |
|
|
114
141
|
| :--- | :--- |
|
|
115
|
-
| `/nan-mcp status` | Shows
|
|
116
|
-
| `/nan-mcp enable [target]` | Enables `
|
|
117
|
-
| `/nan-mcp disable [target]` | Disables
|
|
142
|
+
| `/nan-mcp status` | Shows the media bridge state and API key resolution. |
|
|
143
|
+
| `/nan-mcp enable [target]` | Enables `nan-mcp-server` (persisted). |
|
|
144
|
+
| `/nan-mcp disable [target]` | Disables it persistently. |
|
|
118
145
|
|
|
119
|
-
|
|
146
|
+
The bridge resolves the stored `/login nan` credential (stored → env). If neither resolves, the server does not register and a warning is shown in the status output.
|
|
120
147
|
|
|
121
148
|
> [!NOTE]
|
|
122
149
|
> Registration happens when the extension loads. If you run `/login nan` **after** the session started, the factory has already run — run `/reload` (or `/nan-mcp enable`) to register the bridges without restarting pi.
|
|
@@ -168,15 +195,7 @@ registering a server that can never connect. To get MCP in the CLI, enable the c
|
|
|
168
195
|
|
|
169
196
|
---
|
|
170
197
|
|
|
171
|
-
###
|
|
172
|
-
*Official remote MCP server registered natively via `pi.registerMcpServer()`.*
|
|
173
|
-
|
|
174
|
-
- **`mcp__nan_search__web_search(query, ...)`**: Performs web searches through NaN's gateway.
|
|
175
|
-
> pi 0.99.0–0.99.1 used the hyphenated form (`mcp__nan-search__web_search`); pi 0.99.2+ sanitizes
|
|
176
|
-
> tool names to `[A-Za-z0-9_]` only, so hyphens became underscores. The underscore form applies
|
|
177
|
-
> from 0.99.2 onward through pi 1.0.
|
|
178
|
-
|
|
179
|
-
### 2. Community Media MCP Server
|
|
198
|
+
### Community Media MCP Server
|
|
180
199
|
*Bridges [`nan-mcp-server`](https://github.com/luciferfran/nan-mcp-server) via a minimal local stdio client.*
|
|
181
200
|
|
|
182
201
|
- **Session-scoped**: The server connects at registration time (eager). Appears in `/mcp` with source "extension".
|
|
@@ -194,10 +213,9 @@ registering a server that can never connect. To get MCP in the CLI, enable the c
|
|
|
194
213
|
| `mcp__nan_media__rerank_documents` | Document reranking (Qwen3-Reranker) |
|
|
195
214
|
| `mcp__nan_media__get_quota` | Per-model token quota (used / cap / remaining / reset) |
|
|
196
215
|
|
|
197
|
-
> pi 0.99.0–0.99.1 used the hyphenated
|
|
198
|
-
>
|
|
199
|
-
>
|
|
200
|
-
> through pi 1.0.
|
|
216
|
+
> pi 0.99.0–0.99.1 used the hyphenated form (`mcp__nan-media__*`); pi 0.99.2+ sanitizes tool
|
|
217
|
+
> names to `[A-Za-z0-9_]` only, so hyphens became underscores. The underscore form applies from
|
|
218
|
+
> 0.99.2 onward through pi 1.2.
|
|
201
219
|
|
|
202
220
|
#### 🔧 Media Bridge Configuration
|
|
203
221
|
|
|
@@ -207,7 +225,6 @@ registering a server that can never connect. To get MCP in the CLI, enable the c
|
|
|
207
225
|
| `NAN_MEDIA_MCP_VERSION` | `1.3.0` | Pinned server version (recommended). |
|
|
208
226
|
| `NAN_MEDIA_MCP_COMMAND` | — | Custom command override. |
|
|
209
227
|
| `NAN_MEDIA_MCP_TIMEOUT_MS` | `120000` | Per-call timeout. |
|
|
210
|
-
| `NAN_MCP_TOOLS` | — | Override for the official bridge (`0` to disable). |
|
|
211
228
|
|
|
212
229
|
#### 🤖 Automated Update Detection
|
|
213
230
|
|
|
@@ -381,4 +398,4 @@ bun test # Run all tests
|
|
|
381
398
|
bun run typecheck # Run typechecking
|
|
382
399
|
```
|
|
383
400
|
|
|
384
|
-
*Releases follow strict semver. CI publishes automatically on merge to `main`.*
|
|
401
|
+
*Releases follow strict semver. CI publishes automatically on merge to `main`.*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtrabanco/pi-nan-provider",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "NaN Builders (api.nan.builders) model provider for pi - OpenAI-compatible registration with a models.dev-generated fallback, tier-aware live catalog, and
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "NaN Builders (api.nan.builders) model provider for pi - OpenAI-compatible registration with a models.dev-generated fallback, tier-aware live catalog, and an optional community media MCP bridge",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
7
7
|
"pi-package",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// This file is auto-generated by scripts/generate-models.ts
|
|
2
2
|
// Do not edit manually — run `bun run generate-models` to update.
|
|
3
3
|
//
|
|
4
|
-
// Source: https://models.dev/api.json (provider "nan"), fetched 2026-10-
|
|
4
|
+
// Source: https://models.dev/api.json (provider "nan"), fetched 2026-10-16T17:43:45.590Z
|
|
5
5
|
// Provenance: every contextWindow/maxTokens/input/cost value traces to
|
|
6
6
|
// models.dev or to the per-entry notes below. Nothing is invented; entries
|
|
7
7
|
// models.dev documents incompletely are omitted and flagged instead.
|
|
@@ -459,7 +459,7 @@ export const NAN_GENERATED_MODELS: readonly GeneratedModelEntry[] = [
|
|
|
459
459
|
export const GENERATED_CATALOG_META = {
|
|
460
460
|
source: "https://models.dev/api.json",
|
|
461
461
|
modelsDevProvider: "nan",
|
|
462
|
-
fetchedAt: "2026-10-
|
|
462
|
+
fetchedAt: "2026-10-16T17:43:45.590Z",
|
|
463
463
|
modelCount: 6,
|
|
464
464
|
models: ["deepseek-v4-flash","gemma4","glm5.3-flash","mimo-v2.6-flash","qwen3.6","qwen3.8-flash"],
|
|
465
465
|
notes: [
|
package/src/commands.ts
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `/nan-mcp` — slash command configuring
|
|
2
|
+
* `/nan-mcp` — slash command configuring the native MCP server this
|
|
3
3
|
* package registers:
|
|
4
4
|
*
|
|
5
|
-
* - `nan-search` — the official NaN remote MCP server (api.nan.builders/mcp),
|
|
6
|
-
* exposing `mcp__nan_search__web_search` (pi >=0.99.2). Default: enabled.
|
|
7
|
-
* (pi 0.99.0–0.99.1 used the hyphenated form `mcp__nan-search__web_search`.)
|
|
8
5
|
* - `nan-media` — the community stdio media server (flux-2-klein / kokoro /
|
|
9
6
|
* whisper), exposing `mcp__nan_media__generate_image` etc. Default: enabled.
|
|
10
7
|
* (pi 0.99.0–0.99.1 used the hyphenated form `mcp__nan-media__*`.)
|
|
11
8
|
*
|
|
12
|
-
*
|
|
13
|
-
* /nan-mcp enables/disables
|
|
9
|
+
* The server is session-scoped (visible in /mcp, source "extension").
|
|
10
|
+
* /nan-mcp enables/disables it via pi.registerMcpServer / pi.unregisterMcpServer,
|
|
14
11
|
* persisting the toggle in nan-provider.json.
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* Explicit env vars (NAN_MCP_TOOLS, NAN_MEDIA_MCP) override the persisted
|
|
19
|
-
* toggles for the session.
|
|
13
|
+
* Target aliases: `nan-mcp-server` (`media`, `media-mcp`, `nan-media`).
|
|
14
|
+
* Explicit env var (NAN_MEDIA_MCP) overrides the persisted toggle for the session.
|
|
20
15
|
*
|
|
21
|
-
* Native behavior
|
|
16
|
+
* Native behavior:
|
|
22
17
|
* - `disable` IMMEDIATELY hides tools (unregisterMcpServer disconnects the
|
|
23
18
|
* server; the MCP extension hides tools when disconnected).
|
|
24
19
|
* - `enable` IMMEDIATELY registers the server for the current session.
|
|
@@ -29,55 +24,31 @@
|
|
|
29
24
|
|
|
30
25
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
31
26
|
import { mediaMcpCommand, mediaMcpEnabled, mediaMcpSource, mediaMcpEnvExplicit, mediaMcpEnvTruthy, mediaMcpTimeoutSec } from "./mcp/media-server.ts";
|
|
32
|
-
import { webSearchBridgeEnabled, webSearchBridgeSource, mcpToolsEnvExplicit, mcpToolsDisabled } from "./mcp/api-key.ts";
|
|
33
27
|
import { NAN_API_KEY_ENV, tryResolveNanApiKeyViaRegistry, resolveStoredNanApiKey } from "./mcp/api-key.ts";
|
|
34
28
|
import { NAN_STATE_FILE, MCP_SERVER_NAMES, readBridgeState, writeBridgeState, type BridgeKey, readState, writeState } from "./mcp/state.ts";
|
|
35
29
|
import { connectorMissingMessage, hasMcpConnector } from "./mcp/host-support.ts";
|
|
36
30
|
|
|
37
31
|
const USAGE =
|
|
38
|
-
"Usage: /nan-mcp [status] · /nan-mcp enable [
|
|
32
|
+
"Usage: /nan-mcp [status] · /nan-mcp enable [nan-mcp-server] · /nan-mcp disable [nan-mcp-server]";
|
|
39
33
|
|
|
40
34
|
/** Canonical bridge targets with aliases. */
|
|
41
35
|
const TARGETS: Record<string, BridgeKey> = {
|
|
42
|
-
"web-search": "webSearch",
|
|
43
|
-
search: "webSearch",
|
|
44
|
-
web_search: "webSearch",
|
|
45
|
-
official: "webSearch",
|
|
46
|
-
"nan-web-search": "webSearch",
|
|
47
36
|
"nan-mcp-server": "mediaMcp",
|
|
48
37
|
media: "mediaMcp",
|
|
49
38
|
"media-mcp": "mediaMcp",
|
|
50
39
|
"nan-media": "mediaMcp",
|
|
51
40
|
};
|
|
52
41
|
|
|
53
|
-
function
|
|
54
|
-
return
|
|
42
|
+
function envOverrideLabel(): string {
|
|
43
|
+
return `env NAN_MEDIA_MCP=${process.env.NAN_MEDIA_MCP ?? ""} (overrides persisted)`;
|
|
55
44
|
}
|
|
56
45
|
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
? `env NAN_MCP_TOOLS=${process.env.NAN_MCP_TOOLS ?? ""} (overrides persisted)`
|
|
60
|
-
: `env NAN_MEDIA_MCP=${process.env.NAN_MEDIA_MCP ?? ""} (overrides persisted)`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function sourceLabel(bridge: BridgeKey, source: "env" | "persisted" | "default"): string {
|
|
64
|
-
if (source === "env") return envOverrideLabel(bridge);
|
|
46
|
+
function sourceLabel(source: "env" | "persisted" | "default"): string {
|
|
47
|
+
if (source === "env") return envOverrideLabel();
|
|
65
48
|
if (source === "persisted") {
|
|
66
|
-
return `persisted in <agentDir>/${NAN_STATE_FILE} (
|
|
49
|
+
return `persisted in <agentDir>/${NAN_STATE_FILE} (nan-mcp-server: ${readBridgeState("mediaMcp")})`;
|
|
67
50
|
}
|
|
68
|
-
return "default (
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Resolve a bridge key to its native server name. */
|
|
72
|
-
function serverName(bridge: BridgeKey): string {
|
|
73
|
-
return MCP_SERVER_NAMES[bridge];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Describe the bridge in the status message. */
|
|
77
|
-
function bridgeDescription(bridge: BridgeKey): string {
|
|
78
|
-
return bridge === "webSearch"
|
|
79
|
-
? "web-search bridge (official NaN MCP → mcp__nan_search__web_search)"
|
|
80
|
-
: "nan-mcp-server bridge (community media MCP → mcp__nan_media__generate_image/edit_image/text_to_speech/list_voices/speech_to_text)";
|
|
51
|
+
return "default (bridge is enabled by default)";
|
|
81
52
|
}
|
|
82
53
|
|
|
83
54
|
/** Check if a native server of this name is currently registered. */
|
|
@@ -89,39 +60,27 @@ function nativeServerRegistered(pi: ExtensionAPI, name: string): boolean {
|
|
|
89
60
|
}
|
|
90
61
|
}
|
|
91
62
|
|
|
92
|
-
/** Status message listing
|
|
63
|
+
/** Status message listing the media bridge gate state and native registration.
|
|
93
64
|
* `ctx` is the command context (provides modelRegistry for key lookup).
|
|
94
65
|
*/
|
|
95
66
|
async function statusMessage(pi: ExtensionAPI, ctx: ExtensionCommandContext): Promise<string> {
|
|
96
67
|
const lines: string[] = [];
|
|
97
68
|
|
|
98
69
|
if (!hasMcpConnector(pi)) {
|
|
99
|
-
lines.push(connectorMissingMessage([MCP_SERVER_NAMES.
|
|
70
|
+
lines.push(connectorMissingMessage([MCP_SERVER_NAMES.mediaMcp]));
|
|
100
71
|
}
|
|
101
72
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Key status for web-search
|
|
110
|
-
const keyStatus = await resolveKeyStatus(ctx);
|
|
111
|
-
lines.push(` API key: ${keyStatus}.`);
|
|
112
|
-
}
|
|
113
|
-
if (bridge === "mediaMcp") {
|
|
114
|
-
lines.push(` Media spawn command: ${mediaMcpCommand().join(" ")}.`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
lines.push("Configure with /nan-mcp enable|disable [web-search|nan-mcp-server]; no target = both.");
|
|
73
|
+
const enabled = mediaMcpEnabled();
|
|
74
|
+
const source = mediaMcpSource();
|
|
75
|
+
const regStatus = nativeServerRegistered(pi, MCP_SERVER_NAMES.mediaMcp) ? "registered (native)" : "not registered";
|
|
76
|
+
lines.push(`nan-mcp-server bridge: ${enabled ? "enabled" : "disabled"} — ${sourceLabel(source)}, ${regStatus}.`);
|
|
77
|
+
lines.push(` Media spawn command: ${mediaMcpCommand().join(" ")}.`);
|
|
78
|
+
lines.push(` API key: ${await resolveKeyStatus(ctx)}.`);
|
|
79
|
+
lines.push("Configure with /nan-mcp enable|disable [nan-mcp-server]; no target for the media server.");
|
|
119
80
|
return lines.join("\n");
|
|
120
81
|
}
|
|
121
82
|
|
|
122
|
-
/** Check API key resolution status
|
|
123
|
-
* `ctx` is the command context (provides modelRegistry).
|
|
124
|
-
*/
|
|
83
|
+
/** Check API key resolution status. */
|
|
125
84
|
async function resolveKeyStatus(ctx: ExtensionCommandContext): Promise<string> {
|
|
126
85
|
const registryKey = await tryResolveNanApiKeyViaRegistry(ctx.modelRegistry);
|
|
127
86
|
const storedKey = resolveStoredNanApiKey();
|
|
@@ -130,37 +89,22 @@ async function resolveKeyStatus(ctx: ExtensionCommandContext): Promise<string> {
|
|
|
130
89
|
return "NOT SET — run /login nan or export NAN_API_KEY";
|
|
131
90
|
}
|
|
132
91
|
|
|
133
|
-
/** Parse an optional target argument; no target =
|
|
134
|
-
function parseTarget(token: string | undefined):
|
|
135
|
-
if (token === undefined) return
|
|
136
|
-
|
|
137
|
-
if (!key) return undefined;
|
|
138
|
-
return { bridges: [key], target: token.toLowerCase() };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function describeBridges(bridges: BridgeKey[]): string {
|
|
142
|
-
const names = bridges.map((bridge) => targetName(bridge));
|
|
143
|
-
return names.length === 2 ? "both bridges" : names.join(" and ");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Check if a bridge is effectively enabled (gate resolved).
|
|
148
|
-
*/
|
|
149
|
-
function bridgeEffectivelyEnabled(bridge: BridgeKey): boolean {
|
|
150
|
-
return bridge === "webSearch" ? webSearchBridgeEnabled() : mediaMcpEnabled();
|
|
92
|
+
/** Parse an optional target argument; no target = media server. */
|
|
93
|
+
function parseTarget(token: string | undefined): string | undefined {
|
|
94
|
+
if (token === undefined) return "mediaMcp";
|
|
95
|
+
return TARGETS[token.toLowerCase()];
|
|
151
96
|
}
|
|
152
97
|
|
|
153
98
|
export function registerNanMcpCommand(pi: ExtensionAPI): void {
|
|
154
99
|
pi.registerCommand("nan-mcp", {
|
|
155
100
|
description:
|
|
156
|
-
"NaN MCP configuration: enable/disable/status for
|
|
101
|
+
"NaN MCP configuration: enable/disable/status for the community media server",
|
|
157
102
|
getArgumentCompletions: (argumentPrefix: string) => {
|
|
158
103
|
const prefix = argumentPrefix.trim().toLowerCase();
|
|
159
104
|
const items = [
|
|
160
|
-
{ value: "enable", label: "enable", description: "Enable
|
|
161
|
-
{ value: "disable", label: "disable", description: "Disable
|
|
105
|
+
{ value: "enable", label: "enable", description: "Enable the media server and persist it" },
|
|
106
|
+
{ value: "disable", label: "disable", description: "Disable the media server persistently" },
|
|
162
107
|
{ value: "status", label: "status", description: "Show current server status and registration" },
|
|
163
|
-
{ value: "enable web-search", label: "enable web-search", description: "Enable the official NaN web-search server" },
|
|
164
108
|
{ value: "enable nan-mcp-server", label: "enable nan-mcp-server", description: "Enable the community media server" },
|
|
165
109
|
];
|
|
166
110
|
const filtered = items.filter((item) => item.value.startsWith(prefix));
|
|
@@ -181,20 +125,17 @@ export function registerNanMcpCommand(pi: ExtensionAPI): void {
|
|
|
181
125
|
if (parsed === undefined || rest.length > 0) {
|
|
182
126
|
ctx.ui.notify(
|
|
183
127
|
parsed === undefined
|
|
184
|
-
? `Unknown target "${[rawTarget, ...rest].filter(Boolean).join(" ")}".
|
|
128
|
+
? `Unknown target "${[rawTarget, ...rest].filter(Boolean).join(" ")}". Target: nan-mcp-server (or omit).`
|
|
185
129
|
: USAGE,
|
|
186
130
|
"warning",
|
|
187
131
|
);
|
|
188
132
|
return;
|
|
189
133
|
}
|
|
190
|
-
const { bridges, target } = parsed;
|
|
191
134
|
const enabled = subcommand === "enable";
|
|
135
|
+
const where = rawTarget ? `for "${rawTarget.toLowerCase()}"` : "for the media server";
|
|
192
136
|
|
|
193
|
-
|
|
194
|
-
writeBridgeState(bridge, enabled);
|
|
195
|
-
}
|
|
137
|
+
writeBridgeState("mediaMcp", enabled);
|
|
196
138
|
|
|
197
|
-
const where = target ? `for "${target}"` : "for both bridges";
|
|
198
139
|
const persistence = `persisted in <agentDir>/${NAN_STATE_FILE}`;
|
|
199
140
|
|
|
200
141
|
// Without an extension that handles `mcp_servers_change` pi cannot connect the
|
|
@@ -203,84 +144,56 @@ export function registerNanMcpCommand(pi: ExtensionAPI): void {
|
|
|
203
144
|
// so it applies as soon as a connector (built-in `mcp`) is available.
|
|
204
145
|
if (enabled && !hasMcpConnector(pi)) {
|
|
205
146
|
ctx.ui.notify(
|
|
206
|
-
`Enabled ${
|
|
207
|
-
connectorMissingMessage(
|
|
147
|
+
`Enabled ${where}, ${persistence} — NOT registered. ` +
|
|
148
|
+
connectorMissingMessage([MCP_SERVER_NAMES.mediaMcp]),
|
|
208
149
|
"warning",
|
|
209
150
|
);
|
|
210
151
|
return;
|
|
211
152
|
}
|
|
212
153
|
|
|
213
154
|
// Immediate native registration/unregistration.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
// Resolve key for media: stored credential → registry → env.
|
|
239
|
-
const registryKey = await tryResolveNanApiKeyViaRegistry(ctx.modelRegistry);
|
|
240
|
-
const storedKey = resolveStoredNanApiKey();
|
|
241
|
-
const apiKey = storedKey ?? registryKey ?? process.env[NAN_API_KEY_ENV];
|
|
242
|
-
if (apiKey) {
|
|
243
|
-
const mediaArgs = mediaMcpCommand();
|
|
244
|
-
pi.registerMcpServer(name, {
|
|
245
|
-
type: "stdio",
|
|
246
|
-
command: mediaArgs[0]!,
|
|
247
|
-
args: mediaArgs.slice(1),
|
|
248
|
-
env: { [NAN_API_KEY_ENV]: apiKey },
|
|
249
|
-
exposure: "direct",
|
|
250
|
-
timeout: mediaMcpTimeoutSec(),
|
|
251
|
-
});
|
|
252
|
-
} else {
|
|
253
|
-
ctx.ui.notify(
|
|
254
|
-
`${NAN_API_KEY_ENV} is not set. Export it or run /login nan.`,
|
|
255
|
-
"warning",
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
155
|
+
const name = MCP_SERVER_NAMES.mediaMcp;
|
|
156
|
+
if (enabled) {
|
|
157
|
+
if (mediaMcpEnabled()) {
|
|
158
|
+
const registryKey = await tryResolveNanApiKeyViaRegistry(ctx.modelRegistry);
|
|
159
|
+
const storedKey = resolveStoredNanApiKey();
|
|
160
|
+
const apiKey = storedKey ?? registryKey ?? process.env[NAN_API_KEY_ENV];
|
|
161
|
+
if (apiKey) {
|
|
162
|
+
const mediaArgs = mediaMcpCommand();
|
|
163
|
+
pi.registerMcpServer(name, {
|
|
164
|
+
type: "stdio",
|
|
165
|
+
command: mediaArgs[0]!,
|
|
166
|
+
args: mediaArgs.slice(1),
|
|
167
|
+
env: { [NAN_API_KEY_ENV]: apiKey },
|
|
168
|
+
exposure: "direct",
|
|
169
|
+
timeout: mediaMcpTimeoutSec(),
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
ctx.ui.notify(
|
|
173
|
+
`${NAN_API_KEY_ENV} is not set. Export it or run /login nan.`,
|
|
174
|
+
"warning",
|
|
175
|
+
);
|
|
259
176
|
}
|
|
260
|
-
} else {
|
|
261
|
-
pi.unregisterMcpServer(name);
|
|
262
177
|
}
|
|
178
|
+
} else {
|
|
179
|
+
pi.unregisterMcpServer(name);
|
|
263
180
|
}
|
|
264
181
|
|
|
265
182
|
if (enabled) {
|
|
266
|
-
|
|
267
|
-
const allRegistered = bridges.every((b: BridgeKey) =>
|
|
268
|
-
nativeServerRegistered(pi, serverName(b)),
|
|
269
|
-
);
|
|
270
|
-
if (allRegistered) {
|
|
183
|
+
if (nativeServerRegistered(pi, name)) {
|
|
271
184
|
ctx.ui.notify(
|
|
272
|
-
`Enabled ${
|
|
185
|
+
`Enabled ${where}, ${persistence}. Native server registered for this session.`,
|
|
273
186
|
"info",
|
|
274
187
|
);
|
|
275
188
|
} else {
|
|
276
189
|
ctx.ui.notify(
|
|
277
|
-
`Enabled ${
|
|
190
|
+
`Enabled ${where}, ${persistence} — API key missing; server not registered.`,
|
|
278
191
|
"warning",
|
|
279
192
|
);
|
|
280
193
|
}
|
|
281
194
|
} else {
|
|
282
195
|
ctx.ui.notify(
|
|
283
|
-
`Disabled ${
|
|
196
|
+
`Disabled ${where}, ${persistence}. Native server unregistered — tools hidden immediately.`,
|
|
284
197
|
"warning",
|
|
285
198
|
);
|
|
286
199
|
}
|
package/src/index.ts
CHANGED
|
@@ -15,17 +15,13 @@
|
|
|
15
15
|
* 0.84 alike), and the openai-completions streaming implementation is
|
|
16
16
|
* resolved dynamically — see provider-factory.ts for why.
|
|
17
17
|
*
|
|
18
|
-
* 2. MCP
|
|
19
|
-
* - `nan-search` — the official NaN remote MCP server
|
|
20
|
-
* (https://api.nan.builders/mcp), exposing `mcp__nan_search__web_search`
|
|
21
|
-
* (pi >=0.99.2; 0.99.0-0.99.1 kept the hyphen: `mcp__nan-search__web_search`).
|
|
22
|
-
* Default: enabled. NAN_MCP_TOOLS=0 to disable.
|
|
18
|
+
* 2. MCP server via pi's native registerMcpServer (pi >=0.99):
|
|
23
19
|
* - `nan-media` — the community stdio media server
|
|
24
20
|
* (flux-2-klein / kokoro / whisper), exposing
|
|
25
21
|
* `mcp__nan_media__generate_image` etc. Default: enabled,
|
|
26
22
|
* NAN_MEDIA_MCP=0 to disable.
|
|
27
23
|
*
|
|
28
|
-
*
|
|
24
|
+
* It is session-scoped (visible in /mcp with source "extension").
|
|
29
25
|
* Exposure: `direct` — tools are declared to the model like built-ins.
|
|
30
26
|
*/
|
|
31
27
|
|
|
@@ -40,7 +36,7 @@ import {
|
|
|
40
36
|
import { baselineModels } from "./fetch-models.ts";
|
|
41
37
|
import { createNanCompatibleProvider, type OpenAICompatibleProviderConfig } from "./provider-factory.ts";
|
|
42
38
|
import { PROVIDERS } from "./providers.ts";
|
|
43
|
-
import { NAN_API_KEY_ENV,
|
|
39
|
+
import { NAN_API_KEY_ENV, resolveNanApiKey, tryResolveNanApiKeyViaRegistry, resolveStoredNanApiKey } from "./mcp/api-key.ts";
|
|
44
40
|
import { mediaMcpCommand, mediaMcpEnabled, mediaMcpEnvExplicit, mediaMcpEnvTruthy, mediaMcpTimeoutSec } from "./mcp/media-server.ts";
|
|
45
41
|
import { registerMcpHostGuard } from "./mcp/host-support.ts";
|
|
46
42
|
|
|
@@ -95,26 +91,6 @@ async function registerProviderCompat(
|
|
|
95
91
|
const NO_NATIVE_MCP_GUARD_MSG =
|
|
96
92
|
"pi-nan-provider 0.10+ requires pi >= 0.99 for MCP tools (native MCP); upgrade pi or stay on package 0.9.x";
|
|
97
93
|
|
|
98
|
-
/** Register the official NaN web-search MCP server (session-scoped). */
|
|
99
|
-
async function registerWebSearchMcpServer(pi: ExtensionAPI, apiKey: string | undefined): Promise<boolean> {
|
|
100
|
-
if (!webSearchBridgeEnabled()) return false;
|
|
101
|
-
if (!apiKey) {
|
|
102
|
-
console.warn(
|
|
103
|
-
"[pi-nan-provider] NAN_API_KEY is not set — web-search server not registered. " +
|
|
104
|
-
"Run `/login nan` in pi or export `NAN_API_KEY=<your-key>`.",
|
|
105
|
-
);
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
pi.registerMcpServer("nan-search", {
|
|
109
|
-
type: "http",
|
|
110
|
-
url: "https://api.nan.builders/mcp",
|
|
111
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
112
|
-
exposure: "direct",
|
|
113
|
-
description: "Web search through NaN's gateway (official NaN MCP server)",
|
|
114
|
-
});
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
94
|
/** Register the community media stdio MCP server (session-scoped). */
|
|
119
95
|
function registerMediaMcpServer(pi: ExtensionAPI, apiKey: string | undefined): boolean {
|
|
120
96
|
if (!mediaMcpEnabled()) return false;
|
|
@@ -139,11 +115,11 @@ function registerMediaMcpServer(pi: ExtensionAPI, apiKey: string | undefined): b
|
|
|
139
115
|
}
|
|
140
116
|
|
|
141
117
|
/**
|
|
142
|
-
* Register MCP
|
|
118
|
+
* Register the MCP server via pi's native registerMcpServer.
|
|
143
119
|
*
|
|
144
|
-
* Session-scoped:
|
|
145
|
-
* visible to the model (exposure: "direct"), and user mcp.json
|
|
146
|
-
* with the same name
|
|
120
|
+
* Session-scoped: the server appears in /mcp with source "extension", is
|
|
121
|
+
* visible to the model (exposure: "direct"), and a user mcp.json entry
|
|
122
|
+
* with the same name takes precedence.
|
|
147
123
|
*
|
|
148
124
|
* On pi <0.99 (registerMcpServer absent) this does NOT bridge — it
|
|
149
125
|
* notifies once and skips (same loud-skip style as existing guards).
|
|
@@ -162,7 +138,6 @@ async function registerMcpServersNative(pi: ExtensionAPI): Promise<void> {
|
|
|
162
138
|
const storedKey = resolveStoredNanApiKey();
|
|
163
139
|
const apiKey = storedKey ?? process.env[NAN_API_KEY_ENV];
|
|
164
140
|
|
|
165
|
-
await registerWebSearchMcpServer(pi, apiKey);
|
|
166
141
|
registerMediaMcpServer(pi, apiKey);
|
|
167
142
|
}
|
|
168
143
|
|
package/src/mcp/api-key.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { bridgeSource, resolveBridgeEnabled } from "./state.ts";
|
|
2
1
|
import { readStoredCredential } from "@earendil-works/pi-coding-agent";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
|
-
* NaN API key resolution — shared across every surface (
|
|
4
|
+
* NaN API key resolution — shared across every surface (media, usage).
|
|
6
5
|
*
|
|
7
6
|
* Strategy: pi's stored credential (modelRegistry) wins → env var fallback.
|
|
8
7
|
* Never logs or embeds the key beyond the Authorization header.
|
|
@@ -40,8 +39,6 @@ export function resolveStoredNanApiKey(): string | undefined {
|
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
/** Env var that overrides the official web_search bridge. */
|
|
44
|
-
export const NAN_MCP_TOOLS_ENV = "NAN_MCP_TOOLS";
|
|
45
42
|
/** Env var that overrides the media bridge. */
|
|
46
43
|
export const NAN_MEDIA_MCP_ENV = "NAN_MEDIA_MCP";
|
|
47
44
|
/** The NaN API key env var used everywhere. */
|
|
@@ -74,18 +71,6 @@ export async function tryResolveNanApiKeyViaRegistry(
|
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
/** Whether MCP tool registration is disabled via env (NAN_MCP_TOOLS=0|false|off). */
|
|
78
|
-
export function mcpToolsDisabled(): boolean {
|
|
79
|
-
const value = process.env[NAN_MCP_TOOLS_ENV]?.trim().toLowerCase();
|
|
80
|
-
return value === "0" || value === "false" || value === "off";
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Whether NAN_MCP_TOOLS is explicitly set (any value). */
|
|
84
|
-
export function mcpToolsEnvExplicit(): boolean {
|
|
85
|
-
const value = process.env[NAN_MCP_TOOLS_ENV];
|
|
86
|
-
return value !== undefined && value.trim() !== "";
|
|
87
|
-
}
|
|
88
|
-
|
|
89
74
|
/** Whether NAN_MEDIA_MCP is explicitly set (any value). */
|
|
90
75
|
export function mediaMcpEnvExplicit(): boolean {
|
|
91
76
|
const value = process.env[NAN_MEDIA_MCP_ENV];
|
|
@@ -97,13 +82,3 @@ export function mediaMcpEnvTruthy(): boolean {
|
|
|
97
82
|
const value = process.env[NAN_MEDIA_MCP_ENV]?.trim().toLowerCase();
|
|
98
83
|
return value === "1" || value === "true" || value === "on";
|
|
99
84
|
}
|
|
100
|
-
|
|
101
|
-
/** Source of the effective web-search enablement (env / persisted / default). */
|
|
102
|
-
export function webSearchBridgeSource(): import("./state.ts").BridgeSource {
|
|
103
|
-
return bridgeSource("webSearch", mcpToolsEnvExplicit());
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Whether the web-search bridge is enabled by gate (env → persisted → default). */
|
|
107
|
-
export function webSearchBridgeEnabled(): boolean {
|
|
108
|
-
return resolveBridgeEnabled("webSearch", mcpToolsEnvExplicit(), !mcpToolsDisabled(), true);
|
|
109
|
-
}
|
package/src/mcp/state.ts
CHANGED
|
@@ -17,8 +17,6 @@ import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
|
17
17
|
export const NAN_STATE_FILE = "nan-provider.json";
|
|
18
18
|
|
|
19
19
|
export interface NanProviderState {
|
|
20
|
-
/** Persisted enablement of the official web_search MCP bridge. */
|
|
21
|
-
webSearch?: boolean;
|
|
22
20
|
/** Persisted enablement of the community nan-mcp-server media bridge. */
|
|
23
21
|
mediaMcp?: boolean;
|
|
24
22
|
}
|
|
@@ -27,7 +25,6 @@ export type BridgeKey = keyof NanProviderState;
|
|
|
27
25
|
|
|
28
26
|
/** Server name each bridge registers with `pi.registerMcpServer()`. Single source of truth. */
|
|
29
27
|
export const MCP_SERVER_NAMES: Record<BridgeKey, string> = {
|
|
30
|
-
webSearch: "nan-search",
|
|
31
28
|
mediaMcp: "nan-media",
|
|
32
29
|
};
|
|
33
30
|
|