Cala — the knowledge layer for AI agents
Cala is a knowledge graph of the world's public data: named entities, their attributes, the relationships between them, and time-stamped observations — every value carrying a source and date. Query it instead of scraping the web.
This skill assumes the Cala tools are loaded (their schemas carry the params and body shapes). It covers what the schemas can't: which tool to reach for, the query language, the read discipline, and how to fail.
Access
MCP (preferred): connect to https://api.cala.ai/mcp/ with your API key, then call the tools directly. If the Cala tools are not in your context, the server is not configured — halt and send the user to https://docs.cala.ai/integrations/mcp.
REST (fallback): base URL https://api.cala.ai/v1, header X-API-KEY: <key>. Endpoints mirror the tool names 1:1; spec at https://api.cala.ai/openapi.json. Get a key at https://console.cala.ai/api-keys.
Pick the right tool
Filter / list / "find all X where Y"? → knowledge_query
Open-ended "what", "who", "explain"? → knowledge_search
Have a name, want the entity (a seed UUID)? → entity_search
Have a UUID, want a coarse profile? → entity_retrieval (no body)
Have a UUID, don't know what's queryable? → entity_introspection
Have a UUID, want specific fields/rels/metrics → entity_introspection → entity_retrieval
Go structured. knowledge_query and a projected entity_retrieval cost far fewer tokens and behave more predictably than knowledge_search. When you know the shape of the answer, take the structured path; reach for knowledge_search only for genuinely open-ended questions.
knowledge_query — structured filter
A dot-notation query language. Read each . as one hop across the graph: OpenAI.founded.year hops the founded edge, then reads year. Natural variations in field names are interpreted, so write what you mean.
- Operators:
=exact ·!=not equal ·><<=>=numeric ·,= AND on one field (investors=Sequoia Capital,Andreessen Horowitz→ backed by both, not either). - Modifiers, in clause order
filters → order_by → limit → return():order_by=field ASC|DESC— changes which records surface, not just their order (default sort is relevance).limit=N— cap results.return(f1, f2, ...)— project only these fields; cuts token cost.
- Numeric fields may return approximate strings (
"over 100M","~206,753") — synthesize, don't treat as exact.
{"input": "OpenAI.founded.year"}
{"input": "startups.location=Spain.funding>10M.order_by=funding DESC.limit=5.return(name, funding, sector)"}
{"input": "companies.investors=Sequoia Capital,Andreessen Horowitz"}
{"input": "companies.sector=climate tech.funding_round.series=A,B.location=Southern Europe.return(name, funding, investors)"}
Returns results (rows) + entities (everything mentioned — locations, people, not 1:1 with rows). Feed entity UUIDs to entity_retrieval to go deeper.
Named entities — entity_search → entity_retrieval
Resolve a name to a seed entity with entity_search (fuzzy name → UUID; each hit carries name, entity_type, description — use the description to pick).
From the seed UUID, entity_retrieval reads in two modes:
- Coarse profile — no body; default properties only, no relationships or numerical observations.
- Projection — a body naming exact
properties,relationships, andnumerical_observations; the only way to get relationships or time-series, and the leanest read.
Introspect → project. The queryable schema varies per entity, so run entity_introspection on the seed first — it returns the populated neighborhood (properties, incoming/outgoing relationships, and metric UUIDs). Then project exactly those. Projecting blind wastes round-trips and may request empty edges.
Relationship shape. Edges are UPPER_SNAKE types with a direction, traversed incoming or outgoing. Dozens exist — for example, incoming to a company FOUNDED (founders), IS_CEO_OF (execs), IS_DIRECT_OWNER_OF (investors); outgoing IS_ULTIMATE_PARENT_OF (subsidiaries). Introspection is the source of truth for which edges a given entity actually has.
Observations (numerical_observations) are time-series — financials, macro indicators, FX, positioning — each keyed by a metric UUID from introspection. They are the latest sourced values with history, not live ticks.
Every property and relationship carries its provenance (sources: name, document URL, date). Preserve it — pass source and date through when you present a fact.
knowledge_search — narrative answer with provenance
For open-ended questions. Returns content (markdown) plus the provenance chain:
explainability[i]={content: "claim", references: [ctx-id, ...]}— each claim maps to context IDs.context[j]= source docs, each withidandorigins[k].document.url(citable link) +origins[k].source.name(publisher).
To cite a claim: match its references to context[j].id, then use that context's origins URL and publisher. entities holds UUIDs for drill-down.
Failure — halt, don't confabulate
On any failure, halt and surface it. Never silently fall back to web search, training recall, or memory — that is confabulation dressed as a Cala answer. The user can ask for a fallback explicitly.
- Unreachable → halt; point to
https://console.cala.ai/api-keysandhttps://docs.cala.ai/integrations/mcp. - Timeout (queries can take up to 180s; usually the host, not Cala) → retry the same call once, then halt: "raise the MCP client timeout to ~180s and retry."
- No data → distinguish
{"results": [], "entities": []}(no match / too ambiguous) from{"results": [{"error": "...too complex..."}], "entities": null}(too complex). Broaden or simplify, or switch toknowledge_search; if still empty, halt. - Rate-limited (HTTP 429) → halt, surface, don't retry.