For more information about this repository, visit the project page at https://www.drupal.org/project/midgard
  • PHP 69.9%
  • TypeScript 22.7%
  • Shell 6.8%
  • CSS 0.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Andrés Moreno b60ee1f631 fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1
Security:
- Bifrost now refuses an authorization request from a Bifrost client with
  a plain code challenge, or none, and registers its consumers with PKCE
  required; drush updatedb turns that on for clients registered before.
  Midgard no longer relies on the OAuth server's PKCE settings.

Also:
- Bifrost runs on drupal/mcp_server 2.0.0-beta6, pinned to that exact
  release, and declares mcp/sdk ^0.8. The two known issues of alpha3 are
  fixed there: a tool that loads entities answers as plain JSON, and a
  client of revision 2026-07-28 is served.
- Requires Config Guardian 1.1, which rolls back through Drupal core's
  configuration importer. Eir scores an import on the changes the importer
  applies, leaves Midgard's own rollbacks and their previews alone, and
  lists what Drupal also changed during a rollback. Heimdall records a
  recovery rollback it could not apply.
- Requires e0ipso/simple_oauth_21 1.13.1 or later, which declares the
  packages the MCP Server OAuth companion depends on.

By: andresmoreno28
2026-10-18 16:45:27 +02:00
config Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
modules fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
recipes fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
src fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
tests fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
ui feat: #2 Move Bifrost to MCP Server 2.x 2026-10-01 13:01:59 +02:00
.cspell.json fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
.eslintignore Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
.gitlab-ci.yml feat: #2 Move Bifrost to MCP Server 2.x 2026-10-01 13:01:59 +02:00
.stylelintrc.json Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
CHANGELOG.md fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
composer.json fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
logo.png logo.png: the project logo drupal.org shows on the project page, in Project Browser and on GitLab 2026-10-16 19:19:06 +02:00
midgard.info.yml Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
midgard.install Add Eir config import gate, fix the token list and the install docs 2026-09-29 15:53:10 +02:00
midgard.libraries.yml Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
midgard.links.menu.yml Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
midgard.module Add Eir config import gate, fix the token list and the install docs 2026-09-29 15:53:10 +02:00
midgard.permissions.yml Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
midgard.routing.yml Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
midgard.services.yml Initial release of Midgard — AI operations layer for Drupal 2026-06-16 22:35:54 +02:00
phpcs.xml.dist Add Eir config import gate, fix the token list and the install docs 2026-09-29 15:53:10 +02:00
phpstan.neon.dist feat: #2 Move Bifrost to MCP Server 2.x 2026-10-01 13:01:59 +02:00
README.md fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00
SECURITY.md fix: Bifrost requires PKCE S256 itself; MCP Server 2.0.0-beta6 and Config Guardian 1.1 2026-10-18 16:45:27 +02:00

Midgard

Turn Drupal 11 into a safe, auditable AI operations layer.

What is Midgard

Midgard wires an AI Provider Layer (via drupal/ai), an MCP Bridge (midgard_bifrost), and an HMAC-chained Audit Ledger (midgard_heimdall) into a coherent whole on top of Drupal 11. The security primitive is simple: the recipe declares config structure, the drush command injects secrets at runtime from your process environment — the key never enters Drupal config or git. Three paths reach the same out-of-the-box state: the setup wizard at /admin/midgard/setup, the midgard_quickstart recipe, or drush midgard:install.

Where it fits: Midgard is the governance layer for config-AI. As AI agents increasingly write real Drupal configuration, Midgard makes that writing safe — guarded, audited, and reversible. The output worth governing is config, not text: Midgard governs the agents that mutate your site rather than competing to generate the changes, and it complements the official governance work in core rather than replacing it.

Prerequisites

  • Drupal core 11.3+
  • PHP 8.3+
  • Composer 2.7+
  • Drush 13.7+
  • DDEV (recommended for local development; production uses your hosting's standard tooling)

Drupal compatibility

Midgard targets Drupal 11. Its own code is already Drupal 12-clean — upgrade_status reports zero deprecated API calls across the suite — and it will declare ^12 as soon as its AI and OAuth dependencies (drupal/ai, drupal/simple_oauth, drupal/mcp_server, drupal/key) do. Declaring Drupal 12 support before then would be inaccurate, since Composer could not resolve those dependencies on Drupal 12 yet. Drupal 11 receives upstream support well into 2027.

Install

Midgard is a standard Drupal contrib module, currently in alpha. Its MCP bridge runs on MCP Server 2.0.0-beta6 and the MCP Server OAuth companion 1.0.0-alpha1, pinned to those exact releases until MCP Server 2.0 is stable. A project with the default minimum-stability: stable therefore needs a stability flag for the three packages:

# In a Drupal 11 site (DDEV recommended for local dev):
ddev composer require 'drupal/midgard:^1.0@alpha' \
  'drupal/mcp_server:2.0.0-beta6@beta' \
  'drupal/mcp_server_oauth:1.0.0-alpha1@alpha'
ddev drush en midgard -y

A plain composer require drupal/midgard stops with "Could not find a version of package drupal/midgard matching your minimum-stability (stable)", and drupal/midgard:^1.0@alpha alone stops on drupal/mcp_server.

If you are starting from scratch, create a standard Drupal 11 project first:

composer create-project drupal/recommended-project my-site && cd my-site
ddev config --auto && ddev start
ddev composer require 'drupal/midgard:^1.0@alpha' \
  'drupal/mcp_server:2.0.0-beta6@beta' \
  'drupal/mcp_server_oauth:1.0.0-alpha1@alpha'
ddev drush site:install standard --yes
ddev drush en midgard -y

Roadmap: composer create-project drupal/midgard-project <dir> will provide a pre-wired project scaffold once drupal/midgard-project ships (per ADR-023). That package does not exist yet.

Set your API key

Put the key in .ddev/config.local.yaml (create the file if it does not exist), then run ddev restart:

web_environment:
  - MIDGARD_ANTHROPIC_API_KEY=sk-ant-...

Do not use ddev config --web-environment-add for the key: it writes into .ddev/config.yaml, which DDEV projects normally commit. The .ddev/.gitignore that DDEV generates ignores config.local.yaml, but DDEV only writes that file on the project's first ddev start, so start the project once before adding the key (or check git status afterwards).

Scope the key to a single workspace when you create it (the default workspace is fine). A key that is not tied to one workspace needs an anthropic-workspace-id header on every request, and the Anthropic provider for drupal/ai does not send one.

The key never enters Drupal config or git. drush midgard:install reads this env var at runtime via MidgardApiKeyResolver. There is no --api-key= flag; presence of the env var does not imply validity — the doctor's secret.key_resolves check verifies that.

Configure

ddev drush midgard:install --provider=anthropic --no-interaction

This runs a sequence of steps: pre-flight checks, applies the midgard_quickstart recipe, provisions the Key entity, populates AI routing for three tiers (flagship / workhorse / economy), assigns Heimdall permissions to the administrator role, and runs the doctor. Expect ~30 seconds on a fresh site.

Verify

ddev drush midgard:doctor
# Expected on a clean install: zero blockers and two by-design warnings
# (bifrost.client_count — no demo client; secret.heimdall_hmac — lazy on first approval).

The two expected warnings are by design:

  • bifrost.client_count — no implicit Bifrost OAuth client; opt in with --create-demo-client at install time.
  • secret.heimdall_hmac — the HMAC approval secret is lazy-initialized on first approval token issuance.

Try it

ddev launch /admin/midgard

The Midgard Hall renders. The setup wizard at /admin/midgard/setup is the GUI alternative to drush midgard:install and reaches the same state.

Gate config imports (Eir)

Heimdall guards configuration changes made at runtime. A drush config:import from config/sync is a different path: the importer applies the whole changelist and Heimdall can only record those saves after the fact. The optional midgard_eir submodule puts a gate in front of that path — the drush command and the config sync UI alike.

ddev drush en midgard_eir -y

Before an import applies anything, Eir hashes the full staged content, gives the changelist a risk score and takes a snapshot. Below the write threshold (threshold_write in midgard.heimdall.settings, 45 by default) the import goes through and gets a chained audit row. At or above it the whole import is refused, nothing is applied, and a pending entry waits for a decision:

ddev drush midgard:imports:list                       # pending and approved imports
ddev drush midgard:imports:approve <hash>             # single-use approval of that changelist
ddev drush config:import -y                           # the approved changelist now goes through
ddev drush midgard:imports:deny <hash>                # or refuse it
ddev drush midgard:imports:rollback <hash> --dry-run  # what a rollback would restore
ddev drush midgard:imports:rollback <hash>            # restore the pre-import values

An approval covers the exact changelist that was scored: if any staged value changes, it is a different changelist and it is scored again. It is also single-use and short-lived: approval_ttl_seconds in midgard.heimdall.settings (30 minutes by default). An approval that expires unused is recorded as expired, and the next import is scored and paused again. Two limits in this release: the gate is all-or-nothing per import (not per item), and a rollback restores configuration the import changed but does not delete configuration it created. With the module disabled, config import behaves exactly as in core.

Next steps

  • Design rationale: the architecture and ADRs (ADR-020 through ADR-024) live in specs/ in the development repository — see the issue queue for questions.
  • Selective installs: the 5 recipes in recipes/ let you apply only the layers you need (e.g. midgard_core without Bifrost).
  • Teardown: drush midgard:uninstall --help — preserves secrets by default; 4 opt-in purge flags (--purge-secrets, --purge-keys, --purge-clients, --purge-audit-entries) for nuclear cleanup.
  • GUI configuration changes: the wizard at /admin/midgard/setup.

Similar projects and how they differ

Midgard occupies a specific niche: governance of config-AI — making the configuration that AI agents write to Drupal safe, auditable, and reversible. It doesn't compete with the projects below; it relates to them with three verbs. Midgard builds on the AI framework, governs the agents that mutate config, composes with the automation layer, and complements the official governance work. The thesis is that the output worth governing is config, not text.

drupal/ai — built on. drupal/ai is the provider abstraction and AI framework. Midgard is a hard dependency on it, not an alternative: the AI Provider Layer, MCP Bridge, and Audit Ledger all sit on top of drupal/ai.

AI Agents (and its agent family, e.g. the Views agent) — we govern what they generate. AI Agents turns natural-language instructions into Drupal config and content changes via tool-calling. Midgard doesn't generate those changes — it governs them: each write passes through the guard, lands a tamper-evident audit row, and stays reversible via config_guardian snapshot/rollback. AI Agents is the producer; Midgard is the safety net.

AI Generation — we govern what it writes to disk. AI Generation creates real Drupal config, modules, and recipes from a prompt. It's the most literal expression of "the output is config": where it writes configuration, Midgard makes that write guarded, audited, and reversible. Generation and governance are complementary halves.

Apex AI — a different axis. Apex AI is a multi-provider orchestration engine: it governs the model calls (routing, cost, middleware). Midgard governs the config mutations an AI makes. Both use the word "governance"; they govern different things, at different layers.

ECA — we compose with it. ECA is the no-code automation substrate (events-conditions-actions). Midgard composes with it rather than replacing it — the "output is config" path can emit ECA models, and Midgard governs the resulting changes.

MCP Server — Bifrost rides on it. MCP Server exposes Drupal over the Model Context Protocol with OAuth 2.1. Midgard's midgard_bifrost submodule builds on MCP Server behind a small wrapper firewall and adds allowlisted, audited, reversible tool execution. Midgard's contribution is the governance layer over MCP — not the transport itself.

Context Control Center — Muninn will bridge it. CCC governs the context an agent sees (a managed, moderated context library). Midgard governs the changes an agent makes. They're adjacent layers of the same safety story; a future Midgard submodule (Muninn) will bridge CCC.

Workspaces (the official governance work) — we complement, not compete. Tag1 is extending Workspaces — Drupal's staging/review/rollback layer — so AI agents inherit governed staging through interface contracts: the official, in-core trajectory. Midgard is the lightweight, install-now, config-AI-specific option that fills the gap today and is designed to interoperate with those contracts as they land. It is explicitly not a competitor to core.

Midgard is pre-1.0 and not yet covered by a Drupal security advisory — see SECURITY.md and CHANGELOG.md for maturity. The comparison above is about approach, not maturity.

AI disclosure

Midgard was built with AI-assisted tooling — used for code generation, test scaffolding, and documentation drafting. Every change has been reviewed, tested, and validated by the maintainer before it landed: the suite ships a comprehensive automated test set across unit, kernel, and functional layers, and the full DrupalCI pipeline (phpcs, PHPStan level 6, PHPUnit, plus spell/style/lint checks) must pass before any release. AI accelerated the work; responsibility for the code's correctness, security model, and design rests entirely with the maintainer.