Memories API is a local FastAPI and MCP service for shared memory across chatbots and agents. It gives every client one SQLite-backed source of truth for preferences, facts, identity notes, and event history, so memory written by one tool is immediately available to the next instead of living in separate per-agent silos. That keeps long-running conversations consistent and makes recall behavior easier to inspect and reason about.
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install .uvicorn app.main:app --host 127.0.0.1 --port 8000 --reloadInteractive docs are available at http://127.0.0.1:8000/docs
The running app exposes:
- REST API at
http://127.0.0.1:8000/memories - Health endpoint at
http://127.0.0.1:8000/health - Readiness endpoint at
http://127.0.0.1:8000/ready - MCP streamable HTTP endpoint at
http://127.0.0.1:8000/mcp - MCP stdio entrypoint with
python -m app.mcp_server
The API and MCP server share the same server-managed memory shape. See docs/data_object_schema.md for the full schema.
The main fields and lifecycle rules are:
contentandtagsare required when creating a memory.memory_typedefaults tofactandstatusdefaults toactive.id,created_at,updated_at,last_accessed_at, andversionare managed by the server.- Allowed
memory_typevalues arepreference,fact,identity, andevent. - Allowed
statusvalues areactive,invalid, anddeleted. POST /memories/batchis all-or-nothing when validation fails.PATCHonly changesupdated_atandversionwhen an editable field actually changes.DELETE /memories/{id}is a soft delete that marks the row asdeleted.
The REST API is available at http://127.0.0.1:8000/memories.
curl http://127.0.0.1:8000/memoriesUse the interactive docs to create, update, delete, and filter memories.
The MCP server includes record, inspect, revise, retire, bootstrap, and search tools.
For prompt and resource design details, see docs/mcp_prompt_skill_design.md.
For local debugging, start the MCP Inspector:
mcp dev app/mcp_server.py
Interactive MCP Inspector available at http://localhost:6274.
Add to your claude_desktop_config.json or mcp-config.json file:
{
"mcpServers": {
"memories-api": {
"command": "python",
"args": ["-m", "app.mcp_server"],
"cwd": "path\to\Memories API",
"env": {
"MEMORIES_DB_FILE": "path\to\Memories API\data.db"
}
}
}
}
Point the client at:
http://127.0.0.1:8000/mcp
If the client application runs in Docker on the same machine, use the host-reachable URL that the container can resolve, for example:
http://host.docker.internal:8000/mcp
The repo includes a committed example file:
mcp_browser_clients.example.json
Create your real local override file in the repo root:
mcp_browser_clients.local.json
That local override file is gitignored and is the only file read by the app for browser-origin allowlisting.
If mcp_browser_clients.local.json does not exist, browser-based MCP access is denied by default, but the REST API and stdio MCP server still work.
Example local file for Open WebUI running in the browser at http://localhost:3000:
{
"browser_clients": [
{
"name": "open-webui-local",
"origin": "http://localhost:3000"
}
]
}If the local file exists but is invalid, the app logs a warning and denies browser-based MCP access instead of failing startup.
For a local Open WebUI setup, two values matter:
- Browser origin: usually
http://localhost:3000. This is what goes intomcp_browser_clients.local.json. - MCP server URL: if Open WebUI runs in Docker, it may need
http://host.docker.internal:8000/mcpinstead ofhttp://localhost:8000/mcpto reach the host machine.
HTTP and MCP expose one deterministic retrieval contract.
- HTTP uses
GET /memories. - MCP uses
prime_memory_contextfor startup recall andsearch_memoriesfor targeted recall. - Both accept
status,memory_type,tag,q,sort,limit, andoffset. - Both return
items,total,limit,offset, andhas_more. tagmatches exactly.qis case-insensitive lexical matching overcontentand tags, not semantic search;%,_, and\are treated as literal characters.- Filters compose with
AND. - Sort keys are
id,created_at,updated_at, andlast_accessed_at; timestamp sorts are descending withid DESCas a tie-breaker. last_accessed_atis refreshed only for rows actually returned byGET /memories/{id},GET /memories,search_memories, andprime_memory_context.
That keeps retrieval easy to test and consistent across HTTP and MCP.
The API includes lightweight local operational endpoints:
GET /healthreturns{"status": "ok"}as a side-effect-free ping. It does not read memories or refreshlast_accessed_at.GET /readychecks that SQLite is reachable and thememoriestable exists. It may initialize the local DB/schema on first run, but it does not create, read, or update memory rows.
Readiness responses include non-sensitive check status only. A ready response returns:
{
"status": "ready",
"checks": {
"database": "ok",
"memories_table": "ok"
}
}If a readiness check fails, the endpoint returns 503 with status set to not_ready and failed checks marked as failed. Responses do not include local database paths, raw exception text, or environment-derived file locations.
Every HTTP response includes X-Request-Id, including REST responses, MCP streamable HTTP responses, and middleware-generated rejections such as 413, 429, and MCP-origin 403. If a request sends X-Request-Id and it matches ^[A-Za-z0-9._:-]{1,128}$, the server echoes it. Missing or invalid values are replaced with a server-generated UUID.
The app emits one JSON request log record per HTTP request. Normal REST and MCP HTTP requests are logged at INFO; /health and /ready are logged at DEBUG to keep probe traffic quieter. Request log records contain only:
{
"method": "GET",
"path": "/memories",
"status": 200,
"duration_ms": 1.23,
"request_id": "example-request-id"
}The HTTP API and MCP streamable HTTP endpoint include conservative local safety limits by default. These are intended to protect a single-user local service from runaway agents, oversized payloads, and accidental request floods. They are not a substitute for authentication or transport security.
Schema and API limits:
- Memory
contentis capped at8,000characters. - A memory can have at most
20tags. - Each tag is capped at
64characters. tagandqretrieval filters are capped at256characters.POST /memories/batchaccepts at most25memories.
Runtime limits are configured with environment variables:
| Environment variable | Default | Notes |
|---|---|---|
MEMORIES_RATE_LIMITING_ENABLED |
true |
Set to false only as a local debugging escape hatch. |
MEMORIES_RATE_LIMIT_READS_PER_MINUTE |
120 |
Applies to REST memory reads. Reads refresh last_accessed_at. |
MEMORIES_RATE_LIMIT_WRITES_PER_MINUTE |
30 |
Applies to REST create, update, and delete requests. |
MEMORIES_RATE_LIMIT_BATCH_PER_MINUTE |
10 |
Applies to POST /memories/batch. |
MEMORIES_RATE_LIMIT_MCP_PER_MINUTE |
240 |
Applies to MCP streamable HTTP traffic under /mcp. |
MEMORIES_REQUEST_BODY_MAX_BYTES |
1048576 |
Applies to POST and PATCH REST/MCP HTTP request bodies. |
Rate limiting uses a fixed 60-second in-memory window per process. Clients are identified by a trimmed X-Client-Id header, capped at 128 characters, when present; otherwise the client IP is used. A limited request returns 429 with Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
/health and /ready are exempt from normal rate limiting.
Request body-size enforcement returns 413 with X-Request-Body-Limit when the declared or streamed request body exceeds the configured limit.
For behavioral load checks, use the standalone stress harness at scripts/rate_limit_stress.py. It exercises mixed agent usage, fast bursts, concurrent floods, and two-agent isolation across REST and MCP surfaces, then writes an HTML report. See scripts/README.md for setup, tuning options, and result interpretation.
This project is intended for single-user, local-only use on a trusted machine.
- The HTTP API and MCP streamable HTTP endpoint do not implement application-level authentication or authorization.
- Bind the HTTP API and MCP streamable HTTP endpoint to localhost only.
- Browser-based MCP access is denied by default unless you create a local browser client allowlist file.
- Browser origins are matched exactly.
- Default local safety limits reduce accidental runaway behavior but do not make the service safe to expose beyond localhost.
- Do not expose this service to the internet, a LAN, a shared VM, or a reverse proxy unless you add proper authentication, authorization, and transport security.
- Treat the SQLite database file and local MCP/client configuration as sensitive local data.
If you need multi-user or remote access, the current security model is not sufficient.