call_endpoint
Make HTTP requests to test mocked endpoints and inspect admin API services and health checks.
Instructions
Make an HTTP request to a URL and return {status, headers, body}. Use this to demonstrate a mock by hitting it after serve_locally (e.g. http://localhost:PORT/openapi/pet/findByStatus), to inspect the admin API (/.services returns the registered services, /healthz for liveness), or to verify a freshly-mocked endpoint works. Default scope is localhost only; pass allow_remote: true for arbitrary URLs (rare — the bridge isn't a general-purpose HTTP client).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | ||
| method | No | GET | |
| headers | No | ||
| body | No | ||
| allow_remote | No |
Implementation Reference
- lib/tools.js:114-144 (registration)Registration of the 'call_endpoint' tool in the LOCAL_TOOLS registry, with input schema (url, method, headers, body, allow_remote) and handler reference.
call_endpoint: { description: "Make an HTTP request to a URL and return {status, headers, body}. " + "Use this to demonstrate a mock by hitting it after `serve_locally` " + "(e.g. `http://localhost:PORT/openapi/pet/findByStatus`), to inspect " + "the admin API (`/.services` returns the registered services, " + "`/healthz` for liveness), or to verify a freshly-mocked endpoint " + "works. Default scope is localhost only; pass `allow_remote: true` " + "for arbitrary URLs (rare — the bridge isn't a general-purpose " + "HTTP client).", inputSchema: { type: "object", properties: { url: { type: "string" }, method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"], default: "GET", }, headers: { type: "object", additionalProperties: { type: "string" }, }, body: {}, allow_remote: { type: "boolean", default: false }, }, required: ["url"], additionalProperties: false, }, handler: callEndpoint, }, - lib/local.js:279-340 (handler)Actual handler implementation: validates URL localhost scope, constructs HTTP request (auto-sets Content-Type for JSON/text body), executes fetch with 10s timeout, returns response status/headers/body with 4KB truncation.
export async function callEndpoint(args) { const url = args.url; if (typeof url !== "string" || url.length === 0) { throw new Error("`url` must be a non-empty string"); } const method = (args.method || "GET").toUpperCase(); const headers = args.headers && typeof args.headers === "object" ? args.headers : {}; const body = args.body; // Default to localhost-only to keep this tool firmly in the local // plane. Users who want to call an arbitrary URL can pass // `allow_remote: true`, but we want the agent to think twice — the // bridge isn't a general-purpose HTTP client. if (!args.allow_remote && !isLocalhost(url)) { throw new Error( `Refusing to call ${url}: only localhost URLs by default. ` + `Pass allow_remote: true to override.`, ); } const init = { method, headers }; if (body !== undefined && body !== null && method !== "GET" && method !== "HEAD") { init.body = typeof body === "string" ? body : JSON.stringify(body); if (!Object.keys(headers).some((h) => h.toLowerCase() === "content-type")) { init.headers = { ...headers, "Content-Type": typeof body === "string" ? "text/plain" : "application/json", }; } } const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(), 10_000); let res; try { res = await fetch(url, { ...init, signal: ctrl.signal }); } catch (err) { throw new Error(`request failed: ${err.message}`); } finally { clearTimeout(t); } const text = await res.text(); const truncated = text.length > 4000; const bodyOut = truncated ? `${text.slice(0, 4000)}…` : text; let parsed = null; if (!truncated) { try { parsed = JSON.parse(text); } catch { /* not JSON; bodyOut is the text */ } } return { status: res.status, status_text: res.statusText, headers: Object.fromEntries(res.headers), body: parsed ?? bodyOut, truncated, }; } - lib/tools.js:124-141 (schema)Input schema for call_endpoint: url (required), method (enum with default GET), headers (object), body (any), allow_remote (boolean, default false).
inputSchema: { type: "object", properties: { url: { type: "string" }, method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"], default: "GET", }, headers: { type: "object", additionalProperties: { type: "string" }, }, body: {}, allow_remote: { type: "boolean", default: false }, }, required: ["url"], additionalProperties: false, - lib/local.js:570-582 (helper)Helper function isLocalhost used by callEndpoint to enforce localhost-only default scope.
function isLocalhost(url) { try { const u = new URL(url); return ( u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "::1" || u.hostname === "[::1]" ); } catch { return false; } } - lib/tools.js:8-144 (registration)Import of the callEndpoint function from local.js into the tools registry module.
import { callEndpoint, clearMockEndpoints, listMockEndpoints, mockEndpoint, peekOpenapi, serveLocally, stopLocally, } from "./local.js"; import { bridgeStatus } from "./version.js"; export const LOCAL_TOOLS = { check_cli: { description: "Check whether the mockzilla CLI is available — either on the " + "system PATH, in the bridge's own cache (~/.cache/mockzilla-mcp/), " + "or via a `go run` invocation. Call FIRST when the user wants to " + "try mockzilla locally. If nothing resolves, the response carries " + "`install_options`; suggest `install_cli` to the user and ask them " + "which method (download / go-install / go-run) they prefer.", inputSchema: { type: "object", properties: {}, additionalProperties: false, }, handler: checkCli, }, install_cli: { description: "Install the mockzilla CLI for this user. Three methods — ASK the " + "user which one they want before calling:\n" + " • download (recommended): fetch the prebuilt binary for this " + "OS/arch from github.com/mockzilla/mockzilla releases (~38MB). " + "Fast, no toolchain needed.\n" + " • go-install: run `go install <module>@v<version>` to compile " + "from source. Needs Go on PATH.\n" + " • go-run: don't install at all — the bridge stores a `go run " + "<module>@v<version>` invocation. First serve_locally compiles " + "into Go's module cache; later runs are instant. Needs Go.\n" + "Files land in the bridge's own cache, never on system PATH; " + "blow it away with `rm -rf ~/.cache/mockzilla-mcp`.", inputSchema: { type: "object", properties: { method: { type: "string", enum: ["download", "go-install", "go-run"], default: "download", }, }, additionalProperties: false, }, handler: installCli, }, serve_locally: { description: "Start ONE mockzilla portable mock server on this machine that " + "serves any number of APIs together — no mockzilla account " + "needed. Pass `input` as a single spec path / directory / public " + "https URL, OR an array of them to combine multiple APIs into the " + "same server (each becomes a service mounted at /<service>/...). " + "Returns {url, port, pid, services} once listening. Pair with " + "`stop_locally(pid)` to clean up. Prefer this over " + "`deploy_mock_from_*` whenever the user says 'try locally', " + "'experiment', or 'play with' — those tools create persistent " + "hosted bundles, this one is ephemeral. The bridge only runs ONE " + "local server at a time on purpose: if the user wants more APIs, " + "stop the current server and restart with all of them in `input`.\n\n" + "If the user names a well-known API (stripe, twilio, github, " + "openai, slack, etc.) WITHOUT providing a URL, recall the public " + "OpenAPI spec URL from your training knowledge and pass that. Do " + "NOT pass a catalog ID or slug from `list_catalog_products` — " + "that catalog is for the HOSTED `deploy_mock_from_catalog` flow, " + "its ids are not URLs. Examples of public OpenAPI URLs:\n" + " • Stripe: https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.json\n" + " • Twilio: https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_api_v2010.json\n" + " • GitHub: https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json\n" + " • Petstore: https://petstore3.swagger.io/api/v3/openapi.json", inputSchema: { type: "object", properties: { input: { oneOf: [ { type: "string" }, { type: "array", items: { type: "string" }, minItems: 1 }, ], description: "Spec file path(s), directory, or public OpenAPI URL(s). " + "Pass an array to combine multiple APIs into one server.", }, port: { type: "integer", minimum: 0, maximum: 65535, description: "Port to bind on. Omit or pass 0 to let the OS pick a free port.", }, }, required: ["input"], additionalProperties: false, }, handler: serveLocally, }, call_endpoint: { description: "Make an HTTP request to a URL and return {status, headers, body}. " + "Use this to demonstrate a mock by hitting it after `serve_locally` " + "(e.g. `http://localhost:PORT/openapi/pet/findByStatus`), to inspect " + "the admin API (`/.services` returns the registered services, " + "`/healthz` for liveness), or to verify a freshly-mocked endpoint " + "works. Default scope is localhost only; pass `allow_remote: true` " + "for arbitrary URLs (rare — the bridge isn't a general-purpose " + "HTTP client).", inputSchema: { type: "object", properties: { url: { type: "string" }, method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"], default: "GET", }, headers: { type: "object", additionalProperties: { type: "string" }, }, body: {}, allow_remote: { type: "boolean", default: false }, }, required: ["url"], additionalProperties: false, }, handler: callEndpoint, },