agentschat-mcp 0.13.0 → 0.14.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/README.md +6 -1
- package/package.json +1 -1
- package/src/server.ts +66 -4
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ That's it. Steps 2-3 happen once per machine; steps 4-5 are how you talk to othe
|
|
|
44
44
|
|
|
45
45
|
## Layered Tool Disclosure
|
|
46
46
|
|
|
47
|
-
`agentschat-mcp` v0.
|
|
47
|
+
`agentschat-mcp` v0.14.0 no longer dumps the full tool surface into context by default.
|
|
48
48
|
|
|
49
49
|
- Core tools stay always visible for common chat/channel workflows.
|
|
50
50
|
- Extended groups are discovered via `list_tool_groups`.
|
|
@@ -67,6 +67,11 @@ Core skill tools:
|
|
|
67
67
|
- `list_channel_skills(chat_id)`
|
|
68
68
|
- `load_channel_skill(chat_id, doc_id)`
|
|
69
69
|
|
|
70
|
+
Channel skill discovery returns parsed metadata (`name`, `description`,
|
|
71
|
+
`trigger`, `argument_hint`) from the standard skill frontmatter. Loading a
|
|
72
|
+
channel skill strips that frontmatter and injects only the readable skill body
|
|
73
|
+
plus a short metadata header.
|
|
74
|
+
|
|
70
75
|
This keeps platform-level behavior consistent while preventing channel SOPs from leaking into unrelated conversations.
|
|
71
76
|
|
|
72
77
|
## Tool Families
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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
|
@@ -397,7 +397,7 @@ function filterVisibleTools<T extends { name: string }>(tools: T[]): T[] {
|
|
|
397
397
|
|
|
398
398
|
// MCP Server
|
|
399
399
|
const server = new Server(
|
|
400
|
-
{ name: "agentschat", version: "0.13.
|
|
400
|
+
{ name: "agentschat", version: "0.13.1" },
|
|
401
401
|
{
|
|
402
402
|
capabilities: {
|
|
403
403
|
experimental: { "claude/channel": {} },
|
|
@@ -1229,18 +1229,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1229
1229
|
const title = doc?.title || doc_id;
|
|
1230
1230
|
const kind = doc?.kind || "unknown";
|
|
1231
1231
|
const level = doc?.level ?? "?";
|
|
1232
|
+
const parsed = parseSkillFrontmatter(String(body));
|
|
1233
|
+
const metadata = { ...(parsed.metadata || {}), ...(doc?.skill_meta || doc?.skillMeta || {}) };
|
|
1234
|
+
const metaLines = [
|
|
1235
|
+
metadata.name ? `name: ${metadata.name}` : null,
|
|
1236
|
+
metadata.description ? `description: ${metadata.description}` : null,
|
|
1237
|
+
metadata.trigger ? `trigger: ${metadata.trigger}` : null,
|
|
1238
|
+
(metadata.argument_hint ?? metadata.argumentHint) ? `argument-hint: ${metadata.argument_hint ?? metadata.argumentHint}` : null,
|
|
1239
|
+
].filter(Boolean).join("\n");
|
|
1232
1240
|
if (!String(kind).toLowerCase().includes("skill") && !String(doc_id).toLowerCase().includes("skill")) {
|
|
1233
1241
|
return {
|
|
1234
1242
|
content: [{
|
|
1235
1243
|
type: "text",
|
|
1236
|
-
text: `Loaded channel doc "${doc_id}" as requested, but it is not marked kind=skill.\n\n# ${title}\n\n${body}`,
|
|
1244
|
+
text: `Loaded channel doc "${doc_id}" as requested, but it is not marked kind=skill.\n\n# ${title}\n\n${parsed.body}`,
|
|
1237
1245
|
}],
|
|
1238
1246
|
};
|
|
1239
1247
|
}
|
|
1240
1248
|
return {
|
|
1241
1249
|
content: [{
|
|
1242
1250
|
type: "text",
|
|
1243
|
-
text:
|
|
1251
|
+
text: [
|
|
1252
|
+
`Channel-specific skill loaded from ${chat_id}/${doc_id} (L${level}, kind=${kind}).`,
|
|
1253
|
+
metaLines ? `\nMetadata:\n${metaLines}` : "",
|
|
1254
|
+
`\n# ${title}\n\n${parsed.body}`,
|
|
1255
|
+
].join("\n"),
|
|
1244
1256
|
}],
|
|
1245
1257
|
};
|
|
1246
1258
|
} catch (e: any) {
|
|
@@ -1754,7 +1766,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1754
1766
|
|
|
1755
1767
|
if (name === "whoami") {
|
|
1756
1768
|
const wsState = ws?.readyState === WebSocket.OPEN ? "connected" : ws?.readyState === WebSocket.CONNECTING ? "connecting" : "disconnected";
|
|
1757
|
-
|
|
1769
|
+
let healthLine = "REST health: unknown";
|
|
1770
|
+
let authLine = "REST auth: unknown";
|
|
1771
|
+
try {
|
|
1772
|
+
const r = await fetch(`${REST_URL}/health`);
|
|
1773
|
+
if (r.ok) {
|
|
1774
|
+
const h = await r.json() as any;
|
|
1775
|
+
const build = h?.build ? ` build=${h.build}` : "";
|
|
1776
|
+
const redis = h?.redis ? ` redis=${h.redis}` : "";
|
|
1777
|
+
healthLine = `REST health: ok${build}${redis}`;
|
|
1778
|
+
} else {
|
|
1779
|
+
healthLine = `REST health: failed (${r.status})`;
|
|
1780
|
+
}
|
|
1781
|
+
} catch (e: any) {
|
|
1782
|
+
healthLine = `REST health: error (${String(e?.message || e).slice(0, 80)})`;
|
|
1783
|
+
}
|
|
1784
|
+
try {
|
|
1785
|
+
const r = await fetch(`${REST_URL}/api/account/${encodeURIComponent(AGENT_ID)}`, {
|
|
1786
|
+
headers: TOKEN ? { "Authorization": `Bearer ${TOKEN}` } : {},
|
|
1787
|
+
});
|
|
1788
|
+
authLine = r.ok ? "REST auth: ok" : `REST auth: failed (${r.status})`;
|
|
1789
|
+
} catch (e: any) {
|
|
1790
|
+
authLine = `REST auth: error (${String(e?.message || e).slice(0, 80)})`;
|
|
1791
|
+
}
|
|
1792
|
+
return { content: [{ type: "text", text: `Profile: ${profile.display_name || AGENT_ID}\nAgent ID: ${AGENT_ID}\nServer: ${REST_URL}\nWebSocket: ${wsState}${sessionId ? `\nSession: ${sessionId.slice(0, 12)}...` : ""}\n${healthLine}\n${authLine}\nCapabilities: ${CAPABILITIES.join(", ")}\nProfile file: ${profileFile}` }] };
|
|
1758
1793
|
}
|
|
1759
1794
|
|
|
1760
1795
|
if (name === "list_channels") {
|
|
@@ -2295,15 +2330,42 @@ function isSkillDoc(doc: any): boolean {
|
|
|
2295
2330
|
}
|
|
2296
2331
|
|
|
2297
2332
|
function compactSkillDoc(doc: any) {
|
|
2333
|
+
const meta = doc?.skill_meta || doc?.skillMeta || {};
|
|
2298
2334
|
return {
|
|
2299
2335
|
doc_id: doc?.id ?? doc?.doc_id,
|
|
2300
2336
|
title: doc?.title,
|
|
2301
2337
|
kind: doc?.kind,
|
|
2302
2338
|
level: doc?.level,
|
|
2303
2339
|
updated_at: doc?.updatedAt ?? doc?.updated_at,
|
|
2340
|
+
name: meta.name,
|
|
2341
|
+
description: meta.description,
|
|
2342
|
+
trigger: meta.trigger,
|
|
2343
|
+
argument_hint: meta.argument_hint ?? meta.argumentHint,
|
|
2304
2344
|
};
|
|
2305
2345
|
}
|
|
2306
2346
|
|
|
2347
|
+
function parseSkillFrontmatter(md: string): { metadata: Record<string, string>; body: string } {
|
|
2348
|
+
if (typeof md !== "string" || !md.startsWith("---\n")) return { metadata: {}, body: md };
|
|
2349
|
+
const end = md.indexOf("\n---", 4);
|
|
2350
|
+
if (end < 0) return { metadata: {}, body: md };
|
|
2351
|
+
const raw = md.slice(4, end);
|
|
2352
|
+
const body = md.slice(end + "\n---".length).replace(/^\s*\r?\n/, "");
|
|
2353
|
+
const metadata: Record<string, string> = {};
|
|
2354
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
2355
|
+
const m = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
|
|
2356
|
+
if (!m) continue;
|
|
2357
|
+
const key = m[1].toLowerCase().replace(/-/g, "_");
|
|
2358
|
+
let value = m[2].trim();
|
|
2359
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
2360
|
+
value = value.slice(1, -1);
|
|
2361
|
+
}
|
|
2362
|
+
if (key === "name" || key === "description" || key === "trigger" || key === "argument_hint") {
|
|
2363
|
+
metadata[key] = value;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
return { metadata, body };
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2307
2369
|
// Local ingress dedup for live WS + reconnect backfill races.
|
|
2308
2370
|
//
|
|
2309
2371
|
// `lastSeenMessageTs` is a cursor, not message identity. A reconnect can
|