@neiracore/mcp-server 1.0.3 → 1.2.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/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
- import { ACSPClient, randomNonceHex, ACSPError } from '@neiracore/acsp';
3
+ import { ACSPClient, ACSPError } from '@neiracore/acsp';
4
4
  import * as fs from 'fs';
5
5
  import * as path from 'path';
6
6
  import * as os from 'os';
@@ -88,12 +88,6 @@ var ServerContext = class {
88
88
  getAid() {
89
89
  return this.credentials?.aid ?? null;
90
90
  }
91
- /**
92
- * Get the current login key (nk_...) or null.
93
- */
94
- getLoginKey() {
95
- return this.credentials?.login_key ?? null;
96
- }
97
91
  /**
98
92
  * Hot-reload credentials after registration (no server restart needed).
99
93
  */
@@ -335,38 +329,11 @@ function registerSearchTool(server, ctx) {
335
329
  InputSchema2,
336
330
  async (args) => {
337
331
  try {
338
- ctx.requireAuth();
332
+ const client = ctx.requireAuth();
339
333
  const aid = ctx.getAid();
340
- const loginKey = ctx.getLoginKey();
341
334
  ctx.log("debug", `Searching: "${truncate(args.query, 50)}" limit=${args.limit}`);
342
- const body = {
343
- aid,
344
- looking_for: args.query
345
- };
346
- if (args.limit !== void 0) body.limit = args.limit;
347
- const headers = {
348
- "Content-Type": "application/json"
349
- };
350
- if (loginKey) {
351
- headers["Authorization"] = `Bearer ${loginKey}`;
352
- }
353
- const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/search`, {
354
- method: "POST",
355
- headers,
356
- body: JSON.stringify(body)
357
- });
358
- if (!res.ok) {
359
- const text = await res.text();
360
- let msg = `Search failed (HTTP ${res.status})`;
361
- try {
362
- const err = JSON.parse(text);
363
- if (err.message) msg = err.message;
364
- } catch {
365
- }
366
- return textResult(`\u274C ${msg}`);
367
- }
368
- const result = await res.json();
369
- if (!result.matches || result.matches.length === 0) {
335
+ const result = await client.search(args.query, args.limit, aid);
336
+ if (result.matches.length === 0) {
370
337
  return textResult(
371
338
  `\u{1F50D} No agents found matching "${args.query}".
372
339
 
@@ -413,24 +380,11 @@ function registerStatusTool(server, ctx) {
413
380
  InputSchema3,
414
381
  async (args) => {
415
382
  try {
416
- ctx.requireAuth();
383
+ const client = ctx.requireAuth();
417
384
  const targetAid = args.aid ?? ctx.getAid();
418
385
  const isSelf = !args.aid || args.aid === ctx.getAid();
419
386
  ctx.log("debug", `Status check for: ${targetAid.slice(0, 8)}...`);
420
- const res = await fetch(
421
- `${ctx.getBaseUrl()}/api/acsp/status?aid=${targetAid}`
422
- );
423
- if (!res.ok) {
424
- const text = await res.text();
425
- let msg = `Status check failed (HTTP ${res.status})`;
426
- try {
427
- const err = JSON.parse(text);
428
- if (err.message) msg = err.message;
429
- } catch {
430
- }
431
- return textResult(`\u274C ${msg}`);
432
- }
433
- const result = await res.json();
387
+ const result = await client.status(targetAid);
434
388
  const lines = [
435
389
  isSelf ? "\u{1F4CA} Your Agent Status\n" : `\u{1F4CA} Agent Status: ${targetAid.slice(0, 8)}...
436
390
  `,
@@ -470,9 +424,8 @@ function registerConnectTool(server, ctx) {
470
424
  InputSchema4,
471
425
  async (args) => {
472
426
  try {
473
- ctx.requireAuth();
427
+ const client = ctx.requireAuth();
474
428
  const creds = ctx.credentials;
475
- const loginKey = ctx.getLoginKey();
476
429
  ctx.log("info", `Connecting to: ${args.target_aid.slice(0, 8)}...`);
477
430
  const enrichedMessage = [
478
431
  `[Connection Request from ${creds.agent_name}]`,
@@ -480,41 +433,17 @@ function registerConnectTool(server, ctx) {
480
433
  "",
481
434
  args.message
482
435
  ].join("\n");
483
- const headers = {
484
- "Content-Type": "application/json"
485
- };
486
- if (loginKey) {
487
- headers["Authorization"] = `Bearer ${loginKey}`;
488
- }
489
- const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/message/send`, {
490
- method: "POST",
491
- headers,
492
- body: JSON.stringify({
493
- sender_aid: creds.aid,
494
- recipient_aid: args.target_aid,
495
- content: enrichedMessage
496
- })
436
+ const result = await client.message.send({
437
+ to: args.target_aid,
438
+ content: enrichedMessage
497
439
  });
498
- if (!res.ok) {
499
- const text = await res.text();
500
- let msg = `Connect failed (HTTP ${res.status})`;
501
- try {
502
- const err = JSON.parse(text);
503
- if (err.message) msg = err.message;
504
- } catch {
505
- }
506
- return textResult(`\u274C ${msg}`);
507
- }
508
- const result = await res.json();
509
- const msgId = result.message_id ?? result.id ?? "sent";
510
- const ts = result.created_at ?? result.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
511
- ctx.log("info", `Connection sent: ${msgId}`);
440
+ ctx.log("info", `Connection sent: ${result.message_id}`);
512
441
  return textResult(
513
442
  `\u{1F91D} Connection request sent!
514
443
 
515
444
  To: ${args.target_aid.slice(0, 8)}...
516
445
  Message: ${truncate(args.message, 80)}
517
- Sent at: ${ts}
446
+ ID: ${result.message_id}
518
447
 
519
448
  The target agent will see your name, capabilities, and message.`
520
449
  );
@@ -537,40 +466,15 @@ function registerSendMessageTool(server, ctx) {
537
466
  InputSchema5,
538
467
  async (args) => {
539
468
  try {
540
- ctx.requireAuth();
469
+ const client = ctx.requireAuth();
541
470
  const creds = ctx.credentials;
542
- const loginKey = ctx.getLoginKey();
543
471
  ctx.log("info", `Sending ${args.message_type} to: ${args.to.slice(0, 8)}...`);
544
472
  const content = args.message_type === "text" ? args.content : `[${args.message_type.toUpperCase()}] ${args.content}`;
545
- const headers = {
546
- "Content-Type": "application/json"
547
- };
548
- if (loginKey) {
549
- headers["Authorization"] = `Bearer ${loginKey}`;
550
- }
551
- const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/message/send`, {
552
- method: "POST",
553
- headers,
554
- body: JSON.stringify({
555
- sender_aid: creds.aid,
556
- recipient_aid: args.to,
557
- content
558
- })
473
+ const result = await client.message.send({
474
+ to: args.to,
475
+ content
559
476
  });
560
- if (!res.ok) {
561
- const text = await res.text();
562
- let msg = `Send failed (HTTP ${res.status})`;
563
- try {
564
- const err = JSON.parse(text);
565
- if (err.message) msg = err.message;
566
- } catch {
567
- }
568
- return textResult(`\u274C ${msg}`);
569
- }
570
- const result = await res.json();
571
- const msgId = result.message_id ?? result.id ?? "sent";
572
- const ts = result.created_at ?? result.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
573
- ctx.log("info", `Message sent: ${msgId}`);
477
+ ctx.log("info", `Message sent: ${result.message_id}`);
574
478
  return textResult(
575
479
  `\u2709\uFE0F Message sent!
576
480
 
@@ -578,8 +482,7 @@ function registerSendMessageTool(server, ctx) {
578
482
  To: ${args.to.slice(0, 8)}...
579
483
  Type: ${args.message_type}
580
484
  Preview: ${truncate(args.content, 80)}
581
- Sent at: ${ts}
582
- ID: ${msgId}`
485
+ ID: ${result.message_id}`
583
486
  );
584
487
  } catch (err) {
585
488
  ctx.log("error", `Send message failed: ${String(err)}`);
@@ -589,34 +492,49 @@ function registerSendMessageTool(server, ctx) {
589
492
  );
590
493
  }
591
494
  var InputSchema6 = {
592
- name: z.string().min(1, "Group name is required").max(128, "Group name must be 128 characters or less"),
495
+ name: z.string().min(1, "Group name is required").max(64, "Group name must be 64 characters or less"),
593
496
  description: z.string().max(1024, "Description must be 1024 characters or less").optional()
594
497
  };
595
498
  function registerCreateGroupTool(server, ctx) {
596
499
  server.tool(
597
500
  "neiracore_create_group",
598
- "Create a new privacy group on the Neiracore network. You become the first member automatically. Other agents can join via group ID.",
501
+ "Create a new group on the Neiracore network. You become the first member automatically. Other agents can join via the invite code.",
599
502
  InputSchema6,
600
503
  async (args) => {
601
504
  try {
602
- const client = ctx.requireAuth();
505
+ ctx.requireAuth();
506
+ const creds = ctx.credentials;
603
507
  ctx.log("info", `Creating group: ${args.name}`);
604
- const result = await client.group.create({
605
- name: args.name,
606
- description: args.description
508
+ const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/groups`, {
509
+ method: "POST",
510
+ headers: {
511
+ "Content-Type": "application/json",
512
+ Authorization: `Bearer ${creds.login_key}`
513
+ },
514
+ body: JSON.stringify({
515
+ aid: creds.aid,
516
+ name: args.name,
517
+ description: args.description ?? void 0
518
+ })
607
519
  });
608
- ctx.log("info", `Group created: ${result.group_id}`);
520
+ if (!res.ok) {
521
+ const errBody = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
522
+ throw new Error(errBody.message ?? errBody.error ?? `HTTP ${res.status}`);
523
+ }
524
+ const result = await res.json();
525
+ const group = result.group ?? result;
526
+ ctx.log("info", `Group created: ${String(group.id ?? "unknown")}`);
609
527
  const output = [
610
528
  "\u{1F3D8}\uFE0F Group created!\n",
611
529
  formatSection("", [
612
- ["Group ID", result.group_id],
613
- ["Name", result.name],
614
- ["Description", result.description ?? "(none)"],
615
- ["Created by", result.created_by.slice(0, 8) + "..."],
616
- ["Created at", result.created_at]
530
+ ["Group ID", String(group.id ?? "(unknown)")],
531
+ ["Name", String(group.name ?? args.name)],
532
+ ["Description", String(group.description ?? args.description ?? "(none)")],
533
+ ["Invite Code", String(group.invite_code ?? result.invite_code ?? "(none)")],
534
+ ["Members", String(group.member_count ?? 1)]
617
535
  ]),
618
536
  "",
619
- `Share the Group ID with other agents so they can use neiracore_join_group.`
537
+ `Share the invite code with other agents so they can join.`
620
538
  ].join("\n");
621
539
  return textResult(output);
622
540
  } catch (err) {
@@ -627,33 +545,54 @@ function registerCreateGroupTool(server, ctx) {
627
545
  );
628
546
  }
629
547
  var InputSchema7 = {
630
- group_id: z.string().regex(/^grp_[A-Za-z0-9_-]{21}$/, "Group ID must match format grp_XXXXXXXXXXXXXXXXXXXXX")
548
+ group_id: z.string().optional().describe("UUID of the group/channel to join"),
549
+ group_name: z.string().optional().describe("Name of the group/channel to join (alternative to group_id)")
631
550
  };
632
551
  function registerJoinGroupTool(server, ctx) {
633
552
  server.tool(
634
553
  "neiracore_join_group",
635
- "Join an existing privacy group on the Neiracore network. Provide the group ID shared by another agent.",
554
+ "Join a public group or channel on the Neiracore network. Provide either a group_id or group_name.",
636
555
  InputSchema7,
637
556
  async (args) => {
638
557
  try {
639
- const client = ctx.requireAuth();
558
+ ctx.requireAuth();
640
559
  const creds = ctx.credentials;
641
- ctx.log("info", `Joining group: ${args.group_id}`);
642
- const commitment = randomNonceHex();
643
- const result = await client.group.join({
644
- groupId: args.group_id,
645
- commitment
560
+ if (!args.group_id && !args.group_name) {
561
+ return {
562
+ content: [{ type: "text", text: "\u274C Provide either group_id or group_name." }],
563
+ isError: true
564
+ };
565
+ }
566
+ const target = args.group_id ?? args.group_name;
567
+ ctx.log("info", `Joining group: ${target}`);
568
+ const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/channels/join`, {
569
+ method: "POST",
570
+ headers: {
571
+ "Content-Type": "application/json",
572
+ Authorization: `Bearer ${creds.login_key}`
573
+ },
574
+ body: JSON.stringify({
575
+ aid: creds.aid,
576
+ channel_id: args.group_id ?? void 0,
577
+ channel_name: args.group_name ?? void 0
578
+ })
646
579
  });
647
- ctx.log("info", `Joined group: ${result.group_id}`);
648
- return textResult(
649
- `\u2705 Joined group!
650
-
651
- Group ID: ${result.group_id}
652
- Agent: ${creds.agent_name} (${result.aid.slice(0, 8)}...)
653
- Joined at: ${result.joined_at}
654
-
655
- You can now collaborate with other group members.`
656
- );
580
+ if (!res.ok) {
581
+ const errBody = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
582
+ throw new Error(errBody.message ?? errBody.error ?? `HTTP ${res.status}`);
583
+ }
584
+ const result = await res.json();
585
+ const ch = result.channel ?? result;
586
+ ctx.log("info", `Joined group: ${String(ch.name ?? target)}`);
587
+ const output = [
588
+ "\u2705 Joined group!\n",
589
+ formatSection("", [
590
+ ["Group", String(ch.name ?? target)],
591
+ ["ID", String(ch.id ?? args.group_id ?? "(unknown)")],
592
+ ["Members", String(ch.member_count ?? "unknown")]
593
+ ])
594
+ ].join("\n");
595
+ return textResult(output);
657
596
  } catch (err) {
658
597
  ctx.log("error", `Join group failed: ${String(err)}`);
659
598
  return handleToolError(err);
@@ -670,46 +609,42 @@ var InputSchema8 = {
670
609
  function registerProposeTool(server, ctx) {
671
610
  server.tool(
672
611
  "neiracore_propose",
673
- "Start a knowledge exchange negotiation with another agent. Describe what you offer and optionally what you want in return. Creates a secure negotiation thread.",
612
+ "Send a knowledge exchange proposal to another agent. Describe what you offer and optionally what you want in return. The proposal is delivered as a structured message to the target agent's inbox.",
674
613
  InputSchema8,
675
614
  async (args) => {
676
615
  try {
677
616
  const client = ctx.requireAuth();
678
617
  const creds = ctx.credentials;
679
618
  ctx.log("info", `Proposing to ${args.to.slice(0, 8)}...: "${truncate(args.topic, 40)}"`);
680
- const proposalBody = [
681
- `[PROPOSAL] ${args.topic}`,
682
- "",
683
- `OFFER: ${args.offer}`,
684
- args.request ? `REQUEST: ${args.request}` : "",
685
- "",
686
- `From: ${creds.agent_name} (${creds.aid})`
687
- ].filter(Boolean).join("\n");
688
- const nonce = randomNonceHex();
689
- const result = await client.thread.create({
690
- responderAid: args.to,
691
- encryptedBody: proposalBody,
692
- msgNonce: nonce,
693
- ephX25519Pub: "0".repeat(64),
694
- // Placeholder — v1 uses plaintext
695
- subject: args.topic,
696
- tags: ["proposal", "mcp"],
697
- ttlHours: 72
619
+ const proposal = {
620
+ type: "proposal",
621
+ version: "1.0",
622
+ topic: args.topic,
623
+ offer: args.offer,
624
+ request: args.request ?? null,
625
+ from: {
626
+ aid: creds.aid,
627
+ name: creds.agent_name,
628
+ capabilities: creds.capabilities
629
+ }
630
+ };
631
+ const result = await client.message.send({
632
+ to: args.to,
633
+ content: JSON.stringify(proposal)
698
634
  });
699
- ctx.log("info", `Proposal created: thread ${result.thread_id}`);
635
+ ctx.log("info", `Proposal sent: ${result.message_id}`);
700
636
  const output = [
701
637
  "\u{1F4CB} Proposal sent!\n",
702
638
  formatSection("", [
703
- ["Thread ID", result.thread_id],
704
- ["Status", result.status],
639
+ ["Message ID", result.message_id],
705
640
  ["To", args.to.slice(0, 8) + "..."],
706
641
  ["Topic", args.topic],
707
642
  ["Offer", truncate(args.offer, 60)],
708
- ["Request", args.request ? truncate(args.request, 60) : "(open)"],
709
- ["Expires", result.expires_at]
643
+ ["Request", args.request ? truncate(args.request, 60) : "(open)"]
710
644
  ]),
711
645
  "",
712
- `The target agent will see your proposal. They can accept, counter-offer, or reject.`
646
+ `The target agent will see your proposal in their inbox.`,
647
+ `They can respond with neiracore_send_message.`
713
648
  ].join("\n");
714
649
  return textResult(output);
715
650
  } catch (err) {