mock
Generate mock data for API endpoints from OpenAPI specs to enable frontend development without a backend.
Instructions
Genera datos mock/fake para un endpoint basándose en su spec OpenAPI importada. Útil para frontend sin backend.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Nombre del API importada | |
| method | Yes | Método HTTP del endpoint | |
| path | Yes | Path del endpoint (ej: "/users", "/blog") | |
| target | No | Generar mock del body de request o de la response (default: response) | |
| status | No | Status code de la respuesta a mockear (default: "200" o "201") | |
| count | No | Número de items mock a generar si el schema es un array (default: 3) |
Implementation Reference
- src/tools/mock.ts:9-83 (handler)Main handler function 'generateMockData' that generates fake/mock data based on an OpenAPI schema. Handles objects, arrays, strings (with format-specific generation like date-time, email, uuid, etc.), numbers, and booleans.
function generateMockData(schema: ApiSpecSchema, depth = 0): unknown { if (depth > 8) return null // If example exists, use it if (schema.example !== undefined) return schema.example // If enum, pick first value if (schema.enum && schema.enum.length > 0) { return schema.enum[Math.floor(Math.random() * schema.enum.length)] } switch (schema.type) { case 'object': { if (!schema.properties) return {} const obj: Record<string, unknown> = {} for (const [key, propSchema] of Object.entries(schema.properties)) { obj[key] = generateMockData(propSchema, depth + 1) } return obj } case 'array': { if (!schema.items) return [] const count = Math.floor(Math.random() * 3) + 1 // 1-3 items return Array.from({ length: count }, () => generateMockData(schema.items!, depth + 1), ) } case 'string': { switch (schema.format) { case 'date-time': return new Date().toISOString() case 'date': return new Date().toISOString().split('T')[0] case 'email': return `user${Math.floor(Math.random() * 1000)}@example.com` case 'uri': case 'url': return 'https://example.com/resource' case 'uuid': return crypto.randomUUID() case 'ipv4': return `192.168.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}` default: { // Use description or key name for smarter generation const desc = (schema.description ?? '').toLowerCase() if (desc.includes('name') || desc.includes('nombre')) return `Test User ${Math.floor(Math.random() * 100)}` if (desc.includes('title') || desc.includes('título')) return `Test Title ${Math.floor(Math.random() * 100)}` if (desc.includes('description') || desc.includes('descripción')) return 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' if (desc.includes('password') || desc.includes('contraseña')) return 'TestPass123!' if (desc.includes('slug')) return `test-slug-${Math.floor(Math.random() * 1000)}` if (desc.includes('phone') || desc.includes('teléfono')) return '+34612345678' return `mock-string-${Math.floor(Math.random() * 10000)}` } } } case 'number': case 'integer': return Math.floor(Math.random() * 1000) case 'boolean': return Math.random() > 0.5 default: return null } } - src/tools/mock.ts:85-239 (handler)Tool handler function 'registerMockTool' that registers the 'mock' tool with the MCP server. Contains the main logic for looking up an API spec endpoint by method+path, extracting the request/response schema, and calling generateMockData to produce fake data.
export function registerMockTool(server: McpServer, storage: Storage): void { server.tool( 'mock', 'Genera datos mock/fake para un endpoint basándose en su spec OpenAPI importada. Útil para frontend sin backend.', { name: z.string().describe('Nombre del API importada'), method: z .enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']) .describe('Método HTTP del endpoint'), path: z.string().describe('Path del endpoint (ej: "/users", "/blog")'), target: z .enum(['request', 'response']) .optional() .describe('Generar mock del body de request o de la response (default: response)'), status: z .string() .optional() .describe('Status code de la respuesta a mockear (default: "200" o "201")'), count: z .number() .optional() .describe('Número de items mock a generar si el schema es un array (default: 3)'), }, async (params) => { try { const spec = await storage.getSpec(params.name) if (!spec) { return { content: [ { type: 'text' as const, text: `Error: API '${params.name}' no encontrada. Usa api_import para importarla primero.`, }, ], isError: true, } } const endpoint = spec.endpoints.find( (ep) => ep.method === params.method && ep.path === params.path, ) if (!endpoint) { return { content: [ { type: 'text' as const, text: `Error: Endpoint ${params.method} ${params.path} no encontrado en '${params.name}'.`, }, ], isError: true, } } const target = params.target ?? 'response' let schema: ApiSpecSchema | undefined if (target === 'request') { // Get request body schema const content = endpoint.requestBody?.content if (content) { const jsonContent = content['application/json'] schema = jsonContent?.schema } if (!schema) { return { content: [ { type: 'text' as const, text: `Error: El endpoint ${params.method} ${params.path} no tiene un body schema definido.`, }, ], isError: true, } } } else { // Get response body schema const statusCode = params.status ?? (params.method === 'POST' ? '201' : '200') const response = endpoint.responses?.[statusCode] if (!response) { // Try to find any 2xx response const twoXX = Object.keys(endpoint.responses ?? {}).find((s) => s.startsWith('2')) if (twoXX && endpoint.responses) { const fallbackResp = endpoint.responses[twoXX] const content = fallbackResp?.content if (content) { const jsonContent = content['application/json'] schema = jsonContent?.schema } } } else { const content = response.content if (content) { const jsonContent = content['application/json'] schema = jsonContent?.schema } } if (!schema) { return { content: [ { type: 'text' as const, text: `Error: No se encontró un schema de respuesta para ${params.method} ${params.path}.`, }, ], isError: true, } } } // Generate mock data let mockData: unknown if (schema.type === 'array' && params.count) { // Generate specific number of items mockData = Array.from({ length: params.count }, () => generateMockData(schema!.items ?? { type: 'object' }), ) } else { mockData = generateMockData(schema) } const label = target === 'request' ? 'REQUEST BODY' : 'RESPONSE' return { content: [ { type: 'text' as const, text: [ `Mock ${label} para ${params.method} ${params.path}:`, '', '```json', JSON.stringify(mockData, null, 2), '```', '', 'Datos generados automáticamente desde el spec OpenAPI.', target === 'request' ? 'Puedes usar estos datos directamente en un request.' : 'Estos son datos de ejemplo que devolvería el endpoint.', ].join('\n'), }, ], } } catch (error) { const message = error instanceof Error ? error.message : String(error) return { content: [{ type: 'text' as const, text: `Error: ${message}` }], isError: true, } } }, ) - src/lib/types.ts:122-132 (schema)Type definition for 'ApiSpecSchema' which describes the OpenAPI schema structure used by generateMockData (type, format, properties, items, enum, example, etc.).
export interface ApiSpecSchema { type?: string format?: string properties?: Record<string, ApiSpecSchema> items?: ApiSpecSchema required?: string[] enum?: unknown[] description?: string example?: unknown $ref?: string } - src/server.ts:69-70 (registration)Registration point where 'registerMockTool(server, storage)' is called in the MCP server factory.
registerMockTool(server, storage) registerLoadTestTool(server, storage) - src/__tests__/mock-fetch.ts:7-116 (helper)Test helper 'createMockFetch' and 'installMockFetch' for creating a mock fetch function used in tests (unrelated to the 'mock' tool itself, but shares naming).
export function createMockFetch() { return vi.fn(async (url: string | URL, init?: RequestInit) => { const urlStr = url.toString() const method = init?.method ?? 'GET' const bodyStr = init?.body as string | undefined // Parse request headers const reqHeaders: Record<string, string> = {} if (init?.headers) { const entries = init.headers instanceof Headers ? Array.from(init.headers.entries()) : Object.entries(init.headers as Record<string, string>) for (const [k, v] of entries) { reqHeaders[k] = v } } // Invalid URLs if (!urlStr.startsWith('http://') && !urlStr.startsWith('https://')) { throw new TypeError(`Invalid URL: ${urlStr}`) } // Simulate httpbin.org/get if (urlStr.includes('/get')) { const responseBody = { url: urlStr.split('?')[0], headers: { ...reqHeaders, ...(reqHeaders['Authorization'] ? { Authorization: reqHeaders['Authorization'] } : {}), }, args: Object.fromEntries(new URL(urlStr).searchParams.entries()), } return new Response(JSON.stringify(responseBody), { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'content-length': '256' }, }) } // Simulate httpbin.org/post if (urlStr.includes('/post')) { let json = null try { json = bodyStr ? JSON.parse(bodyStr) : null } catch { /* not JSON */ } const responseBody = { url: urlStr.split('?')[0], headers: reqHeaders, json, data: bodyStr ?? '', } return new Response(JSON.stringify(responseBody), { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'content-length': '256' }, }) } // Simulate httpbin.org/headers if (urlStr.includes('/headers')) { const responseBody = { headers: reqHeaders, } return new Response(JSON.stringify(responseBody), { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'content-length': '128' }, }) } // Simulate httpbin.org/status/{code} const statusMatch = urlStr.match(/\/status\/(\d+)/) if (statusMatch) { const code = parseInt(statusMatch[1]) return new Response('', { status: code, statusText: `Status ${code}`, headers: { 'content-length': '0' }, }) } // Default: 200 OK with echo return new Response(JSON.stringify({ method, url: urlStr }), { status: 200, statusText: 'OK', headers: { 'content-type': 'application/json', 'content-length': '64' }, }) }) } /** * Installs mock fetch globally. Call in beforeAll/beforeEach. * Returns the mock for assertions. */ export function installMockFetch() { const mockFetch = createMockFetch() vi.stubGlobal('fetch', mockFetch) return mockFetch } /** * Restores original fetch. Call in afterAll/afterEach. */ export function restoreFetch() { vi.unstubAllGlobals() }