UNPKG

@civic/hub-bridge

Version:

Stdio <-> HTTP/SSE MCP bridge with Civic auth handling

127 lines 5.67 kB
export {}; // import { AbstractHook } from "@civic/hook-common"; // import type { HookResponse, ToolCall, HookContext } from "@civic/hook-common"; // import { logger } from "../utils/logger.js"; // // /** // * SessionRecoveryHook detects when the server has dropped the session // * and automatically recreates the client connection. // * // * This hook monitors responses for the specific error message: // * "Bad Request: No valid session ID provided or not an initialize request" // * which indicates the server has lost the session. // */ // export class SessionRecoveryHook extends AbstractHook { // private isRecovering = false; // // get name(): string { // return "SessionRecoveryHook"; // } // // /** // * Check if the error indicates a dropped session // */ // private isSessionDroppedError(error: unknown): boolean { // // Check for Error object with session drop message // if (error instanceof Error) { // const isSessionError = error.message.includes("No valid session ID provided or not an initialize request"); // if (isSessionError) { // logger.info(`SessionRecoveryHook: Detected session drop: ${error.message}`); // return true; // } // } // // return false; // } // // async processToolException(toolError: unknown, originalToolCall: ToolCall, context: HookContext): Promise<HookResponse> { // try { // // Skip if we're already in recovery process to avoid recursion // if (this.isRecovering) { // return { response: "continue" }; // } // // if (!context || !isPassthroughServerContext(context)) { // logger.error(`Hook ${this.name} is not running in PassthroughServerContext, skipping session recovery. Context: ${JSON.stringify(context)}`); // return { response: "continue" }; // } // // // Check if this error indicates a session drop // const isSessionDropped = this.isSessionDroppedError(toolError); // // if (isSessionDropped) { // logger.info(`SessionRecoveryHook: Starting session recovery for tool: ${originalToolCall.name}`); // // // Set recovery flag to prevent recursion // this.isRecovering = true; // // try { // const passthroughServerHookContext = getPassthroughServerContext(context)!; // // // Recreate the client // const newClient = await passthroughServerHookContext.recreateClient(); // // // Now retry the original tool call with the new client // try { // // Call the target client's tool with arguments (potentially modified by hook) // const toolCallWithArgs = { // ...originalToolCall, // arguments: // typeof originalToolCall.arguments === "object" && // originalToolCall.arguments !== null // ? (originalToolCall.arguments as Record<string, unknown>) // : undefined, // }; // const result = await newClient.callTool(toolCallWithArgs); // // logger.info(`SessionRecoveryHook: Tool call succeeded after recovery`); // // return { // response: "abort" as const, // body: result, // reason: "Session was invalid an needed to be recovered" as const, // }; // // } catch (retryError) { // logger.error(`SessionRecoveryHook: Tool call failed after recovery:`, retryError); // // // Return the retry error instead of the original session error // return { // response: "abort" as const, // body: { // jsonrpc: "2.0", // error: { // code: -32603, // message: "Tool call failed after session recovery.", // data: { // recoveryPerformed: true, // originalError: toolError, // retryError: retryError // } // }, // id: null // } // }; // } // // } finally { // // Always reset the recovery flag // this.isRecovering = false; // } // } // // // Continue with the original error if no session drop detected // return { response: "continue" }; // // } catch (processingError) { // logger.error("SessionRecoveryHook: Error in processToolException:", processingError); // // // Reset recovery flag on error // this.isRecovering = false; // // // Continue with original error on processing failure to avoid breaking the flow // return { response: "continue" }; // } // } // } //# sourceMappingURL=session-recovery-hook.js.map