Skip to main content
Glama

invoke_function

Test deployed functions via HTTP by sending requests with custom methods, headers, and bodies to verify functionality without building a frontend.

Instructions

Invoke a deployed function via HTTP. Returns the function's response body and status code. Useful for testing functions without building a frontend.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_idYesThe project ID
nameYesFunction name to invoke
methodNoHTTP method (default: POST)
bodyNoRequest body (string or JSON object)
headersNoAdditional headers to send

Implementation Reference

  • Main handler function that executes the invoke_function tool. Validates project exists, constructs HTTP request with headers and body, calls the deployed function via API, handles payment-required (402) errors, and returns formatted response with status code, duration, and response body.
    export async function handleInvokeFunction(args: {
      project_id: string;
      name: string;
      method?: string;
      body?: string | Record<string, unknown>;
      headers?: Record<string, string>;
    }): Promise<{ content: Array<{ type: "text"; text: string }>; isError?: boolean }> {
      const project = getProject(args.project_id);
      if (!project) return projectNotFound(args.project_id);
    
      const method = args.method || "POST";
      const requestHeaders: Record<string, string> = {
        apikey: project.service_key,
        ...(args.headers || {}),
      };
    
      const startTime = Date.now();
    
      const res = await apiRequest(`/functions/v1/${args.name}`, {
        method,
        headers: requestHeaders,
        body: method !== "GET" && method !== "HEAD" ? args.body : undefined,
      });
    
      const durationMs = Date.now() - startTime;
    
      if (res.is402) {
        const body = res.body as Record<string, unknown>;
        return {
          content: [
            {
              type: "text",
              text: `## Payment Required\n\nAPI call limit exceeded. Renew or upgrade your project.\n\n\`\`\`json\n${JSON.stringify(body, null, 2)}\n\`\`\``,
            },
          ],
        };
      }
    
      if (!res.ok) return formatApiError(res, "invoking function");
    
      const bodyStr = typeof res.body === "string"
        ? res.body
        : JSON.stringify(res.body, null, 2);
    
      const lines = [
        `## Function Response`,
        ``,
        `| Field | Value |`,
        `|-------|-------|`,
        `| status | ${res.status} |`,
        `| duration | ${durationMs}ms |`,
        ``,
        `**Response body:**`,
        `\`\`\`json`,
        bodyStr,
        `\`\`\``,
      ];
    
      return { content: [{ type: "text", text: lines.join("\n") }] };
    }
  • Input schema definition using Zod validation. Defines required parameters: project_id and name (function name), and optional parameters: method (HTTP method, defaults to POST), body (string or JSON object), and headers (additional HTTP headers).
    export const invokeFunctionSchema = {
      project_id: z.string().describe("The project ID"),
      name: z.string().describe("Function name to invoke"),
      method: z
        .string()
        .optional()
        .describe("HTTP method (default: POST)"),
      body: z
        .union([z.string(), z.record(z.unknown())])
        .optional()
        .describe("Request body (string or JSON object)"),
      headers: z
        .record(z.string())
        .optional()
        .describe("Additional headers to send"),
    };
  • src/index.ts:146-151 (registration)
    Tool registration with MCP server. Registers 'invoke_function' tool with its description, schema, and async handler wrapper that delegates to handleInvokeFunction.
    server.tool(
      "invoke_function",
      "Invoke a deployed function via HTTP. Returns the function's response body and status code. Useful for testing functions without building a frontend.",
      invokeFunctionSchema,
      async (args) => handleInvokeFunction(args),
    );
  • API request utility function used by the handler. Makes HTTP requests to the API endpoint, handles network errors, parses JSON/text responses, and returns standardized ApiResponse object with ok status, HTTP status code, and body.
    export async function apiRequest(
      path: string,
      opts: ApiRequestOptions = {},
    ): Promise<ApiResponse> {
      const { method = "GET", headers = {}, body, rawBody } = opts;
      const url = `${getApiBase()}${path}`;
    
      const fetchHeaders: Record<string, string> = { ...headers };
      let fetchBody: string | undefined;
    
      if (rawBody !== undefined) {
        fetchBody = rawBody;
      } else if (body !== undefined) {
        fetchHeaders["Content-Type"] = fetchHeaders["Content-Type"] || "application/json";
        fetchBody = JSON.stringify(body);
      }
    
      let res: Response;
      try {
        res = await fetch(url, {
          method,
          headers: fetchHeaders,
          body: fetchBody,
        });
      } catch (err) {
        return {
          ok: false,
          status: 0,
          body: { error: `Network error: ${(err as Error).message}` },
        };
      }
    
      let resBody: unknown;
      const contentType = res.headers.get("content-type") || "";
      if (contentType.includes("application/json")) {
        resBody = await res.json();
      } else {
        resBody = await res.text();
      }
    
      if (res.status === 402) {
        return { ok: false, is402: true, status: 402, body: resBody };
      }
    
      return { ok: res.ok, status: res.status, body: resBody };
    }
  • Error formatting utilities used by the handler. formatApiError creates agent-friendly error messages with status codes, hints, and actionable next steps. projectNotFound returns consistent error when project ID is not in keystore.
    export function formatApiError(
      res: { status: number; body: unknown },
      context: string,
    ): ToolResult {
      const body =
        res.body && typeof res.body === "object"
          ? (res.body as Record<string, unknown>)
          : null;
    
      // Primary message — try message (PostgREST), then error, then fallback
      const primary = body
        ? (body.message as string) || (body.error as string) || "Unknown error"
        : typeof res.body === "string"
          ? (res.body as string)
          : "Unknown error";
    
      const lines: string[] = [
        `Error ${context}: ${primary} (HTTP ${res.status})`,
      ];
    
      // Supplementary fields from the API response
      if (body) {
        if (body.hint) lines.push(`Hint: ${body.hint}`);
        if (body.retry_after)
          lines.push(`Retry after: ${body.retry_after} seconds`);
        if (body.expires_at) lines.push(`Expires: ${body.expires_at}`);
        if (body.renew_url) lines.push(`Renew URL: ${body.renew_url}`);
        if (body.usage) {
          const u = body.usage as Record<string, unknown>;
          const parts: string[] = [];
          if (u.api_calls !== undefined)
            parts.push(`API calls: ${u.api_calls}/${u.limit || "?"}`);
          if (u.storage_bytes !== undefined)
            parts.push(
              `Storage: ${u.storage_bytes}/${u.storage_limit || "?"} bytes`,
            );
          if (parts.length > 0) lines.push(`Usage: ${parts.join(", ")}`);
        }
      }
    
      // Actionable guidance based on HTTP status
      switch (res.status) {
        case 401:
          lines.push(
            `\nNext step: Re-provision the project with \`provision_postgres_project\`, or check that your service key is correct.`,
          );
          break;
        case 403:
          lines.push(
            `\nNext step: The project lease may have expired. Use \`get_usage\` to check status, or \`renew_project\` to extend the lease.`,
          );
          break;
        case 404:
          lines.push(
            `\nNext step: Check that the resource name and project ID are correct.`,
          );
          break;
        case 429:
          lines.push(`\nNext step: Rate limit hit. Wait and retry.`);
          break;
        default:
          if (res.status >= 500) {
            lines.push(`\nNext step: Server error. Try again in a moment.`);
          }
      }
    
      return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
    }
    
    /**
     * Consistent "project not found in key store" error.
     */
    export function projectNotFound(projectId: string): ToolResult {
      return {
        content: [
          {
            type: "text",
            text:
              `Error: Project \`${projectId}\` not found in key store. ` +
              `Use \`provision_postgres_project\` to create a project first.`,
          },
        ],
        isError: true,
      };
    }

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/kychee-com/run402'

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