docs/design/external-context-mem0-extension.md
Status: PR2 implementation
Date: 2026-08-28
Related profile: External Context Provider Extensions
Related implementation proposal: PR #9952
Mem0-compatible services integrate through a self-contained local stdio
Extension named external-context-mem0. The Extension implements External
Context MCP Profile v1 and translates the fixed context_search({ query })
contract through an administrator-owned, versioned dialect file.
Qwen does not publish, select, or maintain provider presets. The instance file
binds one endpoint, scope, credential environment-variable name, timeout, and
absolute dialect path. The dialect file describes request and response
differences within the existing closed DialectV1 grammar. Its id is an
administrator-managed audit label and has no registry or file-name semantics.
External Context MCP Profile v1 remains the only public Qwen interoperability
boundary. Qwen Core does not gain a provider registry, public provider SDK,
dynamic module loading, or new third-party cases in its private
ProviderConfig union. The existing direct integration remains available for
compatibility and is not modified by this design.
context_search({ query }) while
endpoint, credential, scope, timeout, and dialect remain administrator-owned.flowchart LR
Q["Qwen Core"] --> P["External Context MCP Profile v1"]
P --> E["external-context-mem0 stdio Extension"]
I["Administrator-owned InstanceConfigV2"] --> E
D["Administrator-owned DialectV1"] --> E
E --> R["Bounded request engine"]
R --> S["Compatible HTTP service"]
The local Extension owns configuration loading and HTTP translation. Qwen sees only the MCP profile. A service that cannot fit the bounded dialect grammar owns a separate MCP implementation instead of expanding the grammar or Qwen Core.
Four independent version axes remain explicit:
schemaVersion: 2.dialectVersion: 1 unchanged.An incompatible instance schema or dialect version fails closed. In
particular, the old schemaVersion: 1 plus preset shape is not retained as a
compatibility branch because the shipped registry never provided a usable
provider.
InstanceConfigV2 supplies deployment-specific binding:
{
"schemaVersion": 2,
"dialectPath": "/etc/qwen/external-context/memory.dialect.json",
"endpoint": {
"origin": "https://memory.example.com",
"basePath": "",
"allowInsecureHttp": false
},
"credentialEnv": "MEMORY_API_KEY",
"scope": {
"userId": "repository-memory"
},
"timeoutMs": 5000
}
QWEN_EXTERNAL_CONTEXT_MEM0_CONFIG is supplied through the Extension process
environment and points to this file by absolute path. It is not a normal Qwen
setting. Managed deployments provide it through a system environment,
controlled launcher, or pinned MCP configuration. Production files should
normally live outside workspaces; a repository-owned file is trusted only when
the repository is inside the deployment's administrative trust boundary.
dialectPath must also be absolute. The Extension does not resolve it relative
to the instance file, interpolate environment variables, load URLs, search
fallback locations, or watch for changes. Both files are capped at 64 KiB and
read once during startup. Changes take effect only after restarting the
Extension.
The endpoint separates origin from basePath so authority validation remains
independent of path joining. Scope values are fixed administrator input, not
tool arguments. Every non-omit scope location in the dialect requires the
matching instance value, while every omit location rejects a supplied value.
Administrators choose fixed scope or omit; there is no optional-scope
grammar.
Credentials are never stored in either file. credentialEnv names the process
environment variable containing the credential, which is read only after both
files and all semantic constraints validate.
The second file conforms to the unchanged DialectV1 schema. This synthetic
example illustrates the contract without naming a provider:
{
"dialectVersion": 1,
"id": "organization-memory-v1",
"auth": "authorization-token",
"search": {
"method": "POST",
"path": "/memories/search",
"queryLocation": "json",
"userIdLocation": "json.filters",
"agentIdLocation": "omit",
"appIdLocation": "omit",
"limitField": "limit"
},
"response": {
"collection": "results",
"idField": "id",
"contentField": "memory",
"titleField": "omit",
"uriField": "omit",
"scoreField": "score",
"updatedAtField": "omit"
}
}
The grammar stays deliberately closed:
authorization-token, authorization-bearer, or
x-api-key.GET or POST with one static exact path.json,
json.filters, query, or omit locations supported by each field.top_k, limit, or omit.results or a root array.threshold and rerank are typed fields, not request fragments.The dialect cannot define arbitrary headers, body interpolation, JSONPath,
code, environment-variable expansion, redirects, or response transformations.
Its id does not have to match its file name or any value in the instance
file. Administrators own its naming and versioning policy.
Startup is deterministic:
QWEN_EXTERNAL_CONTEXT_MEM0_CONFIG from the process environment.InstanceConfigV2.dialectPath, then bounded-read and parse DialectV1.credentialEnv.The process exposes no tool if any step fails. Errors use fixed categories for unavailable or invalid instance configuration, non-absolute dialect paths, unavailable or invalid dialect configuration, and invalid endpoint, path, scope, or dialect semantics. Messages never include the real path, endpoint, query, credential, or upstream response.
query; it cannot select the endpoint, credential,
scope, dialect, timeout, or result limit.allowInsecureHttp opt-in for a trusted private network.The manifest exposes exactly context_search. A dialect cannot enable memory
creation, update, deletion, or Auto Recall. Write protocols require a separate
future profile or Extension because their idempotency, duplication, timeout,
and authorization semantics do not fit the retrieval grammar.
The npm package publishes only the bundled runtime, canonical schemas, Extension manifest, and README. It contains no administrator dialect, provider preset, provider identifier, or provider-specific contract fixture.
The public package name is @qwen-code/external-context-mem0. Its package and
Extension manifest versions follow the Qwen Code release version and are
updated in the same release commit. Administrators can install the latest
release with qwen extensions install @qwen-code/external-context-mem0 or pin
an explicit npm version. Installation never creates an instance file, dialect
file, credential, or ordinary Qwen setting.
The normal release workflow builds the self-contained bundle, checks whether
the exact package version already exists, and publishes with the release's npm
dist-tag and provenance. The package participates in the shared
already-published guard so a partial release cannot be overwritten by a retry.
Because npm requires a package to exist before trusted publishing can be
configured, the publish step remains behind the
NPM_EXTERNAL_CONTEXT_MEM0_TRUSTED_PUBLISHING_ENABLED repository variable
until a maintainer completes the one-time public bootstrap publish and binds
the package to the release.yml workflow in the production-release
environment. The bootstrap should publish the first actual Qwen release that
contains this change, not invent a second version line. Future releases use
trusted publishing and require no npm token.
When a service fits DialectV1, its administrator writes and validates a local
dialect file. When it does not fit, the service owner publishes a separate MCP
Extension implementing External Context MCP Profile v1. Qwen does not add a
built-in provider rollout for either case.
InstanceConfigV2 and DialectV1 files, while preserving the request engine
and profile boundary.There is no Qwen-maintained provider-preset PR3. Administrators own compatible dialect data; incompatible protocols use their own MCP Extension.
Verification covers both canonical schemas; 64 KiB file limits; unavailable, malformed, unsupported, relative, and semantically invalid configurations; credential ordering; synthetic GET and POST request contracts; response normalization; the MCP tool surface; real stdio MCP startup against a local synthetic HTTP service; restart-only reload behavior; package contents; build, typecheck, lint, and tests. No verification contacts a live provider service.