@kynver-app/mcp-agent-os 0.3.68 → 0.3.71

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.
Files changed (74) hide show
  1. package/dist/index.js +3 -19
  2. package/dist/sse.js +3 -15
  3. package/dist/worker.js +2 -0
  4. package/package.json +5 -4
  5. package/dist/context/context-budget-receipt.d.ts +0 -9
  6. package/dist/context/context-budget-receipt.js +0 -34
  7. package/dist/index.d.ts +0 -7
  8. package/dist/lib/kynver-client.d.ts +0 -23
  9. package/dist/lib/kynver-client.js +0 -151
  10. package/dist/lib/mcp-json-result.d.ts +0 -12
  11. package/dist/lib/mcp-json-result.js +0 -30
  12. package/dist/lib/mcp-sse-server.d.ts +0 -44
  13. package/dist/lib/mcp-sse-server.js +0 -441
  14. package/dist/lib/package-version.d.ts +0 -7
  15. package/dist/lib/package-version.js +0 -44
  16. package/dist/server.d.ts +0 -11
  17. package/dist/server.js +0 -751
  18. package/dist/sse.d.ts +0 -1
  19. package/dist/tools/action-required.d.ts +0 -24
  20. package/dist/tools/action-required.js +0 -140
  21. package/dist/tools/cmo-control.d.ts +0 -20
  22. package/dist/tools/cmo-control.js +0 -26
  23. package/dist/tools/command-center.d.ts +0 -24
  24. package/dist/tools/command-center.js +0 -155
  25. package/dist/tools/context-expand.d.ts +0 -18
  26. package/dist/tools/context-expand.js +0 -26
  27. package/dist/tools/context-preview.d.ts +0 -14
  28. package/dist/tools/context-preview.js +0 -51
  29. package/dist/tools/current-focus.d.ts +0 -17
  30. package/dist/tools/current-focus.js +0 -126
  31. package/dist/tools/diagnostics.d.ts +0 -24
  32. package/dist/tools/diagnostics.js +0 -69
  33. package/dist/tools/graph.d.ts +0 -25
  34. package/dist/tools/graph.js +0 -153
  35. package/dist/tools/kariad-marketing-loop.d.ts +0 -21
  36. package/dist/tools/kariad-marketing-loop.js +0 -41
  37. package/dist/tools/kariad-outcome-import.d.ts +0 -17
  38. package/dist/tools/kariad-outcome-import.js +0 -37
  39. package/dist/tools/macro-controls.d.ts +0 -23
  40. package/dist/tools/macro-controls.js +0 -54
  41. package/dist/tools/memory-ledger.d.ts +0 -19
  42. package/dist/tools/memory-ledger.js +0 -48
  43. package/dist/tools/memory-quality-feedback.d.ts +0 -23
  44. package/dist/tools/memory-quality-feedback.js +0 -123
  45. package/dist/tools/memory-vault.d.ts +0 -21
  46. package/dist/tools/memory-vault.js +0 -24
  47. package/dist/tools/mesh-liveness.d.ts +0 -20
  48. package/dist/tools/mesh-liveness.js +0 -40
  49. package/dist/tools/monitor.d.ts +0 -17
  50. package/dist/tools/monitor.js +0 -51
  51. package/dist/tools/operating-rules.d.ts +0 -7
  52. package/dist/tools/operating-rules.js +0 -39
  53. package/dist/tools/plan-audit-rollup.d.ts +0 -19
  54. package/dist/tools/plan-audit-rollup.js +0 -49
  55. package/dist/tools/plan-progress.d.ts +0 -2
  56. package/dist/tools/plan-progress.js +0 -128
  57. package/dist/tools/plan-to-factory.d.ts +0 -2
  58. package/dist/tools/plan-to-factory.js +0 -23
  59. package/dist/tools/plans.d.ts +0 -29
  60. package/dist/tools/plans.js +0 -205
  61. package/dist/tools/pr-backlog.d.ts +0 -22
  62. package/dist/tools/pr-backlog.js +0 -43
  63. package/dist/tools/provider-evidence.d.ts +0 -23
  64. package/dist/tools/provider-evidence.js +0 -60
  65. package/dist/tools/skills.d.ts +0 -28
  66. package/dist/tools/skills.js +0 -79
  67. package/dist/tools/task-board-watchdog.d.ts +0 -20
  68. package/dist/tools/task-board-watchdog.js +0 -42
  69. package/dist/tools/taskflow.d.ts +0 -21
  70. package/dist/tools/taskflow.js +0 -124
  71. package/dist/tools/tasks.d.ts +0 -26
  72. package/dist/tools/tasks.js +0 -251
  73. package/dist/tools/workbench.d.ts +0 -17
  74. package/dist/tools/workbench.js +0 -178
@@ -1,30 +0,0 @@
1
- function orderedStringifyForMcp(value, priorityKeys) {
2
- const ordered = {};
3
- const seen = new Set();
4
- for (const key of priorityKeys) {
5
- if (key in value) {
6
- ordered[key] = value[key];
7
- seen.add(key);
8
- }
9
- }
10
- for (const [key, val] of Object.entries(value)) {
11
- if (!seen.has(key))
12
- ordered[key] = val;
13
- }
14
- return JSON.stringify(ordered, null, 2);
15
- }
16
- /** Default MCP text result — pretty JSON for small payloads. */
17
- export function jsonResult(data) {
18
- return {
19
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
20
- structuredContent: data,
21
- };
22
- }
23
- /** Truncation-safe MCP result — priority keys serialize first. */
24
- export function jsonResultOrdered(data, priorityKeys) {
25
- const text = orderedStringifyForMcp(data, priorityKeys);
26
- return {
27
- content: [{ type: "text", text }],
28
- structuredContent: data,
29
- };
30
- }
@@ -1,44 +0,0 @@
1
- import { createServer, type IncomingMessage } from "node:http";
2
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- export declare const MAX_SSE_BODY_BYTES: number;
4
- export type SessionAuth = {
5
- kind: "apiKey";
6
- apiKey: string;
7
- } | {
8
- kind: "internal";
9
- userId: string;
10
- };
11
- type RequestContextStore = {
12
- run: <T>(store: {
13
- userId?: string;
14
- }, fn: () => Promise<T>) => Promise<T>;
15
- };
16
- export type StartKynverMcpSseServerOptions = {
17
- label: string;
18
- defaultPort: number;
19
- createMcpServer: () => McpServer | Promise<McpServer>;
20
- requestContext: RequestContextStore;
21
- };
22
- export declare function headerValue(req: IncomingMessage, name: string): string | null;
23
- export declare function timingSafeEqualString(a: string, b: string): boolean;
24
- export declare function hasForbiddenQueryAuth(url: URL): boolean;
25
- export declare function resolveListenHost(): string;
26
- /** Positive integer from env, else fallback. */
27
- export declare function intFromEnv(name: string, fallback: number): number;
28
- /**
29
- * Resolve + validate the Kynver API origin the server sends bearer keys to.
30
- * A poisoned/misconfigured value would exfiltrate API keys, so fail closed on
31
- * anything that isn't https (http is allowed only for localhost dev).
32
- */
33
- export declare function resolveApiBaseUrl(): string;
34
- /** First client IP from x-forwarded-for, else the socket address. */
35
- export declare function clientIp(req: IncomingMessage): string;
36
- type RateLimiter = {
37
- check: (key: string) => boolean;
38
- };
39
- /** Minimal in-process fixed-window limiter (the SSE server is single-process). */
40
- export declare function createFixedWindowRateLimiter(limit: number, windowMs: number): RateLimiter;
41
- export declare function parseJsonBody(req: IncomingMessage, maxBytes?: number): Promise<unknown>;
42
- export declare function validateMcporterServerName(name: string): string;
43
- export declare function startKynverMcpSseServer(options: StartKynverMcpSseServerOptions): ReturnType<typeof createServer>;
44
- export {};
@@ -1,441 +0,0 @@
1
- // @generated — synced from packages/kynver-mcp-sse-lib/src/index.ts via scripts/sync-mcp-sse-server.mjs
2
- // Do not edit by hand; change the canonical file and re-run the sync script.
3
- import { createServer } from "node:http";
4
- import { randomUUID, timingSafeEqual } from "node:crypto";
5
- import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
6
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
- export const MAX_SSE_BODY_BYTES = 1 * 1024 * 1024;
8
- const FORBIDDEN_QUERY_KEYS = ["apiKey", "secret", "userId"];
9
- const TRUTHY_ENV = new Set(["1", "true", "yes", "on"]);
10
- function isServiceSecretImpersonationRejectedInProduction() {
11
- const override = process.env.KYNVER_ALLOW_SERVICE_SECRET_IMPERSONATION?.trim().toLowerCase();
12
- if (override !== undefined && TRUTHY_ENV.has(override))
13
- return false;
14
- return process.env.NODE_ENV === "production" || process.env.VERCEL_ENV === "production";
15
- }
16
- export function headerValue(req, name) {
17
- const v = req.headers[name];
18
- return typeof v === "string" ? v : Array.isArray(v) ? (v[0] ?? null) : null;
19
- }
20
- export function timingSafeEqualString(a, b) {
21
- const left = Buffer.from(a);
22
- const right = Buffer.from(b);
23
- if (left.length !== right.length)
24
- return false;
25
- return timingSafeEqual(left, right);
26
- }
27
- export function hasForbiddenQueryAuth(url) {
28
- return FORBIDDEN_QUERY_KEYS.some((key) => url.searchParams.has(key));
29
- }
30
- export function resolveListenHost() {
31
- const configured = process.env.MCP_SSE_HOST?.trim();
32
- return configured || "127.0.0.1";
33
- }
34
- /** Positive integer from env, else fallback. */
35
- export function intFromEnv(name, fallback) {
36
- const raw = process.env[name];
37
- if (!raw)
38
- return fallback;
39
- const n = parseInt(raw, 10);
40
- return Number.isFinite(n) && n > 0 ? n : fallback;
41
- }
42
- /**
43
- * Resolve + validate the Kynver API origin the server sends bearer keys to.
44
- * A poisoned/misconfigured value would exfiltrate API keys, so fail closed on
45
- * anything that isn't https (http is allowed only for localhost dev).
46
- */
47
- export function resolveApiBaseUrl() {
48
- const raw = process.env.KYNVER_API_URL?.trim();
49
- if (!raw)
50
- return "https://kynver.com";
51
- let parsed;
52
- try {
53
- parsed = new URL(raw);
54
- }
55
- catch {
56
- throw new Error(`Invalid KYNVER_API_URL: ${raw}`);
57
- }
58
- const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
59
- if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalhost)) {
60
- throw new Error(`KYNVER_API_URL must be https (got "${parsed.protocol}//${parsed.hostname}"); API keys are sent to this origin.`);
61
- }
62
- return raw.replace(/\/$/, "");
63
- }
64
- /** First client IP from x-forwarded-for, else the socket address. */
65
- export function clientIp(req) {
66
- const fwd = headerValue(req, "x-forwarded-for");
67
- if (fwd)
68
- return fwd.split(",")[0].trim();
69
- return req.socket?.remoteAddress ?? "unknown";
70
- }
71
- /** Minimal in-process fixed-window limiter (the SSE server is single-process). */
72
- export function createFixedWindowRateLimiter(limit, windowMs) {
73
- const hits = new Map();
74
- return {
75
- check(key) {
76
- const now = Date.now();
77
- const entry = hits.get(key);
78
- if (!entry || now >= entry.resetAt) {
79
- hits.set(key, { count: 1, resetAt: now + windowMs });
80
- return true;
81
- }
82
- if (entry.count >= limit)
83
- return false;
84
- entry.count += 1;
85
- return true;
86
- },
87
- };
88
- }
89
- export function parseJsonBody(req, maxBytes = MAX_SSE_BODY_BYTES) {
90
- return new Promise((resolve, reject) => {
91
- const chunks = [];
92
- let totalSize = 0;
93
- req.on("data", (chunk) => {
94
- totalSize += chunk.length;
95
- if (totalSize > maxBytes) {
96
- req.destroy();
97
- reject(new Error("Request body too large"));
98
- return;
99
- }
100
- chunks.push(chunk);
101
- });
102
- req.on("end", () => {
103
- try {
104
- resolve(JSON.parse(Buffer.concat(chunks).toString()));
105
- }
106
- catch (error) {
107
- reject(error);
108
- }
109
- });
110
- req.on("error", reject);
111
- });
112
- }
113
- export function validateMcporterServerName(name) {
114
- const trimmed = name.trim();
115
- if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
116
- throw new Error("Invalid mcporter server name.");
117
- }
118
- return trimmed;
119
- }
120
- function bearerToken(req) {
121
- const auth = headerValue(req, "authorization");
122
- if (!auth)
123
- return "";
124
- const match = /^Bearer\s+(.+)$/i.exec(auth);
125
- return match?.[1]?.trim() ?? "";
126
- }
127
- function resolveInternalAuth(req, serviceSecret) {
128
- const internalUserId = headerValue(req, "x-kynver-user-id");
129
- const internalSecret = headerValue(req, "x-kynver-service-secret");
130
- if (!internalUserId || !serviceSecret || !internalSecret)
131
- return null;
132
- if (isServiceSecretImpersonationRejectedInProduction())
133
- return null;
134
- if (!timingSafeEqualString(internalSecret, serviceSecret))
135
- return null;
136
- return { userId: internalUserId, auth: { kind: "internal", userId: internalUserId } };
137
- }
138
- function verifyPostAuth(req, url, sessionAuth, serviceSecret) {
139
- if (sessionAuth.kind === "apiKey") {
140
- const token = bearerToken(req);
141
- return token.length > 0 && timingSafeEqualString(token, sessionAuth.apiKey);
142
- }
143
- const internal = resolveInternalAuth(req, serviceSecret);
144
- return (internal !== null &&
145
- internal.userId === sessionAuth.userId &&
146
- internal.auth.kind === "internal");
147
- }
148
- async function resolveUserFromApiKey(baseUrl, apiKey) {
149
- try {
150
- const res = await fetch(`${baseUrl.replace(/\/$/, "")}/api/users/me`, {
151
- headers: { Authorization: `Bearer ${apiKey}` },
152
- });
153
- if (!res.ok)
154
- return null;
155
- const data = (await res.json());
156
- return data.id ?? null;
157
- }
158
- catch {
159
- return null;
160
- }
161
- }
162
- function parseUrl(req, port) {
163
- return new URL(req.url ?? "/", `http://localhost:${port}`);
164
- }
165
- function json(res, status, body) {
166
- res.writeHead(status, { "Content-Type": "application/json" });
167
- res.end(JSON.stringify(body));
168
- }
169
- export function startKynverMcpSseServer(options) {
170
- const port = parseInt(process.env.PORT ?? String(options.defaultPort), 10);
171
- const baseUrl = resolveApiBaseUrl();
172
- const serviceSecret = process.env.KYNVER_SERVICE_SECRET ?? "";
173
- const host = resolveListenHost();
174
- const sessions = {};
175
- const streamableSessions = {};
176
- // Resource / abuse limits (env-overridable; in-process per server).
177
- const maxSessions = intFromEnv("MCP_SSE_MAX_SESSIONS", 200);
178
- const maxSessionsPerUser = intFromEnv("MCP_SSE_MAX_SESSIONS_PER_USER", 20);
179
- const idleTimeoutMs = intFromEnv("MCP_SSE_IDLE_TIMEOUT_MS", 5 * 60 * 1000);
180
- const rateLimitPerMin = intFromEnv("MCP_SSE_RATE_LIMIT_PER_MIN", 120);
181
- const rateLimiter = createFixedWindowRateLimiter(rateLimitPerMin, 60 * 1000);
182
- // Close idle sessions (both transports) so a flood of half-open connections
183
- // can't pin memory.
184
- const idleSweep = setInterval(() => {
185
- const now = Date.now();
186
- for (const [sid, entry] of Object.entries(sessions)) {
187
- if (now - entry.lastActivity > idleTimeoutMs) {
188
- delete sessions[sid];
189
- void entry.transport.close().catch(() => { });
190
- }
191
- }
192
- for (const [sid, entry] of Object.entries(streamableSessions)) {
193
- if (now - entry.lastActivity > idleTimeoutMs) {
194
- delete streamableSessions[sid];
195
- void entry.transport.close().catch(() => { });
196
- }
197
- }
198
- }, 60 * 1000);
199
- idleSweep.unref?.();
200
- const activeSessionCount = () => Object.keys(sessions).length + Object.keys(streamableSessions).length;
201
- const activeSessionCountForUser = (userId) => Object.values(sessions).filter((s) => s.userId === userId).length +
202
- Object.values(streamableSessions).filter((s) => s.userId === userId).length;
203
- const httpServer = createServer(async (req, res) => {
204
- const url = parseUrl(req, port);
205
- const path = url.pathname;
206
- if (req.method === "GET" && path === "/sse") {
207
- // Pre-auth rate limit by client IP (we don't know the user yet).
208
- if (!rateLimiter.check(`sse:${clientIp(req)}`)) {
209
- json(res, 429, { error: "Too many requests. Slow down and retry." });
210
- return;
211
- }
212
- if (hasForbiddenQueryAuth(url)) {
213
- json(res, 400, {
214
- error: "Internal auth via query params is not allowed. Use X-Kynver-User-Id and X-Kynver-Service-Secret headers.",
215
- });
216
- return;
217
- }
218
- let userId = null;
219
- let auth = null;
220
- const internal = serviceSecret ? resolveInternalAuth(req, serviceSecret) : null;
221
- if (internal) {
222
- userId = internal.userId;
223
- auth = internal.auth;
224
- }
225
- else {
226
- const apiKey = bearerToken(req);
227
- if (apiKey) {
228
- userId = await resolveUserFromApiKey(baseUrl, apiKey);
229
- if (userId)
230
- auth = { kind: "apiKey", apiKey };
231
- }
232
- }
233
- if (!userId || !auth) {
234
- json(res, 401, {
235
- error: "Unauthorized. Pass API key via Authorization: Bearer <key> or internal X-Kynver-User-Id + X-Kynver-Service-Secret headers.",
236
- });
237
- return;
238
- }
239
- // Connection caps — bound total + per-user concurrent sessions (across
240
- // both transports) so an authenticated caller can't exhaust
241
- // memory/sockets by opening many.
242
- if (activeSessionCount() >= maxSessions) {
243
- json(res, 503, { error: "Server at capacity. Try again shortly." });
244
- return;
245
- }
246
- if (activeSessionCountForUser(userId) >= maxSessionsPerUser) {
247
- json(res, 429, { error: "Too many concurrent connections for this account." });
248
- return;
249
- }
250
- console.log(`[MCP SSE] New connection (${options.label}, user: ${userId.slice(0, 8)}...)`);
251
- const transport = new SSEServerTransport("/messages", res);
252
- sessions[transport.sessionId] = { transport, userId, auth, lastActivity: Date.now() };
253
- res.on("close", () => {
254
- delete sessions[transport.sessionId];
255
- });
256
- const server = await options.createMcpServer();
257
- await server.connect(transport);
258
- return;
259
- }
260
- if (req.method === "POST" && path === "/messages") {
261
- const sessionId = url.searchParams.get("sessionId");
262
- const entry = sessionId ? sessions[sessionId] : undefined;
263
- if (!entry) {
264
- json(res, 400, { error: "No active SSE session for this sessionId" });
265
- return;
266
- }
267
- if (!verifyPostAuth(req, url, entry.auth, serviceSecret)) {
268
- json(res, 401, { error: "Unauthorized. Re-send the same credentials used to open the SSE session." });
269
- return;
270
- }
271
- // Per-user rate limit on tool/message traffic.
272
- if (!rateLimiter.check(`msg:${entry.userId}`)) {
273
- json(res, 429, { error: "Too many requests. Slow down and retry." });
274
- return;
275
- }
276
- entry.lastActivity = Date.now();
277
- try {
278
- const body = await parseJsonBody(req);
279
- await options.requestContext.run({ userId: entry.userId }, async () => {
280
- await entry.transport.handlePostMessage(req, res, body);
281
- });
282
- }
283
- catch (error) {
284
- json(res, 400, {
285
- error: error instanceof Error ? error.message : "Invalid request body",
286
- });
287
- }
288
- return;
289
- }
290
- // Streamable HTTP transport (MCP spec 2025-03-26+) — the transport modern
291
- // connector clients (Grok, ChatGPT, Gemini, Claude) expect for remote
292
- // servers. One path, three verbs: POST carries JSON-RPC (initialize opens a
293
- // session; the response's mcp-session-id header identifies it), GET opens
294
- // the standalone server→client SSE stream, DELETE terminates the session.
295
- // Auth model mirrors the legacy /sse pair: credentials are verified on the
296
- // initialize POST, bound to the session, and every subsequent request must
297
- // re-present the same credentials.
298
- if (path === "/mcp") {
299
- if (req.method !== "POST" && req.method !== "GET" && req.method !== "DELETE") {
300
- res.writeHead(405, { "Content-Type": "application/json", Allow: "GET, POST, DELETE" });
301
- res.end(JSON.stringify({ error: "Method not allowed" }));
302
- return;
303
- }
304
- if (hasForbiddenQueryAuth(url)) {
305
- json(res, 400, {
306
- error: "Internal auth via query params is not allowed. Use X-Kynver-User-Id and X-Kynver-Service-Secret headers.",
307
- });
308
- return;
309
- }
310
- const sessionId = headerValue(req, "mcp-session-id");
311
- if (sessionId) {
312
- const entry = streamableSessions[sessionId];
313
- if (!entry) {
314
- // 404 (not 400) per spec so a well-behaved client re-initializes.
315
- json(res, 404, { error: "Unknown or expired MCP session. Re-initialize." });
316
- return;
317
- }
318
- if (!verifyPostAuth(req, url, entry.auth, serviceSecret)) {
319
- json(res, 401, {
320
- error: "Unauthorized. Re-send the same credentials used to initialize the MCP session.",
321
- });
322
- return;
323
- }
324
- if (!rateLimiter.check(`msg:${entry.userId}`)) {
325
- json(res, 429, { error: "Too many requests. Slow down and retry." });
326
- return;
327
- }
328
- entry.lastActivity = Date.now();
329
- try {
330
- const body = req.method === "POST" ? await parseJsonBody(req) : undefined;
331
- await options.requestContext.run({ userId: entry.userId }, async () => {
332
- await entry.transport.handleRequest(req, res, body);
333
- });
334
- }
335
- catch (error) {
336
- if (!res.headersSent) {
337
- json(res, 400, {
338
- error: error instanceof Error ? error.message : "Invalid request body",
339
- });
340
- }
341
- }
342
- return;
343
- }
344
- // No session header — only an initialize POST may open a session. (The
345
- // transport itself rejects a sessionless non-initialize POST with 400.)
346
- if (req.method !== "POST") {
347
- json(res, 400, {
348
- error: "Missing mcp-session-id header. Open a session with an initialize POST first.",
349
- });
350
- return;
351
- }
352
- // Pre-auth rate limit by client IP (we don't know the user yet).
353
- if (!rateLimiter.check(`mcp:${clientIp(req)}`)) {
354
- json(res, 429, { error: "Too many requests. Slow down and retry." });
355
- return;
356
- }
357
- let userId = null;
358
- let auth = null;
359
- const internal = serviceSecret ? resolveInternalAuth(req, serviceSecret) : null;
360
- if (internal) {
361
- userId = internal.userId;
362
- auth = internal.auth;
363
- }
364
- else {
365
- const apiKey = bearerToken(req);
366
- if (apiKey) {
367
- userId = await resolveUserFromApiKey(baseUrl, apiKey);
368
- if (userId)
369
- auth = { kind: "apiKey", apiKey };
370
- }
371
- }
372
- if (!userId || !auth) {
373
- json(res, 401, {
374
- error: "Unauthorized. Pass API key via Authorization: Bearer <key> or internal X-Kynver-User-Id + X-Kynver-Service-Secret headers.",
375
- });
376
- return;
377
- }
378
- if (activeSessionCount() >= maxSessions) {
379
- json(res, 503, { error: "Server at capacity. Try again shortly." });
380
- return;
381
- }
382
- if (activeSessionCountForUser(userId) >= maxSessionsPerUser) {
383
- json(res, 429, { error: "Too many concurrent connections for this account." });
384
- return;
385
- }
386
- let body;
387
- try {
388
- body = await parseJsonBody(req);
389
- }
390
- catch (error) {
391
- json(res, 400, {
392
- error: error instanceof Error ? error.message : "Invalid request body",
393
- });
394
- return;
395
- }
396
- const boundUserId = userId;
397
- const boundAuth = auth;
398
- const transport = new StreamableHTTPServerTransport({
399
- sessionIdGenerator: () => randomUUID(),
400
- onsessioninitialized: (sid) => {
401
- streamableSessions[sid] = {
402
- transport,
403
- userId: boundUserId,
404
- auth: boundAuth,
405
- lastActivity: Date.now(),
406
- };
407
- console.log(`[MCP HTTP] New session (${options.label}, user: ${boundUserId.slice(0, 8)}...)`);
408
- },
409
- });
410
- transport.onclose = () => {
411
- if (transport.sessionId)
412
- delete streamableSessions[transport.sessionId];
413
- };
414
- const server = await options.createMcpServer();
415
- await server.connect(transport);
416
- try {
417
- await options.requestContext.run({ userId: boundUserId }, async () => {
418
- await transport.handleRequest(req, res, body);
419
- });
420
- }
421
- catch (error) {
422
- if (!res.headersSent) {
423
- json(res, 400, {
424
- error: error instanceof Error ? error.message : "Invalid request",
425
- });
426
- }
427
- }
428
- return;
429
- }
430
- if (req.method === "GET" && path === "/health") {
431
- res.writeHead(200, { "Content-Type": "text/plain" });
432
- res.end("ok");
433
- return;
434
- }
435
- json(res, 404, { error: "Not found" });
436
- });
437
- httpServer.listen(port, host, () => {
438
- console.log(`[MCP SSE] ${options.label} listening on ${host}:${port}`);
439
- });
440
- return httpServer;
441
- }
@@ -1,7 +0,0 @@
1
- /** Read a field from the nearest `package.json` above this module. */
2
- export declare function readOwnPackageJsonField(moduleUrl: string, field: "version" | "name"): string;
3
- export declare function readOwnPackageVersion(moduleUrl?: string): string;
4
- export declare function wantsCliVersion(argv: string[]): boolean;
5
- export declare function printCliVersionAndExit(version: string, binName?: string): never;
6
- /** When `--version` / `-v` is present, print package version and exit before side effects. */
7
- export declare function handleCliVersionFlag(argv: string[], moduleUrl?: string, binName?: string): boolean;
@@ -1,44 +0,0 @@
1
- // @generated — synced from packages/kynver-mcp-sse-lib/src/package-version.ts via scripts/sync-mcp-sse-server.mjs
2
- // Do not edit by hand; change the canonical file and re-run the sync script.
3
- import { existsSync, readFileSync } from "node:fs";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- function resolvePackageRoot(moduleUrl) {
7
- let dir = dirname(fileURLToPath(moduleUrl));
8
- for (let depth = 0; depth < 6; depth += 1) {
9
- if (existsSync(join(dir, "package.json")))
10
- return dir;
11
- const parent = dirname(dir);
12
- if (parent === dir)
13
- break;
14
- dir = parent;
15
- }
16
- throw new Error(`package.json not found above ${dirname(fileURLToPath(moduleUrl))}`);
17
- }
18
- /** Read a field from the nearest `package.json` above this module. */
19
- export function readOwnPackageJsonField(moduleUrl, field) {
20
- const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
21
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
22
- const value = pkg[field];
23
- if (typeof value !== "string" || !value.trim()) {
24
- throw new Error(`Missing package.json ${field} at ${pkgPath}`);
25
- }
26
- return value;
27
- }
28
- export function readOwnPackageVersion(moduleUrl = import.meta.url) {
29
- return readOwnPackageJsonField(moduleUrl, "version");
30
- }
31
- export function wantsCliVersion(argv) {
32
- return argv.some((arg) => arg === "--version" || arg === "-v");
33
- }
34
- export function printCliVersionAndExit(version, binName) {
35
- console.log(binName ? `${binName} ${version}` : version);
36
- process.exit(0);
37
- }
38
- /** When `--version` / `-v` is present, print package version and exit before side effects. */
39
- export function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
40
- if (!wantsCliVersion(argv))
41
- return false;
42
- printCliVersionAndExit(readOwnPackageVersion(moduleUrl), binName);
43
- return true;
44
- }
package/dist/server.d.ts DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Kynver Agentic OS MCP server: slug-keyed agent identity, goals, projects,
3
- * sessions, contacts, and long-term memory.
4
- *
5
- * Each tool proxies to `/api/agent-os/{slug}/*` on Kynver. The optional `slug`
6
- * argument resolves in order: explicit arg → `KYNVER_AGENT_OS_SLUG` → cached
7
- * `GET /api/agent-os` `{ primarySlug }` → list first item → legacy `"ghost"`.
8
- */
9
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
- export declare function resolveAgentContext(explicitAgentContext?: string): string | undefined;
11
- export declare function createAgentOsServer(): McpServer;