import { z } from "zod" import { mcpCatalogResponseSchema, mcpConnectionsSchema, mcpSettingsSchema, mcpPolicyPreviewResponseSchema, mcpPollStorageCapacitySchema, type McpConnectionCreate } from "@mmapp/contracts/mcp" import { requestJson } from "./http-client" import { downloadHumanArtifact } from "./mcp-artifact" export const mcpRecordsSchema = z.object({ items: z.array(z.record(z.string(), z.unknown())), total: z.number().int(), nextCursor: z.string().nullable() }) export const mcpOverviewSchema = z.object({ connections: z.number(), calls: z.number(), errors: z.number(), approvals: z.number(), running: z.number(), enabled: z.boolean(), observedAt: z.string(), pollStorageCapacity:mcpPollStorageCapacitySchema.default({status:"unavailable"}) }) const deviceCatalogSchema = z.object({ servers: z.array(z.object({ id: z.number(), name: z.string(), host: z.string(), enabled: z.boolean() })), resources: z.array(z.object({ id: z.string(), title: z.string(), read: z.boolean(), create: z.boolean(), update: z.boolean(), delete: z.boolean(), fields: z.array(z.object({ key: z.string(), type: z.enum(["string", "number", "boolean"]), options: z.array(z.string()).optional(), min: z.number().optional(), max: z.number().optional(), maxLength: z.number().optional(), requiredOnCreate: z.boolean() })) })) }) export function mcpApi(base: string) { const get = async (path: string, schema: z.ZodType, signal?: AbortSignal): Promise => schema.parse(await requestJson(base, "/api/mcp" + path, { signal })) const send = async (path: string, body: unknown, schema: z.ZodType, method = "POST"): Promise => schema.parse(await requestJson(base, "/api/mcp" + path, { method, body: JSON.stringify(body) })) return { downloadArtifact: (receipt: unknown, signal?: AbortSignal) => downloadHumanArtifact(base, receipt, signal), coverage: (signal?: AbortSignal) => get("/coverage", z.object({ version: z.literal(1), routes: z.array(z.object({ method: z.string(), path: z.string(), status: z.enum(["mediated", "excluded", "pending"]), operations: z.array(z.string()), reason: z.string() })) }), signal), uploadRestoreArchive: async (file: File) => z.object({ archiveRef: z.string(), sha256: z.string(), sizeBytes: z.number(), expiresAt: z.string() }).parse(await requestJson(base, "/api/mcp/database/archives", { method: "POST", headers: { "Content-Type": "application/octet-stream" }, body: file })), restorePlan: (connectionId: string, archiveRef: string, expectedSha256: string) => send("/database/plan", { connectionId, archiveRef, expectedSha256 }, z.object({ planId: z.string(), state: z.string() })), restoreReceipt: (id: string) => get(`/database/receipts/${encodeURIComponent(id)}`, z.record(z.string(), z.unknown())), managementChannels: (signal?: AbortSignal) => get("/management-channels", z.object({ channels: z.array(z.record(z.string(), z.unknown())) }), signal), registerManagement: (input: unknown) => send("/management-channels", input, z.object({ id: z.string(), serverId: z.number(), expiresAt: z.string(), verification: z.string(), independence: z.string() })), revokeManagement: (id: string) => send(`/management-channels/${id}`, {}, z.object({ ok: z.literal(true) }), "DELETE"), recoverOperation: (id: string) => send(`/operations/${id}/recover`, {}, z.object({ planId: z.string(), state: z.string() })), checkRoutePath: (id: string, candidateId: string, proofPurpose: "community" | "control-path" = "community") => send(`/routing-profiles/${id}/check`, { candidateId, proofPurpose }, z.record(z.string(), z.unknown())), reconcileOperation: (id: string) => send(`/operations/${id}/reconcile`, {}, z.record(z.string(), z.unknown())), deviceCatalog: (connectionId: string, signal?: AbortSignal) => get("/devices/catalog?" + new URLSearchParams({ connectionId }), deviceCatalogSchema, signal), deviceRows: (connectionId: string, serverId: string, resource: string, offset = 0, signal?: AbortSignal) => get(`/devices/${encodeURIComponent(serverId)}/${encodeURIComponent(resource)}?` + new URLSearchParams({ connectionId, offset: String(offset) }), z.object({ items: z.array(z.record(z.string(), z.unknown())), total: z.number(), nextOffset: z.number().nullable() }), signal), devicePlan: (connectionId: string, serverId: string, resource: string, action: "create" | "update" | "delete", id: string | undefined, fields: Record) => send(`/devices/${encodeURIComponent(serverId)}/${encodeURIComponent(resource)}/plan`, { connectionId, action, id, fields }, z.object({ planId: z.string(), state: z.string() })), revalidate: (id: string) => send("/connections/" + id + "/revalidate", {}, mcpConnectionsSchema.shape.connections.element), apply: (id: string) => send("/plans/" + id + "/apply", {}, z.object({ id: z.string(), state: z.string() })), profilePlan: (input: unknown) => send("/routing-profiles/plan", input, z.object({ planId: z.string(), state: z.string() })), catalog: (signal?: AbortSignal) => get("/catalog", mcpCatalogResponseSchema, signal), connections: (signal?: AbortSignal) => get("/connections", mcpConnectionsSchema, signal), overview: (signal?: AbortSignal) => get("/overview", mcpOverviewSchema, signal), settings: (signal?: AbortSignal) => get("/settings", mcpSettingsSchema, signal), records: (kind: "requests" | "plans" | "operations" | "audit" | "drift", params: URLSearchParams, signal?: AbortSignal) => get("/" + kind + "?" + params, mcpRecordsSchema, signal), create: (input: McpConnectionCreate) => send("/connections", input, z.object({ token: z.string(), connection: mcpConnectionsSchema.shape.connections.element })), update: (id: string, input: McpConnectionCreate) => send("/connections/" + id, input, mcpConnectionsSchema.shape.connections.element, "PUT"), status: (id: string, status: "active" | "paused" | "revoked") => send(`/connections/${id}/status`, { status }, z.object({ ok: z.literal(true) })), rotate: (id: string) => send(`/connections/${id}/rotate`, {}, z.object({ token: z.string() })), check: (id: string) => send(`/connections/${id}/check`, {}, z.object({ ok: z.boolean(), checks: z.record(z.string(), z.boolean()), checkedAt: z.string(), protocolProbe: z.object({ status: z.enum(["passed", "failed", "skipped"]), elapsedMs: z.number(), reason: z.string().optional(), credentialType: z.literal("temporary-no-operations"), clientCredentialsVerified: z.literal(false) }).optional() })), grants: (id: string, signal?: AbortSignal) => get(`/connections/${id}/grants`, z.object({ grants: z.array(z.object({ id: z.string(), grant: z.record(z.string(), z.unknown()), revoked_at: z.string().nullable() })) }), signal), grant: (id: string, grant: unknown) => send(`/connections/${id}/grants`, grant, z.object({ token: z.string(), expiresAt: z.string() })), revokeGrant: (id: string) => send(`/grants/${id}`, {}, z.object({ ok: z.literal(true) }), "DELETE"), profiles: (signal?: AbortSignal) => get("/routing-profiles", z.object({ profiles: z.array(z.record(z.string(), z.unknown())) }), signal), pauseProfile: (id: string) => send(`/routing-profiles/${id}/pause`, {}, z.object({ ok: z.literal(true) })), decision: (id: string, approve: boolean) => send(`/plans/${id}/decision`, { approve }, z.object({ ok: z.literal(true) })), saveSettings: (settings: unknown) => send("/settings", settings, mcpSettingsSchema, "PUT"), pause: () => send("/pause", {}, z.object({ ok: z.literal(true) })), stopAutomation: () => send("/stop-automation", {}, z.object({ ok: z.literal(true) })), preview: (input: unknown) => send("/policy/preview", input, mcpPolicyPreviewResponseSchema), clients: (signal?: AbortSignal) => get("/oauth/clients", z.object({ clients: z.array(z.object({ id: z.string(), name: z.string(), redirect_uris: z.array(z.string()), enabled: z.boolean() })) }), signal), createClient: (name: string, redirectUris: string[]) => send("/oauth/clients", { name, redirectUris }, z.object({ clientId: z.string() })), disableClient: (id: string) => send(`/oauth/clients/${id}`, {}, z.object({ ok: z.literal(true) }), "DELETE"), consentRequest: (id: string, signal?: AbortSignal) => get("/oauth/requests/" + id, z.object({ id: z.string(), name: z.string(), redirect_uri: z.string(), scope: z.string() }), signal), consent: (requestId: string, connectionId: string, approve: boolean) => send("/oauth/consent", { requestId, connectionId, approve }, z.object({ redirectUrl: z.string().url() })), protections: (signal?: AbortSignal) => get("/protections", z.object({ protections: z.array(z.object({ id: z.string(), object_ref: z.object({ kind: z.string(), id: z.string(), serverId: z.number().optional(), protection: z.enum(["deny", "human-approval-required"]) }), reason: z.string() })) }), signal), protect: (object: unknown, reason: string) => send("/protections", { object, reason }, z.object({ id: z.string() })), removeProtection: async (id: string) => requestJson(base, "/api/mcp/protections/" + id, { method: "DELETE" }), } } export type McpApi = ReturnType