Files
mcp/CHANGELOG.md
T

14 KiB

Changelog — einherjar/mcp

All notable changes to this module are documented here. Format follows Keep a Changelog. This module adheres to Semantic Versioning.


[1.1.2] — 2026-08-08

Patch. Fixes surfaced by an adversarial review plus the CORS wildcard trap.

Added

  • cors.wildcard-noop rule (validate_snippet). Flags mw.CORS(...) with a "*" literal — which the framework now rejects at boot — and points to CORSAllowAll() or explicit origins.

Changed

  • get_scaffold: CORS is now env-gated (CORSAllowAll in local, mw.CORS(origins) elsewhere); composes launcher.Config and health.Config so EINHERJAR_COMPONENT_STOP_TIMEOUT and EINHERJAR_HEALTH_CHECK_TIMEOUT are reachable and appear in .env.example; the health hook uses health.NewHandlerWithConfig(...).ServeHTTP; .env.example no longer emits APP_CORS_ORIGINS=*.
  • wire builtin: documents the CORS convention and recommends check_env's composes selectors.

Fixed

  • web.server-not-appended no longer false-positives on a correctly-appended server; it fires only when server.New is present and .Append is absent.

[1.1.1] — 2026-08-07

Patch. check_env gains struct-level granularity so it can catch dead vars that module granularity cannot.

Added

  • check_env composes input — declare the exact config structs an app composes as module/subpackage/Struct selectors (e.g. web/server/Config, db-postgres/Config, core/logz/Config), alongside or instead of the coarse modules list. A var counts as composed when any listed module or struct declares it.

Changed

  • not-composed now reasons over the composed var set, not just modules. With struct selectors, check_env flags struct-level dead vars — e.g. EINHERJAR_SERVER_CORS_ORIGINS (which lives on web.Config) is now reported dead when the app composes web/server/Config rather than web.Config. Passing coarse modules: ["web"] keeps the previous behavior.

[1.1.0] — 2026-08-07

Minor release. The env-var story is now derived from the framework's real struct tags end to end: a tool to read the truth, .env.example generated from it, and a rule that rejects invented names. Also folds in the scaffold-symbol fixes that were staged as v1.0.1 (never tagged separately). The generated scaffold compiles clean against einherjar v1.0.0.

Added

  • get_config_env tool. Lists the real environment variables a component config reads — name, declaring module/struct/field, required, and default — parsed from the indexed struct tags (internal/envspec). The single source of truth for which EINHERJAR_* vars exist; no hand-maintained list to drift.
  • check_env tool. Checks a .env / .env.example against the framework: flags EINHERJAR_* names that don't exist (e.g. EINHERJAR_PG_DATABASE), required vars missing for the modules an app composes, and vars set for a module it does not compose (dead vars, the way EINHERJAR_SERVER_CORS_ORIGINS is inert unless you compose web.Config).
  • config.unknown-env-var rule (now twelve). validate_snippet rejects any struct field tagged env:"EINHERJAR_*" whose name the framework does not declare — the exact class of drift that shipped EINHERJAR_PG_DATABASE / _SERVER_ADDR. The valid-name set is injected from the index at startup, so the rule can never drift from the real tags.

Changed

  • get_scaffold now derives .env.example from the index instead of a hand-written string. Required vars (no default) are emitted uncommented with a runnable dev value; defaulted vars are emitted commented, documenting the framework default. Names and defaults can no longer drift from the modules the scaffold composes (core/logz + web/server + db-postgres).
  • The scaffold composes logz.Config instead of hard-coding the logger, so EINHERJAR_LOG_LEVEL / _JSON are live and documented rather than silently ignored. Verified end to end: env.Parse loads slog.Level from EINHERJAR_LOG_LEVEL. (Log format is now env-driven; set EINHERJAR_LOG_JSON=true in production.)
  • The wire builtin routes the incremental flow to the new tools. Composing a component later now points at get_config_env("<module>") for its exact vars and check_env to confirm .env.example is complete — and spells out the split between framework EINHERJAR_* (discoverable, name-checked) and app-owned APP_* (your discipline).

Fixed

  • logz.Loggerlogging.Logger. The logger interface is contracts/logging.Logger; logz.New returns it. The scaffold's health hook and the wire builtin's withUsers example typed loggers as the non-existent logz.Logger.
  • postgres.Componentpostgres.Provider in hook signatures. postgres.New returns a Component (a lifecycle component that embeds Provider); hooks and NewUnitOfWork take a Provider.
  • The real env tags throughout: EINHERJAR_SERVER_ADDREINHERJAR_SERVER_HOST + _PORT, and EINHERJAR_PG_DATABASEEINHERJAR_PG_NAME (plus EINHERJAR_PG_SSL_MODE).
  • The scaffold's minimal health hook no longer takes unused logger/db parameters.

[1.0.0] — 2026-08-07

The MCP reaches v1.0.0, aligned with the v1.0.0 framework. The headline is the canonical application scaffold: the one opinionated starting point so an AI no longer hand-rolls main.go and the launcher when creating a service from zero.

Added

  • get_scaffold tool. Returns the canonical minimum application scaffold as ready-to-write files, with import paths filled from a module argument: a clean main.go (godotenv autoload + wire.Run()), internal/wire/wire.go (the launcher assembly), a composed internal/config/config.go, a health feature hook, and .env.example.
  • Three validate_snippet rules (now eleven total), with a rules test suite: main.dirty (the launcher/components built in main instead of internal/wire), main.no-godotenv-autoload (a wire-convention main that never loads .env), and config.raw-getenv (reading a framework EINHERJAR_* var via os.Getenv instead of composing the component's Config type).
  • Config conventions in the wire builtin. A Config section (compose the framework's component configs, load with caarlos0/env, APP_* for app-owned fields, EINHERJAR_* for framework ones) and a Config & .env.example section: every env var the config reads must also be documented in .env.example, kept in lock-step.

Changed

  • wire builtin re-synced to the current gold standards (iron-dough-api, pei-api): main.go now shows the _ "github.com/joho/godotenv/autoload" blank import (previously omitted, so the AI produced a main that never loaded .env); the assembly file is wire.go (was launcher.go).
  • Version alignment. The README badge and serverVersion were stale at v0.1.0; both now read v1.0.0.

Removed

  • Migrations and seeding from the wire builtin. How migrations run and how the first admin is seeded (via code, a DB team, an endpoint, a webhook, …) is the developer's choice — it belongs to no Einherjar module and is not a hard convention, so it is out of the scaffold and the documented conventions.

[0.2.0] — 2026-06-10

Minor release. The indexer now captures the members of composite types, closing a gap where get_symbol and search_symbols could name a struct or interface but not describe its shape — most painfully, struct tags (env-var keys, json names) were invisible.

Added

  • Struct fields in the index. Each struct-type symbol now carries a fields array — field name, type, raw struct tag (backticks stripped), doc comment, and an embedded marker. get_symbol returns it; previously the signature was truncated to the bare type X struct header, so field names, types, and tags (e.g. env:"EINHERJAR_PG_HOST") were dropped entirely.
  • Interface method sets in the index. Each interface-type symbol now carries a methods array — method name, signature (without the leading func), and doc comment, including embedded interfaces. Consumers can now see what a port like db-postgres Provider actually requires.
  • Search by field/tag/method. search_symbols now also matches a query against struct field names, field types, and struct tags, and against interface method names/signatures — so an env-var key or a method name resolves to the type that declares it.
  • internal/index test suite covering field, tag, embedded-field, interface-method, and search-by-tag capture (the package previously had no tests).

Notes

  • The index schema gains two optional (omitempty) fields; SchemaVersion is unchanged because the change is additive and older consumers parse the new index unchanged.
  • This is a pure indexer/schema change. The live server picks it up on its next image build, which re-runs cmd/indexer (see Dockerfile). A deployment that has not been rebuilt will still serve the older, member-less index.

[0.1.1] — 2026-05-29

Patch release. Two changes to cmd/server make the binary cleaner to run behind a unix socket on a reverse-proxied host, plus two repository-hygiene changes that follow from the same deployment exercise.

Added

  • Systemd socket activation in cmd/server. The binary inherits the listener from LISTEN_FDS via github.com/coreos/go-systemd/v22/activation when present, falling back transparently to TCP -addr binding otherwise. Startup log records "mode":"socket-activated" or "mode":"tcp". Same binary, no flag or env var to toggle.

Changed

  • Health probe path moved from /healthz to <path>/healthz (default /mcp/healthz). Lets a reverse proxy expose the entire MCP service through one location prefix. v0.1.0 consumers hitting the old /healthz route receive 404; update to /mcp/healthz (or whatever path matches your -path flag).
  • README.md deployment section rewritten to be hosting-agnostic. Points at the Dockerfile and systemd socket activation as supported binary modes without prescribing one operator's setup. Adds the SSE-buffering caveat once: any reverse proxy must disable response buffering on the /mcp location, otherwise Server-Sent Events get batched and streamable MCP sessions break.
  • /deploy/ is now .gitignored. Local deployment artefacts (systemd units, reverse-proxy templates, per-release scripts) are operator-specific by design and live outside the public repository. The Dockerfile at the module root remains the only portable, public-facing build artefact.

Dependencies

  • Added: github.com/coreos/go-systemd/v22 v22.7.0 — used by cmd/server to detect and use a systemd-passed listener.

Upgrade notes

If you… Action
Consume the MCP service from an MCP client (Claude, Cursor, Zed, etc.) None — /mcp is unchanged
Monitor the service via the healthz probe Update the probe URL from /healthz to /mcp/healthz
Run the binary directly (no reverse proxy) None — -addr TCP binding still works the same way
Run the binary under systemd with a socket unit The same binary now picks up the inherited listener automatically

[0.1.0] — 2026-05-29

Initial release. The mcp module hosts the Einherjar Model Context Protocol server — a remote, streamable-HTTP service that teaches AI assistants about every other module of the framework.

Added

Server (cmd/server)

  • Streamable-HTTP MCP server built on github.com/modelcontextprotocol/go-sdk v1.0.0
  • Listen address and HTTP path configurable via EINHERJAR_MCP_ADDR (default :8080) and EINHERJAR_MCP_PATH (default /mcp)
  • /healthz liveness endpoint
  • Embedded framework index loaded once at startup; in-memory for the lifetime of the process

Indexer (cmd/indexer)

  • Walks an Einherjar repository checkout and produces data/index.json
  • For each sibling module captures: import path, Go version, README (full + extracted tagline), CHANGELOG, root doc.go package comment, sub-package doc comments, every exported symbol (type/interface/func/method/const/var) with signature + godoc, ADRs, README code-fence examples, dependency edges from go.mod, and the contents of compliance_test.go (interface assertions + structural test names)
  • Appends a synthetic wire module documenting canonical Einherjar application wiring conventions

Tools (10)

  • list_modules — enumerate every Einherjar module with purpose and sub-packages
  • get_module — package doc, dependencies, sub-packages, key symbols, ADRs, compliance counts; optional embedded README
  • search_symbols — full-text search across name, doc, sub-package, module
  • get_symbol — full signature, doc, and source location for one symbol
  • list_adrs — list architectural decision records, optionally filtered by module
  • get_adr — fetch one ADR's markdown body
  • get_example — canonical usage snippets extracted from module READMEs and the wire conventions
  • get_compliance — interface assertions and structural test names from a module's compliance_test.go
  • get_changelog — full CHANGELOG.md markdown for one module
  • validate_snippet — pattern-match a Go snippet against framework conventions; returns findings with severity, hint, and line

Validation rules (8)

  • launcher.missing-run, launcher.no-components, launcher.run-error-discarded
  • logz.direct-env-read
  • web.server-not-appended
  • wire.hook-bad-signature, wire.hook-outside-beforestart, wire.route-specific-after-param

Synthetic wire module

  • Authored in internal/index/builtins/README.md; participates in list_modules, get_module, and get_example exactly like a real module
  • Sections: project layout, Run() shape, feature hook shape, route ordering, authorization, middleware helpers, adapters at the wire boundary, migrations and seeds
  • All examples use einherjar import paths

Packaging

  • Multi-stage Dockerfile that builds from the einherjar repository root (docker build -f mcp/Dockerfile .) so the indexer can walk every sibling module at image-build time
  • Distroless runtime image; static binary; non-root user