@sleep2agi/commhub-server 0.8.4-preview.0 → 0.8.4
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 +1 -1
- package/package.json +1 -1
- package/src/tools.ts +23 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
CommHub: MCP Streamable HTTP + SSE push + REST API for an AI agent network. Single-process Bun server, SQLite-backed, zero config when launched through `anet`.
|
|
4
4
|
|
|
5
|
-
The supported path is to install the `anet` CLI (`@sleep2agi/agent-network
|
|
5
|
+
The supported path is to install the `anet` CLI (`@sleep2agi/agent-network`, currently v2.2.9 at v0.10.10) and run `anet hub start`, which wires up the port, default admin account, recovery admin `utok_`, and local config for you.
|
|
6
6
|
|
|
7
7
|
## Quick start (verified)
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/commhub-server",
|
|
3
|
-
"version": "0.8.4
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "CommHub Server — AI Agent communication hub with MCP protocol, multi-network isolation, user auth, and 17 MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/tools.ts
CHANGED
|
@@ -26,6 +26,22 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
26
26
|
// couldn't tell who actually asked them. Network-bound node tokens are an
|
|
27
27
|
// identity boundary: they must not spoof another node via from_session.
|
|
28
28
|
const defaultFrom = (clientFrom?: string) => (callerTokenIsNetwork && callerAlias) ? callerAlias : (clientFrom || callerAlias || "hub");
|
|
29
|
+
const fromIdentityMismatchReply = (clientFrom?: string) => {
|
|
30
|
+
const requestedFrom = clientFrom?.trim();
|
|
31
|
+
if (!callerTokenIsNetwork || !callerAlias || !requestedFrom || requestedFrom === callerAlias) return null;
|
|
32
|
+
return {
|
|
33
|
+
content: [{
|
|
34
|
+
type: "text" as const,
|
|
35
|
+
text: JSON.stringify({
|
|
36
|
+
ok: false,
|
|
37
|
+
error: "from_session_identity_mismatch",
|
|
38
|
+
message: "network token from_session does not match token-bound node alias",
|
|
39
|
+
token_alias: callerAlias,
|
|
40
|
+
requested_from_session: requestedFrom,
|
|
41
|
+
}),
|
|
42
|
+
}],
|
|
43
|
+
};
|
|
44
|
+
};
|
|
29
45
|
// If enforceNetworkId is set, override any client-supplied network_id
|
|
30
46
|
const getNetworkId = (clientNetId?: string | null) => enforceNetworkId ?? clientNetId ?? null;
|
|
31
47
|
|
|
@@ -605,7 +621,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
605
621
|
parent_task_id: z.string().max(200).optional().describe("Parent task this dispatch is on behalf of. When the child task replies the hub will auto-chain the answer to the parent task's originator, so the user sees the final result even if the intermediate session ends."),
|
|
606
622
|
meta: z.any().optional().describe("Optional structured task metadata, e.g. { attachments: [{ type, path, url, mime, name, size }] }."),
|
|
607
623
|
},
|
|
608
|
-
async ({ alias, task, priority, context, from_session: _fromIn, ttl_seconds, network_id: netId, parent_task_id: parentIn, meta }) => { const from_session = defaultFrom(_fromIn);
|
|
624
|
+
async ({ alias, task, priority, context, from_session: _fromIn, ttl_seconds, network_id: netId, parent_task_id: parentIn, meta }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
609
625
|
const effectiveNetId = getNetworkId(netId);
|
|
610
626
|
const metaJson = normalizeMetaJson(meta);
|
|
611
627
|
// Resolve parent_task_id: explicit > inferred (caller's most recent
|
|
@@ -704,7 +720,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
704
720
|
message: z.string().min(1).max(10000).describe("Message content"),
|
|
705
721
|
from_session: z.string().max(200).optional(),
|
|
706
722
|
},
|
|
707
|
-
async ({ alias, message, from_session: _fromIn }) => { const from_session = defaultFrom(_fromIn);
|
|
723
|
+
async ({ alias, message, from_session: _fromIn }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
708
724
|
const effectiveNetId = getNetworkId(null);
|
|
709
725
|
if (!canWrite(effectiveNetId)) return writeDeniedReply(effectiveNetId);
|
|
710
726
|
console.log(`[${ts()}] ${from_session} → send_message → ${alias}: ${message.slice(0, 60)}`);
|
|
@@ -745,7 +761,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
745
761
|
status: z.enum(["replied", "failed", "cancelled"]).optional().default("replied").describe("Task outcome"),
|
|
746
762
|
from_session: z.string().max(200).optional(),
|
|
747
763
|
},
|
|
748
|
-
async ({ alias, text, in_reply_to, status: replyStatus, from_session: _fromIn }) => { const from_session = defaultFrom(_fromIn);
|
|
764
|
+
async ({ alias, text, in_reply_to, status: replyStatus, from_session: _fromIn }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
749
765
|
const effectiveNetId = getNetworkId(null);
|
|
750
766
|
if (!canWrite(effectiveNetId)) return writeDeniedReply(effectiveNetId);
|
|
751
767
|
console.log(`[${ts()}] ${from_session} → send_reply (${replyStatus}) → ${alias}: ${text.slice(0, 60)}`);
|
|
@@ -820,7 +836,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
820
836
|
task_id: z.string().min(1).max(200).describe("Task ID to acknowledge"),
|
|
821
837
|
from_session: z.string().max(200).optional(),
|
|
822
838
|
},
|
|
823
|
-
async ({ task_id, from_session: _fromIn }) => { const from_session = defaultFrom(_fromIn);
|
|
839
|
+
async ({ task_id, from_session: _fromIn }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
824
840
|
const effectiveNetId = getNetworkId(null);
|
|
825
841
|
if (!canWrite(effectiveNetId)) return writeDeniedReply(effectiveNetId);
|
|
826
842
|
console.log(`[${ts()}] ${from_session} → send_ack → task ${task_id.slice(0, 8)}`);
|
|
@@ -846,7 +862,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
846
862
|
task_id: z.string().min(1).max(200).describe("Task ID to retry"),
|
|
847
863
|
from_session: z.string().max(200).optional(),
|
|
848
864
|
},
|
|
849
|
-
async ({ task_id, from_session: _fromIn }) => { const from_session = defaultFrom(_fromIn);
|
|
865
|
+
async ({ task_id, from_session: _fromIn }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
850
866
|
const effectiveNetId = getNetworkId(null);
|
|
851
867
|
if (!canWrite(effectiveNetId)) return writeDeniedReply(effectiveNetId);
|
|
852
868
|
console.log(`[${ts()}] ${from_session} → retry_task → ${task_id.slice(0, 8)}`);
|
|
@@ -957,7 +973,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
957
973
|
reason: z.string().max(1000).optional().describe("Cancellation reason"),
|
|
958
974
|
from_session: z.string().max(200).optional(),
|
|
959
975
|
},
|
|
960
|
-
async ({ task_id, reason, from_session: _fromIn }) => { const from_session = defaultFrom(_fromIn);
|
|
976
|
+
async ({ task_id, reason, from_session: _fromIn }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
961
977
|
const effectiveNetId = getNetworkId(null);
|
|
962
978
|
if (!canWrite(effectiveNetId)) return writeDeniedReply(effectiveNetId);
|
|
963
979
|
console.log(`[${ts()}] ${from_session} → cancel_task → ${task_id.slice(0, 8)}`);
|
|
@@ -989,7 +1005,7 @@ export function registerTools(server: McpServer, clientIP?: string, enforceNetwo
|
|
|
989
1005
|
new_alias: z.string().min(1).max(200).describe("Target agent alias"),
|
|
990
1006
|
from_session: z.string().max(200).optional(),
|
|
991
1007
|
},
|
|
992
|
-
async ({ task_id, new_alias, from_session: _fromIn }) => { const from_session = defaultFrom(_fromIn);
|
|
1008
|
+
async ({ task_id, new_alias, from_session: _fromIn }) => { const fromMismatch = fromIdentityMismatchReply(_fromIn); if (fromMismatch) return fromMismatch; const from_session = defaultFrom(_fromIn);
|
|
993
1009
|
const effectiveNetId = getNetworkId(null);
|
|
994
1010
|
if (!canWrite(effectiveNetId)) return writeDeniedReply(effectiveNetId);
|
|
995
1011
|
console.log(`[${ts()}] ${from_session} → reassign_task → ${task_id.slice(0, 8)} → ${new_alias}`);
|