Mount the workspace brain as tools in Claude Code, Cursor, or your own agent.
Your Alfera workspace brain, mounted as tools in the agent you already use — Claude Code, Cursor, or your own harness. Point it at one URL and it can do the two things the brain offers: get facts, or get an answer from the worker.
https://api.alfera.ai/v1/mcp
- Facts for your own prompt:
get_memory_contextreturns the rendered block the worker gets each turn, inside a token budget.search_memoryreturns the individual facts as data. - An answer from the worker:
ask_worker, thenwait_for_answerif the wait runs out. - Streamable HTTP, stateless: no session to open, resume, or close. Every request carries its own key, so restarting your client or load-balancing across instances changes nothing.
- The server speaks MCP
2025-06-18and accepts2025-03-26and2024-11-05. - Same brain, same keys, and the same disclosure rules as the Brain API.
Connect
Create a key in the app under Settings → API Keys, pick its scopes and audience, and export the secret:
export ALFERA_API_KEY=ak_live_...
Claude Code
claude mcp add --transport http alfera-brain https://api.alfera.ai/v1/mcp \
--header "Authorization: Bearer $ALFERA_API_KEY"
Cursor — add the server to ~/.cursor/mcp.json (global) or .cursor/mcp.json (one project):
{
"mcpServers": {
"alfera-brain": { "url": "https://api.alfera.ai/v1/mcp", "headers": { "Authorization": "Bearer ak_live_..." } }
}
}
Anything else that speaks Streamable HTTP: POST JSON-RPC to https://api.alfera.ai/v1/mcp with Authorization: Bearer <key> on every request. X-Api-Key: <key> is accepted as well. The endpoint is POST-only, so GET and DELETE answer 405 method_not_allowed.
Verify with whoami
whoami needs no scope, so it answers for any live key and reports what the connection can do.
curl https://api.alfera.ai/v1/mcp \
-H "Authorization: Bearer $ALFERA_API_KEY" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'
{
"key_name": "support-bot",
"key_prefix": "ak_live_x7f4",
"scopes": ["memory.read", "answers.write"],
"key_audience": "external",
"rate_limit": { "requests_per_window": 120, "window_seconds": 60 }
}
tools/list only advertises what this key's scopes allow. A memory.read-only key never sees ask_worker, so a tool missing from your agent's list means the key lacks its scope.
Tools
| Tool | What it does | Required scope |
|---|---|---|
whoami | Reports this connection's key name, prefix, scopes, audience, and rate limit. Call it first. | none |
search_memory | Searches the workspace brain and returns matching facts, most relevant first. Name the end_user to also reach that person's own facts. | memory.read |
get_memory_context | Builds the rendered memory context packet for a query — the same block the worker gets each turn — inside a token budget, for pasting into your own prompt. | memory.read |
teach_memory | Teaches the workspace brain new facts. Each runs through the normal capture pipeline; repeats of something already known are skipped. | memory.write |
ask_worker | Asks the workspace worker a question and waits up to timeout_seconds. Returns {wait_timed_out, answer_id} if time runs out. | answers.write |
wait_for_answer | Keeps waiting for an answer started earlier, by answer_id. | answers.write |
| Tool | Arguments |
|---|---|
whoami | none |
search_memory | query (1–2000), limit (1–100, default 20), end_user {id, name?} |
get_memory_context | query (1–2000), token_budget (100–20000, default 1500), end_user {id, name?} |
teach_memory | facts: 1–20 of {text} (up to 4000 characters each) |
ask_worker | input (1–8000), conversation_id, end_user, agent_slug, timeout_seconds (0–120, default 60), idempotency_key (1–200) |
wait_for_answer | answer_id, timeout_seconds (0–120, default 60) |
- Each tool takes exactly the arguments its REST counterpart takes as a body and returns exactly what that route returns.
search_memoryisPOST /v1/memory/search,get_memory_contextisPOST /v1/memory/context,teach_memoryisPOST /v1/memory/facts.ask_workeris the queuedPOST /v1/answersplus the wait;wait_for_answerisGET /v1/answers/:answer_idplus the wait. Field-level detail is in the Brain API reference. - Disclosure rules are the REST ones, unchanged: an
externalkey receives onlyprofile_fact,preference,decision, andobservation, and never facts about your team,agent_inferredfacts, or facts about an end user the call did not name. Full list: what external keys can never see.
Waiting for an answer
ask_worker queues the question and waits up to timeout_seconds. Answers take seconds to minutes, so long ones outlast the wait. When that happens you get a result, not an error:
{ "wait_timed_out": true, "answer_id": "event_01k9p3zq8w4m1te6h2ndkbx9pf" }
- Call
wait_for_answerwith thatanswer_id—{"answer_id": "event_01k9p3zq8w4m1te6h2ndkbx9pf", "timeout_seconds": 120}— to keep waiting. Each call is a fresh bounded wait, and the answer keeps running the whole time. Repeat as often as needed; do not raise your client's transport timeout instead. - Always send
idempotency_key. Re-callingask_workerafter a timeout without the same key is a new question and a second run: double the cost, two answers, and the conversation lock making them queue behind each other. The same key with the same arguments returns the sameanswer_idand starts nothing; the same key with a different question is refused asidempotency_key_reused. timeout_seconds: 0skips the wait and returns theanswer_idstraight away.
A finished answer comes back whole. Both answer tools always include sources.
{
"answer_id": "event_01k9p3zq8w4m1te6h2ndkbx9pf",
"status": "completed",
"output_text": "She can move it without losing the deposit, as long as the change lands more than 30 days before the current date.",
"conversation_id": "support-ticket-9931",
"usage": { "input_tokens": 2841, "output_tokens": 96 },
"sources": [
{ "fact_id": "fact_01k9p3v8m7f2ha6z0qc4rjxn5w", "fact_text": "Deposits are non-refundable inside 30 days of the event date.", "kind": "decision", "subject": "refund policy" }
]
}
Errors
- Credential problems are plain HTTP. A bad, revoked, or expired key fails the request itself with
401, before any tool runs:{"error":{"code":"api_key_revoked","message":"This API key was revoked"}}. Going over 120 requests in a 60-second window fails the same way with429 rate_limited. The limit is per key, so give each client its own. - Everything a tool decides comes back as a readable tool result, with
isError: trueand a JSON body your agent can act on:{"error":"scope_missing","message":"This API key is missing the 'memory.write' scope","http_status":403}. - An unknown method or an unknown tool name comes back as a JSON-RPC error (
-32601and-32602), because the request never reached a tool.
error | Meaning and fix |
|---|---|
scope_missing | This key lacks the scope the tool needs. Scopes are fixed at creation, so create a key that has it in Settings → API Keys. |
out_of_credits | The workspace balance hit zero. Add credits or upgrade the plan; nothing runs until you do. |
conversation_busy | That conversation_id is still answering the previous message. Wait, or use a different one. |
validation_error | The arguments did not match the tool's schema. The message names the offending fields. |
not_found | No answer with that answer_id in this workspace. |
internal_error | Our side. Retry; if it persists, send us the timestamp. |