Cracked
One key for 63,577 agent tools: scrapers, search, data, AI models. Pay per call, failed runs free. MCP + REST.
Documentation
Updated September 2, 2026 · tested with MCP protocol 2025-06-18 · by Cracked Engineering
MCP server
Cracked exposes a remote MCP server over Streamable HTTP at https://cracked.ai/mcp. Two ways to authenticate: OAuth 2.1 (sign in with your Cracked account from Claude.ai, Claude Desktop, ChatGPT, Cursor) or a bearer API key header (Claude Code, Codex, OpenCode, scripts).
Tools exposed
The generic tools are always registered. On top of them, every smart-run capability is available as its own typed tool (capability id with underscores: instagram-profile becomes instagram_profile), so a client can call weather_forecast({latitude, longitude}) directly instead of discovering and inspecting first.
| Tool | Arguments | Purpose |
|---|---|---|
discover_tools | query, limit?, live_only? | Natural-language search with price, health and availability. |
inspect_tool | provider, endpoint | Schema, price, health, docs. |
run_tool | provider, endpoint, input, wait? | Execute and bill. wait:false for long scrapers. |
run_capability | capability, input, wait? | Smart run by capability id; falls back across providers. |
list_capabilities | id? | Capability ids, input schemas, ranked candidates. Free. |
get_run | run_id, wait_seconds? | Poll an async run. |
get_balance | Workspace balance in USD. | |
create_checkout_link | amount_usd, ref?, promo?, success_url? | Mint a card payment link to hand to your owner. Free until paid. |
recruit_agent | agent_name?, task? | Onboarding prompt for another agent with your referral link embedded (register, first run, claim flow). Free. |
<capability> | the capability's input fields | One tool per capability, e.g. web_search, instagram_profile, weather_forecast. Returns the normalized output first, then a compact run summary (routedTo, status, cost, runId). |
Tool sets
164 capability tools is more than some clients list comfortably, so the set is chosen with a tools query parameter on the server URL. Everything else (auth, OAuth metadata, billing) is identical.
| URL | Tools | Use when |
|---|---|---|
https://cracked.ai/mcp or ?tools=top | generic + 40 most-used capabilities | Default. Instagram, TikTok, YouTube, X, LinkedIn, Google Maps, Amazon, web search and scrape, enrichment, weather, stocks, crypto, image and speech generation, transcription, LLM chat. |
https://cracked.ai/mcp?tools=all | generic + all 164 capabilities | Agents that pick tools by name and schema and can hold a long tool list. |
https://cracked.ai/mcp?tools=core | generic only | Clients with small tool budgets; use run_capability and run_tool. |
The OAuth protected-resource identifier is https://cracked.ai/mcp for all three; clients connecting to /mcp?tools=all discover the same metadata at /.well-known/oauth-protected-resource/mcp and sign in once.
Example tools/list entry for a capability tool:
{
"name": "weather_forecast",
"title": "Weather forecast",
"description": "Current conditions plus daily forecast for coordinates. Use geocode first for a city name. Price: from $0.002/call via Open-Meteo Weather + $0.001 platform fee, billed to the workspace balance. Smart run: Cracked picks the best live tool for \"weather-forecast\", maps the input and falls back to the next provider on failure (1 live).",
"inputSchema": { "type": "object", "properties": { "latitude": { "type": "number", "description": "Latitude" }, "longitude": { "type": "number", "description": "Longitude" }, "days": { "type": "number", "description": "Forecast days 1-16" }, "units": { "type": "string", "description": "celsius or fahrenheit" } }, "required": ["latitude", "longitude"] }
}
Calling it returns the output first, then a compact run summary:
{
"output": { "timezone": "America/Chicago", "current": { "temperature_2m": 91.6, "relative_humidity_2m": 41, ... }, "daily": { ... } },
"run": { "capability": "weather-forecast", "status": "COMPLETED", "routedTo": { "provider": "open-meteo", "endpoint": "/forecast" }, "httpStatus": 200, "runId": "b637d650-...", "totalUsd": 0.003, "durationMs": 1107, "attempts": 1, "fallbackUsed": false, "tried": [ ... ] },
"hints": { "capability": "GET /v1/capabilities?id=weather-forecast" }
}
Claude.ai and Claude Desktop (OAuth)
Settings → Connectors → Add custom connector. Name Cracked, URL https://cracked.ai/mcp (or https://cracked.ai/mcp?tools=all). Claude discovers our OAuth server, registers itself, sends you to the Cracked consent screen, and you pick the workspace to bill. Tokens last 30 days and refresh automatically. Revoke from API keys.
ChatGPT
Settings → Connectors → Create. Connection URL https://cracked.ai/mcp (or https://cracked.ai/mcp?tools=all to expose every capability), authentication OAuth. Same consent flow. ChatGPT lists each capability as a separate action, so the default set keeps the connector page readable.
Claude Code
# default: generic tools + 40 most-used capabilities as typed tools
claude mcp add --transport http cracked https://cracked.ai/mcp --header "Authorization: Bearer ck_live_..."
# every capability as a tool
claude mcp add --transport http cracked "https://cracked.ai/mcp?tools=all" --header "Authorization: Bearer ck_live_..."
# or let Claude Code run the OAuth flow:
claude mcp add --transport http cracked https://cracked.ai/mcp
Cursor
{ "mcpServers": { "cracked": { "url": "https://cracked.ai/mcp?tools=all", "headers": { "Authorization": "Bearer ck_live_..." } } } }
One-click: cracked.ai/install has a Cursor deep link for the default set and one for tools=all.
Codex
codex mcp add cracked --url https://cracked.ai/mcp --header "Authorization: Bearer ck_live_..."
OpenCode
{ "mcp": { "cracked": { "type": "remote", "url": "https://cracked.ai/mcp", "enabled": true, "headers": { "Authorization": "Bearer ck_live_..." } } } }
Native tools for the OpenAI and Anthropic SDKs (no MCP)
The same tool definitions are served as plain JSON at GET https://cracked.ai/v1/capabilities/tools (free, no auth, CORS *, cached one hour). Paste the array into tools=[...], then execute each call the model makes with POST https://cracked.ai/v1/run {"capability", "input"}. The tool name is the capability id with underscores; /v1/run accepts either spelling.
| Query | Returns |
|---|---|
?format=openai (default) | [{ type: "function", function: { name, description, parameters } }] |
?format=anthropic | [{ name, description, input_schema }] |
?format=mcp | [{ name, title, description, inputSchema }], what /mcp lists |
&set=top (default) / &set=all | 40 most-used capabilities / all 164 |
OpenAI SDK (Python)
import json, os, requests
from openai import OpenAI
CRACKED = "https://cracked.ai"
H = {"Authorization": f"Bearer {os.environ['CRACKED_API_KEY']}", "content-type": "application/json"}
tools = requests.get(f"{CRACKED}/v1/capabilities/tools?format=openai&set=top").json()
def run_capability(name, arguments):
r = requests.post(f"{CRACKED}/v1/run", headers=H, json={"capability": name, "input": arguments}, timeout=300).json()
return r.get("output") if r.get("status") == "COMPLETED" else {"error": r.get("status"), "detail": r.get("providerResponse"), "attempts": r.get("attempts")}
client = OpenAI()
messages = [{"role": "user", "content": "How many followers does @nike have on Instagram, and what is the weather in Austin?"}]
while True:
resp = client.chat.completions.create(model="gpt-4.1", messages=messages, tools=tools)
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls:
print(msg.content); break
for call in msg.tool_calls:
out = run_capability(call.function.name, json.loads(call.function.arguments))
messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps(out)})
Anthropic SDK (Python)
import json, os, requests
import anthropic
CRACKED = "https://cracked.ai"
H = {"Authorization": f"Bearer {os.environ['CRACKED_API_KEY']}", "content-type": "application/json"}
tools = requests.get(f"{CRACKED}/v1/capabilities/tools?format=anthropic&set=top").json()
def run_capability(name, arguments):
r = requests.post(f"{CRACKED}/v1/run", headers=H, json={"capability": name, "input": arguments}, timeout=300).json()
return r.get("output") if r.get("status") == "COMPLETED" else {"error": r.get("status"), "detail": r.get("providerResponse"), "attempts": r.get("attempts")}
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "How many followers does @nike have on Instagram, and what is the weather in Austin?"}]
while True:
resp = client.messages.create(model="claude-opus-5", max_tokens=16000, tools=tools, messages=messages)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
print(next(b.text for b in resp.content if b.type == "text")); break
results = []
for block in resp.content:
if block.type == "tool_use":
out = run_capability(block.name, block.input)
results.append({"type": "tool_result", "tool_use_id": block.id, "content": json.dumps(out)})
messages.append({"role": "user", "content": results})
Anthropic SDK (TypeScript)
import Anthropic from "@anthropic-ai/sdk";
const CRACKED = "https://cracked.ai";
const H = { Authorization: \`Bearer ${process.env.CRACKED_API_KEY}\`, "content-type": "application/json" };
const tools = (await (await fetch(\`${CRACKED}/v1/capabilities/tools?format=anthropic&set=top\`)).json()) as Anthropic.Tool[];
async function runCapability(name: string, input: unknown) {
const r = await (await fetch(\`${CRACKED}/v1/run\`, { method: "POST", headers: H, body: JSON.stringify({ capability: name, input }) })).json();
return r.status === "COMPLETED" ? r.output : { error: r.status, detail: r.providerResponse, attempts: r.attempts };
}
const client = new Anthropic();
const messages: Anthropic.MessageParam[] = [{ role: "user", content: "Top 5 news stories about peptides this week, with URLs." }];
for (;;) {
const resp = await client.messages.create({ model: "claude-opus-5", max_tokens: 16000, tools, messages });
messages.push({ role: "assistant", content: resp.content });
if (resp.stop_reason !== "tool_use") { console.log(resp.content.find((b) => b.type === "text")?.text); break; }
const results: Anthropic.ToolResultBlockParam[] = [];
for (const block of resp.content) if (block.type === "tool_use") results.push({ type: "tool_result", tool_use_id: block.id, content: JSON.stringify(await runCapability(block.name, block.input)) });
messages.push({ role: "user", content: results });
}
Long scrapers: send "wait": false to /v1/run, get HTTP 202 with a runId, then poll GET /v1/runs/{runId}?wait=30. Non-2xx runs are never billed. See Smart run for the routing rules and the API reference for every field.
OAuth details
- Discovery:
https://cracked.ai/.well-known/oauth-authorization-serverandhttps://cracked.ai/.well-known/oauth-protected-resource/mcp. - Dynamic client registration (RFC 7591) at
/oauth/register; no manual client setup. - Authorization code with PKCE (S256), refresh tokens, revocation at
/oauth/revoke. - Scopes:
tools:discover,tools:run,wallet:read,offline_access. - An access token is a workspace-scoped key (
ck_oauth_…) that also works on the HTTP API.
Verify from the shell
curl -s "https://cracked.ai/mcp?tools=all" -H "Authorization: Bearer ck_live_..." -H "content-type: application/json" -H "accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# call a capability tool directly
curl -s https://cracked.ai/mcp -H "Authorization: Bearer ck_live_..." -H "content-type: application/json" -H "accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"weather_forecast","arguments":{"latitude":30.27,"longitude":-97.74}}}'