MCP

Memory server

Streamable HTTP MCP server for tenant-scoped memory search, write, and feedback.

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.

Provisional Ledger class: provisional
Verified 2026-10-21Tools work locally; audit durability and governance remain open

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=stdio and IBEX_MCP_ALLOW_STDIO=true; it is forbidden when IBEX_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

ToolArgumentsPermissionBoundary
search_memoryquery 1–2000 chars, limit 1–50 (default 5), optional agent_idMemoryReadOrg from the validated principal; agent verified active
write_memorycontent 1–8000 chars, category enum, confidence 0–1 (default 0.6), optional agent_idMemoryWriteSets metadata.mcp_source=mcp_explicit
record_feedbackmemory_id, feedback enum, optional session_id, trace_id, notes ≤2000MemoryWriteAgent-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.

FailureBehavior
Limit exceededisError with rate_limited
Redis unavailable in developmentExplicit no-Redis exception is allowed only in development
Redis unavailable in staging/productionrate_limit_unavailable; do not admit the call

Audit durability

With no ClickHouse URL, the default sink writes metadata to logs only. With a ClickHouse URL, the HTTP sink inserts into 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

CodeMeaning
unauthorizedMissing/invalid bearer, or failed token validation
auth_unavailableAuthService could not be reached
permission_deniedToken lacks the required permission or the agent is inactive/cross-org
invalid_paramsTool arguments failed schema validation
rate_limitedOrg MCP budget exhausted
rate_limit_unavailableProtected-profile limiter unavailable
backend_unavailableMemory HTTP timeout, transport error, or 5xx
backend_errorMemory returned a non-auth client/server error

Cross-tenant agents are rejected at verification; memory responses with zero results remain successful.

Configuration

VariablePurpose
IBEX_AUTH_GRPC_ADDRAuthService target
IBEX_MEMORY_HTTP_URLMemory service origin; empty makes tools fail closed
IBEX_MCP_MEMORY_TIMEOUT_MSMemory HTTP timeout; default 5000 ms
IBEX_MCP_REDIS_URLRedis for rate limiting; required in protected profiles
IBEX_MCP_RATE_LIMIT_RPMOrg-wide requests per minute; default 120
IBEX_MCP_RATE_LIMIT_ORG_OVERRIDESPer-org RPM overrides
IBEX_MCP_CLICKHOUSE_URLOptional audit sink; unset means logs only
IBEX_MCP_TRANSPORTstreamable_http or stdio
IBEX_MCP_ALLOW_STDIORequired 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_gateway enforcement core yet.
  • Hosted capacity, multi-replica behavior, and durable audit retention are unproven.