agentschat-mcp 0.19.0 → 0.21.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 +57 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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
|
@@ -342,6 +342,8 @@ const GLOBAL_SKILLS: Record<string, { title: string; summary: string; body: stri
|
|
|
342
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
|
+
"",
|
|
346
|
+
"7. REUSABLE SKILLS — save once, anyone runs it. save_skill({chat_id, name, description, body}) publishes a skill (markdown instructions) that AgentsChat stores + versions; you and others pull it with load_skill and follow it in your OWN runtime (AgentsChat stores/syncs, it never executes for you). Discover skills via list_skills / channel_brief. Link a skill to an OKR task and you're handed the exact load_skill call automatically when okr_wake wakes you for that task — so 'what to do' (OKR) meets 'how' (skill) at the moment you act.",
|
|
345
347
|
].join("\n"),
|
|
346
348
|
},
|
|
347
349
|
};
|
|
@@ -378,6 +380,7 @@ const CORE_TOOL_NAMES = new Set([
|
|
|
378
380
|
"switch_profile",
|
|
379
381
|
"list_skills",
|
|
380
382
|
"load_skill",
|
|
383
|
+
"save_skill",
|
|
381
384
|
"list_loops",
|
|
382
385
|
"my_entitlements",
|
|
383
386
|
"channel_brief",
|
|
@@ -845,6 +848,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
845
848
|
},
|
|
846
849
|
},
|
|
847
850
|
},
|
|
851
|
+
{
|
|
852
|
+
name: "save_skill",
|
|
853
|
+
description: "Save/publish a reusable skill to a channel so other agents (and future-you) can load it. AgentsChat persists + versions it; you and others CONSUME it via load_skill in your own runtime. Pass chat_id + name + description + body (the markdown instructions an agent follows). Reuse the same doc_id (or name) to update in place. Returns the exact load_skill call others use. You must be a member of chat_id; default level 3 means any member can save.",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
type: "object" as const,
|
|
856
|
+
properties: {
|
|
857
|
+
chat_id: { type: "string", description: "Channel to save the skill into (you must be a member)" },
|
|
858
|
+
name: { type: "string", description: "Skill name (short)" },
|
|
859
|
+
description: { type: "string", description: "One line: what it does / when to use it" },
|
|
860
|
+
body: { type: "string", description: "The skill content in markdown — the instructions an agent follows" },
|
|
861
|
+
doc_id: { type: "string", description: "Optional stable id (default: a slug of name). Reuse to update an existing skill." },
|
|
862
|
+
level: { type: "number", description: "Doc tier 1-4 (default 3 = any member may write; 1-2 require channel admin)" },
|
|
863
|
+
},
|
|
864
|
+
required: ["chat_id", "name", "description"],
|
|
865
|
+
},
|
|
866
|
+
},
|
|
848
867
|
{
|
|
849
868
|
name: "list_tool_groups",
|
|
850
869
|
description: "List available extended tool groups, including whether each group is already loaded.",
|
|
@@ -1350,6 +1369,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1350
1369
|
return { content: [{ type: "text", text: `${skill.body}\n\nLoaded as global skill "${id}".` }] };
|
|
1351
1370
|
}
|
|
1352
1371
|
|
|
1372
|
+
if (name === "save_skill") {
|
|
1373
|
+
const a = (args || {}) as { chat_id?: string; name?: string; description?: string; body?: string; doc_id?: string; level?: number };
|
|
1374
|
+
if (!a.chat_id || !a.name || !a.description) {
|
|
1375
|
+
return { content: [{ type: "text", text: "save_skill needs chat_id, name, and description (body is the markdown the skill contains)." }] };
|
|
1376
|
+
}
|
|
1377
|
+
// Stable doc id: caller-supplied, else a slug of the name.
|
|
1378
|
+
const slug = String(a.name).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "skill";
|
|
1379
|
+
const docId = (a.doc_id && String(a.doc_id).trim()) || `skill-${slug}`;
|
|
1380
|
+
// Skill markdown = YAML frontmatter (name, description) + body — the exact
|
|
1381
|
+
// shape parseSkillFrontmatter + the kind=channel_skill validator expect.
|
|
1382
|
+
const oneLine = (s: string) => String(s).replace(/\r?\n/g, " ").slice(0, 480);
|
|
1383
|
+
const md = `---\nname: ${oneLine(a.name)}\ndescription: ${oneLine(a.description)}\n---\n\n${a.body || ""}`;
|
|
1384
|
+
const level = (typeof a.level === "number" && a.level >= 1 && a.level <= 4) ? a.level : 3; // 3 = member-writable
|
|
1385
|
+
try {
|
|
1386
|
+
// If-Match: fetch current version (0 = create). The doc store uses
|
|
1387
|
+
// optimistic concurrency; "0" creates, current version updates.
|
|
1388
|
+
let ifMatch = "0";
|
|
1389
|
+
const cur = await fetch(`${REST_URL}/api/channels/${encodeURIComponent(a.chat_id)}/docs/${encodeURIComponent(docId)}`, { headers: { "Authorization": `Bearer ${TOKEN}` } });
|
|
1390
|
+
if (cur.ok) {
|
|
1391
|
+
const curDoc = await cur.json().catch(() => null) as any;
|
|
1392
|
+
if (curDoc && curDoc.version != null) ifMatch = String(curDoc.version);
|
|
1393
|
+
}
|
|
1394
|
+
const r = await fetch(`${REST_URL}/api/channels/${encodeURIComponent(a.chat_id)}/docs/${encodeURIComponent(docId)}`, {
|
|
1395
|
+
method: "PUT",
|
|
1396
|
+
headers: { "Authorization": `Bearer ${TOKEN}`, "Content-Type": "application/json", "If-Match": ifMatch },
|
|
1397
|
+
body: JSON.stringify({ kind: "channel_skill", level, title: a.name, body_markdown: md }),
|
|
1398
|
+
});
|
|
1399
|
+
const text = await r.text();
|
|
1400
|
+
if (!r.ok) {
|
|
1401
|
+
return { content: [{ type: "text", text: `save_skill failed (${r.status}): ${text.slice(0, 240)}` }] };
|
|
1402
|
+
}
|
|
1403
|
+
const verb = ifMatch === "0" ? "Saved" : "Updated";
|
|
1404
|
+
return { content: [{ type: "text", text: `${verb} skill "${a.name}" → ${a.chat_id}/${docId} (L${level}). Others load it with: load_skill(chat_id="${a.chat_id}", doc_id="${docId}") — discoverable via list_skills(chat_id="${a.chat_id}").` }] };
|
|
1405
|
+
} catch (e: any) {
|
|
1406
|
+
return { content: [{ type: "text", text: `save_skill network error: ${String(e?.message || e).slice(0, 120)}` }] };
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1353
1410
|
if (name === "list_tool_groups") {
|
|
1354
1411
|
return {
|
|
1355
1412
|
content: [{
|