-
Release v0.2.0 Stable
released this
2026-06-10 10:39:12 -06:00 | 0 commits to main since this releasev0.2.0
code.nochebuena.dev/einherjar/mcpMinor release. The framework indexer now captures the members of composite
types — struct fields (with their tags) and interface method sets — closing a
gap whereget_symbolandsearch_symbolscould name a type but not describe
its shape. Additive and backward-compatible: no breaking changes.
Changes
internal/index— schemaSymbolgainsfieldsandmethods. A struct-type symbol now carries
afieldsarray (field name, type, raw struct tag with backticks
stripped, doc comment, and anembeddedflag); an interface-type symbol now
carries amethodsarray (method name, signature without the leading
func, doc comment), embedded interfaces included. Both areomitemptyand
absent for every other kind.SchemaVersionis unchanged. The two additions are additive and
omitempty, so a consumer built against the v0.1.x index parses the new
index without modification.
internal/index— builder- The builder previously truncated every declaration at the first
{, so the
index stored only the one-linetype X struct/type X interfaceheader
and dropped all field and method detail — including struct tags such as
env:"EINHERJAR_PG_HOST". It now inspects each type'sTypeSpecand fills
the newSymbolmembers via a non-truncating printer, alongside the existing
one-lineSignature.
Tools
get_symbolnow returns a struct's fields (with tags) and an interface's
method set in addition to the signature, doc, and source location.search_symbolsnow also matches the query against struct field names,
field types, and struct tags, and against interface method names and
signatures — so an env-var key or a method name resolves to the type that
declares it.
Tests
- New
internal/index/builder_test.go(the package had no tests before)
covers field/tag/embedded-field capture, interface-method and
embedded-interface capture, and discovery of a struct by one of its tags.
Docs
CHANGELOG.mdandREADME.mdupdated to describe the richerget_symbol
output and the broadenedsearch_symbolsmatching.
Upgrade Notes
If you… Action Consume the MCP service from an MCP client (Claude, Cursor, Zed, etc.) None — responses are a strict superset; existing fields are unchanged, fields/methodsare new and optional.Parse the index JSON directly None required — the schema version is unchanged and the new keys are omitemptyadditions. Readfields/methodsto use them.Operate / deploy the service Rebuild and redeploy the image. The index is regenerated at image build ( Dockerfilerunscmd/indexer); a running v0.1.x image keeps serving the older, member-less index until rebuilt. This redeploy also refreshes any index that had drifted behind the framework source.
API
No change to the count of the public surface: the 10 tools and 8 validation
rules from v0.1.0 remain. What changed is additive —get_symbolreturns
struct fields and interface methods,search_symbolsmatches them, and the
index schema gains twoomitemptyfields onSymbol. The schema version
string is unchanged.
Install
go install code.nochebuena.dev/einherjar/mcp/cmd/server@v0.2.0
Dependencies
Module Version Status Role github.com/modelcontextprotocol/go-sdkv1.0.0 unchanged from v0.1.1 Official Go MCP SDK — server, streamable-HTTP transport, tool registration github.com/coreos/go-systemd/v22v22.7.0 unchanged from v0.1.1 Inherit listener from systemd via LISTEN_FDS; loaded only bycmd/servergithub.com/google/jsonschema-gov0.3.0 unchanged (indirect) JSON Schema generation for tool input/output validation github.com/yosida95/uritemplate/v3v3.0.2 unchanged (indirect) RFC 6570 URI template support used by the SDK No dependencies added, removed, or upgraded in this release.
Downloads
-
Release v0.1.1 Stable
released this
2026-05-29 14:09:46 -06:00 | 1 commits to main since this releasev0.1.1
code.nochebuena.dev/einherjar/mcpPatch release. Two changes to
cmd/servermake 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.
Changes
cmd/server-
Systemd socket activation. The binary now inherits the listener from
systemd'sLISTEN_FDSprotocol via
github.com/coreos/go-systemd/v22/activation
when present, falling back transparently to TCP-addrbinding when not.
The startup log records"mode":"socket-activated"or"mode":"tcp"so
operators can confirm which path is live. Same binary, same flags, both
modes — no env var or flag toggles behaviour. -
Health probe moves under
<path>/healthz. The handler is now
registered relative to the-pathflag. With the default-path /mcp,
the probe URL becomes/mcp/healthz. The legacy root-level/healthz
route is no longer registered. This lets a reverse proxy expose the entire
MCP service through one location prefix instead of needing a second
forward for the probe.
Docs
- Deployment section in
README.mdrewritten to be hosting-agnostic.
The v0.1.0 draft prescribed a specific systemd layout; the new text
points at theDockerfileand at systemd socket activation as a supported
binary mode without dictating one operator's setup. Adds an explicit note
that any reverse proxy must disable response buffering on the/mcp
location (streamable MCP delivers tool results via Server-Sent Events;
default nginx, Envoy, or Caddy buffering breaks the stream).
Repository hygiene
/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. TheDockerfileat the
module root remains the only portable, public-facing build artefact.
Upgrade Notes
If you… Action Consume the MCP service from an MCP client (Claude, Cursor, Zed, etc.) None — the /mcpendpoint is unchanged.Monitor the service via the healthz probe Update the probe URL from /healthzto/mcp/healthz(or<your -path>/healthzif you have customised the path).Run the binary directly (no reverse proxy) None — the legacy -addrflag still binds a TCP listener; activation only kicks in when systemd has passed a listener.Run the binary under systemd with a socket unit The same binary now inherits the systemd listener automatically; no recompile, no flags.
API
No tool surface, no validation rules, no index schema, and no behaviour of
the indexer changed. The 10 tools and 8 validation rules listed in
v0.1.0's release notes remain the public surface. The only
externally-visible URL change is the move of/healthzto
<path>/healthz.
Install
go install code.nochebuena.dev/einherjar/mcp/cmd/server@v0.1.1
Dependencies
Module Version Status Role github.com/modelcontextprotocol/go-sdkv1.0.0 unchanged from v0.1.0 Official Go MCP SDK — server, streamable-HTTP transport, tool registration github.com/coreos/go-systemd/v22v22.7.0 new in v0.1.1 Inherit listener from systemd via LISTEN_FDS; loaded only bycmd/servergithub.com/google/jsonschema-gov0.3.0 unchanged (indirect) JSON Schema generation for tool input/output validation github.com/yosida95/uritemplate/v3v3.0.2 unchanged (indirect) RFC 6570 URI template support used by the SDK Downloads
-
-
Release v0.1.0 Stable
released this
2026-05-29 12:14:53 -06:00 | 2 commits to main since this releasev0.1.0
code.nochebuena.dev/einherjar/mcp
Architecture Decisions Resolved
Decision Outcome Deployment model — remote HTTP vs MCPB bundle vs local stdio Remote streamable-HTTP — every Einherjar user adds one URL; no per-machine Go install Knowledge model — live filesystem reads at runtime vs build-time index Build-time index — cmd/indexerproduces JSON; server embeds viago:embed; runtime reads nothing from diskSDK / language — TypeScript vs Python (FastMCP) vs Go Go — github.com/modelcontextprotocol/go-sdkv1.0.0; natural for a framework that is GoTool surface — 1-tool-per-action vs search+execute 1-per-action (10 tools) — surface is small enough to enumerate; AI assistants discover everything from tools/listModule dependency on einherjar/* None at compile time — indexer parses framework source on disk; mcp stays outside the framework dependency graph Wiring conventions exposure Synthetic 15th "wire" module — authored in internal/index/builtins/README.md, participates inlist_modules/get_module/get_examplelike a real moduleValidation engine — real Go AST checks vs pattern rules Pattern rules (go/parser + selector-based matchers) — lightweight, no external tooling required at request time Root doc.gopackage comment surfaceFirst-class Module.Docfield — promoted from the empty-nameSubPackageentry and returned byget_modulecompliance_test.go indexing Parsed for var _ Iface = implassertions andTest*function names + doc comments; surfaced viaget_complianceModule dependency edges Captured from each go.mod's require lines (einherjar/* only); returned byget_module.dependsOnauth-jwt.IssueTokenPairparity with micro-libIntentionally not provided by the framework — wirecanonical example shows the application signing access and refresh tokens itself viaSigner.Sign(...)data/index.jsongit handlingCommitted once as a stub via git add -f;.gitignorestrips subsequent indexer regenerations
Tools
Ten tools registered on initialise. Tool descriptions are surfaced via the MCP
tools/listmethod; this section documents the input schemas and structured
output shapes.list_modules// input {} // output { "modules": [{ "name": "core", "importPath": "code.nochebuena.dev/einherjar/core", "purpose": "...", "goVersion": "1.26", "subPackages": ["launcher", "..."] }] }get_module// input { "name": "core", "includeReadme": false } // output { "name": "core", "importPath": "...", "purpose": "...", "doc": "<root doc.go comment>", "goVersion": "1.26", "dependsOn": ["contracts"], "subPackages": [{ "name": "launcher", "importPath": "...", "doc": "..." }], "keySymbols": [{ "kind": "interface", "name": "Launcher", "subPackage": "launcher", "signature": "type Launcher interface { ... }" }], "adrs": [{ "id": "ADR-001", "title": "core module composition" }], "compliance": { "interfaceAssertCount": 7, "testCount": 15 }, "readme": "<markdown, when includeReadme=true>" }search_symbols// input { "query": "Launcher", "limit": 25, "module": "core", "kind": "interface" } // output { "results": [{ "module": "core", "subPackage": "launcher", "kind": "interface", "name": "Launcher", "signature": "...", "doc": "...", "file": "launcher/launcher.go", "line": 18 }], "total": 1 }get_symbol// input { "module": "core", "name": "Launcher", "subPackage": "launcher" } // output { "matches": [{ "module": "core", "subPackage": "launcher", "kind": "interface", "name": "Launcher", "signature": "...", "doc": "...", "file": "...", "line": 0 }] }list_adrs// input { "module": "core" } // output { "adrs": [{ "module": "core", "id": "ADR-001", "title": "core module composition" }] }get_adr// input { "module": "core", "id": "ADR-001" } // output { "module": "core", "id": "ADR-001", "title": "...", "body": "<markdown>" }get_example// input { "module": "wire", "topic": "hook" } // output { "examples": [{ "module": "wire", "subPackage": "", "title": "Feature hook", "language": "go", "code": "<source>" }] }get_compliance// input { "module": "core" } // output { "module": "core", "interfaceAsserts": [{ "module": "core", "interface": "logging.Logger", "impl": "logz.New(logz.Config{})", "file": "compliance_test.go", "line": 27 }], "tests": [{ "module": "core", "name": "TestAtMostOneExportedTypePerFile", "doc": "", "file": "compliance_test.go", "line": 0 }] }get_changelog// input { "module": "core" } // output { "module": "core", "changelog": "<markdown>" }validate_snippet// input { "code": "package wire\n\nimport ..." } // output { "summary": "1 error, 2 warnings, 0 notes", "findings": [{ "ruleId": "launcher.missing-run", "severity": "error", "module": "core", "message": "core/launcher constructed but Run() never called...", "hint": "After lc := launcher.New(logger); lc.Append(...); call ...", "line": 7 }] }Validation rules shipped at v0.1.0
Rule ID Severity Module What it catches launcher.missing-runerror core Launcher constructed without lc.Run()launcher.no-componentswarning core launcher.New()called without any.Append(...)launcher.run-error-discardedwarning core lc.Run()invoked as a statement (return value ignored)logz.direct-env-readwarning core os.Getenv("EINHERJAR_LOG_*")bypassing logz configweb.server-not-appendedinfo web web/server constructed but not added to the launcher wire.hook-bad-signaturewarning wire with<Feature>(...)first param is notlauncher.Launcherwire.hook-outside-beforestartwarning wire repo/service/handler construction or route registration at the top level of a hook (outside lc.BeforeStart)wire.route-specific-after-paramwarning web /users/{id}registered before sibling/users/me(chi shadows the literal route)
Commands
cmd/serverUsage: einherjar-mcp [flags] Flags: -addr string listen address (default ":8080", env EINHERJAR_MCP_ADDR) -path string MCP HTTP path (default "/mcp", env EINHERJAR_MCP_PATH) Endpoints: POST /mcp — streamable-HTTP MCP transport GET /healthz — liveness probe (200 "ok")cmd/indexerUsage: indexer [-out path] <einherjar-repo-root> Flags: -out string output path for the generated index (default "data/index.json") Argument: einherjar-repo-root directory containing sibling Einherjar modules (auth, core, web, …); defaults to ".."
Environment
Variable Default Effect EINHERJAR_MCP_ADDR:8080Listen address for the MCP server EINHERJAR_MCP_PATH/mcpHTTP path served by the streamable-HTTP endpoint
Install
Binary
go install code.nochebuena.dev/einherjar/mcp/cmd/server@v0.1.0 einherjar-mcp -addr :8080 -path /mcpThe installed binary embeds whatever framework index was produced at the moment
the module was built. For an index built against your own checkout, build
locally — see README.md.Container
docker build -f mcp/Dockerfile -t einherjar-mcp:0.1.0 . # from the einherjar repo root docker run --rm -p 8080:8080 einherjar-mcp:0.1.0The image is multi-stage; runtime is
gcr.io/distroless/static-debian12:nonroot.
Dependencies
Module Version Role github.com/modelcontextprotocol/go-sdkv1.0.0 Official Go MCP SDK — server, streamable-HTTP transport, tool registration github.com/google/jsonschema-gov0.3.0 (indirect) JSON Schema generation for tool input/output validation github.com/yosida95/uritemplate/v3v3.0.2 (indirect) RFC 6570 URI template support used by the SDK mcphas no compile-time dependency on anycode.nochebuena.dev/einherjar/*
module. The indexer reads framework source from disk and writes a JSON blob; the
server embeds the blob. This isolation letsmcpindex any version of the
framework without versioning itself in lock-step.Downloads