cross-agent-teams-mcp 0.5.10 → 0.5.12
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/cli.d.ts +1 -0
- package/dist/cli.js +137 -17
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +12 -2
- package/src/daemon/network-origin.ts +12 -0
- package/src/daemon/server.ts +68 -5
- package/src/daemon/shutdown.ts +79 -5
- package/src/mcp/transport.ts +19 -7
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from "url";
|
|
|
8
8
|
|
|
9
9
|
// src/daemon/server.ts
|
|
10
10
|
import Fastify from "fastify";
|
|
11
|
+
import { createServer as createHttpServer } from "http";
|
|
11
12
|
|
|
12
13
|
// src/storage/db.ts
|
|
13
14
|
import Database from "better-sqlite3";
|
|
@@ -3712,12 +3713,14 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
3712
3713
|
sessionIdGenerator: () => randomUUID5(),
|
|
3713
3714
|
onsessioninitialized: (sid) => {
|
|
3714
3715
|
sessionIdForCaller = sid;
|
|
3716
|
+
const now2 = Date.now();
|
|
3715
3717
|
sessions.set(sid, {
|
|
3716
3718
|
transport,
|
|
3717
3719
|
server,
|
|
3718
3720
|
sessionId: sid,
|
|
3719
3721
|
agentIdHolder,
|
|
3720
|
-
createdAt:
|
|
3722
|
+
createdAt: now2,
|
|
3723
|
+
lastActivityAt: now2,
|
|
3721
3724
|
clientInfo: void 0,
|
|
3722
3725
|
originInfo: { origin: "local", remote_addr: null }
|
|
3723
3726
|
});
|
|
@@ -3771,12 +3774,14 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
3771
3774
|
registerSvc
|
|
3772
3775
|
);
|
|
3773
3776
|
server.connect(transport);
|
|
3777
|
+
const now = Date.now();
|
|
3774
3778
|
return {
|
|
3775
3779
|
transport,
|
|
3776
3780
|
server,
|
|
3777
3781
|
sessionId: "",
|
|
3778
3782
|
agentIdHolder,
|
|
3779
|
-
createdAt:
|
|
3783
|
+
createdAt: now,
|
|
3784
|
+
lastActivityAt: now,
|
|
3780
3785
|
originInfo: { origin: "local", remote_addr: null }
|
|
3781
3786
|
};
|
|
3782
3787
|
}
|
|
@@ -3820,6 +3825,7 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
3820
3825
|
}
|
|
3821
3826
|
if (session) {
|
|
3822
3827
|
session.originInfo = originInfo;
|
|
3828
|
+
session.lastActivityAt = Date.now();
|
|
3823
3829
|
}
|
|
3824
3830
|
if (body?.method === "initialize") {
|
|
3825
3831
|
const params = body.params;
|
|
@@ -3842,6 +3848,7 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
3842
3848
|
const sid = req.headers["mcp-session-id"];
|
|
3843
3849
|
const session = sid ? sessions.get(sid) : void 0;
|
|
3844
3850
|
if (!session) return reply.code(400).send({ error: "unknown_session" });
|
|
3851
|
+
session.lastActivityAt = Date.now();
|
|
3845
3852
|
await session.transport.handleRequest(req.raw, reply.raw);
|
|
3846
3853
|
return reply;
|
|
3847
3854
|
});
|
|
@@ -3849,14 +3856,15 @@ function mountMcp(app, db, fanout, channelWakeFanout, opts = {}) {
|
|
|
3849
3856
|
const sid = req.headers["mcp-session-id"];
|
|
3850
3857
|
const session = sid ? sessions.get(sid) : void 0;
|
|
3851
3858
|
if (!session) return reply.code(400).send({ error: "unknown_session" });
|
|
3859
|
+
session.lastActivityAt = Date.now();
|
|
3852
3860
|
await session.transport.handleRequest(req.raw, reply.raw);
|
|
3853
3861
|
return reply;
|
|
3854
3862
|
});
|
|
3855
|
-
function reapOrphanSessions(now, graceMs =
|
|
3863
|
+
function reapOrphanSessions(now, graceMs = 18e5) {
|
|
3856
3864
|
for (const session of sessions.values()) {
|
|
3857
3865
|
if (session.agentIdHolder.current !== void 0) continue;
|
|
3858
|
-
const
|
|
3859
|
-
if (
|
|
3866
|
+
const idleMs = now - session.lastActivityAt;
|
|
3867
|
+
if (idleMs < graceMs) continue;
|
|
3860
3868
|
try {
|
|
3861
3869
|
void session.transport.close();
|
|
3862
3870
|
} catch {
|
|
@@ -4036,10 +4044,14 @@ function classifyPeerAddress(address) {
|
|
|
4036
4044
|
function isLoopbackHost(host) {
|
|
4037
4045
|
return host === "localhost" || isLoopbackAddress(host);
|
|
4038
4046
|
}
|
|
4047
|
+
function bindHostCoversIpv4Loopback(host) {
|
|
4048
|
+
return host === "127.0.0.1" || host === "localhost" || host === "0.0.0.0";
|
|
4049
|
+
}
|
|
4039
4050
|
|
|
4040
4051
|
// src/daemon/server.ts
|
|
4041
4052
|
var DEFAULT_KEEP_ALIVE_TIMEOUT_MS = 12e4;
|
|
4042
4053
|
var DEFAULT_ORPHAN_GC_INTERVAL_MS = 6e4;
|
|
4054
|
+
var DEFAULT_ORPHAN_GC_IDLE_MS = 18e5;
|
|
4043
4055
|
function parsePositiveInt(raw, fallback) {
|
|
4044
4056
|
const n = Number(raw);
|
|
4045
4057
|
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
@@ -4073,9 +4085,10 @@ async function buildServer(opts) {
|
|
|
4073
4085
|
}, cleanupIntervalMs);
|
|
4074
4086
|
if (typeof interval.unref === "function") interval.unref();
|
|
4075
4087
|
const orphanGcIntervalMs = opts.orphanGcIntervalMs ?? parsePositiveInt(process.env.ORPHAN_GC_INTERVAL_MS, DEFAULT_ORPHAN_GC_INTERVAL_MS);
|
|
4088
|
+
const orphanGcIdleMs = opts.orphanGcIdleMs ?? parsePositiveInt(process.env.ORPHAN_GC_IDLE_MS, DEFAULT_ORPHAN_GC_IDLE_MS);
|
|
4076
4089
|
const orphanGcInterval = setInterval(() => {
|
|
4077
4090
|
try {
|
|
4078
|
-
mcp.reapOrphanSessions(Date.now());
|
|
4091
|
+
mcp.reapOrphanSessions(Date.now(), orphanGcIdleMs);
|
|
4079
4092
|
} catch {
|
|
4080
4093
|
}
|
|
4081
4094
|
}, orphanGcIntervalMs);
|
|
@@ -4092,10 +4105,46 @@ async function buildServer(opts) {
|
|
|
4092
4105
|
async function startServer(opts) {
|
|
4093
4106
|
const app = await buildServer(opts);
|
|
4094
4107
|
const host = opts.host ?? "127.0.0.1";
|
|
4108
|
+
const companionRef = { server: void 0 };
|
|
4109
|
+
app.addHook("onClose", async () => {
|
|
4110
|
+
const server = companionRef.server;
|
|
4111
|
+
if (!server) return;
|
|
4112
|
+
await new Promise((resolve) => {
|
|
4113
|
+
server.close(() => resolve());
|
|
4114
|
+
});
|
|
4115
|
+
});
|
|
4095
4116
|
await app.listen({ port: opts.port, host });
|
|
4096
4117
|
const addr = app.server.address();
|
|
4097
4118
|
const port = addr && typeof addr === "object" ? addr.port : opts.port;
|
|
4098
|
-
|
|
4119
|
+
const companionEnabled = opts.loopbackCompanion !== false;
|
|
4120
|
+
if (companionEnabled && !bindHostCoversIpv4Loopback(host)) {
|
|
4121
|
+
const handler = app.server.listeners("request")[0];
|
|
4122
|
+
if (!handler) {
|
|
4123
|
+
await app.close();
|
|
4124
|
+
throw new Error("loopback_companion_no_handler: Fastify did not expose a request handler");
|
|
4125
|
+
}
|
|
4126
|
+
const companion = createHttpServer(handler);
|
|
4127
|
+
try {
|
|
4128
|
+
await new Promise((resolve, reject) => {
|
|
4129
|
+
const onErr = (err) => reject(err);
|
|
4130
|
+
companion.once("error", onErr);
|
|
4131
|
+
companion.listen(port, "127.0.0.1", () => {
|
|
4132
|
+
companion.removeListener("error", onErr);
|
|
4133
|
+
resolve();
|
|
4134
|
+
});
|
|
4135
|
+
});
|
|
4136
|
+
} catch (err) {
|
|
4137
|
+
try {
|
|
4138
|
+
companion.close();
|
|
4139
|
+
} catch {
|
|
4140
|
+
}
|
|
4141
|
+
await app.close();
|
|
4142
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4143
|
+
throw new Error(`loopback_companion_bind_failed: ${detail}`);
|
|
4144
|
+
}
|
|
4145
|
+
companionRef.server = companion;
|
|
4146
|
+
}
|
|
4147
|
+
return { app, port, host, loopbackCompanion: companionRef.server };
|
|
4099
4148
|
}
|
|
4100
4149
|
|
|
4101
4150
|
// src/daemon/pid.ts
|
|
@@ -4130,17 +4179,76 @@ function releasePidFile(path) {
|
|
|
4130
4179
|
}
|
|
4131
4180
|
|
|
4132
4181
|
// src/daemon/shutdown.ts
|
|
4133
|
-
|
|
4134
|
-
|
|
4182
|
+
var DEFAULT_GRACE_MS = 5e3;
|
|
4183
|
+
function resolveGraceMs(explicit) {
|
|
4184
|
+
if (typeof explicit === "number" && Number.isFinite(explicit)) {
|
|
4185
|
+
return explicit < 0 ? 0 : Math.floor(explicit);
|
|
4186
|
+
}
|
|
4187
|
+
const raw = process.env.XATS_SHUTDOWN_GRACE_MS;
|
|
4188
|
+
if (raw === void 0 || raw === "") return DEFAULT_GRACE_MS;
|
|
4189
|
+
const n = Number(raw);
|
|
4190
|
+
if (!Number.isFinite(n)) return DEFAULT_GRACE_MS;
|
|
4191
|
+
return n < 0 ? 0 : Math.floor(n);
|
|
4192
|
+
}
|
|
4193
|
+
function wireShutdown(app, pidPath, opts = {}) {
|
|
4194
|
+
const graceMs = resolveGraceMs(opts.graceMs);
|
|
4195
|
+
const exit = opts.exit ?? ((code) => {
|
|
4196
|
+
process.exit(code);
|
|
4197
|
+
});
|
|
4198
|
+
const extraForceClose = opts.extraForceClose;
|
|
4199
|
+
let shuttingDown = false;
|
|
4200
|
+
const handler = (_signal) => {
|
|
4201
|
+
if (shuttingDown) {
|
|
4202
|
+
releasePidFile(pidPath);
|
|
4203
|
+
exit(0);
|
|
4204
|
+
return;
|
|
4205
|
+
}
|
|
4206
|
+
shuttingDown = true;
|
|
4207
|
+
void runDrain(app, pidPath, graceMs, exit, extraForceClose);
|
|
4208
|
+
};
|
|
4209
|
+
process.on("SIGTERM", handler);
|
|
4210
|
+
process.on("SIGINT", handler);
|
|
4211
|
+
}
|
|
4212
|
+
function forceCloseAll(app, extra) {
|
|
4213
|
+
try {
|
|
4214
|
+
app.server.closeAllConnections();
|
|
4215
|
+
} catch {
|
|
4216
|
+
}
|
|
4217
|
+
if (extra) {
|
|
4218
|
+
try {
|
|
4219
|
+
extra();
|
|
4220
|
+
} catch {
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
async function runDrain(app, pidPath, graceMs, exit, extraForceClose) {
|
|
4225
|
+
if (graceMs <= 0) {
|
|
4226
|
+
forceCloseAll(app, extraForceClose);
|
|
4135
4227
|
try {
|
|
4136
4228
|
await app.close();
|
|
4137
4229
|
} catch {
|
|
4138
4230
|
}
|
|
4139
4231
|
releasePidFile(pidPath);
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4232
|
+
exit(0);
|
|
4233
|
+
return;
|
|
4234
|
+
}
|
|
4235
|
+
let timer;
|
|
4236
|
+
const deadline = new Promise((resolve) => {
|
|
4237
|
+
timer = setTimeout(() => resolve("timeout"), graceMs);
|
|
4238
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
4239
|
+
});
|
|
4240
|
+
const closed = app.close().then(() => "closed").catch(() => "closed");
|
|
4241
|
+
const winner = await Promise.race([closed, deadline]);
|
|
4242
|
+
if (winner === "timeout") {
|
|
4243
|
+
forceCloseAll(app, extraForceClose);
|
|
4244
|
+
try {
|
|
4245
|
+
await app.close();
|
|
4246
|
+
} catch {
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
if (timer) clearTimeout(timer);
|
|
4250
|
+
releasePidFile(pidPath);
|
|
4251
|
+
exit(0);
|
|
4144
4252
|
}
|
|
4145
4253
|
|
|
4146
4254
|
// src/daemon/port.ts
|
|
@@ -4179,13 +4287,15 @@ function parseDaemonCliArgs(argv = process.argv, env = process.env) {
|
|
|
4179
4287
|
const host = parseArg("--host", "127.0.0.1") ?? "127.0.0.1";
|
|
4180
4288
|
const localDevice = resolveLocalDeviceLabel(parseArg("--device"));
|
|
4181
4289
|
const requestedPort = Number(parseArg("--port", "9100"));
|
|
4290
|
+
const loopbackCompanion = !process.argv.includes("--no-loopback-companion");
|
|
4182
4291
|
return {
|
|
4183
4292
|
pidPath: parseArg("--pid-file", join(home, "daemon.pid")),
|
|
4184
4293
|
dbPath: parseArg("--db", join(home, "data.db")),
|
|
4185
4294
|
token,
|
|
4186
4295
|
requestedPort,
|
|
4187
4296
|
host,
|
|
4188
|
-
localDevice
|
|
4297
|
+
localDevice,
|
|
4298
|
+
loopbackCompanion
|
|
4189
4299
|
};
|
|
4190
4300
|
} finally {
|
|
4191
4301
|
process.argv = originalArgv;
|
|
@@ -4209,10 +4319,20 @@ async function runDaemon() {
|
|
|
4209
4319
|
token: args.token,
|
|
4210
4320
|
port,
|
|
4211
4321
|
host: args.host,
|
|
4212
|
-
localDevice: args.localDevice
|
|
4322
|
+
localDevice: args.localDevice,
|
|
4323
|
+
loopbackCompanion: args.loopbackCompanion
|
|
4324
|
+
});
|
|
4325
|
+
const companion = started.loopbackCompanion;
|
|
4326
|
+
wireShutdown(started.app, args.pidPath, {
|
|
4327
|
+
extraForceClose: companion ? () => {
|
|
4328
|
+
try {
|
|
4329
|
+
companion.closeAllConnections();
|
|
4330
|
+
} catch {
|
|
4331
|
+
}
|
|
4332
|
+
} : void 0
|
|
4213
4333
|
});
|
|
4214
|
-
|
|
4215
|
-
console.log(`listening on ${started.host}:${started.port} device=${args.localDevice}`);
|
|
4334
|
+
const companionSuffix = companion ? ` (+ 127.0.0.1:${started.port} loopback companion)` : "";
|
|
4335
|
+
console.log(`listening on ${started.host}:${started.port}${companionSuffix} device=${args.localDevice}`);
|
|
4216
4336
|
}
|
|
4217
4337
|
function resolveDaemonPort(explicit) {
|
|
4218
4338
|
if (explicit !== void 0) {
|