claudemesh-cli 0.7.1 → 0.8.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/index.js +531 -20
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -47663,6 +47663,75 @@ var TOOLS = [
47663
47663
  },
47664
47664
  required: ["name"]
47665
47665
  }
47666
+ },
47667
+ {
47668
+ name: "mesh_mcp_deploy",
47669
+ description: "Deploy an MCP server to the mesh from a zip file or git repo. Runs on the broker VPS, persists across peer sessions. Default scope: private (only you).",
47670
+ inputSchema: {
47671
+ type: "object",
47672
+ properties: {
47673
+ server_name: { type: "string", description: "Unique name for the server in this mesh" },
47674
+ file_id: { type: "string", description: "File ID of uploaded zip (from share_file)" },
47675
+ git_url: { type: "string", description: "Git repo URL" },
47676
+ git_branch: { type: "string", description: "Branch to clone (default: main)" },
47677
+ env: { type: "object", description: "Environment variables. Use $vault:<key> for vault secrets." },
47678
+ runtime: { type: "string", enum: ["node", "python", "bun"], description: "Runtime (auto-detected if omitted)" },
47679
+ memory_mb: { type: "number", description: "Memory limit in MB (default: 256)" },
47680
+ network_allow: { type: "array", items: { type: "string" }, description: "Allowed outbound hosts (default: none)" },
47681
+ scope: { description: "Visibility: 'peer' (default), 'mesh', or {group/groups/role/peers}" }
47682
+ },
47683
+ required: ["server_name"]
47684
+ }
47685
+ },
47686
+ {
47687
+ name: "mesh_mcp_undeploy",
47688
+ description: "Stop and remove a managed MCP server from the mesh.",
47689
+ inputSchema: { type: "object", properties: { server_name: { type: "string" } }, required: ["server_name"] }
47690
+ },
47691
+ {
47692
+ name: "mesh_mcp_update",
47693
+ description: "Pull latest code and restart a git-sourced MCP server.",
47694
+ inputSchema: { type: "object", properties: { server_name: { type: "string" } }, required: ["server_name"] }
47695
+ },
47696
+ {
47697
+ name: "mesh_mcp_logs",
47698
+ description: "View recent logs from a managed MCP server.",
47699
+ inputSchema: { type: "object", properties: { server_name: { type: "string" }, lines: { type: "number", description: "Lines (default: 50, max: 1000)" } }, required: ["server_name"] }
47700
+ },
47701
+ {
47702
+ name: "mesh_mcp_scope",
47703
+ description: "Get or set the visibility scope of a deployed MCP server.",
47704
+ inputSchema: { type: "object", properties: { server_name: { type: "string" }, scope: { description: "New scope to set. Omit to read current." } }, required: ["server_name"] }
47705
+ },
47706
+ {
47707
+ name: "mesh_mcp_schema",
47708
+ description: "Inspect tool schemas for a deployed MCP server.",
47709
+ inputSchema: { type: "object", properties: { server_name: { type: "string" }, tool_name: { type: "string", description: "Specific tool (omit for all)" } }, required: ["server_name"] }
47710
+ },
47711
+ {
47712
+ name: "mesh_mcp_catalog",
47713
+ description: "List all deployed services in the mesh with status, scope, and tool count.",
47714
+ inputSchema: { type: "object", properties: {} }
47715
+ },
47716
+ {
47717
+ name: "mesh_skill_deploy",
47718
+ description: "Deploy a multi-file skill bundle from a zip or git repo.",
47719
+ inputSchema: { type: "object", properties: { file_id: { type: "string" }, git_url: { type: "string" }, git_branch: { type: "string" } } }
47720
+ },
47721
+ {
47722
+ name: "vault_set",
47723
+ description: "Store an encrypted credential in your vault. Reference in mesh_mcp_deploy with $vault:<key>.",
47724
+ inputSchema: { type: "object", properties: { key: { type: "string" }, value: { type: "string", description: "Secret value or local file path (for type=file)" }, type: { type: "string", enum: ["env", "file"] }, mount_path: { type: "string" }, description: { type: "string" } }, required: ["key", "value"] }
47725
+ },
47726
+ {
47727
+ name: "vault_list",
47728
+ description: "List your vault entries (keys and metadata only, no secret values).",
47729
+ inputSchema: { type: "object", properties: {} }
47730
+ },
47731
+ {
47732
+ name: "vault_delete",
47733
+ description: "Remove a credential from your vault.",
47734
+ inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] }
47666
47735
  }
47667
47736
  ];
47668
47737
 
@@ -47751,6 +47820,10 @@ class BrokerClient {
47751
47820
  peerFileResponseResolvers = new Map;
47752
47821
  peerDirResponseResolvers = new Map;
47753
47822
  sharedDirs = [process.cwd()];
47823
+ _serviceCatalog = [];
47824
+ get serviceCatalog() {
47825
+ return this._serviceCatalog;
47826
+ }
47754
47827
  closed = false;
47755
47828
  reconnectAttempt = 0;
47756
47829
  helloTimer = null;
@@ -47858,6 +47931,9 @@ class BrokerClient {
47858
47931
  this._statsCounters.errors = rs.errors ?? 0;
47859
47932
  }
47860
47933
  }
47934
+ if (msg.services) {
47935
+ this._serviceCatalog = msg.services;
47936
+ }
47861
47937
  resolve();
47862
47938
  return;
47863
47939
  }
@@ -48146,6 +48222,14 @@ class BrokerClient {
48146
48222
  mcpListResolvers = new Map;
48147
48223
  mcpCallResolvers = new Map;
48148
48224
  mcpCallForwardHandler = null;
48225
+ vaultAckResolvers = new Map;
48226
+ vaultListResolvers = new Map;
48227
+ mcpDeployResolvers = new Map;
48228
+ mcpLogsResolvers = new Map;
48229
+ mcpSchemaServiceResolvers = new Map;
48230
+ mcpCatalogResolvers = new Map;
48231
+ mcpScopeResolvers = new Map;
48232
+ skillDeployResolvers = new Map;
48149
48233
  async messageStatus(messageId) {
48150
48234
  if (!this.ws || this.ws.readyState !== this.ws.OPEN)
48151
48235
  return null;
@@ -48678,6 +48762,138 @@ class BrokerClient {
48678
48762
  this.ws.send(JSON.stringify({ type: "peer_dir_request", targetPubkey, dirPath, ...pattern ? { pattern } : {}, _reqId: reqId }));
48679
48763
  });
48680
48764
  }
48765
+ async vaultSet(key, ciphertext, nonce, sealedKey, entryType, mountPath, description) {
48766
+ return new Promise((resolve) => {
48767
+ const reqId = `vset_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
48768
+ const timer = setTimeout(() => {
48769
+ this.vaultAckResolvers.delete(reqId);
48770
+ resolve(false);
48771
+ }, 1e4);
48772
+ this.vaultAckResolvers.set(reqId, { resolve, timer });
48773
+ this.sendRaw({ type: "vault_set", key, ciphertext, nonce, sealed_key: sealedKey, entry_type: entryType, mount_path: mountPath, description, _reqId: reqId });
48774
+ });
48775
+ }
48776
+ async vaultList() {
48777
+ return new Promise((resolve) => {
48778
+ const reqId = `vlist_${Date.now()}`;
48779
+ const timer = setTimeout(() => {
48780
+ this.vaultListResolvers.delete(reqId);
48781
+ resolve([]);
48782
+ }, 1e4);
48783
+ this.vaultListResolvers.set(reqId, { resolve, timer });
48784
+ this.sendRaw({ type: "vault_list", _reqId: reqId });
48785
+ });
48786
+ }
48787
+ async vaultDelete(key) {
48788
+ return new Promise((resolve) => {
48789
+ const reqId = `vdel_${Date.now()}`;
48790
+ const timer = setTimeout(() => {
48791
+ this.vaultAckResolvers.delete(reqId);
48792
+ resolve(false);
48793
+ }, 1e4);
48794
+ this.vaultAckResolvers.set(reqId, { resolve, timer });
48795
+ this.sendRaw({ type: "vault_delete", key, _reqId: reqId });
48796
+ });
48797
+ }
48798
+ async mcpDeploy(serverName, source, config2, scope) {
48799
+ return new Promise((resolve) => {
48800
+ const reqId = `deploy_${Date.now()}`;
48801
+ const timer = setTimeout(() => {
48802
+ this.mcpDeployResolvers.delete(reqId);
48803
+ resolve({ status: "timeout" });
48804
+ }, 60000);
48805
+ this.mcpDeployResolvers.set(reqId, { resolve, timer });
48806
+ this.sendRaw({ type: "mcp_deploy", server_name: serverName, source, config: config2, scope, _reqId: reqId });
48807
+ });
48808
+ }
48809
+ async mcpUndeploy(serverName) {
48810
+ return new Promise((resolve) => {
48811
+ const reqId = `undeploy_${Date.now()}`;
48812
+ const timer = setTimeout(() => {
48813
+ this.mcpDeployResolvers.delete(reqId);
48814
+ resolve(false);
48815
+ }, 1e4);
48816
+ this.mcpDeployResolvers.set(reqId, { resolve: (r) => resolve(r.status === "stopped"), timer });
48817
+ this.sendRaw({ type: "mcp_undeploy", server_name: serverName, _reqId: reqId });
48818
+ });
48819
+ }
48820
+ async mcpUpdate(serverName) {
48821
+ return new Promise((resolve) => {
48822
+ const reqId = `update_${Date.now()}`;
48823
+ const timer = setTimeout(() => {
48824
+ this.mcpDeployResolvers.delete(reqId);
48825
+ resolve({ status: "timeout" });
48826
+ }, 60000);
48827
+ this.mcpDeployResolvers.set(reqId, { resolve, timer });
48828
+ this.sendRaw({ type: "mcp_update", server_name: serverName, _reqId: reqId });
48829
+ });
48830
+ }
48831
+ async mcpLogs(serverName, lines) {
48832
+ return new Promise((resolve) => {
48833
+ const reqId = `logs_${Date.now()}`;
48834
+ const timer = setTimeout(() => {
48835
+ this.mcpLogsResolvers.delete(reqId);
48836
+ resolve([]);
48837
+ }, 1e4);
48838
+ this.mcpLogsResolvers.set(reqId, { resolve, timer });
48839
+ this.sendRaw({ type: "mcp_logs", server_name: serverName, lines, _reqId: reqId });
48840
+ });
48841
+ }
48842
+ async mcpScope(serverName, scope) {
48843
+ return new Promise((resolve) => {
48844
+ const reqId = `scope_${Date.now()}`;
48845
+ const timer = setTimeout(() => {
48846
+ this.mcpScopeResolvers.delete(reqId);
48847
+ resolve({ scope: { type: "peer" }, deployed_by: "unknown" });
48848
+ }, 1e4);
48849
+ this.mcpScopeResolvers.set(reqId, { resolve, timer });
48850
+ this.sendRaw({ type: "mcp_scope", server_name: serverName, scope, _reqId: reqId });
48851
+ });
48852
+ }
48853
+ async mcpServiceSchema(serverName, toolName) {
48854
+ return new Promise((resolve) => {
48855
+ const reqId = `schema_${Date.now()}`;
48856
+ const timer = setTimeout(() => {
48857
+ this.mcpSchemaServiceResolvers.delete(reqId);
48858
+ resolve([]);
48859
+ }, 1e4);
48860
+ this.mcpSchemaServiceResolvers.set(reqId, { resolve, timer });
48861
+ this.sendRaw({ type: "mcp_schema", server_name: serverName, tool_name: toolName, _reqId: reqId });
48862
+ });
48863
+ }
48864
+ async mcpCatalog() {
48865
+ return new Promise((resolve) => {
48866
+ const reqId = `catalog_${Date.now()}`;
48867
+ const timer = setTimeout(() => {
48868
+ this.mcpCatalogResolvers.delete(reqId);
48869
+ resolve([]);
48870
+ }, 1e4);
48871
+ this.mcpCatalogResolvers.set(reqId, { resolve, timer });
48872
+ this.sendRaw({ type: "mcp_catalog", _reqId: reqId });
48873
+ });
48874
+ }
48875
+ async skillDeploy(source) {
48876
+ return new Promise((resolve) => {
48877
+ const reqId = `skilldeploy_${Date.now()}`;
48878
+ const timer = setTimeout(() => {
48879
+ this.skillDeployResolvers.delete(reqId);
48880
+ resolve({ name: "unknown", files: [] });
48881
+ }, 30000);
48882
+ this.skillDeployResolvers.set(reqId, { resolve, timer });
48883
+ this.sendRaw({ type: "skill_deploy", source, _reqId: reqId });
48884
+ });
48885
+ }
48886
+ async getServiceTools(serviceName) {
48887
+ const cached2 = this._serviceCatalog.find((s) => s.name === serviceName);
48888
+ if (cached2?.tools?.length)
48889
+ return cached2.tools;
48890
+ return this.mcpServiceSchema(serviceName);
48891
+ }
48892
+ sendRaw(payload) {
48893
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN)
48894
+ return;
48895
+ this.ws.send(JSON.stringify(payload));
48896
+ }
48681
48897
  close() {
48682
48898
  this.closed = true;
48683
48899
  this.stopStatsReporting();
@@ -49179,6 +49395,78 @@ class BrokerClient {
49179
49395
  this.resolveFromMap(this.webhookListResolvers, msgReqId, webhooks);
49180
49396
  return;
49181
49397
  }
49398
+ if (msg.type === "vault_ack") {
49399
+ const reqId = msg._reqId;
49400
+ if (reqId && this.vaultAckResolvers.has(reqId)) {
49401
+ const r = this.vaultAckResolvers.get(reqId);
49402
+ clearTimeout(r.timer);
49403
+ this.vaultAckResolvers.delete(reqId);
49404
+ r.resolve(msg.action !== "not_found");
49405
+ }
49406
+ }
49407
+ if (msg.type === "vault_list_result") {
49408
+ const reqId = msg._reqId;
49409
+ if (reqId && this.vaultListResolvers.has(reqId)) {
49410
+ const r = this.vaultListResolvers.get(reqId);
49411
+ clearTimeout(r.timer);
49412
+ this.vaultListResolvers.delete(reqId);
49413
+ r.resolve(msg.entries ?? []);
49414
+ }
49415
+ }
49416
+ if (msg.type === "mcp_deploy_status") {
49417
+ const reqId = msg._reqId;
49418
+ if (reqId && this.mcpDeployResolvers.has(reqId)) {
49419
+ const r = this.mcpDeployResolvers.get(reqId);
49420
+ clearTimeout(r.timer);
49421
+ this.mcpDeployResolvers.delete(reqId);
49422
+ r.resolve({ status: msg.status, tools: msg.tools, error: msg.error });
49423
+ }
49424
+ }
49425
+ if (msg.type === "mcp_logs_result") {
49426
+ const reqId = msg._reqId;
49427
+ if (reqId && this.mcpLogsResolvers.has(reqId)) {
49428
+ const r = this.mcpLogsResolvers.get(reqId);
49429
+ clearTimeout(r.timer);
49430
+ this.mcpLogsResolvers.delete(reqId);
49431
+ r.resolve(msg.lines ?? []);
49432
+ }
49433
+ }
49434
+ if (msg.type === "mcp_schema_result") {
49435
+ const reqId = msg._reqId;
49436
+ if (reqId && this.mcpSchemaServiceResolvers.has(reqId)) {
49437
+ const r = this.mcpSchemaServiceResolvers.get(reqId);
49438
+ clearTimeout(r.timer);
49439
+ this.mcpSchemaServiceResolvers.delete(reqId);
49440
+ r.resolve(msg.tools ?? []);
49441
+ }
49442
+ }
49443
+ if (msg.type === "mcp_catalog_result") {
49444
+ const reqId = msg._reqId;
49445
+ if (reqId && this.mcpCatalogResolvers.has(reqId)) {
49446
+ const r = this.mcpCatalogResolvers.get(reqId);
49447
+ clearTimeout(r.timer);
49448
+ this.mcpCatalogResolvers.delete(reqId);
49449
+ r.resolve(msg.services ?? []);
49450
+ }
49451
+ }
49452
+ if (msg.type === "mcp_scope_result") {
49453
+ const reqId = msg._reqId;
49454
+ if (reqId && this.mcpScopeResolvers.has(reqId)) {
49455
+ const r = this.mcpScopeResolvers.get(reqId);
49456
+ clearTimeout(r.timer);
49457
+ this.mcpScopeResolvers.delete(reqId);
49458
+ r.resolve({ scope: msg.scope, deployed_by: msg.deployed_by });
49459
+ }
49460
+ }
49461
+ if (msg.type === "skill_deploy_ack") {
49462
+ const reqId = msg._reqId;
49463
+ if (reqId && this.skillDeployResolvers.has(reqId)) {
49464
+ const r = this.skillDeployResolvers.get(reqId);
49465
+ clearTimeout(r.timer);
49466
+ this.skillDeployResolvers.delete(reqId);
49467
+ r.resolve({ name: msg.name, files: msg.files ?? [] });
49468
+ }
49469
+ }
49182
49470
  if (msg.type === "error") {
49183
49471
  this.debug(`broker error: ${msg.code} ${msg.message}`);
49184
49472
  const id = msg.id ? String(msg.id) : null;
@@ -49232,7 +49520,15 @@ class BrokerClient {
49232
49520
  [this.peerFileResponseResolvers, { error: "broker error" }],
49233
49521
  [this.peerDirResponseResolvers, { error: "broker error" }],
49234
49522
  [this.webhookAckResolvers, null],
49235
- [this.webhookListResolvers, []]
49523
+ [this.webhookListResolvers, []],
49524
+ [this.vaultAckResolvers, false],
49525
+ [this.vaultListResolvers, []],
49526
+ [this.mcpDeployResolvers, { status: "error" }],
49527
+ [this.mcpLogsResolvers, []],
49528
+ [this.mcpSchemaServiceResolvers, []],
49529
+ [this.mcpCatalogResolvers, []],
49530
+ [this.mcpScopeResolvers, { scope: { type: "peer" }, deployed_by: "unknown" }],
49531
+ [this.skillDeployResolvers, { name: "unknown", files: [] }]
49236
49532
  ];
49237
49533
  for (const [map2, defaultVal] of allMaps) {
49238
49534
  const first = map2.entries().next().value;
@@ -49428,6 +49724,10 @@ function formatPush(p, meshSlug) {
49428
49724
  ${body}`;
49429
49725
  }
49430
49726
  async function startMcpServer() {
49727
+ const serviceIdx = process.argv.indexOf("--service");
49728
+ if (serviceIdx !== -1 && process.argv[serviceIdx + 1]) {
49729
+ return startServiceProxy(process.argv[serviceIdx + 1]);
49730
+ }
49431
49731
  const config2 = loadConfig();
49432
49732
  const myName = config2.displayName ?? "unnamed";
49433
49733
  const myRole = config2.role ?? process.env.CLAUDEMESH_ROLE ?? null;
@@ -50830,6 +51130,120 @@ ${lines.join(`
50830
51130
  process.on("SIGTERM", shutdown);
50831
51131
  process.on("SIGINT", shutdown);
50832
51132
  }
51133
+ async function startServiceProxy(serviceName) {
51134
+ const config2 = loadConfig();
51135
+ if (config2.meshes.length === 0) {
51136
+ process.stderr.write(`[mesh:${serviceName}] no meshes joined
51137
+ `);
51138
+ process.exit(1);
51139
+ }
51140
+ const mesh = config2.meshes[0];
51141
+ const client2 = new BrokerClient(mesh, {
51142
+ displayName: config2.displayName ?? `proxy:${serviceName}`
51143
+ });
51144
+ try {
51145
+ await client2.connect();
51146
+ } catch (e) {
51147
+ process.stderr.write(`[mesh:${serviceName}] broker connect failed: ${e instanceof Error ? e.message : String(e)}
51148
+ `);
51149
+ process.exit(1);
51150
+ }
51151
+ await new Promise((r) => setTimeout(r, 1500));
51152
+ let tools = [];
51153
+ try {
51154
+ const fetched = await client2.getServiceTools(serviceName);
51155
+ tools = fetched;
51156
+ } catch {
51157
+ const cached2 = client2.serviceCatalog.find((s) => s.name === serviceName);
51158
+ if (cached2) {
51159
+ tools = cached2.tools;
51160
+ }
51161
+ }
51162
+ if (tools.length === 0) {
51163
+ process.stderr.write(`[mesh:${serviceName}] no tools found — service may not be running
51164
+ `);
51165
+ }
51166
+ const server = new Server({ name: `mesh:${serviceName}`, version: "0.1.0" }, { capabilities: { tools: {} } });
51167
+ server.setRequestHandler(ListToolsRequestSchema, () => ({
51168
+ tools: tools.map((t) => ({
51169
+ name: t.name,
51170
+ description: `[mesh:${serviceName}] ${t.description}`,
51171
+ inputSchema: t.inputSchema
51172
+ }))
51173
+ }));
51174
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
51175
+ const toolName = req.params.name;
51176
+ const args = req.params.arguments ?? {};
51177
+ if (client2.status !== "open") {
51178
+ let waited = 0;
51179
+ while (client2.status !== "open" && waited < 1e4) {
51180
+ await new Promise((r) => setTimeout(r, 500));
51181
+ waited += 500;
51182
+ }
51183
+ if (client2.status !== "open") {
51184
+ return {
51185
+ content: [
51186
+ {
51187
+ type: "text",
51188
+ text: `Service temporarily unavailable — broker reconnecting. Retry in a few seconds.`
51189
+ }
51190
+ ],
51191
+ isError: true
51192
+ };
51193
+ }
51194
+ }
51195
+ try {
51196
+ const result = await client2.mcpCall(serviceName, toolName, args);
51197
+ if (result.error) {
51198
+ return {
51199
+ content: [{ type: "text", text: `Error: ${result.error}` }],
51200
+ isError: true
51201
+ };
51202
+ }
51203
+ const resultText = typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2);
51204
+ return {
51205
+ content: [{ type: "text", text: resultText }]
51206
+ };
51207
+ } catch (e) {
51208
+ return {
51209
+ content: [
51210
+ {
51211
+ type: "text",
51212
+ text: `Call failed: ${e instanceof Error ? e.message : String(e)}`
51213
+ }
51214
+ ],
51215
+ isError: true
51216
+ };
51217
+ }
51218
+ });
51219
+ client2.onPush((push) => {
51220
+ if (push.event === "mcp_undeployed" && push.eventData?.name === serviceName) {
51221
+ process.stderr.write(`[mesh:${serviceName}] service undeployed — exiting
51222
+ `);
51223
+ client2.close();
51224
+ process.exit(0);
51225
+ }
51226
+ if (push.event === "mcp_updated" && push.eventData?.name === serviceName) {
51227
+ const newTools = push.eventData?.tools;
51228
+ if (Array.isArray(newTools)) {
51229
+ tools = newTools;
51230
+ server.notification({
51231
+ method: "notifications/tools/list_changed"
51232
+ }).catch(() => {});
51233
+ }
51234
+ }
51235
+ });
51236
+ const transport = new StdioServerTransport;
51237
+ await server.connect(transport);
51238
+ const keepalive = setInterval(() => {}, 1000);
51239
+ const shutdown = () => {
51240
+ clearInterval(keepalive);
51241
+ client2.close();
51242
+ process.exit(0);
51243
+ };
51244
+ process.on("SIGTERM", shutdown);
51245
+ process.on("SIGINT", shutdown);
51246
+ }
50833
51247
 
50834
51248
  // src/commands/install.ts
50835
51249
  init_config();
@@ -51526,8 +51940,8 @@ async function runHook(args) {
51526
51940
  // src/commands/launch.ts
51527
51941
  init_config();
51528
51942
  import { spawn } from "node:child_process";
51529
- import { mkdtempSync, writeFileSync as writeFileSync4, rmSync, readdirSync, statSync } from "node:fs";
51530
- import { tmpdir, hostname as hostname2 } from "node:os";
51943
+ import { mkdtempSync, writeFileSync as writeFileSync4, rmSync, readdirSync, statSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
51944
+ import { tmpdir, hostname as hostname2, homedir as homedir4 } from "node:os";
51531
51945
  import { join as join4 } from "node:path";
51532
51946
  import { createInterface } from "node:readline";
51533
51947
  async function pickMesh(meshes) {
@@ -51726,6 +52140,44 @@ async function runLaunch(flags, rawArgs) {
51726
52140
  rmSync(full, { recursive: true, force: true });
51727
52141
  }
51728
52142
  } catch {}
52143
+ try {
52144
+ const claudeConfigPath = join4(homedir4(), ".claude.json");
52145
+ if (existsSync3(claudeConfigPath)) {
52146
+ const claudeConfig = JSON.parse(readFileSync3(claudeConfigPath, "utf-8"));
52147
+ const mcpServers = claudeConfig.mcpServers ?? {};
52148
+ let cleaned = 0;
52149
+ for (const key of Object.keys(mcpServers)) {
52150
+ if (!key.startsWith("mesh:"))
52151
+ continue;
52152
+ const meta2 = mcpServers[key]?._meshSession;
52153
+ if (!meta2?.pid)
52154
+ continue;
52155
+ try {
52156
+ process.kill(meta2.pid, 0);
52157
+ } catch {
52158
+ delete mcpServers[key];
52159
+ cleaned++;
52160
+ }
52161
+ }
52162
+ if (cleaned > 0) {
52163
+ claudeConfig.mcpServers = mcpServers;
52164
+ writeFileSync4(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
52165
+ `, "utf-8");
52166
+ }
52167
+ }
52168
+ } catch {}
52169
+ let serviceCatalog = [];
52170
+ try {
52171
+ const tmpClient = new BrokerClient(mesh, { displayName });
52172
+ await tmpClient.connect();
52173
+ await new Promise((r) => setTimeout(r, 2000));
52174
+ serviceCatalog = tmpClient.serviceCatalog;
52175
+ tmpClient.close();
52176
+ } catch {
52177
+ if (!args.quiet) {
52178
+ console.log(" (Could not fetch service catalog — mesh services won't be natively available)");
52179
+ }
52180
+ }
51729
52181
  const tmpDir = mkdtempSync(join4(tmpdir(), "claudemesh-"));
51730
52182
  const sessionConfig = {
51731
52183
  version: 1,
@@ -51743,6 +52195,50 @@ async function runLaunch(flags, rawArgs) {
51743
52195
  await confirmPermissions();
51744
52196
  }
51745
52197
  }
52198
+ const meshMcpEntries = [];
52199
+ if (serviceCatalog.length > 0) {
52200
+ const claudeConfigPath = join4(homedir4(), ".claude.json");
52201
+ let claudeConfig = {};
52202
+ try {
52203
+ claudeConfig = JSON.parse(readFileSync3(claudeConfigPath, "utf-8"));
52204
+ } catch {
52205
+ claudeConfig = {};
52206
+ }
52207
+ const mcpServers = claudeConfig.mcpServers ?? {};
52208
+ const sessionTag = `${process.pid}`;
52209
+ for (const svc of serviceCatalog) {
52210
+ if (svc.status !== "running")
52211
+ continue;
52212
+ const entryKey = `mesh:${svc.name}:${sessionTag}`;
52213
+ const entry = {
52214
+ command: "claudemesh",
52215
+ args: ["mcp", "--service", svc.name],
52216
+ env: {
52217
+ CLAUDEMESH_CONFIG_DIR: tmpDir
52218
+ },
52219
+ _meshSession: {
52220
+ pid: process.pid,
52221
+ meshSlug: mesh.slug,
52222
+ serviceName: svc.name,
52223
+ createdAt: new Date().toISOString()
52224
+ }
52225
+ };
52226
+ mcpServers[entryKey] = entry;
52227
+ meshMcpEntries.push({ key: entryKey, entry });
52228
+ }
52229
+ claudeConfig.mcpServers = mcpServers;
52230
+ writeFileSync4(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
52231
+ `, "utf-8");
52232
+ if (!args.quiet && meshMcpEntries.length > 0) {
52233
+ console.log(` ${meshMcpEntries.length} mesh service(s) registered as native MCPs:`);
52234
+ for (const { key } of meshMcpEntries) {
52235
+ const svcName = key.split(":")[1];
52236
+ const svc = serviceCatalog.find((s) => s.name === svcName);
52237
+ console.log(` ${svcName} (${svc?.tools.length ?? 0} tools)`);
52238
+ }
52239
+ console.log("");
52240
+ }
52241
+ }
51746
52242
  const filtered = [];
51747
52243
  for (let i = 0;i < args.claudeArgs.length; i++) {
51748
52244
  if (args.claudeArgs[i] === "--dangerously-load-development-channels" || args.claudeArgs[i] === "--dangerously-skip-permissions") {
@@ -51767,10 +52263,25 @@ async function runLaunch(flags, rawArgs) {
51767
52263
  ...process.env,
51768
52264
  CLAUDEMESH_CONFIG_DIR: tmpDir,
51769
52265
  CLAUDEMESH_DISPLAY_NAME: displayName,
52266
+ MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "30000",
52267
+ MAX_MCP_OUTPUT_TOKENS: process.env.MAX_MCP_OUTPUT_TOKENS ?? "50000",
51770
52268
  ...role ? { CLAUDEMESH_ROLE: role } : {}
51771
52269
  }
51772
52270
  });
51773
52271
  const cleanup = () => {
52272
+ if (meshMcpEntries.length > 0) {
52273
+ try {
52274
+ const claudeConfigPath = join4(homedir4(), ".claude.json");
52275
+ const claudeConfig = JSON.parse(readFileSync3(claudeConfigPath, "utf-8"));
52276
+ const mcpServers = claudeConfig.mcpServers ?? {};
52277
+ for (const { key } of meshMcpEntries) {
52278
+ delete mcpServers[key];
52279
+ }
52280
+ claudeConfig.mcpServers = mcpServers;
52281
+ writeFileSync4(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
52282
+ `, "utf-8");
52283
+ } catch {}
52284
+ }
51774
52285
  try {
51775
52286
  rmSync(tmpDir, { recursive: true, force: true });
51776
52287
  } catch {}
@@ -51803,12 +52314,12 @@ async function runLaunch(flags, rawArgs) {
51803
52314
  }
51804
52315
 
51805
52316
  // src/commands/status.ts
51806
- import { statSync as statSync2, existsSync as existsSync3 } from "node:fs";
52317
+ import { statSync as statSync2, existsSync as existsSync4 } from "node:fs";
51807
52318
  init_config();
51808
52319
  // package.json
51809
52320
  var package_default = {
51810
52321
  name: "claudemesh-cli",
51811
- version: "0.7.1",
52322
+ version: "0.8.0",
51812
52323
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
51813
52324
  keywords: [
51814
52325
  "claude-code",
@@ -51909,7 +52420,7 @@ async function runStatus() {
51909
52420
  console.log("─".repeat(60));
51910
52421
  const configPath = getConfigPath();
51911
52422
  let configPerms = "missing";
51912
- if (existsSync3(configPath)) {
52423
+ if (existsSync4(configPath)) {
51913
52424
  const st = statSync2(configPath);
51914
52425
  const mode = (st.mode & 511).toString(8).padStart(4, "0");
51915
52426
  configPerms = mode === "0600" ? `${mode} ✓` : `${mode} ⚠ (expected 0600)`;
@@ -51958,8 +52469,8 @@ async function runStatus() {
51958
52469
 
51959
52470
  // src/commands/doctor.ts
51960
52471
  init_config();
51961
- import { existsSync as existsSync4, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
51962
- import { homedir as homedir4, platform as platform2 } from "node:os";
52472
+ import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
52473
+ import { homedir as homedir5, platform as platform2 } from "node:os";
51963
52474
  import { join as join5 } from "node:path";
51964
52475
  import { spawnSync as spawnSync2 } from "node:child_process";
51965
52476
  function checkNode() {
@@ -51984,8 +52495,8 @@ function checkClaudeOnPath() {
51984
52495
  };
51985
52496
  }
51986
52497
  function checkMcpRegistered() {
51987
- const claudeConfig = join5(homedir4(), ".claude.json");
51988
- if (!existsSync4(claudeConfig)) {
52498
+ const claudeConfig = join5(homedir5(), ".claude.json");
52499
+ if (!existsSync5(claudeConfig)) {
51989
52500
  return {
51990
52501
  name: "claudemesh MCP registered in ~/.claude.json",
51991
52502
  pass: false,
@@ -51993,7 +52504,7 @@ function checkMcpRegistered() {
51993
52504
  };
51994
52505
  }
51995
52506
  try {
51996
- const cfg = JSON.parse(readFileSync3(claudeConfig, "utf-8"));
52507
+ const cfg = JSON.parse(readFileSync4(claudeConfig, "utf-8"));
51997
52508
  const registered = Boolean(cfg.mcpServers?.["claudemesh"]);
51998
52509
  return {
51999
52510
  name: "claudemesh MCP registered in ~/.claude.json",
@@ -52010,8 +52521,8 @@ function checkMcpRegistered() {
52010
52521
  }
52011
52522
  }
52012
52523
  function checkHooksRegistered() {
52013
- const settings = join5(homedir4(), ".claude", "settings.json");
52014
- if (!existsSync4(settings)) {
52524
+ const settings = join5(homedir5(), ".claude", "settings.json");
52525
+ if (!existsSync5(settings)) {
52015
52526
  return {
52016
52527
  name: "Status hooks registered in ~/.claude/settings.json",
52017
52528
  pass: false,
@@ -52019,7 +52530,7 @@ function checkHooksRegistered() {
52019
52530
  };
52020
52531
  }
52021
52532
  try {
52022
- const raw = readFileSync3(settings, "utf-8");
52533
+ const raw = readFileSync4(settings, "utf-8");
52023
52534
  const has = raw.includes("claudemesh hook ");
52024
52535
  return {
52025
52536
  name: "Status hooks registered in ~/.claude/settings.json",
@@ -52036,7 +52547,7 @@ function checkHooksRegistered() {
52036
52547
  }
52037
52548
  function checkConfigFile() {
52038
52549
  const path = getConfigPath();
52039
- if (!existsSync4(path)) {
52550
+ if (!existsSync5(path)) {
52040
52551
  return {
52041
52552
  name: "~/.claudemesh/config.json exists and parses",
52042
52553
  pass: true,
@@ -52140,15 +52651,15 @@ async function runDoctor() {
52140
52651
 
52141
52652
  // src/commands/welcome.ts
52142
52653
  init_config();
52143
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
52144
- import { homedir as homedir5 } from "node:os";
52654
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
52655
+ import { homedir as homedir6 } from "node:os";
52145
52656
  import { join as join6 } from "node:path";
52146
52657
  function detectState() {
52147
- const claudeConfig = join6(homedir5(), ".claude.json");
52658
+ const claudeConfig = join6(homedir6(), ".claude.json");
52148
52659
  let mcpRegistered = false;
52149
- if (existsSync5(claudeConfig)) {
52660
+ if (existsSync6(claudeConfig)) {
52150
52661
  try {
52151
- const cfg = JSON.parse(readFileSync4(claudeConfig, "utf-8"));
52662
+ const cfg = JSON.parse(readFileSync5(claudeConfig, "utf-8"));
52152
52663
  mcpRegistered = Boolean(cfg.mcpServers?.["claudemesh"]);
52153
52664
  } catch {}
52154
52665
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -49,8 +49,8 @@
49
49
  "typescript": "5.9.3",
50
50
  "vitest": "4.0.14",
51
51
  "@turbostarter/eslint-config": "0.1.0",
52
- "@turbostarter/tsconfig": "0.1.0",
53
52
  "@turbostarter/prettier-config": "0.1.0",
53
+ "@turbostarter/tsconfig": "0.1.0",
54
54
  "@turbostarter/vitest-config": "0.1.0"
55
55
  },
56
56
  "scripts": {