agentmemory-mcp-gateway

agentmemory-mcp-gateway

Enables remote MCP clients to securely access AgentMemory tools like memory_recall, memory_smart_search, and memory_save through OAuth 2.1 and Streamable HTTP, while keeping the AgentMemory backend secret private.

Category
访问服务器

README

agentmemory-mcp-gateway

Single-user OAuth 2.1 gateway that exposes a small AgentMemory MCP tool set to remote clients.

MCP clients authenticate to this service. This service authenticates to AgentMemory. The AgentMemory backend secret never leaves the gateway.

What it does

  • Speaks remote MCP over Streamable HTTP at /mcp
  • Acts as the OAuth authorization server and protected resource
  • Allows exactly one pre-seeded human to sign in and grant consent
  • Forwards allowlisted tools/list and tools/call traffic to AgentMemory REST
  • Fails closed when AgentMemory is unavailable

Intended clients: ChatGPT, Notion Custom Agents, Codex cloud, and other standards-compliant remote MCP clients.

Public URL shape:

https://memory-mcp.example.com/mcp

Architecture

MCP client
  -> HTTPS gateway (this service)
    -> private AgentMemory REST API

Trust boundaries:

  • MCP clients see only the public HTTPS origin, OAuth metadata, and allowlisted tool schemas/results.
  • AgentMemory stays on Railway private networking. Clients never receive AGENTMEMORY_URL or AGENTMEMORY_SECRET.
  • Incoming Authorization headers are used only to validate the client access token. The gateway always builds a new Authorization: Bearer ${AGENTMEMORY_SECRET} header for upstream calls.
  • SQLite stores authentication and OAuth state only. It is not a memory database.

This is a separate Railway service from AgentMemory. Run exactly one replica.

Why REST instead of @agentmemory/mcp

@agentmemory/mcp can fall back to a local memory database when upstream is unreachable. That is unacceptable for a remote personal gateway.

This service calls only:

  • GET /agentmemory/mcp/tools
  • POST /agentmemory/mcp/call with { "name": string, "arguments": object }

If AgentMemory is down, malformed, or times out, the gateway returns a safe MCP error. It does not create, open, or write another memory store.

Why SQLite exists

SQLite at DATABASE_PATH (default /data/oauth.sqlite) holds:

  • the one user and password hash
  • sessions and consent
  • OAuth client registrations
  • authorization codes
  • access/refresh-token and revocation state
  • signing keys / JWKS

It never stores AgentMemory observations or embeddings.

The in-memory rate limiter is also single-replica only. Do not scale this service horizontally.

Strict single-user model

  • Email/password only
  • No GitHub, social login, magic links, invitations, or password recovery
  • No public signup and no user-management API
  • Client registration (CIMD / DCR) is not human registration
  • Only the seeded user's durable ID may sign in, approve consent, or receive usable MCP tokens
  • Production startup fails if the user table does not contain exactly one row

Authentication errors are generic. They do not disclose whether an email exists.

Environment

Variable Required Purpose
PUBLIC_URL yes Canonical public origin. No path, query, fragment, or credentials. HTTPS except loopback.
BETTER_AUTH_SECRET yes Better Auth signing/encryption secret, 32+ characters
DATABASE_PATH yes SQLite file path, for example /data/oauth.sqlite
AGENTMEMORY_URL yes Private AgentMemory origin
AGENTMEMORY_SECRET yes Backend bearer for AgentMemory, 32+ characters
ALLOWED_TOOLS no Default memory_recall,memory_smart_search,memory_save
PORT no Listen port. Railway sets this. Default 8080
ADMIN_EMAIL seed only Administrator email
ADMIN_PASSWORD seed only Strong generated password, 20+ characters

PUBLIC_URL is the single issuer and the origin for /mcp. The protected resource identifier is ${PUBLIC_URL}/mcp.

Copy .env.example. It contains placeholders only.

Local development

nvm install
cp .env.example .env
# fill local loopback values, for example PUBLIC_URL=http://127.0.0.1:8080
npm install
npm run seed-admin
# remove ADMIN_PASSWORD from .env
npm run dev

Useful checks:

npm run format
npm run lint
npm run typecheck
npm test
npm run build

Secure one-time administrator seeding

railway run only injects variables into a local command. It cannot write to the Railway volume. Seed inside the deployed container after /data is mounted.

Local

npm run seed-admin
# remove ADMIN_PASSWORD from .env

Production image / Railway

The image includes dist/seed-admin.js and starts with node dist/start.js.

  1. Generate a long random password in 1Password. Do not store it in git, SQLite, Docker, or logs.
  2. Set temporary ADMIN_EMAIL and ADMIN_PASSWORD (20+ characters) on the service.
  3. Deploy or restart so the container runs with /data mounted.
  4. With those variables set, node dist/start.js runs node dist/seed-admin.js in-process, prints the durable user ID, and exits 0 without opening the HTTP port.
  5. Remove ADMIN_PASSWORD and ADMIN_EMAIL, then restart. The process then serves HTTP.
  6. If both variables are still set after a user exists, startup logs that they must be removed and exits 0 so Railway does not crash-loop.
  7. If only one of ADMIN_EMAIL or ADMIN_PASSWORD is set, startup fails closed and does not serve HTTP.

Manual in-container equivalent after the volume exists:

railway ssh -- node dist/seed-admin.js

Do not use railway run npm run seed-admin for production seeding. That command runs on your machine.

The production HTTP process will not start until that one user exists and the seed variables are gone.

Docker

docker build -t agentmemory-mcp-gateway .
docker run --rm -p 8080:8080 \
  -e PUBLIC_URL=http://127.0.0.1:8080 \
  -e BETTER_AUTH_SECRET=... \
  -e DATABASE_PATH=/data/oauth.sqlite \
  -e AGENTMEMORY_URL=http://127.0.0.1:3111 \
  -e AGENTMEMORY_SECRET=... \
  -v gateway-data:/data \
  agentmemory-mcp-gateway

The entrypoint starts as root, verifies DATABASE_PATH is an absolute file under /data (or RAILWAY_VOLUME_MOUNT_PATH), chowns only that directory plus the SQLite/WAL/SHM files, then drops to UID/GID 10001 before node runs. It never recursively chowns / or other parents. Mount a persistent volume at /data.

Railway

  1. Create a new service from this repository. Do not deploy onto the AgentMemory service.
  2. Use the Dockerfile / railway.json in the repo root.
  3. Attach a persistent volume mounted at /data. Railway mounts volumes as root and replaces the image /data directory.
  4. Set RAILWAY_RUN_UID=0 so the entrypoint can chown /data, then drop to UID 10001. Leaving the process as root is a tradeoff; this image does not keep root after startup.
  5. Set replicas to 1. A single SQLite volume cannot be shared safely.
  6. Set the environment variables above. Use the private AgentMemory URL, such as http://<agentmemory-service>.railway.internal:3111.
  7. Attach the public custom domain and set PUBLIC_URL to that exact https:// origin.
  8. Seed the administrator once with the in-container path above, then delete the temporary password variables.
  9. Confirm GET /healthz returns {"ok":true}.

Do not put AgentMemory on the public internet for this flow. The gateway is the only public MCP endpoint.

Connecting ChatGPT

  1. Deploy with a stable HTTPS origin and /mcp.
  2. In ChatGPT, add a remote MCP / connector URL: https://<your-domain>/mcp.
  3. Prefer CIMD if ChatGPT offers it. DCR remains enabled as a fallback.
  4. Complete the hosted sign-in and consent screens as the seeded user.
  5. Confirm memory_recall, memory_smart_search, and memory_save appear.

ChatGPT discovers /.well-known/oauth-protected-resource and the authorization-server metadata automatically.

Connecting Notion Custom Agents

  1. Enable custom MCP servers in the Notion workspace if required.
  2. Add a custom MCP server URL: https://<your-domain>/mcp.
  3. Notion uses OAuth and typically DCR unless a client is preregistered.
  4. Sign in as the seeded user and approve consent.
  5. Enable only the tools that agent should use.

Basic end-to-end verification

curl -sS https://<your-domain>/healthz
curl -sS https://<your-domain>/.well-known/oauth-authorization-server
curl -sS https://<your-domain>/.well-known/oauth-protected-resource
curl -sS -D- https://<your-domain>/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'

The /mcp call must return 401 with a WWW-Authenticate challenge that points at protected-resource metadata. After a real client login, tools/list must show only the allowlisted tools.

Revoking clients and tokens

SQLite is the source of truth for OAuth clients, refresh tokens, and consent.

  • Delete or rotate BETTER_AUTH_SECRET only if you intend to invalidate signing material and re-seed carefully.
  • Removing an oauthClient row, related tokens, and consent records revokes that client.
  • Replacing the SQLite file logs every client out.

There is no admin API. Use a one-off sqlite3 session against the volume if you need to revoke a specific client.

Backup and recovery

Copy /data/oauth.sqlite and the -wal/-shm files together while the service is stopped, or use sqlite3 .backup. A lost volume means every OAuth client must reconnect and the administrator must be seeded again. This backup is authentication state, not AgentMemory.

Known limitations

  • One replica only. Rate limits are in-memory.
  • No password reset. If the password is lost, restore SQLite from backup or delete the user table and seed again.
  • No dashboard and no multi-user support.
  • The MCP handler keeps official SDK legacy (2025) protocol support in stateless mode so ChatGPT and Notion are not rejected. The OAuth stack follows current Better Auth MCP APIs, including CIMD plus explicit DCR.
  • mTLS client authentication advertised by ChatGPT is terminated at the HTTPS edge, not verified inside this process.

Cloud agents

Cursor Cloud uses .cursor/environment.json:

  • Dockerfile — Ubuntu 24.04, Node 24 (nvm), npm, and agentfiles
  • install — refreshes agentfiles and runs npm ci when package-lock.json exists

Local development uses the same Node version via .nvmrc for the cloud image. The gateway runtime itself targets Node 22.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选