# einherjar/mcp [![version](https://img.shields.io/badge/version-v0.1.0-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/mcp) [![license](https://img.shields.io/badge/license-AGPL--3.0-22863A?style=flat-square)](LICENSE) [![go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev) > Every warrior who knew the sagas had a skald nearby. This is yours. `code.nochebuena.dev/einherjar/mcp` is the Einherjar **Model Context Protocol** server. It is a remote, streamable-HTTP service that teaches AI assistants about every other module of the framework: which package exposes which type, what each module promises via its compliance tests, the canonical wiring shape for a service, and whether a snippet of Go follows the conventions. Anyone who works in an Einherjar codebase can point their AI tools at one URL and get answers grounded in the actual source. --- ## What Is Einherjar? In Norse mythology, the Einherjar are the chosen warriors of Valhalla — selected not for glory, but to be ready for what comes after. They train. They prepare. They build the capability that others will rely on. This framework is named for that purpose. Every module is a piece of that preparation: built carefully, documented for those who were never in the room, and designed to hold under pressure. --- ## Commands | Command | Purpose | |---|---| | `cmd/server` | Streamable-HTTP MCP server. Embeds the framework index at build time and serves it over a single HTTP endpoint. | | `cmd/indexer` | Walks an Einherjar repository checkout and writes the framework index to `data/index.json`. | --- ## Tools The server exposes **ten** tools to MCP-aware clients (Claude desktop, Claude Code, Cursor, Zed, and anything else that speaks MCP): | Tool | Purpose | |---|---| | `list_modules` | Enumerate every Einherjar module with its purpose and sub-packages | | `get_module` | Package doc, dependencies, sub-packages, key types, compliance counts; optional README | | `search_symbols` | Find a type, function, or interface by name, doc text, sub-package, or module | | `get_symbol` | Full signature, doc comment, and source location for one symbol | | `list_adrs` | List architectural decision records, optionally restricted to one module | | `get_adr` | Fetch a single ADR's markdown body | | `get_example` | Canonical usage snippet — pulled from module READMEs and from the synthetic `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 | `validate_snippet` ships **eight** wiring-convention rules at v0.1.0: `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`, and `wire.route-specific-after-param`. --- ## Build Flow ``` build time runtime ┌──────────────────────────────┐ ┌──────────────────────────┐ │ cmd/indexer ../ │ │ cmd/server │ │ walks every Einherjar │ │ streamable-HTTP MCP │ │ module, parses Go pkgs, │ ──▶ │ tools served from the │ │ reads READMEs + ADRs │ │ embedded index.json │ │ ⇒ data/index.json (embed) │ │ │ └──────────────────────────────┘ └──────────────────────────┘ ``` The indexer is a separate command. It produces `data/index.json` which the server embeds via `//go:embed`, so the deployed binary is self-contained and reads nothing from disk at runtime. --- ## Usage ### Local run ```bash # 1. Build the framework index from the sibling Einherjar modules go run ./cmd/indexer .. # 2. Build and run the server go build -o bin/einherjar-mcp ./cmd/server ./bin/einherjar-mcp -addr :8080 -path /mcp ``` ### Container ```bash # Build the image from the einherjar repo root so the indexer can walk every # sibling module at image-build time. docker build -f mcp/Dockerfile -t einherjar-mcp:0.1.0 . docker run --rm -p 8080:8080 einherjar-mcp:0.1.0 ``` ### Environment variables | Variable | Default | Effect | |---|---|---| | `EINHERJAR_MCP_ADDR` | `:8080` | Listen address for the MCP server | | `EINHERJAR_MCP_PATH` | `/mcp` | HTTP path served by the streamable-HTTP endpoint | --- ## Wiring Conventions (the synthetic `wire` module) The MCP server ships a 15th, **synthetic** module called `wire`. It is not an Einherjar module — it documents the canonical *application* shape that uses Einherjar modules. The content lives at `internal/index/builtins/README.md` and is embedded at build time. AI assistants discover it via `list_modules` and read it via `get_module` and `get_example` the same way they read any real module. The conventions captured: project layout (`cmd//main.go`, `internal/wire/*.go`, domain layout per feature), the fixed shape of `Run()`, the fixed shape of a `with` hook (one `lc.BeforeStart` containing all construction and route registration), route-ordering rules for chi, the `authz` middleware helper, when to use `skipPublicPaths` vs `skipMethodPath`, and adapter patterns at the wire boundary. --- ## Dependency Rules ``` contracts (zero dependencies) ↑ core, web, auth, … (every framework module) ↑ mcp (reads framework source at index-time only) ``` `mcp` imports **nothing** from other Einherjar modules at compile time. The indexer parses the framework source on disk and writes a JSON blob; the server embeds that blob. This keeps `mcp` outside the framework dependency graph: it can index any version of einherjar without versioning itself in lock-step. --- ## Verification ```bash cd mcp/ go build ./... # must compile clean go vet ./... # no warnings go test ./... # all tests pass gofmt -l . # no output ``` All four commands must produce clean output before a PR will be reviewed. --- ## Architecture Decisions No ADRs at `v0.1.0`. The structural decisions in this release (synthetic `wire` module, `go:embed` of the index, build-time-not-runtime knowledge model, primitives not response shapes) are captured in the framework-wide memory and in this README. --- > *A blade is sharper when the warrior knows its name.* > *This is what tells them.*