@noelclaw/mcp 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MIROSHARK_TOOLS = void 0;
4
4
  exports.handleMirosharkTool = handleMirosharkTool;
5
- const convex_js_1 = require("../convex.js");
5
+ const CONVEX_SITE = process.env.NOELCLAW_CONVEX_URL ?? "https://api.noelclaw.com";
6
6
  exports.MIROSHARK_TOOLS = [
7
7
  {
8
8
  name: "miroshark_simulate",
9
- description: "Run a MiroShark multi-agent simulation. Describe a scenario in plain English and get back strategic insights from a network of AI agents (market actors, risk managers, analysts). " +
10
- "Returns a simulation ID you can poll with miroshark_status.",
9
+ description: "Run a MiroShark multi-agent simulation. Describe any scenario in plain English — market crashes, policy changes, social events — and get back a running simulation with AI agents acting as market participants, analysts, and social actors. " +
10
+ "Handles the full setup automatically (knowledge graph, agent profiles). Returns a simulation_id to poll with miroshark_status.",
11
11
  inputSchema: {
12
12
  type: "object",
13
13
  properties: {
@@ -15,21 +15,13 @@ exports.MIROSHARK_TOOLS = [
15
15
  type: "string",
16
16
  description: "Plain-English description of the scenario to simulate. E.g. 'What happens if ETH drops 20% and whale wallets start selling?'",
17
17
  },
18
- agents: {
19
- type: "number",
20
- description: "Number of agents in the simulation (default: 10, max: 50)",
21
- },
22
- steps: {
23
- type: "number",
24
- description: "Number of simulation steps to run (default: 5)",
25
- },
26
18
  },
27
19
  required: ["scenario"],
28
20
  },
29
21
  },
30
22
  {
31
23
  name: "miroshark_status",
32
- description: "Poll the status and results of a MiroShark simulation by ID. Returns agent outputs, consensus findings, and final strategic insights when complete.",
24
+ description: "Poll the status of a MiroShark simulation. Returns preparation progress, running progress, or final results. Automatically starts the simulation when agent preparation completes.",
33
25
  inputSchema: {
34
26
  type: "object",
35
27
  properties: {
@@ -42,37 +34,121 @@ exports.MIROSHARK_TOOLS = [
42
34
  },
43
35
  },
44
36
  ];
37
+ // ── HTTP helpers ──────────────────────────────────────────────────────────────
38
+ function authHeaders() {
39
+ const key = process.env.NOELCLAW_API_KEY ?? process.env.NOELCLAW_SESSION_TOKEN;
40
+ return key ? { Authorization: `Bearer ${key}` } : {};
41
+ }
42
+ async function miroJson(path, method, body, timeoutMs = 90000) {
43
+ const res = await fetch(`${CONVEX_SITE}${path}`, {
44
+ method,
45
+ headers: { ...authHeaders(), "Content-Type": "application/json" },
46
+ body: body !== undefined ? JSON.stringify(body) : undefined,
47
+ signal: AbortSignal.timeout(timeoutMs),
48
+ });
49
+ const text = await res.text();
50
+ if (!res.ok)
51
+ throw new Error(`MiroShark ${method} ${path} [${res.status}]: ${text.slice(0, 300)}`);
52
+ let json;
53
+ try {
54
+ json = JSON.parse(text);
55
+ }
56
+ catch {
57
+ throw new Error(`MiroShark non-JSON response: ${text.slice(0, 200)}`);
58
+ }
59
+ if (json.success === false)
60
+ throw new Error(`MiroShark error: ${json.error ?? JSON.stringify(json).slice(0, 300)}`);
61
+ return json.data ?? json;
62
+ }
63
+ async function miroForm(path, form, timeoutMs = 120000) {
64
+ // No Content-Type header — browser/fetch sets multipart boundary automatically
65
+ const res = await fetch(`${CONVEX_SITE}${path}`, {
66
+ method: "POST",
67
+ headers: authHeaders(),
68
+ body: form,
69
+ signal: AbortSignal.timeout(timeoutMs),
70
+ });
71
+ const text = await res.text();
72
+ if (!res.ok)
73
+ throw new Error(`MiroShark POST ${path} [${res.status}]: ${text.slice(0, 300)}`);
74
+ let json;
75
+ try {
76
+ json = JSON.parse(text);
77
+ }
78
+ catch {
79
+ throw new Error(`MiroShark non-JSON response: ${text.slice(0, 200)}`);
80
+ }
81
+ if (json.success === false)
82
+ throw new Error(`MiroShark error: ${json.error ?? JSON.stringify(json).slice(0, 300)}`);
83
+ return json.data ?? json;
84
+ }
85
+ async function pollUntilDone(taskPath, pollIntervalMs = 8000, maxWaitMs = 180000) {
86
+ const deadline = Date.now() + maxWaitMs;
87
+ while (Date.now() < deadline) {
88
+ await new Promise(r => setTimeout(r, pollIntervalMs));
89
+ const task = await miroJson(taskPath, "GET");
90
+ const s = (task.status ?? "").toLowerCase();
91
+ if (s === "completed" || s === "success")
92
+ return task;
93
+ if (s === "failed" || s === "error") {
94
+ throw new Error(`Task failed: ${task.error ?? task.message ?? s}`);
95
+ }
96
+ }
97
+ throw new Error("Task timed out after 3 minutes");
98
+ }
99
+ // ── Tool handler ──────────────────────────────────────────────────────────────
45
100
  async function handleMirosharkTool(name, args) {
46
101
  const a = (args ?? {});
102
+ // ── miroshark_simulate ────────────────────────────────────────────────────
47
103
  if (name === "miroshark_simulate") {
48
104
  if (!a.scenario?.trim()) {
49
105
  return { content: [{ type: "text", text: "scenario is required" }], isError: true };
50
106
  }
51
107
  try {
52
- // Step 1: parse scenario into simulation params
53
- const asked = await (0, convex_js_1.callConvex)("/miroshark/api/simulation/ask", "POST", { question: a.scenario });
54
- // Step 2: create simulation
55
- const created = await (0, convex_js_1.callConvex)("/miroshark/api/simulation/create", "POST", {
56
- ...asked,
57
- num_agents: Math.min(a.agents ?? 10, 50),
58
- num_steps: a.steps ?? 5,
59
- });
108
+ // Step 1: convert plain-English question into a structured seed document
109
+ const asked = await miroJson("/miroshark/api/simulation/ask", "POST", { question: a.scenario });
110
+ const { title, seed_document, simulation_requirement } = asked;
111
+ // Step 2: generate knowledge-graph ontology from the seed document
112
+ const form = new FormData();
113
+ form.append("simulation_requirement", simulation_requirement ?? a.scenario);
114
+ form.append("project_name", (title ?? a.scenario).slice(0, 100));
115
+ form.append("url_docs", JSON.stringify([{
116
+ title: title ?? "Simulation Context",
117
+ url: "",
118
+ text: seed_document ?? a.scenario,
119
+ }]));
120
+ const ontology = await miroForm("/miroshark/api/graph/ontology/generate", form);
121
+ const projectId = ontology.project_id;
122
+ if (!projectId)
123
+ throw new Error("No project_id in ontology response");
124
+ // Step 3: kick off the async graph build
125
+ const built = await miroJson("/miroshark/api/graph/build", "POST", { project_id: projectId });
126
+ const graphTaskId = built.task_id;
127
+ if (!graphTaskId)
128
+ throw new Error("No task_id in graph build response");
129
+ // Step 4: wait for graph to finish (up to 3 min)
130
+ await pollUntilDone(`/miroshark/api/graph/task/${graphTaskId}`);
131
+ // Step 5: create simulation from the built graph
132
+ const created = await miroJson("/miroshark/api/simulation/create", "POST", { project_id: projectId });
60
133
  const simId = created.simulation_id ?? created.id;
61
134
  if (!simId)
62
- throw new Error("No simulation ID in create response");
63
- // Step 3: prepare + start
64
- await (0, convex_js_1.callConvex)(`/miroshark/api/simulation/${simId}/prepare`, "POST", {});
65
- await (0, convex_js_1.callConvex)(`/miroshark/api/simulation/${simId}/start`, "POST", {});
135
+ throw new Error("No simulation_id in create response");
136
+ // Step 6: kick off agent preparation (async — don't block)
137
+ const prepared = await miroJson("/miroshark/api/simulation/prepare", "POST", { simulation_id: simId });
138
+ const prepTaskId = prepared.task_id;
66
139
  return {
67
140
  content: [{
68
141
  type: "text",
69
142
  text: [
70
- `**MiroShark simulation started**`,
143
+ `**MiroShark simulation queued** ✓`,
144
+ ``,
71
145
  `Scenario: ${a.scenario}`,
146
+ `Project: \`${projectId}\``,
72
147
  `Simulation ID: \`${simId}\``,
73
- `Agents: ${a.agents ?? 10} · Steps: ${a.steps ?? 5}`,
148
+ `Status: preparing agents${prepTaskId ? ` (task: ${prepTaskId})` : ""}`,
74
149
  ``,
75
- `Poll results with: \`miroshark_status simulation_id="${simId}"\``,
150
+ `Agent preparation runs in the background. Poll progress with:`,
151
+ `\`miroshark_status simulation_id="${simId}"\``,
76
152
  ].join("\n"),
77
153
  }],
78
154
  };
@@ -81,38 +157,106 @@ async function handleMirosharkTool(name, args) {
81
157
  return { content: [{ type: "text", text: `MiroShark error: ${err.message}` }], isError: true };
82
158
  }
83
159
  }
160
+ // ── miroshark_status ──────────────────────────────────────────────────────
84
161
  if (name === "miroshark_status") {
85
162
  if (!a.simulation_id?.trim()) {
86
163
  return { content: [{ type: "text", text: "simulation_id is required" }], isError: true };
87
164
  }
165
+ const simId = a.simulation_id.trim();
88
166
  try {
89
- const data = await (0, convex_js_1.callConvex)(`/miroshark/api/simulation/${a.simulation_id}/status`, "GET");
90
- const status = data.status ?? "unknown";
91
- const lines = [
92
- `**MiroShark Simulation \`${a.simulation_id}\`**`,
93
- `Status: **${status}**`,
94
- ];
95
- if (data.progress != null)
96
- lines.push(`Progress: ${data.progress}%`);
97
- if (status === "completed" && data.results) {
98
- lines.push("", "**Results**");
99
- if (data.results.summary)
100
- lines.push(data.results.summary);
101
- if (Array.isArray(data.results.insights)) {
102
- for (const insight of data.results.insights.slice(0, 5)) {
103
- lines.push(`• ${typeof insight === "string" ? insight : JSON.stringify(insight)}`);
104
- }
167
+ // Check run status first
168
+ const runStatus = await miroJson(`/miroshark/api/simulation/${simId}/run-status`, "GET").catch(() => ({ runner_status: "idle" }));
169
+ const runnerStatus = (runStatus?.runner_status ?? "idle").toLowerCase();
170
+ // If not yet running, check whether agents are prepared by probing /config
171
+ // (config only exists after /prepare completes)
172
+ if (runnerStatus === "idle") {
173
+ const config = await miroJson(`/miroshark/api/simulation/${simId}/config`, "GET").catch(() => null);
174
+ if (!config) {
175
+ // Preparation still in progress check profiles for real-time progress
176
+ const profiles = await miroJson(`/miroshark/api/simulation/${simId}/profiles/realtime`, "GET").catch(() => null);
177
+ const total = profiles?.total_expected ?? "?";
178
+ const ready = profiles?.profiles_ready ?? 0;
179
+ return {
180
+ content: [{
181
+ type: "text",
182
+ text: [
183
+ `**MiroShark \`${simId}\`** — preparing agents`,
184
+ total !== "?" ? `Profiles: ${ready} / ${total} ready` : `Profiles generating...`,
185
+ ``,
186
+ `Poll again in ~10 seconds.`,
187
+ ].join("\n"),
188
+ }],
189
+ };
105
190
  }
106
- if (data.results.consensus)
107
- lines.push("", `**Consensus:** ${data.results.consensus}`);
191
+ // Config exists → agents prepared → auto-start
192
+ await miroJson("/miroshark/api/simulation/start", "POST", {
193
+ simulation_id: simId,
194
+ platform: "parallel",
195
+ });
196
+ return {
197
+ content: [{
198
+ type: "text",
199
+ text: [
200
+ `**MiroShark \`${simId}\`** — simulation started`,
201
+ ``,
202
+ `Agents are now active. Poll again in ~15 seconds for progress.`,
203
+ `\`miroshark_status simulation_id="${simId}"\``,
204
+ ].join("\n"),
205
+ }],
206
+ };
108
207
  }
109
- else if (status === "failed") {
110
- lines.push("", `Error: ${data.error ?? "unknown"}`);
208
+ if (runnerStatus === "running") {
209
+ const round = runStatus.current_round ?? 0;
210
+ const total = runStatus.total_rounds ?? "?";
211
+ const pct = runStatus.progress_percent?.toFixed(1) ?? "0";
212
+ const twitterActs = runStatus.twitter_actions_count ?? 0;
213
+ const redditActs = runStatus.reddit_actions_count ?? 0;
214
+ return {
215
+ content: [{
216
+ type: "text",
217
+ text: [
218
+ `**MiroShark \`${simId}\`** — running`,
219
+ `Round: ${round} / ${total} (${pct}%)`,
220
+ `Actions: ${twitterActs} Twitter · ${redditActs} Reddit`,
221
+ ``,
222
+ `Simulation in progress — poll again in ~15 seconds.`,
223
+ ].join("\n"),
224
+ }],
225
+ };
111
226
  }
112
- else {
113
- lines.push("", "Simulation still running poll again in a few seconds.");
227
+ if (runnerStatus === "completed" || runnerStatus === "stopped") {
228
+ // Fetch a sample of agent actions for the summary
229
+ const actionsData = await miroJson(`/miroshark/api/simulation/${simId}/actions?limit=10`, "GET").catch(() => ({ actions: [] }));
230
+ const actions = actionsData?.actions ?? [];
231
+ const lines = [
232
+ `**MiroShark \`${simId}\`** — ${runnerStatus}`,
233
+ ``,
234
+ `Rounds completed: ${runStatus.current_round ?? "?"}`,
235
+ `Total actions: ${runStatus.total_actions_count ?? actions.length}`,
236
+ ];
237
+ if (actions.length > 0) {
238
+ lines.push("", "**Sample agent activity:**");
239
+ for (const act of actions.slice(0, 8)) {
240
+ const who = act.agent_name ?? act.agent_id ?? "agent";
241
+ const what = act.action_type ?? act.type ?? "action";
242
+ const content = act.content ?? act.text ?? "";
243
+ lines.push(`• **${who}** [${what}]${content ? `: ${String(content).slice(0, 80)}` : ""}`);
244
+ }
245
+ }
246
+ lines.push("", `Full transcript: \`miroshark_status\` returns results above.`);
247
+ return { content: [{ type: "text", text: lines.join("\n") }] };
114
248
  }
115
- return { content: [{ type: "text", text: lines.join("\n") }] };
249
+ // Fallback: unknown state
250
+ return {
251
+ content: [{
252
+ type: "text",
253
+ text: [
254
+ `**MiroShark \`${simId}\`** — status: ${runnerStatus || "unknown"}`,
255
+ ``,
256
+ `If agents are still preparing, poll again shortly.`,
257
+ ].filter(Boolean).join("\n"),
258
+ }],
259
+ };
116
260
  }
117
261
  catch (err) {
118
262
  return { content: [{ type: "text", text: `MiroShark error: ${err.message}` }], isError: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noelclaw/mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Noelclaw as an MCP skill — persistent memory, multi-agent coordination, scenario simulation, DeFi execution, and Sentinel-gated playbooks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {