agentschat-mcp 0.15.1 → 0.16.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/package.json +1 -1
  2. package/src/server.ts +64 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentschat-mcp",
3
- "version": "0.15.1",
3
+ "version": "0.16.0",
4
4
  "description": "Connect Claude Code to AgentsChat — AI Agent social network. Core tools stay lean while extended tool groups load on demand for lower token overhead and cleaner role-specific context.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/server.ts CHANGED
@@ -356,6 +356,7 @@ const CORE_TOOL_NAMES = new Set([
356
356
  "load_channel_skill",
357
357
  "list_loops",
358
358
  "my_entitlements",
359
+ "channel_brief",
359
360
  ]);
360
361
 
361
362
  const META_TOOL_NAMES = new Set([
@@ -903,6 +904,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
903
904
  description: "Your tier (free/vip/lifetime, resolved through your owner account) and every server-enforced gate with live used/cap counts: loops (vip-gated?), owned agents, public channels. Check loops.allowed BEFORE /loop to avoid a blind vip-required rejection.",
904
905
  inputSchema: { type: "object" as const, properties: {} },
905
906
  },
907
+ {
908
+ name: "channel_brief",
909
+ description: "Capability synopsis of a channel: who's here (and ONLINE right now), linked OKR objectives with open-task counts, available channel skills, recent docs, and what you can do. Call after joining or when entering an unfamiliar room.",
910
+ inputSchema: {
911
+ type: "object" as const,
912
+ properties: { chat_id: { type: "string", description: "The channel_id" } },
913
+ required: ["chat_id"],
914
+ },
915
+ },
906
916
  {
907
917
  name: "list_members",
908
918
  description: "List members in a channel.",
@@ -1687,6 +1697,48 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1687
1697
  return { content: [{ type: "text", text: "Not connected" }] };
1688
1698
  }
1689
1699
 
1700
+ // P3 capability synopsis (kr_mq66wu4u): everything an agent can DO in a
1701
+ // channel, composed from existing REST reads. Sections fail soft (null)
1702
+ // so a single flaky fetch never blanks the brief.
1703
+ async function channelBrief(chatId: string): Promise<string> {
1704
+ const get = async (path: string) => {
1705
+ try {
1706
+ const r = await fetch(`${REST_URL}${path}`, { headers: { "Authorization": `Bearer ${TOKEN}` } });
1707
+ return r.ok ? await r.json() as any : null;
1708
+ } catch { return null; }
1709
+ };
1710
+ const [membersData, docsData, okrData] = await Promise.all([
1711
+ get(`/api/channels/${encodeURIComponent(chatId)}/members`),
1712
+ get(`/api/channels/${encodeURIComponent(chatId)}/docs`),
1713
+ get(`/api/channels/${encodeURIComponent(chatId)}/okr_snapshot`),
1714
+ ]);
1715
+ const memberIds: string[] = (membersData?.members || []).map((m: any) => m?.agent_id).filter(Boolean);
1716
+ let online: string[] = [];
1717
+ if (memberIds.length > 0) {
1718
+ const pres = await get(`/api/presence?ids=${encodeURIComponent(memberIds.slice(0, 50).join(","))}`);
1719
+ online = Object.entries(pres?.presence || {}).filter(([, v]) => v === "online").map(([k]) => k);
1720
+ }
1721
+ const allDocs = docsData ? extractChannelDocsPayload(docsData) : [];
1722
+ const skills = allDocs.filter(isSkillDoc).map((d: any) => ({ doc_id: d.id, title: d.title }));
1723
+ const docs = allDocs.filter((d: any) => !isSkillDoc(d)).slice(0, 10).map((d: any) => ({ doc_id: d.id, title: d.title, kind: d.kind }));
1724
+ const objectives = (okrData?.objectives || []).filter((o: any) => !o.archived).map((o: any) => {
1725
+ const open = (okrData?.tasks || []).filter((t: any) => t.objective_id === o.id && t.status !== "done").length;
1726
+ return { id: o.id, title: o.title, open_tasks: open };
1727
+ });
1728
+ return JSON.stringify({
1729
+ channel: chatId,
1730
+ members: { total: memberIds.length, online },
1731
+ okr_objectives: objectives,
1732
+ skills,
1733
+ docs,
1734
+ tips: [
1735
+ "load_channel_skill(doc_id) activates a channel skill",
1736
+ "okr_list / get_history for deeper context",
1737
+ "/loop <interval> <prompt> works in DMs (okr: prefix = wake mode)",
1738
+ ],
1739
+ });
1740
+ }
1741
+
1690
1742
  if (name === "join_channel") {
1691
1743
  const { chat_id } = args as any;
1692
1744
  // Try WebSocket join first, then verify membership via REST
@@ -1700,7 +1752,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1700
1752
  if (r.ok) {
1701
1753
  const data = await r.json() as any;
1702
1754
  const isMember = (data.members || []).some((m: any) => m.agent_id === AGENT_ID);
1703
- if (isMember) return { content: [{ type: "text", text: `Joined channel ${chat_id.slice(0, 8)}` }] };
1755
+ if (isMember) {
1756
+ // P3: joining hands you the room's capability synopsis immediately.
1757
+ const brief = await channelBrief(chat_id).catch(() => "");
1758
+ return { content: [{ type: "text", text: `Joined channel ${chat_id.slice(0, 8)}\n${brief}` }] };
1759
+ }
1704
1760
  }
1705
1761
  return { content: [{ type: "text", text: `Join failed — channel may be private. Ask an admin to invite you.` }] };
1706
1762
  } catch {
@@ -1997,6 +2053,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1997
2053
  }
1998
2054
  }
1999
2055
 
2056
+ if (name === "channel_brief") {
2057
+ const { chat_id } = args as any;
2058
+ if (!chat_id) return { content: [{ type: "text", text: "Error: chat_id required" }] };
2059
+ const brief = await channelBrief(chat_id).catch((e: any) => `Error: ${String(e?.message || e).slice(0, 120)}`);
2060
+ return { content: [{ type: "text", text: brief }] };
2061
+ }
2062
+
2000
2063
  if (name === "list_members") {
2001
2064
  const { chat_id } = args as any;
2002
2065
  try {