@pyxmate/memory 1.0.1 → 1.1.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.
- package/dist/cli/pyx-mem.mjs +148 -55
- package/package.json +1 -1
package/dist/cli/pyx-mem.mjs
CHANGED
|
@@ -563,9 +563,91 @@ function createReadCredentials(providerFactory) {
|
|
|
563
563
|
};
|
|
564
564
|
}
|
|
565
565
|
|
|
566
|
+
// src/mcp/proxy-server.ts
|
|
567
|
+
import { Client as McpClient } from "@modelcontextprotocol/sdk/client/index.js";
|
|
568
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
569
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
570
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
571
|
+
import {
|
|
572
|
+
CallToolRequestSchema,
|
|
573
|
+
GetPromptRequestSchema,
|
|
574
|
+
ListPromptsRequestSchema,
|
|
575
|
+
ListToolsRequestSchema
|
|
576
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
577
|
+
var REMOTE_MCP_PATH = "/mcp";
|
|
578
|
+
function createProxyServer(client, version) {
|
|
579
|
+
const caps = client.getServerCapabilities();
|
|
580
|
+
const server = new Server(client.getServerVersion() ?? { name: "pyx-memory", version }, {
|
|
581
|
+
capabilities: {
|
|
582
|
+
// Advertise the PRESENCE of tools/prompts, but NOT their `listChanged`
|
|
583
|
+
// sub-capability: this proxy does not relay the remote's list_changed
|
|
584
|
+
// notifications, so claiming listChanged would be a capability the host
|
|
585
|
+
// could rely on but we never honor (Codex: keep the proxy transparent).
|
|
586
|
+
...caps?.tools ? { tools: {} } : {},
|
|
587
|
+
...caps?.prompts ? { prompts: {} } : {}
|
|
588
|
+
},
|
|
589
|
+
instructions: client.getInstructions()
|
|
590
|
+
});
|
|
591
|
+
if (caps?.tools) {
|
|
592
|
+
server.setRequestHandler(
|
|
593
|
+
ListToolsRequestSchema,
|
|
594
|
+
(req) => client.listTools(req.params)
|
|
595
|
+
);
|
|
596
|
+
server.setRequestHandler(
|
|
597
|
+
CallToolRequestSchema,
|
|
598
|
+
(req) => client.callTool(req.params)
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
if (caps?.prompts) {
|
|
602
|
+
server.setRequestHandler(
|
|
603
|
+
ListPromptsRequestSchema,
|
|
604
|
+
(req) => client.listPrompts(req.params)
|
|
605
|
+
);
|
|
606
|
+
server.setRequestHandler(
|
|
607
|
+
GetPromptRequestSchema,
|
|
608
|
+
(req) => client.getPrompt(req.params)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
return server;
|
|
612
|
+
}
|
|
613
|
+
async function runMcpProxyServer(opts) {
|
|
614
|
+
const version = opts.version ?? (true ? "1.1.1" : "0.0.0-dev");
|
|
615
|
+
const read = await opts.readCredentials();
|
|
616
|
+
if (!read.ok) {
|
|
617
|
+
const text = read.result.content.map((c) => c.type === "text" ? c.text : "").join(" ").trim();
|
|
618
|
+
throw new Error(text || "pyx-memory credentials are unavailable. Run: pyx-mem login");
|
|
619
|
+
}
|
|
620
|
+
const { endpoint, apiKey } = read.credentials;
|
|
621
|
+
const url = new URL(`${endpoint.replace(/\/+$/, "")}${REMOTE_MCP_PATH}`);
|
|
622
|
+
const client = new McpClient({ name: "pyx-mem-proxy", version }, { capabilities: {} });
|
|
623
|
+
const clientTransport = new StreamableHTTPClientTransport(url, {
|
|
624
|
+
requestInit: { headers: { Authorization: `Bearer ${apiKey}` } }
|
|
625
|
+
});
|
|
626
|
+
try {
|
|
627
|
+
await client.connect(clientTransport);
|
|
628
|
+
} catch (err) {
|
|
629
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
630
|
+
throw new Error(
|
|
631
|
+
`Could not reach the pyx-memory remote MCP at ${url.href}: ${msg}. Verify the endpoint and key with \`pyx-mem doctor\`, or run the bundled server with \`pyx-mem mcp\`.`
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
try {
|
|
635
|
+
const server = createProxyServer(client, version);
|
|
636
|
+
const transport = new StdioServerTransport();
|
|
637
|
+
const closed = new Promise((resolve3) => {
|
|
638
|
+
transport.onclose = () => resolve3();
|
|
639
|
+
});
|
|
640
|
+
await server.connect(transport);
|
|
641
|
+
await closed;
|
|
642
|
+
} finally {
|
|
643
|
+
await client.close().catch(() => {
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
566
648
|
// src/mcp/server.ts
|
|
567
649
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
568
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
650
|
+
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
569
651
|
|
|
570
652
|
// src/mcp/prompts/index.ts
|
|
571
653
|
import { z } from "zod";
|
|
@@ -1377,7 +1459,7 @@ var ALL_TOOL_NAMES = ALL_TOOLS.map((t) => t.name);
|
|
|
1377
1459
|
// src/mcp/server.ts
|
|
1378
1460
|
async function runMcpServer(opts) {
|
|
1379
1461
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
1380
|
-
const version = opts.version ?? (true ? "1.
|
|
1462
|
+
const version = opts.version ?? (true ? "1.1.1" : "0.0.0-dev");
|
|
1381
1463
|
const server = new McpServer(
|
|
1382
1464
|
{ name: "pyx-memory", version },
|
|
1383
1465
|
{ instructions: PYX_MEMORY_INSTRUCTIONS, capabilities: { tools: {}, prompts: {} } }
|
|
@@ -1396,7 +1478,7 @@ async function runMcpServer(opts) {
|
|
|
1396
1478
|
for (const prompt of ALL_PROMPTS) {
|
|
1397
1479
|
prompt.register(server);
|
|
1398
1480
|
}
|
|
1399
|
-
const transport = new
|
|
1481
|
+
const transport = new StdioServerTransport2();
|
|
1400
1482
|
const closed = new Promise((resolve3) => {
|
|
1401
1483
|
transport.onclose = () => resolve3();
|
|
1402
1484
|
});
|
|
@@ -1405,14 +1487,15 @@ async function runMcpServer(opts) {
|
|
|
1405
1487
|
}
|
|
1406
1488
|
|
|
1407
1489
|
// src/cli/commands/mcp.ts
|
|
1408
|
-
async function mcpCommand() {
|
|
1490
|
+
async function mcpCommand(opts = {}) {
|
|
1409
1491
|
const readCredentials = createReadCredentials(() => getDefaultKeychain());
|
|
1410
1492
|
const onStdinEnd = new Promise((resolve3) => {
|
|
1411
1493
|
process.stdin.once("end", resolve3);
|
|
1412
1494
|
process.stdin.once("close", resolve3);
|
|
1413
1495
|
});
|
|
1414
1496
|
try {
|
|
1415
|
-
|
|
1497
|
+
const run = opts.remote ? runMcpProxyServer({ readCredentials }) : runMcpServer({ readCredentials });
|
|
1498
|
+
await Promise.race([run, onStdinEnd]);
|
|
1416
1499
|
return EXIT.OK;
|
|
1417
1500
|
} catch (err) {
|
|
1418
1501
|
process.stderr.write(`Internal error: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -1544,22 +1627,28 @@ function indent(text) {
|
|
|
1544
1627
|
// src/cli/commands/mcp-install.ts
|
|
1545
1628
|
var VALID_SCOPES = /* @__PURE__ */ new Set(["local", "user", "project"]);
|
|
1546
1629
|
var SERVER_NAME = "pyx-memory";
|
|
1547
|
-
|
|
1630
|
+
function pyxMemArgs(remote) {
|
|
1631
|
+
return remote ? ["mcp", "--remote"] : ["mcp"];
|
|
1632
|
+
}
|
|
1633
|
+
function mcpEntry(remote) {
|
|
1634
|
+
return { command: "pyx-mem", args: pyxMemArgs(remote) };
|
|
1635
|
+
}
|
|
1548
1636
|
function mcpInstallClaudeCodeCommand(opts = {}) {
|
|
1549
1637
|
const scope = opts.scope ?? "local";
|
|
1550
1638
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1639
|
+
const remote = opts.remote ?? false;
|
|
1551
1640
|
const spawnSync = opts._spawnSync ?? nodeSpawnSync;
|
|
1552
1641
|
const probe = spawnSync("claude", ["--version"], { stdio: "pipe" });
|
|
1553
1642
|
if (probe.error || probe.status !== 0) {
|
|
1554
|
-
printClaudeCodeManualInstructions(scope);
|
|
1643
|
+
printClaudeCodeManualInstructions(scope, remote);
|
|
1555
1644
|
return EXIT.OK;
|
|
1556
1645
|
}
|
|
1557
|
-
const cmd = ["mcp", "add", SERVER_NAME, "-s", scope, "--", "pyx-mem",
|
|
1646
|
+
const cmd = ["mcp", "add", SERVER_NAME, "-s", scope, "--", "pyx-mem", ...pyxMemArgs(remote)];
|
|
1558
1647
|
const res = spawnSync("claude", cmd, { stdio: "inherit" });
|
|
1559
1648
|
if (res.status !== 0) {
|
|
1560
1649
|
process.stderr.write(`\`claude ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
|
|
1561
1650
|
`);
|
|
1562
|
-
printClaudeCodeManualInstructions(scope);
|
|
1651
|
+
printClaudeCodeManualInstructions(scope, remote);
|
|
1563
1652
|
return EXIT.USAGE;
|
|
1564
1653
|
}
|
|
1565
1654
|
process.stdout.write(
|
|
@@ -1570,20 +1659,16 @@ To populate the knowledge graph, YOU pass entities and relationships (or triples
|
|
|
1570
1659
|
);
|
|
1571
1660
|
return EXIT.OK;
|
|
1572
1661
|
}
|
|
1573
|
-
function printClaudeCodeManualInstructions(scope) {
|
|
1662
|
+
function printClaudeCodeManualInstructions(scope, remote) {
|
|
1574
1663
|
process.stdout.write(
|
|
1575
1664
|
[
|
|
1576
1665
|
"The `claude` CLI is not on PATH, or the install command failed.",
|
|
1577
1666
|
"",
|
|
1578
1667
|
"Run this once Claude Code is installed:",
|
|
1579
|
-
` claude mcp add ${SERVER_NAME} -s ${scope} -- pyx-mem
|
|
1668
|
+
` claude mcp add ${SERVER_NAME} -s ${scope} -- pyx-mem ${pyxMemArgs(remote).join(" ")}`,
|
|
1580
1669
|
"",
|
|
1581
1670
|
"Or add this entry manually to your Claude Code MCP config (.mcp.json):",
|
|
1582
|
-
JSON.stringify(
|
|
1583
|
-
{ mcpServers: { [SERVER_NAME]: { command: "pyx-mem", args: ["mcp"] } } },
|
|
1584
|
-
null,
|
|
1585
|
-
2
|
|
1586
|
-
),
|
|
1671
|
+
JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpEntry(remote) } }, null, 2),
|
|
1587
1672
|
"",
|
|
1588
1673
|
"Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
|
|
1589
1674
|
""
|
|
@@ -1593,19 +1678,20 @@ function printClaudeCodeManualInstructions(scope) {
|
|
|
1593
1678
|
function mcpInstallCodexCommand(opts = {}) {
|
|
1594
1679
|
const scope = opts.scope ?? "user";
|
|
1595
1680
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1681
|
+
const remote = opts.remote ?? false;
|
|
1596
1682
|
const spawnSync = opts._spawnSync ?? nodeSpawnSync;
|
|
1597
1683
|
const home = opts._homeDir ?? homedir();
|
|
1598
1684
|
const probe = spawnSync("codex", ["--version"], { stdio: "pipe" });
|
|
1599
1685
|
if (probe.error || probe.status !== 0) {
|
|
1600
|
-
printCodexManualInstructions(home);
|
|
1686
|
+
printCodexManualInstructions(home, remote);
|
|
1601
1687
|
return EXIT.OK;
|
|
1602
1688
|
}
|
|
1603
|
-
const cmd = ["mcp", "add", SERVER_NAME, "--", "pyx-mem",
|
|
1689
|
+
const cmd = ["mcp", "add", SERVER_NAME, "--", "pyx-mem", ...pyxMemArgs(remote)];
|
|
1604
1690
|
const res = spawnSync("codex", cmd, { stdio: "inherit" });
|
|
1605
1691
|
if (res.status !== 0) {
|
|
1606
1692
|
process.stderr.write(`\`codex ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
|
|
1607
1693
|
`);
|
|
1608
|
-
printCodexManualInstructions(home);
|
|
1694
|
+
printCodexManualInstructions(home, remote);
|
|
1609
1695
|
return EXIT.USAGE;
|
|
1610
1696
|
}
|
|
1611
1697
|
process.stdout.write(
|
|
@@ -1615,20 +1701,20 @@ Restart Codex CLI to make the tools available. No API key was written to config.
|
|
|
1615
1701
|
);
|
|
1616
1702
|
return EXIT.OK;
|
|
1617
1703
|
}
|
|
1618
|
-
function printCodexManualInstructions(home) {
|
|
1704
|
+
function printCodexManualInstructions(home, remote) {
|
|
1619
1705
|
const configPath = join(home, ".codex", "config.toml");
|
|
1620
1706
|
process.stdout.write(
|
|
1621
1707
|
[
|
|
1622
1708
|
"The `codex` CLI is not on PATH, or the install command failed.",
|
|
1623
1709
|
"",
|
|
1624
1710
|
"Run this once Codex CLI is installed:",
|
|
1625
|
-
` codex mcp add ${SERVER_NAME} -- pyx-mem
|
|
1711
|
+
` codex mcp add ${SERVER_NAME} -- pyx-mem ${pyxMemArgs(remote).join(" ")}`,
|
|
1626
1712
|
"",
|
|
1627
1713
|
`Or add this section manually to ${configPath}:`,
|
|
1628
1714
|
"",
|
|
1629
1715
|
`[mcp_servers.${SERVER_NAME}]`,
|
|
1630
1716
|
`command = "pyx-mem"`,
|
|
1631
|
-
`args =
|
|
1717
|
+
`args = ${JSON.stringify(pyxMemArgs(remote))}`,
|
|
1632
1718
|
"",
|
|
1633
1719
|
"Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
|
|
1634
1720
|
""
|
|
@@ -1640,11 +1726,12 @@ function mcpInstallCursorCommand(opts = {}) {
|
|
|
1640
1726
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1641
1727
|
const home = opts._homeDir ?? homedir();
|
|
1642
1728
|
const filePath = scope === "project" ? join(process.cwd(), ".cursor", "mcp.json") : join(home, ".cursor", "mcp.json");
|
|
1643
|
-
return writeJsonAndReport(filePath, "Cursor");
|
|
1729
|
+
return writeJsonAndReport(filePath, "Cursor", mcpEntry(opts.remote ?? false));
|
|
1644
1730
|
}
|
|
1645
1731
|
function mcpInstallClineCommand(opts = {}) {
|
|
1646
1732
|
const scope = opts.scope ?? "user";
|
|
1647
1733
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1734
|
+
const remote = opts.remote ?? false;
|
|
1648
1735
|
process.stdout.write(
|
|
1649
1736
|
[
|
|
1650
1737
|
"Cline MCP config is managed through the Cline UI in VS Code (the file",
|
|
@@ -1655,11 +1742,7 @@ function mcpInstallClineCommand(opts = {}) {
|
|
|
1655
1742
|
'3. Click "MCP Servers" \u2192 "Configure MCP Servers".',
|
|
1656
1743
|
`4. Add this entry under "mcpServers":`,
|
|
1657
1744
|
"",
|
|
1658
|
-
JSON.stringify(
|
|
1659
|
-
{ mcpServers: { [SERVER_NAME]: { command: "pyx-mem", args: ["mcp"] } } },
|
|
1660
|
-
null,
|
|
1661
|
-
2
|
|
1662
|
-
),
|
|
1745
|
+
JSON.stringify({ mcpServers: { [SERVER_NAME]: mcpEntry(remote) } }, null, 2),
|
|
1663
1746
|
"",
|
|
1664
1747
|
"5. Save the file. Cline will reload the MCP servers automatically.",
|
|
1665
1748
|
"",
|
|
@@ -1672,6 +1755,7 @@ function mcpInstallClineCommand(opts = {}) {
|
|
|
1672
1755
|
function mcpInstallContinueCommand(opts = {}) {
|
|
1673
1756
|
const scope = opts.scope ?? "user";
|
|
1674
1757
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1758
|
+
const remote = opts.remote ?? false;
|
|
1675
1759
|
const home = opts._homeDir ?? homedir();
|
|
1676
1760
|
const yamlPath = join(home, ".continue", "mcpServers", `${SERVER_NAME}.yaml`);
|
|
1677
1761
|
process.stdout.write(
|
|
@@ -1690,7 +1774,7 @@ function mcpInstallContinueCommand(opts = {}) {
|
|
|
1690
1774
|
` - name: ${SERVER_NAME}`,
|
|
1691
1775
|
` command: pyx-mem`,
|
|
1692
1776
|
` args:`,
|
|
1693
|
-
` -
|
|
1777
|
+
...pyxMemArgs(remote).map((arg) => ` - ${arg}`),
|
|
1694
1778
|
"",
|
|
1695
1779
|
"Continue auto-loads new YAML blocks on next chat session \u2014 no restart",
|
|
1696
1780
|
"needed in most setups.",
|
|
@@ -1706,24 +1790,25 @@ function mcpInstallWindsurfCommand(opts = {}) {
|
|
|
1706
1790
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1707
1791
|
const home = opts._homeDir ?? homedir();
|
|
1708
1792
|
const filePath = join(home, ".codeium", "windsurf", "mcp_config.json");
|
|
1709
|
-
return writeJsonAndReport(filePath, "Windsurf");
|
|
1793
|
+
return writeJsonAndReport(filePath, "Windsurf", mcpEntry(opts.remote ?? false));
|
|
1710
1794
|
}
|
|
1711
1795
|
function mcpInstallGeminiCliCommand(opts = {}) {
|
|
1712
1796
|
const scope = opts.scope ?? "user";
|
|
1713
1797
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1798
|
+
const remote = opts.remote ?? false;
|
|
1714
1799
|
const spawnSync = opts._spawnSync ?? nodeSpawnSync;
|
|
1715
1800
|
const home = opts._homeDir ?? homedir();
|
|
1716
1801
|
const probe = spawnSync("gemini", ["--version"], { stdio: "pipe" });
|
|
1717
1802
|
if (probe.error || probe.status !== 0) {
|
|
1718
|
-
return writeJsonAndReport(geminiConfigPath(home, scope), "Gemini CLI");
|
|
1803
|
+
return writeJsonAndReport(geminiConfigPath(home, scope), "Gemini CLI", mcpEntry(remote));
|
|
1719
1804
|
}
|
|
1720
1805
|
const geminiScope = scope === "local" ? "project" : scope;
|
|
1721
|
-
const cmd = ["mcp", "add", "-s", geminiScope, SERVER_NAME, "pyx-mem",
|
|
1806
|
+
const cmd = ["mcp", "add", "-s", geminiScope, SERVER_NAME, "pyx-mem", ...pyxMemArgs(remote)];
|
|
1722
1807
|
const res = spawnSync("gemini", cmd, { stdio: "inherit" });
|
|
1723
1808
|
if (res.status !== 0) {
|
|
1724
1809
|
process.stderr.write(`\`gemini ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
|
|
1725
1810
|
`);
|
|
1726
|
-
return writeJsonAndReport(geminiConfigPath(home, scope), "Gemini CLI");
|
|
1811
|
+
return writeJsonAndReport(geminiConfigPath(home, scope), "Gemini CLI", mcpEntry(remote));
|
|
1727
1812
|
}
|
|
1728
1813
|
process.stdout.write(
|
|
1729
1814
|
`Installed pyx-memory MCP server in Gemini CLI (scope: ${geminiScope}).
|
|
@@ -1740,7 +1825,7 @@ function mcpInstallPiCommand(opts = {}) {
|
|
|
1740
1825
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1741
1826
|
const home = opts._homeDir ?? homedir();
|
|
1742
1827
|
const filePath = scope === "user" ? join(home, ".pi", "agent", "mcp.json") : join(process.cwd(), ".pi", "mcp.json");
|
|
1743
|
-
return writeJsonAndReport(filePath, "Pi", {
|
|
1828
|
+
return writeJsonAndReport(filePath, "Pi", mcpEntry(opts.remote ?? false), {
|
|
1744
1829
|
prereq: "pi has no native MCP support \u2014 install the third-party adapter once:\n pi install npm:pi-mcp-adapter"
|
|
1745
1830
|
});
|
|
1746
1831
|
}
|
|
@@ -1749,18 +1834,19 @@ function mcpInstallOhMyPiCommand(opts = {}) {
|
|
|
1749
1834
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1750
1835
|
const home = opts._homeDir ?? homedir();
|
|
1751
1836
|
const filePath = scope === "user" ? join(home, ".omp", "agent", "mcp.json") : join(process.cwd(), ".omp", "mcp.json");
|
|
1752
|
-
return writeJsonAndReport(filePath, "oh-my-pi");
|
|
1837
|
+
return writeJsonAndReport(filePath, "oh-my-pi", mcpEntry(opts.remote ?? false));
|
|
1753
1838
|
}
|
|
1754
1839
|
function mcpInstallOpenClawCommand(opts = {}) {
|
|
1755
1840
|
const scope = opts.scope ?? "user";
|
|
1756
1841
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1842
|
+
const remote = opts.remote ?? false;
|
|
1757
1843
|
const spawnSync = opts._spawnSync ?? nodeSpawnSync;
|
|
1758
1844
|
const probe = spawnSync("openclaw", ["--version"], { stdio: "pipe" });
|
|
1759
1845
|
if (probe.error || probe.status !== 0) {
|
|
1760
|
-
printOpenClawManualInstructions();
|
|
1846
|
+
printOpenClawManualInstructions(remote);
|
|
1761
1847
|
return EXIT.OK;
|
|
1762
1848
|
}
|
|
1763
|
-
const entryJson = JSON.stringify(
|
|
1849
|
+
const entryJson = JSON.stringify(mcpEntry(remote));
|
|
1764
1850
|
const cmd = ["mcp", "set", SERVER_NAME, entryJson];
|
|
1765
1851
|
const res = spawnSync("openclaw", cmd, { stdio: "inherit" });
|
|
1766
1852
|
if (res.status !== 0) {
|
|
@@ -1768,7 +1854,7 @@ function mcpInstallOpenClawCommand(opts = {}) {
|
|
|
1768
1854
|
`\`openclaw ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
|
|
1769
1855
|
`
|
|
1770
1856
|
);
|
|
1771
|
-
printOpenClawManualInstructions();
|
|
1857
|
+
printOpenClawManualInstructions(remote);
|
|
1772
1858
|
return EXIT.USAGE;
|
|
1773
1859
|
}
|
|
1774
1860
|
process.stdout.write(
|
|
@@ -1778,8 +1864,9 @@ Restart OpenClaw to make the tools available. No API key was written to openclaw
|
|
|
1778
1864
|
);
|
|
1779
1865
|
return EXIT.OK;
|
|
1780
1866
|
}
|
|
1781
|
-
function printOpenClawManualInstructions() {
|
|
1782
|
-
const
|
|
1867
|
+
function printOpenClawManualInstructions(remote) {
|
|
1868
|
+
const entry = mcpEntry(remote);
|
|
1869
|
+
const entryJson = JSON.stringify(entry);
|
|
1783
1870
|
process.stdout.write(
|
|
1784
1871
|
[
|
|
1785
1872
|
"The `openclaw` CLI is not on PATH, or the install command failed.",
|
|
@@ -1790,7 +1877,7 @@ function printOpenClawManualInstructions() {
|
|
|
1790
1877
|
"Or add this entry manually under `mcp.servers` in your OpenClaw config",
|
|
1791
1878
|
"(run `openclaw config file` to print the active config path):",
|
|
1792
1879
|
"",
|
|
1793
|
-
JSON.stringify({ mcp: { servers: { [SERVER_NAME]:
|
|
1880
|
+
JSON.stringify({ mcp: { servers: { [SERVER_NAME]: entry } } }, null, 2),
|
|
1794
1881
|
"",
|
|
1795
1882
|
"Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
|
|
1796
1883
|
""
|
|
@@ -1800,18 +1887,19 @@ function printOpenClawManualInstructions() {
|
|
|
1800
1887
|
function mcpInstallHermesCommand(opts = {}) {
|
|
1801
1888
|
const scope = opts.scope ?? "user";
|
|
1802
1889
|
if (!validateScope(scope)) return EXIT.USAGE;
|
|
1890
|
+
const remote = opts.remote ?? false;
|
|
1803
1891
|
const spawnSync = opts._spawnSync ?? nodeSpawnSync;
|
|
1804
1892
|
const probe = spawnSync("hermes", ["--version"], { stdio: "pipe" });
|
|
1805
1893
|
if (probe.error || probe.status !== 0) {
|
|
1806
|
-
printHermesManualInstructions();
|
|
1894
|
+
printHermesManualInstructions(remote);
|
|
1807
1895
|
return EXIT.OK;
|
|
1808
1896
|
}
|
|
1809
|
-
const cmd = ["mcp", "add", SERVER_NAME, "--command", "pyx-mem", "--args",
|
|
1897
|
+
const cmd = ["mcp", "add", SERVER_NAME, "--command", "pyx-mem", "--args", ...pyxMemArgs(remote)];
|
|
1810
1898
|
const res = spawnSync("hermes", cmd, { stdio: "inherit" });
|
|
1811
1899
|
if (res.status !== 0) {
|
|
1812
1900
|
process.stderr.write(`\`hermes ${cmd.join(" ")}\` exited with code ${res.status ?? "null"}.
|
|
1813
1901
|
`);
|
|
1814
|
-
printHermesManualInstructions();
|
|
1902
|
+
printHermesManualInstructions(remote);
|
|
1815
1903
|
return EXIT.USAGE;
|
|
1816
1904
|
}
|
|
1817
1905
|
process.stdout.write(
|
|
@@ -1821,13 +1909,13 @@ Restart Hermes to make the tools available. No API key was written to config.yam
|
|
|
1821
1909
|
);
|
|
1822
1910
|
return EXIT.OK;
|
|
1823
1911
|
}
|
|
1824
|
-
function printHermesManualInstructions() {
|
|
1912
|
+
function printHermesManualInstructions(remote) {
|
|
1825
1913
|
process.stdout.write(
|
|
1826
1914
|
[
|
|
1827
1915
|
"The `hermes` CLI is not on PATH, or the install command failed.",
|
|
1828
1916
|
"",
|
|
1829
1917
|
"Run this once Hermes is installed:",
|
|
1830
|
-
` hermes mcp add ${SERVER_NAME} --command pyx-mem --args
|
|
1918
|
+
` hermes mcp add ${SERVER_NAME} --command pyx-mem --args ${pyxMemArgs(remote).join(" ")}`,
|
|
1831
1919
|
"",
|
|
1832
1920
|
"Or add this block under `mcp_servers:` in your Hermes config",
|
|
1833
1921
|
"(typically ~/.hermes/config.yaml):",
|
|
@@ -1835,7 +1923,7 @@ function printHermesManualInstructions() {
|
|
|
1835
1923
|
`${SERVER_NAME}:`,
|
|
1836
1924
|
" command: pyx-mem",
|
|
1837
1925
|
" args:",
|
|
1838
|
-
|
|
1926
|
+
...pyxMemArgs(remote).map((arg) => ` - ${arg}`),
|
|
1839
1927
|
"",
|
|
1840
1928
|
"Do NOT add your pyx-memory API key here. Run `pyx-mem login` to store it in the OS credential store.",
|
|
1841
1929
|
""
|
|
@@ -1848,11 +1936,11 @@ function validateScope(scope) {
|
|
|
1848
1936
|
`);
|
|
1849
1937
|
return false;
|
|
1850
1938
|
}
|
|
1851
|
-
function writeJsonAndReport(filePath, agentLabel, opts = {}) {
|
|
1939
|
+
function writeJsonAndReport(filePath, agentLabel, entry, opts = {}) {
|
|
1852
1940
|
const result = mergeWriteJsonMcpEntry({
|
|
1853
1941
|
filePath,
|
|
1854
1942
|
serverName: SERVER_NAME,
|
|
1855
|
-
entry
|
|
1943
|
+
entry
|
|
1856
1944
|
});
|
|
1857
1945
|
const stream = result.exitCode === EXIT.OK ? process.stdout : process.stderr;
|
|
1858
1946
|
stream.write(result.message);
|
|
@@ -2101,9 +2189,14 @@ Commands:
|
|
|
2101
2189
|
logout Delete stored pyx-memory credentials.
|
|
2102
2190
|
doctor [--json] Diagnose keychain, credentials, backend, MCP startup.
|
|
2103
2191
|
scaffold [--name <dir>] Generate Docker, env, SDK, and memory design-guide starter files.
|
|
2104
|
-
mcp
|
|
2105
|
-
|
|
2192
|
+
mcp [--remote] Start stdio MCP server. --remote proxies to the hosted
|
|
2193
|
+
server so tools + instructions stay server-owned
|
|
2194
|
+
(zero-touch updates; key stays in the keychain).
|
|
2195
|
+
mcp install <target> [--scope user|local|project] [--remote]
|
|
2106
2196
|
Install pyx-memory MCP config for your AI agent.
|
|
2197
|
+
--remote installs the hosted thin-proxy (zero-touch
|
|
2198
|
+
updates; key stays in the keychain) instead of the
|
|
2199
|
+
bundled stdio server.
|
|
2107
2200
|
Targets: claude-code, codex, cursor, cline, continue, windsurf, gemini-cli, pi, oh-my-pi, openclaw, hermes.
|
|
2108
2201
|
|
|
2109
2202
|
Notes:
|
|
@@ -2171,7 +2264,7 @@ var INSTALL_TARGETS = {
|
|
|
2171
2264
|
hermes: mcpInstallHermesCommand
|
|
2172
2265
|
};
|
|
2173
2266
|
var VALID_TARGETS = Object.keys(INSTALL_TARGETS);
|
|
2174
|
-
function runMcpInstall(target, scope) {
|
|
2267
|
+
function runMcpInstall(target, scope, remote) {
|
|
2175
2268
|
if (target === void 0 || !Object.hasOwn(INSTALL_TARGETS, target)) {
|
|
2176
2269
|
process.stderr.write(
|
|
2177
2270
|
`Error: unknown install target \`${target ?? ""}\`. Expected: ${VALID_TARGETS.join(", ")}.
|
|
@@ -2180,14 +2273,14 @@ function runMcpInstall(target, scope) {
|
|
|
2180
2273
|
return EXIT.USAGE;
|
|
2181
2274
|
}
|
|
2182
2275
|
const handler = INSTALL_TARGETS[target];
|
|
2183
|
-
return handler({ scope });
|
|
2276
|
+
return handler({ scope, remote });
|
|
2184
2277
|
}
|
|
2185
2278
|
function runMcpCommand(parsed) {
|
|
2186
|
-
if (parsed.subcommand === void 0) return mcpCommand();
|
|
2279
|
+
if (parsed.subcommand === void 0) return mcpCommand({ remote: parsed.flags.remote === true });
|
|
2187
2280
|
if (parsed.subcommand === "install") {
|
|
2188
2281
|
const target = parsed.positional[0];
|
|
2189
2282
|
const scope = typeof parsed.flags.scope === "string" ? parsed.flags.scope : void 0;
|
|
2190
|
-
return runMcpInstall(target, scope);
|
|
2283
|
+
return runMcpInstall(target, scope, parsed.flags.remote === true);
|
|
2191
2284
|
}
|
|
2192
2285
|
process.stderr.write(
|
|
2193
2286
|
`Error: unknown subcommand \`mcp ${parsed.subcommand}\`. Run \`pyx-mem --help\`.
|