ninerouter-mcp 1.0.1 → 1.1.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/README.md +315 -182
- package/dist/create-config.js +14 -14
- package/dist/index.js +3 -3
- package/dist/ninerouter-client.js +38 -36
- package/dist/server.js +9 -9
- package/dist/tools/common.js +1 -8
- package/dist/tools/embeddings.js +17 -12
- package/dist/tools/media.js +159 -86
- package/dist/tools/models.js +12 -9
- package/dist/tools/web.js +28 -22
- package/package.json +37 -8
package/README.md
CHANGED
|
@@ -1,182 +1,315 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
##
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
1
|
+
9Router MCP Server
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
[](https://github.com/k1rana/ninerouter-mcp/actions/workflows/ci.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/ninerouter-mcp)
|
|
6
|
+
[](https://www.npmjs.com/package/ninerouter-mcp)
|
|
7
|
+
|
|
8
|
+
MCP server that exposes 9Router capabilities as native tools for any MCP client: model discovery, automatic fallback, and Zod-validated inputs.
|
|
9
|
+
|
|
10
|
+
> **Status:** 9Router already hides provider-specific complexity behind a single API. This server exposes that API through MCP, so agents and apps can call web search, web fetch, image generation, TTS, STT, and embeddings as standard MCP tools — no skill-file loading, no per-provider glue code.
|
|
11
|
+
|
|
12
|
+
## Why use it
|
|
13
|
+
|
|
14
|
+
- Tools are always registered; no manual skill loading (saves tokens and context).
|
|
15
|
+
- Automatic model fallback when the primary model fails.
|
|
16
|
+
- Zod-validated inputs with clear error messages before any network call.
|
|
17
|
+
- One config file for endpoint, auth, and default models with fallback chains.
|
|
18
|
+
- Single transport (`stdio`); works with any MCP-capable client.
|
|
19
|
+
|
|
20
|
+
Chat / code generation is intentionally **not** included — that is what the host model is for.
|
|
21
|
+
|
|
22
|
+
## Contents
|
|
23
|
+
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
- [Configuration](#configuration)
|
|
26
|
+
- [Run](#run)
|
|
27
|
+
- [Tools](#tools)
|
|
28
|
+
- [Troubleshooting](#troubleshooting)
|
|
29
|
+
- [Development](#development)
|
|
30
|
+
- [License](#license)
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
The package is `ninerouter-mcp` on npm. The JSON body is identical across every MCP client — only the top-level key and file path differ. Paste the block into the right key in your client's config:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"ninerouter": {
|
|
39
|
+
"type": "stdio",
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["-y", "ninerouter-mcp"],
|
|
42
|
+
"env": {
|
|
43
|
+
"NINEROUTER_URL": "http://localhost:20128"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Top-level keys by client: VS Code (`servers`), OpenCode (`mcp`, rename `env` → `environment`, set `type: "local"` and `enabled: true`), Claude Code / Cursor / Windsurf / Claude Desktop / Zed (`mcpServers` or `context_servers`, drop the `type` line). JetBrains uses a UI dialog at **Settings → Tools → AI Assistant → MCP** with the same command, args, and env.
|
|
50
|
+
|
|
51
|
+
### Claude Code
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
claude mcp add --scope user ninerouter -e NINEROUTER_URL=http://localhost:20128 -- npx -y ninerouter-mcp
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Codex CLI
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codex mcp add ninerouter --env NINEROUTER_URL=http://localhost:20128 -- npx -y ninerouter-mcp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Hermes Agent
|
|
64
|
+
|
|
65
|
+
Add to `~/.hermes/config.yaml`:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
mcp_servers:
|
|
69
|
+
ninerouter:
|
|
70
|
+
command: npx
|
|
71
|
+
args:
|
|
72
|
+
- -y
|
|
73
|
+
- ninerouter-mcp
|
|
74
|
+
env:
|
|
75
|
+
NINEROUTER_URL: http://localhost:20128
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Configuration
|
|
79
|
+
|
|
80
|
+
The server needs a 9Router base URL. Optionally it accepts an API key and default-model fallbacks for each tool.
|
|
81
|
+
|
|
82
|
+
Sources, in priority order (highest first):
|
|
83
|
+
|
|
84
|
+
1. CLI flag `--config <path>` (or `--config-file`, `-c`, `NINEROUTER_CONFIG` env)
|
|
85
|
+
2. `~/.config/ninerouter-mcp/config.toml`
|
|
86
|
+
3. Environment variables: `NINEROUTER_URL`, `NINEROUTER_KEY`
|
|
87
|
+
|
|
88
|
+
If the config file exists, it wins over environment variables.
|
|
89
|
+
|
|
90
|
+
### Quick setup
|
|
91
|
+
|
|
92
|
+
Generate a starter config file and edit it:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx ninerouter-mcp create-config
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This writes `~/.config/ninerouter-mcp/config.toml` and refuses to overwrite an existing file.
|
|
99
|
+
|
|
100
|
+
### Manual setup (env vars)
|
|
101
|
+
|
|
102
|
+
**Windows (PowerShell):**
|
|
103
|
+
|
|
104
|
+
```powershell
|
|
105
|
+
$env:NINEROUTER_URL = "http://localhost:20128"
|
|
106
|
+
$env:NINEROUTER_KEY = "sk-..." # optional
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**macOS / Linux:**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
export NINEROUTER_URL=http://localhost:20128
|
|
113
|
+
export NINEROUTER_KEY=sk-... # optional
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`NINEROUTER_KEY` is optional and only required when your 9Router instance has auth enabled.
|
|
117
|
+
|
|
118
|
+
### Manual setup (config file)
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
# 9Router base URL (required)
|
|
122
|
+
base_url = "http://localhost:20128"
|
|
123
|
+
|
|
124
|
+
# Optional API key
|
|
125
|
+
# api_key = "sk-..."
|
|
126
|
+
|
|
127
|
+
# Default models with fallback support.
|
|
128
|
+
# Single string = one model.
|
|
129
|
+
# Array = tried in order, first success wins, errors are aggregated.
|
|
130
|
+
[default_models]
|
|
131
|
+
web_search = ["tavily/search", "brave-search/search"]
|
|
132
|
+
web_fetch = ["firecrawl/fetch", "jina-reader/fetch"]
|
|
133
|
+
generate_image = "openai/dall-e-3"
|
|
134
|
+
text_to_speech = "openai/tts-1"
|
|
135
|
+
speech_to_text = ["openai/whisper-1", "groq/whisper-large-v3-turbo"]
|
|
136
|
+
embeddings = "openai/text-embedding-3-small"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The `ninerouter` table is also accepted as an alias for top-level `base_url` and `api_key`:
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[ninerouter]
|
|
143
|
+
base_url = "http://localhost:20128"
|
|
144
|
+
api_key = "sk-..."
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Point to a non-default config file:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npx -y ninerouter-mcp --config /path/to/config.toml
|
|
151
|
+
# or
|
|
152
|
+
NINEROUTER_CONFIG=/path/to/config.toml npx -y ninerouter-mcp
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Run
|
|
156
|
+
|
|
157
|
+
After `npm install` from this repo:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm run build
|
|
161
|
+
npm start
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Or in watch mode during development:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npm run dev
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Published package users can run it directly:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx -y ninerouter-mcp
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Tools
|
|
177
|
+
|
|
178
|
+
Every tool is registered with the MCP server at startup. Unless noted, all tools return a single text content block with pretty-printed JSON.
|
|
179
|
+
|
|
180
|
+
### `list_models`
|
|
181
|
+
|
|
182
|
+
Discover valid model ids before calling other tools.
|
|
183
|
+
|
|
184
|
+
| Parameter | Type | Required | Description |
|
|
185
|
+
| --------- | ------ | -------- | -------------------------------------------------------------------------------------------------------- |
|
|
186
|
+
| `kind` | string | no | One of `chat`, `image`, `tts`, `embedding`, `web`, `stt`, `image-to-text`. Omit for default chat models. |
|
|
187
|
+
|
|
188
|
+
### `web_search`
|
|
189
|
+
|
|
190
|
+
Search the web through 9Router.
|
|
191
|
+
|
|
192
|
+
| Parameter | Type | Default | Notes |
|
|
193
|
+
| -------------- | ------ | ------- | ---------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `query` | string | — | Required. |
|
|
195
|
+
| `model` | string | — | 9Router model id (e.g. `tavily/search`). Falls back to `provider`, then `default_models.web_search`. |
|
|
196
|
+
| `provider` | string | — | Alias for `model`. |
|
|
197
|
+
| `maxResults` | number | `5` | 1–20. |
|
|
198
|
+
| `searchType` | string | `web` | `web` or `news`. |
|
|
199
|
+
| `country` | string | — | |
|
|
200
|
+
| `language` | string | — | |
|
|
201
|
+
| `timeRange` | string | — | |
|
|
202
|
+
| `domainFilter` | string | — | |
|
|
203
|
+
|
|
204
|
+
### `web_fetch`
|
|
205
|
+
|
|
206
|
+
Fetch a URL and return it as markdown, text, or HTML.
|
|
207
|
+
|
|
208
|
+
| Parameter | Type | Default | Notes |
|
|
209
|
+
| --------------- | ------ | ---------- | -------------------------------------------- |
|
|
210
|
+
| `url` | string | — | Required. Must be a valid URL. |
|
|
211
|
+
| `model` | string | — | e.g. `jina-reader/fetch`, `firecrawl/fetch`. |
|
|
212
|
+
| `provider` | string | — | Alias for `model`. |
|
|
213
|
+
| `format` | string | `markdown` | `markdown`, `text`, or `html`. |
|
|
214
|
+
| `maxCharacters` | number | `8000` | Truncation limit. |
|
|
215
|
+
|
|
216
|
+
### `generate_image`
|
|
217
|
+
|
|
218
|
+
Text-to-image generation. Always writes a file and returns the image as a base64 content block plus `{ outputPath, bytes, contentType }`. Omit `outputPath` to write to the OS temp dir.
|
|
219
|
+
|
|
220
|
+
| Parameter | Type | Default | Notes |
|
|
221
|
+
| ------------ | ------ | ----------- | --------------------------------------------------------------------------------------------------- |
|
|
222
|
+
| `prompt` | string | — | Required. |
|
|
223
|
+
| `model` | string | — | e.g. `openai/dall-e-3`, `gemini/gemini-3-pro-image-preview`. |
|
|
224
|
+
| `provider` | string | — | Alias for `model`. |
|
|
225
|
+
| `n` | number | `1` | 1–10. |
|
|
226
|
+
| `size` | string | `1024x1024` | |
|
|
227
|
+
| `quality` | string | — | `standard` or `hd`. |
|
|
228
|
+
| `outputPath` | string | OS tmpdir | Where to write the file. Default `os.tmpdir()/ninerouter-<slug>-<ts>.<ext>`. Ext from content-type. |
|
|
229
|
+
|
|
230
|
+
### `text_to_speech`
|
|
231
|
+
|
|
232
|
+
Synthesize audio. Always writes a file and returns the audio as a base64 content block plus `{ outputPath, bytes, contentType }`. Omit `outputPath` to write to the OS temp dir.
|
|
233
|
+
|
|
234
|
+
| Parameter | Type | Default | Notes |
|
|
235
|
+
| ------------ | ------ | --------- | --------------------------------------------------------------------------------------------------- |
|
|
236
|
+
| `input` | string | — | Required. |
|
|
237
|
+
| `model` | string | — | e.g. `openai/tts-1`, `edge-tts/vi-VN-HoaiMyNeural`. |
|
|
238
|
+
| `provider` | string | — | Alias for `model`. |
|
|
239
|
+
| `outputPath` | string | OS tmpdir | Where to write the file. Default `os.tmpdir()/ninerouter-<slug>-<ts>.<ext>`. Ext from content-type. |
|
|
240
|
+
|
|
241
|
+
### `speech_to_text`
|
|
242
|
+
|
|
243
|
+
Transcribe audio. Provide exactly one of `audioPath` or `audioBase64`.
|
|
244
|
+
|
|
245
|
+
| Parameter | Type | Default | Notes |
|
|
246
|
+
| ---------------- | ------ | ------- | ------------------------------------------------------- |
|
|
247
|
+
| `audioPath` | string | — | Local file path. |
|
|
248
|
+
| `audioBase64` | string | — | Base64 payload. |
|
|
249
|
+
| `fileName` | string | derived | Used for the multipart upload filename. |
|
|
250
|
+
| `model` | string | — | e.g. `openai/whisper-1`, `groq/whisper-large-v3-turbo`. |
|
|
251
|
+
| `provider` | string | — | Alias for `model`. |
|
|
252
|
+
| `language` | string | — | ISO-639-1 code, e.g. `en`, `vi`. |
|
|
253
|
+
| `prompt` | string | — | |
|
|
254
|
+
| `responseFormat` | string | `json` | `json`, `text`, `verbose_json`, `srt`, `vtt`. |
|
|
255
|
+
| `temperature` | number | — | 0–1. |
|
|
256
|
+
|
|
257
|
+
### `embeddings`
|
|
258
|
+
|
|
259
|
+
Generate embeddings for a string or a batch of strings.
|
|
260
|
+
|
|
261
|
+
| Parameter | Type | Default | Notes |
|
|
262
|
+
| ---------------- | --------------- | ------- | ------------------------------------------------------------- |
|
|
263
|
+
| `input` | string \| array | — | Required. Either one string or an array of non-empty strings. |
|
|
264
|
+
| `model` | string | — | e.g. `openai/text-embedding-3-small`. |
|
|
265
|
+
| `provider` | string | — | Alias for `model`. |
|
|
266
|
+
| `encodingFormat` | string | `float` | `float` or `base64`. |
|
|
267
|
+
| `dimensions` | number | — | Optional override. |
|
|
268
|
+
|
|
269
|
+
## Behavior notes
|
|
270
|
+
|
|
271
|
+
- **Fallback chain.** For every model-using tool: if `model`/`provider` is set, only that model is tried. Otherwise the chain in `default_models.<tool>` is tried in order. If all entries fail, the tool throws an `All models failed. Errors: ...` error that includes every per-model message.
|
|
272
|
+
- **`provider` is an alias for `model`** on every tool that accepts a model. Set whichever reads better for your use case.
|
|
273
|
+
- **Image and audio are always written to a file and returned as a content block.** `generate_image` and `text_to_speech` request `b64_json` / `mp3` from upstream, write the bytes to `outputPath` (or the OS temp dir if you omit it), and return the asset as an MCP `image` / `audio` content block plus `{ outputPath, bytes, contentType }`. The host can display inline or just use the path. Extension is derived from upstream `content-type`.
|
|
274
|
+
- **Config file wins over env vars.** If you need different settings for a single run, prefer `--config` over exporting env vars.
|
|
275
|
+
- **STT multipart upload.** The tool sends the audio as `multipart/form-data`; `fileName` only matters when the upstream provider inspects the filename.
|
|
276
|
+
- **Config is read once at startup.** Edit `config.toml` or change `NINEROUTER_URL` / `NINEROUTER_KEY`, then restart the MCP server in your client. Hot-reload is not implemented.
|
|
277
|
+
|
|
278
|
+
## Troubleshooting
|
|
279
|
+
|
|
280
|
+
- **`NINEROUTER_URL is required`** — set the env var or create `~/.config/ninerouter-mcp/config.toml` with `base_url`.
|
|
281
|
+
- **`No model specified and no default_models.<tool> configured`** — either pass `model` in the call or add a `default_models` entry to your config.
|
|
282
|
+
- **`All models failed. Errors: ...`** — every fallback model returned an error; the aggregated message includes each one for diagnosis.
|
|
283
|
+
- **Auth errors (401/403)** — your 9Router instance requires a key; set `NINEROUTER_KEY` or `api_key` in the config file.
|
|
284
|
+
- **STT fails with "Provide audioPath or audioBase64"** — exactly one of those two must be set.
|
|
285
|
+
|
|
286
|
+
## Development
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npm install
|
|
290
|
+
npm run dev # tsx watch mode
|
|
291
|
+
npm run build # tsc -> dist/
|
|
292
|
+
npm start # node dist/index.js
|
|
293
|
+
npm run check # typecheck + lint + prettier --check
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Project layout:
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
src/
|
|
300
|
+
index.ts # bin entry; dispatches create-config or server
|
|
301
|
+
server.ts # McpServer setup
|
|
302
|
+
ninerouter-client.ts # config + HTTP helpers
|
|
303
|
+
create-config.ts # `ninerouter-mcp create-config` subcommand
|
|
304
|
+
tools/
|
|
305
|
+
models.ts # list_models
|
|
306
|
+
web.ts # web_search, web_fetch
|
|
307
|
+
media.ts # generate_image, text_to_speech, speech_to_text
|
|
308
|
+
embeddings.ts # embeddings
|
|
309
|
+
common.ts # shared fallback + json helpers
|
|
310
|
+
config.example.toml # sample config (mirrors create-config output)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
package/dist/create-config.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { writeFile, mkdir, access } from
|
|
3
|
-
import { join } from
|
|
4
|
-
import { homedir } from
|
|
5
|
-
import { constants } from
|
|
6
|
-
const DEFAULT_CONFIG_DIR = join(homedir(),
|
|
7
|
-
const DEFAULT_CONFIG_PATH = join(DEFAULT_CONFIG_DIR,
|
|
2
|
+
import { writeFile, mkdir, access } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { constants } from 'node:fs';
|
|
6
|
+
const DEFAULT_CONFIG_DIR = join(homedir(), '.config', 'ninerouter-mcp');
|
|
7
|
+
const DEFAULT_CONFIG_PATH = join(DEFAULT_CONFIG_DIR, 'config.toml');
|
|
8
8
|
const DEFAULT_CONFIG = `# NineRouter MCP Configuration
|
|
9
9
|
# Generated on ${new Date().toISOString()}
|
|
10
10
|
|
|
@@ -48,23 +48,23 @@ async function createConfig() {
|
|
|
48
48
|
try {
|
|
49
49
|
await access(DEFAULT_CONFIG_PATH, constants.F_OK);
|
|
50
50
|
console.log(`⚠️ Config file already exists at: ${DEFAULT_CONFIG_PATH}`);
|
|
51
|
-
console.log(
|
|
52
|
-
console.log(
|
|
51
|
+
console.log('To avoid overwriting your config, the operation was cancelled.');
|
|
52
|
+
console.log('\nTo create a new config, first backup or delete the existing file.');
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
catch {
|
|
56
56
|
// File doesn't exist, safe to create
|
|
57
57
|
}
|
|
58
58
|
// Write config file
|
|
59
|
-
await writeFile(DEFAULT_CONFIG_PATH, DEFAULT_CONFIG,
|
|
59
|
+
await writeFile(DEFAULT_CONFIG_PATH, DEFAULT_CONFIG, 'utf8');
|
|
60
60
|
console.log(`✅ Config file created at: ${DEFAULT_CONFIG_PATH}`);
|
|
61
|
-
console.log(
|
|
62
|
-
console.log(
|
|
63
|
-
console.log(
|
|
64
|
-
console.log(
|
|
61
|
+
console.log('\nNext steps:');
|
|
62
|
+
console.log('1. Edit the config file to set your 9Router base URL');
|
|
63
|
+
console.log('2. Optionally configure default models for each tool');
|
|
64
|
+
console.log('3. Restart your MCP client to load the new config');
|
|
65
65
|
}
|
|
66
66
|
catch (error) {
|
|
67
|
-
console.error(
|
|
67
|
+
console.error('❌ Failed to create config file:', error);
|
|
68
68
|
process.exit(1);
|
|
69
69
|
}
|
|
70
70
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Check if user wants to create config
|
|
3
|
-
if (process.argv.includes(
|
|
4
|
-
await import(
|
|
3
|
+
if (process.argv.includes('create-config')) {
|
|
4
|
+
await import('./create-config.js');
|
|
5
5
|
}
|
|
6
6
|
else {
|
|
7
7
|
// Start MCP server
|
|
8
|
-
await import(
|
|
8
|
+
await import('./server.js');
|
|
9
9
|
}
|
|
10
10
|
export {};
|