3ngram 1.3.0
3ngram v1.3.0
Minor release: the MCP surface catches up with the 2026-07-28 protocol. Two
specification MUSTs are closed, two protocol primitives the server had never
served are now served — resources and completions — and clients finally receive
enough description to use the tool surface well rather than guess at it.
There is one new REST endpoint, GET /api/v1/version, so deploy tooling can
tell a finished rollout from one still in flight.
Protocol compliance
-
server/discovernow advertises a cache hint — the same one-hour
cacheScope: publicthe tool and prompt catalogs carry. The spec requires
hints on every cacheable result, and discovery is one of them. This was not a
case of the field being merely absent: the SDK's 2026 codec fills unconfigured
hints from its own defaults (ttlMs: 0,cacheScope: private), so discovery
was actively advertising itself as immediately stale and clients re-probed on
every reconnect. -
/mcpnow validates theOriginheader. A request with noOriginis
allowed — the spec's requirement is conditional on the header being present,
and no non-browser client sends one — while a presentOriginmust appear in
an allowlist or the request is refused403ahead of authentication and rate
limiting.Hostis deliberately not validated; see Upgrading below, because
this one has a configuration consequence.
New protocol surface
-
Memory bodies are readable as resources:
threengram://memory/{id}, via
resources/templates/listandresources/read.resources/readis cacheable
on 2026-07-28, so a client that pulled atruncated: truesearch hit can cache
the full body instead of re-callingget_memoriesevery session.The body carries only fields that never change after a write — content, topic,
type, scope, project,recordedAt— and deliberately omits lifecycle state
(status, validity, commitment status, tags). That omission is what makes a
24-hourcacheScope: privateTTL sound: nothing in a cached body can go stale,
because supersession mints a new memory with a new id rather than editing the
old one. Lifecycle questions stay on the tool calls, which are never cacheable.Reads enforce the same tenant, read-scope, and access guards as the read tools,
and an id belonging to another tenant is reported identically to one that does
not exist.resources/listreturns nothing by design — enumerating a tenant's
corpus is the firehose the no-firehose rule exists to prevent. -
Argument completion (
completion/complete) over the tenant's own scope
names, so a client can offer real values instead of making the user recall
them. It is an adapter over the existing facets read and carries the same
guards: tenant from verified auth rather than the request,memory:read
enforced fail-closed, access gate ahead of the read. A caller that may not read
completes to an empty list rather than an error.
Describing the server to clients
-
server/discovercarriesinstructions— a short usage policy telling the
model to open withbriefing, to search before asserting something is unknown,
that memory is append-only, and that scope and project decide what later reads
return. Until now the only guidance an agent received was eleven individual
tool descriptions with no cross-tool framing. -
Every tool declares annotations (
readOnlyHint,destructiveHint,
idempotentHint,openWorldHint), so a client can auto-approve a read like
searchinstead of giving it the same friction as a write.destructiveHint
isfalseon every memory write, which is accurate rather than optimistic:
append-and-supersede never destroys memory data.
REST
-
GET /api/v1/versionreturns the running server's package version.
Nothing previously exposed build identity over HTTP —/healthreports
liveness and/readyreadiness, but neither says which build answered, so a
post-deploy probe could verify the previous build and report success.Authenticated like every
/api/v1route, so the exact version is never
disclosed on an unauthenticated surface, but deliberately ungated: the response
is the server's own build identity, not tenant data, and a deploy probe must
stay answerable when the access gate itself is broken.
Upgrading
One configuration change is worth acting on before upgrading a browser
client. Origin validation is fail-closed: with neither WEB_APP_URL nor the
new optional MCP_ALLOWED_ORIGINS configured, the allowlist is empty and any
request carrying an Origin header is refused 403.
This is safe for normal use — Claude Desktop, the CLI, and agent runtimes send no
Origin at all and are unaffected — but a browser-based client needs its origin
listed. WEB_APP_URL is allowlisted automatically; set MCP_ALLOWED_ORIGINS
(comma-separated) for any additional origins. For local development against MCP
Inspector, that is MCP_ALLOWED_ORIGINS=http://localhost:6274.
No migrations. No other configuration changes.
Library versions
@3ngram/core 0.8.6, @3ngram/db 0.7.5, @3ngram/schema 0.6.4,
@3ngram/config 0.2.6, @3ngram/llm 0.2.4.
Immutable container reference
ghcr.io/b3dmar/3ngram@sha256:d63ee2dcd740d0d660bec6e4734260ce6b335056308c71a9ea43138bf5f84568