// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { readEnv } from './util'; import { getLogger } from './logger'; const INSTRUCTIONS_CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes interface InstructionsCacheEntry { fetchedInstructions: string; fetchedAt: number; } const instructionsCache = new Map(); export async function getInstructions(stainlessApiKey: string | undefined): Promise { const now = Date.now(); const cacheKey = stainlessApiKey ?? ''; const cached = instructionsCache.get(cacheKey); if (cached && now - cached.fetchedAt <= INSTRUCTIONS_CACHE_TTL_MS) { return cached.fetchedInstructions; } // Evict stale entries so the cache doesn't grow unboundedly. for (const [key, entry] of instructionsCache) { if (now - entry.fetchedAt > INSTRUCTIONS_CACHE_TTL_MS) { instructionsCache.delete(key); } } const fetchedInstructions = await fetchLatestInstructions(stainlessApiKey); instructionsCache.set(cacheKey, { fetchedInstructions, fetchedAt: now }); return fetchedInstructions; } async function fetchLatestInstructions(stainlessApiKey: string | undefined): Promise { // Setting the stainless API key is optional, but may be required // to authenticate requests to the Stainless API. const response = await fetch( readEnv('CODE_MODE_INSTRUCTIONS_URL') ?? 'https://api.stainless.com/api/ai/instructions/xray', { method: 'GET', headers: { ...(stainlessApiKey && { Authorization: stainlessApiKey }) }, }, ); let instructions: string | undefined; if (!response.ok) { getLogger().warn( 'Warning: failed to retrieve MCP server instructions. Proceeding with default instructions...', ); instructions = '\n This is the xray MCP server.\n\n Available tools:\n - search_docs: Search SDK documentation to find the right methods and parameters.\n - execute: Run TypeScript code against a pre-authenticated SDK client. Define an async run(client) function.\n\n Workflow:\n - If unsure about the API, call search_docs first.\n - Write complete solutions in a single execute call when possible. For large datasets, use API filters to narrow results or paginate within a single execute block.\n - If execute returns an error, read the error and fix your code rather than retrying the same approach.\n - Variables do not persist between execute calls. Return or log all data you need.\n - Individual HTTP requests to the API have a 30-second timeout. If a request times out, try a smaller query or add filters.\n - Code execution has a total timeout of approximately 5 minutes. If your code times out, simplify it or break it into smaller steps.\n '; } instructions ??= ((await response.json()) as { instructions: string }).instructions; instructions += `\nPrefer the use of the "create queries" (\`POST /v1/queries\`) endpoint for most operations, especially when it comes to more complex queries (e.g. counts, distincts, other aggregates, non-trivial filters), or when needing to fetch request logs that are further back in time. It takes arbitrary DuckDB-compatible SQL (which is mostly the same as Postgres). The struct of the the request logs table is nearly identical to the request logs returned by the \`/v1/request_logs\` API endpoints. When using the queries endpoint, always send the \`X-Xray-Tenant-Id: *\` header and value. Queries are inherently cross-tenant, so the API always requires that that a wildcard tenant ID (\`*\`) is sent. The queries endpoint returns a maximum of 1,000 rows, so if more results than that are needed, fall back to other endpoints. When querying the request logs list or retrieve endpoints, large fields like request and response bodies are omitted by default. To include them in the response, set the \`include\` query parameter to include \`request_body\` and/or \`response_body\`. `; return instructions; }