cwtools-mcp 0.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 +279 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +76 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +212 -0
- package/dist/cwtools-mcp.cjs +49480 -0
- package/dist/hosts/lspProcessHost.d.ts +49 -0
- package/dist/hosts/lspProcessHost.js +412 -0
- package/dist/hosts/nodeHostServices.d.ts +3 -0
- package/dist/hosts/nodeHostServices.js +506 -0
- package/dist/hosts/projectDetect.d.ts +7 -0
- package/dist/hosts/projectDetect.js +151 -0
- package/dist/hosts/projectSettings.d.ts +11 -0
- package/dist/hosts/projectSettings.js +265 -0
- package/dist/hosts/revalidation.d.ts +12 -0
- package/dist/hosts/revalidation.js +157 -0
- package/dist/hosts/vscodeCache.d.ts +5 -0
- package/dist/hosts/vscodeCache.js +153 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +25 -0
- package/dist/mcp/bridgeProxy.d.ts +4 -0
- package/dist/mcp/bridgeProxy.js +281 -0
- package/dist/mcp/resources.d.ts +15 -0
- package/dist/mcp/resources.js +151 -0
- package/dist/mcp/toolHandlers.d.ts +9 -0
- package/dist/mcp/toolHandlers.js +58 -0
- package/dist/mcp/toolRegistrar.d.ts +2 -0
- package/dist/mcp/toolRegistrar.js +7 -0
- package/dist/mcp/transportHttp.d.ts +7 -0
- package/dist/mcp/transportHttp.js +101 -0
- package/dist/mcp/transportStdio.d.ts +2 -0
- package/dist/mcp/transportStdio.js +8 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +85 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# cwtools-mcp
|
|
2
|
+
|
|
3
|
+
[English](#english) | [中文](#zh-cn)
|
|
4
|
+
|
|
5
|
+
<a id="english"></a>
|
|
6
|
+
|
|
7
|
+
## English
|
|
8
|
+
|
|
9
|
+
CWTools MCP is the external-agent entry point for this extension's Paradox /
|
|
10
|
+
Stellaris semantic tools.
|
|
11
|
+
|
|
12
|
+
### Default Mode: Extension Bridge
|
|
13
|
+
|
|
14
|
+
By default, `cwtools-mcp` is a lightweight MCP proxy. It does **not** start a
|
|
15
|
+
second `CWTools Server` process. Instead, it connects to the MCP bridge started
|
|
16
|
+
inside the active VS Code-compatible extension host and reuses that host's:
|
|
17
|
+
|
|
18
|
+
- existing CWTools language client;
|
|
19
|
+
- current workspace root;
|
|
20
|
+
- Problems diagnostics from the IDE;
|
|
21
|
+
- rules/cache/localisation/user settings;
|
|
22
|
+
- shared indexes and AI read tools.
|
|
23
|
+
|
|
24
|
+
This keeps memory use low and makes MCP diagnostics match the IDE.
|
|
25
|
+
|
|
26
|
+
Bridge mode is deliberately strict about project identity without requiring a
|
|
27
|
+
project path in global MCP settings. By default, the proxy discovers the current
|
|
28
|
+
client workspace from MCP roots, known per-session environment variables, or the
|
|
29
|
+
MCP process cwd. That workspace must match the `workspaceRoot` served by the
|
|
30
|
+
extension bridge. If they do not match, tool calls return `bridge_unavailable`
|
|
31
|
+
instead of silently answering from a different project. `--workspace` remains an
|
|
32
|
+
optional override for clients that cannot expose a per-project root.
|
|
33
|
+
|
|
34
|
+
The extension writes both files below into the current host's own
|
|
35
|
+
`globalStorage/mcp/` directory when the project is active:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
cwtools-mcp.cjs
|
|
39
|
+
bridge-manifest.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
External agents should run the `cwtools-mcp.cjs` copied by the same host they are
|
|
43
|
+
using. The proxy reads `bridge-manifest.json` next to itself. This is host-name
|
|
44
|
+
agnostic: VS Code, Cursor, VSCodium, Antigravity, and other compatible hosts all
|
|
45
|
+
work as long as they support the VS Code extension APIs and activate this
|
|
46
|
+
extension.
|
|
47
|
+
|
|
48
|
+
### Quick Setup
|
|
49
|
+
|
|
50
|
+
Use the `globalStorage` path from the compatible host where the extension is
|
|
51
|
+
active. The placeholder below means the directory that contains the host's
|
|
52
|
+
`foreverskywalker.foreverskywalker-stellaris-cwtools` global storage folder.
|
|
53
|
+
|
|
54
|
+
#### Codex
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
codex mcp add cwtools -- node "<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs" --stdio
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Claude Code
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
claude mcp add cwtools --scope user -- node "<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs" --stdio
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Antigravity
|
|
67
|
+
|
|
68
|
+
Antigravity reads MCP servers from `~/.gemini/config/mcp_config.json`. Add this
|
|
69
|
+
server entry:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"cwtools": {
|
|
75
|
+
"command": "node",
|
|
76
|
+
"args": [
|
|
77
|
+
"<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs",
|
|
78
|
+
"--stdio"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or merge it into an existing config with a Node one-liner:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
node -e "const fs=require('fs'),os=require('os'),path=require('path');const p=path.join(os.homedir(),'.gemini','config','mcp_config.json');const s=process.argv[1];const cfg=fs.existsSync(p)?JSON.parse(fs.readFileSync(p,'utf8')):{};cfg.mcpServers={...(cfg.mcpServers||{}),cwtools:{command:'node',args:[s,'--stdio']}};fs.mkdirSync(path.dirname(p),{recursive:true});fs.writeFileSync(p,JSON.stringify(cfg,null,2)+'\n')" "<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If the compatible host is closed, the workspace is not active, the manifest is
|
|
92
|
+
stale, or the client workspace differs from the bridge workspace, tool
|
|
93
|
+
calls return `bridge_unavailable` with recovery instructions. The proxy
|
|
94
|
+
intentionally does not silently fall back to a separate language server.
|
|
95
|
+
|
|
96
|
+
### Optional Standalone Mode
|
|
97
|
+
|
|
98
|
+
Use standalone mode only when you explicitly want the legacy behavior: the MCP
|
|
99
|
+
process starts its own CWTools language server and builds its own diagnostic
|
|
100
|
+
state.
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --stdio
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
HTTP transport is available in both modes:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
cwtools-mcp --http --host 127.0.0.1 --port 3000
|
|
110
|
+
cwtools-mcp --standalone --workspace /path/to/mod --http --host 127.0.0.1 --port 3000
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Standalone Rules And Vanilla Cache
|
|
114
|
+
|
|
115
|
+
These options apply to standalone mode:
|
|
116
|
+
|
|
117
|
+
```sh
|
|
118
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --game-path "/path/to/Stellaris"
|
|
119
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --cache "/path/to/.cwtools"
|
|
120
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --rules "/path/to/rules/config"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
When standalone mode lacks a vanilla `.cwb` cache or `--game-path`, vanilla IDs
|
|
124
|
+
may be absent and diagnostics can differ from the IDE. Bridge mode avoids this by
|
|
125
|
+
using the active extension host's loaded state.
|
|
126
|
+
|
|
127
|
+
### Tools
|
|
128
|
+
|
|
129
|
+
The MCP surface remains read-only. It exposes the generated CWTools semantic
|
|
130
|
+
tools such as:
|
|
131
|
+
|
|
132
|
+
- `query_types`
|
|
133
|
+
- `query_rules`
|
|
134
|
+
- `query_scope`
|
|
135
|
+
- `get_diagnostics`
|
|
136
|
+
- `explore_pdx_project` for a bounded live semantic graph with dependency edges and freshness
|
|
137
|
+
- `query_workspace_index`
|
|
138
|
+
- `query_localisation_index`
|
|
139
|
+
- `get_pdx_block`
|
|
140
|
+
- completion, document/workspace symbols, definition and reference lookup
|
|
141
|
+
- deep semantic queries for scripted effects/triggers, enums, static modifiers,
|
|
142
|
+
variables, and entity info
|
|
143
|
+
|
|
144
|
+
File edits are intentionally not exposed through this MCP server. External agents
|
|
145
|
+
should edit files through their own environment and then call MCP diagnostics or
|
|
146
|
+
semantic tools again.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
<a id="zh-cn"></a>
|
|
151
|
+
|
|
152
|
+
## 中文
|
|
153
|
+
|
|
154
|
+
CWTools MCP 是本扩展提供给外部 Agent 的 Paradox / Stellaris 语义工具入口。
|
|
155
|
+
|
|
156
|
+
### 默认模式:插件内 Bridge
|
|
157
|
+
|
|
158
|
+
默认情况下,`cwtools-mcp` 是一个轻量 MCP 代理。它**不会**再启动第二个
|
|
159
|
+
`CWTools Server` 进程,而是连接当前已激活的 VS Code 兼容宿主内的 MCP
|
|
160
|
+
bridge,并复用该宿主中的:
|
|
161
|
+
|
|
162
|
+
- 已有 CWTools 语言客户端;
|
|
163
|
+
- 当前工作区根目录;
|
|
164
|
+
- IDE Problems 面板诊断;
|
|
165
|
+
- rules/cache/localisation/用户设置;
|
|
166
|
+
- 共享索引和 AI 只读工具。
|
|
167
|
+
|
|
168
|
+
这样可以降低内存占用,并让 MCP 诊断数量与 IDE 保持一致。
|
|
169
|
+
|
|
170
|
+
Bridge 模式会严格校验项目身份,但不要求把项目路径写死到全局 MCP 设置里。默认情况下,
|
|
171
|
+
代理会从 MCP roots、已知的按会话注入的环境变量或 MCP 进程 cwd 推断当前客户端工作区。
|
|
172
|
+
这个工作区必须与扩展 bridge 暴露的 `workspaceRoot` 一致。不一致时工具调用会返回
|
|
173
|
+
`bridge_unavailable`,不会静默使用另一个项目回答。`--workspace` 只保留给无法暴露
|
|
174
|
+
按项目 root 的客户端作为可选覆盖项。
|
|
175
|
+
|
|
176
|
+
项目激活时,扩展会把下面两个文件写入当前宿主自己的 `globalStorage/mcp/`
|
|
177
|
+
目录:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
cwtools-mcp.cjs
|
|
181
|
+
bridge-manifest.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
外部 Agent 应运行同一个宿主复制出来的 `cwtools-mcp.cjs`。代理会读取同目录的
|
|
185
|
+
`bridge-manifest.json`。这条主路径不依赖宿主目录名:VS Code、Cursor、
|
|
186
|
+
VSCodium、Antigravity,以及其他兼容 VS Code 扩展 API 的宿主都可以使用。
|
|
187
|
+
|
|
188
|
+
### 快速接入
|
|
189
|
+
|
|
190
|
+
请使用实际运行扩展的兼容宿主自己的 `globalStorage` 路径。下面的
|
|
191
|
+
`<host-globalStorage>` 代表该宿主下
|
|
192
|
+
`foreverskywalker.foreverskywalker-stellaris-cwtools` 全局存储目录所在位置。
|
|
193
|
+
|
|
194
|
+
#### Codex
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
codex mcp add cwtools -- node "<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs" --stdio
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### Claude Code
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
claude mcp add cwtools --scope user -- node "<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs" --stdio
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Antigravity
|
|
207
|
+
|
|
208
|
+
Antigravity 从 `~/.gemini/config/mcp_config.json` 读取 MCP 服务器。添加下面这个
|
|
209
|
+
server 条目:
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"mcpServers": {
|
|
214
|
+
"cwtools": {
|
|
215
|
+
"command": "node",
|
|
216
|
+
"args": [
|
|
217
|
+
"<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs",
|
|
218
|
+
"--stdio"
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
也可以用下面的 Node 一行命令合并进已有配置,不会覆盖其他 MCP server:
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
node -e "const fs=require('fs'),os=require('os'),path=require('path');const p=path.join(os.homedir(),'.gemini','config','mcp_config.json');const s=process.argv[1];const cfg=fs.existsSync(p)?JSON.parse(fs.readFileSync(p,'utf8')):{};cfg.mcpServers={...(cfg.mcpServers||{}),cwtools:{command:'node',args:[s,'--stdio']}};fs.mkdirSync(path.dirname(p),{recursive:true});fs.writeFileSync(p,JSON.stringify(cfg,null,2)+'\n')" "<host-globalStorage>/foreverskywalker.foreverskywalker-stellaris-cwtools/mcp/cwtools-mcp.cjs"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
如果兼容宿主未打开、工作区未激活、manifest 已失效,或客户端工作区与 bridge
|
|
232
|
+
工作区不一致,工具调用会返回 `bridge_unavailable` 和恢复说明。代理不会静默回退并启动单独的语言服务。
|
|
233
|
+
|
|
234
|
+
### 可选 Standalone 模式
|
|
235
|
+
|
|
236
|
+
只有在明确需要旧行为时才使用 standalone 模式:MCP 进程会自行启动一份 CWTools
|
|
237
|
+
语言服务器并构建独立诊断状态。
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --stdio
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
两种模式都支持 HTTP transport:
|
|
244
|
+
|
|
245
|
+
```sh
|
|
246
|
+
cwtools-mcp --http --host 127.0.0.1 --port 3000
|
|
247
|
+
cwtools-mcp --standalone --workspace /path/to/mod --http --host 127.0.0.1 --port 3000
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Standalone 的规则与原版缓存
|
|
251
|
+
|
|
252
|
+
下面这些参数只适用于 standalone 模式:
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --game-path "/path/to/Stellaris"
|
|
256
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --cache "/path/to/.cwtools"
|
|
257
|
+
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --rules "/path/to/rules/config"
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
如果 standalone 模式缺少 vanilla `.cwb` 缓存或 `--game-path`,原版 ID 可能缺失,
|
|
261
|
+
诊断也可能与 IDE 不一致。Bridge 模式通过复用当前扩展宿主的已加载状态来避免这个问题。
|
|
262
|
+
|
|
263
|
+
### 工具
|
|
264
|
+
|
|
265
|
+
MCP 入口仍保持只读。它暴露生成出来的 CWTools 语义工具,例如:
|
|
266
|
+
|
|
267
|
+
- `query_types`
|
|
268
|
+
- `query_rules`
|
|
269
|
+
- `query_scope`
|
|
270
|
+
- `get_diagnostics`
|
|
271
|
+
- `explore_pdx_project`:返回带依赖边与 freshness 的有界 live 语义图
|
|
272
|
+
- `query_workspace_index`
|
|
273
|
+
- `query_localisation_index`
|
|
274
|
+
- `get_pdx_block`
|
|
275
|
+
- 补全、document/workspace symbols、定义和引用查询
|
|
276
|
+
- scripted effects/triggers、enums、static modifiers、variables、entity info 等深层语义查询
|
|
277
|
+
|
|
278
|
+
文件写入不会通过这个 MCP server 暴露。外部 Agent 应使用自己的环境编辑文件,
|
|
279
|
+
然后再调用 MCP 诊断或语义工具复查。
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const nodeHostServices_1 = require("./hosts/nodeHostServices");
|
|
6
|
+
const bridgeProxy_1 = require("./mcp/bridgeProxy");
|
|
7
|
+
const transportHttp_1 = require("./mcp/transportHttp");
|
|
8
|
+
const transportStdio_1 = require("./mcp/transportStdio");
|
|
9
|
+
const server_1 = require("./server");
|
|
10
|
+
// Tear down host-owned resources (the spawned CWTools Server child) exactly
|
|
11
|
+
// once, then exit. MCP clients like Codex disconnect by closing the stdio pipe
|
|
12
|
+
// rather than asking us to shut down; without this the LSP child's pipes keep
|
|
13
|
+
// the event loop alive and orphan a multi-GB server process. host.dispose()
|
|
14
|
+
// only kills the child THIS process spawned, never any other instance.
|
|
15
|
+
function installLifecycle(host, stdio) {
|
|
16
|
+
let disposed = false;
|
|
17
|
+
const dispose = () => {
|
|
18
|
+
if (disposed)
|
|
19
|
+
return;
|
|
20
|
+
disposed = true;
|
|
21
|
+
try {
|
|
22
|
+
host.dispose?.();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// best-effort cleanup
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const shutdown = () => {
|
|
29
|
+
dispose();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
};
|
|
32
|
+
// Synchronous safety net for any other exit path.
|
|
33
|
+
process.once('exit', dispose);
|
|
34
|
+
process.once('SIGINT', shutdown);
|
|
35
|
+
process.once('SIGTERM', shutdown);
|
|
36
|
+
if (process.platform !== 'win32')
|
|
37
|
+
process.once('SIGHUP', shutdown);
|
|
38
|
+
// In stdio mode the client closing the pipe (EOF on our stdin) is the
|
|
39
|
+
// disconnect signal — shut down instead of lingering.
|
|
40
|
+
if (stdio) {
|
|
41
|
+
process.stdin.once('end', shutdown);
|
|
42
|
+
process.stdin.once('close', shutdown);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function main() {
|
|
46
|
+
const config = (0, config_1.parseCliArgs)(process.argv.slice(2));
|
|
47
|
+
if (!config.standalone) {
|
|
48
|
+
if (config.http) {
|
|
49
|
+
await (0, transportHttp_1.runHttpTransport)(() => (0, bridgeProxy_1.createBridgeProxyMcpServer)(config), { host: config.host, port: config.port });
|
|
50
|
+
}
|
|
51
|
+
else if (config.stdio) {
|
|
52
|
+
const server = (0, bridgeProxy_1.createBridgeProxyMcpServer)(config);
|
|
53
|
+
await (0, transportStdio_1.runStdioTransport)(server);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw new Error('No MCP transport selected. Use --stdio or --http.');
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const host = (0, nodeHostServices_1.createNodeHostServices)(config);
|
|
61
|
+
installLifecycle(host, !!config.stdio && !config.http);
|
|
62
|
+
if (config.http) {
|
|
63
|
+
await (0, transportHttp_1.runHttpTransport)(() => (0, server_1.createCwtoolsMcpServer)(host), { host: config.host, port: config.port });
|
|
64
|
+
}
|
|
65
|
+
else if (config.stdio) {
|
|
66
|
+
const server = (0, server_1.createCwtoolsMcpServer)(host);
|
|
67
|
+
await (0, transportStdio_1.runStdioTransport)(server);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
throw new Error('No MCP transport selected. Use --stdio or --http.');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
main().catch(error => {
|
|
74
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
});
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CwtoolsMcpConfig {
|
|
2
|
+
workspaceRoot: string;
|
|
3
|
+
game?: string;
|
|
4
|
+
serverPath?: string;
|
|
5
|
+
gamePath?: string;
|
|
6
|
+
cachePath?: string;
|
|
7
|
+
rulesPath?: string;
|
|
8
|
+
bridgeManifestPath?: string;
|
|
9
|
+
stdio: boolean;
|
|
10
|
+
http: boolean;
|
|
11
|
+
host: string;
|
|
12
|
+
port: number;
|
|
13
|
+
enableWrites: boolean;
|
|
14
|
+
allowedTools: string[];
|
|
15
|
+
forceStart: boolean;
|
|
16
|
+
standalone: boolean;
|
|
17
|
+
workspaceRootExplicit: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function parseCliArgs(argv: string[]): CwtoolsMcpConfig;
|
|
20
|
+
export declare function helpText(): string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseCliArgs = parseCliArgs;
|
|
37
|
+
exports.helpText = helpText;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function parseCliArgs(argv) {
|
|
40
|
+
const config = {
|
|
41
|
+
workspaceRoot: process.cwd(),
|
|
42
|
+
stdio: true,
|
|
43
|
+
http: false,
|
|
44
|
+
host: '127.0.0.1',
|
|
45
|
+
port: 3000,
|
|
46
|
+
enableWrites: false,
|
|
47
|
+
allowedTools: [],
|
|
48
|
+
forceStart: false,
|
|
49
|
+
standalone: false,
|
|
50
|
+
workspaceRootExplicit: false,
|
|
51
|
+
};
|
|
52
|
+
for (let index = 0; index < argv.length; index++) {
|
|
53
|
+
const arg = argv[index];
|
|
54
|
+
switch (arg) {
|
|
55
|
+
case '--workspace':
|
|
56
|
+
config.workspaceRoot = path.resolve(readValue(argv, ++index, arg));
|
|
57
|
+
config.workspaceRootExplicit = true;
|
|
58
|
+
break;
|
|
59
|
+
case '--game':
|
|
60
|
+
config.game = readValue(argv, ++index, arg);
|
|
61
|
+
break;
|
|
62
|
+
case '--server-path':
|
|
63
|
+
config.serverPath = path.resolve(readValue(argv, ++index, arg));
|
|
64
|
+
break;
|
|
65
|
+
case '--game-path':
|
|
66
|
+
config.gamePath = path.resolve(readValue(argv, ++index, arg));
|
|
67
|
+
break;
|
|
68
|
+
case '--cache':
|
|
69
|
+
config.cachePath = path.resolve(readValue(argv, ++index, arg));
|
|
70
|
+
break;
|
|
71
|
+
case '--rules':
|
|
72
|
+
config.rulesPath = resolveRulesPath(readValue(argv, ++index, arg));
|
|
73
|
+
break;
|
|
74
|
+
case '--bridge-manifest':
|
|
75
|
+
config.bridgeManifestPath = path.resolve(readValue(argv, ++index, arg));
|
|
76
|
+
break;
|
|
77
|
+
case '--stdio':
|
|
78
|
+
config.stdio = true;
|
|
79
|
+
config.http = false;
|
|
80
|
+
break;
|
|
81
|
+
case '--http':
|
|
82
|
+
config.http = true;
|
|
83
|
+
config.stdio = false;
|
|
84
|
+
break;
|
|
85
|
+
case '--host':
|
|
86
|
+
config.host = readValue(argv, ++index, arg);
|
|
87
|
+
break;
|
|
88
|
+
case '--port':
|
|
89
|
+
config.port = readPort(readValue(argv, ++index, arg));
|
|
90
|
+
break;
|
|
91
|
+
case '--enable-writes':
|
|
92
|
+
config.enableWrites = true;
|
|
93
|
+
break;
|
|
94
|
+
case '--force-start':
|
|
95
|
+
config.forceStart = true;
|
|
96
|
+
break;
|
|
97
|
+
case '--standalone':
|
|
98
|
+
config.standalone = true;
|
|
99
|
+
break;
|
|
100
|
+
case '--allow-tool':
|
|
101
|
+
config.allowedTools.push(readValue(argv, ++index, arg));
|
|
102
|
+
break;
|
|
103
|
+
case '--help':
|
|
104
|
+
case '-h':
|
|
105
|
+
throw new Error(helpText());
|
|
106
|
+
default:
|
|
107
|
+
if (arg?.startsWith('--allow-tool=')) {
|
|
108
|
+
config.allowedTools.push(arg.slice('--allow-tool='.length));
|
|
109
|
+
}
|
|
110
|
+
else if (arg?.startsWith('--workspace=')) {
|
|
111
|
+
config.workspaceRoot = path.resolve(arg.slice('--workspace='.length));
|
|
112
|
+
config.workspaceRootExplicit = true;
|
|
113
|
+
}
|
|
114
|
+
else if (arg?.startsWith('--game=')) {
|
|
115
|
+
config.game = arg.slice('--game='.length);
|
|
116
|
+
}
|
|
117
|
+
else if (arg?.startsWith('--server-path=')) {
|
|
118
|
+
config.serverPath = path.resolve(arg.slice('--server-path='.length));
|
|
119
|
+
}
|
|
120
|
+
else if (arg?.startsWith('--game-path=')) {
|
|
121
|
+
config.gamePath = path.resolve(arg.slice('--game-path='.length));
|
|
122
|
+
}
|
|
123
|
+
else if (arg?.startsWith('--cache=')) {
|
|
124
|
+
config.cachePath = path.resolve(arg.slice('--cache='.length));
|
|
125
|
+
}
|
|
126
|
+
else if (arg?.startsWith('--rules=')) {
|
|
127
|
+
config.rulesPath = resolveRulesPath(arg.slice('--rules='.length));
|
|
128
|
+
}
|
|
129
|
+
else if (arg?.startsWith('--bridge-manifest=')) {
|
|
130
|
+
config.bridgeManifestPath = path.resolve(arg.slice('--bridge-manifest='.length));
|
|
131
|
+
}
|
|
132
|
+
else if (arg?.startsWith('--host=')) {
|
|
133
|
+
config.host = arg.slice('--host='.length);
|
|
134
|
+
}
|
|
135
|
+
else if (arg?.startsWith('--port=')) {
|
|
136
|
+
config.port = readPort(arg.slice('--port='.length));
|
|
137
|
+
}
|
|
138
|
+
else if (arg === '--http') {
|
|
139
|
+
config.http = true;
|
|
140
|
+
config.stdio = false;
|
|
141
|
+
}
|
|
142
|
+
else if (arg === '--standalone') {
|
|
143
|
+
config.standalone = true;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
throw new Error(`Unknown argument: ${arg}\n\n${helpText()}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return config;
|
|
151
|
+
}
|
|
152
|
+
function resolveRulesPath(value) {
|
|
153
|
+
const resolved = path.resolve(value);
|
|
154
|
+
if (resolved.toLowerCase().endsWith('.zip')) {
|
|
155
|
+
throw new Error('--rules must be a directory, not a .zip archive');
|
|
156
|
+
}
|
|
157
|
+
return resolved;
|
|
158
|
+
}
|
|
159
|
+
function readPort(value) {
|
|
160
|
+
const port = Number(value);
|
|
161
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
162
|
+
throw new Error(`Invalid --port value: ${value}`);
|
|
163
|
+
}
|
|
164
|
+
return port;
|
|
165
|
+
}
|
|
166
|
+
function readValue(argv, index, flag) {
|
|
167
|
+
const value = argv[index];
|
|
168
|
+
if (!value || value.startsWith('--')) {
|
|
169
|
+
throw new Error(`Missing value for ${flag}`);
|
|
170
|
+
}
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
function helpText() {
|
|
174
|
+
return [
|
|
175
|
+
'Usage:',
|
|
176
|
+
' cwtools-mcp [--stdio] [--bridge-manifest <path>] [--workspace <path>]',
|
|
177
|
+
' cwtools-mcp --http [--host 127.0.0.1] [--port 3000] [--bridge-manifest <path>] [--workspace <path>]',
|
|
178
|
+
' cwtools-mcp --standalone --workspace <path> [--game stellaris] [--stdio] [--server-path <path>]',
|
|
179
|
+
' cwtools-mcp --standalone --workspace <path> --http [--host 127.0.0.1] [--port 3000]',
|
|
180
|
+
' cwtools-mcp --standalone --workspace <path> --enable-writes',
|
|
181
|
+
' cwtools-mcp --standalone --workspace <path> --enable-writes --allow-tool write_localisation',
|
|
182
|
+
'',
|
|
183
|
+
'Default bridge mode:',
|
|
184
|
+
' The script connects to the extension-host MCP bridge written by the active',
|
|
185
|
+
' VS Code-compatible host. The manifest (bridge-manifest.json) is looked up next',
|
|
186
|
+
' to this script first, then auto-detected under the host globalStorage of VS Code,',
|
|
187
|
+
' Insiders, VSCodium, Cursor, and Antigravity. It does not start a second CWTools',
|
|
188
|
+
' language server. The client workspace (MCP roots, environment workspace, cwd,',
|
|
189
|
+
' or --workspace when supplied) must match the bridge workspace exactly. If the',
|
|
190
|
+
' compatible host is closed or the workspace does not match, tool calls return an',
|
|
191
|
+
' actionable unavailable error.',
|
|
192
|
+
' Use --standalone only when you intentionally want the legacy self-hosted LSP mode.',
|
|
193
|
+
'',
|
|
194
|
+
'Vanilla data (needed for vanilla IDs and correct mod-vs-vanilla diagnostics):',
|
|
195
|
+
' (auto) If neither flag is given, the VS Code cwtools extension cache',
|
|
196
|
+
' in globalStorage is auto-detected and reused.',
|
|
197
|
+
' --game-path <dir> Vanilla install/data dir; the server builds the cache from it (slow first run).',
|
|
198
|
+
' --cache <dir> Dir holding a pre-built <game>.cwb cache (overrides auto-detection),',
|
|
199
|
+
' loaded directly instead of rebuilding. Without any, results are mod-only.',
|
|
200
|
+
'',
|
|
201
|
+
'Rules source (priority: --rules > installed extension > dev checkout):',
|
|
202
|
+
' --rules <dir> Explicit CWT rules directory (a .zip is rejected). When omitted, the',
|
|
203
|
+
' rules the installed VS Code extension pulled into globalStorage are used.',
|
|
204
|
+
'',
|
|
205
|
+
'Project gate:',
|
|
206
|
+
' (default) Tools are always listed. The language server starts only when the',
|
|
207
|
+
' workspace is (or sits in/above) a Paradox mod (descriptor.mod, common/,',
|
|
208
|
+
' events/, localisation/, .cwtools/). On other workspaces tool calls are',
|
|
209
|
+
' rejected with a reason and no server spawns.',
|
|
210
|
+
' --force-start Treat the workspace as supported even without mod markers.',
|
|
211
|
+
].join('\n');
|
|
212
|
+
}
|