exomind 0.2.6 → 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/README.md CHANGED
@@ -56,6 +56,22 @@ exomind install --with-hook
56
56
 
57
57
  即便不装 hook,skill 也会让 Agent 主动遵循飞轮规则。完整接入步骤见服务端仓库 `myExoMindManager/docs/new-machine-setup.md`。
58
58
 
59
+ ## 作为 MCP 工具接入(可选,更确定 + 跨宿主)
60
+
61
+ `exomind mcp` 启动一个**本地 stdio MCP server**,把 ingest/query/search/entity/relations/stats 暴露为**typed tool**。Agent 直接调 tool(确定参数/返回),不必"读 skill → 拼 bash"。复用同一份凭证(`~/.exomind/config.json`),本地 stdio 所以三平台都能跑。
62
+
63
+ **Claude Code**(`~/.claude.json` 或项目 `.mcp.json`):
64
+ ```json
65
+ { "mcpServers": { "exomind": { "command": "exomind", "args": ["mcp"] } } }
66
+ ```
67
+
68
+ **OpenCode**(`opencode.json`):
69
+ ```json
70
+ { "mcp": { "exomind": { "type": "local", "command": ["exomind", "mcp"] } } }
71
+ ```
72
+
73
+ > 与 skill+hook 并存,不冲突:MCP 提供"确定的工具调用",skill 提供"何时用的指导",hook 提供"每 prompt 的强制注入"。详见 [三层模式说明](./docs/mcp.md)。
74
+
59
75
  ## 命令一览
60
76
 
61
77
  | 命令 | 说明 |
package/dist/cli.js CHANGED
@@ -3119,7 +3119,7 @@ var {
3119
3119
  // package.json
3120
3120
  var package_default = {
3121
3121
  name: "exomind",
3122
- version: "0.2.6",
3122
+ version: "0.3.0",
3123
3123
  description: "ExoMind \u8DE8\u5E73\u53F0\u547D\u4EE4\u884C\u5BA2\u6237\u7AEF \u2014 \u901A\u8FC7 REST \u4E0E ExoMind \u77E5\u8BC6\u5E93\u4EA4\u4E92(ingest/query/search/review),\u66FF\u4EE3 Windows \u4E0D\u53EF\u7528\u7684 MCP \u5BA2\u6237\u7AEF\u3002",
3124
3124
  bin: {
3125
3125
  exomind: "dist/cli.js"
@@ -3624,6 +3624,160 @@ async function runHook(client) {
3624
3624
  }
3625
3625
  }
3626
3626
 
3627
+ // src/mcp.ts
3628
+ var readline = __toESM(require("readline"));
3629
+ var PROTOCOL_VERSION = "2024-11-05";
3630
+ var TOOLS = [
3631
+ {
3632
+ name: "ingest",
3633
+ description: "\u5411 ExoMind \u77E5\u8BC6\u5E93\u5BFC\u5165\u77E5\u8BC6(\u6587\u672C),\u81EA\u52A8\u62BD\u53D6\u5B9E\u4F53/\u5173\u7CFB\u3002",
3634
+ inputSchema: {
3635
+ type: "object",
3636
+ properties: {
3637
+ content: { type: "string", description: "\u8981\u5BFC\u5165\u7684\u6587\u672C\u5185\u5BB9" },
3638
+ title: { type: "string", description: "\u6807\u9898(\u53EF\u9009)" },
3639
+ tags: { type: "array", items: { type: "string" }, description: "\u6807\u7B7E(\u53EF\u9009)" }
3640
+ },
3641
+ required: ["content"]
3642
+ }
3643
+ },
3644
+ {
3645
+ name: "query",
3646
+ description: "LLM \u95EE\u7B54:\u57FA\u4E8E\u77E5\u8BC6\u5E93\u56DE\u7B54\u95EE\u9898\u3002",
3647
+ inputSchema: {
3648
+ type: "object",
3649
+ properties: {
3650
+ question: { type: "string", description: "\u95EE\u9898" },
3651
+ tags: { type: "array", items: { type: "string" }, description: "\u6807\u7B7E\u8FC7\u6EE4(\u53EF\u9009)" }
3652
+ },
3653
+ required: ["question"]
3654
+ }
3655
+ },
3656
+ {
3657
+ name: "search",
3658
+ description: "\u5168\u6587\u641C\u7D22\u77E5\u8BC6\u5E93\u3002",
3659
+ inputSchema: {
3660
+ type: "object",
3661
+ properties: {
3662
+ keyword: { type: "string", description: "\u5173\u952E\u8BCD" },
3663
+ limit: { type: "integer", description: "\u8FD4\u56DE\u6570\u91CF(\u9ED8\u8BA4 10)" }
3664
+ },
3665
+ required: ["keyword"]
3666
+ }
3667
+ },
3668
+ {
3669
+ name: "entity",
3670
+ description: "\u83B7\u53D6\u5B9E\u4F53\u8BE6\u60C5(\u63CF\u8FF0\u3001\u522B\u540D\u3001\u5173\u7CFB)\u3002",
3671
+ inputSchema: {
3672
+ type: "object",
3673
+ properties: { name: { type: "string", description: "\u5B9E\u4F53\u540D\u79F0" } },
3674
+ required: ["name"]
3675
+ }
3676
+ },
3677
+ {
3678
+ name: "relations",
3679
+ description: "\u83B7\u53D6\u5B9E\u4F53\u7684\u5173\u8054\u5B9E\u4F53(\u53EF\u8FBE\u6027)\u3002",
3680
+ inputSchema: {
3681
+ type: "object",
3682
+ properties: {
3683
+ name: { type: "string", description: "\u5B9E\u4F53\u540D\u79F0" },
3684
+ depth: { type: "integer", description: "\u6DF1\u5EA6 1-3(\u9ED8\u8BA4 1)" }
3685
+ },
3686
+ required: ["name"]
3687
+ }
3688
+ },
3689
+ {
3690
+ name: "stats",
3691
+ description: "\u77E5\u8BC6\u5E93\u7EDF\u8BA1(\u8282\u70B9/\u5173\u7CFB\u6570)\u3002",
3692
+ inputSchema: { type: "object", properties: {} }
3693
+ }
3694
+ ];
3695
+ async function handleTool(client, name, args) {
3696
+ switch (name) {
3697
+ case "ingest":
3698
+ return client.post("/ingest", args, { timeoutMs: opTimeout(3e5) });
3699
+ case "query":
3700
+ return client.post("/query", args, { timeoutMs: opTimeout(18e4) });
3701
+ case "search":
3702
+ return client.get("/search", { q: args.keyword ?? args.q, limit: args.limit ?? 10 });
3703
+ case "entity":
3704
+ return client.get(`/entities/${encodeURIComponent(String(args.name))}`);
3705
+ case "relations":
3706
+ return client.get(`/relations/${encodeURIComponent(String(args.name))}`, { depth: args.depth ?? 1 });
3707
+ case "stats":
3708
+ return client.get("/stats");
3709
+ default:
3710
+ throw new Error(`\u672A\u77E5\u5DE5\u5177: ${name}`);
3711
+ }
3712
+ }
3713
+ async function handleMessage(client, msg) {
3714
+ const id = msg.id;
3715
+ const method = msg.method;
3716
+ if (method === "initialize") {
3717
+ return {
3718
+ jsonrpc: "2.0",
3719
+ id,
3720
+ result: {
3721
+ protocolVersion: PROTOCOL_VERSION,
3722
+ capabilities: { tools: {} },
3723
+ serverInfo: { name: "exomind", version: package_default.version }
3724
+ }
3725
+ };
3726
+ }
3727
+ if (method === "notifications/initialized" || method === "initialized") return null;
3728
+ if (method === "ping") return { jsonrpc: "2.0", id, result: {} };
3729
+ if (method === "tools/list") return { jsonrpc: "2.0", id, result: { tools: TOOLS } };
3730
+ if (method === "tools/call") {
3731
+ const params = msg.params ?? {};
3732
+ try {
3733
+ const data = await handleTool(client, params.name ?? "", params.arguments ?? {});
3734
+ return {
3735
+ jsonrpc: "2.0",
3736
+ id,
3737
+ result: {
3738
+ content: [{ type: "text", text: typeof data === "string" ? data : JSON.stringify(data, null, 2) }]
3739
+ }
3740
+ };
3741
+ } catch (e) {
3742
+ const message = e instanceof Error ? e.message : String(e);
3743
+ return {
3744
+ jsonrpc: "2.0",
3745
+ id,
3746
+ result: { content: [{ type: "text", text: `\u9519\u8BEF: ${message}` }], isError: true }
3747
+ };
3748
+ }
3749
+ }
3750
+ return { jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown method: ${method}` } };
3751
+ }
3752
+ async function runMcpServer(client) {
3753
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
3754
+ rl.on("line", async (line) => {
3755
+ const trimmed = line.trim();
3756
+ if (!trimmed) return;
3757
+ let msg;
3758
+ try {
3759
+ msg = JSON.parse(trimmed);
3760
+ } catch {
3761
+ return;
3762
+ }
3763
+ try {
3764
+ const resp = await handleMessage(client, msg);
3765
+ if (resp) process.stdout.write(JSON.stringify(resp) + "\n");
3766
+ } catch (e) {
3767
+ process.stdout.write(
3768
+ JSON.stringify({
3769
+ jsonrpc: "2.0",
3770
+ id: msg?.id ?? null,
3771
+ error: { code: -32603, message: e instanceof Error ? e.message : String(e) }
3772
+ }) + "\n"
3773
+ );
3774
+ }
3775
+ });
3776
+ return new Promise((resolve6) => {
3777
+ rl.on("close", () => resolve6());
3778
+ });
3779
+ }
3780
+
3627
3781
  // src/commands/ingest.ts
3628
3782
  var path5 = __toESM(require("path"));
3629
3783
 
@@ -4056,10 +4210,10 @@ async function daily(client, opts) {
4056
4210
  }
4057
4211
 
4058
4212
  // src/commands/login.ts
4059
- var readline = __toESM(require("readline/promises"));
4213
+ var readline2 = __toESM(require("readline/promises"));
4060
4214
  var import_node_process = require("process");
4061
4215
  async function prompt(question) {
4062
- const rl = readline.createInterface({ input: import_node_process.stdin, output: import_node_process.stdout });
4216
+ const rl = readline2.createInterface({ input: import_node_process.stdin, output: import_node_process.stdout });
4063
4217
  try {
4064
4218
  return (await rl.question(question)).trim();
4065
4219
  } finally {
@@ -4248,6 +4402,17 @@ program2.command("hook").description("UserPromptSubmit \u94A9\u5B50(\u7531 Claud
4248
4402
  }
4249
4403
  process.exit(0);
4250
4404
  });
4405
+ program2.command("mcp").description("\u542F\u52A8 stdio MCP server(\u4F9B Claude Code/OpenCode/Cursor \u4F5C\u4E3A MCP \u5DE5\u5177\u63A5\u5165)").action(async (...a) => {
4406
+ const command = a[a.length - 1];
4407
+ const root = rootOptions(command);
4408
+ setJsonMode(false);
4409
+ const cfg = resolveConfig({
4410
+ baseUrl: root.baseUrl,
4411
+ apiKey: root.apiKey
4412
+ });
4413
+ const client = new ApiClient(cfg);
4414
+ await runMcpServer(client);
4415
+ });
4251
4416
  program2.command("install").description("\u5B89\u88C5 Claude Code skill(\u53EF\u9009 --with-hook \u5199\u5165 UserPromptSubmit)").option("--with-hook", "\u540C\u65F6\u5199\u5165 ~/.claude/settings.json \u7684 hook").action(run(install));
4252
4417
  async function main() {
4253
4418
  const first = process.argv[2];