@nwire/mcp 0.7.1 → 0.8.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 +54 -20
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -33
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +40 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +278 -0
- package/dist/inspect.js.map +1 -0
- package/dist/write-tools.d.ts +27 -0
- package/dist/write-tools.d.ts.map +1 -0
- package/dist/write-tools.js +212 -0
- package/dist/write-tools.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/inspect-tools.test.ts +332 -0
- package/src/__tests__/mcp-io.test.ts +180 -0
- package/src/__tests__/write-tools.test.ts +350 -0
- package/src/index.ts +106 -38
- package/src/inspect.ts +315 -0
- package/src/write-tools.ts +238 -0
package/dist/inspect.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inspect tools — proxy a running wire's `/_nwire/*` introspection
|
|
3
|
+
* endpoints (or fall back to the on-disk `.nwire/` cache) as MCP tools.
|
|
4
|
+
*
|
|
5
|
+
* Today's `@nwire/mcp` only exposed the kernel's CLI commands. The
|
|
6
|
+
* running wire mounts a rich read-only surface (manifest, recent events,
|
|
7
|
+
* recent telemetry, dispatch…) at `/_nwire/*`. We adapt that surface
|
|
8
|
+
* into MCP tools so an AI client can ask "what actions does this app
|
|
9
|
+
* expose?" or "show me the last 100 events" without booting anything.
|
|
10
|
+
*
|
|
11
|
+
* Discovery follows the same pattern Studio uses (see
|
|
12
|
+
* `packages/nwire-studio/vite.config.ts`):
|
|
13
|
+
*
|
|
14
|
+
* 1. If `NWIRE_INSPECT_URL` is set, trust it.
|
|
15
|
+
* 2. Otherwise, probe a small fixed list of ports for a wire that
|
|
16
|
+
* answers `GET /_nwire/events/recent?limit=1` with a JSON array.
|
|
17
|
+
* 3. Cache the result for 5s so a burst of tool calls is cheap.
|
|
18
|
+
*
|
|
19
|
+
* For tools that have a disk equivalent (`manifest`, `hooks`,
|
|
20
|
+
* `plugins`), we fall through to `${cwd}/.nwire/<file>.json` when no
|
|
21
|
+
* live wire answers. That makes the inspect tools usable in a cold
|
|
22
|
+
* project too — just run `nwire cache` once.
|
|
23
|
+
*/
|
|
24
|
+
import { request as httpRequest } from "node:http";
|
|
25
|
+
import { createConnection } from "node:net";
|
|
26
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
27
|
+
import { resolve } from "node:path";
|
|
28
|
+
// ─── Port discovery ────────────────────────────────────────────────
|
|
29
|
+
const DEFAULT_PROBE_PORTS = [
|
|
30
|
+
3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010,
|
|
31
|
+
3030, 3040, 3050, 4000, 8080,
|
|
32
|
+
];
|
|
33
|
+
const PROBE_TIMEOUT_MS = 150;
|
|
34
|
+
const CACHE_TTL_MS = 5_000;
|
|
35
|
+
const NEGATIVE_CACHE_TTL_MS = 1_500;
|
|
36
|
+
let discovered = { url: undefined, expires: 0 };
|
|
37
|
+
/** Reset the URL cache. Tests use this to flush between cases. */
|
|
38
|
+
export function resetInspectDiscoveryCache() {
|
|
39
|
+
discovered = { url: undefined, expires: 0 };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Discover the base URL of a running wire. Honours `NWIRE_INSPECT_URL`
|
|
43
|
+
* first (and does NOT validate it — callers asked for it explicitly),
|
|
44
|
+
* then probes a fixed list of ports.
|
|
45
|
+
*/
|
|
46
|
+
export async function discoverInspectUrl() {
|
|
47
|
+
if (process.env.NWIRE_INSPECT_URL) {
|
|
48
|
+
return process.env.NWIRE_INSPECT_URL.replace(/\/$/, "");
|
|
49
|
+
}
|
|
50
|
+
if (Date.now() < discovered.expires)
|
|
51
|
+
return discovered.url;
|
|
52
|
+
const ports = parsePorts(process.env.NWIRE_PROBE_PORTS) ?? DEFAULT_PROBE_PORTS;
|
|
53
|
+
for (const port of ports) {
|
|
54
|
+
if (await isNwireWire(port, PROBE_TIMEOUT_MS)) {
|
|
55
|
+
const url = `http://127.0.0.1:${port}`;
|
|
56
|
+
discovered = { url, expires: Date.now() + CACHE_TTL_MS };
|
|
57
|
+
return url;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
discovered = { url: undefined, expires: Date.now() + NEGATIVE_CACHE_TTL_MS };
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function parsePorts(raw) {
|
|
64
|
+
if (!raw)
|
|
65
|
+
return undefined;
|
|
66
|
+
const ports = raw.split(",").map((s) => Number(s.trim())).filter((n) => n > 0);
|
|
67
|
+
return ports.length > 0 ? ports : undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Probe one port: open a socket, then GET `/_nwire/events/recent?limit=1`
|
|
71
|
+
* and require a JSON-array body. This rejects unrelated services on the
|
|
72
|
+
* same port (TCP-only probes were too permissive).
|
|
73
|
+
*/
|
|
74
|
+
function isNwireWire(port, timeoutMs) {
|
|
75
|
+
return new Promise((resolveProbe) => {
|
|
76
|
+
const sock = createConnection({ host: "127.0.0.1", port });
|
|
77
|
+
sock.setTimeout(timeoutMs, () => { sock.destroy(); resolveProbe(false); });
|
|
78
|
+
sock.once("error", () => resolveProbe(false));
|
|
79
|
+
sock.once("connect", () => {
|
|
80
|
+
sock.destroy();
|
|
81
|
+
const req = httpRequest({ host: "127.0.0.1", port, path: "/_nwire/events/recent?limit=1", method: "GET" }, (res) => {
|
|
82
|
+
if (res.statusCode !== 200) {
|
|
83
|
+
res.resume();
|
|
84
|
+
return resolveProbe(false);
|
|
85
|
+
}
|
|
86
|
+
let buf = "";
|
|
87
|
+
res.setEncoding("utf8");
|
|
88
|
+
res.on("data", (c) => { buf += c; if (buf.length > 1024)
|
|
89
|
+
res.destroy(); });
|
|
90
|
+
res.on("end", () => resolveProbe(buf.trimStart().startsWith("[")));
|
|
91
|
+
});
|
|
92
|
+
req.setTimeout(timeoutMs, () => { req.destroy(); resolveProbe(false); });
|
|
93
|
+
req.once("error", () => resolveProbe(false));
|
|
94
|
+
req.end();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// ─── HTTP helpers ──────────────────────────────────────────────────
|
|
99
|
+
async function httpGetJson(url) {
|
|
100
|
+
return new Promise((resolveGet, rejectGet) => {
|
|
101
|
+
const u = new URL(url);
|
|
102
|
+
const req = httpRequest({ host: u.hostname, port: u.port || 80, path: u.pathname + u.search, method: "GET" }, (res) => {
|
|
103
|
+
let buf = "";
|
|
104
|
+
res.setEncoding("utf8");
|
|
105
|
+
res.on("data", (c) => { buf += c; });
|
|
106
|
+
res.on("end", () => {
|
|
107
|
+
if ((res.statusCode ?? 0) >= 400) {
|
|
108
|
+
return rejectGet(new Error(`GET ${url} → ${res.statusCode}`));
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
resolveGet(buf.length === 0 ? undefined : JSON.parse(buf));
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
rejectGet(new Error(`GET ${url} returned non-JSON: ${err.message}`));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
req.setTimeout(5_000, () => { req.destroy(); rejectGet(new Error(`GET ${url} timed out`)); });
|
|
119
|
+
req.once("error", rejectGet);
|
|
120
|
+
req.end();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// ─── Disk fallback ─────────────────────────────────────────────────
|
|
124
|
+
function readDiskCache(file) {
|
|
125
|
+
const path = resolve(process.cwd(), ".nwire", file);
|
|
126
|
+
if (!existsSync(path))
|
|
127
|
+
return undefined;
|
|
128
|
+
try {
|
|
129
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Resolve the full manifest. Live wire wins; otherwise disk cache.
|
|
137
|
+
* Throws a helpful error if neither is available.
|
|
138
|
+
*/
|
|
139
|
+
async function getManifest() {
|
|
140
|
+
const url = await discoverInspectUrl();
|
|
141
|
+
if (url) {
|
|
142
|
+
try {
|
|
143
|
+
return (await httpGetJson(`${url}/_nwire/manifest`));
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// fall through to disk
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const disk = readDiskCache("manifest.json");
|
|
150
|
+
if (disk)
|
|
151
|
+
return disk;
|
|
152
|
+
throw new Error("no running wire and no .nwire/manifest.json on disk. Start a wire or run `nwire cache`.");
|
|
153
|
+
}
|
|
154
|
+
function filterByModule(items, module) {
|
|
155
|
+
if (typeof module !== "string" || module.length === 0)
|
|
156
|
+
return items;
|
|
157
|
+
return items.filter((it) => it.module === module);
|
|
158
|
+
}
|
|
159
|
+
export const inspectTools = [
|
|
160
|
+
{
|
|
161
|
+
name: "manifest",
|
|
162
|
+
description: "Return the full .nwire manifest of the running wire (live HTTP) or the on-disk cache.",
|
|
163
|
+
inputSchema: { type: "object", additionalProperties: false, properties: {} },
|
|
164
|
+
async run() {
|
|
165
|
+
return getManifest();
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: "list_actions",
|
|
170
|
+
description: "List actions exposed by the wire. Optional `module` filter (e.g. \"submissions\").",
|
|
171
|
+
inputSchema: {
|
|
172
|
+
type: "object",
|
|
173
|
+
additionalProperties: false,
|
|
174
|
+
properties: { module: { type: "string" } },
|
|
175
|
+
},
|
|
176
|
+
async run(args) {
|
|
177
|
+
const manifest = await getManifest();
|
|
178
|
+
const actions = (manifest.actions ?? readDiskCache("actions.json") ?? []);
|
|
179
|
+
return filterByModule(actions, args.module);
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "list_events",
|
|
184
|
+
description: "List events declared by the wire. Optional `module` filter.",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
additionalProperties: false,
|
|
188
|
+
properties: { module: { type: "string" } },
|
|
189
|
+
},
|
|
190
|
+
async run(args) {
|
|
191
|
+
const manifest = await getManifest();
|
|
192
|
+
const events = (manifest.events ?? readDiskCache("events.json") ?? []);
|
|
193
|
+
return filterByModule(events, args.module);
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "list_hooks",
|
|
198
|
+
description: "List runtime hooks (reads .nwire/hooks.json from disk).",
|
|
199
|
+
inputSchema: { type: "object", additionalProperties: false, properties: {} },
|
|
200
|
+
async run() {
|
|
201
|
+
// hooks.json lives on disk; the running wire does not currently
|
|
202
|
+
// expose it via /_nwire (it's scanner output, not runtime state).
|
|
203
|
+
const disk = readDiskCache("hooks.json");
|
|
204
|
+
if (disk)
|
|
205
|
+
return disk;
|
|
206
|
+
// Try the manifest as a last resort — some scans embed hooks there.
|
|
207
|
+
try {
|
|
208
|
+
const manifest = await getManifest();
|
|
209
|
+
return manifest.hooks ?? [];
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
throw new Error(".nwire/hooks.json not found. Run `nwire cache`.");
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: "list_plugins",
|
|
218
|
+
description: "List discovered plugins (reads .nwire/plugins.json). Optional `kind` filter: \"plugin\" | \"module\".",
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: "object",
|
|
221
|
+
additionalProperties: false,
|
|
222
|
+
properties: { kind: { type: "string", enum: ["plugin", "module"] } },
|
|
223
|
+
},
|
|
224
|
+
async run(args) {
|
|
225
|
+
let plugins = readDiskCache("plugins.json");
|
|
226
|
+
if (!plugins) {
|
|
227
|
+
try {
|
|
228
|
+
const manifest = await getManifest();
|
|
229
|
+
plugins = (manifest.plugins ?? []);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
throw new Error(".nwire/plugins.json not found. Run `nwire cache`.");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (typeof args.kind === "string" && args.kind.length > 0) {
|
|
236
|
+
return plugins.filter((p) => p.kind === args.kind);
|
|
237
|
+
}
|
|
238
|
+
return plugins;
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: "recent_events",
|
|
243
|
+
description: "GET /_nwire/events/recent?limit=N from the running wire. Requires a live wire.",
|
|
244
|
+
inputSchema: {
|
|
245
|
+
type: "object",
|
|
246
|
+
additionalProperties: false,
|
|
247
|
+
properties: { limit: { type: "number", minimum: 1, maximum: 10_000 } },
|
|
248
|
+
},
|
|
249
|
+
async run(args) {
|
|
250
|
+
const url = await discoverInspectUrl();
|
|
251
|
+
if (!url)
|
|
252
|
+
throw new Error("no running wire detected; recent_events requires a live wire.");
|
|
253
|
+
const limit = typeof args.limit === "number" ? args.limit : 100;
|
|
254
|
+
return httpGetJson(`${url}/_nwire/events/recent?limit=${limit}`);
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "recent_telemetry",
|
|
259
|
+
description: "GET /_nwire/telemetry/recent?limit=N from the running wire. Requires a live wire.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
additionalProperties: false,
|
|
263
|
+
properties: { limit: { type: "number", minimum: 1, maximum: 10_000 } },
|
|
264
|
+
},
|
|
265
|
+
async run(args) {
|
|
266
|
+
const url = await discoverInspectUrl();
|
|
267
|
+
if (!url) {
|
|
268
|
+
throw new Error("no running wire detected; recent_telemetry requires a live wire.");
|
|
269
|
+
}
|
|
270
|
+
const limit = typeof args.limit === "number" ? args.limit : 100;
|
|
271
|
+
return httpGetJson(`${url}/_nwire/telemetry/recent?limit=${limit}`);
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
];
|
|
275
|
+
export function findInspectTool(name) {
|
|
276
|
+
return inspectTools.find((t) => t.name === name);
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspect.js","sourceRoot":"","sources":["../src/inspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write tools — the mutating counterpart to `inspect.ts`.
|
|
3
|
+
*
|
|
4
|
+
* Where the inspect tools proxy `GET /_nwire/*` (or fall back to the
|
|
5
|
+
* `.nwire/` disk cache), write tools drive the running wire through its
|
|
6
|
+
* mutating endpoints:
|
|
7
|
+
*
|
|
8
|
+
* POST /_nwire/dispatch — invoke a registered action ← `dispatch_action`
|
|
9
|
+
* POST /_nwire/command — invoke a defineCommand ← NOT YET MOUNTED
|
|
10
|
+
* POST /_nwire/replay — replay a hook recording ← NOT YET MOUNTED
|
|
11
|
+
*
|
|
12
|
+
* The wire ships only `/_nwire/dispatch` today (see
|
|
13
|
+
* `packages/nwire-http/src/inspect.ts`). For tools whose endpoint does
|
|
14
|
+
* not yet exist, we ship the tool ANYWAY so the contract is visible in
|
|
15
|
+
* `tools/list`, but the call returns `isError: true` with an explicit
|
|
16
|
+
* "endpoint not implemented on wire side" message rather than
|
|
17
|
+
* synthesizing a fake response. The companion follow-up in
|
|
18
|
+
* `@nwire/http` will mount the missing routes.
|
|
19
|
+
*
|
|
20
|
+
* These tools register alongside `inspectTools` and reuse the same URL
|
|
21
|
+
* discovery cache (`discoverInspectUrl`). They do NOT modify the
|
|
22
|
+
* existing read-only surface.
|
|
23
|
+
*/
|
|
24
|
+
import { type InspectToolDef } from "./inspect.js";
|
|
25
|
+
export declare const writeTools: readonly InspectToolDef[];
|
|
26
|
+
export declare function findWriteTool(name: string): InspectToolDef | undefined;
|
|
27
|
+
//# sourceMappingURL=write-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-tools.d.ts","sourceRoot":"","sources":["../src/write-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AA4MvE,eAAO,MAAM,UAAU,EAAE,SAAS,cAAc,EAI/C,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEtE"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write tools — the mutating counterpart to `inspect.ts`.
|
|
3
|
+
*
|
|
4
|
+
* Where the inspect tools proxy `GET /_nwire/*` (or fall back to the
|
|
5
|
+
* `.nwire/` disk cache), write tools drive the running wire through its
|
|
6
|
+
* mutating endpoints:
|
|
7
|
+
*
|
|
8
|
+
* POST /_nwire/dispatch — invoke a registered action ← `dispatch_action`
|
|
9
|
+
* POST /_nwire/command — invoke a defineCommand ← NOT YET MOUNTED
|
|
10
|
+
* POST /_nwire/replay — replay a hook recording ← NOT YET MOUNTED
|
|
11
|
+
*
|
|
12
|
+
* The wire ships only `/_nwire/dispatch` today (see
|
|
13
|
+
* `packages/nwire-http/src/inspect.ts`). For tools whose endpoint does
|
|
14
|
+
* not yet exist, we ship the tool ANYWAY so the contract is visible in
|
|
15
|
+
* `tools/list`, but the call returns `isError: true` with an explicit
|
|
16
|
+
* "endpoint not implemented on wire side" message rather than
|
|
17
|
+
* synthesizing a fake response. The companion follow-up in
|
|
18
|
+
* `@nwire/http` will mount the missing routes.
|
|
19
|
+
*
|
|
20
|
+
* These tools register alongside `inspectTools` and reuse the same URL
|
|
21
|
+
* discovery cache (`discoverInspectUrl`). They do NOT modify the
|
|
22
|
+
* existing read-only surface.
|
|
23
|
+
*/
|
|
24
|
+
import { request as httpRequest } from "node:http";
|
|
25
|
+
import { discoverInspectUrl } from "./inspect.js";
|
|
26
|
+
/**
|
|
27
|
+
* POST JSON to a URL. Resolves with `{ status, body }` even for 4xx/5xx
|
|
28
|
+
* — the caller decides what to do with non-2xx responses, because the
|
|
29
|
+
* wire returns useful diagnostics in the body (e.g. action-not-found).
|
|
30
|
+
*/
|
|
31
|
+
async function httpPostJson(url, payload) {
|
|
32
|
+
return new Promise((resolvePost, rejectPost) => {
|
|
33
|
+
const u = new URL(url);
|
|
34
|
+
const data = JSON.stringify(payload ?? {});
|
|
35
|
+
const req = httpRequest({
|
|
36
|
+
host: u.hostname,
|
|
37
|
+
port: u.port || 80,
|
|
38
|
+
path: u.pathname + u.search,
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
"Content-Length": Buffer.byteLength(data).toString(),
|
|
43
|
+
},
|
|
44
|
+
}, (res) => {
|
|
45
|
+
let buf = "";
|
|
46
|
+
res.setEncoding("utf8");
|
|
47
|
+
res.on("data", (c) => { buf += c; });
|
|
48
|
+
res.on("end", () => {
|
|
49
|
+
const status = res.statusCode ?? 0;
|
|
50
|
+
if (buf.length === 0)
|
|
51
|
+
return resolvePost({ status, body: undefined });
|
|
52
|
+
try {
|
|
53
|
+
resolvePost({ status, body: JSON.parse(buf) });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Non-JSON body — surface it raw so callers see the truth.
|
|
57
|
+
resolvePost({ status, body: buf });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
req.setTimeout(10_000, () => {
|
|
62
|
+
req.destroy();
|
|
63
|
+
rejectPost(new Error(`POST ${url} timed out`));
|
|
64
|
+
});
|
|
65
|
+
req.once("error", rejectPost);
|
|
66
|
+
req.write(data);
|
|
67
|
+
req.end();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// ─── Tools ─────────────────────────────────────────────────────────
|
|
71
|
+
const NO_WIRE = "no running wire detected; this tool requires a live wire (set NWIRE_INSPECT_URL or start one).";
|
|
72
|
+
/**
|
|
73
|
+
* Invoke a registered action via `POST /_nwire/dispatch`. Returns the
|
|
74
|
+
* wire's envelope + result on success; surfaces the wire's diagnostic
|
|
75
|
+
* body on 4xx/5xx without rewriting it.
|
|
76
|
+
*/
|
|
77
|
+
const dispatchAction = {
|
|
78
|
+
name: "dispatch_action",
|
|
79
|
+
description: "Invoke a registered action on the running wire (POST /_nwire/dispatch). " +
|
|
80
|
+
"Args: { action: string, input: object, user?: { id, roles? } }. " +
|
|
81
|
+
"Returns the action result + correlation envelope, or the wire's error body on failure.",
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: "object",
|
|
84
|
+
additionalProperties: false,
|
|
85
|
+
required: ["action"],
|
|
86
|
+
properties: {
|
|
87
|
+
action: { type: "string" },
|
|
88
|
+
input: { type: "object", additionalProperties: true },
|
|
89
|
+
user: {
|
|
90
|
+
type: "object",
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
properties: {
|
|
93
|
+
id: { type: "string" },
|
|
94
|
+
roles: { type: "array", items: { type: "string" } },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
tenant: { type: "string" },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
async run(args) {
|
|
101
|
+
const url = await discoverInspectUrl();
|
|
102
|
+
if (!url)
|
|
103
|
+
throw new Error(NO_WIRE);
|
|
104
|
+
const action = args.action;
|
|
105
|
+
if (!action)
|
|
106
|
+
throw new Error("dispatch_action requires `action`");
|
|
107
|
+
const user = args.user;
|
|
108
|
+
const tenant = args.tenant;
|
|
109
|
+
const payload = {
|
|
110
|
+
action,
|
|
111
|
+
input: args.input ?? {},
|
|
112
|
+
...(user?.id !== undefined && { userId: user.id }),
|
|
113
|
+
...(tenant !== undefined && { tenant }),
|
|
114
|
+
};
|
|
115
|
+
const { status, body } = await httpPostJson(`${url}/_nwire/dispatch`, payload);
|
|
116
|
+
if (status >= 400) {
|
|
117
|
+
throw new Error(`dispatch failed (${status}): ${typeof body === "string" ? body : JSON.stringify(body)}`);
|
|
118
|
+
}
|
|
119
|
+
return body;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Invoke a `defineCommand` on the wire. The wire does NOT currently
|
|
124
|
+
* expose a `/_nwire/command` endpoint (see
|
|
125
|
+
* `packages/nwire-http/src/inspect.ts`); this tool gap-flags until that
|
|
126
|
+
* endpoint is mounted. The intended shape is documented in the error.
|
|
127
|
+
*/
|
|
128
|
+
const runCommand = {
|
|
129
|
+
name: "run_command",
|
|
130
|
+
description: "Invoke a registered defineCommand on the running wire by name. " +
|
|
131
|
+
"Args: { name: string, args: object }. " +
|
|
132
|
+
"NOTE: the wire-side endpoint (/_nwire/command) is not yet mounted — see gap-flag in tool output.",
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: "object",
|
|
135
|
+
additionalProperties: false,
|
|
136
|
+
required: ["name"],
|
|
137
|
+
properties: {
|
|
138
|
+
name: { type: "string" },
|
|
139
|
+
args: { type: "object", additionalProperties: true },
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
async run(args) {
|
|
143
|
+
const url = await discoverInspectUrl();
|
|
144
|
+
if (!url)
|
|
145
|
+
throw new Error(NO_WIRE);
|
|
146
|
+
const name = args.name;
|
|
147
|
+
if (!name)
|
|
148
|
+
throw new Error("run_command requires `name`");
|
|
149
|
+
// Try the canonical endpoint first; if the wire returns 404 we surface
|
|
150
|
+
// a clear gap message rather than silently faking something.
|
|
151
|
+
const { status, body } = await httpPostJson(`${url}/_nwire/command`, {
|
|
152
|
+
name,
|
|
153
|
+
args: args.args ?? {},
|
|
154
|
+
});
|
|
155
|
+
if (status === 404) {
|
|
156
|
+
throw new Error("endpoint not implemented yet on wire side: POST /_nwire/command. " +
|
|
157
|
+
"See packages/nwire-http/src/inspect.ts — only /_nwire/dispatch is mounted today. " +
|
|
158
|
+
"Workaround: invoke kernel commands via the CLI (`nwire please ...`) until the route lands.");
|
|
159
|
+
}
|
|
160
|
+
if (status >= 400) {
|
|
161
|
+
throw new Error(`run_command failed (${status}): ${typeof body === "string" ? body : JSON.stringify(body)}`);
|
|
162
|
+
}
|
|
163
|
+
return body;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Replay a `@nwire/hooks` recording on the wire. Same gap as
|
|
168
|
+
* `run_command` — `/_nwire/replay` is not yet mounted; tool ships so
|
|
169
|
+
* the contract is visible.
|
|
170
|
+
*/
|
|
171
|
+
const replayTrace = {
|
|
172
|
+
name: "replay_trace",
|
|
173
|
+
description: "Replay a @nwire/hooks recording on the running wire (POST /_nwire/replay). " +
|
|
174
|
+
"Args: { recording: object } where `recording` is the output of `record()`. " +
|
|
175
|
+
"NOTE: the wire-side endpoint (/_nwire/replay) is not yet mounted — see gap-flag in tool output.",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: "object",
|
|
178
|
+
additionalProperties: false,
|
|
179
|
+
required: ["recording"],
|
|
180
|
+
properties: {
|
|
181
|
+
recording: { type: "object", additionalProperties: true },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
async run(args) {
|
|
185
|
+
const url = await discoverInspectUrl();
|
|
186
|
+
if (!url)
|
|
187
|
+
throw new Error(NO_WIRE);
|
|
188
|
+
const recording = args.recording;
|
|
189
|
+
if (!recording || typeof recording !== "object") {
|
|
190
|
+
throw new Error("replay_trace requires `recording` (object from @nwire/hooks record()).");
|
|
191
|
+
}
|
|
192
|
+
const { status, body } = await httpPostJson(`${url}/_nwire/replay`, { recording });
|
|
193
|
+
if (status === 404) {
|
|
194
|
+
throw new Error("endpoint not implemented yet on wire side: POST /_nwire/replay. " +
|
|
195
|
+
"See packages/nwire-http/src/inspect.ts — recordings can still be replayed in-process via " +
|
|
196
|
+
"`replay()` from @nwire/hooks; HTTP replay against a running wire requires the new route.");
|
|
197
|
+
}
|
|
198
|
+
if (status >= 400) {
|
|
199
|
+
throw new Error(`replay_trace failed (${status}): ${typeof body === "string" ? body : JSON.stringify(body)}`);
|
|
200
|
+
}
|
|
201
|
+
return body;
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
export const writeTools = [
|
|
205
|
+
dispatchAction,
|
|
206
|
+
runCommand,
|
|
207
|
+
replayTrace,
|
|
208
|
+
];
|
|
209
|
+
export function findWriteTool(name) {
|
|
210
|
+
return writeTools.find((t) => t.name === name);
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=write-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-tools.js","sourceRoot":"","sources":["../src/write-tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwire/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Nwire MCP (Model Context Protocol) server — exposes the kernel's CommandRouter as MCP tools over stdio. AI clients (Claude, Cursor, …) drive nwire commands the same way the CLI and Studio do.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@nwire/kernel": "0.
|
|
25
|
+
"@nwire/kernel": "0.8.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.9",
|