Mnemosyne
by flyset
README.md
# MyMCP
MyMCP is the repository and Python host package for an experimental local MCP
server. Released `0.2.0` established the host, application, package, and official
tracked client as MyMCP/`mymcp`; `0.2.1` remains the Ollama schema-compatibility
build and `0.3.0` the host-configuration foundation. The current
MyMCP/package/server version is `0.10.1`, the llama.cpp grammar-safe Tool-schema
compatibility build. It hosts Mnemosyne, the user-governed AI memory domain, through a
bundled plugin.
Its intended direction is a local, client-neutral MCP host and governance gateway
that composes narrowly scoped integrations behind one machine-local endpoint.
Mnemosyne supplies the current notebook-like memory capability while retaining
its explicit user-governed boundaries.
## Current Status
This repository currently contains a FastAPI-based MCP host with the Mnemosyne
memory domain built in. Phase 1 provides kind-qualified contracts,
`ActivatedTool`/`PluginContribution` composition, trusted internal effect and
consent metadata, host-owned public bindings and `list_tools`, an immutable
`HostRuntime` with an opaque generation, explicit `build_production_runtime`,
and runtime-bound MCP dispatch through `create_app(runtime)`.
Phase 2's declaration/parity boundary and vertical Mnemosyne extraction are
implemented. The bundled plugin owns its inert manifest, trusted adapter,
configuration, memory domain, and `memory_*` MCP adapters under
`mymcp/plugins/mnemosyne/`. Manifest-v1 parsing remains strict (JSON/UTF-8,
duplicate-key, and 64 KiB bound), and bootstrap validates exact
manifest/definition/contribution parity before generation construction.
The plugin's trusted in-process Mnemosyne `0.3.0` adapter supplies canonical
registrations. Every capability declares its own contract version: `memory_recall`,
`memory_list`, `memory_remember`, and `memory_revise` are `1.2.0`; the other four
`memory_*` capabilities are `1.1.0`. The MyMCP
host/package/server is independently `0.10.1`. A test-owned,
version-keyed canonical Tool-definition digest ledger retains readable
properties/required-field fingerprints and historical entries, so definition
drift cannot silently reuse a capability version. The ledger covers declared Tool
definitions only; behavioral/version impact remains review-gated. Host
configuration supplies one immutable startup snapshot before bootstrap; bootstrap
validates it before reading the fixed packaged resource, then validates that
resource against the plugin definition and selected gate-controlled contribution.
`create_production_app()` is the local convenience factory and supported Uvicorn
factory target. Ordinary imports are side-effect free; no global app,
startup/methods modules, static `ToolIntegration`, or dynamic discovery are used.
The `0.2.1` compatibility build removes redundant nonblank regular-expression
constraints from `memory_recall`, `memory_remember`, and `memory_revise` Tool
schemas because Ollama's llama.cpp grammar converter rejects the `\s`/`\S`
constructs even when fully anchored. Existing server validation still accepts
multi-word and multiline values containing non-whitespace and rejects
whitespace-only values. Tool names, fields, bounds, results, mutation gates,
consent, and storage behavior are unchanged.
The `0.10.1` compatibility build (TRACK_050) makes the published Mnemosyne Tool
schemas convertible by llama.cpp grammar-constrained tool calling:
`memory_remember` and `memory_revise` stop publishing the `content` `maxLength:
4000`, `memory_remember`'s `occurred_at` pattern uses `[0-9]` instead of the `\d`
shorthand, and `memory_list` publishes a flat schema without the top-level `oneOf`
combinator and without the `cursor` `maxLength: 4096`. Server-side length and
timestamp enforcement is unchanged and authoritative. Tool names, fields,
results, mutation gates, consent, and storage behavior are unchanged; the three
Tools' capability contracts advance to `1.2.0`.
TRACK_034 delivered the MyMCP `0.2.0` public-host cutover: server/application
identity, package metadata, the canonical repository, and the tracked OpenCode
policy use MyMCP/`mymcp`. The prior repository URL redirects to the canonical
repository. Routes and protocol remain unchanged. Mnemosyne permanently retains plugin
`mnemosyne`, `memory_*` Tools, `MNEMOSYNE_*` configuration, `~/.mnemosyne`, and
its storage, record, logging, and consent semantics. Repository/origin/history/
tag/placeholder checks, the tracked and ignored OpenCode migration with Claude
exclusion, the normal endpoint and final client reconnect, and isolated
approved-once and rejected/no-Tools-call checks are complete. The public,
non-draft, non-prerelease GitHub release
[`MyMCP 0.2.0: public-host cutover`](https://github.com/flyset/MyMCP/releases/tag/mymcp-v0.2.0)
is tagged `mymcp-v0.2.0` at `c2852bc` and contains one wheel,
`mymcp-0.2.0-py3-none-any.whl`, with matching GitHub/local SHA-256
`531cc9a603d16399b12650fd09d3bc76f43b4d5d1b15fed0377a6197c820e3e7`.
Host configuration schemas 1–7 provide XDG-based process settings and ordered
external-plugin declarations. Schema 1 remains exact compatibility and rejects
enabled declarations with `enabled_plugin_unsupported`; schema 2 supports
validated startup composition. Schema 3 adds required explicit anonymous intent
and ordered Authentication adapter declarations. Schema 4 adds the non-secret
operator-bearer verifier source selector. Schema 5 adds one bounded OAuth issuer
intent and integrates the `oauth-jwt-jwks-v1` production Authentication method.
The two production Bearer methods are mutually exclusive per process; token shape
never selects an adapter. OAuth composition acquires one immutable validation
snapshot at startup and requires anonymous access disabled. It establishes only a
normalized identity through unchanged Authentication contract version 1.
When OAuth is enabled, MyMCP conditionally serves RFC 9728 protected-resource
metadata at `/.well-known/oauth-protected-resource/mcp` and sends its exact
Bearer metadata challenge on failed `/mcp` authentication. The loopback HTTP
resource is an explicit local interoperability exception, not remote or TLS-ready
OAuth deployment.
MyMCP `0.10.0` introduced bounded process-local MCP sessions for registered
principals under Streamable HTTP revision `2025-11-25`. Each `/mcp` request is
authenticated before session validation. A successful registered, response-bearing
`initialize` offering that revision returns one opaque `MCP-Session-Id`; later
registered POST and GET requests require that header and exactly one
`MCP-Protocol-Version: 2025-11-25`. Schema 6 can explicitly set
`[mcp].strict_protocol_version = false` to permit only an absent protocol header
on an already authenticated, validated registered session; it resolves from that
session's immutable negotiated version. Supplied invalid or conflicting headers
and all anonymous traffic remain strict. Sessions bind the complete normalized
principal and runtime generation, expire after configurable inactivity or
absolute limits (30 minutes and eight hours by default), disappear at restart, and can be terminated with an
authenticated `DELETE /mcp`. Session failures are body-free 400, 404, or 503
responses. Schema 7 accepts required native integer lifetime keys from `0`
through `2,592,000` seconds; zero disables that limit, and both may be disabled.
The settings are startup-fixed and require restart. Anonymous access remains explicitly configured and stateless; after
initialization, its POST and GET traffic carries `MCP-Protocol-Version: 2025-11-25`
but never an `MCP-Session-Id`. Sessions
do not authorize Tools, replace mutation consent, expose data to plugins, or add
Governance policy. Governance policy, Tool authorization, exact-call approval, and
security audit remain unimplemented. See [Authentication](docs/AUTHENTICATION.md).
Gateway governance remains deferred. See
[host configuration](docs/CONFIGURATION.md) and
`docs/PLUGIN_ARCHITECTURE.md` for the target and migration boundaries.
Implemented tools:
- `list_tools` — reports the server version and lists the exposed tools
- `memory_recall` — retrieves bounded, relevant, user-approved JSON memory records from one allowlisted local scope or canonical namespace
- `memory_list` — completely and deterministically inventories valid memories in one bounded scope or canonical container without returning record content
- `memory_inspect` — returns one exact canonical or legacy memory selected by a versioned structured reference
- `memory_remember` — when explicitly enabled, validates and atomically persists one approved canonical version-2 memory
- `memory_revise` — when independently enabled, revision-checks and atomically replaces the complete caller-mutable state of one exact canonical version-2 memory
- `memory_archive` — when explicitly enabled, revision-checks and archives one exact canonical version-2 memory
- `memory_restore` — when explicitly enabled, revision-checks and restores one exact archived canonical version-2 memory
- `memory_forget` — when independently enabled, permanently deletes one exact archived canonical version-2 memory at its expected revision
This is not yet a full memory substitute. A `memory_recall` request requires a
free-form `query` and exactly one high-level scope (`self`, `relationship`,
`preference`, `practice`, `project`, `knowledge`, or `agent`). It optionally accepts a
canonical `namespace_id` and 1–10 unique free-form `tags`. With no namespace,
scope-wide recall ranks compatible version-1 and version-2 records. A valid
namespace narrows candidate discovery to that exact canonical version-2 namespace
before ranking and excludes legacy version-1 records; it is not a collection
selector. Query terms and tags rank valid records; recall never searches another
scope or accepts a client-supplied path. The Tool schema publishes scope as an
explicit string enum so clients can discover the complete vocabulary. All eight
public `memory_*` schemas include the same `agent` scope; generated clients with
closed scope unions must refresh their schemas.
Matching calls return a normal Tool result with `status: ok` and at most five
memory records. Results include an inspect-compatible versioned reference, the
record ID, scope, title, content, tags, and matched terms/tags, but never include
filesystem paths or internal scores. A legacy reference contains schema version,
scope, and ID. A canonical reference additionally contains namespace ID and the
nullable collection ID. An absent scope or selected namespace directory, or no
relevant record, returns
`{"status":"no_matches","memories":[]}`. Invalid arguments retain stable Tool
errors with code `invalid_query`, `invalid_scope`, `invalid_namespace`, or
`invalid_tags`; unreadable or excessive sources return `memory_source_unavailable` or
`candidate_limit_exceeded` Tool errors.
Recall remains read-only. It does not create, update, delete, or automatically
extract memory, and it does not persist recall requests. Calls remain visible
through the MCP client's existing Tool-call/session representation.
## Listing Memory
`memory_list` is registered by default as a read-only Tool for complete bounded
discovery. It is distinct from both other read operations:
- recall ranks records by a query, excludes non-matches and archived canonical
records, and returns at most five results;
- listing enumerates every valid selected record without a query or relevance
score, includes archived canonical records, and returns compact metadata in
deterministic pages;
- inspection retrieves the complete user-visible contents of one exact listed
or recalled reference.
Every list request requires exactly one scope. Its Tool schema exposes `scope`,
`namespace_id`, `collection_id`, `page_size`, and `cursor` as top-level object
properties for clients that do not project properties nested in composition
branches. Scope uses the same explicit seven-value string enum as recall. Four
mutually exclusive conditional branches retain the strict scope-wide/namespace
and initial/continuation request variants. Tool and property descriptions repeat
those combination rules for clients that retain the flat properties but discard
composition constraints. A scope-wide request includes compatible legacy and
canonical records:
```json
{"scope":"project"}
```
Supplying `namespace_id` selects canonical records only. With a namespace,
collection selection is omission-sensitive:
```json
{"scope":"project","namespace_id":"mnemosyne"}
```
Omitted `collection_id` means collectionless records and every collection in
the namespace. Native JSON null means collectionless records only:
```json
{
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": null
}
```
A string selects one exact collection:
```json
{
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions"
}
```
The string `"null"` is the literal collection ID `null`; clients must send an
actual JSON null for collectionless selection. A collection selector without a
namespace is invalid. Requests never accept a path, root, filename, query, tag,
sort key, arbitrary filter, content field, or cross-scope selector.
Initial requests may include `page_size` from 1 through 100; the default is 50.
When more selected records remain, repeat the exact selector with the returned
opaque cursor and omit `page_size`:
```json
{
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"cursor": "<opaque cursor>"
}
```
A successful response is always `status: ok`, including an empty inventory:
```json
{
"status": "ok",
"memories": [
{
"reference": {
"schema_version": 2,
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"id": "mem_0123456789abcdef0123456789abcdef"
},
"title": "Discovery contract",
"inspectability": "exact",
"kind": "decision",
"lifecycle": {"state": "archived"}
}
],
"page": {
"number": 1,
"count": 1,
"total_count": 1,
"total_pages": 1,
"truncated": false,
"next_cursor": null
}
}
```
Legacy items contain only their version-1 reference, nullable title, and
`inspectability`. Canonical items additionally contain kind and lifecycle state,
but not lifecycle revision. Listing never returns content, tags, labels,
language, provenance, timestamps, paths, fingerprints, retrieval scores, or
match evidence.
Ordering is fixed: schema version first, then public identity. Collectionless
canonical records precede collected records within a namespace. Duplicate
legacy sources remain separate countable items and use their unexposed relative
paths only as a final ordering tie-breaker. Their shared reference is marked
`inspectability: ambiguous`; exact inspection continues to return
`ambiguous_reference` for that reference.
Cursors are authenticated, bound to the exact selector and complete selected
valid-record snapshot, and fixed to the original page size. Selected additions,
removals, relocations, byte rewrites, lifecycle changes, or valid/invalid
transitions return `status: conflict`, code `stale_cursor`. Cursors also become
stale after a server restart because their key and process marker are not
persisted. Malformed, current-process-tampered, selector-mismatched, or otherwise
incompatible cursors return `status: invalid_request`, code `invalid_cursor`.
Start a fresh listing after either cursor error.
Other stable errors are `invalid_request`, `invalid_scope`,
`invalid_namespace`, `invalid_collection`, `invalid_page_size`,
`candidate_limit_exceeded`, `memory_source_unavailable`, and `internal_error`.
Candidate overflow and unsafe or unavailable selected sources fail without a
partial inventory. Listing does not initialize a missing memory root, create an
index or snapshot store, persist cursors, or change any record. Its one terminal
log event contains only bounded outcome, selector-presence, count, page, and
error metadata; it omits selector values, IDs, titles, cursors, content, paths,
fingerprints, exception details, and tracebacks.
## Inspecting Memory
`memory_inspect` is registered by default as a read-only Tool. It accepts exactly
one `reference` object and no path, memory root, filename, query, lifecycle
selector, or broad list selector. Use the versioned reference returned by
`memory_recall` or add `schema_version: 2` to the canonical identity returned by
`memory_remember`:
```json
{
"reference": {
"schema_version": 2,
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"id": "mem_0123456789abcdef0123456789abcdef"
}
}
```
A legacy selector is limited to its actual identity:
```json
{
"reference": {
"schema_version": 1,
"scope": "preference",
"id": "rainy-weekend"
}
}
```
Canonical success returns `status: ok`, the versioned structured reference, and
the complete user-visible version-2 record: scope, namespace, optional
collection, kind, language, nullable title, content, tags, provenance,
lifecycle, and timestamps. Active and archived records use the same result
shape; exact inspection does not filter archived memory. Legacy success returns
only its versioned reference and fields that version 1 can represent: schema
version, ID, nullable title, content, and tags. It does not invent namespace,
collection, kind, language, provenance, lifecycle, or timestamps.
Invalid references, missing records, ambiguous legacy IDs, excessive legacy
candidates, unsafe or unavailable sources, and unexpected failures return
bounded Tool errors with stable codes: `invalid_reference`, `not_found`,
`ambiguous_reference`, `candidate_limit_exceeded`,
`memory_source_unavailable`, or `internal_error`. Results never include paths,
fingerprints, retrieval scores, storage wrappers, or unrelated records.
Inspection never creates, changes, migrates, archives, restores, or deletes a
record and does not initialize a missing memory root. Its one terminal log event
contains only the outcome, stable code/field where applicable, reference schema
version, and scope. It omits IDs, memory text and metadata, complete arguments,
paths, exception details, and tracebacks. Shared skipped-record warnings likewise
contain only scope and a bounded reason.
Memory scopes, records, paths, storage, retrieval, listing selection, ordering,
pagination, cursor policy, content policy, and lifecycle policy live in the
plugin-owned `mymcp/plugins/mnemosyne/memory/` domain rather than inside
individual MCP Tools. The domain
includes mutation-disabled revise, archive, restore, and physical-delete
primitives. `memory_remember`, `memory_revise`, paired
`memory_archive` / `memory_restore`, and `memory_forget` are absent from
discovery and dispatch unless their independent operator gates enable them.
Enabling one gate does not enable another capability.
The MCP host composes kind-qualified `ActivatedTool` values into
`PluginContribution` objects, validates host-owned public bindings, and builds
one immutable `HostRuntime`. The host binds `list_tools` to the complete final
surface and retains trusted origins, effect/consent metadata, plugin inventory,
and an opaque runtime generation. Duplicate plugin, capability, or public names
fail composition rather than overwriting a registration.
The explicit production bootstrap validates one immutable host-configuration
snapshot, preflights every enabled external manifest before any external import,
then reads the fixed packaged inert Mnemosyne manifest, validates exact
definition and selected-contribution parity before generation construction, and
composes the trusted Mnemosyne `0.3.0` adapter over
canonical registrations. The plugin resolves immutable mutation settings once,
selects and orders memory Tools, and lazily resolves the configured root for each
validated operation. Runtime-bound MCP dispatch, `tools/list`, and `list_tools`
all use the same runtime registry. `mymcp/settings.py` retains only host
server/process identity; plugin-owned `configuration.py` retains the unchanged
`MNEMOSYNE_*`, `~/.mnemosyne/config.toml`, and memory-root contracts. Host
configuration is separately documented in [Configuration](docs/CONFIGURATION.md).
Handlers
remain narrow operation adapters. Synthetic multi-contribution tests prove
ordered aggregation, complete reporting, dispatch, and collision rejection.
This static bundled-plugin seam preserves Mnemosyne's public server identity,
Tool names, schemas, ordering, gates, results, errors, configuration, and
storage contracts. The manifest grants no requested authority. This is not
dynamic discovery, adds no public Tool namespacing, and provides no installation
or lifecycle system.
## Revising Memory
Revision is disabled by default and has an independent gate. Persist it in the
fixed settings file or use the process override:
```toml
[memory]
revise_enabled = true
```
```bash
export MNEMOSYNE_MEMORY_REVISE_ENABLED=true
```
Only exact lowercase `true` and `false` are accepted. Restart the server and
reconnect or restart the MCP client after changing enablement. Clients that
cannot require approval for every complete exact replacement must leave
revision disabled.
Inspect the record immediately before proposing revision. Recall returns an
inspect-compatible reference, but inspection supplies the complete current
record, lifecycle state, and revision needed for review and approval. Every
revision request contains six unconditional fields plus `collection_label` when
the reference identifies an existing collection:
```json
{
"reference": {
"schema_version": 2,
"scope": "preference",
"namespace_id": "tea",
"collection_id": "favorites",
"id": "mem_0123456789abcdef0123456789abcdef"
},
"expected_revision": 3,
"namespace_label": "Tea",
"collection_label": "Favorites",
"title": "Japanese green tea",
"content": "The user enjoys sencha and gyokuro.",
"tags": ["tea", "japanese-green-tea"]
}
```
`namespace_label` and `title` may be null, but their keys remain required. When
`reference.collection_id` is a string, `collection_label` is also required and
may be either a replacement string or native JSON null. When
`reference.collection_id` is null, the record has no collection and
`collection_label` may be omitted; an explicit native JSON null remains accepted.
The literal string `"null"` is text, not JSON null. Content is nonblank and at
most 4,000 characters; tags contain zero to ten unique normalized items of at
most 50 characters. Unknown fields, legacy references, paths, patches, language
or identity changes, provenance, lifecycle targets, timestamps, and model
confirmation/consent fields are rejected.
Clients that stringify structured Tool fields receive the existing one-layer
compatibility normalization: a JSON-encoded reference object, tags array, or
integer revision is decoded where its schema position disallows strings. A
nullable text position permits real strings, so string `"null"` is deliberately
not coerced. Collectionless callers that cannot emit top-level JSON null should
omit `collection_label`.
The caller replaces only namespace label, the label of an existing collection,
title, content, and tags. Mnemosyne preserves schema version, record ID, scope,
namespace kind/ID, collection presence/ID, memory kind, language, provenance,
lifecycle state, and `created_at`. On change, the server increments lifecycle
revision once and updates `updated_at`. Labels never rename directories or
relocate the record. Revision atomically replaces the same source file and
creates no second record, patch history, backup, diff, or tombstone.
Success returns only status, canonical versioned reference, and lifecycle:
```json
{
"status": "revised",
"reference": {
"schema_version": 2,
"scope": "preference",
"namespace_id": "tea",
"collection_id": "favorites",
"id": "mem_0123456789abcdef0123456789abcdef"
},
"lifecycle": {"state": "active", "revision": 4}
}
```
`revised` means one atomic replacement occurred. A current-revision normalized
no-op returns `already_current` without a write, timestamp update, or revision
increment; stale revision conflicts before no-op detection. Archived memory may
be revised but remains archived, inspectable, and excluded from normal recall.
Active memory remains recall-eligible.
The shared bounded content policy checks every replacement label, title,
content value, and tag before storage access. It refuses recognized private-key,
credential, authorization-header, JWT-shaped, payment-card, and SSN-shaped
values with `disallowed_content`. Compact-token recognition requires JOSE
compact-serialization evidence—a base64url JSON header segment beginning `eyJ`
followed by two further segments—so ordinary dotted text such as versions,
dates, hostnames, and module paths is stored normally. This is signature
detection, not complete semantic DLP; secrets and sensitive personal data
remain prohibited.
Both `memory_revise` and `memory_remember` make a content refusal actionable
without echoing the rejected value. The Tool error contains the first bounded
caller-visible field, one broad reason, and stable remediation guidance:
```json
{
"status": "refused",
"code": "disallowed_content",
"field": "content",
"reason": "compact_token_shape",
"message": "memory field resembles content that Mnemosyne does not store; review the named field and retry only if the user confirms that the formatting is benign"
}
```
The fixed reason vocabulary is `private_key_shape`, `credential_shape`,
`compact_token_shape`, `payment_card_shape`, and
`government_identifier_shape`. Remember maps namespace IDs/labels to
`field: namespace`, collection IDs/labels to `field: collection`, and reports
`title`, `content`, or `tags` directly. Revision reports its flat replacement
fields: `namespace_label`, `collection_label`, `title`, `content`, or `tags`.
Tags never expose an index.
Only the deterministic first match is reported. Results and logs never include
the matched value, offset, regular expression, provider-specific detector,
fingerprint, or rejected content. The metadata is diagnostic, not permission to
evade policy: do not obfuscate suspected sensitive data. Retry only after the
user verifies that the formatting is benign and approves the complete revised
call.
Stable bounded error codes are `invalid_reference`,
`invalid_expected_revision`, `invalid_record`, `invalid_collection`,
`disallowed_content`, `mutation_disabled`, `not_found`, `revision_conflict`,
`write_conflict`, `memory_source_unavailable`,
`replacement_outcome_uncertain`, and `internal_error`. Error statuses are,
as applicable, `invalid_request`, `refused`, `policy_error`, `not_found`,
`conflict`, `storage_error`, `uncertain`, or `internal_error`.
`replacement_outcome_uncertain` means the atomic replacement may already be
visible, but parent-directory durability was not confirmed. Do not retry
blindly: inspect the same reference first. If the requested replacement is
already present, do not retry; otherwise use the inspected current revision and
obtain new exact per-call approval. If inspection is unavailable, the outcome
remains uncertain.
Logger `mcp.memory_revise` emits one terminal event containing only outcome,
stable code/field where applicable, schema version, scope, and successful
lifecycle state/revision. It omits IDs, labels, title, content, tags, complete
arguments, paths, fingerprints, exception details, and tracebacks.
`list_tools` prefixes the discovered Tool names with the same static server
version exposed by MCP initialize and `/version`, for example
`Server: mymcp 0.10.1.`. Restart the server and reconnect the client after an
upgrade; a prior marker identifies a stale process.
## Archiving and Restoring Memory
Archive and restore are disabled by default and are enabled together. Persist
the operator choice in the same fixed settings file:
```toml
[memory]
archive_restore_enabled = true
```
Or use the process override:
```bash
export MNEMOSYNE_MEMORY_ARCHIVE_RESTORE_ENABLED=true
```
Only exact lowercase `true` and `false` are accepted. This gate is independent
of `remember_enabled`; enabling either capability does not enable the other.
Restart the server and MCP client after changing enablement.
Each lifecycle request contains exactly the canonical version-2 reference from
`memory_inspect` and its current positive revision:
```json
{
"reference": {
"schema_version": 2,
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"id": "mem_0123456789abcdef0123456789abcdef"
},
"expected_revision": 1
}
```
The request accepts no legacy reference, path, root, filename, query, content,
target state, timestamp, or consent field. Inspect immediately before proposing
the mutation so the complete exact reference and current revision can be shown
for approval. A stale revision always returns `revision_conflict`, even if the
record has since reached the requested state.
Archive changes `active` to `archived`, increments revision once, updates
`updated_at`, and atomically replaces the same file. Archived memory is excluded
from normal recall but remains available through exact inspection. Restore
performs the inverse transition and returns the record to recall. A
current-revision archive of archived memory returns `already_archived`; a
current-revision restore of active memory returns `already_active`. Idempotent
outcomes do not write, increment revision, or update metadata.
Success contains only status, canonical versioned reference, and lifecycle:
```json
{
"status": "archived",
"reference": {
"schema_version": 2,
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"id": "mem_0123456789abcdef0123456789abcdef"
},
"lifecycle": {"state": "archived", "revision": 2}
}
```
The four normal statuses are `archived`, `already_archived`, `restored`, and
`already_active`. Stable bounded error codes are `invalid_reference`,
`invalid_expected_revision`, `mutation_disabled`, `not_found`,
`revision_conflict`, `write_conflict`, `memory_source_unavailable`,
`replacement_outcome_uncertain`, and `internal_error`. Results never expose
record content, paths, fingerprints, or storage wrappers.
For archive and restore, `replacement_outcome_uncertain` means the new
lifecycle state may already be visible but directory durability was not
confirmed. Inspect the same reference before any newly approved retry. This is
distinct from forget's deletion-specific uncertain outcome.
Each server call logs one terminal event under `mcp.memory_archive` or
`mcp.memory_restore`. Logs contain only outcome, stable code/field where
applicable, schema version, scope, and successful lifecycle state/revision. They
omit IDs, namespace/collection identity, labels, title, content, tags, complete
arguments, paths, exception details, and tracebacks.
## Permanently Forgetting Memory
Forget is disabled by default and has its own gate. It is not enabled by
remember or archive/restore. Persist the operator choice in the fixed settings
file:
```toml
[memory]
forget_enabled = true
```
Or use the exact process override:
```bash
export MNEMOSYNE_MEMORY_FORGET_ENABLED=true
```
Only lowercase `true` and `false` are accepted. Restart the server and MCP
client after changing either source. Clients that cannot require approval for
every exact call must leave forget disabled.
Forget accepts the same exact canonical version-2 reference and positive
`expected_revision` request shape shown above for lifecycle mutation. It accepts
no legacy reference, path, content, fingerprint, target state, timestamp, or
confirmation field. The record must already be archived at that current
revision. A stale request returns `revision_conflict` before state eligibility;
a current active record returns `not_archived` unchanged. Inspect immediately
before proposing forget so the complete record, archived state, reference, and
current revision can be reviewed for per-call approval.
Success physically removes the one source file, leaves organizational
directories intact, creates no tombstone, hidden history, backup, retained hash,
or deletion manifest, and returns only:
```json
{
"status": "forgotten",
"reference": {
"schema_version": 2,
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"id": "mem_0123456789abcdef0123456789abcdef"
}
}
```
A second call returns `not_found`; Mnemosyne cannot fabricate idempotent proof
without a tombstone. Stable bounded codes are `invalid_reference`,
`invalid_expected_revision`, `mutation_disabled`, `not_archived`, `not_found`,
`revision_conflict`, `write_conflict`, `memory_source_unavailable`,
`deletion_outcome_uncertain`, and `internal_error`.
`deletion_outcome_uncertain` means unlink may have succeeded but directory
durability was not confirmed. Do not retry blindly. Inspect the same reference:
if it is absent, do not retry; if it is present, review it again, require it to
remain archived, use its current revision, and obtain new per-call approval. If
inspection is unavailable, the outcome remains uncertain.
Forgetting is irreversible within Mnemosyne: restore cannot recover a forgotten
record. Physical source deletion is not secure erasure and does not remove
filesystem journal data, snapshots, backups, MCP-client history, logs outside
Mnemosyne, or external copies. Logger `mcp.memory_forget` emits one terminal
content-free event with only bounded outcome/code/field, schema version, and
scope metadata.
## Remembering Memory
Remember is disabled by default. With no setting, an accepted disabled setting,
or the exact environment value `false`, `tools/list` and `list_tools` omit
`memory_remember` and direct dispatch treats it as unknown.
Persist the operator's choice in the one fixed local settings file:
```text
~/.mnemosyne/config.toml
```
```toml
[memory]
remember_enabled = true
```
The file is optional and read-only to Mnemosyne. Startup does not create or
edit it or its parent. A missing file, empty document, absent or empty
`[memory]` table, absent key, or TOML boolean `false` all mean disabled. The
document may contain only the optional `[memory]` table, which may contain only
the optional TOML booleans `remember_enabled`, `archive_restore_enabled`,
`forget_enabled`, and `revise_enabled`;
strings such as `"true"`, unknown keys/tables, and malformed TOML fail startup
closed.
For one process-level override, use:
```bash
export MNEMOSYNE_MEMORY_REMEMBER_ENABLED=true
```
Each environment variable overrides only its matching setting. Mnemosyne parses
all four mutation environment values first; an invalid supplied value fails
startup before file access without being echoed. If any setting remains
unresolved, Mnemosyne reads the strict file at most once for unresolved values.
The file is bypassed only when all four environment variables are supplied.
Every unresolved setting finally defaults to false.
The server reads enablement once at startup, so restart it after changing either
source. Restart or reconnect the MCP client as well so it refreshes Tool
discovery. Enabling the server-side Tool does not establish user consent. A
compatible MCP client must separately show the complete arguments and require
user approval for every exact call.
The settings source is limited to 16 KiB of UTF-8 TOML. Mnemosyne rejects a
symlinked `.mnemosyne` directory or file, non-directory/non-regular source,
unreadable source, and—on POSIX—a directory or file writable by group or others.
Use mode `0700` for `~/.mnemosyne` and `0600` for `config.toml`; non-writable
`0755`/`0644` modes are also accepted because this file is not a secret store.
Configuration failures use stable bounded messages and do not expose supplied
values, file contents, parser details, underlying exception text, or the
absolute settings path.
Nine caller-owned fields are always required, including nullable values. The
Tool schema publishes those nine plus optional `occurred_at` in top-level
`properties`, while retaining the nine-field top-level required list, so clients
that keep only a flat object schema can construct both ordinary memories and
events. Seven scope-specific `oneOf` branches additionally narrow namespace kind
and memory kind for clients that support composition:
```json
{
"scope": "project",
"namespace": {
"kind": "project",
"id": "mnemosyne",
"label": "Mnemosyne"
},
"collection": {
"id": "decisions",
"label": "Decisions"
},
"kind": "decision",
"language": "en",
"title": "Remember consent boundary",
"content": "Durable memory requires approval for each exact Tool call.",
"tags": ["architecture", "consent"],
"origin": "user_approved_proposal"
}
```
For a completed project occurrence, use `kind: "event"` and include its known
occurrence instant separately from persistence time:
```json
{
"scope": "project",
"namespace": {"kind": "project", "id": "mnemosyne", "label": "Mnemosyne"},
"collection": {"id": "events", "label": "Events"},
"kind": "event",
"language": "en",
"title": "Track activated",
"content": "Track 021 moved to active execution.",
"tags": ["track-021"],
"origin": "explicit_user_statement",
"occurred_at": "2026-07-20T15:00:00Z"
}
```
`occurred_at` is required exactly for `event`, rejected for every other kind,
and uses strict UTC-second form `YYYY-MM-DDTHH:MM:SSZ`. It is immutable after
creation: revision has no occurrence replacement field, and archive/restore
preserve it. Existing non-event version-2 records remain valid without the
field and serialize without an invented null value.
`namespace.label`, `collection`, `collection.label`, `language`, and `title`
may be null, but their keys remain required. Tags are a required zero-to-ten
item array. Namespace/collection IDs are 1–64 safe identifier characters,
labels are at most 100 characters, language tags at most 35, title at most 200,
content at most 4,000, and each tag at most 50. Unknown fields are rejected at
every level. The allowed dimensions are derived from the shared domain:
| Scope | Namespace kinds | Memory kinds |
| --- | --- | --- |
| `self` | `aspect` | `attribute` |
| `relationship` | `person`, `group`, `relationship` | `perspective`, `summary` |
| `preference` | `domain` | `preference` |
| `practice` | `domain` | `practice` |
| `project` | `project` | `decision`, `constraint`, `state`, `event`, `question`, `reference`, `summary` |
| `knowledge` | `topic` | `reference`, `summary` |
| `agent` | `agent` | `persona`, `policy`, `checklist`, `failure_mode` |
The shared domain defines writing guidance for every allowed `(scope, kind)`
pair. Complete scope branches publish only their applicable guidance, while the
top-level `kind` description groups all guidance by scope for clients that
discard schema composition. Shared kinds such as `summary` and `reference`
therefore retain scope-specific meaning.
The `agent` scope holds user-approved operational refinements for one named AI
agent, not user-profile memory. Structural or boot configuration remains in that
agent's boot file; Mnemosyne records may hold approved session-managed refinements
and do not automatically synchronize with the file. Agent records use the same
consent, no-secrets, lifecycle, structured-reference, storage, and gate contracts
as every other scope, with no cross-agent authority, routing, or isolation.
Public `origin` is caller-supplied provenance context: use
`explicit_user_statement` for a direct user statement or
`user_approved_proposal` for an approved proposed memory. Origin is not consent;
consent remains the MCP client's approval of the complete exact call.
`recorded_via` is instead server-assigned. Callers cannot supply a filesystem
path, record ID, persistence timestamps, lifecycle state, revision,
`recorded_via`, or model-authored confirmation/consent field. The event-only
`occurred_at` value is the sole caller-owned structural time.
A new memory returns only its status, structured reference, and lifecycle:
```json
{
"status": "remembered",
"reference": {
"scope": "project",
"namespace_id": "mnemosyne",
"collection_id": "decisions",
"id": "mem_0123456789abcdef0123456789abcdef"
},
"lifecycle": {
"state": "active",
"revision": 1
}
}
```
An exact active duplicate returns `already_exists`; an archived duplicate
returns `existing_archived`. Both identify the existing reference/lifecycle and
write no second file. Validation, content refusal, disabled mutation, candidate
overflow, generated-ID conflict, storage failure, and unexpected failure return
bounded Tool errors with stable codes and no path or submitted content. Content
refusal uses the bounded field/reason contract documented under revision above.
Validation uses `invalid_scope`, `invalid_namespace`, `invalid_collection`,
`invalid_kind`, `invalid_record`, or `invalid_origin`; policy refusal uses
`disallowed_content`; disabled mutation uses `mutation_disabled`; bounded
discovery uses `candidate_limit_exceeded`; publication conflict uses
`write_conflict`; storage/path failures use `memory_source_unavailable`; and an
unexpected failure uses `internal_error`.
Event duplicate identity includes `occurred_at`: otherwise-equal events at the
same occurrence instant are duplicates, while events at different instants are
distinct. Existing non-event duplicate identity is unchanged.
Before duplicate discovery or directory creation, the shared policy rejects
recognized private-key blocks, declared provider token/API-key prefixes,
Basic/Bearer authorization headers, credential assignments and
credential-bearing URLs, compact JWT-shaped values, Luhn-valid 13–19 digit
payment-card values, and SSN-shaped identifiers. It inspects
namespace/collection IDs and labels, title, content, and tags. This is
intentionally bounded signature detection, not semantic DLP: false positives
and false negatives remain possible. Unrecognized secrets and sensitive
personal facts remain prohibited, and the user must review the exact proposed
arguments before approval.
Remember logs one terminal event containing only outcome, stable error
code/field where applicable, scope, namespace kind, memory kind,
collection-present state, origin, and generated ID/lifecycle after a domain
result. Logs never include labels, title, content, tags, language,
`occurred_at`, complete arguments, rejected values, filesystem paths, exception
messages, or tracebacks.
## Filesystem Memory
The default memory root is:
```text
~/.mnemosyne/memory
```
No manual first-run initialization is required. When an enabled, valid,
policy-accepted `memory_remember` call reaches canonical creation, Mnemosyne
lazily creates any missing directories from the nearest existing ancestor
through the record directory. Every newly created directory uses private mode
`0700` on POSIX, and the record file uses mode `0600`. Existing directories are
left unchanged rather than chmodded.
Resolving either settings source, starting the server, recalling memory,
disabled or invalid remember calls, and content-policy refusals do not
initialize the memory root. Filesystem initialization therefore remains behind
the existing operator-enable and per-call consent boundaries.
Set an explicit root for another local location:
```bash
export MNEMOSYNE_MEMORY_ROOT=/path/to/memory
```
The memory domain recognizes only the seven fixed scope directories beneath the
memory root:
```text
memory/
self/
relationship/
preference/
practice/
project/
knowledge/
agent/
```
Legacy version-1 records remain readable in their existing locations. Their
minimal format is:
```json
{
"schema_version": 1,
"id": "rainy-weekend",
"title": "Rainy weekend activities",
"content": "On rainy weekend afternoons, the user prefers museums or quiet cafés.",
"tags": ["leisure", "rainy-day", "weekend"]
}
```
Version-1 `schema_version`, `id`, and `content` are required; `title` and `tags`
are optional. No background migration or rewrite occurs.
Canonical version-2 records are self-contained and use a deterministic path:
```text
<scope>/<namespace-id>/<collection-id?>/<memory-id>.json
```
For example:
```text
project/mnemosyne/decisions/mem_0123456789abcdef0123456789abcdef.json
```
```json
{
"schema_version": 2,
"id": "mem_0123456789abcdef0123456789abcdef",
"scope": "project",
"namespace": {
"kind": "project",
"id": "mnemosyne",
"label": "Mnemosyne"
},
"collection": {
"id": "decisions",
"label": "Decisions"
},
"kind": "decision",
"language": "en",
"title": "Shared memory ownership",
"content": "Canonical memory concepts belong to the shared memory domain.",
"tags": ["architecture", "memory-domain"],
"provenance": {
"origin": "explicit_user_statement",
"recorded_via": "memory_remember"
},
"lifecycle": {
"state": "active",
"revision": 1
},
"created_at": "2026-07-18T12:00:00Z",
"updated_at": "2026-07-18T12:00:00Z"
}
```
Scope, namespace, optional collection, kind, language, content, tags,
provenance, persistence timestamps, optional event occurrence time, and
lifecycle are separate dimensions. IDs determine paths; mutable labels do not.
Record metadata must agree with its location. Unknown fields or mismatched paths
make a version-2 record invalid.
Canonical project events persist `"kind": "event"` and a strict
`"occurred_at"` value. Exact inspection returns that field; recall and listing
retain their existing relevance and identity ordering rather than becoming
chronological views. Mnemosyne does not create timeline resources, infer
causality, automatically supersede state, or make event records fully
append-only. A first-class many-to-many temporal model remains deferred until a
concrete usage need demonstrates it.
Version-2 records are either `active` or `archived`; normal recall excludes
archived records. Revision replaces the same file atomically without retaining
hidden prior content. Forgetting is physical deletion with no tombstone.
Remember creation, complete-state revision, reversible archive/restore, and
archived-only physical forget are exposed through explicit MCP Tools behind
independent default-off gates. Revision atomically replaces the same canonical
file and retains no hidden prior content.
All record files are limited to 64 KiB, content to 4,000 characters, and titles
to 200 characters. Invalid, oversized, too-deep, or unsafe records are skipped
and logged without their content.
Recall retrieval case-folds and tokenizes the query, relative path, title, content, and
record tags. Exact request-tag overlap has the strongest weight, followed by
title, path/record-tag, and content matches. Ties are resolved deterministically.
When `namespace_id` is present, discovery first narrows to that canonical
namespace; a selected symlink is skipped safely, while a selected non-directory
is unavailable. Symlinks are otherwise rejected, no more than 1,000 candidate
files are accepted in the selected discovery root, and no more than five records
are returned.
Files remain the source of truth: inspect them directly and delete a record by
deleting its file.
There is no required manifest or persistent content-bearing index. Exact
inspection and complete listing use structured selectors and never accept a
filesystem path. Scope-wide listing applies the same 1,000-candidate bound;
canonical namespace and collection selectors narrow to their deterministic safe
container before candidate counting. Collectionless and exact-collection
discovery scan only the canonical path level. Listing fails instead of returning
a partial inventory when the selected container exceeds the bound.
Every mutation remains disabled unless its operator gate enables it and the MCP
client can require approval for every exact call. A model-provided confirmation
field is not consent.
## MCP Validation
An MCP request envelope must be an object. Otherwise the server returns
JSON-RPC error `-32600` with the message `Invalid Request` and `id: null`.
When present, its `params` value must also be an object. Otherwise the server
returns JSON-RPC error `-32602` with the message `Invalid params` and preserves
the request ID. For `tools/call`, `params.arguments` must likewise be an object
when present; non-object Tool arguments receive the same `-32602` response
before Tool selection, schema-aware normalization, or handler dispatch.
MCP notifications omit `id` and receive HTTP `202` with no JSON-RPC response
body. `notifications/initialized` and `notifications/cancelled` are accepted;
cancellation is currently a no-op because tool handlers complete synchronously.
For compatibility with MCP clients that stringify structured Tool arguments,
dispatch uses the selected Tool's advertised input schema to remove at most one
extra JSON-encoding layer. Normalization occurs only where the schema disallows
a string and the decoded JSON value has a permitted type. Correctly typed values
are unchanged; fields that permit strings are never decoded; malformed,
wrong-type, or repeatedly encoded values continue to the Tool's normal bounded
validation. The advertised Tool schemas and canonical memory formats remain
unchanged. For `memory_list`, a stringified page size can therefore normalize to
an integer, while `collection_id` strings—including `"null"`—remain strings
because that field legitimately permits them. Collectionless selection requires
native JSON null.
## Intended Roles
MyMCP is intended to become a local-first MCP host and client-neutral governance
gateway. It owns Tool composition, identity, plugin contracts, routing, and
reusable host mechanisms without absorbing integration-specific domain policy.
Its Tools-only architecture separates one host-owned logical plugin API from
bundled implementations and configured trusted external plugins. It retains
qualified `(plugin_id, capability_kind, capability_local_id)` origin internally
and binds that identity to the endpoint's flat MCP Tool name. Bootstrap first
preflights all enabled external manifests, then imports zero-argument
`mymcp_plugin_v1` entrypoints that return `PluginAdapter` values for parity
validation and complete composition. Bundled Mnemosyne comes first; externals
follow configuration and capability order, with deterministic
`<plugin-id>__<tool-local-id>` bindings. A collision fails complete startup.
External code is operator-installed and operator-trusted in process;
compatibility validation is not safety validation. MyMCP does not manage
installation, dependencies/environments, secrets, upgrades/rollback, hot
activation, lifecycle/workers, isolation/sandboxing, supervision, or resource
control. Authenticated client policy, host-verifiable exact-call approval, and
bounded security audit remain unimplemented.
Mnemosyne remains the bundled user-governed memory domain. It gives agents
controlled access to approved durable records and bounded retrieval while
permanently preserving its Tool, configuration, storage, record, logging,
consent, and memory-domain identity. Its configuration, memory domain, MCP
adapters, and plugin composition live under `mymcp/plugins/mnemosyne/`.
## Non-Goals
MyMCP and its built-in Mnemosyne domain are not intended to provide:
- generic shell execution or unrestricted filesystem access;
- secret storage;
- hidden mutation or governance that bypasses visible user consent;
- a multi-user platform before an explicit threat model supports one;
- MCPB as the native MyMCP plugin format; or
- a host-managed plugin installer, lifecycle manager, marketplace, or plugin
isolation claim.
## Running the Server
Clone the canonical MyMCP repository target:
```bash
git clone https://github.com/flyset/MyMCP.git
cd MyMCP
```
The canonical repository is <https://github.com/flyset/MyMCP>; the former URL
redirects here.
Optionally create host configuration at
`$XDG_CONFIG_HOME/mymcp/config.toml`, or `~/.config/mymcp/config.toml` when the
XDG value is absent, empty, or relative:
```toml
schema_version = 2
[server]
address = "127.0.0.1"
port = 8000
[[plugins]]
id = "example-plugin"
enabled = true
manifest_path = "/opt/mymcp-plugins/example-plugin/manifest.json"
module = "example_plugin"
```
Schema 3 requires explicit anonymous Authentication intent. Schema 4 preserves
schema-3 server, plugin, anonymous, and adapter declarations and adds the
operator-bearer verifier source selector:
```toml
schema_version = 4
[authentication]
anonymous_enabled = false
[[authentication.adapters]]
id = "local-client"
type = "operator-bearer-v1"
enabled = true
route = { source = "authorization", scheme = "bearer" }
[authentication.operator_bearer]
verifier_path = "/etc/mymcp/operator-bearer.json"
```
`verifier_path` is non-secret source metadata, not a credential or verifier
value. The verifier file is read only for an enabled `operator-bearer-v1`
declaration. Schema-4 anonymous access remains independent of registered
adapters; an evidence-free request is anonymous only when `anonymous_enabled` is
true. Schemas 1–3 and an absent file preserve their existing anonymous defaults.
Schema 5 adds OAuth as an alternative, startup-fixed production method. Use only
a reserved issuer in examples; the value below is not a live provider:
```toml
schema_version = 5
[server]
address = "127.0.0.1"
port = 8000
[authentication]
anonymous_enabled = false
[[authentication.adapters]]
id = "local-oauth"
type = "oauth-jwt-jwks-v1"
enabled = true
route = { source = "authorization", scheme = "bearer" }
[authentication.oauth_jwt]
issuer = "https://issuer.invalid"
```
The exact `oauth_jwt` table contains only the non-secret canonical HTTPS
`issuer`; it is required whenever an `oauth-jwt-jwks-v1` declaration exists,
including a disabled declaration, and prohibited otherwise. A schema-5 document
cannot declare OAuth and `operator-bearer-v1` together, even when either is
disabled. Enabled OAuth requires `anonymous_enabled = false`. Startup fetches
the configured issuer metadata and same-origin JWKS once into an immutable
validation snapshot; changing issuer keys or configuration requires restart.
For enabled OAuth only, the protected resource is derived from the validated
loopback binding, never `Host` or forwarded headers. With the example binding,
the exact resource is `http://127.0.0.1:8000/mcp` and metadata is available only
at `http://127.0.0.1:8000/.well-known/oauth-protected-resource/mcp`. Its JSON has
exactly `resource`, one `authorization_servers` value, and
`bearer_methods_supported: ["header"]`, with `Cache-Control: no-store` and no
scopes. OAuth authentication failures on `/mcp` remain body-free HTTP `401` and
carry exactly `WWW-Authenticate: Bearer resource_metadata="<derived metadata URL>"`.
Operator-bearer and anonymous-only configurations remain challenge-free.
This literal loopback HTTP endpoint is a local interoperability exception; it is
not HTTPS or remote-deployment support. MyMCP is a resource server only and does
not provide authorization-server metadata, OpenID Provider metadata, dynamic
client registration, or `/register`.
Only literal
loopback addresses and ports 1–65535 are supported. Schema 1 remains supported
with exactly `id`/`enabled` and preserves `enabled_plugin_unsupported`; schema 2
requires exact `id`/`enabled`/absolute `manifest_path`/dotted `module` fields.
Disabled plugins are neither accessed, imported, nor exposed. Enabled schema-2
plugins are preflighted before imports and are operator-trusted in-process code. See
[host configuration](docs/CONFIGURATION.md) for safety rules, errors, restart
behavior, and the strict schema.
For an enabled `operator-bearer-v1` adapter, send exactly one HTTP header:
```text
Authorization: Bearer mymcp1.<32-lowercase-hex-id>.<43-character-base64url-secret>
```
`Bearer` is ASCII case-insensitive, followed by exactly one ASCII space. The
credential itself is ASCII and exact: no padding, tabs, leading/trailing
whitespace, Unicode, extra components, or alternate base64 alphabet. Missing
evidence follows `anonymous_enabled`; submitted malformed, duplicate, unsupported,
unknown, revoked, or invalid evidence returns an empty HTTP `401` before MCP body
parsing, logging, streaming, or dispatch. See [Authentication](docs/AUTHENTICATION.md)
and [Configuration](docs/CONFIGURATION.md) for provisioning and verifier rules.
The MCP endpoint is exposed at:
```text
http://127.0.0.1:8000/mcp
```
Additional operational endpoints:
- `http://127.0.0.1:8000/health`
- `http://127.0.0.1:8000/version`
Create a local virtual environment and install the project in editable mode:
```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .
```
Start the development server with auto-reload:
```bash
mymcp-dev
```
Start the server without auto-reload:
```bash
mymcp
```
The supported direct ASGI target is the application factory
`mymcp.app:create_production_app` with Uvicorn factory mode. The former internal
module-level target `mymcp.app:app` no longer exists.
At startup, host-configuration loading is reported through the
`mymcp.host.configuration` logger. The `mymcp` and `mymcp-dev` launchers configure
standard Python logging at `INFO`; direct Uvicorn factory use retains Uvicorn's
logging ownership, and programmatic callers configure Python logging themselves.
See [Configuration](docs/CONFIGURATION.md#logging) for the bounded event
format, restart behavior, and troubleshooting guidance.
Run the test suite after installing the `test` extra:
```bash
mymcp-test
```
## OpenCode Configuration
The tracked `opencode.json` registers this server as remote connection and agent
`mymcp`. At both policy levels it denies the broad `mymcp_*` prefix first, then
allows exactly four read-only Tools (`list_tools`, `memory_recall`,
`memory_inspect`, and `memory_list`), then asks for the five exact mutation
Tools (remember, revise, archive, restore, and forget). The order matters:
OpenCode uses the last matching Tool-name rule and per-agent permissions can
override top-level rules. The tracked and ignored OpenCode migration is complete;
the legacy Claude configuration is excluded. The normal MyMCP endpoint and final
client reconnect were verified. In isolated checks, one approved mutation was
approved once and one rejected mutation made no Tools call; neither check changed
the memory root.
```json
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"mymcp_*": "deny",
"mymcp_list_tools": "allow",
"mymcp_memory_recall": "allow",
"mymcp_memory_inspect": "allow",
"mymcp_memory_list": "allow",
"mymcp_memory_remember": "ask",
"mymcp_memory_revise": "ask",
"mymcp_memory_archive": "ask",
"mymcp_memory_restore": "ask",
"mymcp_memory_forget": "ask"
},
"mcp": {
"mymcp": {
"type": "remote",
"url": "http://127.0.0.1:8000/mcp",
"enabled": true
}
},
"agent": {
"mymcp": {
"permission": {
"mymcp_*": "deny",
"mymcp_list_tools": "allow",
"mymcp_memory_recall": "allow",
"mymcp_memory_inspect": "allow",
"mymcp_memory_list": "allow",
"mymcp_memory_remember": "ask",
"mymcp_memory_revise": "ask",
"mymcp_memory_archive": "ask",
"mymcp_memory_restore": "ask",
"mymcp_memory_forget": "ask"
}
}
}
}
```
This client permission does not enable mutation on the server. The operator
must separately set the applicable remember, revise, archive/restore, or forget
server gate and restart the server. After changing `opencode.json` or agent
policy, quit and restart OpenCode so it reloads configuration and Tool discovery.
For every proposed mutation call, review the complete Tool arguments and choose
`once`. Rejecting a mutation prevents the request from reaching Mnemosyne and
therefore produces no write. Do not start OpenCode with `--auto` or enable
interactive auto-approval while memory mutation is enabled: each bypasses
approval on later exact calls. If either mode was used, disable server mutation
and restart both the server and OpenCode before continuing. Any additional
per-agent permission override must preserve this order: broad `mymcp_*`
denial first, explicit lower-breadth read-only allows (including `memory_list`)
next, and the exact mutation asks last.
## Integrating Mnemosyne with a Repository
After Mnemosyne's memory Tools are available to an AI client, add a repository-root
`MEMORY.md`. It is the repository-local operating contract: it tells agents which
project context to retrieve, where durable records belong, and when records should
be created, revised, archived, or forgotten.
Keep the repository, Git history, backlog, and documentation as the authoritative
engineering record. Mnemosyne preserves concise, user-approved orientation,
rationale, and delivery history across sessions.
Start with `docs/GETTING_STARTED.md` for a project memory-map template and
integration guidance. Agents should read `docs/MANUAL.md` for general safe Tool-use
guidance before operating on memory.
## Roadmap Shape
The roadmap converges on the complete approved end state rather than freezing a
bundled-only callable or deferring identity and security boundaries:
1. **Delivered:** kind-qualified Tools-only identity, effect/consent metadata,
host-owned bindings, immutable `HostRuntime`, a trusted Mnemosyne adapter,
explicit bootstrap, runtime injection, immutable definition contracts, strict
64 KiB manifest parsing, complete packaged declaration, exact parity, static
validation before generation, wheel inclusion, and the one canonical
Mnemosyne implementation under `mymcp/plugins/mnemosyne/`—without changing
public behavior.
2. **Released:** MyMCP/`mymcp` `0.2.0` host, application, package, repository,
and tracked-client identity preserve all Mnemosyne plugin/domain identities.
The public non-draft, non-prerelease release is tagged `mymcp-v0.2.0` at
`c2852bc`.
3. Add startup composition for operator-installed trusted external plugins.
Before implementation loading where possible, validate inert manifests,
compatibility, and identities/versions; then load the trusted implementation
and validate definitions, contributions, and public bindings before runtime
construction. Invalid composition fails startup clearly.
4. Keep plugin installation, dependencies, environments, configuration, updates,
rollback, and process stop/restart operator-managed. Configured plugins may
run in process; MyMCP provides no sandbox, isolation, supervision,
killability, or resource-control guarantee.
5. Build authenticated local-client routing, policy-filtered discovery/dispatch,
single-use exact-call approval, and bounded security audit behind the one
machine-local endpoint.
6. Generalize host services only after a second real plugin proves them reusable.
MCPB may later package MyMCP as one complete server. It is not the native MyMCP
plugin distribution, manifest, or execution boundary.
See `docs/PLUGIN_ARCHITECTURE.md` for the target and incremental migration rules.
See `VISION.md` for MyMCP's broader scope and boundaries. See
`docs/MNEMOSYNE_VISION.md` for the built-in memory domain's notebook model,
safety contract, and direction.
See `docs/ARCHITECTURE.md` for the current code organization.
See `docs/PLUGIN_ARCHITECTURE.md` for the approved plugin end state and roadmap
alignment.
See `docs/GETTING_STARTED.md` for repository-level project-memory integration,
and `docs/MANUAL.md` for operating guidance for agents using memory Tools.
See `docs/AI_WORKFLOW.md` for contribution and verification gates, and
`docs/GLOSSARY.md` for canonical terms and public-contract language.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues