Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
TypeScript
at incident-api
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";import type { Scope, Workspace } from "@openstatus/db/src/schema";import { type ServiceContext, ServiceError } from "@openstatus/services";import { ZodError } from "zod";
import { tb } from "@/libs/clients";
/** * Build a `ServiceContext` for an MCP-originated request. * * `apiKey.id` is the real key identifier captured by the auth * middleware โ audit rows read it via `actor.keyId` so mutations * attribute to the specific key, not the workspace. * `apiKey.createdById` is the openstatus user who created the API key * (custom keys only); propagated to `actor.userId` so * `audit_log.actor_user_id` is populated. * * `actor.type` is `"mcp"` (not `"apiKey"`) so audit logs can be sliced * by transport surface without leaning on side-channel metadata. */export function toServiceCtx(args: { workspace: Workspace; apiKey: { id: string; createdById?: number; scopes: Scope[] }; requestId?: string;}): ServiceContext { return { workspace: args.workspace, actor: { type: "mcp", keyId: args.apiKey.id, userId: args.apiKey.createdById, scopes: args.apiKey.scopes, }, requestId: args.requestId, tb, };}
/** * Convert any error from a tool's `execute` body into either a * recoverable `CallToolResult` (so the LLM can retry with corrected * input) or a thrown `McpError` (so the JSON-RPC envelope surfaces a * transport-level failure). Mirrors the discrimination in * `apps/server/src/routes/rpc/adapter.ts`. */export function mapError(err: unknown): CallToolResult { // ZodError surfaces from a service's `Input.parse(args.input)` โ // either a type mismatch the LLM passed or a refine() failure // (e.g. `from > to` on `CreateMaintenanceInput`). Both are // recoverable: the LLM can read the message and retry. Treated // the same as `ServiceError.VALIDATION`. if (err instanceof ZodError) { return { isError: true, content: [{ type: "text", text: `Invalid input: ${err.message}` }], }; } if (err instanceof ServiceError) { switch (err.code) { case "NOT_FOUND": case "VALIDATION": case "CONFLICT": case "LIMIT_EXCEEDED": case "PRECONDITION_FAILED": // PRECONDITION_FAILED is recoverable in the LLM sense: the // user can take a different action (cancel a subscription, // free a quota slot) and try again. Surfacing it as // `isError: true` lets the assistant relay the message to // the user instead of retrying with mangled arguments, // which is what `InvalidParams` would prompt. return { isError: true, content: [{ type: "text", text: err.message }], }; case "UNAUTHORIZED": case "FORBIDDEN": case "INTERNAL": throw new McpError(serviceErrorToMcpCode(err.code), err.message); } } const message = err instanceof Error ? err.message : "Unknown error"; throw new McpError(ErrorCode.InternalError, message);}
function serviceErrorToMcpCode( code: "UNAUTHORIZED" | "FORBIDDEN" | "INTERNAL",): ErrorCode { switch (code) { case "UNAUTHORIZED": case "FORBIDDEN": // JSON-RPC has no dedicated auth code; `InvalidRequest` is the // closest standard fit and matches what other MCP servers // return for permission failures. return ErrorCode.InvalidRequest; case "INTERNAL": return ErrorCode.InternalError; }}
/** * Run a tool body through the error mapper. The unrecoverable branch * throws synchronously inside `mapError`, so the outer `try` catches it * and re-throws to the SDK; recoverable errors return a `CallToolResult` * that the SDK forwards as the tool's response. */export async function runTool<T>( fn: () => Promise<T>, toContent: (value: T) => CallToolResult,): Promise<CallToolResult> { try { const value = await fn(); return toContent(value); } catch (err) { return mapError(err); }}