This guide shows how to connect Open WebUI to a local MemoryOps deployment via the MCP HTTP Streamable transport, verify the connection, and run a full memory read/write round-trip.
- MemoryOps API server running on :8080
- Docker Compose infra up (postgres, redis, qdrant)
- Migrations applied:
sqlx migrate run - MCP server started with
MCP_TRANSPORT=httpand bound to localhost/private networking. - Open WebUI installed and reachable from your browser
- API key obtained from
POST /v1/workspaces(created once at workspace bootstrap)
Create a workspace (example):
# Create workspace — returns workspace_id + api_key in a single call
curl -sS -X POST http://localhost:8080/v1/workspaces \
-H 'Content-Type: application/json' \
-d '{"name": "my-workspace"}'
# Response: {"workspace_id": "YOUR_WORKSPACE_ID", "api_key": "YOUR_MEMORYOPS_API_KEY"}Service ports (local dev): API :8080, MCP :3003, Frontend :5173, Postgres :5432, Redis :6379, Qdrant HTTP :6333, Qdrant gRPC :6334.
The compose service uses HTTP transport and binds to 127.0.0.1:3003 by default. Keep it local/private; do not expose MCP directly to the internet. To start an explicit local MCP container:
# Local/private HTTP transport
docker compose --profile mcp run --rm --service-ports \
-e MCP_TRANSPORT=http \
-e MCP_PORT=3003 \
mcpOr for local Rust development (no Docker for MCP):
MCP_TRANSPORT=http \
MCP_PORT=3003 \
DATABASE_URL=postgres://memoryops:memoryops@localhost:5432/memoryops \
REDIS_URL=redis://localhost:6379 \
QDRANT_URL=http://localhost:6334 \
cargo run -p mcpWhen the MCP server is running it is reachable at: http://localhost:3003/mcp
Use the explicit local command shown above when you want the MCP process available to Open WebUI:
docker compose --profile mcp run --rm --service-ports \
-e MCP_TRANSPORT=http \
-e MCP_PORT=3003 \
mcpIf you prefer running the MCP binary locally for development, set the environment and run the service directly:
MCP_TRANSPORT=http \
MCP_PORT=3003 \
DATABASE_URL=postgres://memoryops:memoryops@localhost:5432/memoryops \
REDIS_URL=redis://localhost:6379 \
QDRANT_URL=http://localhost:6334 \
cargo run -p mcp- Open WebUI → Settings → Tools → Add Tool Server (label may be “MCP Servers” in some versions).
- URL field:
http://localhost:3003(note: newer Open WebUI versions append/mcpautomatically — verify your OWUI version; try bothhttp://localhost:3003andhttp://localhost:3003/mcpif tools fail to appear). - Auth: Bearer token — supply your API key as
Authorization: Bearer mops_<prefix>_<hex32>(the alternative headerX-API-Key: mops_...also works). - Save and verify the tool list loads.
Authorization header example:
Authorization: Bearer YOUR_MEMORYOPS_API_KEY
Use the Open WebUI tool test panel to call memory_retrieve with this payload:
{ "query": "recent decisions", "limit": 5 }An HTTP 200 with a JSON body containing memories is expected. An empty memories: [] is valid when no memories exist yet — not an error.
- Open the model configuration in Open WebUI.
- Under Tools enable the MemoryOps tool server you added.
- Select which tools the model may call (recommended minimum:
memory_retrieve, plusmemory_store/memory_observeto enable writes). - Save and start a conversation. The model will call enabled tools automatically when it decides they are needed.
NOTE:
workspace_idis NEVER a tool parameter — it is injected from the authenticated MCP session and must not be included in tool inputs.
-
User: “Show recent decisions about the auth service.”
-
Model → Tool Call:
memory_retrieve
{ "query": "auth service decisions", "limit": 5 }- MemoryOps → Response:
{
"memories": [
{
"id": "3f9a...",
"content": "Adopt workspace-scoped API keys for service-to-service calls.",
"memory_type": "semantic",
"tags": ["auth","api-keys"],
"score": 0.87,
"importance_score": 0.72,
"created_at": "2026-04-28T10:00:00Z",
"source": "github"
}
],
"skills": []
}-
Model uses that context in its reply to the user.
-
User confirms the model's recommendation is correct.
-
Model logs the confirmed fact via
memory_observe(async processing path):
{
"content": "Confirmed: adopt workspace-scoped API keys for service-to-service calls.",
"source": "openwebui",
"tags": ["decision","auth"]
}MemoryOps → Response: {"id":"...","status":"queued"}
- Inspect queued observations with
memory_list_observations. Ifprocessed_at: nullit means the item is queued and awaiting processor work — this is normal, not an error.
memory_store: direct, immediate persistence of a memory unit. Use for well-formed, high-confidence decisions you want available for immediate retrieval.memory_observe: submits a raw observation into the async processor pipeline. Use for unstructured notes or things you want the processor to classify/score.
Rule of thumb: use memory_store for important, retrievable decisions; use memory_observe for noisy or raw content you want the system to consolidate.
You can submit feedback ratings inline in the same memory_retrieve call. This is preferred over a separate memory_feedback call.
Example:
{
"query": "recent auth decisions",
"limit": 5,
"feedback": {
"query_id": "previous-trace-uuid",
"ratings": [
{ "memory_id": "3f9a...", "rating": 1 }
]
}
}| Symptom | Root Cause | Fix |
|---|---|---|
| CORS error in browser | — | MCP already sets CorsLayer::permissive(). Check you're pointing at port 3003, not 8080. |
401 Unauthorized |
Wrong header or key format | Header must be Authorization: Bearer mops_<prefix>_<hex32>. X-API-Key: mops_... also works. |
Session errors / Mcp-Session-Id rejected |
Wrong transport or stale session | Set MCP_TRANSPORT=http when starting MCP and retry initialization. |
| Connection refused on port 3003 | MCP profile not running | Run docker compose --profile mcp ... with transport override, or cargo run -p mcp with env vars set. |
memories: [] in response |
No memories ingested yet | Valid. Seed data: API_KEY=... bash scripts/seed.sh, or ingest via memory_store / memory_observe. |
| Tools not showing in Open WebUI | OWUI version quirk | Some OWUI versions expect /mcp appended to the URL, others don't. Try both http://localhost:3003 and http://localhost:3003/mcp. |
processed_at: null in list_observations |
Job queued, not yet processed | Normal. Processor workers run async. Check RUST_LOG=debug for worker activity. If stuck, check Ollama is running (ollama serve). |
Terse checklist: add tool server → test memory_retrieve → enable tools on model → test write with memory_observe.