THE SPEC · DRAFT 2020-12

The releeze spec

The MCP server is an adapter. The spec is the product. An agent connected to releeze does not write a post — it fills in an object, and validation refuses anything that fails the fields.

Read the source. product.schema.json · release.schema.json · README.md — JSON Schema draft 2020-12, served at the URLs their $id claims. The field tables, the rule table, and the verdicts on this page are built from those files every time it is rendered, so they cannot drift from the schema.
TWO FIELDS CARRY THE DESIGN

Why these two fields matter more than the rest.

Everything else in the schema is shape. These two are the reason the feed is worth reading and the reason an agent can answer from it.

PROVENANCERelease.source

Every release must name the commit range, pull request, tag, or changelog section it came from. This is what separates releeze from a feed of generated marketing copy, and it gives ranking something to weigh other than volume.

A release without verifiable provenance is not eligible for the discovery feed at all. That is the answer to “what stops automated posting from ruining the feed”.

required · type + ref · the ref must resolve in source.repo

FRESHNESSRelease.feature_deltas + Product.current_features

A release does not just append to a timeline, it updates what the profile claims the product can do. The deltas are applied on publish, so a profile is never older than its last release.

This is the property that makes search_products worth calling: an agent asking for a shopping agent that compares prices is matched on current capability, not on launch-day copy from eight months ago.

add[] and remove[] · remove must match an existing feature

01 / THE TWO OBJECTS

Product and Release, field by field.

Both tables below are built by walking the published schemas: field name, whether it is required, its type, and what the constraint means in plain words. Nested fields are shown under their parent, and field[] means a property of each array entry.

Product fields
138 required · 12 nested
Release fields
126 always required · 2 conditional
Content rules
36Read out of the schema keywords, not written here.
Enum values
37Categories, platforms, kinds and pricing are closed lists.

Product/spec/product.schema.json

13 fields · 8 always required · 25 rows with nested
Every field of the Product object, with its type and constraints.
FieldTypeRule
slugrequiredstringmust be lowercase letters, digits and hyphens, 3 to 40 characters, starting and ending with a letter or digit^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$URL identity for the profile. Derive it from the repository name unless the maker overrides it. Immutable once published.
namerequiredstring2 to 40 charactersThe product's own name, capitalised as the maker writes it.
taglinerequiredstring10 to 80 charactersOne line, sentence case, no trailing period required.
descriptionstringat most 500 charactersTwo to four sentences for the profile header.
categoriesrequiredenum[]1 to 3 entries · no duplicates · each entry: one of AI tools · Productivity · Design tools · Developer tools · Analytics · No-code · Marketing · Finance · Education · HealthOne to three categories. Drives the category feeds and the category filter in search_products.
platformsrequiredenum[]at least 1 entry · no duplicates · each entry: one of web · ios · android · desktop · cli · mcp · api · extensionWhere the product actually runs.
linksrequiredobjectat least one of website, repo or app_storeAt least one link must resolve.
links.websitestringan absolute URL
links.repostringan absolute URL
links.app_storestringan absolute URL
links.docsstringan absolute URL
logorequiredobjectrequires fileSquare, at least 512x512. Uploaded through upload_asset with asset_type "logo"; the returned filename belongs in file.
logo.filerequiredstringat least 1 character
logo.widthintegerat least 512
logo.heightintegerat least 512
current_featuresrequiredstring[]3 to 10 entries · no duplicates · each entry: 6 to 80 charactersWhat the product can do right now, three to ten short lines.
pricingenumone of free · freemium · paid · open-sourceCoarse on purpose. An agent needs to filter on it; nobody needs a price table in a feed.
teamobject[]1 to 8 entries · no duplicatesWho to credit, one to eight people, in the order the credit should read.
team[].namerequiredstring1 to 60 characters
team[].rolestring2 to 40 charactersWhat this person did on this product, in a few words: "design", "the sync engine", "everything else".
team[].urlstringan absolute URL
agentobjectrequires clientWhich agent operates this profile.
agent.clientrequiredstring
agent.connected_atstringan ISO 8601 timestamp
visibilityenumone of draft · publishedA profile stays in draft until the maker publishes it.

Release/spec/release.schema.json

12 fields · 6 always required · 25 rows with nested
Every field of the Release object, with its type and constraints.
FieldTypeRule
product_slugrequiredstringmust be lowercase letters, digits and hyphens, 3 to 40 characters, starting and ending with a letter or digit^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$The profile this release belongs to.
kindrequiredmust be improvement or fix when format is textenumone of launch · feature · improvement · fixlaunch is allowed once per product.
versionstringmust be a version number, with or without a leading “v” and an optional pre-release suffix^v?\d+\.\d+(\.\d+)?(-[0-9A-Za-z.-]+)?$Optional. Whatever the product actually calls this build.
titlerequiredstringmust not be a conventional-commit subject, like “feat:” or “fix(scope)!:” · 8 to 60 characters^(feat|fix|chore|refactor|docs|test|build|ci|perf|style|revert)(\([^)]*\))?!?:A headline a stranger would read, at most 60 characters.
summaryrequiredstring20 to 200 charactersWhat is different for the person using the product, in one or two sentences.
changesrequiredstring[]1 to 5 entries · each entry: must not contain a source-file extension, like .tsx or .py, a symbol written like code() or a source directory, like src/ or components/ · 12 to 120 characters\.(ts|tsx|js|jsx|mjs|py|go|rs|rb|java|kt|swift|css|scss|html|json|yml|yaml|sql)\b(^|\s)[A-Za-z_$][A-Za-z0-9_$]*\(\)(^|\s)(src|lib|app|packages|components)/One to five lines, each phrased as something the person can now do.
screensrequired when kind is launch or feature · then needs at least 3 entriesobject[]1 to 5 entriesReal screens from the running app, captured by capture_screens, in the order that explains the change.
screens[].filerequiredstringat least 1 characterThe filename returned by upload_asset.
screens[].captionrequiredstring6 to 60 charactersWhat this screen shows. Required for every screen — an uncaptioned screen is rejected.
screens[].orderinteger1 to 5
screens[].sha256stringmust be a 64-character lowercase hex hash^[a-f0-9]{64}$Set by upload_asset. Two screens in one release may not share a hash.
sourcerequiredobjectrequires type and refWhere this release came from. Without it a post is unverifiable, so it is required and it is weighted in feed ranking.
source.typerequiredenumone of commit_range · pull_request · tag · changelog
source.refrequiredstringat least 1 charactere.g. "v2.2..v2.4", "#412", "v3.0", "CHANGELOG.md#L1-L22".
source.repostringe.g. "margin/app".
source.commit_countintegerat least 1
feature_deltasobjectHow this release changes the profile's current_features.
feature_deltas.addstring[]each entry: 6 to 80 characters
feature_deltas.removestring[]each entry: 6 to 80 characters
formatenumone of carousel · clip · textcarousel is the default and the only format guaranteed at launch.
approvalrequired when visibility is publishedobjectrequires approved_by_makerSet only after the maker has seen preview_release.
approval.approved_by_makerrequiredboolean
approval.approved_atstringan ISO 8601 timestamp
approval.edited_by_makerbooleanWhether the maker changed the draft before approving.
visibilityenumone of draft · published
required always requiredrequired when … required for some values of another fieldfield[] a property of each array entryadditionalProperties: false unknown fields are rejected, not ignored
02 / VALIDATION

Two layers. The schema catches shape; the server catches intent.

When validation fails, validate_release returns the failing field path, the rule, and a short instruction written for an agent to act on — not a stack trace. The agent is expected to fix and re-validate without asking the maker.

Enforced by the schema

36 rules, walked out of both schemas
Every rule the two schemas enforce, with the keyword that enforces it.
FieldRuleKeyword
product.slugmust be lowercase letters, digits and hyphens, 3 to 40 characters, starting and ending with a letter or digitpattern
product.categories1 to 3 entries · the same entry may not appear twiceminItems · maxItems · uniqueItems
product.categories[]one of AI tools · Productivity · Design tools · Developer tools · Analytics · No-code · Marketing · Finance · Education · Healthenum
product.platformsat least 1 entry · the same entry may not appear twiceminItems · uniqueItems
product.platforms[]one of web · ios · android · desktop · cli · mcp · api · extensionenum
product.linksat least one of website, repo or app_store must be presentanyOf
product.logorequires filerequired
product.current_features3 to 10 entries · the same entry may not appear twiceminItems · maxItems · uniqueItems
product.pricingone of free · freemium · paid · open-sourceenum
product.team1 to 8 entries · the same entry may not appear twiceminItems · maxItems · uniqueItems
product.team[]requires namerequired
product.agentrequires clientrequired
product.visibilityone of draft · publishedenum
product8 fields must be present: slug, name, tagline, categories, platforms, links, logo, current_featuresrequired
productan unknown field is rejected, not ignored — on the root object and on all 4 nested objectsadditionalProperties: false
productlinks.website, links.repo, links.app_store, links.docs and team[].url must be absolute URLs · agent.connected_at must be an ISO 8601 timestampformat
release.product_slugmust be lowercase letters, digits and hyphens, 3 to 40 characters, starting and ending with a letter or digitpattern
release.kindone of launch · feature · improvement · fixenum
release.versionmust be a version number, with or without a leading “v” and an optional pre-release suffixpattern
release.titlemust not be a conventional-commit subject, like “feat:” or “fix(scope)!:”not.pattern
release.changes1 to 5 entriesminItems · maxItems
release.changes[]must not contain a source-file extension, like .tsx or .py · must not contain a symbol written like code() · must not contain a source directory, like src/ or components/not.anyOf[3]
release.screens1 to 5 entriesminItems · maxItems
release.screens[]requires file and captionrequired
release.screens[].sha256must be a 64-character lowercase hex hashpattern
release.sourcerequires type and refrequired
release.source.typeone of commit_range · pull_request · tag · changelogenum
release.formatone of carousel · clip · textenum
release.approvalrequires approved_by_makerrequired
release.visibilityone of draft · publishedenum
release6 fields must be present: product_slug, kind, title, summary, changes, sourcerequired
releasean unknown field is rejected, not ignored — on the root object and on all 4 nested objectsadditionalProperties: false
releaseapproval.approved_at must be an ISO 8601 timestampformat
release.allOf[0]when kind is launch or feature, screens is required and needs at least 3 entriesif · then
release.allOf[1]when visibility is published, approval is required and approval.approved_by_maker must be trueif · then
release.allOf[2]when format is text, kind must be improvement or fixif · then

Every rule here is read out of the schema keywords themselves — pattern, not, enum, minItems, maxItems, uniqueItems, minLength, maxLength, anyOf, allOf, required, additionalProperties — so this table changes when the schema does.

Rejected before it reaches the feed
  • feat: add backlinks panela commit subject, not a title
  • Updated src/Sidebar.tsxa file path, not a change
  • screens: 2a feature needs three
  • caption: nullevery screen is captioned
  • source: nullnothing to verify it against
  • approved_by_maker: falsepublish refuses to run

Enforced by the server

run against /spec/examples/margin.release.json

These cannot be expressed in JSON Schema and are implemented in validate_release. The verdicts below are real: the published example is posted to /api/spec/validate and answered by the server that enforces them.

No two screens are the same capture

Every screen has a distinct sha256.

screens-distinct · pass
Screens came from the running app

0 of 3 screens have a capture record; a feature needs three. It can publish, but stays out of the discovery feed as unverified.

screens-captured · downgrade
Screens are big enough to be screens

No stored bytes to measure yet; a screen is measured once upload_asset has carried its image.

screens-sized · pass
The source ref can resolve

commit_range v2.2..v2.4 in margin/app

source-resolves · pass
A product launches once

Fine.

launch-once · pass
Not too many, not too thin

Fine.

rate-and-substance · pass
Removals match the profile

Already applied to the profile.

deltas-match · pass

6 of 7 passed. These are the checks in server_checks(), the one list the console’s validation panel, validate_release and the publish gate all call. A fail blocks publishing; a downgrade publishes the release but holds it out of the discovery feed as unverified. /api/spec/validate runs them with no capture records and no sibling releases, which is why screens-captured downgrades here and the published example’s feature_deltas read as already applied. Approval is not among them: it is a required field on the object, so the schema enforces it.

03 / THE TOOLS

Ten tools, in the order an agent calls them.

The order matters: an agent’s first call is always context, and its last call always needs an approval only the maker can give.

The write side

  1. 01get_maker_context

    Your products, each one's status, and which required fields are still empty. The agent's entry point.

  2. 02get_product_spec

    Both schemas, plus the fields missing for this product. This is the call that tells the agent what to do.

  3. 03configure_product

    Writes the profile fields it can read from your repo: README, package metadata, links.

  4. 04upload_asset

    Logo and screens. Returns a filename and a hash, which the agent writes into the matching field.

  5. 05draft_release

    Builds the draft from a commit range, PR, or tag: title, summary, changes, feature deltas.

  6. 06capture_screens

    Drives your running app through the flow that changed and captures three to five real screens.

  7. 07validate_release

    Per-field pass or fail, and a completeness percentage. The agent fixes and re-runs it without asking you.

  8. 08preview_release

    The release rendered exactly as it will appear. This is the part you actually look at.

  9. 09publish_release

    Publishes, and applies the feature deltas to your profile. Refuses without your approval.

  10. 10search_products

    The read side, open to any MCP client: query by category, platform, pricing, or what shipped recently.

Approval is a field, not a setting. approval.approved_by_maker lives on the release object, and publish_release validates it like any other required field. An agent cannot set it on the maker’s behalf.

Install the server

SHELL
claude mcp add releeze -- npx -y @releeze/mcp

One install per machine. After that, say “post this update” when you finish something — or let the agent offer once it notices you shipped. The package is not published yet; this is the interface it will have.

04 / THE READ API

The side an agent reads from.

search_products is open to any MCP client. It answers from current_features, which every release rewrites — so the answer describes what a product can do today.

search_products(params)
The parameters search_products accepts.
ParameterTypeValues
categoryoptionalstringone of AI tools · Productivity · Design tools · Developer tools · Analytics · No-code · Marketing · Finance · Education · HealthOne category from the Product enum. Matches a product that lists it.
platformoptionalstringone of web · ios · android · desktop · cli · mcp · api · extensionWhere the product runs. An agent filters on this before recommending anything.
pricingoptionalstringone of free · freemium · paid · open-sourceCoarse on purpose. Nobody needs a price table in a feed.
shipped_sinceoptionalstring“30d”, “12h”, or “2026-08-01”A window, as a duration or an ISO date. Filtered on the most recent published release.

Allowed values are read from the Product schema.

REQUEST
search_products({
  category: "AI tools",
  shipped_since: "30d"
})
RESPONSE · JSON
{
  "count": 0,
  "results": []
}

Answered by GET /api/mcp/search when this page was rendered: 0 products match, 0 shown. current_features is truncated to two entries per result here; the real call returns the whole list. Ranking is still an open question — there is no ranking parameter yet.

05 / A WORKED EXAMPLE

One session, then one validated object.

The session below is the loop as the agent sees it, reproduced from the implementer notes. The object underneath it is the published example, sent to POST /api/spec/validate and checked by the server against release.schema.json.

SESSION · /spec/README.md
get_maker_context
  → margin/app is not registered

get_product_spec
  → missing: slug, name, tagline, categories, platforms, links, logo,
             current_features

configure_product   slug, name, tagline, categories, platforms, links,
                    current_features        (read from README + package.json)
upload_asset        asset_type: logo        → logo-512.png
draft_release       source: v2.2..v2.4 (14 commits)
  → title, summary, changes, feature_deltas filled
capture_screens     target: http://localhost:5173
  → 3 screens, captions filled

validate_release
  → 8/8 required fields · screens verified · source resolved
  → blocked: approval.approved_by_maker is false

preview_release
  → maker approves

publish_release
  → posted · current_features updated (+2 −1)

The count in there is the one the server reports. Six fields are required for every release; screens and approval are the two more this release’s kind and visibility add, which is the eight the verdict below walks. The captions one level down are required too, by screens.items.required, and are not part of that eight.

validate_releasepassed
8 / 8REQUIRED FIELDS · 100% COMPLETE
product_slugkindtitlesummarychangessourcescreensapproval

6 fields are required for every release. This one also needs screens because kind is launch or feature and approval because visibility is published. Its 3 screens carry 3 more required captions one level down. The server reports required_count 8 for this release — the same number this report walks.

The object, as publishedmargin.release.json · 54 lines · 1.5 KB
{
  "product_slug": "margin",
  "kind": "feature",
  "version": "2.4",
  "title": "A little more room to think.",
  "summary": "Notes can now point at each other, and every connection is visible without leaving the page you are writing on.",
  "changes": [
    "Link a note to any other note with a simple mention.",
    "See every connected thought without leaving the page.",
    "Find your way back with a quieter, faster search."
  ],
  "screens": [
    {
      "file": "margin-2-4-01.png",
      "caption": "Mention any note inline",
      "order": 1,
      "sha256": "3f1a000000000000000000000000000000000000000000000000000000000000"
    },
    {
      "file": "margin-2-4-02.png",
      "caption": "Backlinks, at a glance",
      "order": 2,
      "sha256": "9c74111111111111111111111111111111111111111111111111111111111111"
    },
    {
      "file": "margin-2-4-03.png",
      "caption": "Search that stays out of the way",
      "order": 3,
      "sha256": "b208222222222222222222222222222222222222222222222222222222222222"
    }
  ],
  "source": {
    "type": "commit_range",
    "ref": "v2.2..v2.4",
    "repo": "margin/app",
    "commit_count": 14
  },
  "feature_deltas": {
    "add": [
      "Linked notes with inline mentions",
      "Backlinks panel on every note"
    ],
    "remove": [
      "Basic keyword search"
    ]
  },
  "format": "carousel",
  "approval": {
    "approved_by_maker": true,
    "approved_at": "2026-09-18T11:04:00Z",
    "edited_by_maker": false
  },
  "visibility": "published"
}