agentmemory-mcp-gateway
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@agentmemory-mcp-gatewaysearch my memory for notes about the Q3 launch plan"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
/mcpActs as the OAuth authorization server and protected resource
Allows exactly one pre-seeded human to sign in and grant consent
Forwards allowlisted
tools/listandtools/calltraffic to AgentMemory RESTFails 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/mcpRelated MCP server: Remote MCP Server
Architecture
MCP client
-> HTTPS gateway (this service)
-> private AgentMemory REST APITrust 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_URLorAGENTMEMORY_SECRET.Incoming
Authorizationheaders are used only to validate the client access token. The gateway always builds a newAuthorization: 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/toolsPOST /agentmemory/mcp/callwith{ "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.
Hosted consent CSRF
GET /consent requires an authenticated session and renders a short-lived HMAC token into both the Allow and Deny forms. The token is bound to the session ID, the exact raw signed OAuth query, an expiry, and a consent-CSRF domain marker.
POST /consent accepts nothing else as proof. A missing, malformed, expired, forged, wrong-session, or wrong-query token returns 403 {"error":"Request denied"} and logs only a coarse reason such as csrf_expired. Browser Origin, Referer, and Fetch Metadata headers are neither trusted nor required, because hosted authentication windows (Safari in particular) send Origin: null and cross-site Fetch Metadata for a legitimate same-page form post.
After the token verifies, the gateway forwards the submission to Better Auth's /oauth2/consent with Origin pinned to PUBLIC_URL. Better Auth's own origin, CSRF, and signed-OAuth-query checks stay enabled, so a direct call to /oauth2/consent is still rejected.
Environment
Variable | Required | Purpose |
| yes | Canonical public origin. No path, query, fragment, or credentials. HTTPS except loopback. |
| yes | Better Auth signing/encryption secret, 32+ characters |
| yes | SQLite file path, for example |
| yes | Private AgentMemory origin |
| yes | Backend bearer for AgentMemory, 32+ characters |
| no | Default |
| no | Listen port. Railway sets this. Default |
| seed only | Administrator email |
| 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 devUseful checks:
npm run format
npm run lint
npm run typecheck
npm test
npm run buildSecure 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 .envProduction image / Railway
The image includes dist/seed-admin.js and starts with node dist/start.js.
Generate a long random password in 1Password. Do not store it in git, SQLite, Docker, or logs.
Set temporary
ADMIN_EMAILandADMIN_PASSWORD(20+ characters) on the service.Deploy or restart so the container runs with
/datamounted.With those variables set,
node dist/start.jsrunsnode dist/seed-admin.jsin-process, prints the durable user ID, and exits0without opening the HTTP port.Remove
ADMIN_PASSWORDandADMIN_EMAIL, then restart. The process then serves HTTP.If both variables are still set after a user exists, startup logs that they must be removed and exits
0so Railway does not crash-loop.If only one of
ADMIN_EMAILorADMIN_PASSWORDis set, startup fails closed and does not serve HTTP.
Manual in-container equivalent after the volume exists:
railway ssh -- node dist/seed-admin.jsDo 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-gatewayThe 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
Create a new service from this repository. Do not deploy onto the AgentMemory service.
Use the Dockerfile /
railway.jsonin the repo root.Attach a persistent volume mounted at
/data. Railway mounts volumes as root and replaces the image/datadirectory.Set
RAILWAY_RUN_UID=0so the entrypoint canchown/data, then drop to UID10001. Leaving the process as root is a tradeoff; this image does not keep root after startup.Set replicas to 1. A single SQLite volume cannot be shared safely.
Set the environment variables above. Use the private AgentMemory URL, such as
http://<agentmemory-service>.railway.internal:3111.Attach the public custom domain and set
PUBLIC_URLto that exacthttps://origin.Seed the administrator once with the in-container path above, then delete the temporary password variables.
Confirm
GET /healthzreturns{"ok":true}.
Do not put AgentMemory on the public internet for this flow. The gateway is the only public MCP endpoint.
Connecting ChatGPT
Deploy with a stable HTTPS origin and
/mcp.In ChatGPT, add a remote MCP / connector URL:
https://<your-domain>/mcp.Prefer CIMD if ChatGPT offers it. DCR remains enabled as a fallback.
Complete the hosted sign-in and consent screens as the seeded user.
Confirm
memory_recall,memory_smart_search, andmemory_saveappear.
ChatGPT discovers /.well-known/oauth-protected-resource and the authorization-server metadata automatically.
Connecting Notion Custom Agents
Enable custom MCP servers in the Notion workspace if required.
Add a custom MCP server URL:
https://<your-domain>/mcp.Notion uses OAuth and typically DCR unless a client is preregistered.
Sign in as the seeded user and approve consent.
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.
MCP diagnostic logs
Railway shows HTTP status codes only, and MCP answers most protocol failures with HTTP 200 and a JSON-RPC error inside the body. Every /mcp exchange therefore writes one-line JSON to stdout, or stderr when it failed:
{"log":"mcp","ts":"...","event":"mcp.request","httpMethod":"POST","userAgent":"openai-mcp/1.0.0","envelope":"request","mcpMethod":"tools/list","rpcId":"2"}
{"log":"mcp","ts":"...","event":"agentmemory.list_tools.succeeded","upstreamStatus":200,"durationMs":2,"toolCount":3,"toolNames":["memory_recall","memory_smart_search","memory_save"]}
{"log":"mcp","ts":"...","event":"mcp.response","httpMethod":"POST","userAgent":"openai-mcp/1.0.0","envelope":"request","mcpMethod":"tools/list","rpcId":"2","httpStatus":200,"durationMs":4,"toolCount":3,"toolNames":["memory_recall","memory_smart_search","memory_save"]}Filter Railway logs on "log":"mcp". Useful events:
Event | Meaning |
| An exchange started: MCP method, JSON-RPC id, envelope kind, agent |
| It finished: HTTP status, duration, and safe per-method detail |
| It threw instead of answering |
| The MCP SDK reported an out-of-band or protocol error |
| The |
| The |
| Upstream AgentMemory call, with its HTTP status |
mcp.response adds the negotiated protocolVersion and advertised capabilities for initialize, toolCount plus toolNames for tools/list, and rpcErrorCode plus rpcErrorMessage whenever the body carries a JSON-RPC error. A -32601 Method not found line names the exact MCP method a client wanted but this gateway does not implement.
src/mcp-log.ts is the only writer. It accepts strings, numbers, booleans, and string arrays; anything else logs as [unsupported]. Field names that could carry a credential log as [redacted], values are run through the same redaction as safeLog, and long values are truncated. Authorization headers, tokens, cookies, OAuth codes, tool arguments, tool results, memory contents, and request or response bodies are never logged.
Revoking clients and tokens
SQLite is the source of truth for OAuth clients, refresh tokens, and consent.
Delete or rotate
BETTER_AUTH_SECRETonly if you intend to invalidate signing material and re-seed carefully.Removing an
oauthClientrow, 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) with npm 11, and agentfiles
install — refreshes agentfiles and runs
npm ciwhenpackage-lock.jsonexists
Local development, CI, Docker, and the gateway runtime all use Node 24 and npm 11. .nvmrc is 24.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Persistent memory for AI agents with OAuth-backed hosted MCP access.
The Remote MCP server acts as a standardized bridge between LLM applications (like Claude, ChatGPT, and Cursor) and external services, enabling AI agents to access external tools and resources. Its primary capability is providing a centralized search tool to discover other MCP servers and their respective tools. Unlike local implementations, it runs remotely with OAuth authentication and permission controls for security.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables remote access to the MemPalace MCP server via HTTP, supporting bearer token authentication and concurrent clients while exposing all mempalace tools.-
- FlicenseNot gradedqualityDmaintenanceEnables running MCP tools remotely on Cloudflare Workers with OAuth login. Supports tool calls like math operations through MCP clients.-
- FlicenseNot gradedqualityDmaintenanceRemote MCP server with built-in OAuth authentication via Cloudflare Access, enabling secure tool invocation after user sign-in.-
- FlicenseNot gradedqualityCmaintenanceEnables running MCP tools remotely on Cloudflare Workers with OAuth authentication, allowing clients like Claude to call tools via SSE.-
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/martindzejky/agentmemory-mcp-gateway'
If you have feedback or need assistance with the MCP directory API, please join our Discord server