Skip to content

Releases: basphere/ratatosk-mcp

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 18:29
b2e0d33

Changed

  • The repository moved to the basphere organization, and every path that
    carries the owner name moved with it: the Go module is
    github.com/basphere/ratatosk-mcp, the image is
    ghcr.io/basphere/ratatosk-mcp (also the chart's image.repository
    default), and the official MCP Registry name is
    io.github.basphere/ratatosk-mcp. GitHub redirects the old web and git
    URLs, but none of those three redirect: go get on the old module path
    stays on the last tag published under it, the old image tags stay where they
    are, and the old registry entry becomes a separate server that no longer
    receives updates. Existing installs keep running — they reference tags that
    still exist — but upgrading means pointing at the new paths.

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 23:20

Fixed

  • check_stack refuses a component that carries no project instead of
    scanning every project for it. A model that sent project_slug where the
    schema says project had the key dropped, and the empty name went upstream
    as "no project filter": one call paged all 2,435 changes and returned
    676,771 characters, which the calling model then timed out re-reading. The
    refusal names the keys that arrived and shows the expected shape, so one
    retry fixes it; the same call now gets a 318-byte answer and makes no
    upstream request. maxComponents bounded how many components fan out; this
    bounds each one. Reported by the basphere hub from a live run.

Changed

  • A check_stack briefing shortens quotes to 120 characters with an ellipsis;
    detail:"full" and get_release still carry them verbatim. Measured over a
    five-component stack, quotes were 36% of the entry payload (median 90
    characters, longest 548), so this bounds the tail without reducing the
    typical entry to a fragment. The briefing is where a caller decides what to
    look at; the verbatim quote is one call away, where the decision is made.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 10:04

Changed

  • The agent prompt now says what a tool answer costs, not just whether it is
    useful. A measured run pulled 1.4M characters from one cluster-wide pod
    listing in json — larger than the model's whole context window — because
    the rules called that call useless without calling it expensive, and a
    self-hosted model re-processes every past tool answer on every later turn.
    The prompt now asks for the default (wide) listing, reserves full specs
    for a named resource, starts check_stack at brief, and passes an explicit
    limit to list_changes. Chart and kagent example only — no image change,
    so a helm upgrade from a checkout picks it up without a release.
  • check_stack briefings carry a shorter tail: other_changes is capped at 25
    per component instead of 100, with the overflow still counted in
    other_changes_omitted. action_required and check_config are untouched
    and remain uncapped. A measured five-component briefing was 85,948 characters
    and this tail was most of it, for changes the server had already judged to
    need no decision. Asking callers to narrow with severity_min did not work:
    it went unused in 13 of 13 measured calls across two model fleets, so the
    default had to be the budget.
  • list_changes returns 20 changes when no limit is given, down from the
    upstream default of 50. One measured default call was 104,463 characters.
    Paging is unchanged — a caller syncing a local copy raises limit and follows
    next_since as before.
  • check_stack accepts the argument shapes models actually send: components
    JSON-encoded as a string, and name as an alias for project. Both were
    refused before, and a refusal is not cheap on the other side — a self-hosted
    model spent about two minutes per rejection regenerating arguments, 8 out of 8
    calls in one measured session. Rejections that remain now carry a minimal
    correct example instead of only echoing what arrived.

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 20 Aug 11:03

Hosted usage now counts — one bit upstream, no record of anyone

The hosted endpoint reaches its upstream over an internal network, so upstream metrics could not tell a hosted user's call from the operator's own traffic — hosted adoption always read as zero. This release closes that blind spot.

  • In HTTP mode, each caller is classified as self or external against MCP_SELF_CALLERS (comma-separated addresses; an entry ending in . matches as a prefix), and only that class travels upstream as X-Ratatosk-Caller.
  • The address itself is compared in process and discarded — neither logged nor sent. What leaves the process is the one-bit class, nothing else.
  • Unset — the default, and every self-hosted install — classifies all callers as external. It costs nothing and changes no behavior; upgrading is a version bump. No tool contract changes.

Full Changelog: v0.7.5...v0.8.0

v0.7.5

Choose a tag to compare

@github-actions github-actions released this 16 Aug 07:31

Hardening pass over the HTTP surface

A security review of the server turned up four ways an install could be worn down over time — none of them a data exposure, all of them about staying up. This release closes them. No tool contract changes; upgrading is a version bump.

  • The HTTP server now sets its own timeouts. Go's zero-value server leaves read, write and idle deadlines unset, so a client that dribbles a request header one byte at a time could hold a connection open indefinitely — and do it before the rate limiter ever counted the request.
  • check_stack accepts at most 100 components. A larger list fanned out to full upstream paging per component, so one call could turn into thousands of upstream requests and burn the caller's own rate budget.
  • Idle sessions expire after 30 minutes in stateful HTTP mode. Without a timeout the SDK keeps every session forever, so a long-running install that reconnects often grows in memory. Stateless mode — required by the 2026-07-28 MCP revision, and what the hosted endpoint runs — allocates no session and was never affected.
  • Upstream failures no longer echo the request URL back to the caller. A failed call now answers with the class of failure (upstream request failed (connection_refused)); the address stays in the server's own log.

Full Changelog: v0.7.4...v0.7.5

v0.7.4

Choose a tag to compare

@github-actions github-actions released this 10 Aug 09:50

A version whose line does not exist is a dropped prefix, not an empty answer

0.7.3 stopped comparing across release lines. This is the other half of that change.

Agents do not get versions handed to them — they read them off image tags, and an image tag rarely carries the release-line prefix. knative publishes knative-v1.12.0 while its images say v1.12.0. Pass the bare version and every one of that project's releases lands on the far side of the line gate, so the briefing comes back empty and reads as "nothing to do".

This is not confined to projects that publish several lines. A project with exactly one line has the same problem when that line is a prefixed one. Across the tracked corpus, five projects publish no main-line release at all: cloudevents, flatcar, knative, linkerd and openfeature.

  • check_stack now says so. When the running version's line does not exist for that project, note names the lines that do and includes a real tag from one of them, so the shape to send is shown rather than described.
  • It does not cry wolf. Projects whose main line genuinely exists — containerd, wasmcloud — produce no warning even though they also publish a second line. Verified against the live API in both directions.

If you pass versions read from image tags, this is the release that tells you when that lost something.

Full Changelog: v0.7.3...v0.7.4

v0.7.3

Choose a tag to compare

@github-actions github-actions released this 10 Aug 09:14

A repository can publish more than one thing, and versions across them have no order

containerd tags its Go API module api/v1.11.1 beside the runtime's own v2.2.5. Flatcar ships lts-4081.3.9 and stable-4593.2.4 as parallel channels. OpenFeature publishes core/, flagd/ and flagd-proxy/ from one repository. Each is a separate release line, and asking which of two lines is "newer" is not a hard question — it is a meaningless one.

check_stack was asking it anyway. An operator on containerd v1.7.28 was offered api/v1.11.0 as part of their upgrade path: a Go library, presented as work to do on their container runtime. It parsed, so nothing complained.

  • Only the line your running version belongs to is compared. Everything else is dropped before a key is even computed, and note reports how many and why.
  • The line is read from the tag — the maintainer already wrote it there — rather than kept in a hand-maintained prefix list. That list was itself a bug found the same day: stable- was on it and lts- was not, so Flatcar's entire LTS train parsed to nothing and vanished from every range query.
  • Pass the tag as published, prefix included: flagd/v0.16.1, lts-4081.3.9, api/v1.11.1. A bare v0.16.1 is read as the main line and will not find the flagd releases.
  • Projects that prefix every tag alike are unaffected. knative-v…, edge-… and the like resolve to a single line for the whole project, so a same-line comparison behaves exactly as before. Across the tracked corpus, five projects out of seventy-five have more than one line.

This is the counterpart to 0.7.1's branch awareness, and deliberately not an extension of it. A branch is an ordered position within one line, which is why "already fixed at or below the running version" means something there. Lines have no order at all, so they are excluded from candidacy rather than compared and filtered.

Measured against the live API: containerd v1.7.28 drops the two api/ releases and keeps its twenty; openfeature at flagd/v0.15.0 returns the nine flagd releases and excludes ten from the other two products — where before it returned nothing at all, because those tags did not parse.

Full Changelog: v0.7.2...v0.7.3

v0.7.2

Choose a tag to compare

@github-actions github-actions released this 10 Aug 07:45

The per-caller limit now actually engages behind a CDN

0.7.1 introduced a per-caller rate limit for shared endpoints and keyed its buckets on X-Forwarded-For first. That was wrong behind a CDN. With no trusted_proxies configured, the reverse proxy sees a Cloudflare edge as its peer and writes that address into the header — and edges rotate, so a single caller collected a fresh bucket every few requests and the limit never fired. Sixty-two consecutive calls through the hosted endpoint all returned 200 where the sixty-first should have been a 429.

  • CF-Connecting-IP is now read first, then X-Forwarded-For, X-Real-IP, and the peer address. Cloudflare writes that header itself and it cannot be forged from outside it.
  • Whatever proxy sits in front must set one of these and strip whatever the client supplied. A limiter keyed on a caller-controlled value is not a limiter. The chart values and the install guide now say so in all three languages.

Nothing else changed. If you self-host with the limiter off — the default — 0.7.1 and 0.7.2 are identical for you.

Full Changelog: v0.7.1...v0.7.2

v0.7.1

Choose a tag to compare

@github-actions github-actions released this 10 Aug 07:34

A briefing that knows which branch you are on

An operator running containerd 2.2.5 was told to act on two CVEs their own branch had already closed — CVE-2026-46680 in v2.2.4, CVE-2026-47262 in v2.2.5 itself. Both were real fixes, both had been backported across every supported branch on the same day, and check_stack reads only releases newer than the running version, so it saw the v2.3.x occurrences and nothing else. It is the failure get_matter exists to prevent, pointing the other way: not crediting an install with fixes it never received, but demanding work it finished a release ago.

  • A matter already fixed at or below the running version, on the running branch, is no longer reported. How many were excluded, and why, goes into note — nothing disappears without saying so.
  • Branch equality is what makes that safe. A backport to v2.1.9 proves nothing about v2.2.4, which was cut from a different branch and may never have received it. Only the running version's own branch is evidence, so that is the only thing the rule accepts.
  • same_matter_also_addressed_in now names every release on record that carried the matter, not only those inside the returned window. The window starts above the running version, which meant the branch an operator most needed to see was the one systematically missing from it.

Measured against the live API: 2.2.5 now returns no action_required and a note explaining the two exclusions; 2.2.3 correctly reports v2.2.4, its own branch's fix, with the four sibling branches listed; 2.1.8 reports v2.1.9 and excludes three.

Rate limits that hold up when several people share an endpoint

The hosted endpoint reaches the upstream API over an internal network, deliberately bypassing the CDN and reverse proxy so that /v1 paths — which carry project slugs and running versions — never reach an access log. The cost of that design was steeper than intended: with no forwarding header surviving the hop, the upstream could not tell hosted callers apart, so every hosted user in the world shared one bucket of 60 requests a minute. A single fifteen-component check_stack ate a quarter of it, and what a caller saw when the bucket ran dry was not an error but a briefing with fetch failed quietly filling in for components.

  • MCP_RATE_LIMIT_PER_MIN (chart: rateLimitPerMin), off by default. Each caller address gets its own budget of N tool calls a minute; over it, 429 with Retry-After.
  • This lives in the server rather than upstream on purpose. Forwarding the caller address to the API would have fixed the fairness and broken the separation that makes the hosted endpoint what it is — the web tier currently cannot correlate who asked with what was asked, because it never receives the who. A structural guarantee survives a future careless log line; a policy one does not. The address is a bucket key and nothing else: never logged, never sent upstream, dropped when its window expires.
  • Off is the default because the same binary is what people self-host, and a self-hosted server is single-tenant.
  • The upstream per-IP limit is now 1200 requests/minute (was 60). One tool call is not one request, and the documentation now states the two limits in their own units instead of implying they are the same number.

Also

  • The install rehearsal extracted the tool list from main.go with a pattern that assumed one space after Name:. gofmt aligns struct fields, so the pattern matched nothing and the script exited before printing — an install failure in the logs, a stale regex in fact. It now tolerates alignment and fails loudly on an empty list.

Full Changelog: v0.7.0...v0.7.1

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 10 Aug 06:09

Release notes, read as changes rather than facts

Ratatosk split its analysis pipeline: what can be decided mechanically is now done mechanically, and the model is asked only for what actually needs judgment. The unit that comes out the other side is no longer a fact carrying a severity — it is a change, described by three axes that answer three different questions. This release moves the whole server, chart, examples and documentation onto that model.

Upgrading: two tools were renamed. facts_by_entity is now changes_by_entity, and list_facts is now list_changes. A client naming the old ones will not find them, and any allow-list that enumerates tools — the kagent toolNames field, for one — needs the new names plus get_matter.

  • Three axes on every change. family (security / breaking / deprecated) says what kind of thing it is. bucket (action / check / plan) says how to act on it now. applies_if says whether it is yours to act on at all, structured into named targets wherever the server could parse it — so an agent greps the running configuration for ["CreateContainer", "sandbox"] instead of interpreting an English sentence.
  • get_matter is new. One matter across every release branch that addressed it, oldest first. This exists because the branches do not carry the same fixes: one containerd roll-up landed on five branches carrying ten, ten, four, four and two advisory ids respectively. An operator on the two-advisory branch, told only about the newest entry, would credit their install with eight fixes it never received.
  • check_stack says what it actually does. The briefing has always split on bucket, but its own hint — the text an agent reads to decide what to do — still described the split as "critical/high". It now states the rule it follows, and says plainly that severity in a briefing is derived by this server from the entry's advisories: useful for ranking urgency, never for deciding who is affected.
  • The kagent agent was configured for tools that no longer exist. Its toolNames still listed facts_by_entity and list_facts, which meant three tools that do exist were never exposed to it. Its system prompt also instructed the model about retired response fields (fixed_in, coverage, mandatory). Both are corrected in the chart template and the example manifest; the triage procedure itself — the measured rules about reclassification, unresolved conditions and roster coverage — is unchanged.
  • Documentation rewritten from live responses. The tools reference (en/ko/ja) now covers all seven tools with responses captured from the running API, including the parts that are easy to misread: changes: [] is an audited silence that ships the original note with it, max_severity: null means "no advisory ids" rather than "nothing serious", and seq is an analysis cursor, not a timeline.

Full Changelog: v0.6.2...v0.7.0