Install
$ agentstack add mcp-vortx-ai-emem Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
About
emem
Earth as memory, for real-world agents.
Hosted · Docs · Spec · OpenAPI · Try it · /verify · Gallery · HF Space
Ask an AI agent what is on the ground at 19.07° N, 72.87° E and it will guess. It has no fixed handle for that patch of Earth, and no way to prove whatever number it returns. emem is the handle. It is a shared memory of the planet that an agent can read, write, and cite, where every answer is signed so anyone can check it later without trusting the server that produced it.
The planet is cut into fixed cells about 9.55 m across, the way a page is cut into words. One measurement at one cell is a fact: an elevation, a rainfall total, this year's forest loss, a satellite embedding. Every fact is signed. When an agent asks about a place nobody has measured yet, the responder pulls the value from a real satellite source, signs it, and hands it back in the same response. Nothing is pre-seeded. Every cell on Earth answers from the first request.
emem is a protocol. A fact is named by the blake3 hash of its own bytes, so the name carries the data's fingerprint and means the same thing on every machine. The responder signs that name with an ed25519 key, the kind that secures SSH and HTTPS. Any responder can serve a fact and any client can verify it offline, with no account and no key to manage. Paste a fact id into a chat and a colleague pulls the same bytes from any node and checks the signature in their own browser at /verify. The hosted node is https://emem.dev. The same binary self-hosts with one docker run, and the same handlers answer both MCP and plain REST. Run enough nodes and you get a federation: independent responders that resolve the same ids byte for byte and write down where they disagree. The memory gets more trustworthy as more agents use it.
How a fact gets made and proven
A cell64 addresses a place the way a token addresses text in an LLM. Every patch of ground about 9.55 m wide gets a 64-bit id, and ids that look alike sit physically near each other. A fact is one measurement at that cell, keyed by (cell, band, time) and packed in a fixed byte order (canonical CBOR) so the same reading hashes the same way on every machine. That blake3 hash is the fact's content id. Change one byte and the id changes, so the id proves the bytes. The responder signs it. The signed envelope it returns is the receipt, and the receipt checks out offline against the responder's public key without any trust in the server.
When an agent asks for a band at a cell that has no signed fact yet, the responder fetches the underlying tile through one of its 46 upstream sources, signs the result under its own key, persists it, and returns it in the same response. A cold read takes about 180 ms. A warm read is under ten. Five of the 46 schemes are declared but not yet wired (openet.30m.daily, dynamic_world.v1, tropomi.s5p.ch4, tropomi.s5p.no2, viirs.dnb.monthly); they answer with a typed Absence. When a band genuinely has no value at a cell, because the place is outside coverage or the upstream is unreachable, the answer is still a signed absence with a reason you can read. An empty answer is a citable receipt. The catalog never promises more than it can sign.
Try it (no install, no key)
# Geocode a place to a cell64.
curl -s -X POST https://emem.dev/v1/locate \
-H 'content-type: application/json' \
-d '{"q":"Bengaluru"}' | jq .cell64
# "defi.zb493.xuqA.zcb5f" # (geocoder result, may drift)
# Recall a band at that cell (auto-fetched if cold).
curl -s -X POST https://emem.dev/v1/recall \
-H 'content-type: application/json' \
-d '{"cell":"defi.zb493.xuqA.zcb5f","bands":["weather.temperature_2m"]}' \
| jq '.facts[0]'
# Ask a free-text question; the foundation-embedding fan-out fires
# automatically on "find places like" / "what changed" intents.
curl -s -X POST https://emem.dev/v1/ask \
-H 'content-type: application/json' \
-d '{"q":"find places like Yellowstone","place":"Yellowstone National Park"}' \
| jq '.answer'
# Hunter mode: discover event hotspots over a named region. The same
# classifier reads "find in " from /v1/ask and routes
# here; structured callers can hit /v1/hunt directly.
curl -s -X POST https://emem.dev/v1/hunt \
-H 'content-type: application/json' \
-d '{"event":"algal_bloom","region":"Lake Erie"}' \
| jq '.hotspots[0]'
The receipt's fact_cid is a durable handle. Re-fetching it from any responder, in any year, returns the same bytes.
Verify an answer (four curls)
The pitch lives or dies on this flow. Every recall response carries a receipt with fact_cids[], a merkle_proof, and an Ed25519 signature over a domain-separated, length-prefixed preimage: blake3("emem.preimage.v1" ‖ "receipt" ‖ tagged(request_id, served_at, [scope], [as_of], [edges], [manifest], primitive, cells[], fact_cids[])). Tagging every field and prefixing its length means no two distinct responses can ever share signed bytes; the receipt's preimage_version selects the rule, and pre-v1 receipts still verify under the original one. The signer's public key is stable; the receipt verifies offline against any copy of the responder pubkey. The merkle tree uses RFC 6962 leaf/node domain separation and rejects duplicate leaves.
Here is a real one. Ask https://emem.dev for the elevation under Denver and it returns the city's nickname as a signed number, mile-high at 1609 m, which anyone can re-check without trusting the server:
// POST /v1/recall {"cell":"defi.zb5c4.guxe.nuxe","bands":["copdem30m.elevation_mean"]}
{
"facts": [{ "cell": "defi.zb5c4.guxe.nuxe", "band": "copdem30m.elevation_mean",
"value": 1609.0, "unit": "m", "source": "copernicus.dem.glo30" }],
"receipt": {
"primitive": "emem.recall",
"fact_cids": ["72wdchiyurfrjxz7zat6kor7gjnvsn564fbrzjkmlhagoy4rrh4a"],
"responder_pubkey_b32": "777er3yihgifqmv5hmc2wwmy…",
"preimage_version": 1,
"signature": "…ed25519 over the canonical preimage…"
}
}
Paste that fact_cid into /verify and the page re-derives the hash and checks the signature in your browser. The four curls below do the same from a shell:
# 1. Resolve a place to a cell64.
CELL=$(curl -s -X POST https://emem.dev/v1/locate \
-H 'content-type: application/json' \
-d '{"q":"Golden Gate Park, San Francisco"}' | jq -r .cell64)
# 2. Recall a band and capture the receipt envelope.
curl -s -X POST https://emem.dev/v1/recall \
-H 'content-type: application/json' \
-d "{\"cell\":\"$CELL\",\"bands\":[\"indices.ndvi\"]}" > /tmp/recall.json
jq '.receipt | {primitive, served_at, responder_pubkey_b32, fact_cids, merkle_proof: .merkle_proof.root}' \
/tmp/recall.json
# 3. Ask the responder to verify its own signature (server-side check).
jq '{receipt: .receipt}' /tmp/recall.json > /tmp/receipt.json
curl -s -X POST https://emem.dev/v1/verify_receipt \
-H 'content-type: application/json' --data @/tmp/receipt.json
# {"valid":true,"preimage_blake3_hex":"…","fact_cids_count":1,"signer_pubkey_b32":"…",…}
# 4. Reproduce: pull the same fact_cid from any responder, on any day.
# The cell, band, tslot, and derivation.fn_key are content-addressed, so
# the bytes you receive will hash to the same fact_cid.
jq '.facts[0].derivation' /tmp/recall.json
For a browser-only verify, open /verify/; the page does the same Ed25519 check in WebCrypto + @noble/ed25519 so you never have to trust the responder you got the receipt from. A guided walk lives at /demos/signed-answer.
Architecture
One binary. The same handlers answer MCP and plain REST, reads need no auth, and every write lands in an append-only signed log. Four content-addressed manifests (bandscid, algorithmscid, sourcescid, schemacid) pin exactly what produced each answer. The full deployment suite lives at /docs/diagrams.
The memory layer
A cache hands back a tile. A memory remembers what it saw, links it to what it saw before, and says so when two sources disagree. emem gives an agent that second thing on top of the fact store, and the agent owns it.
An agent's memory of Earth, drawn as an engram. Each cell is a node and each edge a synapse that relates, supersedes, or disagrees. Recall draws signed facts inward to the lotus where the shared memory consolidates, and every node carries its own signature.
Writes land in /memories/ as content-addressed, signed files. memory_create makes one, memory_str_replace and memory_insert edit it, and memory_search runs a BGE-768 embedding query over the contents through a LanceDB IVF_PQ index, so an agent finds a note it wrote last week by meaning instead of by filename. Each file carries a kind from the CoALA taxonomy: episodic for what happened, semantic for what holds true, procedural for how to do a thing, resource for a pointer out. A write under /memories/by_attester// is capability-bound, so a path owned by one key turns away every other signer. The signature that proves a Sentinel-2 reading is the same signature that proves the agent's own notes are untouched.
The memory connects facts and notices when they fight. memory_bundle folds N facts into one signed envelope, memb:, that resolves to identical bytes on any peer, so an agent hands over a single citation for a whole finding instead of a list of loose ids. memory_contradictions walks the cases where two attesters signed different values at the same (cell, band, tslot) and scores the gap by band kind: normalised spread for a scalar, mean cosine for a vector, mode-share for a category. A second node that read the same Sentinel scene on a cloudier day leaves a trace the agent can weigh instead of a silent overwrite.
Every read takes a bi-temporal bound. as_of_tslot asks what the world looked like at a past moment. as_of_signed_at asks what the system knew at a past moment. Set both and both hold. The receipt records the bound, so an auditor in 2027 takes a 2026 receipt to any peer and replays the exact same query.
Streams
The memory is live, and you can watch it. GET /v1/stream is a Server-Sent Events heartbeat. Every few seconds the responder signs a snapshot of corpus state and pushes it, so a dashboard or an agent follows the shared memory growing without polling for it. The tick is signed like everything else, captured here straight off https://emem.dev:
event: state
data: {
"type": "corpus.state",
"served_at": "2026-06-12T16:17:30Z",
"corpus": { "distinct_cells": 8147, "distinct_bands": 75, "facts_scanned": 32768 },
"responder": { "pubkey_b32": "777er3yihgifqmv5hmc2wwmy…", "key_epoch": 0 },
"signature": {
"alg": "ed25519",
"preimage": "emem.stream.tick|v0.1.0|epoch0|2026-06-12T16:17:30Z|registry:3pbqnyni…|cells:8147",
"signature_b32": "xk2hiluwmfywwnfj…"
}
}
GET /v1/memory/sse?path_prefix=&kind=&attester= is the narrow stream. It pushes one event the moment a memory write commits, filtered server-side, so a compliance subscriber sees a write to a watched path the instant its sled commit lands rather than on the next poll.
Connect your AI assistant
The MCP endpoint is https://emem.dev/mcp. Drop a config snippet into your client.
| Client | Config | |-----------------------|---------------------------------------------------------------------| | Claude Desktop | [examples/claude-desktop.json](examples/claude-desktop.json) | | Claude Code | [examples/claude-code.mcp.json](examples/claude-code.mcp.json) | | Cursor | [examples/cursor.mcp.json](examples/cursor.mcp.json) | | Cline (VS Code) | [examples/cline.mcp.json](examples/cline.mcp.json) | | Gemini CLI | gemini extensions install https://emem.dev/gemini-extension.json | | ChatGPT (Custom GPT) | [examples/openai-gpt-action.json](examples/openai-gpt-action.json) | | LangChain (Python) | [examples/langchain.py](examples/langchain.py) | | LangChain MCP agent | [examples/langchain/](examples/langchain/) | | LlamaIndex (Python) | [examples/llamaindex.py](examples/llamaindex.py) | | LlamaIndex MCP agent | [examples/llamaindex/](examples/llamaindex/) | | Agno MCP agent | [examples/agno/](examples/agno/) | | Pydantic AI MCP agent | [examples/pydantic-ai/](examples/pydantic-ai/) | | AutoGen MCP agent | [examples/autogen/](examples/autogen/) | | CrewAI MCP agent | [examples/crewai/](examples/crewai/) | | Mastra MCP agent | [examples/mastra/](examples/mastra/) |
Python (ememdev) and TypeScript (@emem/client) SDKs live under sdks/ (PyPI / npm publication pending; install from the repo today).
Primitives
81 MCP tools (10 core, 71 extended), 93 documented REST paths under /v1/*, surfaced through /openapi.json. Every tool carries a when_to_use string written for LLM tool-selection, and four MCP behavioural annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint). A no-param tools/list returns all 81 tools (so every MCP client discovers the full surface); pass {"tier":"core"} for just the 10 essentials. Tools are callable via tools/call regardless of tier.
- Locate: name or lat/lng →
cell64. Five-layer cascade: wide-bbox table → embedded gazetteer → GeoNames cities-5000 (68 581 places, in-process) → sled cache → Photon → Nominatim. Polygon geometry from Overturedivisions/division_area. District-level queries reroute through Overture when Nominatim returns a POI courthouse. - Memory substrate (state + tokens + bundles + memory files + search + contradictions + SSE):
POST /v1/statereturns a signed dense per-place embedding (view=encoderdefault 128-D,view=cubefull 1792-D).POST /v1/state_multifans acrossgeotessera+clay_v1+prithvi_eo2+galileo.POST /v1/state_diffreturns residual + L2 + cosine between two vintages.POST /v1/memory_tokencomposesmemt::.POST /v1/memory_bundlecomposes a signed envelopememb:over N (cell, band, tslot) triples. Six MCP file-op verbs (memory_view,memory_create,memory_str_replace,memory_insert,memory_delete,memory_rename) conform to Anthropic's memory-tool spec; every write is ed25519-signed and content-addressed. Paths under/memories/by_attester//...enforce capability binding (ed25519 signature overblake3("emem.memory_write|" + verb + "|" + path + "|" + body_hash)). Each file carries akindfrom the CoALA taxonomy (episodic/semantic/procedural/resource).POST /v1/memory/searchdoes BGE-768 semantic search over file contents via a LanceDB IVF_PQ partition.POST /v1/memory_contradictionswalks a parallel multi-attester index and scores disagreement per band kind (scalar / vector / categorical).GET /v1/memory/sse?path_prefix=&kind=&attester=streams write events with server-side filter. Every read primitive acceptsas_of_tslot+as_of_signed_atfor bi-temporal queries (valid-time + transaction-time); the receipt carries anas_ofblock when set. See [docs/memory.md](docs/memory.md) for the full reference. - Recall / recallmany / recallpolygon: 124 materializer-wired band names across 43 cube slots. Recall answers any wired band, auto-fetching on a cold miss and signing the result. Signed Absence on out-of-coverage.
- Find similar: k-NN over any vector band. Hamming fast path (sign-bit pop-count) auto-derives
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Vortx-AI
- Source: Vortx-AI/emem
- License: Apache-2.0
- Homepage: https://emem.dev/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.0.2 Imported from the upstream source.