@nekzus/liop 2.0.0-alpha.1 → 2.0.0-alpha.3

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 (124) hide show
  1. package/README.md +30 -20
  2. package/dist/bin/agent.d.ts +0 -1
  3. package/dist/bin/agent.js +5 -306
  4. package/dist/bin/agent.js.map +1 -0
  5. package/dist/{bridge/stream.d.ts → bridge.d.ts} +44 -3
  6. package/dist/bridge.js +2 -0
  7. package/dist/bridge.js.map +1 -0
  8. package/dist/chunk-7MAGL6ON.js +33 -0
  9. package/dist/chunk-7MAGL6ON.js.map +1 -0
  10. package/dist/chunk-ANFXJGMP.js +2 -0
  11. package/dist/chunk-ANFXJGMP.js.map +1 -0
  12. package/dist/chunk-DBXGYHKY.js +2 -0
  13. package/dist/chunk-DBXGYHKY.js.map +1 -0
  14. package/dist/chunk-FW6CICSY.js +29 -0
  15. package/dist/chunk-FW6CICSY.js.map +1 -0
  16. package/dist/chunk-HM77MWB6.js +2 -0
  17. package/dist/chunk-HM77MWB6.js.map +1 -0
  18. package/dist/chunk-HNDVAKEK.js +24 -0
  19. package/dist/chunk-HNDVAKEK.js.map +1 -0
  20. package/dist/chunk-HQZHZM6U.js +2 -0
  21. package/dist/chunk-HQZHZM6U.js.map +1 -0
  22. package/dist/chunk-JBMEAXYU.js +13 -0
  23. package/dist/chunk-JBMEAXYU.js.map +1 -0
  24. package/dist/chunk-LYULZHZO.js +3 -0
  25. package/dist/chunk-LYULZHZO.js.map +1 -0
  26. package/dist/chunk-P52IE4L6.js +2 -0
  27. package/dist/chunk-P52IE4L6.js.map +1 -0
  28. package/dist/chunk-PPCOS2NU.js +2 -0
  29. package/dist/chunk-PPCOS2NU.js.map +1 -0
  30. package/dist/chunk-RWRRBYG4.js +2 -0
  31. package/dist/chunk-RWRRBYG4.js.map +1 -0
  32. package/dist/chunk-S6RJHZV2.js +2 -0
  33. package/dist/chunk-S6RJHZV2.js.map +1 -0
  34. package/dist/chunk-UVTEJYHN.js +2 -0
  35. package/dist/chunk-UVTEJYHN.js.map +1 -0
  36. package/dist/client.d.ts +5 -0
  37. package/dist/client.js +2 -0
  38. package/dist/client.js.map +1 -0
  39. package/dist/{gateway/router.d.ts → gateway.d.ts} +30 -5
  40. package/dist/gateway.js +2 -0
  41. package/dist/gateway.js.map +1 -0
  42. package/dist/{client/index.d.ts → index-CyxNLlz7.d.ts} +24 -5
  43. package/dist/index.d.ts +313 -12
  44. package/dist/index.js +31 -12
  45. package/dist/index.js.map +1 -0
  46. package/dist/kyber-2WDOTUQX.js +2 -0
  47. package/dist/kyber-2WDOTUQX.js.map +1 -0
  48. package/dist/{mesh/node.d.ts → mesh.d.ts} +5 -3
  49. package/dist/mesh.js +2 -0
  50. package/dist/mesh.js.map +1 -0
  51. package/dist/{server/index.d.ts → server.d.ts} +125 -12
  52. package/dist/server.js +2 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/types.d.ts +17 -14
  55. package/dist/types.js +2 -26
  56. package/dist/types.js.map +1 -0
  57. package/dist/{crypto/verifier.d.ts → verifier-DTCD9imJ.d.ts} +3 -1
  58. package/dist/verifier-RQRYXA4C.js +2 -0
  59. package/dist/verifier-RQRYXA4C.js.map +1 -0
  60. package/dist/workers/logic-execution.d.ts +4 -2
  61. package/dist/workers/logic-execution.js +2 -123
  62. package/dist/workers/logic-execution.js.map +1 -0
  63. package/dist/workers/zk-verifier.d.ts +4 -2
  64. package/dist/workers/zk-verifier.js +2 -98
  65. package/dist/workers/zk-verifier.js.map +1 -0
  66. package/package.json +32 -19
  67. package/dist/bridge/index.d.ts +0 -37
  68. package/dist/bridge/index.js +0 -249
  69. package/dist/bridge/stream.js +0 -210
  70. package/dist/client/index.js +0 -275
  71. package/dist/crypto/logic-image-id.d.ts +0 -3
  72. package/dist/crypto/logic-image-id.js +0 -27
  73. package/dist/crypto/verifier.js +0 -97
  74. package/dist/economy/estimator.d.ts +0 -53
  75. package/dist/economy/estimator.js +0 -69
  76. package/dist/economy/index.d.ts +0 -5
  77. package/dist/economy/index.js +0 -3
  78. package/dist/economy/otel.d.ts +0 -38
  79. package/dist/economy/otel.js +0 -100
  80. package/dist/economy/telemetry.d.ts +0 -77
  81. package/dist/economy/telemetry.js +0 -224
  82. package/dist/errors.d.ts +0 -14
  83. package/dist/errors.js +0 -19
  84. package/dist/gateway/hybrid.d.ts +0 -23
  85. package/dist/gateway/hybrid.js +0 -199
  86. package/dist/gateway/router.js +0 -1054
  87. package/dist/mesh/index.d.ts +0 -1
  88. package/dist/mesh/index.js +0 -1
  89. package/dist/mesh/node.js +0 -853
  90. package/dist/prompts/adapters.d.ts +0 -16
  91. package/dist/prompts/adapters.js +0 -55
  92. package/dist/rpc/client.d.ts +0 -22
  93. package/dist/rpc/client.js +0 -40
  94. package/dist/rpc/codec/lpm.d.ts +0 -20
  95. package/dist/rpc/codec/lpm.js +0 -36
  96. package/dist/rpc/crypto/aes.d.ts +0 -22
  97. package/dist/rpc/crypto/aes.js +0 -47
  98. package/dist/rpc/crypto/kyber.d.ts +0 -27
  99. package/dist/rpc/crypto/kyber.js +0 -70
  100. package/dist/rpc/proto.d.ts +0 -2
  101. package/dist/rpc/proto.js +0 -33
  102. package/dist/rpc/server.d.ts +0 -13
  103. package/dist/rpc/server.js +0 -50
  104. package/dist/rpc/tls.d.ts +0 -26
  105. package/dist/rpc/tls.js +0 -54
  106. package/dist/rpc/types.d.ts +0 -28
  107. package/dist/rpc/types.js +0 -5
  108. package/dist/sandbox/guardian.d.ts +0 -18
  109. package/dist/sandbox/guardian.js +0 -58
  110. package/dist/sandbox/wasi.d.ts +0 -36
  111. package/dist/sandbox/wasi.js +0 -233
  112. package/dist/security/guardian.d.ts +0 -22
  113. package/dist/security/guardian.js +0 -52
  114. package/dist/security/zk.d.ts +0 -37
  115. package/dist/security/zk.js +0 -76
  116. package/dist/server/index.js +0 -1047
  117. package/dist/server/ner-scanner.d.ts +0 -29
  118. package/dist/server/ner-scanner.js +0 -141
  119. package/dist/server/pii.d.ts +0 -66
  120. package/dist/server/pii.js +0 -428
  121. package/dist/utils/logger.d.ts +0 -21
  122. package/dist/utils/logger.js +0 -70
  123. package/dist/utils/mcpCompact.d.ts +0 -11
  124. package/dist/utils/mcpCompact.js +0 -29
@@ -1,249 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { LiopServer } from "../server/index.js";
3
- import { log } from "../utils/logger.js";
4
- /**
5
- * LIOP MCP Bridge
6
- * A bi-directional bridge that allows legacy MCP servers to join the LIOP mesh,
7
- * or exposes a LIOP server as an MCP-compatible stdio process for tools like Claude Desktop.
8
- */
9
- export class LiopMcpBridge {
10
- options;
11
- liopServer = null;
12
- legacyMcpServer = null;
13
- constructor(source, options = {}) {
14
- this.options = options;
15
- // Determine mode: Exposing LIOP to MCP (Claude) or Wrapping MCP to LIOP (Mesh)
16
- if (source instanceof LiopServer) {
17
- this.liopServer = source;
18
- log.info("[LIOP-Bridge] Mode: EXPOSE (LIOP -> MCP Stdio)");
19
- }
20
- else if (source instanceof McpServer) {
21
- this.legacyMcpServer = source;
22
- log.info("[LIOP-Bridge] Mode: WRAP (Legacy MCP -> LIOP Mesh)");
23
- }
24
- }
25
- /**
26
- * Handles an incoming standard MCP JSON-RPC 2.0 payload.
27
- * Pipes it to the underlying server (LIOP or Legacy MCP).
28
- */
29
- async handleJsonRpcRequest(payload) {
30
- const id = payload.id;
31
- const method = payload.method;
32
- const params = payload.params;
33
- if (payload.jsonrpc !== "2.0") {
34
- return this.errorResponse(id, -32600, "Invalid Request");
35
- }
36
- // Mode: EXPOSE (Standard behavior used by Claude Desktop)
37
- if (this.liopServer) {
38
- return this.handleLiopToMcp(id, method, params);
39
- }
40
- // Mode: WRAP (Redirecting via internal LiopServer after connect())
41
- if (this.legacyMcpServer && this.liopServer) {
42
- return this.handleLiopToMcp(id, method, params);
43
- }
44
- return this.errorResponse(id, -32601, "Bridge source not configured");
45
- }
46
- async handleLiopToMcp(id, method, params) {
47
- if (!this.liopServer)
48
- return null;
49
- if (method === "initialize") {
50
- return this.successResponse(id, {
51
- protocolVersion: "2025-11-25",
52
- capabilities: {
53
- prompts: {},
54
- resources: {},
55
- tools: {},
56
- },
57
- serverInfo: this.liopServer.getServerInfo(),
58
- });
59
- }
60
- if (method === "notifications/initialized")
61
- return undefined;
62
- if (method === "ping")
63
- return this.successResponse(id, {});
64
- if (method === "tools/list") {
65
- const tools = this.liopServer.listTools();
66
- return this.successResponse(id, { tools });
67
- }
68
- if (method === "resources/list") {
69
- const resources = this.liopServer.listResources();
70
- return this.successResponse(id, { resources });
71
- }
72
- if (method === "prompts/list") {
73
- const prompts = this.liopServer.listPrompts();
74
- return this.successResponse(id, { prompts });
75
- }
76
- if (method === "prompts/get") {
77
- if (!params?.name) {
78
- return this.errorResponse(id, -32602, "Missing prompt name");
79
- }
80
- try {
81
- const result = await this.liopServer.getPrompt({
82
- name: params.name,
83
- arguments: params.arguments,
84
- });
85
- return this.successResponse(id, result);
86
- }
87
- catch (err) {
88
- return this.errorResponse(id, -32000, err.message);
89
- }
90
- }
91
- if (method === "resources/read") {
92
- if (!params?.uri) {
93
- return this.errorResponse(id, -32602, "Missing resource URI");
94
- }
95
- try {
96
- const result = await this.liopServer.readResource(params.uri);
97
- return this.successResponse(id, result);
98
- }
99
- catch (err) {
100
- return this.errorResponse(id, -32000, err.message);
101
- }
102
- }
103
- if (method === "tools/call") {
104
- if (!params?.name) {
105
- return this.errorResponse(id, -32602, "Missing tool name");
106
- }
107
- const request = {
108
- name: params.name,
109
- arguments: params.arguments || {},
110
- };
111
- try {
112
- const result = await this.liopServer.callTool(request);
113
- const isVerified = await this.verifyZkReceipt(request, result);
114
- if (!isVerified) {
115
- return this.successResponse(id, {
116
- content: [
117
- {
118
- type: "text",
119
- text: "ALERT [LIOP ZERO-TRUST SHIELD] ZK Verification Failed. The mathematical ImageID does not match the original payload.",
120
- },
121
- ],
122
- isError: true,
123
- });
124
- }
125
- return this.successResponse(id, result);
126
- }
127
- catch (err) {
128
- return this.errorResponse(id, -32000, err.message);
129
- }
130
- }
131
- return this.errorResponse(id, -32601, "Method not found");
132
- }
133
- successResponse(id, result) {
134
- return { jsonrpc: "2.0", id, result };
135
- }
136
- errorResponse(id, code, message) {
137
- return { jsonrpc: "2.0", id, error: { code, message } };
138
- }
139
- async verifyZkReceipt(request, result) {
140
- if (!request.arguments?.payload ||
141
- typeof request.arguments.payload !== "string") {
142
- return true;
143
- }
144
- try {
145
- const payload = request.arguments.payload;
146
- const contentText = result.content[0]?.text;
147
- if (contentText && typeof contentText === "string") {
148
- try {
149
- const data = JSON.parse(contentText);
150
- if (data.image_id || data.zk_receipt) {
151
- // 1. Instantiate the Industrial Verifier ( backed by Piscina Worker Pool )
152
- const { LiopVerifier } = await import("../crypto/verifier.js");
153
- const verifier = new LiopVerifier();
154
- // 2. Delegate the heavy mathematical check (ZK Journal + Seal)
155
- const isAuthentic = await verifier.verifyZkReceipt(Buffer.from(payload, "utf-8"), data.image_id, Buffer.from(data.zk_receipt || "", "base64"));
156
- if (!isAuthentic) {
157
- return false;
158
- }
159
- data.audit_status =
160
- "VERIFIED: ZK-Receipt & ImageID Mathematically Verified by LiopMcpBridge";
161
- result.content[0].text = JSON.stringify(data);
162
- }
163
- }
164
- catch {
165
- // Output not JSON
166
- }
167
- }
168
- return true;
169
- }
170
- catch (e) {
171
- log.info("[LIOP-Bridge] ZK-Verifier Failure:", e);
172
- return false;
173
- }
174
- }
175
- /**
176
- * Connects the bridge via stdio or Mesh depending on mode.
177
- */
178
- async connect() {
179
- // In WRAP mode, we actually need to create a LiopServer and join the mesh
180
- if (this.legacyMcpServer) {
181
- const { LiopServer } = await import("../server/index.js");
182
- this.liopServer = new LiopServer(this.options.serverInfo || {
183
- name: "liop-bridge",
184
- version: "1.0.0",
185
- }, { security: this.options.security });
186
- if (this.options.publishToMesh) {
187
- await this.liopServer.connect();
188
- // Automatically Bridge Legacy Capabilities to LIOP Mesh
189
- // biome-ignore lint/suspicious/noExplicitAny: Internal legacy MCP properties are completely opaque and unexported
190
- const legacy = this.legacyMcpServer;
191
- // 1. Sync Tools
192
- if (legacy._registeredTools) {
193
- for (const [name, tool] of Object.entries(legacy._registeredTools)) {
194
- // biome-ignore lint/suspicious/noExplicitAny: Opaque legacy structure
195
- const t = tool;
196
- this.liopServer.tool(name, t.description || "", t.inputSchema || {},
197
- // biome-ignore lint/suspicious/noExplicitAny: Opaque legacy callback args
198
- async (args) => {
199
- return await t.handler(args);
200
- });
201
- }
202
- }
203
- // 2. Sync Resources
204
- if (legacy._registeredResources) {
205
- for (const [uri, resource] of Object.entries(legacy._registeredResources)) {
206
- // biome-ignore lint/suspicious/noExplicitAny: Opaque legacy structure
207
- const r = resource;
208
- this.liopServer.resource(r.name, uri, r.metadata?.description || "", r.metadata?.mimeType || "application/octet-stream", async () => {
209
- const res = await r.readCallback(new URL(uri));
210
- return res.contents[0].text;
211
- });
212
- }
213
- }
214
- }
215
- return;
216
- }
217
- // In EXPOSE mode, listen to stdio (Claude Desktop)
218
- const readline = await import("node:readline");
219
- const rl = readline.createInterface({
220
- input: process.stdin,
221
- output: process.stdout,
222
- terminal: false,
223
- });
224
- const shutdown = async () => {
225
- log.info("[LIOP-Bridge] Disconnecting session...");
226
- if (this.liopServer)
227
- await this.liopServer.close();
228
- process.exit(0);
229
- };
230
- rl.on("close", shutdown);
231
- process.on("SIGINT", shutdown);
232
- process.on("SIGTERM", shutdown);
233
- rl.on("line", async (line) => {
234
- if (!line.trim())
235
- return;
236
- try {
237
- const payload = JSON.parse(line);
238
- const response = await this.handleJsonRpcRequest(payload);
239
- if (response) {
240
- process.stdout.write(`${JSON.stringify(response)}\n`);
241
- }
242
- }
243
- catch (e) {
244
- log.error(`[LIOP-Bridge] Error: ${e.message}`);
245
- }
246
- });
247
- }
248
- }
249
- export * from "./stream.js";
@@ -1,210 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { serve } from "@hono/node-server";
3
- import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
4
- import { Hono } from "hono";
5
- import { cors } from "hono/cors";
6
- import { log } from "../utils/logger.js";
7
- import { LiopMcpBridge } from "./index.js";
8
- const DEFAULT_MAX_SESSIONS_PER_IP = 10;
9
- const DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
10
- const EVICTION_INTERVAL_MS = 60 * 1000; // Check every minute
11
- /**
12
- * LiopStreamBridge
13
- *
14
- * Exposes a LiopServer over a remote HTTP network using the industry-standard
15
- * MCP Streamable HTTP Transport + Hono JS.
16
- *
17
- * Supports concurrent multi-client connections via per-session transport instances (Map pattern).
18
- * External agents connect using only a URL + Bearer Token (Zero-Trust).
19
- *
20
- * Security hardening:
21
- * - Zero-Trust Bearer Token enforcement
22
- * - Per-IP rate limiting on session creation
23
- * - Automatic eviction of idle sessions (TTL)
24
- */
25
- export class LiopStreamBridge {
26
- options;
27
- app;
28
- httpServer = null;
29
- bridgeLogic;
30
- activeSessions;
31
- evictionTimer = null;
32
- maxSessionsPerIp;
33
- sessionTimeoutMs;
34
- constructor(internalServer, options = {}) {
35
- this.options = options;
36
- this.app = new Hono();
37
- this.bridgeLogic = new LiopMcpBridge(internalServer);
38
- this.activeSessions = new Map();
39
- this.maxSessionsPerIp =
40
- options.maxSessionsPerIp ?? DEFAULT_MAX_SESSIONS_PER_IP;
41
- this.sessionTimeoutMs =
42
- options.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
43
- this.setupRoutes();
44
- }
45
- /**
46
- * Creates a new per-session transport instance and wires it to the LIOPMcpBridge logic.
47
- */
48
- createSessionTransport(clientIp) {
49
- const transport = new WebStandardStreamableHTTPServerTransport({
50
- sessionIdGenerator: () => randomUUID(),
51
- onsessioninitialized: (sessionId) => {
52
- this.activeSessions.set(sessionId, {
53
- transport,
54
- lastActivity: Date.now(),
55
- clientIp,
56
- });
57
- log.info(`[LIOP-StreamBridge] Session opened: ${sessionId} (IP: ${clientIp})`);
58
- },
59
- });
60
- // Wire the transport's incoming messages to the LiopMcpBridge JSON-RPC router
61
- transport.onmessage = async (message) => {
62
- // Touch activity timestamp on every message
63
- if (transport.sessionId) {
64
- const entry = this.activeSessions.get(transport.sessionId);
65
- if (entry)
66
- entry.lastActivity = Date.now();
67
- }
68
- try {
69
- const result = await this.bridgeLogic.handleJsonRpcRequest(message);
70
- // Notifications return undefined — no response needed
71
- if (result !== undefined) {
72
- await transport.send(result);
73
- }
74
- }
75
- catch (err) {
76
- log.info("[LIOP-StreamBridge] JSON-RPC error:", err.message);
77
- }
78
- };
79
- transport.onclose = () => {
80
- if (transport.sessionId) {
81
- this.activeSessions.delete(transport.sessionId);
82
- log.info(`[LIOP-StreamBridge] Session closed: ${transport.sessionId}`);
83
- }
84
- };
85
- return transport;
86
- }
87
- /**
88
- * Returns the number of active sessions for a given IP.
89
- */
90
- countSessionsByIp(ip) {
91
- let count = 0;
92
- for (const entry of this.activeSessions.values()) {
93
- if (entry.clientIp === ip)
94
- count++;
95
- }
96
- return count;
97
- }
98
- /**
99
- * Extracts client IP from the request (supports X-Forwarded-For for reverse proxies).
100
- */
101
- getClientIp(c) {
102
- return (c.req.header("x-forwarded-for")?.split(",")[0]?.trim() ||
103
- c.req.header("x-real-ip") ||
104
- "unknown");
105
- }
106
- /**
107
- * Evicts sessions that have been idle longer than the configured timeout.
108
- */
109
- evictIdleSessions() {
110
- const now = Date.now();
111
- for (const [sessionId, entry] of this.activeSessions) {
112
- if (now - entry.lastActivity > this.sessionTimeoutMs) {
113
- log.info(`[LIOP-StreamBridge] Evicting idle session: ${sessionId}`);
114
- entry.transport.close().catch(() => {
115
- /* Swallow close errors */
116
- });
117
- this.activeSessions.delete(sessionId);
118
- }
119
- }
120
- }
121
- setupRoutes() {
122
- this.app.use("*", cors());
123
- // Initialize strict zero-trust token if not provided
124
- if (!process.env.ZERO_TRUST_TOKEN) {
125
- process.env.ZERO_TRUST_TOKEN = randomUUID();
126
- log.info("=".repeat(60));
127
- log.info("⚠️ STRICT ZERO-TRUST MODE ENABLED ⚠️");
128
- log.info("No ZERO_TRUST_TOKEN found in environment.");
129
- log.info("A secure ephemeral token has been generated for this session:");
130
- log.info(`Token: ${process.env.ZERO_TRUST_TOKEN}`);
131
- log.info("=".repeat(60));
132
- }
133
- // ZTA (Zero-Trust Architecture) Security Middleware
134
- this.app.use("/mcp", async (c, next) => {
135
- const auth = c.req.header("Authorization");
136
- const expectedToken = process.env.ZERO_TRUST_TOKEN;
137
- if (!auth?.startsWith("Bearer ") ||
138
- auth.split(" ")[1] !== expectedToken) {
139
- log.info("[LIOP-StreamBridge] ALERT: Access denied - Invalid Zero-Trust token.");
140
- return c.json({ error: "Unauthorized: LIOP Zero-Trust Policy Enforced" }, 401);
141
- }
142
- await next();
143
- });
144
- // Multi-Session Streamable HTTP Handler
145
- this.app.all("/mcp", async (c) => {
146
- const sessionId = c.req.header("mcp-session-id");
147
- // Route to existing session if session ID is present
148
- if (sessionId) {
149
- const existing = this.activeSessions.get(sessionId);
150
- if (!existing) {
151
- return c.json({ error: "Session not found" }, 404);
152
- }
153
- // Touch activity on every routed request
154
- existing.lastActivity = Date.now();
155
- const response = await existing.transport.handleRequest(c.req.raw);
156
- // If DELETE, the transport closes internally but onclose may not fire.
157
- // Explicitly clean up the session from the Map.
158
- if (c.req.method === "DELETE") {
159
- this.activeSessions.delete(sessionId);
160
- log.info(`[LIOP-StreamBridge] Session closed (DELETE): ${sessionId}`);
161
- }
162
- return response;
163
- }
164
- // No session ID → New client initializing.
165
- // Rate-limit: enforce max sessions per IP
166
- const clientIp = this.getClientIp(c);
167
- const currentSessions = this.countSessionsByIp(clientIp);
168
- if (currentSessions >= this.maxSessionsPerIp) {
169
- log.info(`[LIOP-StreamBridge] Rate limit hit for IP: ${clientIp} (${currentSessions} sessions)`);
170
- return c.json({ error: "Too Many Sessions: Rate limit exceeded" }, 429);
171
- }
172
- const transport = this.createSessionTransport(clientIp);
173
- return transport.handleRequest(c.req.raw);
174
- });
175
- }
176
- /**
177
- * Starts the LiopStreamBridge HTTP server and session eviction timer.
178
- */
179
- async start(port) {
180
- const listenPort = port ?? this.options.port ?? 3000;
181
- // Start the idle session eviction timer
182
- this.evictionTimer = setInterval(() => this.evictIdleSessions(), EVICTION_INTERVAL_MS);
183
- return new Promise((resolve) => {
184
- this.httpServer = serve({
185
- fetch: this.app.fetch,
186
- port: listenPort,
187
- }, (info) => {
188
- log.info(`[LIOP-StreamBridge] Streamable HTTP Gateway on http://localhost:${info.port}/mcp`);
189
- resolve();
190
- });
191
- });
192
- }
193
- /**
194
- * Graceful shutdown — closes all active sessions, stops timers, and releases port.
195
- */
196
- async stop() {
197
- if (this.evictionTimer) {
198
- clearInterval(this.evictionTimer);
199
- this.evictionTimer = null;
200
- }
201
- for (const [id, entry] of this.activeSessions) {
202
- await entry.transport.close();
203
- this.activeSessions.delete(id);
204
- }
205
- if (this.httpServer) {
206
- this.httpServer.close();
207
- log.info("[LIOP-StreamBridge] HTTP ports released.");
208
- }
209
- }
210
- }