@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.
- package/dist/cli.cjs +249 -6
- 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 .
|
|
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
|
|
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
|
-
|
|
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) => {
|