Five recipes. Same 10 verbs everywhere. Every section ends with a remember → query round-trip you can run today against the production API at api.kaeva.app.
You need an UNBLOCK API key. Format: unb_<32hex>. The catalog-api derives a stable user_id from SHA-256(key) — see services/catalog-api/src/auth/api-key.ts.
If you don't have one yet, generate a 32-hex string locally and use that as your dogfood key. Production keychain-bind ships in Sprint 8; until then, the API-key path is the canonical auth surface.
# One-liner to mint a local dogfood key
echo "unb_$(python -c 'import secrets; print(secrets.token_hex(16))')"
api.kaeva.app. If you're hitting a staging Worker, swap the URL to https://unblock-catalog-api-v01.virajsharma5599.workers.dev. The MCP shim source lives at services/dogfood-mcp/server.py.
Cursor reads MCP servers from .cursor/mcp.json at the project root (or ~/.cursor/mcp.json for global). Add the UNBLOCK shim and restart the workspace.
git clone https://github.com/Viraj0518/UNBLOCK_0.1.0.git
cd UNBLOCK_0.1.0/services/dogfood-mcp
python -m venv venv && source venv/Scripts/activate
pip install -r requirements.txt
.cursor/mcp.json{
"mcpServers": {
"unblock": {
"command": "python",
"args": ["/abs/path/to/UNBLOCK_0.1.0/services/dogfood-mcp/server.py"],
"env": {
"UNBLOCK_API_KEY": "unb_REPLACE_WITH_32HEX",
"UNBLOCK_CATALOG_API": "https://api.kaeva.app"
}
}
}
}
Reopen Cursor, then in the agent panel:
@unblock remember the decision: "Cursor mcp.json is project-scoped"
@unblock query "Cursor mcp"
Round-trip should return your block at top-rank within ~1 s. If you see AUTH_INVALID, your key isn't in unb_<32hex> shape.
VS Code's native MCP support landed in 1.97. Two paths: native (.vscode/mcp.json) or via Continue.dev for older versions.
.vscode/mcp.json{
"servers": {
"unblock": {
"type": "stdio",
"command": "python",
"args": ["${workspaceFolder}/services/dogfood-mcp/server.py"],
"env": {
"UNBLOCK_API_KEY": "unb_REPLACE_WITH_32HEX",
"UNBLOCK_CATALOG_API": "https://api.kaeva.app"
}
}
}
}
~/.continue/config.yamlmcpServers:
- name: unblock
command: python
args:
- /abs/path/to/UNBLOCK_0.1.0/services/dogfood-mcp/server.py
env:
UNBLOCK_API_KEY: unb_REPLACE_WITH_32HEX
UNBLOCK_CATALOG_API: https://api.kaeva.app
Open the chat panel, switch to Agent mode, then:
/unblock_remember content="VS Code MCP wired" block_type=note
/unblock_query text="VS Code MCP" scope=mine top_k=5
Claude Code reads MCP servers from ~/.claude.json, or per-project from .claude/settings.json. The dogfood shim is the same Python server everywhere.
claude mcp add unblock python \
/abs/path/to/UNBLOCK_0.1.0/services/dogfood-mcp/server.py \
--env UNBLOCK_API_KEY=unb_REPLACE_WITH_32HEX \
--env UNBLOCK_CATALOG_API=https://api.kaeva.app
~/.claude.json{
"mcpServers": {
"unblock": {
"command": "python",
"args": ["/abs/path/to/UNBLOCK_0.1.0/services/dogfood-mcp/server.py"],
"env": {
"UNBLOCK_API_KEY": "unb_REPLACE_WITH_32HEX",
"UNBLOCK_CATALOG_API": "https://api.kaeva.app"
}
}
}
}
Restart Claude Code. The 10 unblock_* tools (plus unblock_ingest for substrate dogfood) appear in the tool list.
Use unblock_remember to store: "Claude Code MCP works end-to-end"
Then unblock_query for "Claude Code MCP".
You should see your block come back as the top result. The full tool roster is documented in services/dogfood-mcp/README.md.
Codex doesn't speak MCP natively yet — wire UNBLOCK as a function-calling layer. The 10 verbs map 1:1 to OpenAI tool definitions.
UNBLOCK_TOOLS = [
{
"type": "function",
"function": {
"name": "unblock_remember",
"description": "Capture a discrete block of knowledge. Returns a stable block_id.",
"parameters": {
"type": "object",
"required": ["content"],
"properties": {
"content": {"type": "string"},
"block_type": {"type": "string", "enum": [
"note","snippet","doc","code","trace","decision",
"anti-pattern","dataset","exploit","kg","other","conversation"
]},
"scope": {"type": "string", "enum": ["private","team","public"]},
"tags": {"type": "array", "items": {"type": "string"}},
},
},
},
},
{
"type": "function",
"function": {
"name": "unblock_query",
"description": "Semantic search across blocks. Returns ranked results.",
"parameters": {
"type": "object",
"required": ["text"],
"properties": {
"text": {"type": "string"},
"scope": {"type": "string", "enum": ["mine","public","all"]},
"top_k": {"type": "integer", "minimum": 1, "maximum": 100},
},
},
},
},
# … 8 more verbs: share, list, purchase, verify, attest, subscribe, update, extract
]
import os, httpx
API = os.environ["UNBLOCK_CATALOG_API"] # https://api.kaeva.app
KEY = os.environ["UNBLOCK_API_KEY"]
def call_unblock(name: str, args: dict) -> dict:
"""Codex tool-call → UNBLOCK HTTP. name = unblock_."""
verb = name.removeprefix("unblock_")
r = httpx.post(
f"{API}/v1/{verb}",
json=args,
headers={"X-API-Key": KEY, "Content-Type": "application/json"},
timeout=30.0,
)
r.raise_for_status()
return r.json()
export UNBLOCK_API_KEY=unb_REPLACE_WITH_32HEX
export UNBLOCK_CATALOG_API=https://api.kaeva.app
python -c "from bridge import call_unblock; \
blk = call_unblock('unblock_remember', {'content': 'Codex bridge live'}); \
print(blk); \
print(call_unblock('unblock_query', {'text': 'Codex bridge', 'top_k': 3}))"
Perplexity doesn't host arbitrary MCP servers — wire UNBLOCK as an HTTP API-key bridge from a Perplexity Pro space or Comet agent. Pattern: an outbound webhook in your space hits a tiny shim Worker that forwards to UNBLOCK with the right header.
// perplexity-bridge.js — deploy with `wrangler deploy`
export default {
async fetch(req, env) {
const url = new URL(req.url);
const verb = url.pathname.replace(/^\/+/, ""); // e.g. "remember"
const body = req.method === "POST" ? await req.text() : "";
const upstream = await fetch(`https://api.kaeva.app/v1/${verb}`, {
method: req.method,
headers: {
"X-API-Key": env.UNBLOCK_API_KEY,
"Content-Type": "application/json",
},
body: body || undefined,
});
return new Response(await upstream.text(), {
status: upstream.status,
headers: { "Content-Type": "application/json" },
});
},
};
In your Perplexity space's connected actions, point the action endpoint at your bridge URL (https://unblock-bridge.<your-account>.workers.dev/remember) and define the input schema:
{
"name": "unblock_remember",
"description": "Save a fact UNBLOCK should remember.",
"method": "POST",
"url": "https://unblock-bridge.example.workers.dev/remember",
"body": {
"content": "{{user_input}}",
"block_type": "note",
"scope": "private"
}
}
curl -X POST https://unblock-bridge.example.workers.dev/remember \
-H "Content-Type: application/json" \
-d '{"content": "Perplexity bridge live", "block_type": "note"}'
curl -X POST https://unblock-bridge.example.workers.dev/query \
-H "Content-Type: application/json" \
-d '{"text": "Perplexity bridge", "top_k": 3}'
Pro tip: the bridge pattern works for any tool that lacks native MCP — Glean, Notion, Slack-AI, etc. One Worker, one secret, ten verbs.
Whatever IDE or shim you wired, the round-trip is the same: POST /v1/remember then POST /v1/query. If both return 200, you're integrated.
curl -X POST https://api.kaeva.app/v1/remember \
-H "X-API-Key: unb_REPLACE_WITH_32HEX" \
-H "Content-Type: application/json" \
-d '{"content": "smoke test from cookbook", "block_type": "note"}'
# → {"block_id": "blk_…", "version": 1, "user_id": "usr_…", …}
curl -X POST https://api.kaeva.app/v1/query \
-H "X-API-Key: unb_REPLACE_WITH_32HEX" \
-H "Content-Type: application/json" \
-d '{"text": "smoke test", "scope": "mine", "top_k": 5}'
# → {"results": [{"block_id": "blk_…", "score": 0.94, …}]}
usr_ namespaces). See architecture for the user-isolation model.