@plutojl/mcp 0.1.0 → 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 (2) hide show
  1. package/dist/cli.cjs +249 -6
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -39340,7 +39340,9 @@ Usage:
39340
39340
 
39341
39341
  Commands:
39342
39342
  run Start the MCP server (and Pluto server if needed)
39343
- install Add MCP config to .claude/ or .vscode/ directory
39343
+ install Add MCP config to .mcp.json or mcp.json
39344
+ tools List available MCP tools on a running server
39345
+ call Call an MCP tool on a running server
39344
39346
 
39345
39347
  Run options:
39346
39348
  --mcp-port <port> MCP server port (default: ${DEFAULTS.mcpPort})
@@ -39355,13 +39357,26 @@ Install options:
39355
39357
  --dry-run Print config without writing
39356
39358
  --force Overwrite existing config without prompting
39357
39359
 
39360
+ Call options:
39361
+ npx @plutojl/mcp call <tool_name> [json_args]
39362
+ --mcp-port <port> MCP server port (default: ${DEFAULTS.mcpPort})
39363
+ --raw Output raw JSON response
39364
+
39365
+ Tools options:
39366
+ --mcp-port <port> MCP server port (default: ${DEFAULTS.mcpPort})
39367
+
39358
39368
  Examples:
39359
39369
  npx @plutojl/mcp run
39360
39370
  npx @plutojl/mcp run --pluto-url http://localhost:1234
39361
39371
  npx @plutojl/mcp install
39362
39372
  npx @plutojl/mcp install --target all --global
39373
+ npx @plutojl/mcp tools
39374
+ npx @plutojl/mcp call get_notebook_status
39375
+ npx @plutojl/mcp call start_pluto_server '{"port": 1234}'
39376
+ npx @plutojl/mcp call open_notebook '{"path": "/tmp/nb.pluto.jl"}'
39363
39377
  `);
39364
39378
  }
39379
+ var COMMANDS = /* @__PURE__ */ new Set(["run", "install", "call", "tools", "help"]);
39365
39380
  function parseArgs(argv) {
39366
39381
  const args = { command: "help" };
39367
39382
  let i = 0;
@@ -39374,7 +39389,7 @@ function parseArgs(argv) {
39374
39389
  }
39375
39390
  if (i < argv.length) {
39376
39391
  const cmd = argv[i];
39377
- if (cmd === "run" || cmd === "install" || cmd === "help") {
39392
+ if (COMMANDS.has(cmd)) {
39378
39393
  args.command = cmd;
39379
39394
  } else {
39380
39395
  console.error(`Unknown command: ${cmd}`);
@@ -39383,6 +39398,16 @@ function parseArgs(argv) {
39383
39398
  }
39384
39399
  i++;
39385
39400
  }
39401
+ if (args.command === "call") {
39402
+ while (i < argv.length && !argv[i].startsWith("-")) {
39403
+ if (!args.toolName) {
39404
+ args.toolName = argv[i];
39405
+ } else if (!args.toolArgs) {
39406
+ args.toolArgs = argv[i];
39407
+ }
39408
+ i++;
39409
+ }
39410
+ }
39386
39411
  while (i < argv.length) {
39387
39412
  const flag = argv[i];
39388
39413
  if (flag === "--help" || flag === "-h") {
@@ -39413,6 +39438,8 @@ function parseArgs(argv) {
39413
39438
  args.dryRun = true;
39414
39439
  } else if (flag === "--force") {
39415
39440
  args.force = true;
39441
+ } else if (flag === "--raw") {
39442
+ args.raw = true;
39416
39443
  } else {
39417
39444
  console.warn(`Unknown flag: ${flag}`);
39418
39445
  }
@@ -63662,10 +63689,8 @@ async function run(config) {
63662
63689
  console.log(`[cli] Health check: http://localhost:${config.mcpPort}/health`);
63663
63690
  console.log(`[cli] Press Ctrl+C to stop
63664
63691
  `);
63665
- console.log(
63666
- `Tip: Run 'npx @plutojl/mcp install' to configure Claude Code
63667
- `
63668
- );
63692
+ console.log(`Tip: Run 'npx @plutojl/mcp install' to configure Claude Code
63693
+ `);
63669
63694
  const shutdown = async () => {
63670
63695
  console.log("\n[cli] Shutting down...");
63671
63696
  try {
@@ -63759,9 +63784,208 @@ async function installMcpConfig(args) {
63759
63784
  }
63760
63785
  }
63761
63786
 
63787
+ // ../../src/cli/call.ts
63788
+ async function mcpRequest(port, method, params = {}) {
63789
+ const sseUrl = `http://localhost:${port}/mcp`;
63790
+ const sseResponse = await fetch(sseUrl, {
63791
+ headers: { Accept: "text/event-stream" }
63792
+ });
63793
+ if (!sseResponse.ok || !sseResponse.body) {
63794
+ throw new Error(
63795
+ `Failed to connect to MCP server at ${sseUrl} (${sseResponse.status})`
63796
+ );
63797
+ }
63798
+ const reader = sseResponse.body.getReader();
63799
+ const decoder = new TextDecoder();
63800
+ let buffer2 = "";
63801
+ let sessionUrl;
63802
+ while (!sessionUrl) {
63803
+ const { done, value } = await reader.read();
63804
+ if (done) throw new Error("SSE stream ended before receiving endpoint");
63805
+ buffer2 += decoder.decode(value, { stream: true });
63806
+ const lines = buffer2.split("\n");
63807
+ for (let i = 0; i < lines.length - 1; i++) {
63808
+ const line = lines[i];
63809
+ if (line.startsWith("data: ") && !sessionUrl) {
63810
+ sessionUrl = line.slice(6).trim();
63811
+ }
63812
+ }
63813
+ buffer2 = lines[lines.length - 1];
63814
+ }
63815
+ const messagesUrl = `http://localhost:${port}${sessionUrl}`;
63816
+ const requestId = 1;
63817
+ await fetch(messagesUrl, {
63818
+ method: "POST",
63819
+ headers: { "Content-Type": "application/json" },
63820
+ body: JSON.stringify({
63821
+ jsonrpc: "2.0",
63822
+ id: 0,
63823
+ method: "initialize",
63824
+ params: {
63825
+ protocolVersion: "2024-11-05",
63826
+ capabilities: {},
63827
+ clientInfo: { name: "plutojl-mcp-cli", version: "0.1.0" }
63828
+ }
63829
+ })
63830
+ });
63831
+ let initDone = false;
63832
+ while (!initDone) {
63833
+ const { done, value } = await reader.read();
63834
+ if (done) break;
63835
+ buffer2 += decoder.decode(value, { stream: true });
63836
+ if (buffer2.includes('"initialize"') || buffer2.includes('"id":0')) {
63837
+ initDone = true;
63838
+ }
63839
+ }
63840
+ await fetch(messagesUrl, {
63841
+ method: "POST",
63842
+ headers: { "Content-Type": "application/json" },
63843
+ body: JSON.stringify({
63844
+ jsonrpc: "2.0",
63845
+ method: "notifications/initialized"
63846
+ })
63847
+ });
63848
+ await fetch(messagesUrl, {
63849
+ method: "POST",
63850
+ headers: { "Content-Type": "application/json" },
63851
+ body: JSON.stringify({
63852
+ jsonrpc: "2.0",
63853
+ id: requestId,
63854
+ method,
63855
+ params
63856
+ })
63857
+ });
63858
+ buffer2 = "";
63859
+ const timeout2 = setTimeout(() => {
63860
+ reader.cancel().catch(() => {
63861
+ });
63862
+ }, 3e4);
63863
+ try {
63864
+ while (true) {
63865
+ const { done, value } = await reader.read();
63866
+ if (done) break;
63867
+ buffer2 += decoder.decode(value, { stream: true });
63868
+ const parts = buffer2.split("\n\n");
63869
+ buffer2 = parts.pop() ?? "";
63870
+ for (const part of parts) {
63871
+ let eventData = "";
63872
+ for (const line of part.split("\n")) {
63873
+ if (line.startsWith("data: ")) {
63874
+ eventData += line.slice(6);
63875
+ }
63876
+ }
63877
+ if (eventData) {
63878
+ try {
63879
+ const parsed = JSON.parse(eventData);
63880
+ if (parsed.id === requestId) {
63881
+ if (parsed.error) {
63882
+ throw new Error(
63883
+ `MCP error ${parsed.error.code}: ${parsed.error.message}`
63884
+ );
63885
+ }
63886
+ return parsed.result;
63887
+ }
63888
+ } catch (e) {
63889
+ if (e instanceof SyntaxError) continue;
63890
+ throw e;
63891
+ }
63892
+ }
63893
+ }
63894
+ }
63895
+ } finally {
63896
+ clearTimeout(timeout2);
63897
+ reader.cancel().catch(() => {
63898
+ });
63899
+ }
63900
+ throw new Error("No response received from MCP server");
63901
+ }
63902
+ async function listTools(port) {
63903
+ const result = await mcpRequest(port, "tools/list");
63904
+ const tools = result?.tools ?? [];
63905
+ if (tools.length === 0) {
63906
+ console.log("No tools available.");
63907
+ return;
63908
+ }
63909
+ const maxLen = Math.max(...tools.map((t) => t.name.length));
63910
+ for (const tool of tools) {
63911
+ const params = tool.inputSchema?.properties ? Object.keys(tool.inputSchema.properties) : [];
63912
+ const paramStr = params.length > 0 ? ` (${params.join(", ")})` : "";
63913
+ console.log(
63914
+ ` ${tool.name.padEnd(maxLen)} ${tool.description ?? ""}${paramStr}`
63915
+ );
63916
+ }
63917
+ }
63918
+ async function callTool(port, toolName, argsJson, raw2) {
63919
+ let args;
63920
+ try {
63921
+ args = JSON.parse(argsJson);
63922
+ } catch {
63923
+ console.error(`Invalid JSON arguments: ${argsJson}`);
63924
+ process.exit(1);
63925
+ }
63926
+ const result = await mcpRequest(port, "tools/call", {
63927
+ name: toolName,
63928
+ arguments: args
63929
+ });
63930
+ if (raw2) {
63931
+ console.log(JSON.stringify(result, null, 2));
63932
+ return;
63933
+ }
63934
+ const content = result?.content;
63935
+ if (content) {
63936
+ for (const item of content) {
63937
+ if (item.type === "text" && item.text) {
63938
+ console.log(item.text);
63939
+ }
63940
+ }
63941
+ }
63942
+ if (result?.isError) {
63943
+ process.exit(1);
63944
+ }
63945
+ }
63946
+
63947
+ // ../../src/cli/preflight.ts
63948
+ async function preflight(port, opts = {}) {
63949
+ const mcpUrl = `http://localhost:${port}`;
63950
+ let health;
63951
+ try {
63952
+ const res = await fetch(`${mcpUrl}/health`, {
63953
+ signal: AbortSignal.timeout(3e3)
63954
+ });
63955
+ health = await res.json();
63956
+ } catch {
63957
+ console.error(`Error: Cannot reach MCP server at ${mcpUrl}`);
63958
+ console.error(``);
63959
+ console.error(`The MCP server is not running. Start it first:`);
63960
+ console.error(``);
63961
+ console.error(` npx @plutojl/mcp run`);
63962
+ console.error(``);
63963
+ console.error(
63964
+ `Or, if you are using a different port, pass --mcp-port <port>.`
63965
+ );
63966
+ process.exit(1);
63967
+ }
63968
+ if (opts.requirePluto && !health.plutoServerRunning) {
63969
+ console.error(`Error: MCP server is running but Pluto is not connected.`);
63970
+ console.error(``);
63971
+ console.error(`Start Pluto via the MCP server:`);
63972
+ console.error(``);
63973
+ console.error(` npx @plutojl/mcp call start_pluto_server`);
63974
+ console.error(``);
63975
+ console.error(`Or connect to an existing Pluto server:`);
63976
+ console.error(``);
63977
+ console.error(
63978
+ ` npx @plutojl/mcp call connect_to_pluto_server '{"port": 1234}'`
63979
+ );
63980
+ process.exit(1);
63981
+ }
63982
+ return { mcpRunning: true, plutoRunning: health.plutoServerRunning };
63983
+ }
63984
+
63762
63985
  // ../../src/cli/index.ts
63763
63986
  async function main() {
63764
63987
  const args = parseArgs(process.argv.slice(2));
63988
+ const mcpPort = args.mcpPort ?? DEFAULTS.mcpPort;
63765
63989
  switch (args.command) {
63766
63990
  case "run": {
63767
63991
  const config = resolveRunConfig(args);
@@ -63773,6 +63997,25 @@ async function main() {
63773
63997
  await installMcpConfig(installArgs);
63774
63998
  break;
63775
63999
  }
64000
+ case "tools": {
64001
+ await preflight(mcpPort);
64002
+ await listTools(mcpPort);
64003
+ break;
64004
+ }
64005
+ case "call": {
64006
+ if (!args.toolName) {
64007
+ console.error("Usage: npx @plutojl/mcp call <tool_name> [json_args]");
64008
+ process.exit(1);
64009
+ }
64010
+ await preflight(mcpPort);
64011
+ await callTool(
64012
+ mcpPort,
64013
+ args.toolName,
64014
+ args.toolArgs ?? "{}",
64015
+ args.raw ?? false
64016
+ );
64017
+ break;
64018
+ }
63776
64019
  }
63777
64020
  }
63778
64021
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plutojl/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Standalone MCP server for Pluto.jl notebooks — run with npx @plutojl/mcp",
5
5
  "license": "MIT",
6
6
  "repository": {