Remote mcp server to let an agent interact with zep graphs.
One address for any MCP client, signed in with your Odel account.
5 read2 act
Grouped by tool name.
Odel charges nothing per call.
search_memoryHybrid semantic + keyword search over the caller's memory graph. Defaults to searching both the personal space and the shared space, returning ranked facts (graph edges).4 parametersquerystringrequiredNatural-language search query.
spacestringoptionalRestrict to one space, or 'all' to query both.
One of self, shared, all
Default "all"
scopestringoptionalWhat to return: edges (facts), nodes (entities), or raw episodes.
One of edges, nodes, episodes
Default "edges"
limitintegeroptionalDefault 10
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 1,
"description": "Natural-language search query."
},
"space": {
"default": "all",
"description": "Restrict to one space, or 'all' to query both.",
"type": "string",
"enum": [
"self",
"shared",
"all"
]
},
"scope": {
"default": "edges",
"description": "What to return: edges (facts), nodes (entities), or raw episodes.",
"type": "string",
"enum": [
"edges",
"nodes",
"episodes"
]
},
"limit": {
"default": 10,
"type": "integer",
"minimum": 1,
"maximum": 50
}
},
"required": [
"query"
]
}list_recentReturns the most recent raw episodes (untransformed memory entries) for a space. Useful for debugging what's been ingested or auditing recent activity.2 parametersspacestringoptionalOne of self, shared
Default "self"
limitintegeroptionalDefault 10
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"space": {
"default": "self",
"type": "string",
"enum": [
"self",
"shared"
]
},
"limit": {
"default": 10,
"type": "integer",
"minimum": 1,
"maximum": 50
}
}
}whoamiReports the caller's Odel identity and the Zep graphs their memory spaces map to. Useful for debugging the secrets wiring.No parametersTakes no parameters.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}get_entityLooks up an entity by name and returns the entity node plus all facts (edges) involving it. Use this for entity-centric questions like 'what do I know about Eyeshare?' โ complementary to search_memory, which is query-driven.2 parametersentitystringrequiredEntity name to look up (matched semantically).
spacestringoptionalOne of self, shared, all
Default "all"
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"entity": {
"type": "string",
"minLength": 1,
"description": "Entity name to look up (matched semantically)."
},
"space": {
"default": "all",
"type": "string",
"enum": [
"self",
"shared",
"all"
]
}
},
"required": [
"entity"
]
}explain_episodeGiven an episode UUID, returns the nodes (entities) and edges (facts) Zep extracted from it. Use for auditing extraction quality, tracing where a fact came from, or debugging why search returned (or didn't return) something. Episode UUIDs come from add_memory, list_recent, or search_memory results.2 parametersepisodeUuidstring ยท uuidrequiredUUID of the episode to inspect.
spacestringoptionalThe space the episode was written to.
One of self, shared
Default "self"
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"episodeUuid": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "UUID of the episode to inspect."
},
"space": {
"default": "self",
"description": "The space the episode was written to.",
"type": "string",
"enum": [
"self",
"shared"
]
}
},
"required": [
"episodeUuid"
]
}add_memoryStores a piece of text in the caller's memory graph. Defaults to the caller's personal space; pass space='shared' to write to the shared knowledge graph. Episodes are processed asynchronously by Zep โ entities and relationships extracted in the background.2 parameterscontentstringrequiredThe text content to remember.
spacestringoptionalWhich space to write to.
One of self, shared
Default "self"
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"content": {
"type": "string",
"minLength": 1,
"description": "The text content to remember."
},
"space": {
"default": "self",
"description": "Which space to write to.",
"type": "string",
"enum": [
"self",
"shared"
]
}
},
"required": [
"content"
]
}add_factAsserts a structured fact (subject โ relation โ object) directly into the graph, bypassing entity extraction. Node names are matched against existing entities, and Zep invalidates existing facts the new one contradicts, so recording the new state of something is also how a superseded fact is retired.7 parameterssourceNodestringrequiredName of the source entity (subject).
relationstringrequiredRelation in SCREAMING_SNAKE_CASE, e.g. WORKS_ON, REPORTS_TO.
targetNodestringrequiredName of the target entity (object).
factstringrequiredNatural-language statement of the fact.
spacestringoptionalOne of self, shared
Default "self"
validAtstring ยท date-timeoptionalISO 8601 timestamp the fact became true.
invalidAtstring ยท date-timeoptionalONLY set when a hard expiration is known at write time (e.g. contract end date, deprecation deadline).
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"sourceNode": {
"type": "string",
"minLength": 1,
"description": "Name of the source entity (subject)."
},
"relation": {
"type": "string",
"minLength": 1,
"pattern": "^[A-Z][A-Z0-9_]*$",
"description": "Relation in SCREAMING_SNAKE_CASE, e.g. WORKS_ON, REPORTS_TO."
},
"targetNode": {
"type": "string",
"minLength": 1,
"description": "Name of the target entity (object)."
},
"fact": {
"type": "string",
"minLength": 1,
"description": "Natural-language statement of the fact."
},
"space": {
"default": "self",
"type": "string",
"enum": [
"self",
"shared"
]
},
"validAt": {
"description": "ISO 8601 timestamp the fact became true.",
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
},
"invalidAt": {
"description": "ONLY set when a hard expiration is known at write time (e.g. contract end date, deprecation deadline).",
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
}
},
"required": [
"sourceNode",
"relation",
"targetNode",
"fact"
]
}Odel module that exposes Zep Cloud as long-term memory for agents. Each caller has a personal space and, optionally, a shared space, both backed by Zep's temporal knowledge graph.
Built on @odel/module-sdk and the Zep V4 SDK, it runs on Cloudflare Workers as a stateless MCP server: a fresh server and transport per request.
Each user configures these when installing the module. Odel injects them per request, so the module itself stores nothing.
| Secret | Required | Value |
|---|---|---|
ZEP_API_KEY | yes | Zep Cloud project API key. |
ZEP_USER_GRAPH_UUID | yes | The graph UUID of your Zep user. It is a different value from the user's own UUID. |
ZEP_SHARED_GRAPH_UUID | no | UUID of a shared graph for memories across users. Without it, only space='self' is available. |
The secrets are declared once in src/config.ts. That schema drives the odel://config manifest, the runtime check, and tool gating: an Odel caller who has not configured the required secrets does not see the tools.
| Tool | Purpose |
|---|---|
add_memory | Write text into a space; entities/relationships extracted async. |
search_memory | Hybrid semantic + keyword search over edges, nodes or episodes. |
add_fact | Assert a structured (subject, relation, object) triple. Nodes are matched by name, and contradicted facts are invalidated by Zep. |
get_entity | Look up an entity by name and return all facts involving it โ entity-centric retrieval. |
explain_episode | Show what nodes/edges Zep extracted from a given episode โ audit extraction quality. |
list_recent | List recent raw episodes, newest first โ debug/audit. |
whoami | Report the Odel identity and the graphs the spaces map to. |
Temporal fact lifecycle:
add_fact the new state. Zep invalidates the facts it contradicts and keeps them as history. Zep V4 cannot set invalidAt on an existing edge, so there is no separate invalidate tool.add_fact with invalidAt set.search_memory and get_entity default to space=all: both spaces when a shared graph is configured, otherwise the personal one.
pnpm install
pnpm typecheck
pnpm dev # wrangler dev
Without an Odel envelope (local wrangler dev, an inspector), every tool is listed. A call still needs the secrets in params._meta["app.odel/secrets"].
src/
โโโ index.ts Worker entry, tool registration
โโโ config.ts declared secrets (configSchema)
โโโ spaces.ts space โ Zep graph UUID routing
โโโ zep/client.ts per-call ZepClient from the caller's key, result helpers
โโโ tools/ one file per tool