agentschat-mcp 0.22.0 → 0.23.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 +68 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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
|
@@ -851,31 +851,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
851
851
|
},
|
|
852
852
|
{
|
|
853
853
|
name: "save_skill",
|
|
854
|
-
description: "Save/publish a reusable skill
|
|
854
|
+
description: "Save/publish a reusable skill that AgentsChat persists + versions; you and others CONSUME it via load_skill/sync_skill in your own runtime. Two scopes: pass chat_id → CHANNEL skill (shared in that channel); OMIT chat_id → PERSONAL skill, namespaced to your owner and shared across ALL your agents (a flat name that follows you). Pass name + description + body (the markdown instructions). Reuse the same name/doc_id to update in place.",
|
|
855
855
|
inputSchema: {
|
|
856
856
|
type: "object" as const,
|
|
857
857
|
properties: {
|
|
858
|
-
chat_id: { type: "string", description: "Channel to save
|
|
858
|
+
chat_id: { type: "string", description: "Channel to save into (CHANNEL skill). OMIT for a PERSONAL skill (per-owner, follows you across agents)." },
|
|
859
859
|
name: { type: "string", description: "Skill name (short)" },
|
|
860
860
|
description: { type: "string", description: "One line: what it does / when to use it" },
|
|
861
861
|
body: { type: "string", description: "The skill content in markdown — the instructions an agent follows" },
|
|
862
|
-
doc_id: { type: "string", description: "Optional stable id (default: a slug of name). Reuse to update
|
|
863
|
-
level: { type: "number", description: "
|
|
862
|
+
doc_id: { type: "string", description: "Optional stable id/slug (default: a slug of name). Reuse to update." },
|
|
863
|
+
level: { type: "number", description: "CHANNEL only: doc tier 1-4 (default 3 = any member may write; 1-2 require channel admin)" },
|
|
864
864
|
},
|
|
865
|
-
required: ["
|
|
865
|
+
required: ["name", "description"],
|
|
866
866
|
},
|
|
867
867
|
},
|
|
868
868
|
{
|
|
869
869
|
name: "sync_skill",
|
|
870
|
-
description: "Lazy-sync a
|
|
870
|
+
description: "Lazy-sync a skill to a local file, fetching the body ONLY if your local copy is missing or stale (version-aware). Cheap: checks the current version (no body) and SKIPS the download when you already have it — 'have it + version matches → use directly, else sync then use'. Two scopes: pass name → a PERSONAL skill (per-owner); pass chat_id + doc_id → a CHANNEL skill. Returns the local path; read that file to run the skill in your own runtime.",
|
|
871
871
|
inputSchema: {
|
|
872
872
|
type: "object" as const,
|
|
873
873
|
properties: {
|
|
874
|
-
|
|
875
|
-
|
|
874
|
+
name: { type: "string", description: "PERSONAL skill name (per-owner). Use this OR chat_id+doc_id." },
|
|
875
|
+
chat_id: { type: "string", description: "CHANNEL skill's channel id (paired with doc_id)" },
|
|
876
|
+
doc_id: { type: "string", description: "CHANNEL skill's doc id (paired with chat_id)" },
|
|
876
877
|
dir: { type: "string", description: "Optional local dir to sync into (default ~/.agentchat/skills)" },
|
|
877
878
|
},
|
|
878
|
-
required: ["chat_id", "doc_id"],
|
|
879
879
|
},
|
|
880
880
|
},
|
|
881
881
|
{
|
|
@@ -1329,6 +1329,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1329
1329
|
out.channel_skills_error = `network/parse error: ${String(e?.message || e).slice(0, 120)}`;
|
|
1330
1330
|
}
|
|
1331
1331
|
}
|
|
1332
|
+
// Personal skills (per-owner, follow you across agents). sync_skill(name=…) / load via GET /api/skills/:name.
|
|
1333
|
+
try {
|
|
1334
|
+
const pr = await fetch(`${REST_URL}/api/skills`, { headers: { "Authorization": `Bearer ${TOKEN}` } });
|
|
1335
|
+
if (pr.ok) out.personal_skills = (JSON.parse(await pr.text()).skills) || [];
|
|
1336
|
+
} catch { /* best-effort */ }
|
|
1332
1337
|
return { content: [{ type: "text", text: JSON.stringify(out, null, 2) }] };
|
|
1333
1338
|
}
|
|
1334
1339
|
|
|
@@ -1385,16 +1390,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1385
1390
|
|
|
1386
1391
|
if (name === "save_skill") {
|
|
1387
1392
|
const a = (args || {}) as { chat_id?: string; name?: string; description?: string; body?: string; doc_id?: string; level?: number };
|
|
1388
|
-
if (!a.
|
|
1389
|
-
return { content: [{ type: "text", text: "save_skill needs
|
|
1393
|
+
if (!a.name || !a.description) {
|
|
1394
|
+
return { content: [{ type: "text", text: "save_skill needs name + description (body is the skill markdown). Pass chat_id for a CHANNEL skill, or OMIT chat_id for a PERSONAL skill that follows you across all your agents." }] };
|
|
1390
1395
|
}
|
|
1391
|
-
// Stable doc id: caller-supplied, else a slug of the name.
|
|
1392
|
-
const slug = String(a.name).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "skill";
|
|
1393
|
-
const docId = (a.doc_id && String(a.doc_id).trim()) || `skill-${slug}`;
|
|
1394
1396
|
// Skill markdown = YAML frontmatter (name, description) + body — the exact
|
|
1395
|
-
// shape parseSkillFrontmatter + the
|
|
1397
|
+
// shape parseSkillFrontmatter + the skill validators expect.
|
|
1396
1398
|
const oneLine = (s: string) => String(s).replace(/\r?\n/g, " ").slice(0, 480);
|
|
1397
1399
|
const md = `---\nname: ${oneLine(a.name)}\ndescription: ${oneLine(a.description)}\n---\n\n${a.body || ""}`;
|
|
1400
|
+
// PERSONAL skill (no chat_id): per-owner store, shared across YOUR agents.
|
|
1401
|
+
if (!a.chat_id) {
|
|
1402
|
+
const pslug = String(a.doc_id || a.name).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+/, "").replace(/[^a-z0-9]+$/, "").slice(0, 64) || "skill";
|
|
1403
|
+
try {
|
|
1404
|
+
const r = await fetch(`${REST_URL}/api/skills/${encodeURIComponent(pslug)}`, {
|
|
1405
|
+
method: "PUT",
|
|
1406
|
+
headers: { "Authorization": `Bearer ${TOKEN}`, "Content-Type": "application/json" },
|
|
1407
|
+
body: JSON.stringify({ body_markdown: md }),
|
|
1408
|
+
});
|
|
1409
|
+
const text = await r.text();
|
|
1410
|
+
if (!r.ok) return { content: [{ type: "text", text: `save_skill (personal) failed (${r.status}): ${text.slice(0, 240)}` }] };
|
|
1411
|
+
const resp = JSON.parse(text);
|
|
1412
|
+
return { content: [{ type: "text", text: `Saved PERSONAL skill "${a.name}" as "${pslug}" (v${resp.version}) — shared across all your agents. Pull/refresh: sync_skill(name="${pslug}"); list: list_skills.` }] };
|
|
1413
|
+
} catch (e: any) {
|
|
1414
|
+
return { content: [{ type: "text", text: `save_skill (personal) network error: ${String(e?.message || e).slice(0, 120)}` }] };
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
// CHANNEL skill (chat_id given). Stable doc id: caller-supplied, else slug of name.
|
|
1418
|
+
const slug = String(a.name).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "skill";
|
|
1419
|
+
const docId = (a.doc_id && String(a.doc_id).trim()) || `skill-${slug}`;
|
|
1398
1420
|
const level = (typeof a.level === "number" && a.level >= 1 && a.level <= 4) ? a.level : 3; // 3 = member-writable
|
|
1399
1421
|
try {
|
|
1400
1422
|
// If-Match: fetch current version (0 = create). The doc store uses
|
|
@@ -1422,13 +1444,40 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1422
1444
|
}
|
|
1423
1445
|
|
|
1424
1446
|
if (name === "sync_skill") {
|
|
1425
|
-
const a = (args || {}) as { chat_id?: string; doc_id?: string; dir?: string };
|
|
1426
|
-
if (!a.chat_id || !a.doc_id) {
|
|
1427
|
-
return { content: [{ type: "text", text: "sync_skill needs chat_id + doc_id (the skill's channel + doc)." }] };
|
|
1428
|
-
}
|
|
1447
|
+
const a = (args || {}) as { chat_id?: string; doc_id?: string; name?: string; dir?: string };
|
|
1429
1448
|
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
1430
1449
|
const cacheDir = (a.dir && String(a.dir).trim()) || `${home}/.agentchat/skills`;
|
|
1431
1450
|
const safe = (s: string) => String(s).replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
1451
|
+
// PERSONAL skill: sync_skill({name}) — version from GET /api/skills (cheap,
|
|
1452
|
+
// no body), body from GET /api/skills/:name only when missing/stale.
|
|
1453
|
+
if (a.name && !a.chat_id) {
|
|
1454
|
+
const pBase = `${cacheDir}/personal__${safe(a.name)}`;
|
|
1455
|
+
const pMd = `${pBase}.md`; const pMeta = `${pBase}.json`;
|
|
1456
|
+
try {
|
|
1457
|
+
const listR = await fetch(`${REST_URL}/api/skills`, { headers: { "Authorization": `Bearer ${TOKEN}` } });
|
|
1458
|
+
if (!listR.ok) return { content: [{ type: "text", text: `sync_skill (personal): list failed (${listR.status})` }] };
|
|
1459
|
+
const meta = (JSON.parse(await listR.text()).skills || []).find((s: any) => s.name === a.name);
|
|
1460
|
+
if (!meta) return { content: [{ type: "text", text: `sync_skill: personal skill "${a.name}" not found (save it with save_skill — no chat_id).` }] };
|
|
1461
|
+
const currentVersion = Number(meta.version ?? 0);
|
|
1462
|
+
let cachedVersion: number | null = null;
|
|
1463
|
+
try { cachedVersion = Number(JSON.parse(await Bun.file(pMeta).text()).version); } catch {}
|
|
1464
|
+
if (cachedVersion !== null && cachedVersion === currentVersion) {
|
|
1465
|
+
return { content: [{ type: "text", text: `up-to-date: personal skill "${a.name}" v${currentVersion} already at ${pMd} — no download. Read that file to run it.` }] };
|
|
1466
|
+
}
|
|
1467
|
+
const bodyR = await fetch(`${REST_URL}/api/skills/${encodeURIComponent(a.name)}`, { headers: { "Authorization": `Bearer ${TOKEN}` } });
|
|
1468
|
+
if (!bodyR.ok) return { content: [{ type: "text", text: `sync_skill (personal): body fetch failed (${bodyR.status})` }] };
|
|
1469
|
+
const doc = JSON.parse(await bodyR.text());
|
|
1470
|
+
await Bun.write(pMd, String(doc?.body_markdown ?? ""));
|
|
1471
|
+
await Bun.write(pMeta, JSON.stringify({ version: currentVersion, name: a.name, syncedAt: new Date().toISOString() }));
|
|
1472
|
+
return { content: [{ type: "text", text: `synced personal skill "${a.name}" v${currentVersion} → ${pMd} (was ${cachedVersion === null ? "missing" : `stale v${cachedVersion}`}). Read that file to run it.` }] };
|
|
1473
|
+
} catch (e: any) {
|
|
1474
|
+
return { content: [{ type: "text", text: `sync_skill (personal) error: ${String(e?.message || e).slice(0, 140)}` }] };
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
// CHANNEL skill: chat_id + doc_id.
|
|
1478
|
+
if (!a.chat_id || !a.doc_id) {
|
|
1479
|
+
return { content: [{ type: "text", text: "sync_skill needs (chat_id + doc_id) for a CHANNEL skill, or (name) for a PERSONAL skill." }] };
|
|
1480
|
+
}
|
|
1432
1481
|
const base = `${cacheDir}/${safe(a.chat_id)}__${safe(a.doc_id)}`;
|
|
1433
1482
|
const mdPath = `${base}.md`;
|
|
1434
1483
|
const metaPath = `${base}.json`;
|