--- title: "Use past.dev with your product's users" description: "Enable the end-user MCP server on a project. Your users reach that project's memory from their own assistant, signed in at your identity provider." canonical: https://past.dev/docs/mcp/serving-your-own-users last-updated: 2026-10-09 --- # Use past.dev with your product's users > Enable the end-user MCP server on a project. Your users reach that project's memory from their own assistant, signed in at your identity provider. Product: past.dev MCP Server. Source: https://past.dev/docs/mcp/serving-your-own-users A project can offer its own MCP server to **your** end users. A person signs in at the identity provider you already run, and every tool call answers as the identity they resolved to: project-visible memory plus the audiences that identity is in, and nothing else. past.dev is the authorization server and never a place to sign in. Your users never see a past.dev account, and past.dev never holds a password for them. You register one OAuth application at your provider and paste its credentials into the console. ### The address Enabling the server for a project sets its **handle**: the organization slug and the project slug, joined by a dash. There is nothing to type. Once enabled the handle never changes, even if the project is renamed, because your users have already installed it. - **Server endpoint**: `https://api.past.dev/mcp/` - **Example**: `https://api.past.dev/mcp/acme-support` - **Transport**: `Streamable HTTP` - **Auth**: `OAuth 2.1 · PKCE · your identity provider` The address is the canonical resource URI: the `resource` parameter clients send, the audience of every token, and the issuer of the authorization server. A request under an unknown handle is not found. ### Turning it on 1. Open **Build › MCP server** on the project in the console. 2. Connect an identity provider for the project, and paste the credentials of the OAuth application you registered there. 3. Press **Enable MCP server**. The handle and the address appear, with a *Copy* button and the install snippet for each client. 4. Decide who may use it: everyone the provider signs in, or only identities matching a rule over their traits. 5. Decide whether `remember` is offered, with the project's **write toggle**. ### Which identity providers Five presets are available to every project. A preset fills in the scopes, the join key and the claim mapping. You supply the OIDC application you created at the provider: its issuer, its client id and its client secret. The secret is stored encrypted and is never shown again. | Provider | Groups, and directory sync | | --- | --- | | **Microsoft Entra ID** | Groups arrive in the ID token as object IDs. Directory sync available. | | **Okta** | Groups arrive in the ID token. Directory sync available. | | **Google Workspace** | Google never puts groups in an ID token. A rule over groups here reads them from the directory sync, which makes the sync the way to get them. | | **JumpCloud** | Groups arrive in the ID token, under the attribute name you give the application. Directory sync available. | | **OneLogin** | Groups arrive in the ID token. No directory sync, so a rule can name only the groups the token carries. | **Auth0**, **Clerk**, **Amazon Cognito** and any OpenID Connect provider that publishes a discovery document are opened per organization on request. Those four default to the `sub` claim as the join key, where the five above default to `email`. Ask your past.dev contact to open one. The join key is the claim whose value becomes the identity's `user_id`, so it is the claim that decides when two sign-ins are the same person. A connection also decides what happens to a person the directory has not seen: create an identity for them, which is the default, or refuse them. ### Directory sync Sign-in brings identities in one at a time, at the moment each person first arrives. Directory sync brings every user and group in ahead of any sign-in, so an audience is populated before its people have connected anything. It runs through Nango, every 48 hours, with a **Sync now** button on the connection for the times you want it sooner. The sync writes the same traits a sign-in writes, under its own provenance, and marks a user the provider suspended or deactivated. The connection row reports the last run: its time, the user and group counts, what changed, and when the next run is due. > **One connection per project** > > A project binds to one provider, which is what sends a person straight to their sign-in page with no chooser in between. > > Turning a connection off refuses sign-ins at once and stops its tokens from refreshing. Everything it wrote stays. ### The tools | Tool | Kind | What it does | | --- | --- | --- | | `recall` | read | `query`, optional `limit`. The evidence this identity may see, with its sources. The same result `POST /api/v1/recall` gives for that identity. Scope `memory:read`. | | `answer` | agent | `question`. A grounded answer over the same evidence, with its citations, or an abstention when the evidence does not support one. Scope `memory:read`. | | `remember` | write | `content`, optional `label`. Ingests a data point scoped to the identity's own private audience, so only this identity recalls it. Offered while the project's write toggle is on. Scope `memory:write`. | | `who_am_i` | read | The identity this session resolved to, its traits, the audiences it reaches, and the connection, link and client the session came through. Scope `identity:read`. | The server advertises `memory:read` and `identity:read`, plus `memory:write` while the write toggle is on. Consent lists the tools those scopes reach, and the token carries the consented set. Turning the write toggle off refuses `remember` on existing tokens with `insufficient_scope`, and the tool leaves the client on its next tool listing. > **What a user remembers is theirs** > > `remember` writes through the ordinary ingest path, with the audience set to the identity's own private audience. The request log, the pipeline and retention all see an ordinary data point. It is never widened to the project. ### Signing in A project has exactly one login connection, so the authorization request redirects straight to it. There is no chooser. Two pages are past.dev's own, rendered outside the console and carrying your project's display name: - **Consent**: who the person resolved to, the client's name and redirect host, and the tools it will get, as a plain list. It is remembered per client and identity for as long as the session lives, so a second install of the same client skips it. - **Refused**: one sentence for the reason: the server is off, no login connection is enabled, the connection refuses people it has not seen, or the identity does not match the access rule. The page never says whether an id exists, and never says which trait fell short. A person the directory has not seen gets an identity at first sign-in. Signing in refreshes that identity's traits through the connection's claim mapping, so a group change at your provider changes access at the person's next sign-in with nobody touching the console. ### Deciding who may connect Access is a **rule over traits** on the connection. One rule decides who may connect, so there is no switch to set per person. The default admits everyone your provider signs in. The alternative matches conditions of the form ` `, in the same grammar as an audience rule, so `idp_group is one of [support, leadership]` reads the same in both places. The rule is evaluated at sign-in, at every refresh and on every tool call, against the identity's current traits. An identity that stops matching is refused at its next sign-in, its refresh is refused, and its access token runs out within the hour. ### Agents and services: access links An agent, a bot or a service has no browser and no account at your provider. For those, the console mints an **access link**: one address that is one identity. ``` https://api.past.dev/mcp//link/ ``` The client installs the link as its MCP server URL and nothing else. past.dev answers it with no challenge, because the secret in the path is the credential, and every call runs as that identity with its audiences. The four tools work over a link exactly as over a token, and `who_am_i` names the link. > **A link travels in configuration and logs** > > That is the trade the one-line install makes. Mint one link per install, so revoking one agent leaves the others running. Revocation takes effect on the link's next call. The access rule does not filter links, so a leaked link is revoked to end it. ### Or call the Memory API yourself If your product already has its own MCP server, add tools to it that call the Memory API. Your server authenticates the user, picks the project key, and passes the user's identity to `/recall`. ```bash # Inside your own MCP tool handler, after you have authenticated the user. # The key names the project; there is no projectId field. curl -X POST https://api.past.dev/api/v1/recall \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "what is the Acme pilot budget?", "identity": "demo-user" }' ``` > **Keep the key on your server** > > A past.dev project key reaches everything in its project. Keep it on your server. Never send it to an MCP client, a browser or a user device. > > Recall requires an `identity` and returns project-visible memory plus memory visible to that identity. When you call the API yourself, that mapping is the security boundary and it stays in your code. The end-user server above is the version where past.dev holds that boundary instead. **Memory API endpoints.** The Memory API reference documents the request and response fields for ingestion and recall. Memory API reference: /docs/memory-api/api-reference