@tikoci/rosetta 0.2.0 → 0.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/README.md +122 -113
- package/bin/rosetta.js +10 -4
- package/package.json +4 -1
- package/src/db.ts +2 -15
- package/src/mcp.ts +38 -18
- package/src/paths.ts +92 -0
- package/src/query.test.ts +5 -4
- package/src/query.ts +2 -2
- package/src/release.test.ts +10 -8
- package/src/setup.ts +106 -42
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
MCP server for searching [MikroTik RouterOS documentation](https://help.mikrotik.com/docs/spaces/ROS/overview). Gives your AI assistant searchable access to 317 documentation pages, 4,860 property definitions, 40,000-entry command tree, and 144 hardware product specs — with direct links to help.mikrotik.com.
|
|
4
4
|
|
|
5
|
-
Tested with **Claude Desktop**, **Claude Code**, **VS Code Copilot** (including Copilot CLI), and **
|
|
5
|
+
Tested with **Claude Desktop**, **Claude Code**, **VS Code Copilot** (including Copilot CLI), **Cursor**, and **OpenAI Codex** on macOS, Linux, and Windows.
|
|
6
6
|
|
|
7
7
|
## What is SQL-as-RAG?
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Most retrieval-augmented generation (RAG) systems use vector embeddings to searc
|
|
|
10
10
|
|
|
11
11
|
For structured technical documentation like RouterOS, full-text search with [BM25 ranking](https://www.sqlite.org/fts5.html#the_bm25_function) beats vector similarity. Technical terms like "dhcp-snooping" or "/ip/firewall/filter" are exact tokens — [porter stemming](https://www.sqlite.org/fts5.html#porter_tokenizer) and proximity matching handle the rest. No embedding pipeline, no vector database, no API keys. Just a single SQLite file that searches in milliseconds.
|
|
12
12
|
|
|
13
|
-
The data flows: **HTML docs → SQLite extraction → FTS5 indexes → MCP tools → your AI assistant.** The database is built once from MikroTik's official Confluence documentation export, then the MCP server exposes
|
|
13
|
+
The data flows: **HTML docs → SQLite extraction → FTS5 indexes → MCP tools → your AI assistant.** The database is built once from MikroTik's official Confluence documentation export, then the MCP server exposes 11 search tools over stdio transport.
|
|
14
14
|
|
|
15
15
|
## What's Inside
|
|
16
16
|
|
|
@@ -24,45 +24,53 @@ The data flows: **HTML docs → SQLite extraction → FTS5 indexes → MCP tools
|
|
|
24
24
|
|
|
25
25
|
## Quick Start
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
### Option A: Install with Bun (recommended)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Zero install, zero config, no binary signing issues. Requires [Bun](https://bun.sh/) — no Gatekeeper or SmartScreen warnings since there's no compiled binary to sign.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
**1. Install Bun** (if you don't have it):
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
| macOS (Intel) | `rosetta-macos-x64.zip` |
|
|
37
|
-
| Windows | `rosetta-windows-x64.zip` |
|
|
38
|
-
| Linux | `rosetta-linux-x64.zip` |
|
|
33
|
+
```sh
|
|
34
|
+
# macOS / Linux
|
|
35
|
+
curl -fsSL https://bun.sh/install | bash
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
# Windows
|
|
38
|
+
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
39
|
+
```
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
**2. Configure your MCP client** with `bunx @tikoci/rosetta` as the command. No setup step needed — the database downloads automatically on first launch (~50 MB compressed).
|
|
43
42
|
|
|
44
|
-
|
|
43
|
+
<details>
|
|
44
|
+
<summary><b>VS Code Copilot</b></summary>
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
Open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), choose **"MCP: Add Server…"**, select **"Command (stdio)"**, enter `bunx` as the command, and `@tikoci/rosetta` as the argument.
|
|
47
|
+
|
|
48
|
+
Or add to User Settings JSON (`Cmd+Shift+P` → "Preferences: Open User Settings (JSON)"):
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
```json
|
|
51
|
+
"mcp": {
|
|
52
|
+
"servers": {
|
|
53
|
+
"rosetta": {
|
|
54
|
+
"command": "bunx",
|
|
55
|
+
"args": ["@tikoci/rosetta"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
53
59
|
```
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
</details>
|
|
56
62
|
|
|
57
|
-
>
|
|
58
|
-
>
|
|
59
|
-
> **Windows SmartScreen:** If Windows warns about an unrecognized app, click **More info → Run anyway**.
|
|
63
|
+
<details>
|
|
64
|
+
<summary><b>Claude Code</b></summary>
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
```sh
|
|
67
|
+
claude mcp add rosetta -- bunx @tikoci/rosetta
|
|
68
|
+
```
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
</details>
|
|
64
71
|
|
|
65
|
-
|
|
72
|
+
<details>
|
|
73
|
+
<summary><b>Claude Desktop</b></summary>
|
|
66
74
|
|
|
67
75
|
Edit your Claude Desktop config file:
|
|
68
76
|
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
@@ -74,37 +82,97 @@ Add (or merge into existing config):
|
|
|
74
82
|
{
|
|
75
83
|
"mcpServers": {
|
|
76
84
|
"rosetta": {
|
|
77
|
-
"command": "
|
|
85
|
+
"command": "bunx",
|
|
86
|
+
"args": ["@tikoci/rosetta"]
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
90
|
```
|
|
82
91
|
|
|
83
|
-
|
|
92
|
+
> **PATH note:** Claude Desktop on macOS doesn't always inherit your shell PATH. If `bunx` isn't found, use the full path — typically `~/.bun/bin/bunx`. Run `which bunx` to find it, or use `bunx @tikoci/rosetta --setup` which prints the full-path config for you.
|
|
84
93
|
|
|
85
|
-
|
|
94
|
+
Then **restart Claude Desktop**.
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
</details>
|
|
97
|
+
|
|
98
|
+
<details>
|
|
99
|
+
<summary><b>GitHub Copilot CLI</b></summary>
|
|
90
100
|
|
|
91
|
-
|
|
101
|
+
Inside a `copilot` session, type `/mcp add` to open the interactive form:
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
- **Server Name:** `routeros-rosetta`
|
|
104
|
+
- **Server Type:** 2 (STDIO)
|
|
105
|
+
- **Command:** `bunx @tikoci/rosetta`
|
|
94
106
|
|
|
95
|
-
|
|
107
|
+
Press <kbd>Tab</kbd> to navigate fields, <kbd>Ctrl+S</kbd> to save.
|
|
108
|
+
|
|
109
|
+
</details>
|
|
110
|
+
|
|
111
|
+
<details>
|
|
112
|
+
<summary><b>Cursor</b></summary>
|
|
113
|
+
|
|
114
|
+
Open **Settings → MCP** and add a new server:
|
|
96
115
|
|
|
97
116
|
```json
|
|
98
|
-
|
|
99
|
-
"
|
|
117
|
+
{
|
|
118
|
+
"mcpServers": {
|
|
100
119
|
"rosetta": {
|
|
101
|
-
"command": "
|
|
120
|
+
"command": "bunx",
|
|
121
|
+
"args": ["@tikoci/rosetta"]
|
|
102
122
|
}
|
|
103
123
|
}
|
|
104
124
|
}
|
|
105
125
|
```
|
|
106
126
|
|
|
107
|
-
|
|
127
|
+
</details>
|
|
128
|
+
|
|
129
|
+
<details>
|
|
130
|
+
<summary><b>OpenAI Codex</b></summary>
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
codex mcp add rosetta -- bunx @tikoci/rosetta
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
> **Note:** ChatGPT Apps require a remote HTTPS MCP endpoint and cannot use local stdio servers like this one. Codex (CLI and desktop app) supports stdio and works with `bunx`.
|
|
137
|
+
|
|
138
|
+
</details>
|
|
139
|
+
|
|
140
|
+
**That's it.** First launch takes a moment to download the database; subsequent starts are instant. The database is stored in `~/.rosetta/ros-help.db`.
|
|
141
|
+
|
|
142
|
+
> **Verify it works:** Run `bunx @tikoci/rosetta --setup` to see the database status and print config for all MCP clients.
|
|
143
|
+
>
|
|
144
|
+
> **Auto-update:** `bunx` checks the npm registry each session and uses the latest published version automatically. No manual update needed. (Note: the `~/.rosetta/ros-help.db` database persists across updates — it's re-downloaded only when missing or when you run `--setup --force`.)
|
|
145
|
+
|
|
146
|
+
### Option B: Pre-built binary (no runtime needed)
|
|
147
|
+
|
|
148
|
+
Download a compiled binary from [Releases](https://github.com/tikoci/rosetta/releases) — no Bun, Node.js, or other tools required.
|
|
149
|
+
|
|
150
|
+
**1. Download** the ZIP for your platform from the [latest release](https://github.com/tikoci/rosetta/releases/latest):
|
|
151
|
+
|
|
152
|
+
| Platform | File |
|
|
153
|
+
|----------|------|
|
|
154
|
+
| macOS (Apple Silicon) | `rosetta-macos-arm64.zip` |
|
|
155
|
+
| macOS (Intel) | `rosetta-macos-x64.zip` |
|
|
156
|
+
| Windows | `rosetta-windows-x64.zip` |
|
|
157
|
+
| Linux | `rosetta-linux-x64.zip` |
|
|
158
|
+
|
|
159
|
+
Extract the ZIP to a permanent location (e.g., `~/rosetta` or `C:\rosetta`).
|
|
160
|
+
|
|
161
|
+
**2. Run setup** to download the database and see MCP client config:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
./rosetta --setup
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
On Windows: `.\rosetta.exe --setup`
|
|
168
|
+
|
|
169
|
+
> **macOS Gatekeeper:** If macOS blocks the binary: `xattr -d com.apple.quarantine ./rosetta` or go to **System Settings → Privacy & Security → Allow Anyway**.
|
|
170
|
+
>
|
|
171
|
+
> **Windows SmartScreen:** Click **More info → Run anyway**.
|
|
172
|
+
|
|
173
|
+
**3. Configure your MCP client** using the config printed by `--setup`. It uses the full path to the binary — paste it into your MCP client's config as shown.
|
|
174
|
+
|
|
175
|
+
### Try It
|
|
108
176
|
|
|
109
177
|
Ask your AI assistant questions like:
|
|
110
178
|
|
|
@@ -117,7 +185,7 @@ Ask your AI assistant questions like:
|
|
|
117
185
|
|
|
118
186
|
## MCP Tools
|
|
119
187
|
|
|
120
|
-
The server provides
|
|
188
|
+
The server provides 11 tools, designed to work together:
|
|
121
189
|
|
|
122
190
|
| Tool | What it does |
|
|
123
191
|
|------|-------------|
|
|
@@ -127,6 +195,7 @@ The server provides 10 tools, designed to work together:
|
|
|
127
195
|
| `routeros_search_properties` | Search across 4,860 property names and descriptions |
|
|
128
196
|
| `routeros_command_tree` | Browse the `/ip/firewall/filter` style command hierarchy |
|
|
129
197
|
| `routeros_search_callouts` | Search warnings, notes, and tips across all pages |
|
|
198
|
+
| `routeros_search_changelogs` | Search parsed changelog entries — filter by version range, category, breaking changes |
|
|
130
199
|
| `routeros_command_version_check` | Check which RouterOS versions include a command |
|
|
131
200
|
| `routeros_device_lookup` | Hardware specs for 144 MikroTik products — filter by architecture, RAM, storage, PoE, wireless, LTE |
|
|
132
201
|
| `routeros_stats` | Database health: page/property/command counts, coverage stats |
|
|
@@ -134,78 +203,18 @@ The server provides 10 tools, designed to work together:
|
|
|
134
203
|
|
|
135
204
|
The AI assistant typically starts with `routeros_search`, then drills into specific pages, properties, or the command tree based on what it finds. Each tool's description includes workflow hints (e.g., "→ use `routeros_get_page` to read full content") and empty-result suggestions so the AI knows how to chain tools together — this is where most of the tuning effort goes.
|
|
136
205
|
|
|
137
|
-
##
|
|
138
|
-
|
|
139
|
-
If you have [Bun](https://bun.sh/) installed and prefer not to use the pre-built binary — for example, to avoid Gatekeeper/SmartScreen warnings, or to inspect the code you're running — you can run the MCP server directly from source. No HTML export or command tree data is needed; the database is downloaded from GitHub Releases just like the binary option.
|
|
140
|
-
|
|
141
|
-
### 1. Install Bun
|
|
142
|
-
|
|
143
|
-
```sh
|
|
144
|
-
# macOS / Linux
|
|
145
|
-
curl -fsSL https://bun.sh/install | bash
|
|
146
|
-
# or: brew install oven-sh/bun/bun
|
|
147
|
-
|
|
148
|
-
# Windows
|
|
149
|
-
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### 2. Download and Install
|
|
153
|
-
|
|
154
|
-
```sh
|
|
155
|
-
git clone https://github.com/tikoci/rosetta.git
|
|
156
|
-
cd rosetta
|
|
157
|
-
bun install
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Or download the source archive from the [latest release](https://github.com/tikoci/rosetta/releases/latest) ("Source code" ZIP or tarball), extract it, and run `bun install`.
|
|
161
|
-
|
|
162
|
-
### 3. Run Setup
|
|
163
|
-
|
|
164
|
-
```sh
|
|
165
|
-
bun run src/mcp.ts --setup
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
This downloads the documentation database and prints MCP client configuration. The config uses `bun` as the command with `src/mcp.ts` as the entrypoint:
|
|
169
|
-
|
|
170
|
-
#### Claude Desktop
|
|
171
|
-
|
|
172
|
-
```json
|
|
173
|
-
{
|
|
174
|
-
"mcpServers": {
|
|
175
|
-
"rosetta": {
|
|
176
|
-
"command": "bun",
|
|
177
|
-
"args": ["run", "src/mcp.ts"],
|
|
178
|
-
"cwd": "/path/to/rosetta"
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
#### Claude Code
|
|
185
|
-
|
|
186
|
-
```sh
|
|
187
|
-
claude mcp add rosetta -- bun run src/mcp.ts --cwd /path/to/rosetta
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
#### VS Code Copilot
|
|
191
|
-
|
|
192
|
-
Open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`), choose **"MCP: Add Server…"**, select **"Command (stdio)"**, and enter `bun run src/mcp.ts` with the working directory set to the rosetta folder.
|
|
193
|
-
|
|
194
|
-
Or add to your User Settings JSON:
|
|
195
|
-
|
|
196
|
-
```json
|
|
197
|
-
"mcp": {
|
|
198
|
-
"servers": {
|
|
199
|
-
"rosetta": {
|
|
200
|
-
"command": "bun",
|
|
201
|
-
"args": ["run", "src/mcp.ts"],
|
|
202
|
-
"cwd": "/path/to/rosetta"
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
206
|
+
## Troubleshooting
|
|
207
207
|
|
|
208
|
-
|
|
208
|
+
| Issue | Solution |
|
|
209
|
+
|-------|----------|
|
|
210
|
+
| **First launch is slow** | One-time database download (~50 MB). Subsequent starts are instant. |
|
|
211
|
+
| **`npx @tikoci/rosetta` fails** | This package requires Bun, not Node.js. Use `bunx` instead of `npx`. |
|
|
212
|
+
| **`npm install -g` then `rosetta` fails** | Global npm install works if Bun is on PATH — it delegates to `bun` at runtime. But prefer `bunx` — it's simpler and auto-updates. |
|
|
213
|
+
| **ChatGPT Apps can't connect with `bunx @tikoci/rosetta`** | Expected: ChatGPT Apps supports remote HTTPS MCP endpoints, not local stdio command launch. Use OpenAI Codex for local stdio, or deploy/tunnel a remote MCP URL for ChatGPT. |
|
|
214
|
+
| **Claude Desktop can't find `bunx`** | Claude Desktop on macOS may not inherit shell PATH. Use the full path to bunx (run `which bunx` to find it, typically `~/.bun/bin/bunx`). `bunx @tikoci/rosetta --setup` prints the full-path config. |
|
|
215
|
+
| **macOS Gatekeeper blocks binary** | Use `bunx` install (no Gatekeeper issues), or: `xattr -d com.apple.quarantine ./rosetta` |
|
|
216
|
+
| **Windows SmartScreen warning** | Use `bunx` install (no SmartScreen issues), or click **More info → Run anyway** |
|
|
217
|
+
| **How to update** | `bunx` always uses the latest published version. For binaries, re-download from [Releases](https://github.com/tikoci/rosetta/releases/latest). |
|
|
209
218
|
|
|
210
219
|
## Building from Source
|
|
211
220
|
|
|
@@ -271,7 +280,7 @@ This cross-compiles to macOS (arm64 + x64), Windows (x64), and Linux (x64), crea
|
|
|
271
280
|
|
|
272
281
|
```text
|
|
273
282
|
src/
|
|
274
|
-
├── mcp.ts # MCP server (
|
|
283
|
+
├── mcp.ts # MCP server (11 tools, stdio) + CLI dispatch
|
|
275
284
|
├── setup.ts # --setup: DB download + MCP client config
|
|
276
285
|
├── query.ts # NL → FTS5 query planner, BM25 ranking
|
|
277
286
|
├── db.ts # SQLite schema, WAL mode, FTS5 triggers
|
package/bin/rosetta.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* bin/rosetta.js — Entry point for `
|
|
3
|
+
* bin/rosetta.js — Entry point for `bunx @tikoci/rosetta` (and npx fallback).
|
|
4
4
|
*
|
|
5
5
|
* The server uses bun:sqlite and other Bun APIs, so it requires the Bun runtime.
|
|
6
6
|
* When run under Bun (via bunx), this imports src/mcp.ts directly.
|
|
7
|
-
* When run under Node (via npx), this spawns `bun
|
|
7
|
+
* When run under Node (via npx), this spawns `bun` as a subprocess if available.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { dirname, join } from "node:path";
|
|
@@ -17,17 +17,23 @@ if (typeof Bun !== "undefined") {
|
|
|
17
17
|
// Running under Bun — import TypeScript entry directly
|
|
18
18
|
await import(entry);
|
|
19
19
|
} else {
|
|
20
|
-
// Running under Node — delegate to Bun
|
|
20
|
+
// Running under Node — try to delegate to Bun, but warn the user
|
|
21
|
+
console.error("Note: rosetta requires the Bun runtime. Attempting to run via bun...");
|
|
22
|
+
console.error();
|
|
21
23
|
const { spawn } = await import("node:child_process");
|
|
22
24
|
const proc = spawn("bun", ["run", entry, ...process.argv.slice(2)], {
|
|
23
25
|
stdio: "inherit",
|
|
24
26
|
});
|
|
25
27
|
proc.on("error", (err) => {
|
|
26
28
|
if (err.code === "ENOENT") {
|
|
27
|
-
console.error("rosetta requires
|
|
29
|
+
console.error("rosetta requires Bun (bun:sqlite is not available in Node.js).\n");
|
|
30
|
+
console.error("Recommended: install Bun, then use bunx instead of npx:\n");
|
|
31
|
+
console.error(" curl -fsSL https://bun.sh/install | bash");
|
|
32
|
+
console.error(" bunx @tikoci/rosetta --setup\n");
|
|
28
33
|
console.error("Install Bun: https://bun.sh");
|
|
29
34
|
process.exit(1);
|
|
30
35
|
}
|
|
36
|
+
console.error(`Failed to start bun: ${err.message}`);
|
|
31
37
|
process.exit(1);
|
|
32
38
|
});
|
|
33
39
|
proc.on("exit", (code) => process.exit(code ?? 1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tikoci/rosetta",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "RouterOS documentation as SQLite FTS5 — RAG search + command glossary via MCP",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
26
26
|
"zod": "^4.3.6"
|
|
27
27
|
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"bun": ">=1.1"
|
|
30
|
+
},
|
|
28
31
|
"devDependencies": {
|
|
29
32
|
"@biomejs/biome": "^2.4.7",
|
|
30
33
|
"@types/bun": "^1.3.10",
|
package/src/db.ts
CHANGED
|
@@ -20,22 +20,9 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import sqlite from "bun:sqlite";
|
|
23
|
-
import
|
|
23
|
+
import { resolveDbPath } from "./paths.ts";
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Resolve the base directory for finding ros-help.db:
|
|
29
|
-
* - Compiled binary: directory containing the executable
|
|
30
|
-
* - Dev mode: project root (one level up from src/)
|
|
31
|
-
*/
|
|
32
|
-
const baseDir =
|
|
33
|
-
typeof IS_COMPILED !== "undefined" && IS_COMPILED
|
|
34
|
-
? path.dirname(process.execPath)
|
|
35
|
-
: path.resolve(import.meta.dirname, "..");
|
|
36
|
-
|
|
37
|
-
export const DB_PATH =
|
|
38
|
-
process.env.DB_PATH?.trim() || path.join(baseDir, "ros-help.db");
|
|
25
|
+
export const DB_PATH = resolveDbPath(import.meta.dirname);
|
|
39
26
|
|
|
40
27
|
export const db = new sqlite(DB_PATH);
|
|
41
28
|
|
package/src/mcp.ts
CHANGED
|
@@ -15,22 +15,21 @@
|
|
|
15
15
|
* DB_PATH — absolute path to ros-help.db (default: next to binary or project root)
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
import { resolveVersion } from "./paths.ts";
|
|
19
|
+
|
|
20
|
+
const RESOLVED_VERSION = resolveVersion(import.meta.dirname);
|
|
20
21
|
|
|
21
22
|
// ── CLI dispatch (before MCP server init) ──
|
|
22
23
|
|
|
23
24
|
const args = process.argv.slice(2);
|
|
24
25
|
|
|
25
26
|
if (args.includes("--version") || args.includes("-v")) {
|
|
26
|
-
|
|
27
|
-
console.log(`rosetta ${ver}`);
|
|
27
|
+
console.log(`rosetta ${RESOLVED_VERSION}`);
|
|
28
28
|
process.exit(0);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
if (args.includes("--help") || args.includes("-h")) {
|
|
32
|
-
|
|
33
|
-
console.log(`rosetta ${ver} — MCP server for RouterOS documentation`);
|
|
32
|
+
console.log(`rosetta ${RESOLVED_VERSION} — MCP server for RouterOS documentation`);
|
|
34
33
|
console.log();
|
|
35
34
|
console.log("Usage:");
|
|
36
35
|
console.log(" rosetta Start MCP server (stdio transport)");
|
|
@@ -65,12 +64,8 @@ const { z } = await import("zod/v3");
|
|
|
65
64
|
//
|
|
66
65
|
// Check if DB has data BEFORE importing db.ts. If empty/missing,
|
|
67
66
|
// auto-download so db.ts opens the real database.
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
? (await import("node:path")).dirname(process.execPath)
|
|
71
|
-
: (await import("node:path")).resolve(import.meta.dirname, "..");
|
|
72
|
-
const _dbPath =
|
|
73
|
-
process.env.DB_PATH?.trim() || (await import("node:path")).join(_baseDir, "ros-help.db");
|
|
67
|
+
const { resolveDbPath } = await import("./paths.ts");
|
|
68
|
+
const _dbPath = resolveDbPath(import.meta.dirname);
|
|
74
69
|
|
|
75
70
|
const _pageCount = (() => {
|
|
76
71
|
try {
|
|
@@ -116,7 +111,7 @@ initDb();
|
|
|
116
111
|
|
|
117
112
|
const server = new McpServer({
|
|
118
113
|
name: "rosetta",
|
|
119
|
-
version:
|
|
114
|
+
version: RESOLVED_VERSION,
|
|
120
115
|
});
|
|
121
116
|
|
|
122
117
|
// ---- routeros_search ----
|
|
@@ -451,6 +446,29 @@ Examples:
|
|
|
451
446
|
|
|
452
447
|
// ---- routeros_search_changelogs ----
|
|
453
448
|
|
|
449
|
+
/** Group flat changelog results by version for compact output. */
|
|
450
|
+
function groupChangelogsByVersion(results: Array<{ version: string; released: string | null; category: string; is_breaking: number; description: string }>) {
|
|
451
|
+
const byVersion = new Map<string, { released: string | null; entries: Array<{ category: string; is_breaking: number; description: string }> }>();
|
|
452
|
+
for (const r of results) {
|
|
453
|
+
let group = byVersion.get(r.version);
|
|
454
|
+
if (!group) {
|
|
455
|
+
group = { released: r.released, entries: [] };
|
|
456
|
+
byVersion.set(r.version, group);
|
|
457
|
+
}
|
|
458
|
+
group.entries.push({ category: r.category, is_breaking: r.is_breaking, description: r.description });
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
total_entries: results.length,
|
|
462
|
+
versions: Array.from(byVersion.entries()).map(([version, { released, entries }]) => ({
|
|
463
|
+
version,
|
|
464
|
+
released,
|
|
465
|
+
entry_count: entries.length,
|
|
466
|
+
breaking_count: entries.filter(e => e.is_breaking).length,
|
|
467
|
+
entries,
|
|
468
|
+
})),
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
454
472
|
server.registerTool(
|
|
455
473
|
"routeros_search_changelogs",
|
|
456
474
|
{
|
|
@@ -484,7 +502,7 @@ Coverage depends on which versions were extracted — typically matches ros_vers
|
|
|
484
502
|
from_version: z
|
|
485
503
|
.string()
|
|
486
504
|
.optional()
|
|
487
|
-
.describe("Start of version range,
|
|
505
|
+
.describe("Start of version range, EXCLUSIVE — returns changes AFTER this version (e.g., from_version='7.21.3' excludes 7.21.3 entries, includes 7.22+)"),
|
|
488
506
|
to_version: z
|
|
489
507
|
.string()
|
|
490
508
|
.optional()
|
|
@@ -501,10 +519,10 @@ Coverage depends on which versions were extracted — typically matches ros_vers
|
|
|
501
519
|
.number()
|
|
502
520
|
.int()
|
|
503
521
|
.min(1)
|
|
504
|
-
.max(
|
|
522
|
+
.max(500)
|
|
505
523
|
.optional()
|
|
506
|
-
.default(
|
|
507
|
-
.describe("Max results (default
|
|
524
|
+
.default(50)
|
|
525
|
+
.describe("Max results (default 50, max 500). Version-range queries often need higher limits."),
|
|
508
526
|
},
|
|
509
527
|
},
|
|
510
528
|
async ({ query, version, from_version, to_version, category, breaking_only, limit }) => {
|
|
@@ -535,8 +553,10 @@ Coverage depends on which versions were extracted — typically matches ros_vers
|
|
|
535
553
|
],
|
|
536
554
|
};
|
|
537
555
|
}
|
|
556
|
+
// Group by version for compact output — avoids repeating version/released on every entry
|
|
557
|
+
const grouped = groupChangelogsByVersion(results);
|
|
538
558
|
return {
|
|
539
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
559
|
+
content: [{ type: "text", text: JSON.stringify(grouped, null, 2) }],
|
|
540
560
|
};
|
|
541
561
|
},
|
|
542
562
|
);
|
package/src/paths.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paths.ts — Shared DB path resolution for all entry points.
|
|
3
|
+
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
* 1. Compiled binary (IS_COMPILED) → next to executable
|
|
6
|
+
* 2. Dev mode (.git exists in project root) → project root
|
|
7
|
+
* 3. Package mode (bunx / bun add -g) → ~/.rosetta/
|
|
8
|
+
*
|
|
9
|
+
* DB_PATH env var overrides all modes.
|
|
10
|
+
* This module must NOT import db.ts or bun:sqlite — it's used before the DB is opened.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
declare const IS_COMPILED: boolean;
|
|
18
|
+
declare const VERSION: string;
|
|
19
|
+
|
|
20
|
+
/** True when running as a compiled binary (bun build --compile) */
|
|
21
|
+
export function isCompiled(): boolean {
|
|
22
|
+
try {
|
|
23
|
+
return typeof IS_COMPILED !== "undefined" && IS_COMPILED;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** True when running from a git checkout (dev mode) */
|
|
30
|
+
function isDevMode(projectRoot: string): boolean {
|
|
31
|
+
return existsSync(path.join(projectRoot, ".git"));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the directory where ros-help.db should live.
|
|
36
|
+
* - Compiled: directory containing the executable
|
|
37
|
+
* - Dev: project root (one level up from src/)
|
|
38
|
+
* - Package (bunx / global install): ~/.rosetta/
|
|
39
|
+
*/
|
|
40
|
+
export function resolveBaseDir(srcDir: string): string {
|
|
41
|
+
if (isCompiled()) {
|
|
42
|
+
return path.dirname(process.execPath);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const projectRoot = path.resolve(srcDir, "..");
|
|
46
|
+
if (isDevMode(projectRoot)) {
|
|
47
|
+
return projectRoot;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Package mode — stable user-local directory
|
|
51
|
+
const dataDir = path.join(homedir(), ".rosetta");
|
|
52
|
+
mkdirSync(dataDir, { recursive: true });
|
|
53
|
+
return dataDir;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the full path to ros-help.db.
|
|
58
|
+
* DB_PATH env var overrides all detection logic.
|
|
59
|
+
*/
|
|
60
|
+
export function resolveDbPath(srcDir: string): string {
|
|
61
|
+
const envPath = process.env.DB_PATH?.trim();
|
|
62
|
+
if (envPath) return envPath;
|
|
63
|
+
return path.join(resolveBaseDir(srcDir), "ros-help.db");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Detect invocation mode: "compiled" | "dev" | "package" */
|
|
67
|
+
export type InvocationMode = "compiled" | "dev" | "package";
|
|
68
|
+
|
|
69
|
+
export function detectMode(srcDir: string): InvocationMode {
|
|
70
|
+
if (isCompiled()) return "compiled";
|
|
71
|
+
const projectRoot = path.resolve(srcDir, "..");
|
|
72
|
+
if (isDevMode(projectRoot)) return "dev";
|
|
73
|
+
return "package";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolve the version string.
|
|
78
|
+
* Compiled mode: injected at build time via --define.
|
|
79
|
+
* Dev/package mode: read from package.json.
|
|
80
|
+
*/
|
|
81
|
+
export function resolveVersion(srcDir: string): string {
|
|
82
|
+
try {
|
|
83
|
+
if (typeof VERSION !== "undefined") return VERSION;
|
|
84
|
+
} catch {}
|
|
85
|
+
try {
|
|
86
|
+
const pkgPath = path.join(srcDir, "..", "package.json");
|
|
87
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
88
|
+
return pkg.version ?? "unknown";
|
|
89
|
+
} catch {
|
|
90
|
+
return "unknown";
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/query.test.ts
CHANGED
|
@@ -877,13 +877,13 @@ describe("searchChangelogs", () => {
|
|
|
877
877
|
}
|
|
878
878
|
});
|
|
879
879
|
|
|
880
|
-
test("version range filter works", () => {
|
|
881
|
-
const results = searchChangelogs("", { fromVersion: "7.
|
|
880
|
+
test("version range filter works (from_version exclusive, to_version inclusive)", () => {
|
|
881
|
+
const results = searchChangelogs("", { fromVersion: "7.21", toVersion: "7.22.1" });
|
|
882
882
|
expect(results.length).toBeGreaterThan(0);
|
|
883
883
|
for (const r of results) {
|
|
884
884
|
expect(["7.22", "7.22.1"]).toContain(r.version);
|
|
885
885
|
}
|
|
886
|
-
// Should not include 7.21
|
|
886
|
+
// Should not include 7.21 (from_version is exclusive)
|
|
887
887
|
expect(results.some((r) => r.version === "7.21")).toBe(false);
|
|
888
888
|
});
|
|
889
889
|
|
|
@@ -902,7 +902,8 @@ describe("searchChangelogs", () => {
|
|
|
902
902
|
});
|
|
903
903
|
|
|
904
904
|
test("FTS combined with version range", () => {
|
|
905
|
-
|
|
905
|
+
// from_version is exclusive, so use 7.21 to include 7.22
|
|
906
|
+
const results = searchChangelogs("MLAG", { fromVersion: "7.21", toVersion: "7.22" });
|
|
906
907
|
expect(results.length).toBe(1);
|
|
907
908
|
expect(results[0].category).toBe("bridge");
|
|
908
909
|
});
|
package/src/query.ts
CHANGED
|
@@ -825,14 +825,14 @@ function getChangelogVersions(): string[] {
|
|
|
825
825
|
return rows.map((r) => r.version).sort(compareVersions);
|
|
826
826
|
}
|
|
827
827
|
|
|
828
|
-
/** Filter versions to those within
|
|
828
|
+
/** Filter versions to those within (fromVersion, toVersion] range (fromVersion exclusive, toVersion inclusive). */
|
|
829
829
|
function filterVersionRange(
|
|
830
830
|
versions: string[],
|
|
831
831
|
fromVersion?: string,
|
|
832
832
|
toVersion?: string,
|
|
833
833
|
): string[] {
|
|
834
834
|
return versions.filter((v) => {
|
|
835
|
-
if (fromVersion && compareVersions(v, fromVersion)
|
|
835
|
+
if (fromVersion && compareVersions(v, fromVersion) <= 0) return false;
|
|
836
836
|
if (toVersion && compareVersions(v, toVersion) > 0) return false;
|
|
837
837
|
return true;
|
|
838
838
|
});
|
package/src/release.test.ts
CHANGED
|
@@ -88,25 +88,27 @@ describe("bin/rosetta.js", () => {
|
|
|
88
88
|
// ---------------------------------------------------------------------------
|
|
89
89
|
|
|
90
90
|
describe("build-time constants", () => {
|
|
91
|
-
test("mcp.ts
|
|
91
|
+
test("mcp.ts imports resolveVersion from paths.ts", () => {
|
|
92
92
|
const src = readText("src/mcp.ts");
|
|
93
|
-
expect(src).toContain("
|
|
93
|
+
expect(src).toContain("resolveVersion");
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
test("mcp.ts
|
|
96
|
+
test("mcp.ts does not have hardcoded version fallback", () => {
|
|
97
97
|
const src = readText("src/mcp.ts");
|
|
98
|
-
expect(src).toContain("
|
|
98
|
+
expect(src).not.toContain('"0.2.0"');
|
|
99
|
+
expect(src).not.toContain("\"dev\"");
|
|
99
100
|
});
|
|
100
101
|
|
|
101
|
-
test("
|
|
102
|
-
const src = readText("src/
|
|
102
|
+
test("paths.ts declares IS_COMPILED and VERSION", () => {
|
|
103
|
+
const src = readText("src/paths.ts");
|
|
103
104
|
expect(src).toContain("declare const IS_COMPILED");
|
|
105
|
+
expect(src).toContain("declare const VERSION");
|
|
104
106
|
});
|
|
105
107
|
|
|
106
|
-
test("setup.ts declares REPO_URL and
|
|
108
|
+
test("setup.ts declares REPO_URL and imports resolveVersion", () => {
|
|
107
109
|
const src = readText("src/setup.ts");
|
|
108
110
|
expect(src).toContain("declare const REPO_URL");
|
|
109
|
-
expect(src).toContain("
|
|
111
|
+
expect(src).toContain("resolveVersion");
|
|
110
112
|
});
|
|
111
113
|
|
|
112
114
|
test("build script injects all three constants", () => {
|
package/src/setup.ts
CHANGED
|
@@ -1,51 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* setup.ts — Download the RouterOS documentation database and print MCP client config.
|
|
3
3
|
*
|
|
4
|
-
* Called by `rosetta --setup` (compiled binary)
|
|
4
|
+
* Called by `rosetta --setup` (compiled binary), `bunx @tikoci/rosetta --setup`,
|
|
5
|
+
* or `bun run src/setup.ts` (dev).
|
|
5
6
|
* Downloads ros-help.db.gz from the latest GitHub Release, decompresses it,
|
|
6
7
|
* validates the DB, and prints config snippets for each MCP client.
|
|
7
8
|
*/
|
|
8
9
|
|
|
10
|
+
import { execSync } from "node:child_process";
|
|
9
11
|
import { existsSync, writeFileSync } from "node:fs";
|
|
10
|
-
import path from "node:path";
|
|
11
12
|
import { gunzipSync } from "bun";
|
|
13
|
+
import { detectMode, resolveBaseDir, resolveDbPath, resolveVersion } from "./paths.ts";
|
|
12
14
|
|
|
13
15
|
declare const REPO_URL: string;
|
|
14
|
-
declare const VERSION: string;
|
|
15
16
|
|
|
16
17
|
const GITHUB_REPO =
|
|
17
18
|
typeof REPO_URL !== "undefined" ? REPO_URL : "tikoci/rosetta";
|
|
18
|
-
const RELEASE_VERSION =
|
|
19
|
-
typeof VERSION !== "undefined" ? VERSION : "dev";
|
|
20
|
-
|
|
21
|
-
/** Where the binary (or dev project root) lives */
|
|
22
|
-
function getBaseDir(): string {
|
|
23
|
-
// If IS_COMPILED is defined, use the executable's directory
|
|
24
|
-
// Otherwise use project root (one level up from src/)
|
|
25
|
-
try {
|
|
26
|
-
// @ts-expect-error IS_COMPILED defined at build time
|
|
27
|
-
if (typeof IS_COMPILED !== "undefined" && IS_COMPILED) {
|
|
28
|
-
return path.dirname(process.execPath);
|
|
29
|
-
}
|
|
30
|
-
} catch {
|
|
31
|
-
// not compiled
|
|
32
|
-
}
|
|
33
|
-
return path.resolve(import.meta.dirname, "..");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** The binary/script path for MCP config */
|
|
37
|
-
function getServerCommand(): string {
|
|
38
|
-
try {
|
|
39
|
-
// @ts-expect-error IS_COMPILED defined at build time
|
|
40
|
-
if (typeof IS_COMPILED !== "undefined" && IS_COMPILED) {
|
|
41
|
-
return process.execPath;
|
|
42
|
-
}
|
|
43
|
-
} catch {
|
|
44
|
-
// not compiled
|
|
45
|
-
}
|
|
46
|
-
// Dev mode — bun run src/mcp.ts
|
|
47
|
-
return path.resolve(import.meta.dirname, "mcp.ts");
|
|
48
|
-
}
|
|
19
|
+
const RELEASE_VERSION = resolveVersion(import.meta.dirname);
|
|
49
20
|
|
|
50
21
|
/** Check if a DB file exists and has actual page data */
|
|
51
22
|
function dbHasData(dbPath: string): boolean {
|
|
@@ -90,10 +61,8 @@ export async function downloadDb(
|
|
|
90
61
|
}
|
|
91
62
|
|
|
92
63
|
export async function runSetup(force = false) {
|
|
93
|
-
const
|
|
94
|
-
const dbPath =
|
|
95
|
-
const serverCmd = getServerCommand();
|
|
96
|
-
const isCompiled = serverCmd === process.execPath;
|
|
64
|
+
const mode = detectMode(import.meta.dirname);
|
|
65
|
+
const dbPath = resolveDbPath(import.meta.dirname);
|
|
97
66
|
|
|
98
67
|
console.log(`rosetta ${RELEASE_VERSION}`);
|
|
99
68
|
console.log();
|
|
@@ -118,7 +87,8 @@ export async function runSetup(force = false) {
|
|
|
118
87
|
console.log(`✓ Database ready (${row.c} pages, ${cmdRow.c} commands)`);
|
|
119
88
|
} catch (e) {
|
|
120
89
|
console.error(`✗ Database validation failed: ${e}`);
|
|
121
|
-
|
|
90
|
+
const retryCmd = mode === "compiled" ? "rosetta" : mode === "package" ? "bunx @tikoci/rosetta" : "bun run src/setup.ts";
|
|
91
|
+
console.error(` Try re-downloading with: ${retryCmd} --setup --force`);
|
|
122
92
|
process.exit(1);
|
|
123
93
|
}
|
|
124
94
|
|
|
@@ -128,10 +98,21 @@ export async function runSetup(force = false) {
|
|
|
128
98
|
console.log("Configure your MCP client:");
|
|
129
99
|
console.log("─".repeat(60));
|
|
130
100
|
|
|
131
|
-
if (
|
|
132
|
-
printCompiledConfig(
|
|
101
|
+
if (mode === "compiled") {
|
|
102
|
+
printCompiledConfig(process.execPath);
|
|
103
|
+
} else if (mode === "package") {
|
|
104
|
+
printPackageConfig();
|
|
133
105
|
} else {
|
|
134
|
-
printDevConfig(
|
|
106
|
+
printDevConfig(resolveBaseDir(import.meta.dirname));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Try to resolve the absolute path to bunx (for clients that don't inherit PATH) */
|
|
111
|
+
function resolveBunxPath(): string | null {
|
|
112
|
+
try {
|
|
113
|
+
return execSync("which bunx", { encoding: "utf-8" }).trim() || null;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
135
116
|
}
|
|
136
117
|
}
|
|
137
118
|
|
|
@@ -175,6 +156,78 @@ function printCompiledConfig(serverCmd: string) {
|
|
|
175
156
|
console.log(` }`);
|
|
176
157
|
console.log(` }`);
|
|
177
158
|
console.log();
|
|
159
|
+
|
|
160
|
+
// Copilot CLI
|
|
161
|
+
console.log("▸ GitHub Copilot CLI");
|
|
162
|
+
console.log(` Inside a copilot session, type /mcp add:`);
|
|
163
|
+
console.log(` Name: routeros-rosetta | Type: STDIO | Command: ${serverCmd}`);
|
|
164
|
+
console.log();
|
|
165
|
+
|
|
166
|
+
// OpenAI Codex
|
|
167
|
+
console.log("▸ OpenAI Codex");
|
|
168
|
+
console.log(` codex mcp add rosetta -- ${serverCmd}`);
|
|
169
|
+
console.log();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function printPackageConfig() {
|
|
173
|
+
// Resolve full path to bunx — Claude Desktop doesn't inherit shell PATH
|
|
174
|
+
const bunxFullPath = resolveBunxPath();
|
|
175
|
+
|
|
176
|
+
// Claude Desktop
|
|
177
|
+
const isMac = process.platform === "darwin";
|
|
178
|
+
const configPath = isMac
|
|
179
|
+
? "~/Library/Application\\ Support/Claude/claude_desktop_config.json"
|
|
180
|
+
: "%APPDATA%\\Claude\\claude_desktop_config.json";
|
|
181
|
+
|
|
182
|
+
const bunxCmd = bunxFullPath ? JSON.stringify(bunxFullPath) : "\"bunx\"";
|
|
183
|
+
console.log();
|
|
184
|
+
console.log("▸ Claude Desktop");
|
|
185
|
+
console.log(` Edit: ${configPath}`);
|
|
186
|
+
console.log();
|
|
187
|
+
console.log(` {`);
|
|
188
|
+
console.log(` "mcpServers": {`);
|
|
189
|
+
console.log(` "rosetta": {`);
|
|
190
|
+
console.log(` "command": ${bunxCmd},`);
|
|
191
|
+
console.log(` "args": ["@tikoci/rosetta"]`);
|
|
192
|
+
console.log(` }`);
|
|
193
|
+
console.log(` }`);
|
|
194
|
+
console.log(` }`);
|
|
195
|
+
console.log();
|
|
196
|
+
if (bunxFullPath) {
|
|
197
|
+
console.log(` Note: Full path used because Claude Desktop may not inherit shell PATH.`);
|
|
198
|
+
console.log();
|
|
199
|
+
}
|
|
200
|
+
console.log(` Then restart Claude Desktop.`);
|
|
201
|
+
|
|
202
|
+
// Claude Code (inherits PATH — short form is fine)
|
|
203
|
+
console.log();
|
|
204
|
+
console.log("▸ Claude Code");
|
|
205
|
+
console.log(` claude mcp add rosetta -- bunx @tikoci/rosetta`);
|
|
206
|
+
|
|
207
|
+
// VS Code Copilot (inherits PATH)
|
|
208
|
+
console.log();
|
|
209
|
+
console.log("▸ VS Code Copilot (User Settings JSON)");
|
|
210
|
+
console.log();
|
|
211
|
+
console.log(` "mcp": {`);
|
|
212
|
+
console.log(` "servers": {`);
|
|
213
|
+
console.log(` "rosetta": {`);
|
|
214
|
+
console.log(` "command": "bunx",`);
|
|
215
|
+
console.log(` "args": ["@tikoci/rosetta"]`);
|
|
216
|
+
console.log(` }`);
|
|
217
|
+
console.log(` }`);
|
|
218
|
+
console.log(` }`);
|
|
219
|
+
console.log();
|
|
220
|
+
|
|
221
|
+
// Copilot CLI (inherits PATH)
|
|
222
|
+
console.log("▸ GitHub Copilot CLI");
|
|
223
|
+
console.log(` Inside a copilot session, type /mcp add:`);
|
|
224
|
+
console.log(` Name: routeros-rosetta | Type: STDIO | Command: bunx @tikoci/rosetta`);
|
|
225
|
+
console.log();
|
|
226
|
+
|
|
227
|
+
// OpenAI Codex (inherits PATH)
|
|
228
|
+
console.log("▸ OpenAI Codex");
|
|
229
|
+
console.log(` codex mcp add rosetta -- bunx @tikoci/rosetta`);
|
|
230
|
+
console.log();
|
|
178
231
|
}
|
|
179
232
|
|
|
180
233
|
function printDevConfig(baseDir: string) {
|
|
@@ -212,6 +265,17 @@ function printDevConfig(baseDir: string) {
|
|
|
212
265
|
console.log("▸ VS Code Copilot");
|
|
213
266
|
console.log(` The repo includes .vscode/mcp.json — just open the folder in VS Code.`);
|
|
214
267
|
console.log();
|
|
268
|
+
|
|
269
|
+
// Copilot CLI
|
|
270
|
+
console.log("▸ GitHub Copilot CLI");
|
|
271
|
+
console.log(` Inside a copilot session, type /mcp add:`);
|
|
272
|
+
console.log(` Name: routeros-rosetta | Type: STDIO | Command: bun run src/mcp.ts`);
|
|
273
|
+
console.log();
|
|
274
|
+
|
|
275
|
+
// OpenAI Codex
|
|
276
|
+
console.log("▸ OpenAI Codex");
|
|
277
|
+
console.log(` codex mcp add rosetta -- bun run src/mcp.ts`);
|
|
278
|
+
console.log();
|
|
215
279
|
}
|
|
216
280
|
|
|
217
281
|
// Run directly
|