A memory your assistants keep between conversations.
- id
- 9d3a1f0e-7c2b-4e5a-9a8f-2b6c1d4e7f10
- title
- Postgres pool size for the shared PgBouncer
- content
- Keep DB_POOL_MAX at 2. The shared PgBouncer allows two backend connections for this service; raising it drops connections under load.
- type
- decision
- tags
- postgrespgbouncerdecision
- importance
- 80 / 100
- created
- 2026-09-10 14:02 UTC
- index
- 1 chunk · 1,536-dim vector
memory_search “how many db connections can we use?” → this record, score 0.81Three ways in, one URL.
Every client gets the same endpoint. The first use opens a sign-in page; after that, the assistant calls the tools on its own.
- 1Open Settings → Apps & Connectors → Advanced settings and turn on Developer mode.
- 2Back in Apps & Connectors choose Create. Name it Memory, paste the endpoint as the MCP server URL, and pick OAuth for authentication.
- 3In a chat open the + menu, then More, and switch Memory on. ChatGPT sends you to the sign-in page the first time.
- 4Say something worth keeping: “Remember that…”. ChatGPT calls
memory_store; later questions callmemory_search.
- 1Open Settings → Connectors and choose Add custom connector. On Team and Enterprise an owner adds it for everyone.
- 2Name it Memory, paste the endpoint as the remote MCP server URL, and add it.
- 3Choose Connect and finish the sign-in. In a chat, open the search-and-tools menu and turn Memory on.
- 4Ask Claude to remember or recall things. It picks the right tool and asks you before deleting anything.
claude mcp add --transport http memory \
https://mcp.datahashira.com/mcp
claude # then run /mcp and pick Memory to sign inClients that read a JSON config, such as Cursor or Windsurf, take the same URL:
{ "mcpServers": {
"memory": { "url": "https://mcp.datahashira.com/mcp" }
} }The connector publishes its OAuth metadata at /.well-known/oauth-protected-resource/mcp, so a compliant client discovers the sign-in server on its own.
Once per client, with your username and password.
Your client registers itself, sends you to the connector's own sign-in page, and receives a short-lived token. Google or per-user accounts can be added later without any client changes.
memory:read lets the assistant search and fetch. memory:write lets it store, update and delete.What your assistant can do with it.
Read tools need memory:read; write tools need memory:write. Green parameters are required; the rest show their defaults.
Find memories by meaning and by keywords, narrowed by namespace, tag, type, source or date. Returns nothing rather than a guess when confidence is low.
Hybrid ranking: vector similarity plus trigram keyword match, one result per memory.
Save a fact, decision, preference, note, snippet or document. Long content is split and indexed automatically; identical content returns the existing ID instead of a duplicate.
Up to 100 KB of text per memory. Returns the stable memory ID.
Fetch one complete memory by its stable ID, usually after a search or list has identified it.
Returns the full canonical text, metadata, tags and timestamps.
Browse the newest memories with the same filters as search, without a query and without touching the embedding model.
Newest first.
Change the title, content, type, tags, importance, source or metadata while keeping the same ID. Changed text is re-indexed automatically.
expected_updated_at refuses the write if someone else changed the memory first.
The one fetch at the start of a conversation. Returns the operating instructions, counts per namespace, and the full text of the most important, topic-related and recent memories within a character budget; with lesson it adds exactly one pending lesson and leaves the others out.
Whole memories that don't fit the budget are listed under omitted. Compact excerpts are always included as an index.
Remove a memory so it disappears from every read and search immediately. Runs only with confirm=true, which the assistant sets after you say yes.
Soft delete: an administrator can still recover the record from the database.
Turns a namespace's material into ordered, self-contained lessons using the backend planning model: weak items first, plus a few new items in each lesson. Every lesson is stored as a memory with a number and a status. From then on memory_briefing with lesson: "next" serves one lesson per conversation.
Pending lessons block a new plan unless replace_existing is set. Once every lesson is done, run it again: the finished lessons are archived and the next set starts from the updated material.
Applies a session export JSON written at the end of a chat that had no tool access: stores new memories, updates existing ones by id (a lesson by namespace and lesson_number), and deletes listed ids only with confirm_deletes=true after you say yes.
Up to 100 stores, 100 updates and 50 deletes per call; every item reports stored, duplicate, updated, deleted, not found, skipped or error.
Plain requests, the right tool.
You never name a tool. Copy a prompt, paste it into a chat with Memory switched on, and the assistant picks the tool for you.
“Remember that our deploy window is Tuesdays at 10:00 IST.”
“What did we decide about the Postgres pool size?”
“Show my latest notes tagged onboarding.”
“Make that memory importance 90 and add the tag infra.”
“Forget the note about the old API key.”
“Split my Vietnamese material into eight lessons.”
“Load my briefing with the next lesson.”
“Give me the session JSON.”
“Import this session JSON: { … }”
memory_curriculum again: it archives the finished lessons and plans the next set, weak items first plus a few new ones.One lesson per conversation, in four prompts.
The memory is fetched once at the start of a conversation and written once at the end; nothing is called in between, so a voice chat works exactly like a text chat. Material is split into lessons on the backend, and each conversation gets one lesson. The Vietnamese lessons are the worked example; swap the namespace, topic and goal for any subject.
Call memory_curriculum with namespace "vietnamese_learning", lesson_count 8 and goal "conversational Vietnamese with a Hanoi accent, fast pace". Then list the lesson titles it created.Start by calling memory_briefing with namespaces ["vietnamese_learning"], lesson "next", topic "what to review next" and limit 20. That is the only tool call in this conversation: everything you need is in its memories field, and the lesson field is today's lesson. Follow the instructions field for the whole conversation.
Teach only that lesson: begin with a short unprompted diagnostic on its weak and new items, then one word -> meaning -> pronunciation -> sentence -> drill -> recombination, Northern/Hanoi accent. Tell me which lesson number this is out of how many.
Keep a private running list of what I get right unprompted, what I get wrong or needed a prompt for, and anything new we cover. When every item of the lesson is done, say "Lesson N is complete" and tell me to start a new chat for the next one.Give me the session JSON: exactly one JSON code block in the Session Export format (version 1). Include an "update" for today's lesson by its "namespace" and "lesson_number" (no id) with metadata {"status": "done", "completed_at": ..., "result": ...}, an "update" for the mastery baseline with its complete new text, and a "store" entry of type "lesson_log" for today with date, items covered and my scores. Nothing else inside the block.Import this session JSON with memory_import (no deletions unless I confirm):
<paste the block>memory_update and memory_store instead of producing the JSON. When memory_briefing reports no pending lesson, run the curriculum prompt again: the finished lessons are archived, and the next set reviews your weak items and adds a few new ones. To steer what comes next, store the new words or topics in the namespace first.How a memory is kept.
Canonical text is stored once in PostgreSQL under your account and a namespace, so work can stay apart from personal, or one project from another.
Long entries are split by headings and paragraphs. Each piece gets a 1,536-dimension embedding in pgvector. Search combines vector similarity with trigram keyword matching, applies your filters, then collapses the results to one per memory. Anything under a confidence score of 0.35 is dropped rather than returned as a guess.
Identical content is caught by hash and returns the existing ID. Deleting is soft: the record leaves every read and search at once, but nothing is destroyed without an administrator. Vectors are derived data and can be rebuilt from the text at any time.
The service is stateless between calls, runs behind HTTPS only, and filters every query by both tenant and user. No tool accepts a user or tenant ID as an argument; both come from the verified token.
| Text per memory | 100 KB |
| Metadata per memory | 20 KB JSON |
| Tags per memory | 50 |
| Search results | ≤ 25 |
| List page | ≤ 100 |
| Minimum search score | 0.35 |
| Requests per client address | 120 / min |
| Request body | 750 KB |
| Embedding model | text-embedding-3-small |
Endpoints and the wire contract.
A stateless MCP Streamable HTTP server. Every POST carries its own bearer token; there are no session IDs to keep.
| Route | Purpose | Auth |
|---|---|---|
POST /mcp | MCP JSON-RPC over Streamable HTTP. GET and DELETE return 405. | Bearer token |
GET /.well-known/oauth-protected-resource/mcp | Protected-resource metadata: resource URL, authorization server, scopes. | None |
GET /health/live | Process liveness. | None |
GET /health/ready | Readiness; checks the database connection. | None |
/authorize, /token, /register, /revoke | Built-in OAuth 2.1 authorization server: dynamic client registration, PKCE S256, refresh-token rotation. Metadata at /.well-known/oauth-authorization-server, keys at /.well-known/jwks.json. | Per endpoint |
GET /docs | The connector's built-in docs page. | None |
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
resource_metadata="https://mcp.datahashira.com/.well-known/oauth-protected-resource/mcp",
scope="memory:read memory:write"ES256, issued by the connector's own authorization server, with aud equal to https://mcp.datahashira.com/mcp, a stable sub, and the scopes above in scope or permissions. A call with a valid token but a missing scope returns an insufficient_scope challenge inside the tool result, so the client can ask for more access rather than fail silently.
Source, deployment notes and the full tool schemas live in the GitHub repository.
Everything the owner needs to do.
The service runs on ECS in ap-south-1 behind the shared nginx. Secrets live in SSM under /mcp-memory/; the deploy key in .env.production can read and write them.
Follow the ChatGPT or Claude steps above with https://mcp.datahashira.com/mcp. Sign in with the account stored in SSM (MCP_LOGIN_USERNAME and MCP_LOGIN_PASSWORD). Nothing else needs configuring on the client side.
Registers a throwaway client, opens the sign-in page, exchanges the code with PKCE and calls the live endpoint expecting nine tools.
npm run check:oauthWrite the new value, then restart the task so it is read again; about a minute of downtime.
aws ssm put-parameter --name /mcp-memory/MCP_LOGIN_PASSWORD --type SecureString --overwrite --value 'NEW-PASSWORD'
aws ecs update-service --cluster production-cluster --service mcp-memory --force-new-deploymentEvery access token stops working within an hour; clients refresh on their own.
openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt > key.pem
aws ssm put-parameter --name /mcp-memory/OAUTH_SIGNING_KEY --type SecureString --overwrite --value "$(cat key.pem)" && rm key.pem
aws ecs update-service --cluster production-cluster --service mcp-memory --force-new-deploymentStill pending: the current one was shared in chat.
aws ssm put-parameter --name /mcp-memory/OPENAI_API_KEY --type SecureString --overwrite --value 'sk-proj-...'
aws ecs update-service --cluster production-cluster --service mcp-memory --force-new-deploymentMerge to main. GitHub Actions runs the checks, builds, pushes, runs migrations, rolls the service and streams the CloudWatch log in the run until the rollout is stable. Redeploy without a change from the Actions tab (Run workflow). apps/connector/deploy/deploy.sh is the local fallback.
gh run watch # or open the Actions tabTail the service log, or ask the readiness probe directly.
aws logs tail /ecs/mcp-memory --follow
curl https://mcp.datahashira.com/health/readyReplace the single-account check and the login page in apps/connector/src/modules/oauth/provider.ts; clients and the protocol layer stay as they are.
ap-south-1, cluster production-cluster, service mcp-memory, host port 4001. nginx and TLS are owned by DevOps; ask them for any change there.