xAI API

    There are two routes. In the first, xAI's platform connects to our MCP server. In the second, you give Grok our function definitions and your own code makes each call. For Grok Bot, Grok Build and the Grok chat app, see Grok Bot.

    Not open yet. Opens with early access.

    On this page

    Before you start

    • An xAI API key. The examples use grok-4.7, the model in xAI's own examples. Which models support remote MCP is not documented; replace it with yours.
    • An Understanding API key from the console. You need an account first. Keep it on your server.
    • No sign-in. xAI's remote MCP tool has no sign-in flow. It sends a token or headers that you give it.

    Set it up

    Route one: the remote MCP tool

    curl https://api.x.ai/v1/responses \
      -H "Authorization: Bearer $XAI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "grok-4.7",
        "input": "Is the merchant DRAFTKINGS, MCC 7995, a restricted merchant?",
        "tools": [{
          "type": "mcp",
          "server_url": "https://api.theunderstanding.company/mcp/grok",
          "server_label": "understanding",
          "server_description": "Closed questions about bank transactions and member support messages. Read-only. Each answer has a probability and an abstain flag.",
          "authorization": "'"$UNDERSTANDING_API_KEY"'",
          "allowed_tools": ["check_restricted_merchant", "classify_spend_type", "match_receipt", "pair_refund", "check_return_window", "route_support_message", "ask_closed_question"]
        }]
      }'
    import os
    import requests
    
    res = requests.post(
        "https://api.x.ai/v1/responses",
        headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
        json={
            "model": "grok-4.7",
            "input": "Is the merchant DRAFTKINGS, MCC 7995, a restricted merchant?",
            "tools": [{
                "type": "mcp",
                "server_url": "https://api.theunderstanding.company/mcp/grok",
                "server_label": "understanding",
                "server_description": "Closed questions about bank transactions and member support messages. Read-only. Each answer has a probability and an abstain flag.",
                "authorization": os.environ["UNDERSTANDING_API_KEY"],
                "allowed_tools": ["check_restricted_merchant", "classify_spend_type", "match_receipt", "pair_refund", "check_return_window", "route_support_message", "ask_closed_question"],
            }],
        },
        timeout=120,
    )
    res.raise_for_status()
    print(res.json())

    Three things matter here.

    • authorization may not add the word Bearer. xAI says only that it is "a token set in the Authorization header". So the example sends the bare key, and the /mcp/grok address accepts a bare key as well as Bearer <key>. If it is refused, send "headers": { "Authorization": "Bearer <key>" } instead and drop authorization.
    • Always set allowed_tools. Without it xAI injects every tool definition the server lists into the model's context. The list above is the seven banking question tools. /mcp/grok already offers only those seven, with the shortest descriptions, and none of the three health tools.
    • require_approval and connector_id are not supported on the xAI API. Do not send them.

    Only Streamable HTTP and SSE are supported, and ours is Streamable HTTP. The server must be reachable from the internet.

    Route two: function calling

    The definitions come from our server in the shape xAI shares with OpenAI's strict form:

    Bash
    curl "https://api.theunderstanding.company/v1/tools?format=xai"

    The reply is { "format": "xai", "tools": [ ... ], "execute": { ... } }. xAI requires a parameters object at the root, which these have, and allows up to 350 tools in a request. parallel_tool_calls defaults to true; the tools are safe to run in parallel. Send each call's arguments to POST https://api.theunderstanding.company/v1/tools/{name} with your key and return the answer as function_call_output. Save this as understanding-xai.mjs:

    const API = "https://api.theunderstanding.company";
    const ours = (path, init = {}) =>
      fetch(API + path, {
        ...init,
        headers: { Authorization: `Bearer ${process.env.UNDERSTANDING_API_KEY}`, "Content-Type": "application/json" },
      });
    
    const { tools } = await (await ours("/v1/tools?format=xai")).json();
    
    async function runTool(name, args) {
      const res = await ours(`/v1/tools/${name}`, { method: "POST", body: JSON.stringify(args) });
      const body = await res.json();
      if (!res.ok) return JSON.stringify({ error: body.error });
      const { abstain, ...rest } = body; // abstain first
      return JSON.stringify({ abstain, ...rest });
    }
    
    async function respond(request) {
      const res = await fetch("https://api.x.ai/v1/responses", {
        method: "POST",
        headers: { Authorization: `Bearer ${process.env.XAI_API_KEY}`, "Content-Type": "application/json" },
        body: JSON.stringify(request),
      });
      const json = await res.json();
      if (!res.ok) throw new Error(json.error?.message ?? `HTTP ${res.status}`);
      return json;
    }
    
    const model = "grok-4.7";
    let response = await respond({ model, input: "Is the merchant DRAFTKINGS, MCC 7995, a restricted merchant?", tools });
    for (let round = 0; round < 5; round++) {
      const calls = response.output.filter((item) => item.type === "function_call");
      if (calls.length === 0) break;
      const outputs = [];
      for (const call of calls) {
        outputs.push({ type: "function_call_output", call_id: call.call_id, output: await runTool(call.name, JSON.parse(call.arguments || "{}")) });
      }
      response = await respond({ model, previous_response_id: response.id, input: outputs, tools });
    }
    const message = response.output.find((item) => item.type === "message");
    console.log(message?.content?.map((part) => part.text).join("\n"));
    import json
    import os
    
    import requests
    
    API = "https://api.theunderstanding.company"
    OURS = {"Authorization": f"Bearer {os.environ['UNDERSTANDING_API_KEY']}", "Content-Type": "application/json"}
    THEIRS = {"Authorization": f"Bearer {os.environ['XAI_API_KEY']}", "Content-Type": "application/json"}
    MODEL = "grok-4.7"
    
    tools = requests.get(f"{API}/v1/tools?format=xai", timeout=30).json()["tools"]
    
    def run_tool(name, args):
        res = requests.post(f"{API}/v1/tools/{name}", headers=OURS, json=args, timeout=60)
        body = res.json()
        if not res.ok:
            return json.dumps({"error": body["error"]})
        return json.dumps({"abstain": body["abstain"], **{k: v for k, v in body.items() if k != "abstain"}})
    
    def respond(request):
        res = requests.post("https://api.x.ai/v1/responses", headers=THEIRS, json=request, timeout=120)
        res.raise_for_status()
        return res.json()
    
    response = respond({"model": MODEL, "input": "Is the merchant DRAFTKINGS, MCC 7995, a restricted merchant?", "tools": tools})
    for _ in range(5):
        calls = [item for item in response["output"] if item["type"] == "function_call"]
        if not calls:
            break
        outputs = [
            {"type": "function_call_output", "call_id": c["call_id"], "output": run_tool(c["name"], json.loads(c["arguments"] or "{}"))}
            for c in calls
        ]
        response = respond({"model": MODEL, "previous_response_id": response["id"], "input": outputs, "tools": tools})
    
    for item in response["output"]:
        if item["type"] == "message":
            print("\n".join(part["text"] for part in item["content"]))

    With function calling, xAI never sees your key, and nothing goes through xAI's MCP connector.

    What we tuned for this service

    Route one uses /mcp/grok. It has the shortest descriptions, offers only the seven banking question tools (no get_usage, which would only add to the model's context, and no health tools), returns compact JSON with the abstain flag first, contains no text that reads like an instruction, and accepts a bare key in the Authorization header. Route two uses our standard descriptions in the shared strict form.

    Check that it works

    The model should call check_restricted_merchant with merchant set to DRAFTKINGS and mcc set to 7995. The result is one JSON object:

    JSON
    {"abstain":false,"answer":true,"probability":0.97,"reasons":["mcc_7995","rule_gambling_category","kind_sportsbook"],"decision":"restricted_merchant","type":"predicate"}

    For a case that abstains, ask about LUCKY COINS LLC with code 5816: abstain is true, and Grok should say it is unsure. The numbers show the format. They are not measured results. xAI may show the tool name prefixed with the server label; that is on its side.

    If it doesn't connect

    • xAI says the server refused the credential. Switch between authorization and headers with an explicit Bearer, as above. Check the key with curl https://api.theunderstanding.company/v1/usage -H "Authorization: Bearer $UNDERSTANDING_API_KEY".
    • The request fails because of require_approval. xAI does not support it. Remove it.
    • The context is large and the answers are poor. You left out allowed_tools, so every tool definition is in the context.
    • A call fails with sensitive_data. The arguments held what looks like a card, Social Security or account number. Remove it. See Errors.
    • Too many requests. The limit is 60 requests a minute per key. See Rate limits.