Skip to main content
Glama
kinopeee
by kinopeee

grokbot-mcp-bridge

English | 日本語

What it does: lets Poke — or any MCP client — ask a Grok Bot a question and get the answer back as a normal tool result, even though Grok Bot can only reply asynchronously via a webhook callback.

Who it is for: anyone who can deploy a small app to Fly.io and has a Grok Bot routine with a Web webhook trigger. No knowledge of MCP internals is needed; the quick start below is copy-paste.

How: the bridge holds the Cursor webhook URL/key server-side, so Poke only ever sees the bridge URL and one API key you generate.

How it flows

sequenceDiagram
    participant P as Poke (MCP client)
    participant B as Bridge (https://<app>.fly.dev)
    participant G as Grok Bot routine
    P->>B: tools/call ask_grokbot (Bearer MCP_API_KEY)
    B->>G: POST webhook (run_id, callback_url)
    G-->>B: POST /callbacks/{token} (answer, run_id)
    B-->>P: answer_text

Related MCP server: GrokBot ↔ Hermes Bridge

Documentation

Document

English

日本語

Overview (this file)

README.md

README.ja.md

Bridge specification and operations

SPEC.md

SPEC.ja.md

Calling Grok Bot from Poke

poke-invocation.md

poke-invocation.ja.md

Quick start (Poke → Grok Bot in four steps)

<app> below is app in fly.toml (defaults to grokbot-mcp-bridge).

0. Which value goes where

Required (3 secrets + 1 URL)

Value

Where you get it

Where you enter it

CURSOR_WEBHOOK_URL

Grok Bot app → the routine with the Web webhook trigger → "POST URL" (https://api2.cursor.sh/automations/webhook/…)

Fly secret on the bridge (step 2)

CURSOR_WEBHOOK_API_KEY

Same screen → "Key" (crsr_…). Copy only the key, not the Authorization: Bearer header line

Fly secret on the bridge (step 2)

MCP_API_KEY

Generate yourself (openssl rand -hex 32)

Fly secret (step 2) and Poke → New Integration → "API Key" (step 3) — same value in both

https://<app>.fly.dev/mcp

Fixed by the bridge

Poke → New Integration → "Server URL" (step 3)

Grok Bot routine instructions

This repo: poke-invocation.md § 4 (ready-to-paste template)

Grok Bot app → the same routine → "Instructions" field

Routine screen: chat header → info panel → Routines → Web webhook.

Optional (skip on first setup)

Value

Where you get it

Where you enter it

INBOUND_WEBHOOK_SECRET

Generate yourself (optional)

Fly secret (step 2), and the Grok Bot-side push routine if you use POST /hooks/grokbot

CALLBACK_ALLOWED_HOSTS

Comma-separated hostnames the inbound callback_url may target (e.g. callback.example.com)

Fly secret (step 2), only for push delivery

Only for push delivery via POST /hooks/grokbot (list_grokbot_events). Not needed for ask_grokbot.

1. Prepare the Grok Bot webhook

In the Cursor automation that runs Grok Bot, enable the webhook trigger and copy its URL and crsr_… API key. They become CURSOR_WEBHOOK_URL / CURSOR_WEBHOOK_API_KEY and are stored only on the bridge — Poke never sees them.

The routine's Instructions field must tell Grok Bot to echo run_id and POST the answer to callback_url — paste the template from poke-invocation.md § 4.

2. Deploy the bridge to Fly.io

Log in with flyctl auth login, or for non-interactive use create a token at https://fly.io/tokens and export it as FLY_API_TOKEN (tokens have an expiry you choose at creation; renew before it lapses).

export MCP_API_KEY="$(openssl rand -hex 32)"            # keep this value: Poke needs it in step 3
export INBOUND_WEBHOOK_SECRET="$(openssl rand -hex 32)" # optional (push delivery only)
flyctl apps create <app>
flyctl volumes create bridge_data -r <region> -s 1 -a <app> --yes   # SQLite lives on /data
flyctl secrets set -a <app> \
  CURSOR_WEBHOOK_URL="$CURSOR_WEBHOOK_URL" \
  CURSOR_WEBHOOK_API_KEY="$CURSOR_WEBHOOK_API_KEY" \
  MCP_API_KEY="$MCP_API_KEY" \
  INBOUND_WEBHOOK_SECRET="$INBOUND_WEBHOOK_SECRET" \
  ALLOWED_HOSTS=<app>.fly.dev \
  DB_PATH=/data/bridge.db
flyctl deploy --remote-only --ha=false -a <app>
  • MCP_API_KEY and INBOUND_WEBHOOK_SECRET are not issued by Poke or Cursor; you generate them yourself. echo "$MCP_API_KEY" shows it in the same shell — Poke needs the same value in step 3, and Fly does not show secret values again.

  • INBOUND_WEBHOOK_SECRET is optional. Without it, POST /hooks/grokbot returns 503 and bridge_status reports inbound_webhook_secret_configured: false; ask_grokbot still works.

  • Push delivery via POST /hooks/grokbot also needs CALLBACK_ALLOWED_HOSTS. The value is the hostname of the callback_url your Grok Bot routine puts in the webhook body (comma-separate multiple hosts; example.com also covers api.example.com). To see which hosts past events used, call list_grokbot_events → get_grokbot_event(id) from Poke and check body.callback_url / reply_url / response_url.

    flyctl secrets set -a <app> CALLBACK_ALLOWED_HOSTS=callback.example.com

    Unset → every inbound callback_url is rejected (a well-formed https URL reports callback_error: "allowed_hosts_not_configured"; other URLs fail earlier checks). The event itself is still recorded; ask_grokbot is unaffected.

3. Connect Poke

Add an MCP integration in Poke with:

Field

Value

Name

any label, e.g. grokbot-mcp-bridge

Server URL

https://<app>.fly.dev/mcp (Streamable HTTP). Use https://<app>.fly.dev/sse if the client only supports SSE

API Key

the value of MCP_API_KEY (no Bearer prefix). Poke labels this field optional, but leave it empty and Poke will try OAuth and fail — always set it

4. Verify end to end

  1. GET https://<app>.fly.dev/healthz → 200 {"ok":true}

  2. From Poke, call bridge_status → webhook_url_configured and webhook_api_key_configured are true (inbound_webhook_secret_configured is false if you skipped the optional secret — that is fine)

  3. From Poke, call ask_grokbot with payload={"message": "Introduce yourself briefly."}, wait_seconds=60

    • answer_status: "answered" → answer_text holds the reply

    • answer_status: "pending" → call wait_for_grokbot_answer(run_id) (default 60s, max MAX_WAIT_SECONDS) or cancel_run to stop

    • answer_status: "cancelled" / "expired" → do not wait; start a new ask_grokbot if needed

    Sample result:

    {
      "ok": true,
      "run_id": "06eec502-cf7b-468d-8307-8dcf945e1f17",
      "answer_status": "answered",
      "answer_text": "…the reply text…",
      "summary": "Grok Bot answered: …"
    }
  4. flyctl logs -a <app> shows run created → callback resolved → trigger returning

For the Grok Bot-side contract (echoing run_id, posting to callback_url) and troubleshooting, see poke-invocation.md.

Endpoints

  • GET /healthz

  • GET /

  • POST /mcp (authenticated MCP)

  • GET /sse and POST /messages/ (authenticated MCP)

  • POST /hooks/grokbot (signed inbound Grok Bot events)

  • POST /callbacks/{token} (Grok Bot answers)

MCP tools

  • bridge_status

  • ask_grokbot

  • get_grokbot_run

  • wait_for_grokbot_answer

  • cancel_run

  • list_grokbot_runs

  • list_grokbot_events

  • get_grokbot_event

Resources are available at grokbot://events and grokbot://runs.

Environment variables

CURSOR_WEBHOOK_URL, CURSOR_WEBHOOK_API_KEY, MCP_API_KEY, INBOUND_WEBHOOK_SECRET, DB_PATH, ALLOWED_HOSTS, PUBLIC_BASE_URL, CALLBACK_TTL_SECONDS, CALLBACK_ALLOW_HTTP, CALLBACK_ALLOWED_HOSTS, RUN_RETENTION_SECONDS, EVENT_RETENTION_SECONDS, CLEANUP_INTERVAL_SECONDS, RATE_LIMIT_PER_MINUTE, and MAX_WAIT_SECONDS. See SPEC.md §7 for defaults (key names only; do not put secret values in docs or .env.example).

Callback contract

Grok Bot should POST JSON such as:

{"ok": true, "answer": "...", "run_id": "<echoed uuid>"}

The run_id (or request_id) must echo the UUID sent by ask_grokbot. When an inbound webhook has no callback URL, the bridge returns 「コールバックURLなし」 ("no callback URL") and records the event without delivering a callback.

Commands

uv run --extra dev pytest -q               # tests
uv lock && uv export --no-dev --format requirements-txt --no-emit-project -o requirements.txt   # update pinned deps
flyctl deploy --remote-only --ha=false     # deploy (app / region come from fly.toml)

License

MIT — see LICENSE.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables MCP clients like Codex and Claude Code to start or continue conversations with Grok Build, track progress, read completed answers, and cancel running turns using an existing Grok login.
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables external MCP clients to invoke governed tools with schema validation, budgets, policy checks, human approval for side-effecting actions, and prompt-injection quarantine.
    MIT