MCP Server
Estratos is a Model Context Protocol (MCP) server, so AI assistants can work with your workspace's memory. Use it with Claude, Codex, Copilot Cowork, or any MCP-compatible client.
- Endpoint:
POST https://estratos.ai/mcp - Auth: OAuth 2.1 Bearer tokens (or an API key), with the same scopes as the REST API
- Requires: the
api-accessentitlement on your workspace's plan
Just want to connect an agent? The Connecting agents section has step-by-step guides for Claude Desktop, Codex, Copilot Cowork, and generic MCP clients. This page is the protocol-level reference: the tools, scopes, and discovery documents.
Connecting
For most clients you install the Estratos Memory plugin from a marketplace; generic clients point at the raw endpoint. The first connection runs a one-time OAuth consent in your browser where you pick a workspace — no client ID or API key in any config.
- Claude Desktop / Claude Code — add the plugin marketplace at
https://estratos.ai/plugins. In Claude Code:/plugin marketplace add https://estratos.ai/pluginsthen/plugin install estratos-memory@estratos. - Codex, Copilot Cowork — add the same marketplace source from the client's plugin browser.
- Any other MCP client (Cursor, MCP Inspector, …) — give it the URL
https://estratos.ai/mcpand approve the consent. See OAuth discovery below for what happens automatically.
Connecting with an API key
For headless setups — scripts, CI, or clients without OAuth support — use an API key instead. Workspace owners create one under API Keys in the sidebar (grant it the memories:read, memories:write, and/or memories:delete scopes for the memory tools). Configure your client to send the key as a Bearer token:
{
"mcpServers": {
"estratos": {
"url": "https://estratos.ai/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
API keys are long-lived, so there's no refresh step — but they're a static secret. Treat them like any credential, and revoke them from the API Keys page if they leak.
The six memory tools
Estratos exposes exactly six memory tools. All six are always listed; each enforces its own OAuth scope when called, so a token missing the required scope gets a clear "insufficient scope" error (not a missing tool). Two rules hold across every tool:
- Paths address scopes, never individual memories. A memory body is only ever returned as part of a scope bundle. No tool returns a single memory by key, id, version, or a memory-suffixed path. The one per-memory handle any tool takes is the opaque
memory_idondelete_memory, and only to delete. - Identity comes from the token. The workspace is determined by your access token; you never pass a tenant, account, or workspace argument. Unknown arguments are rejected.
| Tool | Required scope |
|---|---|
describe_hierarchy |
memories:read |
retrieve |
memories:read |
list_memories |
memories:read |
remember |
memories:write |
remember_tagged |
memories:write |
delete_memory |
memories:delete |
Errors are returned as a JSON-RPC-shaped error object inside the tool result (with isError set). Two application codes appear: -32602 (invalid params) and -32002 (insufficient scope).
describe_hierarchy
Describe the active memory hierarchy for the connected workspace. Call this first, so an assistant learns the folder hierarchy before reading or writing.
- Parameters: none.
- Returns: the hierarchy
versionandstatus, the ordered array of layer slugs (layers), andlayer_details— per-layerdisplay_name,description,naming_guidance,examples,depth, and effectiveallow_tagged_memories. A brand-new workspace with no active hierarchy returns emptylayers/layer_detailsand nullversion/status(a success, not an error). - Scope:
memories:read.
retrieve
Retrieve every memory that applies to a scope path, as one Markdown bundle.
- Parameters:
path(string, required) — a dot-separated scope path such asroot.pennine-joinery.vat. Canonical or aliased; never a memory address. - Behavior: walks from the root to the target, interleaving scoped, layer-wide tagged, then value-targeted tagged memories in a fixed order (see how retrieval works). Each block carries a header comment with its kind, title, key, memory id, and version. The requester's own pending content folds in for them only.
- Returns: the concatenated Markdown bundle as text. Zero matches is a success whose text is the empty string.
- Errors: a path that doesn't resolve to a live scope folder — malformed, unknown, archived, or with a memory suffix — returns invalid params. There is no single-memory fallback.
- Scope:
memories:read.
list_memories
List the memory metadata at exactly one scope folder.
- Parameters:
path(string, required) — the scope path whose memories to list. - Returns: metadata only —
memory_id,title,key,kind,scope,version_number, and timestamps — for the scoped memories attached exactly at that folder. Plus the requester's own in-flight proposals at that folder (each withproposal_id,status, andaction). Never any memory content, and never ancestors, descendants, or tagged memories (those surface only in aretrievebundle). The returnedmemory_ids are management handles fordelete_memory. - Errors: the path must already exist (canonically or via an alias);
list_memoriesnever creates a folder, so an unknown path is invalid params. - Scope:
memories:read.
remember
Store a scoped memory at a folder scope path.
- Parameters:
path(required) — the scope path;title(required) — a concise sentence-case noun phrase the assistant generates from the content;content(required) — the memory body as Markdown. - Behavior: resolves the path against the active hierarchy, creating missing folders where the hierarchy allows, and derives a stable storage key from your title and scope (you never supply a key). Routes through the review workflow: a publisher's write publishes; a Basic user's write queues a proposal.
- Returns: the canonical path, the
status(publishedorqueued_for_review), the stablekey(a preview key for a queued proposal), thememory_id(published) orproposal_id(queued), and anyfolders_created. - Errors: a blank title or content, or an unresolvable path, returns invalid params.
- Scope:
memories:write.
remember_tagged
Store a tagged memory on a hierarchy layer, optionally restricted to one segment value.
- Parameters:
layer(required) — the layer slug (e.g.services);title(required);content(required);restricted_to(optional) — a segment value such asvatto target the memory; omit or leave empty for a layer-wide memory. - Behavior: resolves the layer against the active hierarchy. Rejects unknown layers, the structural root layer, and layers whose
allow_tagged_memoriesis false. A concreterestricted_toresolves-or-creates that value; blank means layer-wide. Same publish-vs-propose routing asremember. - Returns: a
scopedescriptor (servicesfor layer-wide,services=vatwhen value-targeted) plus the samestatus/key/id fields asremember. - Errors: an unknown layer returns invalid params listing the valid layer slugs; a layer that disallows tagged returns invalid params; blank title/content returns invalid params.
- Scope:
memories:write.
delete_memory
Soft-delete a memory by its opaque id.
- Parameters:
memory_id(required) — the id returned bylist_memories. Not a path, key, or version. - Behavior: for a publisher, writes a tombstone version and marks the memory deleted (the version history is preserved). For a Basic user, queues a delete proposal — or, if they already have a pending delete on that memory, calling it again withdraws their own pending delete. Returns no memory content, ever.
- Returns: a
status(deleted,queued_for_review, orwithdrawn) and the ids involved. - Errors: an unknown id, an id in another workspace, and an already-deleted id all return the same "memory not found" error, so the tool never leaks whether a memory exists.
- Scope:
memories:delete.
OAuth scopes
Tokens carry scopes that limit what they can do. The memory tools declare and enforce these per call; a token that acts through a user can never exceed that user's role.
| Scope | Grants | Notes |
|---|---|---|
memories:read |
describe_hierarchy, retrieve, list_memories |
Available to every role |
memories:write |
remember, remember_tagged |
Available to every role; a Basic user's writes route through review |
memories:delete |
delete_memory |
Available to every role; a Basic user's deletes route through review |
memories:export |
Memory exports | Available to every role |
hierarchy:admin |
Reserved — hierarchy management | Owner/Admin only |
members:admin |
Reserved — member management | Owner only |
audit:read |
Reserved — audit log access | Owner only |
profile:read |
Baseline identity for the REST API | See the API overview |
webhooks:manage |
Webhook endpoint management (REST) | See Webhooks |
The MCP resource advertises and grants only memories:read, memories:write, and memories:delete — the three scopes its six tools enforce. Its Protected Resource Metadata lists exactly these three, and an authorization request that binds the canonical /mcp resource but asks for any other scope is rejected with invalid_scope. The remaining scopes (memories:export, hierarchy:admin, members:admin, audit:read, profile:read, webhooks:manage) stay valid on the authorization server and REST API but are neither offered on the MCP consent screen nor grantable against /mcp. Granting a scope to a token whose user doesn't hold the matching role has no effect anyway — the role sets the ceiling — and a role downgrade takes effect immediately: a token carrying a scope its user can no longer justify is denied on every call.
Protocol details
- Endpoint:
POST https://estratos.ai/mcp - Protocol: JSON-RPC 2.0 over HTTP, stateless (no server-side sessions)
- Server name / version:
estratos/1.0.0 GET /mcpreturns405 Method Not Allowed(SSE streaming is not supported in stateless mode)DELETE /mcpreturns204 No Content(no sessions to clean up)- Rate limits are shared with the REST API: 60 requests/minute and 500 requests/hour per token
- A
401from/mcpincludesWWW-Authenticate: Bearer realm="Estratos", resource_metadata="…"per RFC 6750 — the entry point for discovery
OAuth discovery
Compliant clients discover everything from the endpoint alone. For client implementers, the pieces are:
- Protected Resource Metadata (RFC 9728):
/.well-known/oauth-protected-resource— declares the MCP endpoint a protected resource and names the authorization server, the three MCP scopes (memories:read,memories:write,memories:delete— a subset of the authorization server's full scope list), and this documentation. - Authorization Server Metadata (RFC 8414):
/.well-known/oauth-authorization-server— publishes the authorize, token, revoke, introspect, and registration endpoints; the supported response types (code), grant types (authorization_code,refresh_token,client_credentials), PKCE method (S256), and token endpoint auth methods (client_secret_basic,client_secret_post,none).
Dynamic client registration
POST /oauth/register (RFC 7591) lets a client register itself without a human creating an app first — registration is open and rate-limited. Send at least a client_name and redirect_uris; the response returns a client_id (and a client_secret for confidential clients). Registering grants no data access — the consent at /oauth/authorize remains the real gate.
PKCE and resource indicators
For dynamically registered (public MCP) clients, two requirements are enforced at authorization time:
- PKCE (RFC 7636) is mandatory, with
code_challenge_method=S256. - Resource indicators (RFC 8707): the
resourceparameter is required and must equal the canonical MCP URL (https://estratos.ai/mcp). It's validated on both the authorize and token requests, and the token is bound to that resource, so it can't be replayed against another audience.
Access tokens expire after two hours and refresh automatically; refresh tokens last 30 days. When a token can't be refreshed (or is revoked), the client re-runs the consent.
Plugin marketplace manifest
Estratos publishes a Claude-compatible plugin marketplace manifest so clients can install Estratos Memory as a plugin rather than adding the MCP server by hand:
GET /plugins— the marketplace URL the Configure agents cards displayGET /.claude-plugin/marketplace.json— the well-known path some tooling fetches directly
Both serve identical content: a marketplace entry for the estratos-memory plugin whose only payload is an MCP server pointer at this deployment's /mcp endpoint. No client ID, secret, or API key goes in the manifest — it's the same OAuth auto-discovery described above, just installed as a plugin. See Connecting agents for the per-client install steps.
Next steps
- Connecting agents — install guides for each client
- Using memories — scoped vs. tagged, retrieval, review, versions, exports
- API Overview — the REST surface, shared error format, and rate limits