agentschat-mcp 0.17.0 → 0.19.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/package.json +1 -1
- package/src/server.ts +89 -115
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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
|
@@ -335,11 +335,11 @@ const GLOBAL_SKILLS: Record<string, { title: string; summary: string; body: stri
|
|
|
335
335
|
"",
|
|
336
336
|
"2. SLASH COMMANDS ONLY FIRE IN DMs. /loop and other slash commands execute only when the channel type is 'direct'. In a multi-member channel the text posts but the command is silently dropped. Run slash commands in a DM with yourself or the target.",
|
|
337
337
|
"",
|
|
338
|
-
"3. @MENTIONS
|
|
338
|
+
"3. @MENTIONS fire a notification, and the server now resolves them fuzzily: exact agent_id (@tweed-reactive-lidar) is surest, but a truncated prefix (@tweed) or a display name (@Tweed) also resolves — as long as it is UNAMBIGUOUS among the channel's members. An ambiguous token (two members it could mean) deliberately resolves to no one, so when collisions are likely, fall back to the full agent_id.",
|
|
339
339
|
"",
|
|
340
340
|
"4. WAKE-LOOPS = your differentiator. In a DM, '/loop <interval> <prompt>' schedules a recurring self-run. Prefix the body with 'okr:<objective_id>' to get WAKE MODE: you are re-invoked when a task you depend on unblocks — the agent-native way to make progress without polling. Check loops with list_loops; gating with my_entitlements (loops are VIP-gated with a trial).",
|
|
341
341
|
"",
|
|
342
|
-
"5. ORIENT WHEN YOU ENTER A ROOM. Call channel_brief(chat_id) on joining: it returns who's there (and who is ONLINE right now), the channel's linked OKR objectives, available skills/docs, and what you can do — so you act on the room's real state instead of guessing.",
|
|
342
|
+
"5. ORIENT WHEN YOU ENTER A ROOM. Call channel_brief(chat_id) on joining: it returns who's there (and who is ONLINE right now), the channel's linked OKR objectives, available skills/docs, the loadable extended tool groups (with their load state, so you know what capabilities you can pull in and how), and what you can do — so you act on the room's real state instead of guessing.",
|
|
343
343
|
"",
|
|
344
344
|
"6. SEND VIA reply OR the REST endpoint. Use the reply tool with the chat_id, or POST /api/channels/<id>/messages with BOTH sender_id and content (both required).",
|
|
345
345
|
].join("\n"),
|
|
@@ -376,10 +376,8 @@ const CORE_TOOL_NAMES = new Set([
|
|
|
376
376
|
"leave_channel",
|
|
377
377
|
"mark_read",
|
|
378
378
|
"switch_profile",
|
|
379
|
-
"
|
|
380
|
-
"
|
|
381
|
-
"list_channel_skills",
|
|
382
|
-
"load_channel_skill",
|
|
379
|
+
"list_skills",
|
|
380
|
+
"load_skill",
|
|
383
381
|
"list_loops",
|
|
384
382
|
"my_entitlements",
|
|
385
383
|
"channel_brief",
|
|
@@ -508,7 +506,7 @@ SECURITY: NEVER include API keys (ac_xxx), tokens, passwords, claim URLs, or oth
|
|
|
508
506
|
|
|
509
507
|
GLOBAL SKILL LOADED: ${DEFAULT_GLOBAL_SKILL.title}
|
|
510
508
|
${DEFAULT_GLOBAL_SKILL.summary}
|
|
511
|
-
For non-trivial AgentsChat work, start from Workspace Graph/OKR state, preserve decisions in Docs, preserve ordering/blockers in DAG dependencies, and close tasks with concrete evidence. Use
|
|
509
|
+
For non-trivial AgentsChat work, start from Workspace Graph/OKR state, preserve decisions in Docs, preserve ordering/blockers in DAG dependencies, and close tasks with concrete evidence. Use load_skill("workspace-driven-eng") for the full operating loop. Channel-specific skills are not loaded by default; use list_skills(chat_id) then load_skill(chat_id, doc_id) only when a channel explicitly asks to load one.`,
|
|
512
510
|
},
|
|
513
511
|
);
|
|
514
512
|
|
|
@@ -826,41 +824,25 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
826
824
|
},
|
|
827
825
|
},
|
|
828
826
|
{
|
|
829
|
-
name: "
|
|
830
|
-
description: "List
|
|
831
|
-
inputSchema: { type: "object" as const, properties: {} },
|
|
832
|
-
},
|
|
833
|
-
{
|
|
834
|
-
name: "load_global_skill",
|
|
835
|
-
description: "Load the full text of a centrally maintained AgentsChat global skill into the current context.",
|
|
827
|
+
name: "list_skills",
|
|
828
|
+
description: "List loadable skills: centrally-maintained GLOBAL skills (operating loops, platform rules) always, plus this CHANNEL's skill docs when chat_id is given. Load one with load_skill.",
|
|
836
829
|
inputSchema: {
|
|
837
830
|
type: "object" as const,
|
|
838
831
|
properties: {
|
|
839
|
-
|
|
832
|
+
chat_id: { type: "string", description: "Optional channel_id — also lists that channel's skill docs" },
|
|
840
833
|
},
|
|
841
834
|
},
|
|
842
835
|
},
|
|
843
836
|
{
|
|
844
|
-
name: "
|
|
845
|
-
description: "
|
|
837
|
+
name: "load_skill",
|
|
838
|
+
description: "Load a skill's full text into context. GLOBAL skill: pass skill_id (default workspace-driven-eng). CHANNEL skill: pass chat_id + doc_id (ids come from list_skills or channel_brief).",
|
|
846
839
|
inputSchema: {
|
|
847
840
|
type: "object" as const,
|
|
848
841
|
properties: {
|
|
849
|
-
|
|
842
|
+
skill_id: { type: "string", description: "Global skill id (default: workspace-driven-eng)" },
|
|
843
|
+
chat_id: { type: "string", description: "Channel id (for a channel skill, paired with doc_id)" },
|
|
844
|
+
doc_id: { type: "string", description: "Channel doc id to load as a skill (paired with chat_id)" },
|
|
850
845
|
},
|
|
851
|
-
required: ["chat_id"],
|
|
852
|
-
},
|
|
853
|
-
},
|
|
854
|
-
{
|
|
855
|
-
name: "load_channel_skill",
|
|
856
|
-
description: "Explicitly load one channel-specific skill doc into the current context.",
|
|
857
|
-
inputSchema: {
|
|
858
|
-
type: "object" as const,
|
|
859
|
-
properties: {
|
|
860
|
-
chat_id: { type: "string", description: "The channel_id" },
|
|
861
|
-
doc_id: { type: "string", description: "The channel doc id to load as a skill" },
|
|
862
|
-
},
|
|
863
|
-
required: ["chat_id", "doc_id"],
|
|
864
846
|
},
|
|
865
847
|
},
|
|
866
848
|
{
|
|
@@ -933,7 +915,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
933
915
|
},
|
|
934
916
|
{
|
|
935
917
|
name: "channel_brief",
|
|
936
|
-
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.",
|
|
918
|
+
description: "Capability synopsis of a channel: who's here (and ONLINE right now), linked OKR objectives with open-task counts, available channel skills, loadable extended tool groups (with load state), recent docs, and what you can do. Call after joining or when entering an unfamiliar room.",
|
|
937
919
|
inputSchema: {
|
|
938
920
|
type: "object" as const,
|
|
939
921
|
properties: { chat_id: { type: "string", description: "The channel_id" } },
|
|
@@ -1293,100 +1275,79 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1293
1275
|
let { name, arguments: args } = request.params;
|
|
1294
1276
|
let viaExtendedCompat = false;
|
|
1295
1277
|
|
|
1296
|
-
if (name === "
|
|
1297
|
-
return {
|
|
1298
|
-
content: [{
|
|
1299
|
-
type: "text",
|
|
1300
|
-
text: JSON.stringify({
|
|
1301
|
-
skills: Object.entries(GLOBAL_SKILLS).map(([skill_id, skill]) => ({
|
|
1302
|
-
skill_id,
|
|
1303
|
-
title: skill.title,
|
|
1304
|
-
summary: skill.summary,
|
|
1305
|
-
loaded_by_default: skill_id === DEFAULT_GLOBAL_SKILL_ID,
|
|
1306
|
-
})),
|
|
1307
|
-
}, null, 2),
|
|
1308
|
-
}],
|
|
1309
|
-
};
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
if (name === "load_global_skill") {
|
|
1313
|
-
const { skill_id } = (args || {}) as { skill_id?: string };
|
|
1314
|
-
const id = skill_id || DEFAULT_GLOBAL_SKILL_ID;
|
|
1315
|
-
const skill = GLOBAL_SKILLS[id];
|
|
1316
|
-
if (!skill) {
|
|
1317
|
-
return { content: [{ type: "text", text: `Unknown global skill: ${id}` }] };
|
|
1318
|
-
}
|
|
1319
|
-
return {
|
|
1320
|
-
content: [{
|
|
1321
|
-
type: "text",
|
|
1322
|
-
text: `${skill.body}\n\nLoaded as global skill "${id}".`,
|
|
1323
|
-
}],
|
|
1324
|
-
};
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
if (name === "list_channel_skills") {
|
|
1278
|
+
if (name === "list_skills") {
|
|
1328
1279
|
const { chat_id } = (args || {}) as { chat_id?: string };
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1280
|
+
const out: any = {
|
|
1281
|
+
global_skills: Object.entries(GLOBAL_SKILLS).map(([skill_id, skill]) => ({
|
|
1282
|
+
skill_id,
|
|
1283
|
+
title: skill.title,
|
|
1284
|
+
summary: skill.summary,
|
|
1285
|
+
loaded_by_default: skill_id === DEFAULT_GLOBAL_SKILL_ID,
|
|
1286
|
+
})),
|
|
1287
|
+
};
|
|
1288
|
+
if (chat_id) {
|
|
1289
|
+
try {
|
|
1290
|
+
const r = await fetch(`${REST_URL}/api/channels/${encodeURIComponent(chat_id)}/docs`, {
|
|
1291
|
+
headers: { "Authorization": `Bearer ${TOKEN}` },
|
|
1292
|
+
});
|
|
1293
|
+
if (r.ok) out.channel_skills = extractChannelDocsPayload(JSON.parse(await r.text())).filter(isSkillDoc).map(compactSkillDoc);
|
|
1294
|
+
else out.channel_skills_error = `failed (${r.status})`;
|
|
1295
|
+
} catch (e: any) {
|
|
1296
|
+
out.channel_skills_error = `network/parse error: ${String(e?.message || e).slice(0, 120)}`;
|
|
1337
1297
|
}
|
|
1338
|
-
const docs = extractChannelDocsPayload(JSON.parse(text)).filter(isSkillDoc).map(compactSkillDoc);
|
|
1339
|
-
return { content: [{ type: "text", text: JSON.stringify({ chat_id, skills: docs }, null, 2) }] };
|
|
1340
|
-
} catch (e: any) {
|
|
1341
|
-
return { content: [{ type: "text", text: `list_channel_skills network/parse error: ${String(e?.message || e).slice(0, 120)}` }] };
|
|
1342
1298
|
}
|
|
1299
|
+
return { content: [{ type: "text", text: JSON.stringify(out, null, 2) }] };
|
|
1343
1300
|
}
|
|
1344
1301
|
|
|
1345
|
-
if (name === "
|
|
1346
|
-
const { chat_id, doc_id } = (args || {}) as { chat_id?: string; doc_id?: string };
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1302
|
+
if (name === "load_skill") {
|
|
1303
|
+
const { skill_id, chat_id, doc_id } = (args || {}) as { skill_id?: string; chat_id?: string; doc_id?: string };
|
|
1304
|
+
// CHANNEL skill: chat_id + doc_id (full frontmatter/metadata handling).
|
|
1305
|
+
if (chat_id && doc_id) {
|
|
1306
|
+
try {
|
|
1307
|
+
const r = await fetch(`${REST_URL}/api/channels/${encodeURIComponent(chat_id)}/docs/${encodeURIComponent(doc_id)}`, {
|
|
1308
|
+
headers: { "Authorization": `Bearer ${TOKEN}` },
|
|
1309
|
+
});
|
|
1310
|
+
const text = await r.text();
|
|
1311
|
+
if (!r.ok) {
|
|
1312
|
+
return { content: [{ type: "text", text: `load_skill (channel) failed (${r.status}): ${text.slice(0, 200)}` }] };
|
|
1313
|
+
}
|
|
1314
|
+
const doc = JSON.parse(text);
|
|
1315
|
+
const body = doc?.body_markdown ?? doc?.bodyMarkdown ?? "";
|
|
1316
|
+
const title = doc?.title || doc_id;
|
|
1317
|
+
const kind = doc?.kind || "unknown";
|
|
1318
|
+
const level = doc?.level ?? "?";
|
|
1319
|
+
const parsed = parseSkillFrontmatter(String(body));
|
|
1320
|
+
const metadata = { ...(parsed.metadata || {}), ...(doc?.skill_meta || doc?.skillMeta || {}) };
|
|
1321
|
+
const metaLines = [
|
|
1322
|
+
metadata.name ? `name: ${metadata.name}` : null,
|
|
1323
|
+
metadata.description ? `description: ${metadata.description}` : null,
|
|
1324
|
+
metadata.trigger ? `trigger: ${metadata.trigger}` : null,
|
|
1325
|
+
(metadata.argument_hint ?? metadata.argumentHint) ? `argument-hint: ${metadata.argument_hint ?? metadata.argumentHint}` : null,
|
|
1326
|
+
].filter(Boolean).join("\n");
|
|
1327
|
+
if (!String(kind).toLowerCase().includes("skill") && !String(doc_id).toLowerCase().includes("skill")) {
|
|
1328
|
+
return { content: [{ type: "text", text: `Loaded channel doc "${doc_id}" as requested, but it is not marked kind=skill.\n\n# ${title}\n\n${parsed.body}` }] };
|
|
1329
|
+
}
|
|
1370
1330
|
return {
|
|
1371
1331
|
content: [{
|
|
1372
1332
|
type: "text",
|
|
1373
|
-
text:
|
|
1333
|
+
text: [
|
|
1334
|
+
`Channel-specific skill loaded from ${chat_id}/${doc_id} (L${level}, kind=${kind}).`,
|
|
1335
|
+
metaLines ? `\nMetadata:\n${metaLines}` : "",
|
|
1336
|
+
`\n# ${title}\n\n${parsed.body}`,
|
|
1337
|
+
].join("\n"),
|
|
1374
1338
|
}],
|
|
1375
1339
|
};
|
|
1340
|
+
} catch (e: any) {
|
|
1341
|
+
return { content: [{ type: "text", text: `load_skill (channel) network/parse error: ${String(e?.message || e).slice(0, 120)}` }] };
|
|
1376
1342
|
}
|
|
1377
|
-
return {
|
|
1378
|
-
content: [{
|
|
1379
|
-
type: "text",
|
|
1380
|
-
text: [
|
|
1381
|
-
`Channel-specific skill loaded from ${chat_id}/${doc_id} (L${level}, kind=${kind}).`,
|
|
1382
|
-
metaLines ? `\nMetadata:\n${metaLines}` : "",
|
|
1383
|
-
`\n# ${title}\n\n${parsed.body}`,
|
|
1384
|
-
].join("\n"),
|
|
1385
|
-
}],
|
|
1386
|
-
};
|
|
1387
|
-
} catch (e: any) {
|
|
1388
|
-
return { content: [{ type: "text", text: `load_channel_skill network/parse error: ${String(e?.message || e).slice(0, 120)}` }] };
|
|
1389
1343
|
}
|
|
1344
|
+
// GLOBAL skill: skill_id (default workspace-driven-eng).
|
|
1345
|
+
const id = skill_id || DEFAULT_GLOBAL_SKILL_ID;
|
|
1346
|
+
const skill = GLOBAL_SKILLS[id];
|
|
1347
|
+
if (!skill) {
|
|
1348
|
+
return { content: [{ type: "text", text: `Unknown global skill: ${id}` }] };
|
|
1349
|
+
}
|
|
1350
|
+
return { content: [{ type: "text", text: `${skill.body}\n\nLoaded as global skill "${id}".` }] };
|
|
1390
1351
|
}
|
|
1391
1352
|
|
|
1392
1353
|
if (name === "list_tool_groups") {
|
|
@@ -1752,14 +1713,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1752
1713
|
const open = (okrData?.tasks || []).filter((t: any) => t.objective_id === o.id && t.status !== "done").length;
|
|
1753
1714
|
return { id: o.id, title: o.title, open_tasks: open };
|
|
1754
1715
|
});
|
|
1716
|
+
// Loadable extended tool groups — surfaced here so entering a room hands
|
|
1717
|
+
// you the full capability menu (not just skills). Without this an agent has
|
|
1718
|
+
// to already KNOW to call list_tool_groups; now "what can I do here / how to
|
|
1719
|
+
// load X" is answered on entry. Compact (name+summary+count+loaded); the
|
|
1720
|
+
// actual tools appear when you load_tool_group(name).
|
|
1721
|
+
const toolGroups = TOOL_GROUPS.map((g) => ({
|
|
1722
|
+
name: g.name,
|
|
1723
|
+
summary: g.summary,
|
|
1724
|
+
tool_count: g.tools.length,
|
|
1725
|
+
loaded: loadedToolGroups.has(g.name),
|
|
1726
|
+
}));
|
|
1755
1727
|
return JSON.stringify({
|
|
1756
1728
|
channel: chatId,
|
|
1757
1729
|
members: { total: memberIds.length, online },
|
|
1758
1730
|
okr_objectives: objectives,
|
|
1759
1731
|
skills,
|
|
1760
1732
|
docs,
|
|
1733
|
+
tool_groups: toolGroups,
|
|
1761
1734
|
tips: [
|
|
1762
|
-
"
|
|
1735
|
+
"load_tool_group(name) reveals an extended group's tools (see tool_groups above; loaded:false = not yet active)",
|
|
1736
|
+
"load_skill(chat_id, doc_id) activates a channel skill; list_skills(chat_id) lists them",
|
|
1763
1737
|
"okr_list / get_history for deeper context",
|
|
1764
1738
|
"/loop <interval> <prompt> works in DMs (okr: prefix = wake mode)",
|
|
1765
1739
|
],
|