Memory server
services/mcp-memory exposes memory tools to MCP clients. It validates bearer credentials through auth, calls the memory service, enforces an org-wide Redis rate limit, and emits audit events.
Transport and auth
- Transport: Streamable HTTP at
/mcp. - Auth: bearer token to
AuthService.ValidateToken; failures are rejected and audited. - stdio: implemented for local clients, but requires
IBEX_MCP_TRANSPORT=stdioandIBEX_MCP_ALLOW_STDIO=true; it is forbidden whenIBEX_ENV=production. - Probes:
/health,/ready,/metrics, and/.well-known/oauth-protected-resource.
The discovery document is not evidence that this service is an OAuth authorization server. It is not.
Tools
| Tool | Arguments | Permission | Boundary |
|---|---|---|---|
search_memory | query 1–2000 chars, limit 1–50 (default 5), optional agent_id | MemoryRead | Org from the validated principal; agent verified active |
write_memory | content 1–8000 chars, category enum, confidence 0–1 (default 0.6), optional agent_id | MemoryWrite | Sets metadata.mcp_source=mcp_explicit |
record_feedback | memory_id, feedback enum, optional session_id, trace_id, notes ≤2000 | MemoryWrite | Agent-scoped principals must still pass ValidateAgent |
The tools call the memory HTTP service through IBEX_MEMORY_HTTP_URL; they do not open a direct database connection. Argument schemas are strict (additionalProperties: false), and unknown arguments are rejected rather than ignored.
write_memory sends:
X-Idempotency-Key = sha256("{org_id}:{agent_id}:{NFC(content).strip()}")
The content is not lowercased. Repeating the same logical write therefore reaches the memory dedup/idempotency path instead of creating a second record. Search success with zero hits is a successful result, not an error.
The tools resolve agent_id in this order: principal agent (when bound), explicit agent_id argument, otherwise the call fails closed. The resolved agent must belong to the principal's organization and be active.
Rate limiting
The limit is an org-wide Redis calendar-minute budget, default 120 requests per minute, with optional per-organization overrides. There is no per-agent or per-tool tier. An unconfigured Redis in development uses the in-process no-op limiter; configured Redis failures are fail-open in development and fail-closed in staging/production.
| Failure | Behavior |
|---|---|
| Limit exceeded | isError with rate_limited |
| Redis unavailable in development | Explicit no-Redis exception is allowed only in development |
| Redis unavailable in staging/production | rate_limit_unavailable; do not admit the call |
Audit durability
ibex.mcp_tool_calls via JSONEachRow, but a full queue drops events and sink failures are observable rather than transactionally guaranteed. The event contains request_id, org_id, agent_id, tool_name, latency_ms, success, error_code, and requested_at — never arguments, content, or tokens.Treat tool-call audit as incomplete until the sink, retention, restore path, and failure handling are verified for the target environment.
Failure codes
| Code | Meaning |
|---|---|
unauthorized | Missing/invalid bearer, or failed token validation |
auth_unavailable | AuthService could not be reached |
permission_denied | Token lacks the required permission or the agent is inactive/cross-org |
invalid_params | Tool arguments failed schema validation |
rate_limited | Org MCP budget exhausted |
rate_limit_unavailable | Protected-profile limiter unavailable |
backend_unavailable | Memory HTTP timeout, transport error, or 5xx |
backend_error | Memory returned a non-auth client/server error |
Cross-tenant agents are rejected at verification; memory responses with zero results remain successful.
Configuration
| Variable | Purpose |
|---|---|
IBEX_AUTH_GRPC_ADDR | AuthService target |
IBEX_MEMORY_HTTP_URL | Memory service origin; empty makes tools fail closed |
IBEX_MCP_MEMORY_TIMEOUT_MS | Memory HTTP timeout; default 5000 ms |
IBEX_MCP_REDIS_URL | Redis for rate limiting; required in protected profiles |
IBEX_MCP_RATE_LIMIT_RPM | Org-wide requests per minute; default 120 |
IBEX_MCP_RATE_LIMIT_ORG_OVERRIDES | Per-org RPM overrides |
IBEX_MCP_CLICKHOUSE_URL | Optional audit sink; unset means logs only |
IBEX_MCP_TRANSPORT | streamable_http or stdio |
IBEX_MCP_ALLOW_STDIO | Required for stdio; never enable in production |
Production discovery and resource URLs must be public HTTPS, non-loopback, and bound to an approved origin.
Run locally
cd services/mcp-memory
uv sync --frozen --extra dev
IBEX_AUTH_GRPC_ADDR=127.0.0.1:9091 \
.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8090
Limits
- No per-agent or per-tool rate limit; the budget is org-wide.
- No OAuth authorization-server implementation; the discovery document advertises a resource, not an issuer.
- Audit is logging-first and not evidence-grade by default.
- Tool results are data, not instructions; do not feed them back as system prompts.
- The MCP memory server does not use the shared
mcp_gatewayenforcement core yet. - Hosted capacity, multi-replica behavior, and durable audit retention are unproven.

