@aliceshimada/mica 1.0.5 → 1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0 - 2026-06-09
4
+
5
+ - Add `mma_kill_kernel` tool: quit a notebook's Wolfram kernel (control agent kernel is protected).
6
+ - Add `mma_restart_kernel` tool: restart a notebook's Wolfram kernel so it can evaluate cells again.
7
+
3
8
  ## 1.0.5 - 2026-06-09
4
9
 
5
10
  - Fix abort disconnection: give control evaluator a separate kernel via `LinkLaunch` instead of cloning `"Local"`.
@@ -3,7 +3,7 @@ import http from "node:http";
3
3
  import { executeBackendMcpTool } from "../mcp/backendTools.js";
4
4
  import { renderDashboard } from "./dashboard.js";
5
5
  const JSON_BODY_LIMIT_BYTES = 1024 * 1024;
6
- const DEFAULT_VERSION = "1.0.5";
6
+ const DEFAULT_VERSION = "1.1.0";
7
7
  export async function createBunHttpApp({ state, host = "127.0.0.1", port, authToken, version = DEFAULT_VERSION }) {
8
8
  const runtimeInfo = {
9
9
  host,
@@ -8,7 +8,7 @@ import { loadRuntimeConfig } from "../runtime/config.js";
8
8
  import { writeSessionFile } from "../runtime/session.js";
9
9
  import { createBunHttpApp } from "./httpServer.js";
10
10
  const MCP_SERVER_NAME = "mica-bun";
11
- const MICA_PACKAGE_VERSION = "1.0.5";
11
+ const MICA_PACKAGE_VERSION = "1.1.0";
12
12
  export async function startBunRuntime(deps = {}) {
13
13
  const config = deps.runtimeConfig ?? loadRuntimeConfig();
14
14
  const bridgeOnly = deps.bridgeOnly ?? config.bridgeOnly;
@@ -83,7 +83,7 @@ export async function runDoctor(deps = {}) {
83
83
  let sessionBaseUrl;
84
84
  if (!_exists(sessionFile)) {
85
85
  fail("Session file", `${sessionFile} (not found)`);
86
- fix("Run: mica start");
86
+ fix("Run: mica mcp");
87
87
  }
88
88
  else {
89
89
  try {
@@ -97,7 +97,7 @@ export async function runDoctor(deps = {}) {
97
97
  }
98
98
  catch (e) {
99
99
  fail("Session file", `${sessionFile} (${e instanceof Error ? e.message : String(e)})`);
100
- fix("Run: mica start");
100
+ fix("Run: mica mcp");
101
101
  }
102
102
  }
103
103
  // -----------------------------------------------------------------------
@@ -173,7 +173,7 @@ export async function runDoctor(deps = {}) {
173
173
  catch (e) {
174
174
  fail("Auth token", "server not reachable");
175
175
  fail("Server /status reachable", e instanceof Error ? e.message : String(e));
176
- fix("Run: mica start");
176
+ fix("Run: mica mcp");
177
177
  fail("Live agent count", "server not reachable");
178
178
  fail("Live notebook count", "server not reachable");
179
179
  }
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -222,10 +222,14 @@ async function main() {
222
222
  });
223
223
  process.exitCode = exitCode;
224
224
  }
225
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
226
- main().catch((error) => {
227
- const message = error instanceof Error ? error.stack ?? error.message : String(error);
228
- process.stderr.write(`${message}\n`);
229
- process.exitCode = 1;
230
- });
225
+ if (process.argv[1]) {
226
+ const scriptReal = realpathSync(fileURLToPath(import.meta.url));
227
+ const argReal = realpathSync(process.argv[1]);
228
+ if (scriptReal === argReal) {
229
+ main().catch((error) => {
230
+ const message = error instanceof Error ? error.stack ?? error.message : String(error);
231
+ process.stderr.write(`${message}\n`);
232
+ process.exitCode = 1;
233
+ });
234
+ }
231
235
  }
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { DEFAULT_TIMEOUTS_MS } from "../backend/protocol.js";
3
- import { abortEvaluationSchema, deleteCellSchema, getCellOutputSchema, insertCellSchema, listCellsSchema, modifyCellSchema, noArgsSchema, readArtifactSchema, readCellSchema, runCellSchema, selectNotebookSchema, saveNotebookSchema, symbolLookupSchema, } from "./toolSchemas.js";
3
+ import { abortEvaluationSchema, deleteCellSchema, getCellOutputSchema, insertCellSchema, killKernelSchema, listCellsSchema, modifyCellSchema, noArgsSchema, readArtifactSchema, readCellSchema, restartKernelSchema, runCellSchema, selectNotebookSchema, saveNotebookSchema, symbolLookupSchema, } from "./toolSchemas.js";
4
4
  import { INSERT_ANCHOR_GUIDANCE, notebookToolDescription } from "./descriptions.js";
5
5
  import { toolFailure, toolSuccess, withToolErrors } from "./toolResults.js";
6
6
  function assertLiveAgent(state) {
@@ -141,6 +141,22 @@ const queuedNotebookTools = [
141
141
  timeoutMs: () => DEFAULT_TIMEOUTS_MS.mutation,
142
142
  requiresExplicitTarget: true,
143
143
  },
144
+ {
145
+ name: "mma_kill_kernel",
146
+ summary: "Quit the Wolfram kernel for a notebook. The control agent kernel is protected and cannot be killed.",
147
+ schema: killKernelSchema.shape,
148
+ permission: "RunCell",
149
+ timeoutMs: () => DEFAULT_TIMEOUTS_MS.mutation,
150
+ requiresExplicitTarget: true,
151
+ },
152
+ {
153
+ name: "mma_restart_kernel",
154
+ summary: "Restart the Wolfram kernel for a notebook so it can evaluate cells again.",
155
+ schema: restartKernelSchema.shape,
156
+ permission: "RunCell",
157
+ timeoutMs: () => DEFAULT_TIMEOUTS_MS.mutation,
158
+ requiresExplicitTarget: true,
159
+ },
144
160
  {
145
161
  name: "mma_get_cell_output",
146
162
  summary: "Read output and messages for one Mathematica notebook cell, refreshing completed run status when observed.",
@@ -12,6 +12,8 @@ const TOOL_GUIDE = [
12
12
  ["mma_delete_cell", "Delete an existing cell."],
13
13
  ["mma_run_cell", "Evaluate one cell and wait for completion or timeout."],
14
14
  ["mma_abort_evaluation", "Abort a running notebook evaluation."],
15
+ ["mma_kill_kernel", "Quit the Wolfram kernel for a notebook (control agent kernel is protected)."],
16
+ ["mma_restart_kernel", "Restart the Wolfram kernel for a notebook so it can evaluate cells again."],
15
17
  ["mma_get_cell_output", "Read output and messages produced by one cell; this may refresh completed run status."],
16
18
  ["mma_read_artifact", "Read large output or message artifacts by byte page; ids may become stale after notebook edits or reruns."],
17
19
  ["mma_save_notebook", "Save the selected notebook when SaveNotebook permission is granted."],
@@ -33,7 +35,7 @@ export const MICA_AGENT_INSTRUCTIONS = [
33
35
  "Tools:",
34
36
  ...TOOL_GUIDE.map(([name, description]) => `- ${name}: ${description}`),
35
37
  ].join("\n");
36
- export function createMicaMcpServer(name, version = "1.0.5") {
38
+ export function createMicaMcpServer(name, version = "1.1.0") {
37
39
  return new McpServer({ name, version }, { instructions: MICA_AGENT_INSTRUCTIONS });
38
40
  }
39
41
  export function registerMicaPrompts(server) {
@@ -38,6 +38,12 @@ export const runCellSchema = z.object({
38
38
  export const abortEvaluationSchema = z.object({
39
39
  ...notebookSelectorFields
40
40
  }).strict();
41
+ export const killKernelSchema = z.object({
42
+ ...notebookSelectorFields
43
+ }).strict();
44
+ export const restartKernelSchema = z.object({
45
+ ...notebookSelectorFields
46
+ }).strict();
41
47
  export const getCellOutputSchema = z.object({
42
48
  ...notebookSelectorFields,
43
49
  cellId: z.string().min(1),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliceshimada/mica",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "Local MCP bridge for controlling live Wolfram Desktop / Mathematica notebooks.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1407,9 +1407,39 @@ AbortEvaluationRequest[args_Association] := Module[{notebookId, record, notebook
1407
1407
  <|"status" -> "abort_requested", "cellId" -> runningCellId, "requestId" -> runningRequestId|>,
1408
1408
  <|"status" -> "idle"|>
1409
1409
  ]
1410
- ];
1410
+ ];
1411
+
1412
+ KillKernelRequest[args_Association] := Module[{notebookId, record, notebook, evaluatorName},
1413
+ notebookId = TargetNotebookId[args];
1414
+ If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
1415
+ record = NotebookRecord[notebookId];
1416
+ If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
1417
+ notebook = Lookup[record, "notebook", None];
1418
+ If[Head[notebook] =!= NotebookObject, Return[Failure["BAD_REQUEST", <|"message" -> "Notebook is unavailable."|>]]];
1419
+ evaluatorName = Quiet @ Check[CurrentValue[notebook, Evaluator], $ControlAgentEvaluatorName];
1420
+ If[evaluatorName === $ControlAgentEvaluatorName,
1421
+ Return[Failure["PROTECTED_EVALUATOR", <|"message" -> "Cannot kill the MICA control agent evaluator."|>]]
1422
+ ];
1423
+ Quiet @ Check[NotebookEvaluate[notebook, "Quit[]", InsertResults -> False], Null];
1424
+ <|"status" -> "killed", "notebookId" -> notebookId|>
1425
+ ];
1426
+
1427
+ RestartKernelRequest[args_Association] := Module[{notebookId, record, notebook, evaluatorName},
1428
+ notebookId = TargetNotebookId[args];
1429
+ If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
1430
+ record = NotebookRecord[notebookId];
1431
+ If[!AssociationQ[record] || record === <||>, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
1432
+ notebook = Lookup[record, "notebook", None];
1433
+ If[Head[notebook] =!= NotebookObject, Return[Failure["BAD_REQUEST", <|"message" -> "Notebook is unavailable."|>]]];
1434
+ evaluatorName = Quiet @ Check[CurrentValue[notebook, Evaluator], $ControlAgentEvaluatorName];
1435
+ If[evaluatorName === $ControlAgentEvaluatorName,
1436
+ Return[Failure["PROTECTED_EVALUATOR", <|"message" -> "Cannot restart the MICA control agent evaluator."|>]]
1437
+ ];
1438
+ Quiet @ Check[NotebookEvaluate[notebook, "Null", InsertResults -> False], Null];
1439
+ <|"status" -> "restarted", "notebookId" -> notebookId|>
1440
+ ];
1411
1441
 
1412
- SaveNotebookRequest[args_Association] := Module[{notebookId, record, notebook},
1442
+ SaveNotebookRequest[args_Association] := Module[{notebookId, record, notebook},
1413
1443
  If[Not @ ConfirmAction["SaveNotebook", "AI requests saving the notebook. Allow?"], Return[$Canceled]];
1414
1444
  notebookId = TargetNotebookId[args];
1415
1445
  If[!StringQ[notebookId] || StringLength[notebookId] == 0, Return[Failure["BAD_REQUEST", <|"message" -> "No notebook is selected."|>]]];
@@ -1777,7 +1807,9 @@ ExecuteRequest[request_Association] := Module[{requestId, tool, args, result},
1777
1807
  "mma_modify_cell", ModifyCellRequest[args],
1778
1808
  "mma_delete_cell", DeleteCellRequest[args],
1779
1809
  "mma_run_cell", RunCellRequest[args],
1780
- "mma_abort_evaluation", AbortEvaluationRequest[args],
1810
+ "mma_abort_evaluation", AbortEvaluationRequest[args],
1811
+ "mma_kill_kernel", KillKernelRequest[args],
1812
+ "mma_restart_kernel", RestartKernelRequest[args],
1781
1813
  "mma_get_cell_output", GetCellOutputById[args],
1782
1814
  "mma_read_artifact", ReadArtifactById[args],
1783
1815
  "mma_save_notebook", SaveNotebookRequest[args],