@macula-io/mcp 0.3.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.
Files changed (60) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +192 -0
  3. package/dist/bin/install.d.ts +2 -0
  4. package/dist/bin/install.js +146 -0
  5. package/dist/bin/install.js.map +1 -0
  6. package/dist/bin/status.d.ts +2 -0
  7. package/dist/bin/status.js +86 -0
  8. package/dist/bin/status.js.map +1 -0
  9. package/dist/bin/uninstall.d.ts +2 -0
  10. package/dist/bin/uninstall.js +81 -0
  11. package/dist/bin/uninstall.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +50 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/install/config_merge.d.ts +24 -0
  16. package/dist/install/config_merge.js +141 -0
  17. package/dist/install/config_merge.js.map +1 -0
  18. package/dist/install/existing_cli.d.ts +6 -0
  19. package/dist/install/existing_cli.js +24 -0
  20. package/dist/install/existing_cli.js.map +1 -0
  21. package/dist/install/mcp_clients/claude_code.d.ts +7 -0
  22. package/dist/install/mcp_clients/claude_code.js +51 -0
  23. package/dist/install/mcp_clients/claude_code.js.map +1 -0
  24. package/dist/install/mcp_clients/claude_desktop.d.ts +7 -0
  25. package/dist/install/mcp_clients/claude_desktop.js +40 -0
  26. package/dist/install/mcp_clients/claude_desktop.js.map +1 -0
  27. package/dist/install/mcp_clients/cursor.d.ts +7 -0
  28. package/dist/install/mcp_clients/cursor.js +30 -0
  29. package/dist/install/mcp_clients/cursor.js.map +1 -0
  30. package/dist/install/mcp_clients/index.d.ts +11 -0
  31. package/dist/install/mcp_clients/index.js +12 -0
  32. package/dist/install/mcp_clients/index.js.map +1 -0
  33. package/dist/install/mcp_clients/windsurf.d.ts +7 -0
  34. package/dist/install/mcp_clients/windsurf.js +30 -0
  35. package/dist/install/mcp_clients/windsurf.js.map +1 -0
  36. package/dist/install/platform.d.ts +9 -0
  37. package/dist/install/platform.js +34 -0
  38. package/dist/install/platform.js.map +1 -0
  39. package/dist/macula_cli.d.ts +88 -0
  40. package/dist/macula_cli.js +189 -0
  41. package/dist/macula_cli.js.map +1 -0
  42. package/dist/mesh_artifact.d.ts +2 -0
  43. package/dist/mesh_artifact.js +63 -0
  44. package/dist/mesh_artifact.js.map +1 -0
  45. package/dist/mesh_call.d.ts +2 -0
  46. package/dist/mesh_call.js +45 -0
  47. package/dist/mesh_call.js.map +1 -0
  48. package/dist/mesh_identity.d.ts +2 -0
  49. package/dist/mesh_identity.js +28 -0
  50. package/dist/mesh_identity.js.map +1 -0
  51. package/dist/mesh_publish.d.ts +2 -0
  52. package/dist/mesh_publish.js +33 -0
  53. package/dist/mesh_publish.js.map +1 -0
  54. package/dist/mesh_watch.d.ts +2 -0
  55. package/dist/mesh_watch.js +53 -0
  56. package/dist/mesh_watch.js.map +1 -0
  57. package/dist/reply.d.ts +16 -0
  58. package/dist/reply.js +24 -0
  59. package/dist/reply.js.map +1 -0
  60. package/package.json +55 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/install/platform.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,gEAAgE;AAChE,oEAAoE;AACpE,4BAA4B;AAE5B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAYzC,MAAM,UAAU,MAAM;IACpB,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC;IAEvB,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7B,OAAO;QACL,EAAE;QACF,IAAI,EAAE,GAAG;QACT,KAAK,EAAE,GAAG,EAAE,IAAI,GAAG,EAAE;KACtB,CAAC;AACJ,CAAC;AAED,SAAS,KAAK,CAAC,CAAkB;IAC/B,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,IAAI,CAAC,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACpC,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,MAAM,IAAI,KAAK,CACb,yBAAyB,CAAC,0CAA0C;QAClE,kEAAkE,CAAC,GAAG,CACzE,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,IAAI,CAAC,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC9B,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IAClC,MAAM,IAAI,KAAK,CACb,qBAAqB,CAAC,kCAAkC,CACzD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,88 @@
1
+ /** The station this server targets when a tool call doesn't override it. */
2
+ export declare function defaultStation(): string;
3
+ export declare class MaculaCliUnavailable extends Error {
4
+ }
5
+ export declare class MaculaCliError extends Error {
6
+ readonly bolt4Code?: number | undefined;
7
+ readonly bolt4Name?: string | undefined;
8
+ readonly retryable?: boolean | undefined;
9
+ constructor(message: string, bolt4Code?: number | undefined, bolt4Name?: string | undefined, retryable?: boolean | undefined);
10
+ }
11
+ export interface IdentityResult {
12
+ node_id: string;
13
+ path: string;
14
+ generated: boolean;
15
+ }
16
+ export declare const identity: () => Promise<IdentityResult>;
17
+ export interface CallResult {
18
+ procedure: string;
19
+ responded_by: string;
20
+ payload: unknown;
21
+ duration_ms: number;
22
+ }
23
+ export declare const call: (args: {
24
+ host?: string;
25
+ procedure: string;
26
+ callArgs?: Record<string, unknown>;
27
+ timeoutMs?: number;
28
+ }) => Promise<CallResult>;
29
+ export interface PublishResult {
30
+ topic: string;
31
+ seq: number;
32
+ duration_ms: number;
33
+ }
34
+ export declare const publish: (args: {
35
+ host?: string;
36
+ topic: string;
37
+ fact: Record<string, unknown>;
38
+ }) => Promise<PublishResult>;
39
+ export interface WatchEvent {
40
+ topic: string;
41
+ publisher: string;
42
+ seq: number;
43
+ payload: unknown;
44
+ delivered_via: string;
45
+ received_at: string;
46
+ }
47
+ /**
48
+ * Parses `macula-cli pubsub watch --json`'s stdout: one WatchEvent per
49
+ * line (NDJSON, not a single envelope -- run() assumes exactly one
50
+ * envelope and doesn't fit this shape). A real failure mid-watch (e.g.
51
+ * the connection drops after some events already arrived) prints as a
52
+ * trailing `{"ok":false,...}` envelope line -- checked by the presence
53
+ * of an "ok" key, not by JSON.parse throwing, since a wrong-shape
54
+ * object is still perfectly valid JSON and would otherwise silently
55
+ * end up in the events array instead of being surfaced as a failure.
56
+ * Exported standalone so this parsing can be unit-tested without
57
+ * spawning a real subprocess.
58
+ */
59
+ export declare function parseWatchOutput(stdout: string): WatchEvent[];
60
+ /**
61
+ * Watches a topic for up to durationSeconds and returns whatever
62
+ * arrived. Bounded and synchronous -- the MCP tool call itself blocks
63
+ * for the duration, which is the honest shape of a one-shot CLI with no
64
+ * background process to keep a standing subscription alive between
65
+ * calls.
66
+ */
67
+ export declare const watch: (args: {
68
+ host?: string;
69
+ topic: string;
70
+ durationSeconds: number;
71
+ count?: number;
72
+ }) => Promise<WatchEvent[]>;
73
+ export interface ArtifactPutResult {
74
+ mcid_hex: string;
75
+ size_bytes: number;
76
+ }
77
+ export declare const artifactPut: (args: {
78
+ host?: string;
79
+ contentBase64: string;
80
+ }) => Promise<ArtifactPutResult>;
81
+ export interface ArtifactGetResult {
82
+ content: string;
83
+ size_bytes: number;
84
+ }
85
+ export declare const artifactGet: (args: {
86
+ host?: string;
87
+ mcidHex: string;
88
+ }) => Promise<ArtifactGetResult>;
@@ -0,0 +1,189 @@
1
+ // Subprocess client for the `macula-cli` binary (macula-io/macula-cli).
2
+ //
3
+ // macula-mcp does NOT speak QUIC, DHT, or Macula RPC directly, and no
4
+ // longer proxies to a local hecate-daemon (treated as obsolete -- it was
5
+ // a leftover of an abandoned local browser/UI plan). It shells out to
6
+ // macula-cli, a one-shot scriptable CLI built for exactly this: every
7
+ // command takes --json and returns {ok, data} or {ok:false, error}.
8
+ //
9
+ // agent harness --MCP/stdio--> macula-mcp --spawns, parses stdout--> macula-cli --QUIC--> mesh
10
+ //
11
+ // This is a deliberately lean rework (see macula-cli's own README/HOWTO
12
+ // guide for the command set): no standing subscriptions, no activity/
13
+ // inbox audit log, no peer listing -- macula-cli is a one-shot process
14
+ // with no daemon and no storage, so none of those persist between
15
+ // invocations. Point-in-time operations only.
16
+ import { spawn } from "node:child_process";
17
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
18
+ import { tmpdir, homedir } from "node:os";
19
+ import { join } from "node:path";
20
+ /** The station this server targets when a tool call doesn't override it. */
21
+ export function defaultStation() {
22
+ return process.env.MACULA_MESH_STATION ?? "station-de-frankfurt.macula.io:4433";
23
+ }
24
+ /** Name/path of the macula-cli binary; override for testing or a non-PATH install. */
25
+ function binPath() {
26
+ return process.env.MACULA_CLI_BIN ?? "macula-cli";
27
+ }
28
+ /**
29
+ * A dedicated, persisted identity for mesh_watch, separate from
30
+ * macula-cli's own default identity (used by every other tool here).
31
+ *
32
+ * Found live (2026-08-29): a station kicks a connection the moment a
33
+ * SECOND connection arrives under the same node ID -- a real
34
+ * anti-duplicate-session guard, not a bug (see macula-cli's own HOWTO
35
+ * guide §1). mesh_watch holds a connection open for up to
36
+ * MAX_DURATION_SECONDS; any OTHER tool call (mesh_call, mesh_publish,
37
+ * ...) that fires while a watch is in flight would otherwise share the
38
+ * same default identity and silently kill the watcher's connection.
39
+ * A separate persisted identity for watch is the minimum fix that
40
+ * covers the common case (watch running alongside other calls) without
41
+ * paying a fresh puzzle-grind on every single tool call the way a
42
+ * per-invocation ephemeral identity would. Two CONCURRENT mesh_watch
43
+ * calls would still collide with each other -- not solved here, a
44
+ * known limitation, not a silent one.
45
+ */
46
+ function watchIdentityPath() {
47
+ return process.env.MACULA_MCP_WATCH_IDENTITY ?? join(homedir(), ".macula-mcp", "watch-identity.seed");
48
+ }
49
+ export class MaculaCliUnavailable extends Error {
50
+ }
51
+ export class MaculaCliError extends Error {
52
+ bolt4Code;
53
+ bolt4Name;
54
+ retryable;
55
+ constructor(message, bolt4Code, bolt4Name, retryable) {
56
+ super(message);
57
+ this.bolt4Code = bolt4Code;
58
+ this.bolt4Name = bolt4Name;
59
+ this.retryable = retryable;
60
+ }
61
+ }
62
+ /**
63
+ * Builds a macula-cli argv: subcommand words, then --json and any other
64
+ * flags, then positionals -- in that order, always. Go's `flag` package
65
+ * stops parsing flags at the first non-flag argument, so a flag placed
66
+ * after a positional (a host, a procedure name, ...) is silently treated
67
+ * as an extra positional instead and the command fails its arg-count
68
+ * check. This bit macula-cli's own README/HOWTO guide enough to document
69
+ * as a top-line gotcha -- and bit this file too, appending --json at the
70
+ * very end, until caught running the built MCP server for real against a
71
+ * live macula-cli rather than just typechecking against it. One place to
72
+ * get the ordering right, not one per call site.
73
+ */
74
+ function argv(subcommand, flags, positionals) {
75
+ return [...subcommand, "--json", ...flags, ...positionals];
76
+ }
77
+ /** Runs a macula-cli argv() and parses its JSON envelope. */
78
+ async function run(args) {
79
+ const { stdout, stderr, code } = await execFile(binPath(), args);
80
+ let parsed;
81
+ try {
82
+ parsed = JSON.parse(stdout);
83
+ }
84
+ catch {
85
+ throw new MaculaCliUnavailable(`macula-cli produced no parseable JSON (exit ${code}). stderr: ${stderr.trim() || "(empty)"}. ` +
86
+ `Is macula-cli installed and on PATH? See MACULA_CLI_BIN to point at a specific binary.`);
87
+ }
88
+ if (!parsed.ok) {
89
+ const e = parsed.error;
90
+ throw new MaculaCliError(e?.message ?? "macula-cli reported failure with no message", e?.bolt4_code, e?.bolt4_name, e?.retryable);
91
+ }
92
+ return parsed.data;
93
+ }
94
+ function execFile(bin, args) {
95
+ return new Promise((resolve, reject) => {
96
+ const child = spawn(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
97
+ let stdout = "";
98
+ let stderr = "";
99
+ child.stdout.on("data", (c) => (stdout += c.toString("utf8")));
100
+ child.stderr.on("data", (c) => (stderr += c.toString("utf8")));
101
+ child.on("error", (e) => reject(new MaculaCliUnavailable(`could not run '${bin}': ${e.message}. Install it (curl -fsSL https://raw.githubusercontent.com/macula-io/macula-cli/master/install.sh | bash) or set MACULA_CLI_BIN.`)));
102
+ child.on("close", (code) => resolve({ stdout, stderr, code }));
103
+ });
104
+ }
105
+ export const identity = () => run(argv(["identity"], [], []));
106
+ export const call = (args) => {
107
+ const flags = [];
108
+ if (args.timeoutMs)
109
+ flags.push("--timeout", `${Math.max(1, Math.round(args.timeoutMs / 1000))}s`);
110
+ if (args.callArgs !== undefined)
111
+ flags.push("--args", JSON.stringify(args.callArgs));
112
+ return run(argv(["call"], flags, [args.host ?? defaultStation(), args.procedure]));
113
+ };
114
+ export const publish = (args) => run(argv(["pubsub", "publish"], ["--payload", JSON.stringify(args.fact)], [args.host ?? defaultStation(), args.topic]));
115
+ /**
116
+ * Parses `macula-cli pubsub watch --json`'s stdout: one WatchEvent per
117
+ * line (NDJSON, not a single envelope -- run() assumes exactly one
118
+ * envelope and doesn't fit this shape). A real failure mid-watch (e.g.
119
+ * the connection drops after some events already arrived) prints as a
120
+ * trailing `{"ok":false,...}` envelope line -- checked by the presence
121
+ * of an "ok" key, not by JSON.parse throwing, since a wrong-shape
122
+ * object is still perfectly valid JSON and would otherwise silently
123
+ * end up in the events array instead of being surfaced as a failure.
124
+ * Exported standalone so this parsing can be unit-tested without
125
+ * spawning a real subprocess.
126
+ */
127
+ export function parseWatchOutput(stdout) {
128
+ const events = [];
129
+ for (const line of stdout.split("\n")) {
130
+ const trimmed = line.trim();
131
+ if (!trimmed)
132
+ continue;
133
+ let parsed;
134
+ try {
135
+ parsed = JSON.parse(trimmed);
136
+ }
137
+ catch {
138
+ continue; // not JSON at all; ignore this line
139
+ }
140
+ if (parsed !== null && typeof parsed === "object" && "ok" in parsed) {
141
+ const envelope = parsed;
142
+ if (envelope.ok === false) {
143
+ throw new MaculaCliError(envelope.error?.message ?? "pubsub watch failed");
144
+ }
145
+ continue; // an {"ok":true,...} envelope line isn't expected here, but skip rather than misparse it as an event
146
+ }
147
+ events.push(parsed);
148
+ }
149
+ return events;
150
+ }
151
+ /**
152
+ * Watches a topic for up to durationSeconds and returns whatever
153
+ * arrived. Bounded and synchronous -- the MCP tool call itself blocks
154
+ * for the duration, which is the honest shape of a one-shot CLI with no
155
+ * background process to keep a standing subscription alive between
156
+ * calls.
157
+ */
158
+ export const watch = async (args) => {
159
+ const flags = ["--identity", watchIdentityPath(), "--duration", `${Math.max(1, Math.round(args.durationSeconds))}s`];
160
+ if (args.count)
161
+ flags.push("--count", String(args.count));
162
+ const full = argv(["pubsub", "watch"], flags, [args.host ?? defaultStation(), args.topic]);
163
+ const { stdout, stderr, code } = await execFile(binPath(), full);
164
+ const events = parseWatchOutput(stdout);
165
+ if (events.length === 0 && code !== 0 && stderr.trim()) {
166
+ throw new MaculaCliUnavailable(`macula-cli pubsub watch failed: ${stderr.trim()}`);
167
+ }
168
+ return events;
169
+ };
170
+ export const artifactPut = async (args) => {
171
+ const dir = await mkdtemp(join(tmpdir(), "macula-mcp-put-"));
172
+ const filePath = join(dir, "artifact");
173
+ try {
174
+ await writeFile(filePath, Buffer.from(args.contentBase64, "base64"));
175
+ const result = await run(argv(["content", "put"], [], [args.host ?? defaultStation(), filePath]));
176
+ return { mcid_hex: result.mcid, size_bytes: result.size_bytes };
177
+ }
178
+ finally {
179
+ await rm(dir, { recursive: true, force: true });
180
+ }
181
+ };
182
+ export const artifactGet = async (args) => {
183
+ const result = await run(argv(["content", "get"], [], [args.host ?? defaultStation(), args.mcidHex]));
184
+ if (!result.content_base64) {
185
+ throw new MaculaCliUnavailable("macula-cli content get returned no content_base64 (unexpected)");
186
+ }
187
+ return { content: result.content_base64, size_bytes: result.size_bytes };
188
+ };
189
+ //# sourceMappingURL=macula_cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macula_cli.js","sourceRoot":"","sources":["../src/macula_cli.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,EAAE;AACF,sEAAsE;AACtE,yEAAyE;AACzE,sEAAsE;AACtE,sEAAsE;AACtE,oEAAoE;AACpE,EAAE;AACF,uGAAuG;AACvG,EAAE;AACF,wEAAwE;AACxE,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAClE,8CAA8C;AAE9C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,4EAA4E;AAC5E,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,qCAAqC,CAAC;AAClF,CAAC;AAED,sFAAsF;AACtF,SAAS,OAAO;IACd,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,YAAY,CAAC;AACpD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,iBAAiB;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,qBAAqB,CAAC,CAAC;AACxG,CAAC;AAED,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAClD,MAAM,OAAO,cAAe,SAAQ,KAAK;IAG5B;IACA;IACA;IAJX,YACE,OAAe,EACN,SAAkB,EAClB,SAAkB,EAClB,SAAmB;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,cAAS,GAAT,SAAS,CAAS;QAClB,cAAS,GAAT,SAAS,CAAS;QAClB,cAAS,GAAT,SAAS,CAAU;IAG9B,CAAC;CACF;AAQD;;;;;;;;;;;GAWG;AACH,SAAS,IAAI,CAAC,UAAoB,EAAE,KAAe,EAAE,WAAqB;IACxE,OAAO,CAAC,GAAG,UAAU,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,GAAG,WAAW,CAAC,CAAC;AAC7D,CAAC;AAED,6DAA6D;AAC7D,KAAK,UAAU,GAAG,CAAI,IAAc;IAClC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;IAEjE,IAAI,MAAmB,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAgB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,oBAAoB,CAC5B,+CAA+C,IAAI,cAAc,MAAM,CAAC,IAAI,EAAE,IAAI,SAAS,IAAI;YAC7F,wFAAwF,CAC3F,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;QACvB,MAAM,IAAI,cAAc,CAAC,CAAC,EAAE,OAAO,IAAI,6CAA6C,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IACpI,CAAC;IACD,OAAO,MAAM,CAAC,IAAS,CAAC;AAC1B,CAAC;AAED,SAAS,QAAQ,CACf,GAAW,EACX,IAAc;IAEd,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACtE,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CACtB,MAAM,CACJ,IAAI,oBAAoB,CACtB,kBAAkB,GAAG,MAAM,CAAC,CAAC,OAAO,kIAAkI,CACvK,CACF,CACF,CAAC;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC;AASD,MAAM,CAAC,MAAM,QAAQ,GAAG,GAA4B,EAAE,CAAC,GAAG,CAAiB,IAAI,CAAC,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAQvG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAKpB,EAAuB,EAAE;IACxB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAClG,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACrF,OAAO,GAAG,CAAa,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACjG,CAAC,CAAC;AAOF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAIvB,EAA0B,EAAE,CAC3B,GAAG,CACD,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CACnH,CAAC;AAUJ;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,oCAAoC;QAChD,CAAC;QACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YACpE,MAAM,QAAQ,GAAG,MAAyB,CAAC;YAC3C,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1B,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,IAAI,qBAAqB,CAAC,CAAC;YAC7E,CAAC;YACD,SAAS,CAAC,qGAAqG;QACjH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,MAAoB,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EAAE,IAK3B,EAAyB,EAAE;IAC1B,MAAM,KAAK,GAAG,CAAC,YAAY,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC;IACrH,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE3F,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvD,MAAM,IAAI,oBAAoB,CAAC,mCAAmC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,IAGjC,EAA8B,EAAE;IAC/B,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACvC,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,MAAM,GAAG,CACtB,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,EAAE,QAAQ,CAAC,CAAC,CACxE,CAAC;QACF,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;IAClE,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;AACH,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,IAAwC,EAA8B,EAAE;IACxG,MAAM,MAAM,GAAG,MAAM,GAAG,CAMrB,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,cAAc,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChF,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3B,MAAM,IAAI,oBAAoB,CAAC,gEAAgE,CAAC,CAAC;IACnG,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,cAAc,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;AAC3E,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMeshArtifact(server: McpServer): void;
@@ -0,0 +1,63 @@
1
+ // Tools: mesh_put / mesh_get — content-addressed artifact exchange.
2
+ //
3
+ // The Content Sharing primitive. An agent produces something (a build
4
+ // output, a generated module, a dataset slice), puts it on the mesh, gets
5
+ // back an MCID (Macula Content ID, 34-byte content hash, surfaced as
6
+ // 68 hex chars). Another node's agent fetches it by that hex MCID.
7
+ //
8
+ // mesh_put writes the base64 content to a temp file and runs
9
+ // `macula-cli content put`, deleting the temp file after -- macula-cli's
10
+ // own put command is file-based (composable, scriptable, testable from a
11
+ // real terminal too), while MCP's put tool hands over in-memory base64
12
+ // bytes; the temp file is the bridge between those two shapes. mesh_get
13
+ // needs no such bridge: `macula-cli content get --json` already returns
14
+ // content_base64 directly in its envelope.
15
+ //
16
+ // No accountable fact_id anymore (that was hecate-daemon's ReckonDB audit
17
+ // trail, dropped with the daemon).
18
+ //
19
+ // Caveat (memory: project_inter_station_routing_unshipped): DHT
20
+ // replication is not fully shipped cross-station -- same-station
21
+ // put/get is reliable, cross-station is best-effort.
22
+ import { z } from "zod";
23
+ import { artifactGet, artifactPut, defaultStation } from "./macula_cli.js";
24
+ import { describeCliError, errorContent, jsonContent } from "./reply.js";
25
+ export function registerMeshArtifact(server) {
26
+ server.tool("mesh_put", "Publish a content-addressed artifact to the mesh. Returns its 68-hex-char MCID. " +
27
+ `Fetch it elsewhere with mesh_get. Defaults to ${defaultStation()} if host isn't given.`, {
28
+ content: z.string().describe("Artifact bytes, base64-encoded."),
29
+ host: z
30
+ .string()
31
+ .optional()
32
+ .describe(`Station to connect through, "host[:port]". Defaults to ${defaultStation()}.`),
33
+ }, async ({ content, host }) => {
34
+ try {
35
+ const res = await artifactPut({ host, contentBase64: content });
36
+ return jsonContent({ mcid_hex: res.mcid_hex, size_bytes: res.size_bytes });
37
+ }
38
+ catch (e) {
39
+ return errorContent(describeCliError("mesh_put failed", e));
40
+ }
41
+ });
42
+ server.tool("mesh_get", "Fetch a content-addressed artifact from the mesh by its hex MCID (68 chars, " +
43
+ `as returned by mesh_put). Returns base64 content. Defaults to ${defaultStation()} if host isn't given.`, {
44
+ mcid_hex: z
45
+ .string()
46
+ .length(68)
47
+ .regex(/^[0-9a-fA-F]+$/, "must be hex")
48
+ .describe("MCID returned by mesh_put."),
49
+ host: z
50
+ .string()
51
+ .optional()
52
+ .describe(`Station to connect through, "host[:port]". Defaults to ${defaultStation()}.`),
53
+ }, async ({ mcid_hex, host }) => {
54
+ try {
55
+ const res = await artifactGet({ host, mcidHex: mcid_hex });
56
+ return jsonContent({ content: res.content, size_bytes: res.size_bytes });
57
+ }
58
+ catch (e) {
59
+ return errorContent(describeCliError("mesh_get failed", e));
60
+ }
61
+ });
62
+ }
63
+ //# sourceMappingURL=mesh_artifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_artifact.js","sourceRoot":"","sources":["../src/mesh_artifact.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,sEAAsE;AACtE,0EAA0E;AAC1E,qEAAqE;AACrE,mEAAmE;AACnE,EAAE;AACF,6DAA6D;AAC7D,yEAAyE;AACzE,yEAAyE;AACzE,uEAAuE;AACvE,wEAAwE;AACxE,wEAAwE;AACxE,2CAA2C;AAC3C,EAAE;AACF,0EAA0E;AAC1E,mCAAmC;AACnC,EAAE;AACF,gEAAgE;AAChE,iEAAiE;AACjE,qDAAqD;AAGrD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,IAAI,CACT,UAAU,EACV,kFAAkF;QAChF,iDAAiD,cAAc,EAAE,uBAAuB,EAC1F;QACE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QAC/D,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,cAAc,EAAE,GAAG,CAAC;KAC3F,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;YAChE,OAAO,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QAC7E,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,UAAU,EACV,8EAA8E;QAC5E,iEAAiE,cAAc,EAAE,uBAAuB,EAC1G;QACE,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,MAAM,CAAC,EAAE,CAAC;aACV,KAAK,CAAC,gBAAgB,EAAE,aAAa,CAAC;aACtC,QAAQ,CAAC,4BAA4B,CAAC;QACzC,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,cAAc,EAAE,GAAG,CAAC;KAC3F,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC3D,OAAO,WAAW,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QAC3E,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMeshCall(server: McpServer): void;
@@ -0,0 +1,45 @@
1
+ // Tool: mesh_call — invoke a procedure advertised on the mesh (REQUESTER).
2
+ //
3
+ // The agent's hands. A peer advertises a procedure; the agent calls it
4
+ // over the mesh instead of a local sandbox or a US SaaS runner.
5
+ // macula-cli does the actual macula:call over QUIC as a one-shot
6
+ // subprocess (connect, call, exit) and returns the RESULT payload or a
7
+ // BOLT#4-vocabulary error.
8
+ //
9
+ // Unlike the old daemon-backed version, this needs a target station:
10
+ // macula-cli isn't already connected to one the way a standing daemon
11
+ // was. `host` defaults to MACULA_MESH_STATION (or macula-cli's own
12
+ // well-known demo station) so most callers never need to think about it.
13
+ import { z } from "zod";
14
+ import { call, defaultStation } from "./macula_cli.js";
15
+ import { describeCliError, errorContent, jsonContent } from "./reply.js";
16
+ export function registerMeshCall(server) {
17
+ server.tool("mesh_call", "Invoke a procedure advertised on the mesh (build, test, search, deploy on commons hardware). " +
18
+ "Macula RPC is procedure-addressed: the target station routes to a peer that advertises it. " +
19
+ `Returns the peer's result plus duration_ms. Defaults to ${defaultStation()} if host isn't given.`, {
20
+ procedure: z.string().describe("Procedure URI, e.g. mri:proc:realm:build."),
21
+ args: z
22
+ .record(z.unknown())
23
+ .optional()
24
+ .describe("Structured arguments for the procedure (plain JSON; macula-cli encodes the wire)."),
25
+ timeout_ms: z
26
+ .number()
27
+ .int()
28
+ .positive()
29
+ .optional()
30
+ .describe("Deadline in milliseconds for the connect + call."),
31
+ host: z
32
+ .string()
33
+ .optional()
34
+ .describe(`Station to connect through, "host[:port]". Defaults to ${defaultStation()}.`),
35
+ }, async ({ procedure, args, timeout_ms, host }) => {
36
+ try {
37
+ const res = await call({ host, procedure, callArgs: args, timeoutMs: timeout_ms });
38
+ return jsonContent({ result: res.payload, responded_by: res.responded_by, duration_ms: res.duration_ms });
39
+ }
40
+ catch (e) {
41
+ return errorContent(describeCliError("mesh_call failed", e));
42
+ }
43
+ });
44
+ }
45
+ //# sourceMappingURL=mesh_call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_call.js","sourceRoot":"","sources":["../src/mesh_call.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,uEAAuE;AACvE,gEAAgE;AAChE,iEAAiE;AACjE,uEAAuE;AACvE,2BAA2B;AAC3B,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,mEAAmE;AACnE,yEAAyE;AAGzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,IAAI,CACT,WAAW,EACX,+FAA+F;QAC7F,6FAA6F;QAC7F,2DAA2D,cAAc,EAAE,uBAAuB,EACpG;QACE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;QAC3E,IAAI,EAAE,CAAC;aACJ,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;aACnB,QAAQ,EAAE;aACV,QAAQ,CAAC,mFAAmF,CAAC;QAChG,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,GAAG,EAAE;aACL,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,QAAQ,CAAC,kDAAkD,CAAC;QAC/D,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,cAAc,EAAE,GAAG,CAAC;KAC3F,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;YACnF,OAAO,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5G,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerIdentity(server: McpServer): void;
@@ -0,0 +1,28 @@
1
+ // Resource: who am I on the mesh.
2
+ //
3
+ // An agent SHOULD read this before it acts. macula-cli's identity is a
4
+ // bare Ed25519 node ID -- no realm, no membership state, since macula-cli
5
+ // is a raw wire-protocol client, not a realm-joined daemon (that concept
6
+ // belonged to hecate-daemon, now dropped). Report what's actually true
7
+ // rather than fake the fields the old daemon-backed shape had.
8
+ //
9
+ // mesh://peers was dropped, not reworked: it was already an admitted
10
+ // stub even under the old daemon-backed design ("v1 surfaces an empty
11
+ // list until hecate_mesh:get_peers/0 returns real data"), and
12
+ // macula-go-sdk has no peer-listing API for macula-cli to wrap either.
13
+ // A real stub in both directions isn't worth keeping around.
14
+ import { identity } from "./macula_cli.js";
15
+ export function registerIdentity(server) {
16
+ server.resource("mesh-identity", "mesh://identity", {
17
+ description: "This node's Ed25519 identity (node ID), minted and persisted locally by macula-cli.",
18
+ mimeType: "application/json",
19
+ }, async (uri) => {
20
+ const id = await identity();
21
+ return {
22
+ contents: [
23
+ { uri: uri.href, mimeType: "application/json", text: JSON.stringify(id, null, 2) },
24
+ ],
25
+ };
26
+ });
27
+ }
28
+ //# sourceMappingURL=mesh_identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_identity.js","sourceRoot":"","sources":["../src/mesh_identity.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,yEAAyE;AACzE,uEAAuE;AACvE,+DAA+D;AAC/D,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,8DAA8D;AAC9D,uEAAuE;AACvE,6DAA6D;AAG7D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,QAAQ,CACb,eAAe,EACf,iBAAiB,EACjB;QACE,WAAW,EAAE,qFAAqF;QAClG,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC5B,OAAO;YACL,QAAQ,EAAE;gBACR,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;aACnF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMeshPublish(server: McpServer): void;
@@ -0,0 +1,33 @@
1
+ // Tool: mesh_publish — emit an integration fact to a mesh topic.
2
+ //
3
+ // One-shot: macula-cli connects, publishes, exits. No delivery
4
+ // confirmation beyond the wire send succeeding -- PUBLISH has no ack on
5
+ // this protocol -- and no accountable event/fact_id anymore, since that
6
+ // was hecate-daemon's own ReckonDB-backed audit trail, dropped along with
7
+ // the daemon itself. `seq` is macula-cli's own current-time-millis
8
+ // (see its README) since nothing here persists a real sequence counter
9
+ // between one-shot invocations.
10
+ import { z } from "zod";
11
+ import { defaultStation, publish } from "./macula_cli.js";
12
+ import { describeCliError, errorContent, jsonContent } from "./reply.js";
13
+ export function registerMeshPublish(server) {
14
+ server.tool("mesh_publish", "Publish an integration fact to a mesh topic so other parties' agents can react. " +
15
+ "Use a business verb for the fact type (e.g. 'module_generated', 'capability_announced'), " +
16
+ `never CRUD. Returns the topic and seq macula-cli reported. Defaults to ${defaultStation()} if host isn't given.`, {
17
+ topic: z.string().describe("Topic name (e.g. 'agents.module_generated')."),
18
+ fact: z.record(z.unknown()).describe("The integration fact payload (plain JSON; macula-cli encodes the wire)."),
19
+ host: z
20
+ .string()
21
+ .optional()
22
+ .describe(`Station to connect through, "host[:port]". Defaults to ${defaultStation()}.`),
23
+ }, async ({ topic, fact, host }) => {
24
+ try {
25
+ const res = await publish({ host, topic, fact });
26
+ return jsonContent({ topic: res.topic, seq: res.seq, duration_ms: res.duration_ms });
27
+ }
28
+ catch (e) {
29
+ return errorContent(describeCliError("mesh_publish failed", e));
30
+ }
31
+ });
32
+ }
33
+ //# sourceMappingURL=mesh_publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_publish.js","sourceRoot":"","sources":["../src/mesh_publish.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,+DAA+D;AAC/D,wEAAwE;AACxE,wEAAwE;AACxE,0EAA0E;AAC1E,mEAAmE;AACnE,uEAAuE;AACvE,gCAAgC;AAGhC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,CAAC,IAAI,CACT,cAAc,EACd,kFAAkF;QAChF,2FAA2F;QAC3F,0EAA0E,cAAc,EAAE,uBAAuB,EACnH;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;QAC1E,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,yEAAyE,CAAC;QAC/G,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,cAAc,EAAE,GAAG,CAAC;KAC3F,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACjD,OAAO,WAAW,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACvF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,gBAAgB,CAAC,qBAAqB,EAAE,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMeshWatch(server: McpServer): void;
@@ -0,0 +1,53 @@
1
+ // Tool: mesh_watch — bounded, synchronous replacement for the old
2
+ // mesh_subscribe / mesh_unsubscribe / mesh_subscriptions / mesh_inbox
3
+ // quartet.
4
+ //
5
+ // Those four tools leaned entirely on hecate-daemon's own event-sourced
6
+ // state: a standing subscription that outlived any one HTTP call, and a
7
+ // ReckonDB-backed inbox an agent could poll later. macula-cli has no
8
+ // daemon and no storage -- nothing persists between invocations -- so
9
+ // there is no honest way to preserve "register once, poll the inbox
10
+ // later" without macula-mcp itself becoming a stateful daemon (a real
11
+ // design fork, deliberately not taken here; see macula-io/macula-cli's
12
+ // own project memory for the tradeoff that was weighed).
13
+ //
14
+ // mesh_watch is the shape that IS honest for a one-shot subprocess: the
15
+ // tool call blocks for up to duration_seconds and returns whatever
16
+ // arrived. An agent that wants "keep listening" calls it again.
17
+ import { z } from "zod";
18
+ import { defaultStation, watch } from "./macula_cli.js";
19
+ import { describeCliError, errorContent, jsonContent } from "./reply.js";
20
+ const MAX_DURATION_SECONDS = 120;
21
+ export function registerMeshWatch(server) {
22
+ server.tool("mesh_watch", "Watch a mesh topic for inbound facts for up to duration_seconds, then return whatever " +
23
+ "arrived. This call BLOCKS for the full duration (or until count events arrive, " +
24
+ "whichever is first) -- there is no standing/background subscription to poll later; " +
25
+ `call this again to keep watching. Defaults to ${defaultStation()} if host isn't given.`, {
26
+ topic: z.string().describe("Topic name (e.g. 'chat.demo')."),
27
+ duration_seconds: z
28
+ .number()
29
+ .positive()
30
+ .max(MAX_DURATION_SECONDS)
31
+ .default(10)
32
+ .describe(`How long to watch, in seconds (max ${MAX_DURATION_SECONDS}).`),
33
+ count: z
34
+ .number()
35
+ .int()
36
+ .positive()
37
+ .optional()
38
+ .describe("Stop early once this many events have arrived."),
39
+ host: z
40
+ .string()
41
+ .optional()
42
+ .describe(`Station to connect through, "host[:port]". Defaults to ${defaultStation()}.`),
43
+ }, async ({ topic, duration_seconds, count, host }) => {
44
+ try {
45
+ const events = await watch({ host, topic, durationSeconds: duration_seconds, count });
46
+ return jsonContent({ topic, event_count: events.length, events });
47
+ }
48
+ catch (e) {
49
+ return errorContent(describeCliError("mesh_watch failed", e));
50
+ }
51
+ });
52
+ }
53
+ //# sourceMappingURL=mesh_watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_watch.js","sourceRoot":"","sources":["../src/mesh_watch.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,sEAAsE;AACtE,WAAW;AACX,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,qEAAqE;AACrE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,uEAAuE;AACvE,yDAAyD;AACzD,EAAE;AACF,wEAAwE;AACxE,mEAAmE;AACnE,gEAAgE;AAGhE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,wFAAwF;QACtF,iFAAiF;QACjF,qFAAqF;QACrF,iDAAiD,cAAc,EAAE,uBAAuB,EAC1F;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QAC5D,gBAAgB,EAAE,CAAC;aAChB,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,GAAG,CAAC,oBAAoB,CAAC;aACzB,OAAO,CAAC,EAAE,CAAC;aACX,QAAQ,CAAC,sCAAsC,oBAAoB,IAAI,CAAC;QAC3E,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,EAAE;aACL,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,QAAQ,CAAC,gDAAgD,CAAC;QAC7D,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,0DAA0D,cAAc,EAAE,GAAG,CAAC;KAC3F,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE;QACjD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC;YACtF,OAAO,WAAW,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ type ToolReply = {
2
+ content: {
3
+ type: "text";
4
+ text: string;
5
+ }[];
6
+ isError?: boolean;
7
+ };
8
+ export declare function jsonContent(value: unknown): ToolReply;
9
+ export declare function errorContent(message: string): ToolReply;
10
+ /**
11
+ * Formats an error caught from a macula_cli.ts call, surfacing the
12
+ * BOLT#4 code/name/retryable when present rather than a bare message —
13
+ * every tool that shells out to macula-cli wants this same shape.
14
+ */
15
+ export declare function describeCliError(prefix: string, e: unknown): string;
16
+ export {};
package/dist/reply.js ADDED
@@ -0,0 +1,24 @@
1
+ // Small helpers for shaping MCP tool replies. Not a "utils" junk drawer —
2
+ // just the reply shapes every tool returns.
3
+ import { MaculaCliError } from "./macula_cli.js";
4
+ export function jsonContent(value) {
5
+ return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
6
+ }
7
+ export function errorContent(message) {
8
+ return { content: [{ type: "text", text: message }], isError: true };
9
+ }
10
+ /**
11
+ * Formats an error caught from a macula_cli.ts call, surfacing the
12
+ * BOLT#4 code/name/retryable when present rather than a bare message —
13
+ * every tool that shells out to macula-cli wants this same shape.
14
+ */
15
+ export function describeCliError(prefix, e) {
16
+ if (e instanceof MaculaCliError) {
17
+ const bolt4 = e.bolt4Name
18
+ ? ` (bolt4=${e.bolt4Name}${e.retryable !== undefined ? `, retryable=${e.retryable}` : ""})`
19
+ : "";
20
+ return `${prefix}: ${e.message}${bolt4}`;
21
+ }
22
+ return `${prefix}: ${e instanceof Error ? e.message : String(e)}`;
23
+ }
24
+ //# sourceMappingURL=reply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply.js","sourceRoot":"","sources":["../src/reply.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,4CAA4C;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,CAAU;IACzD,IAAI,CAAC,YAAY,cAAc,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,CAAC,CAAC,SAAS;YACvB,CAAC,CAAC,WAAW,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG;YAC3F,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,GAAG,MAAM,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,GAAG,MAAM,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AACpE,CAAC"}