MSR JSON manifest

Every Craft application describes itself to software registries and AI agents with an MSR JSON manifest, served natively at:

https://<your-domain>/.well-known/msr.json

MSR JSON is an open, vendor-neutral metadata protocol for software, APIs, AI agents and MCP servers. Registries such as mysoftrank.com, package managers and AI discovery engines read that file instead of scraping a product page, so publishing it is the listing. There is nothing to install and no route to write: the framework mounts the endpoint for you.

What the framework does

| Concern | Behavior | |---|---| | Route | GET/HEAD /.well-known/msr.json, mounted outside the middleware stack like the health probes | | Headers | application/json, Access-Control-Allow-Origin: *, Cache-Control: public, max-age=300 | | Protocol block | Constant MSR JSON 2.0 block; canonical_url derived from your domain | | Release | [project].version from pyproject.toml, date from its heading in CHANGELOG.md | | Descriptions | Translation keys msr.entity.tagline, msr.entity.summary, msr.entity.text, one entry per locale | | Unknown values | Omitted, never guessed | | Incomplete manifest | 404 and a msr_manifest_incomplete log line naming the field |

A manifest is a trust artifact: registries act on it without a human checking. That is why a required field that cannot be resolved turns the endpoint off instead of publishing a placeholder. The most common case is development: a localhost URL is not a public domain, so the endpoint answers 404 until MSR_DOMAIN (or APP_URL) names one.

Configuring it

Everything lives in config/msr.py, driven by environment variables. Fill in what only you know; leave the rest empty.

MSR_DOMAIN=shop.example.com
MSR_TYPE=saas
MSR_DEPLOYMENT=cloud
MSR_LICENSE_TYPE=proprietary
MSR_LICENSE_TERMS=subscription
MSR_VENDOR_NAME="Example Ltd"
MSR_VENDOR_COUNTRY=BR
MSR_SUPPORT_URL=https://shop.example.com/support
MSR_PRICING_MODEL=subscription
MSR_INTEGRATIONS=stripe,postgresql

The accepted values of MSR_TYPE, MSR_DEPLOYMENT, MSR_LICENSE_TERMS and MSR_PRICING_MODEL are enums of the schema. commercial_terms and the pricing model are different enums: usage is valid only as a pricing model. MSR_VENDOR_COUNTRY is ISO 3166-1 alpha-2 in upper case, where the vendor is based.

The release is read from the project by default. Override it with MSR_VERSION, MSR_PUBLISHED_AT (RFC 3339 with a timezone, 2026-09-16T00:00:00Z), MSR_RELEASE_TYPE and MSR_CHANGELOG_URL.

Descriptions are translations

The descriptions are user-facing copy, so they live in the translation store like every other string, in every locale listed in LOCALES. A locale without a msr.entity.summary is left out of the manifest. The skeleton ships generic copy in en, pt-BR and es; replace it with your product's own through the translation admin or a migration.

Checking it

python dev.py msr:show       # print the manifest the endpoint serves
python dev.py msr:validate   # validate it against the canonical schema

msr:validate fetches https://msrjson.org/schemas/msr-2.0.json on every run and validates with jsonschema (pip install craft[msr], included in the dev extra). The schema is never copied into the project: a hand-kept copy drifts from the published one while claiming the same version.

Writing it by hand

A file at public/.well-known/msr.json wins over the generated manifest and is served verbatim, with the same headers. Use it only when the configuration cannot express what you need, and validate it with the command from the specification's agent guide.

Turning it off

MSR_ENABLED=false

An application route on /.well-known/msr.json also takes precedence over the built-in one.