diff --git a/mcp/src/config.ts b/mcp/src/config.ts index 34484822..ed70bbc1 100644 --- a/mcp/src/config.ts +++ b/mcp/src/config.ts @@ -9,6 +9,7 @@ export interface HonchoConfig { export interface Env { HONCHO_API_URL?: string; + ALERT_WEBHOOK_URL?: string; } /** diff --git a/mcp/src/index.ts b/mcp/src/index.ts index 52123dee..3c679dd8 100644 --- a/mcp/src/index.ts +++ b/mcp/src/index.ts @@ -20,6 +20,7 @@ const CORS_HEADERS = { }; const PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource"; +const HEALTHCHECK_TIMEOUT_MS = 15_000; function resourceUrl(request: Request): string { return new URL(request.url).origin; @@ -30,6 +31,41 @@ function authorizationServer(env: Env): string { } export default { + // Probes the authorization server API to confirm it is reachable and healthy. + async scheduled( + _controller: ScheduledController, + env: Env, + _executionCtx: ExecutionContext, + ): Promise { + const upstream = `${authorizationServer(env)}/health`; + + let failure: string | null = null; + try { + const response = await fetch(upstream, { + signal: AbortSignal.timeout(HEALTHCHECK_TIMEOUT_MS), + }); + if (!response.ok) failure = `HTTP ${response.status}`; + } catch (e) { + failure = e instanceof Error ? e.message : String(e); + } + if (!failure) return; + + console.error(`healthcheck failed: ${upstream} — ${failure}`); + if (!env.ALERT_WEBHOOK_URL) return; + + const alertResponse = await fetch(env.ALERT_WEBHOOK_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + content: `🚨 honcho-mcp cannot reach upstream API (${upstream}): ${failure}`, + }), + signal: AbortSignal.timeout(HEALTHCHECK_TIMEOUT_MS), + }); + if (!alertResponse.ok) { + throw new Error(`alert webhook failed: HTTP ${alertResponse.status}`); + } + }, + async fetch( request: Request, env: Env, diff --git a/mcp/wrangler.toml b/mcp/wrangler.toml index 987b0b6b..04fe7894 100644 --- a/mcp/wrangler.toml +++ b/mcp/wrangler.toml @@ -8,6 +8,10 @@ rules = [ { type = "Text", globs = ["**/*.md"], fallthrough = true }, ] +# Executes the scheduled() handler +[triggers] +crons = ["*/5 * * * *"] + [env.production] name = "honcho-mcp"