jefrichat-mcp 0.32.0 → 0.33.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/dist/http.js +62 -7
- package/dist/index.js +32 -7
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -60685,7 +60685,7 @@ var JefriClient = class _JefriClient {
|
|
|
60685
60685
|
lastPresence = "online";
|
|
60686
60686
|
// re-announce this on reconnect
|
|
60687
60687
|
clientInfo;
|
|
60688
|
-
//
|
|
60688
|
+
// handshake, re-sent on reconnect
|
|
60689
60689
|
constructor(server) {
|
|
60690
60690
|
this.server = server.replace(/\/$/, "");
|
|
60691
60691
|
}
|
|
@@ -60970,6 +60970,19 @@ var JefriClient = class _JefriClient {
|
|
|
60970
60970
|
history(conversationId, before) {
|
|
60971
60971
|
this.send({ type: "history", conversationId, ...before ? { before } : {} });
|
|
60972
60972
|
}
|
|
60973
|
+
/** Announce (or update) the HOST APP this connector is embedded in — Claude
|
|
60974
|
+
* Code, Codex, ChatGPT, … — typically detected from the MCP `initialize`
|
|
60975
|
+
* handshake AFTER the hub socket is already open. Merges into clientInfo so
|
|
60976
|
+
* reconnects re-send it, and pushes an immediate client_info frame if live. */
|
|
60977
|
+
announceHost(host) {
|
|
60978
|
+
const h = (host ?? "").trim();
|
|
60979
|
+
this.clientInfo = { ...this.clientInfo ?? { name: "jefri-sdk", version: "0" }, host: h };
|
|
60980
|
+
try {
|
|
60981
|
+
if (this.ws?.readyState === this.ws?.OPEN)
|
|
60982
|
+
this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
|
|
60983
|
+
} catch {
|
|
60984
|
+
}
|
|
60985
|
+
}
|
|
60973
60986
|
close() {
|
|
60974
60987
|
this.closing = true;
|
|
60975
60988
|
this.stopPing();
|
|
@@ -61240,6 +61253,23 @@ function createKeyedPool() {
|
|
|
61240
61253
|
};
|
|
61241
61254
|
}
|
|
61242
61255
|
|
|
61256
|
+
// src/hosts.ts
|
|
61257
|
+
function aggregateFreshHosts(sessions2, poolKey, now, freshMs) {
|
|
61258
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
61259
|
+
for (const s of sessions2) if (s.poolKey === poolKey && s.host && now - s.lastActive < freshMs) hosts.add(s.host);
|
|
61260
|
+
return [...hosts].slice(0, 5).join(",").slice(0, 200);
|
|
61261
|
+
}
|
|
61262
|
+
function formatAgentStatus(agent, body) {
|
|
61263
|
+
const dot = body.online ? "\u{1F7E2} online" : "\u26AA offline";
|
|
61264
|
+
const state = body.active ? "active" : "\u{1F6D1} deactivated";
|
|
61265
|
+
const app2 = body.online && body.app ? ` \xB7 app: ${body.app}` : "";
|
|
61266
|
+
let out = `@${agent} \u2014 ${dot}${app2} \xB7 ${state}`;
|
|
61267
|
+
if (body.online && body.multiHost && Array.isArray(body.hosts) && body.hosts.length > 1)
|
|
61268
|
+
out += `
|
|
61269
|
+
\u26A0\uFE0F ${body.hosts.length} apps recently active on this credential \u2014 ${body.hosts.join(", ")}. If you didn't connect all of them, revoke this key (Connected apps) and reconnect.`;
|
|
61270
|
+
return out;
|
|
61271
|
+
}
|
|
61272
|
+
|
|
61243
61273
|
// src/tools.ts
|
|
61244
61274
|
import fs4 from "node:fs";
|
|
61245
61275
|
import np3 from "node:path";
|
|
@@ -61832,7 +61862,7 @@ ${e?.message ?? e}`);
|
|
|
61832
61862
|
"jefri_agent_apps",
|
|
61833
61863
|
{
|
|
61834
61864
|
title: "See where your agents are running",
|
|
61835
|
-
description: `For an agent you OWN, show which app
|
|
61865
|
+
description: `For an agent you OWN, show which app has been RECENTLY ACTIVE on its connection (auto-detected, e.g. "Claude Code" or "Codex", or the label you gave that key), whether it's online, and whether it's active or deactivated \u2014 plus a warning if more than one app is active on the same credential (a shared/copied token). Omit \`agent\` to list all your agents. Requires running this connector as the agent's human OWNER.`,
|
|
61836
61866
|
inputSchema: {
|
|
61837
61867
|
agent: external_exports.string().optional().describe("username of one of YOUR agents; omit to list all of them")
|
|
61838
61868
|
}
|
|
@@ -61841,10 +61871,7 @@ ${e?.message ?? e}`);
|
|
|
61841
61871
|
if (agent) {
|
|
61842
61872
|
const { status, body } = await ownerApi(c, `/api/agents/${encodeURIComponent(agent)}/status`, "GET");
|
|
61843
61873
|
if (status !== 200) return fail(body?.error === "not allowed" ? NOT_OWNER : `Could not read @${agent} (HTTP ${status}).`);
|
|
61844
|
-
|
|
61845
|
-
const state = body.active ? "active" : "\u{1F6D1} deactivated";
|
|
61846
|
-
const app2 = body.online ? ` \xB7 app: ${body.app || "connected app"}` : "";
|
|
61847
|
-
return ok3(`@${agent} \u2014 ${dot}${app2} \xB7 ${state}`);
|
|
61874
|
+
return ok3(formatAgentStatus(agent, body));
|
|
61848
61875
|
}
|
|
61849
61876
|
const mine = (await c.identities()).filter((i) => i.type === "agent" && i.owner === c.identity?.username);
|
|
61850
61877
|
if (!mine.length) return ok3("You don't own any agents (or you're not connected as their owner).");
|
|
@@ -62943,6 +62970,7 @@ var HUB = process.env.JEFRI_SERVER ?? "http://localhost:4000";
|
|
|
62943
62970
|
var PORT = Number(process.env.MCP_HTTP_PORT ?? 4001);
|
|
62944
62971
|
var log = (...a) => console.error("[jefrichat-mcp-http]", ...a);
|
|
62945
62972
|
var sessions = /* @__PURE__ */ new Map();
|
|
62973
|
+
var HOST_FRESH_MS = Number(process.env.MCP_HOST_FRESH_MS ?? 10 * 60 * 1e3);
|
|
62946
62974
|
var hubPool = createKeyedPool();
|
|
62947
62975
|
var SESSION_TTL_MS = Number(process.env.MCP_SESSION_TTL_MS ?? 4 * 60 * 60 * 1e3);
|
|
62948
62976
|
var MAX_SESSIONS = Number(process.env.MCP_MAX_SESSIONS ?? 1500);
|
|
@@ -62971,7 +62999,10 @@ function closeSession(sid, reason) {
|
|
|
62971
62999
|
} catch {
|
|
62972
63000
|
}
|
|
62973
63001
|
const pooled = hubPool.get(s.poolKey);
|
|
62974
|
-
if (pooled
|
|
63002
|
+
if (pooled) {
|
|
63003
|
+
if (detachRef(pooled, sid)) teardownPool(s.poolKey, pooled, "last session gone");
|
|
63004
|
+
else if (s.host) announcePoolHosts(s.poolKey, pooled);
|
|
63005
|
+
}
|
|
62975
63006
|
log(`session ${sid.slice(0, 8)}\u2026 closed (${reason}) (${sessions.size} active)`);
|
|
62976
63007
|
}
|
|
62977
63008
|
function teardownPool(poolKey, pooled, reason) {
|
|
@@ -62983,6 +63014,12 @@ function teardownPool(poolKey, pooled, reason) {
|
|
|
62983
63014
|
}
|
|
62984
63015
|
log(`hub connection ${poolKey.slice(0, 8)}\u2026 closed (${reason})`);
|
|
62985
63016
|
}
|
|
63017
|
+
function announcePoolHosts(poolKey, pooled) {
|
|
63018
|
+
const joined = aggregateFreshHosts(sessions.values(), poolKey, Date.now(), HOST_FRESH_MS);
|
|
63019
|
+
if (joined === pooled.lastHosts) return;
|
|
63020
|
+
pooled.lastHosts = joined;
|
|
63021
|
+
pooled.acp.announceHost(joined);
|
|
63022
|
+
}
|
|
62986
63023
|
function closePool(poolKey, reason) {
|
|
62987
63024
|
const pooled = hubPool.get(poolKey);
|
|
62988
63025
|
if (!pooled) return;
|
|
@@ -63013,6 +63050,13 @@ setInterval(() => {
|
|
|
63013
63050
|
for (const [sid, s] of sessions) {
|
|
63014
63051
|
if (now - s.lastActive > SESSION_TTL_MS) closeSession(sid, "idle");
|
|
63015
63052
|
}
|
|
63053
|
+
const seenPools = /* @__PURE__ */ new Set();
|
|
63054
|
+
for (const s of sessions.values()) {
|
|
63055
|
+
if (seenPools.has(s.poolKey)) continue;
|
|
63056
|
+
seenPools.add(s.poolKey);
|
|
63057
|
+
const pooled = hubPool.get(s.poolKey);
|
|
63058
|
+
if (pooled) announcePoolHosts(s.poolKey, pooled);
|
|
63059
|
+
}
|
|
63016
63060
|
for (const [ip, e] of initHits) {
|
|
63017
63061
|
if (now > e.resetAt) initHits.delete(ip);
|
|
63018
63062
|
}
|
|
@@ -63151,6 +63195,16 @@ app.post("/mcp", async (req, res) => {
|
|
|
63151
63195
|
}
|
|
63152
63196
|
const mcp = new McpServer({ name: "jefrichat", version: "0.1.0" });
|
|
63153
63197
|
registerAcpTools(mcp, { ensureClient: async () => acp, inbox: pooled.inbox, serverUrl: HUB, local: false });
|
|
63198
|
+
let mySid;
|
|
63199
|
+
mcp.server.oninitialized = () => {
|
|
63200
|
+
try {
|
|
63201
|
+
const raw = mcp.server.getClientVersion()?.name || void 0;
|
|
63202
|
+
const s = mySid ? sessions.get(mySid) : void 0;
|
|
63203
|
+
if (s) s.host = raw;
|
|
63204
|
+
announcePoolHosts(poolKey, pooled);
|
|
63205
|
+
} catch {
|
|
63206
|
+
}
|
|
63207
|
+
};
|
|
63154
63208
|
const transport = new StreamableHTTPServerTransport({
|
|
63155
63209
|
sessionIdGenerator: () => randomUUID(),
|
|
63156
63210
|
onsessioninitialized: (sid) => {
|
|
@@ -63188,6 +63242,7 @@ app.post("/mcp", async (req, res) => {
|
|
|
63188
63242
|
return;
|
|
63189
63243
|
}
|
|
63190
63244
|
sessions.set(sid, { transport, acp, lastActive: Date.now(), tokenHash: sha256(token), inflight: 0, poolKey });
|
|
63245
|
+
mySid = sid;
|
|
63191
63246
|
log(`session ${sid.slice(0, 8)}\u2026 open as @${acp.identity.username} (${sessions.size} active, ${pooled.refs.size} on this connection)`);
|
|
63192
63247
|
}
|
|
63193
63248
|
});
|
package/dist/index.js
CHANGED
|
@@ -36672,7 +36672,7 @@ var JefriClient = class _JefriClient {
|
|
|
36672
36672
|
lastPresence = "online";
|
|
36673
36673
|
// re-announce this on reconnect
|
|
36674
36674
|
clientInfo;
|
|
36675
|
-
//
|
|
36675
|
+
// handshake, re-sent on reconnect
|
|
36676
36676
|
constructor(server2) {
|
|
36677
36677
|
this.server = server2.replace(/\/$/, "");
|
|
36678
36678
|
}
|
|
@@ -36957,6 +36957,19 @@ var JefriClient = class _JefriClient {
|
|
|
36957
36957
|
history(conversationId, before) {
|
|
36958
36958
|
this.send({ type: "history", conversationId, ...before ? { before } : {} });
|
|
36959
36959
|
}
|
|
36960
|
+
/** Announce (or update) the HOST APP this connector is embedded in — Claude
|
|
36961
|
+
* Code, Codex, ChatGPT, … — typically detected from the MCP `initialize`
|
|
36962
|
+
* handshake AFTER the hub socket is already open. Merges into clientInfo so
|
|
36963
|
+
* reconnects re-send it, and pushes an immediate client_info frame if live. */
|
|
36964
|
+
announceHost(host) {
|
|
36965
|
+
const h = (host ?? "").trim();
|
|
36966
|
+
this.clientInfo = { ...this.clientInfo ?? { name: "jefri-sdk", version: "0" }, host: h };
|
|
36967
|
+
try {
|
|
36968
|
+
if (this.ws?.readyState === this.ws?.OPEN)
|
|
36969
|
+
this.ws.send(JSON.stringify({ type: "client_info", ...this.clientInfo }));
|
|
36970
|
+
} catch {
|
|
36971
|
+
}
|
|
36972
|
+
}
|
|
36960
36973
|
close() {
|
|
36961
36974
|
this.closing = true;
|
|
36962
36975
|
this.stopPing();
|
|
@@ -37341,6 +37354,18 @@ async function readCapped(stream, maxBytes, deadline) {
|
|
|
37341
37354
|
return Buffer.concat(chunks);
|
|
37342
37355
|
}
|
|
37343
37356
|
|
|
37357
|
+
// src/hosts.ts
|
|
37358
|
+
function formatAgentStatus(agent, body) {
|
|
37359
|
+
const dot = body.online ? "\u{1F7E2} online" : "\u26AA offline";
|
|
37360
|
+
const state = body.active ? "active" : "\u{1F6D1} deactivated";
|
|
37361
|
+
const app = body.online && body.app ? ` \xB7 app: ${body.app}` : "";
|
|
37362
|
+
let out = `@${agent} \u2014 ${dot}${app} \xB7 ${state}`;
|
|
37363
|
+
if (body.online && body.multiHost && Array.isArray(body.hosts) && body.hosts.length > 1)
|
|
37364
|
+
out += `
|
|
37365
|
+
\u26A0\uFE0F ${body.hosts.length} apps recently active on this credential \u2014 ${body.hosts.join(", ")}. If you didn't connect all of them, revoke this key (Connected apps) and reconnect.`;
|
|
37366
|
+
return out;
|
|
37367
|
+
}
|
|
37368
|
+
|
|
37344
37369
|
// src/notify.ts
|
|
37345
37370
|
import cp from "node:child_process";
|
|
37346
37371
|
import fs2 from "node:fs";
|
|
@@ -38454,7 +38479,7 @@ ${e?.message ?? e}`);
|
|
|
38454
38479
|
"jefri_agent_apps",
|
|
38455
38480
|
{
|
|
38456
38481
|
title: "See where your agents are running",
|
|
38457
|
-
description: `For an agent you OWN, show which app
|
|
38482
|
+
description: `For an agent you OWN, show which app has been RECENTLY ACTIVE on its connection (auto-detected, e.g. "Claude Code" or "Codex", or the label you gave that key), whether it's online, and whether it's active or deactivated \u2014 plus a warning if more than one app is active on the same credential (a shared/copied token). Omit \`agent\` to list all your agents. Requires running this connector as the agent's human OWNER.`,
|
|
38458
38483
|
inputSchema: {
|
|
38459
38484
|
agent: external_exports.string().optional().describe("username of one of YOUR agents; omit to list all of them")
|
|
38460
38485
|
}
|
|
@@ -38463,10 +38488,7 @@ ${e?.message ?? e}`);
|
|
|
38463
38488
|
if (agent) {
|
|
38464
38489
|
const { status, body } = await ownerApi(c, `/api/agents/${encodeURIComponent(agent)}/status`, "GET");
|
|
38465
38490
|
if (status !== 200) return fail(body?.error === "not allowed" ? NOT_OWNER : `Could not read @${agent} (HTTP ${status}).`);
|
|
38466
|
-
|
|
38467
|
-
const state = body.active ? "active" : "\u{1F6D1} deactivated";
|
|
38468
|
-
const app = body.online ? ` \xB7 app: ${body.app || "connected app"}` : "";
|
|
38469
|
-
return ok3(`@${agent} \u2014 ${dot}${app} \xB7 ${state}`);
|
|
38491
|
+
return ok3(formatAgentStatus(agent, body));
|
|
38470
38492
|
}
|
|
38471
38493
|
const mine = (await c.identities()).filter((i) => i.type === "agent" && i.owner === c.identity?.username);
|
|
38472
38494
|
if (!mine.length) return ok3("You don't own any agents (or you're not connected as their owner).");
|
|
@@ -39922,7 +39944,10 @@ async function main() {
|
|
|
39922
39944
|
const transport = new StdioServerTransport();
|
|
39923
39945
|
server.server.oninitialized = () => {
|
|
39924
39946
|
try {
|
|
39925
|
-
|
|
39947
|
+
const rawHost = server.server.getClientVersion()?.name;
|
|
39948
|
+
setAutoHost(rawHost);
|
|
39949
|
+
void ensureClient().then((cl) => cl.announceHost(rawHost)).catch(() => {
|
|
39950
|
+
});
|
|
39926
39951
|
} catch {
|
|
39927
39952
|
}
|
|
39928
39953
|
};
|
package/package.json
CHANGED