Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
urlYes
methodNoGET
headersNo
bodyNo
allow_remoteNo

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,
    },
  • 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,
      };
    }
  • 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,
  • 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,
      },
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It reveals the return shape ({status, headers, body}), default localhost-only scope, and the rarity of allow_remote. However, it could mention rate limits or permissions for making external requests, but the explicit security constraint (localhost default) compensates, making it mostly transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured paragraph of 4 sentences. It front-loads the main action (HTTP request) and follows with specific use cases and constraints. Every sentence adds value without redundancy, making it concise and easily scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema or annotations, the description covers all necessary aspects: return format, typical use cases, security scope, and limitation (not general-purpose). It is sufficient for an agent to understand and correctly invoke the tool, addressing the complexity of making an HTTP request.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% property descriptions, so the description must compensate. It provides meaning for url (example localhost URLs), method (implicit via enum), and allow_remote (rare). Headers and body are not elaborated beyond the schema, but the overall context of making an HTTP request gives them implicit meaning. The description adds value but does not fully detail all parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's core function: making an HTTP request and returning status, headers, body. It distinguishes itself from sibling tools like mock_endpoint or peek_openapi by providing specific use cases (testing mocks, inspecting admin API, verifying endpoints). This satisfies the specific verb+resource requirement and differentiates from alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly outlines when to use the tool (demonstrate a mock, inspect admin API, verify mock endpoint) and when not to (the bridge is not a general-purpose HTTP client, allow_remote is rare). This provides clear context and exclusions, guiding the agent to appropriate usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mockzilla/mockzilla-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server