@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.js CHANGED
@@ -112,12 +112,6 @@ var ServerContext = class {
112
112
  getAid() {
113
113
  return this.credentials?.aid ?? null;
114
114
  }
115
- /**
116
- * Get the current login key (nk_...) or null.
117
- */
118
- getLoginKey() {
119
- return this.credentials?.login_key ?? null;
120
- }
121
115
  /**
122
116
  * Hot-reload credentials after registration (no server restart needed).
123
117
  */
@@ -359,38 +353,11 @@ function registerSearchTool(server, ctx) {
359
353
  InputSchema2,
360
354
  async (args) => {
361
355
  try {
362
- ctx.requireAuth();
356
+ const client = ctx.requireAuth();
363
357
  const aid = ctx.getAid();
364
- const loginKey = ctx.getLoginKey();
365
358
  ctx.log("debug", `Searching: "${truncate(args.query, 50)}" limit=${args.limit}`);
366
- const body = {
367
- aid,
368
- looking_for: args.query
369
- };
370
- if (args.limit !== void 0) body.limit = args.limit;
371
- const headers = {
372
- "Content-Type": "application/json"
373
- };
374
- if (loginKey) {
375
- headers["Authorization"] = `Bearer ${loginKey}`;
376
- }
377
- const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/search`, {
378
- method: "POST",
379
- headers,
380
- body: JSON.stringify(body)
381
- });
382
- if (!res.ok) {
383
- const text = await res.text();
384
- let msg = `Search failed (HTTP ${res.status})`;
385
- try {
386
- const err = JSON.parse(text);
387
- if (err.message) msg = err.message;
388
- } catch {
389
- }
390
- return textResult(`\u274C ${msg}`);
391
- }
392
- const result = await res.json();
393
- if (!result.matches || result.matches.length === 0) {
359
+ const result = await client.search(args.query, args.limit, aid);
360
+ if (result.matches.length === 0) {
394
361
  return textResult(
395
362
  `\u{1F50D} No agents found matching "${args.query}".
396
363
 
@@ -437,24 +404,11 @@ function registerStatusTool(server, ctx) {
437
404
  InputSchema3,
438
405
  async (args) => {
439
406
  try {
440
- ctx.requireAuth();
407
+ const client = ctx.requireAuth();
441
408
  const targetAid = args.aid ?? ctx.getAid();
442
409
  const isSelf = !args.aid || args.aid === ctx.getAid();
443
410
  ctx.log("debug", `Status check for: ${targetAid.slice(0, 8)}...`);
444
- const res = await fetch(
445
- `${ctx.getBaseUrl()}/api/acsp/status?aid=${targetAid}`
446
- );
447
- if (!res.ok) {
448
- const text = await res.text();
449
- let msg = `Status check failed (HTTP ${res.status})`;
450
- try {
451
- const err = JSON.parse(text);
452
- if (err.message) msg = err.message;
453
- } catch {
454
- }
455
- return textResult(`\u274C ${msg}`);
456
- }
457
- const result = await res.json();
411
+ const result = await client.status(targetAid);
458
412
  const lines = [
459
413
  isSelf ? "\u{1F4CA} Your Agent Status\n" : `\u{1F4CA} Agent Status: ${targetAid.slice(0, 8)}...
460
414
  `,
@@ -494,9 +448,8 @@ function registerConnectTool(server, ctx) {
494
448
  InputSchema4,
495
449
  async (args) => {
496
450
  try {
497
- ctx.requireAuth();
451
+ const client = ctx.requireAuth();
498
452
  const creds = ctx.credentials;
499
- const loginKey = ctx.getLoginKey();
500
453
  ctx.log("info", `Connecting to: ${args.target_aid.slice(0, 8)}...`);
501
454
  const enrichedMessage = [
502
455
  `[Connection Request from ${creds.agent_name}]`,
@@ -504,41 +457,17 @@ function registerConnectTool(server, ctx) {
504
457
  "",
505
458
  args.message
506
459
  ].join("\n");
507
- const headers = {
508
- "Content-Type": "application/json"
509
- };
510
- if (loginKey) {
511
- headers["Authorization"] = `Bearer ${loginKey}`;
512
- }
513
- const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/message/send`, {
514
- method: "POST",
515
- headers,
516
- body: JSON.stringify({
517
- sender_aid: creds.aid,
518
- recipient_aid: args.target_aid,
519
- content: enrichedMessage
520
- })
460
+ const result = await client.message.send({
461
+ to: args.target_aid,
462
+ content: enrichedMessage
521
463
  });
522
- if (!res.ok) {
523
- const text = await res.text();
524
- let msg = `Connect failed (HTTP ${res.status})`;
525
- try {
526
- const err = JSON.parse(text);
527
- if (err.message) msg = err.message;
528
- } catch {
529
- }
530
- return textResult(`\u274C ${msg}`);
531
- }
532
- const result = await res.json();
533
- const msgId = result.message_id ?? result.id ?? "sent";
534
- const ts = result.created_at ?? result.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
535
- ctx.log("info", `Connection sent: ${msgId}`);
464
+ ctx.log("info", `Connection sent: ${result.message_id}`);
536
465
  return textResult(
537
466
  `\u{1F91D} Connection request sent!
538
467
 
539
468
  To: ${args.target_aid.slice(0, 8)}...
540
469
  Message: ${truncate(args.message, 80)}
541
- Sent at: ${ts}
470
+ ID: ${result.message_id}
542
471
 
543
472
  The target agent will see your name, capabilities, and message.`
544
473
  );
@@ -561,40 +490,15 @@ function registerSendMessageTool(server, ctx) {
561
490
  InputSchema5,
562
491
  async (args) => {
563
492
  try {
564
- ctx.requireAuth();
493
+ const client = ctx.requireAuth();
565
494
  const creds = ctx.credentials;
566
- const loginKey = ctx.getLoginKey();
567
495
  ctx.log("info", `Sending ${args.message_type} to: ${args.to.slice(0, 8)}...`);
568
496
  const content = args.message_type === "text" ? args.content : `[${args.message_type.toUpperCase()}] ${args.content}`;
569
- const headers = {
570
- "Content-Type": "application/json"
571
- };
572
- if (loginKey) {
573
- headers["Authorization"] = `Bearer ${loginKey}`;
574
- }
575
- const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/message/send`, {
576
- method: "POST",
577
- headers,
578
- body: JSON.stringify({
579
- sender_aid: creds.aid,
580
- recipient_aid: args.to,
581
- content
582
- })
497
+ const result = await client.message.send({
498
+ to: args.to,
499
+ content
583
500
  });
584
- if (!res.ok) {
585
- const text = await res.text();
586
- let msg = `Send failed (HTTP ${res.status})`;
587
- try {
588
- const err = JSON.parse(text);
589
- if (err.message) msg = err.message;
590
- } catch {
591
- }
592
- return textResult(`\u274C ${msg}`);
593
- }
594
- const result = await res.json();
595
- const msgId = result.message_id ?? result.id ?? "sent";
596
- const ts = result.created_at ?? result.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
597
- ctx.log("info", `Message sent: ${msgId}`);
501
+ ctx.log("info", `Message sent: ${result.message_id}`);
598
502
  return textResult(
599
503
  `\u2709\uFE0F Message sent!
600
504
 
@@ -602,8 +506,7 @@ function registerSendMessageTool(server, ctx) {
602
506
  To: ${args.to.slice(0, 8)}...
603
507
  Type: ${args.message_type}
604
508
  Preview: ${truncate(args.content, 80)}
605
- Sent at: ${ts}
606
- ID: ${msgId}`
509
+ ID: ${result.message_id}`
607
510
  );
608
511
  } catch (err) {
609
512
  ctx.log("error", `Send message failed: ${String(err)}`);
@@ -613,34 +516,49 @@ function registerSendMessageTool(server, ctx) {
613
516
  );
614
517
  }
615
518
  var InputSchema6 = {
616
- name: zod.z.string().min(1, "Group name is required").max(128, "Group name must be 128 characters or less"),
519
+ name: zod.z.string().min(1, "Group name is required").max(64, "Group name must be 64 characters or less"),
617
520
  description: zod.z.string().max(1024, "Description must be 1024 characters or less").optional()
618
521
  };
619
522
  function registerCreateGroupTool(server, ctx) {
620
523
  server.tool(
621
524
  "neiracore_create_group",
622
- "Create a new privacy group on the Neiracore network. You become the first member automatically. Other agents can join via group ID.",
525
+ "Create a new group on the Neiracore network. You become the first member automatically. Other agents can join via the invite code.",
623
526
  InputSchema6,
624
527
  async (args) => {
625
528
  try {
626
- const client = ctx.requireAuth();
529
+ ctx.requireAuth();
530
+ const creds = ctx.credentials;
627
531
  ctx.log("info", `Creating group: ${args.name}`);
628
- const result = await client.group.create({
629
- name: args.name,
630
- description: args.description
532
+ const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/groups`, {
533
+ method: "POST",
534
+ headers: {
535
+ "Content-Type": "application/json",
536
+ Authorization: `Bearer ${creds.login_key}`
537
+ },
538
+ body: JSON.stringify({
539
+ aid: creds.aid,
540
+ name: args.name,
541
+ description: args.description ?? void 0
542
+ })
631
543
  });
632
- ctx.log("info", `Group created: ${result.group_id}`);
544
+ if (!res.ok) {
545
+ const errBody = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
546
+ throw new Error(errBody.message ?? errBody.error ?? `HTTP ${res.status}`);
547
+ }
548
+ const result = await res.json();
549
+ const group = result.group ?? result;
550
+ ctx.log("info", `Group created: ${String(group.id ?? "unknown")}`);
633
551
  const output = [
634
552
  "\u{1F3D8}\uFE0F Group created!\n",
635
553
  formatSection("", [
636
- ["Group ID", result.group_id],
637
- ["Name", result.name],
638
- ["Description", result.description ?? "(none)"],
639
- ["Created by", result.created_by.slice(0, 8) + "..."],
640
- ["Created at", result.created_at]
554
+ ["Group ID", String(group.id ?? "(unknown)")],
555
+ ["Name", String(group.name ?? args.name)],
556
+ ["Description", String(group.description ?? args.description ?? "(none)")],
557
+ ["Invite Code", String(group.invite_code ?? result.invite_code ?? "(none)")],
558
+ ["Members", String(group.member_count ?? 1)]
641
559
  ]),
642
560
  "",
643
- `Share the Group ID with other agents so they can use neiracore_join_group.`
561
+ `Share the invite code with other agents so they can join.`
644
562
  ].join("\n");
645
563
  return textResult(output);
646
564
  } catch (err) {
@@ -651,33 +569,54 @@ function registerCreateGroupTool(server, ctx) {
651
569
  );
652
570
  }
653
571
  var InputSchema7 = {
654
- group_id: zod.z.string().regex(/^grp_[A-Za-z0-9_-]{21}$/, "Group ID must match format grp_XXXXXXXXXXXXXXXXXXXXX")
572
+ group_id: zod.z.string().optional().describe("UUID of the group/channel to join"),
573
+ group_name: zod.z.string().optional().describe("Name of the group/channel to join (alternative to group_id)")
655
574
  };
656
575
  function registerJoinGroupTool(server, ctx) {
657
576
  server.tool(
658
577
  "neiracore_join_group",
659
- "Join an existing privacy group on the Neiracore network. Provide the group ID shared by another agent.",
578
+ "Join a public group or channel on the Neiracore network. Provide either a group_id or group_name.",
660
579
  InputSchema7,
661
580
  async (args) => {
662
581
  try {
663
- const client = ctx.requireAuth();
582
+ ctx.requireAuth();
664
583
  const creds = ctx.credentials;
665
- ctx.log("info", `Joining group: ${args.group_id}`);
666
- const commitment = acsp.randomNonceHex();
667
- const result = await client.group.join({
668
- groupId: args.group_id,
669
- commitment
584
+ if (!args.group_id && !args.group_name) {
585
+ return {
586
+ content: [{ type: "text", text: "\u274C Provide either group_id or group_name." }],
587
+ isError: true
588
+ };
589
+ }
590
+ const target = args.group_id ?? args.group_name;
591
+ ctx.log("info", `Joining group: ${target}`);
592
+ const res = await fetch(`${ctx.getBaseUrl()}/api/acsp/channels/join`, {
593
+ method: "POST",
594
+ headers: {
595
+ "Content-Type": "application/json",
596
+ Authorization: `Bearer ${creds.login_key}`
597
+ },
598
+ body: JSON.stringify({
599
+ aid: creds.aid,
600
+ channel_id: args.group_id ?? void 0,
601
+ channel_name: args.group_name ?? void 0
602
+ })
670
603
  });
671
- ctx.log("info", `Joined group: ${result.group_id}`);
672
- return textResult(
673
- `\u2705 Joined group!
674
-
675
- Group ID: ${result.group_id}
676
- Agent: ${creds.agent_name} (${result.aid.slice(0, 8)}...)
677
- Joined at: ${result.joined_at}
678
-
679
- You can now collaborate with other group members.`
680
- );
604
+ if (!res.ok) {
605
+ const errBody = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
606
+ throw new Error(errBody.message ?? errBody.error ?? `HTTP ${res.status}`);
607
+ }
608
+ const result = await res.json();
609
+ const ch = result.channel ?? result;
610
+ ctx.log("info", `Joined group: ${String(ch.name ?? target)}`);
611
+ const output = [
612
+ "\u2705 Joined group!\n",
613
+ formatSection("", [
614
+ ["Group", String(ch.name ?? target)],
615
+ ["ID", String(ch.id ?? args.group_id ?? "(unknown)")],
616
+ ["Members", String(ch.member_count ?? "unknown")]
617
+ ])
618
+ ].join("\n");
619
+ return textResult(output);
681
620
  } catch (err) {
682
621
  ctx.log("error", `Join group failed: ${String(err)}`);
683
622
  return handleToolError(err);
@@ -694,46 +633,42 @@ var InputSchema8 = {
694
633
  function registerProposeTool(server, ctx) {
695
634
  server.tool(
696
635
  "neiracore_propose",
697
- "Start a knowledge exchange negotiation with another agent. Describe what you offer and optionally what you want in return. Creates a secure negotiation thread.",
636
+ "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.",
698
637
  InputSchema8,
699
638
  async (args) => {
700
639
  try {
701
640
  const client = ctx.requireAuth();
702
641
  const creds = ctx.credentials;
703
642
  ctx.log("info", `Proposing to ${args.to.slice(0, 8)}...: "${truncate(args.topic, 40)}"`);
704
- const proposalBody = [
705
- `[PROPOSAL] ${args.topic}`,
706
- "",
707
- `OFFER: ${args.offer}`,
708
- args.request ? `REQUEST: ${args.request}` : "",
709
- "",
710
- `From: ${creds.agent_name} (${creds.aid})`
711
- ].filter(Boolean).join("\n");
712
- const nonce = acsp.randomNonceHex();
713
- const result = await client.thread.create({
714
- responderAid: args.to,
715
- encryptedBody: proposalBody,
716
- msgNonce: nonce,
717
- ephX25519Pub: "0".repeat(64),
718
- // Placeholder — v1 uses plaintext
719
- subject: args.topic,
720
- tags: ["proposal", "mcp"],
721
- ttlHours: 72
643
+ const proposal = {
644
+ type: "proposal",
645
+ version: "1.0",
646
+ topic: args.topic,
647
+ offer: args.offer,
648
+ request: args.request ?? null,
649
+ from: {
650
+ aid: creds.aid,
651
+ name: creds.agent_name,
652
+ capabilities: creds.capabilities
653
+ }
654
+ };
655
+ const result = await client.message.send({
656
+ to: args.to,
657
+ content: JSON.stringify(proposal)
722
658
  });
723
- ctx.log("info", `Proposal created: thread ${result.thread_id}`);
659
+ ctx.log("info", `Proposal sent: ${result.message_id}`);
724
660
  const output = [
725
661
  "\u{1F4CB} Proposal sent!\n",
726
662
  formatSection("", [
727
- ["Thread ID", result.thread_id],
728
- ["Status", result.status],
663
+ ["Message ID", result.message_id],
729
664
  ["To", args.to.slice(0, 8) + "..."],
730
665
  ["Topic", args.topic],
731
666
  ["Offer", truncate(args.offer, 60)],
732
- ["Request", args.request ? truncate(args.request, 60) : "(open)"],
733
- ["Expires", result.expires_at]
667
+ ["Request", args.request ? truncate(args.request, 60) : "(open)"]
734
668
  ]),
735
669
  "",
736
- `The target agent will see your proposal. They can accept, counter-offer, or reject.`
670
+ `The target agent will see your proposal in their inbox.`,
671
+ `They can respond with neiracore_send_message.`
737
672
  ].join("\n");
738
673
  return textResult(output);
739
674
  } catch (err) {