import type { ApiKey, McpServer, RuntimeLog } from "@/lib/types"; export const localDemoUserId = "local-preview"; declare global { // Shared by Next route bundles in local demo mode. var __astrailLocalGeneratedServers: Map | undefined; } function localGeneratedServers() { globalThis.__astrailLocalGeneratedServers ??= new Map(); return globalThis.__astrailLocalGeneratedServers; } export function saveLocalGeneratedServer(server: McpServer) { localGeneratedServers().set(server.id, server); return server; } export function findLocalGeneratedServer(serverId: string) { return localGeneratedServers().get(serverId) ?? null; } export function updateLocalDemoServer(serverId: string, updates: Partial) { const server = localDemoServers().find((item) => item.id === serverId); if (!server) return null; return saveLocalGeneratedServer({ ...server, ...updates }); } export function localDemoServers(): McpServer[] { const now = new Date().toISOString(); const generatedServers = Array.from(localGeneratedServers().values()) .sort((left, right) => String(right.created_at ?? "").localeCompare(String(left.created_at ?? ""))); const securityFixtures: McpServer[] = process.env.ASTRAIL_ENABLE_LOCAL_SECURITY_FIXTURES === "1" ? [ { id: "security-private", user_id: localDemoUserId, name: "Security private MCP fixture", description: "Smoke-test private endpoint auth and runtime policy.", source_url: "https://example.com/openapi.json", source_type: "openapi_url", category: "Security", generated_code: null, tools_json: [ { name: "private_status", description: "Private read fixture.", input_schema: { type: "object", properties: {} }, method: "GET", path: "/", policy: "block", }, { name: "write_probe", description: "Write fixture used to verify actor-role enforcement through endpoint catalog calls.", input_schema: { type: "object", properties: {} }, method: "PATCH", path: "/api/security-smoke/write-probe", policy: "allow", }, { name: "invoke_api_endpoint", description: "Invoke one reviewed endpoint by ID.", input_schema: { type: "object", properties: { endpoint_id: { type: "string" }, arguments: { type: "object" }, }, required: ["endpoint_id"], }, method: "ASTRAIL_META", path: "/_meta/invoke", policy: "allow", }, { name: "ssrf_probe", description: "Blocked SSRF target fixture.", input_schema: { type: "object", properties: {} }, method: "GET", path: "/metadata", }, { name: "credential_error", description: "Credential redaction fixture.", input_schema: { type: "object", properties: {} }, method: "GET", path: "/not-found", }, { name: "argument_secret_echo", description: "Sensitive argument redaction fixture.", input_schema: { type: "object", properties: { access_token: { type: "string", minLength: 8, "x-astrail-name": "x-echo-value", "x-astrail-in": "header", }, }, required: ["access_token"], additionalProperties: false, }, method: "GET", path: "/api/security-smoke/argument-secret-echo", }, ], endpoint_map: [ { method: "GET", path: "/", base_url: "https://example.com", tool_name: "private_status", operation_id: "private_status", summary: "Private status", description: "Private read fixture.", parameters: [], input_schema: { type: "object", properties: {} }, operation_kind: "read", requires_auth: false, }, { method: "PATCH", path: "/api/security-smoke/write-probe", base_url: process.env.ASTRAIL_SECURITY_SMOKE_UPSTREAM_BASE_URL ?? "http://127.0.0.1:3000", tool_name: "write_probe", operation_id: "write_probe", summary: "Write probe", description: "Must be denied for read-only actor roles, including through invoke_api_endpoint.", parameters: [], input_schema: { type: "object", properties: {} }, operation_kind: "write", requires_auth: false, }, { method: "GET", path: "/metadata", base_url: "http://169.254.169.254", tool_name: "ssrf_probe", operation_id: "ssrf_probe", summary: "SSRF probe", description: "Blocked metadata service target.", parameters: [], input_schema: { type: "object", properties: {} }, operation_kind: "read", requires_auth: false, }, { method: "GET", path: "/not-found", base_url: "https://example.com", tool_name: "credential_error", operation_id: "credential_error", summary: "Credential error", description: "Forces an upstream error while provider credentials are injected.", parameters: [], input_schema: { type: "object", properties: {} }, operation_kind: "read", requires_auth: true, }, { method: "GET", path: "/api/security-smoke/argument-secret-echo", base_url: process.env.ASTRAIL_SECURITY_SMOKE_UPSTREAM_BASE_URL ?? "http://127.0.0.1:3000", tool_name: "argument_secret_echo", operation_id: "argument_secret_echo", summary: "Sensitive argument echo", description: "Echoes a sensitive tool argument through a non-sensitive upstream header name.", parameters: [ { name: "x-echo-value", in: "header", required: true, schema: { type: "string" } }, ], input_schema: { type: "object", properties: { access_token: { type: "string", minLength: 8, "x-astrail-name": "x-echo-value", "x-astrail-in": "header", }, }, required: ["access_token"], additionalProperties: false, }, operation_kind: "read", requires_auth: false, }, ], diagnostics: ["Local security smoke fixture."], status: "live", validation_status: "passed", generation_status: "completed", is_public: false, hosted_endpoint: "/api/mcp/security-private", call_count: 0, protocol_version: "2024-11-05", runtime_policy: { roles: { operator: { max_action_level: "read" } }, }, created_at: now, }, { id: "security-public", user_id: localDemoUserId, name: "Security public MCP fixture", description: "Smoke-test public endpoint filtering and permission denial.", source_url: "https://example.com/openapi.json", source_type: "openapi_url", category: "Security", generated_code: null, tools_json: [ { name: "public_echo", description: "Public read fixture.", input_schema: { type: "object", properties: { q: { type: "string", description: "Public query." }, }, required: ["q"], additionalProperties: false, }, method: "GET", path: "/", x_astrail: { visibility: "public", risk: "read", requires_auth: false, auth_schemes: [], required_scopes: [], prerequisites: [], agent_instructions: ["Read-only public fixture."], example_arguments: { q: "hello" }, }, }, { name: "private_delete_everything", description: "Private destructive fixture SECRET_DO_NOT_LEAK.", input_schema: { type: "object", properties: { confirmation: { type: "string", description: "SECRET_DO_NOT_LEAK" }, }, required: ["confirmation"], }, method: "DELETE", path: "/private", visibility: "private", x_astrail: { visibility: "private", risk: "destructive", requires_auth: true, auth_schemes: ["bearer"], required_scopes: ["SECRET_DO_NOT_LEAK"], prerequisites: ["SECRET_DO_NOT_LEAK"], agent_instructions: ["SECRET_DO_NOT_LEAK"], example_arguments: { confirmation: "SECRET_DO_NOT_LEAK" }, }, }, ], endpoint_map: [ { method: "GET", path: "/", base_url: "https://example.com", tool_name: "public_echo", operation_id: "public_echo", summary: "Public echo", description: "Public read fixture.", parameters: [ { name: "q", in: "query", required: true, schema: { type: "string" } }, ], input_schema: { type: "object", properties: { q: { type: "string" }, }, required: ["q"], additionalProperties: false, }, operation_kind: "read", requires_auth: false, visibility: "public", }, { method: "DELETE", path: "/private", base_url: "https://example.com", tool_name: "private_delete_everything", operation_id: "private_delete_everything", summary: "Private destructive endpoint", description: "SECRET_DO_NOT_LEAK", parameters: [], input_schema: { type: "object", properties: { confirmation: { type: "string" }, }, required: ["confirmation"], }, operation_kind: "destructive", requires_auth: true, visibility: "private", }, ], diagnostics: ["Local security smoke fixture."], status: "live", validation_status: "passed", generation_status: "completed", is_public: true, hosted_endpoint: "/api/mcp/security-public", call_count: 0, protocol_version: "2024-11-05", created_at: now, }, ] : []; const demoServers: McpServer[] = [ { id: "local-website-mcp", user_id: localDemoUserId, name: "Hacker News browser server", description: "Website-to-MCP preview generated from a public website.", source_url: "https://news.ycombinator.com", source_type: "website", category: "Website", generated_code: null, tools_json: [ { name: "browser_open_page", description: "Open the page and summarize visible public content.", input_schema: { type: "object", properties: {} }, method: "BROWSER", path: "body", }, ], endpoint_map: [ { method: "BROWSER", path: "body", runtime_kind: "browser", browser_action: "open_page", selector: "body", target_url: "https://news.ycombinator.com/", tool_name: "browser_open_page", operation_id: "browser_open_page", summary: "Open page", description: "Open the inspected website and return a public page summary.", parameters: [], requires_auth: false, }, ], diagnostics: ["Local demo server. Connect persistent workspace storage to save generated endpoint details."], status: "live", validation_status: "passed", generation_status: "completed", is_public: true, hosted_endpoint: "/api/mcp/local-website-mcp", call_count: 128, protocol_version: "2024-11-05", created_at: now, }, { id: "local-openapi", user_id: localDemoUserId, name: "Petstore OpenAPI server", description: "Demo endpoint map generated from the Swagger Petstore spec.", source_url: "https://petstore.swagger.io/v2/swagger.json", source_type: "openapi_url", category: "OpenAPI", generated_code: null, tools_json: [ { name: "list_inventory", description: "List inventory counts from the sample Petstore API.", input_schema: { type: "object", properties: {} }, method: "GET", path: "/store/inventory", }, ], endpoint_map: [ { method: "GET", path: "/store/inventory", base_url: "https://petstore.swagger.io/v2", tool_name: "list_inventory", operation_id: "list_inventory", summary: "List inventory", description: "List inventory counts from the sample Petstore API.", parameters: [], requires_auth: false, }, ], diagnostics: ["Local demo server. Connect persistent workspace storage to save generated endpoint details."], status: "live", validation_status: "passed", generation_status: "completed", is_public: true, hosted_endpoint: "/api/mcp/local-openapi", call_count: 42, protocol_version: "2024-11-05", created_at: now, }, { id: "local-code-mode", user_id: localDemoUserId, name: "Petstore Code Mode server", description: "Demo Code Mode MCP endpoint with search_docs and execute over a Petstore endpoint map.", source_url: "https://petstore.swagger.io/v2/swagger.json", source_type: "openapi_url", category: "OpenAPI", generated_code: null, tools_json: [ { name: "search_docs", description: "Search SDK-style documentation for the generated Petstore API.", input_schema: { type: "object", properties: { query: { type: "string", description: "Search text." }, limit: { type: "integer", description: "Maximum docs to return." }, }, }, method: "ASTRAIL_CODE", path: "search_docs", }, { name: "execute", description: "Run SDK-style TypeScript calls compiled through Astrail's no-eval endpoint map.", input_schema: { type: "object", properties: { code: { type: "string", description: "Example: async function run(client) { return await client.store.getInventory({}); }", }, }, required: ["code"], }, method: "ASTRAIL_CODE", path: "execute", }, ], endpoint_map: [ { method: "GET", path: "/store/inventory", base_url: "https://petstore.swagger.io/v2", tool_name: "petstore_openapi_server_get_inventory", operation_id: "getInventory", summary: "List inventory", description: "Returns pet inventories by status from the sample Petstore API.", tags: ["store"], resource: "store", operation_kind: "read", parameters: [], input_schema: { type: "object", properties: {} }, requires_auth: false, }, ], diagnostics: ["Local Code Mode demo server. Connect persistent workspace storage to save generated endpoint details."], status: "live", validation_status: "passed", generation_status: "completed", is_public: true, hosted_endpoint: "/api/mcp/local-code-mode", call_count: 17, protocol_version: "2024-11-05", created_at: now, }, { id: "runtime-permissions-demo", user_id: localDemoUserId, name: "Petstore Runtime Permissions demo", description: "Local demo endpoint for Astrail runtime method permissions.", source_url: "https://petstore.swagger.io/v2/swagger.json", source_type: "openapi_url", category: "OpenAPI", generated_code: null, tools_json: [ { name: "list_inventory", description: "List inventory counts from the sample Petstore API.", input_schema: { type: "object", properties: {} }, method: "GET", path: "/store/inventory", }, { name: "delete_pet", description: "Delete a pet from the sample Petstore API.", input_schema: { type: "object", properties: { petId: { type: "integer", description: "Pet ID to delete.", "x-astrail-name": "petId", "x-astrail-in": "path" }, }, required: ["petId"], }, method: "DELETE", path: "/pet/{petId}", }, { name: "search_docs", description: "Search SDK-style documentation for the generated Petstore API.", input_schema: { type: "object", properties: { query: { type: "string", description: "Search text." }, limit: { type: "integer", description: "Maximum docs to return." }, }, }, method: "ASTRAIL_CODE", path: "search_docs", }, { name: "execute", description: "Run SDK-style TypeScript calls compiled through Astrail's no-eval endpoint map.", input_schema: { type: "object", properties: { code: { type: "string", description: "Example: await client.store.getInventory({});" }, result_mode: { type: "string", enum: ["compact", "full"] }, }, required: ["code"], }, method: "ASTRAIL_CODE", path: "execute", }, ], endpoint_map: [ { method: "GET", path: "/store/inventory", base_url: "https://petstore.swagger.io/v2", tool_name: "list_inventory", operation_id: "getInventory", summary: "List inventory", description: "Returns pet inventories by status from the sample Petstore API.", tags: ["store"], resource: "store", operation_kind: "read", parameters: [], input_schema: { type: "object", properties: {} }, requires_auth: false, }, { method: "DELETE", path: "/pet/{petId}", base_url: "https://petstore.swagger.io/v2", tool_name: "delete_pet", operation_id: "deletePet", summary: "Delete pet", description: "Deletes a pet from the sample Petstore API.", tags: ["pet"], resource: "pet", operation_kind: "destructive", parameters: [{ name: "petId", in: "path", required: true, schema: { type: "integer" } }], input_schema: { type: "object", properties: { petId: { type: "integer", description: "Pet ID to delete.", "x-astrail-name": "petId", "x-astrail-in": "path" }, }, required: ["petId"], }, requires_auth: false, }, ], runtime_policy: { read_only: true, allow_http_gets: true, allowed_resources: [{ pattern: "^store$", regex: true, match: "resource" }], blocked_methods: [ "client.pet.deletePet", { pattern: "delete_pet", match: "endpoint_id" }, ], }, diagnostics: ["Local runtime permissions demo server."], status: "live", validation_status: "passed", generation_status: "completed", is_public: true, hosted_endpoint: "/api/mcp/runtime-permissions-demo", call_count: 0, protocol_version: "2024-11-05", created_at: now, }, ]; const generatedIds = new Set(generatedServers.map((server) => server.id)); return [ ...generatedServers, ...securityFixtures, ...demoServers.filter((server) => !generatedIds.has(server.id)), ]; } export function localDemoApiKeys(): ApiKey[] { return [ { id: "00000000-0000-4000-8000-000000000001", user_id: localDemoUserId, name: "Anthropic demo key", key_preview: "ag_demo...ready", last_used: new Date(Date.now() - 12 * 60 * 1000).toISOString(), created_at: new Date().toISOString(), }, ]; } export function localDemoLogs(): RuntimeLog[] { return [ { id: "log_1", server_id: "local-website-mcp", user_id: localDemoUserId, tool_name: "browser_open_page", status: "success", method: "BROWSER", path: "body", execution_mode: "website_browser_runtime", upstream_status: 200, trace_id: "agt_demo_browser_01", attempt_count: 1, error_code: null, error: null, latency_ms: 1180, created_at: new Date(Date.now() - 9 * 60 * 1000).toISOString(), }, { id: "log_2", server_id: "local-openapi", user_id: localDemoUserId, tool_name: "list_pets", status: "success", method: "GET", path: "/pets", execution_mode: "safe_rest_execution", upstream_status: 200, trace_id: "agt_demo_openapi_01", attempt_count: 1, error_code: null, error: null, latency_ms: 243, created_at: new Date(Date.now() - 38 * 60 * 1000).toISOString(), }, ]; }