agentschat-mcp 0.29.0 → 0.29.1
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/dist/server.js +4030 -0
- package/package.json +8 -4
- package/src/cli.mjs +20 -0
- package/src/server.ts +27 -23
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentschat-mcp",
|
|
3
3
|
"mcpName": "io.github.swswordholy-tech/agentschat-mcp",
|
|
4
|
-
"version": "0.29.
|
|
4
|
+
"version": "0.29.1",
|
|
5
5
|
"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.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"agentschat-mcp": "src/
|
|
9
|
-
"agentchat-mcp": "src/
|
|
8
|
+
"agentschat-mcp": "src/cli.mjs",
|
|
9
|
+
"agentchat-mcp": "src/cli.mjs"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
12
|
"bun": ">=1.0.0"
|
|
@@ -16,8 +16,10 @@
|
|
|
16
16
|
"dev": "bun --watch src/server.ts",
|
|
17
17
|
"test": "bun test",
|
|
18
18
|
"typecheck": "tsc --noEmit",
|
|
19
|
+
"build": "bun scripts/build.mjs",
|
|
19
20
|
"check:version": "bun scripts/check-version-sync.mjs",
|
|
20
|
-
"verify": "bun scripts/check-version-sync.mjs && tsc --noEmit && bun test"
|
|
21
|
+
"verify": "bun run build && bun scripts/check-version-sync.mjs && tsc --noEmit && bun test",
|
|
22
|
+
"prepublishOnly": "bun run build"
|
|
21
23
|
},
|
|
22
24
|
"keywords": [
|
|
23
25
|
"agentchat",
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
"typescript": "^5.9.3"
|
|
53
55
|
},
|
|
54
56
|
"files": [
|
|
57
|
+
"src/cli.mjs",
|
|
55
58
|
"src/server.ts",
|
|
56
59
|
"src/heartbeat.ts",
|
|
57
60
|
"src/redact.ts",
|
|
@@ -60,6 +63,7 @@
|
|
|
60
63
|
"src/reconnect.ts",
|
|
61
64
|
"src/timestamps.ts",
|
|
62
65
|
"src/argcheck.ts",
|
|
66
|
+
"dist/server.js",
|
|
63
67
|
"README.md"
|
|
64
68
|
]
|
|
65
69
|
}
|
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Universal entry point for the AgentsChat MCP server.
|
|
3
|
+
//
|
|
4
|
+
// One bin, two runtimes — bunx and npx both land here:
|
|
5
|
+
// • Bun (bunx agentschat-mcp / claude mcp add ... bunx ...): run the TypeScript
|
|
6
|
+
// source directly. No build step, full fidelity — this is the path Claude
|
|
7
|
+
// Code users take, and the runtime the plugin is designed for.
|
|
8
|
+
// • Node (npx agentschat-mcp, or registry/directory introspection like Glama
|
|
9
|
+
// that installs + runs over stdio): run the prebuilt Node bundle in dist/.
|
|
10
|
+
// Bun.* file I/O was replaced with node:fs so the exact same logic runs on
|
|
11
|
+
// both runtimes; the bundle inlines local modules + the JSON version import.
|
|
12
|
+
//
|
|
13
|
+
// bunx ignores this shebang and runs the file under Bun (so `typeof Bun` is
|
|
14
|
+
// defined); npx honors the shebang and runs it under Node. CLI args in argv are
|
|
15
|
+
// inherited by the imported entrypoint, so --name/--profile/etc. work unchanged.
|
|
16
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
17
|
+
await import("./server.ts");
|
|
18
|
+
} else {
|
|
19
|
+
await import("../dist/server.js");
|
|
20
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -4,16 +4,14 @@
|
|
|
4
4
|
* 像 weixin 插件一样:WebSocket 消息 → MCP channel notification → Claude Code 对话
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
// Runtime
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
7
|
+
// Runtime: Bun is the primary/first-class runtime (`bunx agentschat-mcp` runs
|
|
8
|
+
// this .ts source directly, no build step). It ALSO runs on plain Node via the
|
|
9
|
+
// prebuilt bundle in dist/ (see src/cli.mjs launcher) so directory/registry
|
|
10
|
+
// tooling that introspects over stdio with Node/npx (e.g. Glama) can install
|
|
11
|
+
// and list tools. All Bun-specific I/O was replaced with node:fs; the WebSocket
|
|
12
|
+
// connect is wrapped in try/catch, so a missing global WebSocket (older Node)
|
|
13
|
+
// degrades to a reconnect log rather than crashing stdio/tools-list. Node 22+
|
|
14
|
+
// (global WebSocket/fetch/FormData) gets full functionality.
|
|
17
15
|
|
|
18
16
|
// ── Proxy bypass ──────────────────────────────────────────────────
|
|
19
17
|
// MCP subprocess inherits the parent's HTTP_PROXY/HTTPS_PROXY which
|
|
@@ -1610,13 +1608,14 @@ function mimeFromPath(p: string): string {
|
|
|
1610
1608
|
}
|
|
1611
1609
|
// Upload a local file via multipart POST /api/upload → { url, mime, size }.
|
|
1612
1610
|
async function uploadLocalFile(path: string): Promise<{ url: string; mime: string; size: number }> {
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const
|
|
1611
|
+
// node:fs (not Bun.file) so this runs on plain Node too — Bun supports node:fs
|
|
1612
|
+
// identically, and the Node path is what registry introspection (Glama) uses.
|
|
1613
|
+
if (!existsSync(path)) throw new Error(`file not found: ${path}`);
|
|
1614
|
+
const mime = mimeFromPath(path);
|
|
1615
|
+
const buf = readFileSync(path);
|
|
1617
1616
|
const name = path.split("/").pop() || "upload";
|
|
1618
1617
|
const form = new FormData();
|
|
1619
|
-
form.append("file", new Blob([
|
|
1618
|
+
form.append("file", new Blob([new Uint8Array(buf)], { type: mime }), name);
|
|
1620
1619
|
// No Content-Type header → fetch derives the multipart boundary itself.
|
|
1621
1620
|
const r = await apiFetch(`${REST_URL}/api/upload`, { method: "POST", headers: { "Authorization": `Bearer ${TOKEN}` }, body: form });
|
|
1622
1621
|
const text = await r.text();
|
|
@@ -1624,7 +1623,7 @@ async function uploadLocalFile(path: string): Promise<{ url: string; mime: strin
|
|
|
1624
1623
|
let data: any;
|
|
1625
1624
|
try { data = JSON.parse(text); } catch { throw new Error(`upload returned non-JSON: ${text.slice(0, 120)}`); }
|
|
1626
1625
|
if (!data?.url) throw new Error(`upload response missing url: ${text.slice(0, 120)}`);
|
|
1627
|
-
return { url: data.url as string, mime: (data.type as string) || mime, size: (data.size as number) ??
|
|
1626
|
+
return { url: data.url as string, mime: (data.type as string) || mime, size: (data.size as number) ?? buf.byteLength };
|
|
1628
1627
|
}
|
|
1629
1628
|
// Post a message carrying exactly one media attachment (image|audio).
|
|
1630
1629
|
async function sendMediaMessage(kind: "image" | "audio", args: any): Promise<{ content: any[]; isError?: boolean }> {
|
|
@@ -1981,15 +1980,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1981
1980
|
if (!meta) return { content: [{ type: "text", text: `sync_skill: personal skill "${a.name}" not found (save it with save_skill — no chat_id).` }], isError: true };
|
|
1982
1981
|
const currentVersion = Number(meta.version ?? 0);
|
|
1983
1982
|
let cachedVersion: number | null = null;
|
|
1984
|
-
try { cachedVersion = Number(JSON.parse(
|
|
1983
|
+
try { cachedVersion = Number(JSON.parse(readFileSync(pMeta, "utf8")).version); } catch {}
|
|
1985
1984
|
if (cachedVersion !== null && cachedVersion === currentVersion) {
|
|
1986
1985
|
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.` }] };
|
|
1987
1986
|
}
|
|
1988
1987
|
const bodyR = await apiFetch(`${REST_URL}/api/skills/${encodeURIComponent(a.name)}`, { headers: { "Authorization": `Bearer ${TOKEN}` } });
|
|
1989
1988
|
if (!bodyR.ok) return { content: [{ type: "text", text: `sync_skill (personal): body fetch failed (${bodyR.status})` }], isError: true };
|
|
1990
1989
|
const doc = JSON.parse(await bodyR.text());
|
|
1991
|
-
|
|
1992
|
-
|
|
1990
|
+
// node:fs (not Bun.write) — runs on plain Node too. Bun.write auto-creates
|
|
1991
|
+
// parent dirs; writeFileSync does not, so mkdir the cache dir first.
|
|
1992
|
+
mkdirSync(dirname(pMd), { recursive: true });
|
|
1993
|
+
writeFileSync(pMd, String(doc?.body_markdown ?? ""));
|
|
1994
|
+
writeFileSync(pMeta, JSON.stringify({ version: currentVersion, name: a.name, syncedAt: new Date().toISOString() }));
|
|
1993
1995
|
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.` }] };
|
|
1994
1996
|
} catch (e: any) {
|
|
1995
1997
|
return { content: [{ type: "text", text: `sync_skill (personal) error: ${String(e?.message || e).slice(0, 140)}` }], isError: true };
|
|
@@ -2013,7 +2015,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2013
2015
|
const currentVersion = Number(meta.version ?? 0);
|
|
2014
2016
|
// 2. Local cache check — skip the body fetch if we already have this version.
|
|
2015
2017
|
let cachedVersion: number | null = null;
|
|
2016
|
-
try { cachedVersion = Number(JSON.parse(
|
|
2018
|
+
try { cachedVersion = Number(JSON.parse(readFileSync(metaPath, "utf8")).version); } catch {}
|
|
2017
2019
|
if (cachedVersion !== null && cachedVersion === currentVersion) {
|
|
2018
2020
|
return { content: [{ type: "text", text: `up-to-date: "${a.doc_id}" v${currentVersion} already at ${mdPath} — no download. Read that file to run it.` }] };
|
|
2019
2021
|
}
|
|
@@ -2022,9 +2024,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2022
2024
|
if (!docR.ok) return { content: [{ type: "text", text: `sync_skill: fetch body failed (${docR.status})` }], isError: true };
|
|
2023
2025
|
const doc = JSON.parse(await docR.text());
|
|
2024
2026
|
const body = String(doc?.body_markdown ?? doc?.bodyMarkdown ?? "");
|
|
2025
|
-
// 4. Write the local mirror + version sidecar.
|
|
2026
|
-
|
|
2027
|
-
|
|
2027
|
+
// 4. Write the local mirror + version sidecar (node:fs, not Bun.write, for
|
|
2028
|
+
// Node compat; mkdir the cache dir first since writeFileSync won't auto-create).
|
|
2029
|
+
mkdirSync(dirname(mdPath), { recursive: true });
|
|
2030
|
+
writeFileSync(mdPath, body);
|
|
2031
|
+
writeFileSync(metaPath, JSON.stringify({ version: currentVersion, title: doc?.title, doc_id: a.doc_id, chat_id: a.chat_id, syncedAt: new Date().toISOString() }));
|
|
2028
2032
|
const was = cachedVersion === null ? "missing" : `stale v${cachedVersion}`;
|
|
2029
2033
|
return { content: [{ type: "text", text: `synced "${doc?.title || a.doc_id}" v${currentVersion} → ${mdPath} (was ${was}). Read that file to run it in your runtime.` }] };
|
|
2030
2034
|
} catch (e: any) {
|