MCP Server
Estratos is a Model Context Protocol (MCP) server, so AI assistants can work with your workspace's memory. Use it now with Claude, Codex, or any MCP-compatible client. The Microsoft 365 Copilot Cowork package has passed real-tenant personal sideload, sign-in, and memory retrieval; installation remains owner/admin-gated while catalog rollout is pending.
- 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
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
Claude installs the Estratos Memory plugin from its marketplace. Codex and generic clients point at the raw endpoint. Copilot Cowork uses a Microsoft 365 app package whose confidential OAuth registration, tenant-ready build, personal sideload, sign-in, and memory retrieval have been proven in a licensed tenant. Other organizations still need an owner/admin deployment. The first available-client connection runs a one-time OAuth consent in your browser where you pick a workspace — no client ID or API key in the Claude or Codex config.
- Claude Desktop — add the marketplace repository
estratos-ai/pluginsunder Customise › Plugins › Add › Add marketplace › Add from a repository (Claude Desktop only syncs git-hosted marketplaces). See the Claude Desktop guide. - Claude Code —
/plugin marketplace add estratos-ai/pluginsthen/plugin install estratos-memory@estratos. The served marketplace athttps://estratos.ai/pluginsworks here too. - Codex —
codex mcp add estratos-memory --url https://estratos.ai/mcp, then approve the consent. See the Codex guide. - Copilot Cowork — real-tenant personal sideload, sign-in, and memory-tool verification passed; organization-catalog rollout remains owner/admin-gated. Public marketplace submission is also pending. See the Copilot Cowork guide.
- 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_idonarchive_memory, and only to archive. - 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 |
archive_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),layer_details— per-layerdisplay_name,description,naming_guidance,examples,depth, and effectiveallow_tagged_memories— and the workspace's unprompted-capture setting, reported twice.proactive_capture_modeisoff,propose_in_chat, orqueue_for_review: whether agents may offer to save memories the user did not ask for, and whether such a save waits in the review queue (owners and admins set it in Settings).proactive_captureis the older boolean, kept and derived —falseonly when the mode isoff— so an agent written against it keeps working. A brand-new workspace with no active hierarchy returns emptylayers/layer_detailsand nullversion/status(a success, not an error); both capture fields are present even then. - 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. - Behaviour: walks from the root to the target and returns applicable memories with explicit priority tiers (see how retrieval works). Higher priority replaces lower priority where blocks conflict; document order has no precedence meaning. Compact headers carry the descriptor, priority, and key; tagged headers also carry
memory_id. The requester's own pending content folds in for them only and is markedpending. - Returns: the concatenated Markdown bundle as text, beginning with a precedence preamble. Zero matches is a success whose text is the empty string, with no preamble.
- 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 forarchive_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;unprompted(optional boolean) — the assistant offered this save rather than the user asking for it. - Behaviour: 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. In a workspace set to
queue_for_review, anunpromptedwrite queues whatever the user's role, so a publisher's agent-initiated save also waits for an approver. - 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;unprompted(optional boolean) — as onremember. - Behaviour: 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, andvalue_created: truewhen the call minted a brand-newrestricted_tovalue (absent on reuse and on layer-wide writes; a queued proposal still mints the value immediately). - 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.
archive_memory
Archive a memory by its opaque id. Archiving is reversible — an approver can restore the memory from the workspace's Archived list, and only an Owner can permanently delete it there (from the web app; nothing is ever hard-deleted).
- Parameters:
memory_id(required) — the id returned bylist_memories. Not a path, key, or version. - Behaviour: for a publisher, writes an archive marker version and moves the memory to the Archived list (the version history is preserved). For a Basic user, queues an archive proposal — or, if they already have a pending archive request on that memory, calling it again withdraws it. Returns no memory content, ever.
- Returns: a
status(archived,queued_for_review, orwithdrawn) and the ids involved. - Errors: an unknown id, an id in another workspace, and an already-archived or 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 |
archive_memory |
Available to every role; a Basic user's archive requests 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/Admin; Admins are limited to Basic members and invitations |
audit:read |
Reserved — audit log access | Owner/Admin 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. An authorization request that binds the canonical /mcp resource but asks for a broader set is narrowed to this subset (RFC 6749 §3.3 — some clients request every scope the authorization-server metadata advertises); a request with no grantable 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 clients can use a refresh token to obtain a replacement without repeating consent. When a token can't be refreshed or is revoked, the client re-runs the consent flow.
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 source points at a read-only git repository this same deployment serves (/plugins/estratos-memory.git, fetchable with plain git clone, including --depth 1). The repository contains the plugin's plugin.json — the MCP server pointer at this deployment's /mcp endpoint — plus the four bundled skills: retrieval, recording, hierarchy, and naming. No client ID, secret, or API key goes in the manifest or the repository — it's the same OAuth auto-discovery described above, just installed as a plugin. See Connecting agents for the per-client install steps.
The same marketplace is mirrored to a public GitHub repository, estratos-ai/plugins, with the plugin bundled inside it — Claude Desktop only syncs marketplaces hosted on a git host, so that repository is the install path its UI accepts. The mirror is generated from the application and published automatically on every production release; the served manifest and the GitHub repository can never disagree about the plugin's contents, and the mirror is not hand-editable.
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