# AIDA MCP

AIDA (AI Developer Assistant) is an MCP server that gives your AI coding assistant repository-specific rules, automated validation, and Git workflow guardrails — across any MCP-compatible tool.

Instead of maintaining separate prompt files for each tool (`.cursorrules`, `CLAUDE.md`, `copilot-instructions.md`), you configure behavior once in `.aida/` and every assistant picks it up.

## Why AIDA

**Context engineering, automated.** AIDA doesn't dump a flat file into the context window. It dynamically selects only the rules relevant to the current task using hybrid scoring (lexical + semantic). Session memory tracks what the AI already received — subsequent calls return only deltas. Your context stays lean even as your rule library grows.

**Batteries included.** Built-in profiles for Python, Kotlin, Gradle, Spring Boot, and GitHub workflows — auto-detected during setup. Your AI gets curated best practices out of the box.

**AI-assisted setup.** Run `aida-mcp init`, then ask your AI to finalize. It analyzes your repo, migrates existing rules, wires validation scripts, and configures git policy — no hand-writing YAML.

**Validation before commit.** Rules can only suggest — LLMs still hallucinate. AIDA routes changed files to the right linters, formatters, and tests automatically, before commit. The AI gets filtered feedback and can self-correct on the spot.

**Git workflow guardrails.** Enforces commit message format, required metadata (risk level, ticket reference), and PR templates. The AI can list, reply to, and resolve PR review threads directly.

**Works with every MCP-compatible tool.** Cursor, Claude Code, OpenAI Codex, JetBrains AI Chat, and Junie. One configuration, every assistant. For teams not yet running MCP, AIDA can export rules as static files.

## Install

```bash
uv tool install aida-mcp    # recommended
pip install aida-mcp         # alternative
```

Requires Python 3.12+ (< 3.15) and an MCP-compatible AI tool.

## Quick start

### Your repo already has `.aida/` configured

Install `aida-mcp` and reload your AI tool. The MCP wiring in the repo connects your assistant to AIDA automatically.

### Setting up a new repo

```bash
cd /path/to/your/repo
aida-mcp init
```

This creates:
- `.aida/` — rules, validation config, git policy, templates
- MCP wiring files (`.mcp.json`, `.cursor/mcp.json`, etc.)

`init` auto-detects your stack and enables matching embedded profiles (Python, Kotlin, Gradle, Spring Boot).

Commit the result and reload your AI tool.

### Finalizing onboarding with AI

`aida-mcp init` scaffolds the configuration structure with sensible defaults, but your repository likely needs customization. Ask your AI assistant to finalize the onboarding:

> "Finalize AIDA onboarding for this repository."

The AI has a built-in onboarding guide and will:

1. **Migrate existing rules** — move guidance from legacy files (`.cursorrules`, root `CLAUDE.md`, etc.) into `.aida/rules/`.
2. **Configure change domains** — analyze your project structure and populate `change_domains.yaml` with domains and globs.
3. **Set up validation** — create or wire validation scripts in `validation_registry.yaml`, configure pipelines in `validation_policy.yaml`.
4. **Tune git policy** — adjust commit/PR templates, risk levels, and ticket prefixes in `git_policy.yaml`.
5. **Enable profiles** — propose embedded profile selections based on your stack in `rules_selection.yaml`.

The AI will ask focused questions when information is ambiguous (e.g., which linters to use, how validation scripts are invoked). After edits, it runs validation to verify the configuration works.

## Directory structure

All configuration lives in `.aida/` and is version-controlled (except local overrides):

```
.aida/
├── rules/                        # Repo-owned rules (.mdc files)
│   ├── general.mdc
│   └── tools/
├── rules_selection.yaml          # Which embedded profiles to enable
├── change_domains.yaml           # Map file paths to logical domains
├── validation_policy.yaml        # Route domains to validation pipelines
├── validation_registry.yaml      # Define validation commands and processors
├── git_policy.yaml               # Commit/PR policies and templates
├── templates/
│   ├── commit-message.txt
│   ├── pr-title.txt
│   └── pr-body.md
├── preferences.yaml              # Team defaults for automation behavior
├── preferences.local.yaml        # [gitignored] Developer overrides
└── rules_selection.local.yaml    # [gitignored] Developer profile overrides
```

---

## Rules

Rules are Markdown files with YAML frontmatter, stored in `.aida/rules/` using the `.mdc` extension.

### Format

```markdown
---
description: Short description used for relevance scoring
alwaysApply: false
---

# Rule Title

Markdown body with guidance for the AI assistant.
```

**Frontmatter fields:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `description` | string | *(required)* | Used by the retrieval engine for relevance scoring |
| `alwaysApply` | bool | `false` | When `true`, rule is included in every response regardless of query |

Rules with `alwaysApply: false` are selected dynamically based on the AI's current task query using hybrid scoring (BM25 lexical + semantic embeddings). Session delta tracking ensures each rule is delivered only once per conversation.

### Embedded profiles

AIDA ships built-in profiles that you can enable in `rules_selection.yaml`:

| Profile | Path |
|---------|------|
| Python core | `profiles/languages/python/core.mdc` |
| Python strict | `profiles/languages/python/strict.mdc` |
| Kotlin | `profiles/languages/kotlin/default.mdc` |
| Gradle | `profiles/build/gradle/default.mdc` |
| Spring Boot | `profiles/frameworks/spring-boot/microservices.mdc` |
| GitHub commit & push | `profiles/workflows/github/commit-and-push.mdc` |
| GitHub pull requests | `profiles/workflows/github/pull-requests.mdc` |
| GitHub review & CI | `profiles/workflows/github/review-and-ci.mdc` |
| Library changes | `profiles/workflows/dependencies/library-changes.mdc` |
| Third-party libraries | `profiles/workflows/dependencies/third-party-libraries.mdc` |

### Rules selection (`rules_selection.yaml`)

Controls which rules are active:

```yaml
version: 1
defaults:
  embedded: core_only    # core_only | selected
  repo: all              # all | selected

include:
  - source: embedded
    path: "profiles/languages/python/**"
  - source: embedded
    path: "profiles/workflows/github/**"

exclude:
  - source: repo
    path: "rules/deprecated/**"
```

- `defaults.embedded: core_only` — only core rules are active; profiles require explicit `include` entries.
- `defaults.embedded: selected` — all embedded rules active; use `exclude` to suppress.
- `defaults.repo: all` — all `.aida/rules/**/*.mdc` files active.
- `defaults.repo: selected` — only explicitly included repo rules active.

Developers can override in `rules_selection.local.yaml` (gitignored) without affecting the team.

### Writing and evolving rules

You can ask your AI assistant to create or update rules:

> "Add a rule for our API versioning conventions."
>
> "Update the testing rule to require integration tests for database changes."

The AI has a built-in rules-authoring guide and will follow best practices:

- **Placement** — repo-specific guidance goes in `.aida/rules/`, organized by topic (e.g., `rules/tools/`, `rules/workflows/`).
- **Scope** — each rule should contain only knowledge the AI cannot infer from code. Point to existing docs rather than duplicating them.
- **Granularity** — prefer extending an existing rule over creating a new file. Use `alwaysApply: true` sparingly (general conventions only); most rules should be query-matched.
- **Structure** — good rules include clear boundaries ("Owns / Does NOT own"), constraints, and common traps. Typical size: 25–50 lines for constraints, up to ~150 for architecture guides.

After creating or modifying rules, reload your AI tool's MCP session for changes to take effect.

---

## Change domains (`change_domains.yaml`)

Maps file paths to logical domains used for validation routing:

```yaml
version: 1
domains:
  - id: python_service
    description: Backend Python service
    match_globs:
      - "services/**/*.py"
    root_depth: 1

  - id: helm_charts
    description: Helm chart definitions
    match_globs:
      - "helm-charts/**"
    root_depth: 1
```

**Fields:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | yes | Unique domain identifier, referenced by validation routes |
| `match_globs` | list[string] | yes | File path patterns to match |
| `description` | string | no | Human-readable description |
| `root_depth` | int | no | Path segment depth for component root extraction |

**`root_depth` explained:** Given a changed file `services/api/src/main.py` and `root_depth: 2`, the component root is `services/api`. This root is available as the `{root}` placeholder in validation commands. `root_depth: 0` means the workspace root (`.`).

---

## Validation

AIDA's validation system routes changed files through domain-specific pipelines that run linters, formatters, and tests.

### Scopes

| Scope | What it validates | When to use |
|-------|-------------------|-------------|
| `pre_commit` | Uncommitted working tree changes | During development, before committing |
| `pre_push` | All commits vs upstream + dirty working tree | Before push or PR creation |

### Validation policy (`validation_policy.yaml`)

Routes domains to pipelines and defines pipeline steps:

```yaml
version: 1
validation_policy:
  codegen: []
  routes:
    - domain: python_service
      pipeline: python-default
    - domain: helm_charts
      pipeline: helm-lint
  pipelines:
    python-default:
      steps:
        - command_id: py.lint
          processor_id: passthrough
        - command_id: py.test
          processor_id: pytest
          processor_config:
            max_failed_tests: 5
    helm-lint:
      steps:
        - command_id: helm.lint
          processor_id: passthrough
```

**Routes:** Each route maps a domain `id` (from `change_domains.yaml`) to a `pipeline`. When files change in a domain, its pipeline runs.

**Pipeline steps:** Executed in order. Each step runs a `command_id` (from the registry) and processes output through a `processor_id`. Optional `processor_config` overrides processor defaults.

**Codegen triggers:** Run code generation before validation when specific files change:

```yaml
codegen:
  - id: protobuf
    match_globs: ["**/*.proto"]
    pipeline: codegen-protos
    scopes: ["pre_push"]          # default: ["pre_push"]
```

Codegen pipelines run before validation pipelines. If codegen fails, validation is skipped.

### Validation registry (`validation_registry.yaml`)

Defines the actual commands and output processors:

```yaml
version: 1
registry:
  includes: []           # paths to other registry files to merge

  commands:
    py.lint:
      argv: ["ruff", "check", "{target_root}"]
      cwd: "{workspace_root}"
      timeout_sec: 300
      env:
        PYTHONPATH: "src"
      streaming: true

    py.test:
      argv: ["pytest", "{target_root}/tests"]
      cwd: "{workspace_root}"
      timeout_sec: 600

  processors:
    custom.json:
      kind: builtin
      builtin_id: external_json
```

**Command fields:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `argv` | list[string] | *(required)* | Command and arguments (no shell interpretation) |
| `cwd` | string | workspace root | Working directory |
| `timeout_sec` | int | 600 | Command timeout in seconds |
| `env` | dict | `{}` | Additional environment variables |
| `streaming` | bool | `true` | Stream output to the AI during execution |

**Placeholders** available in `argv` and `cwd`:

| Placeholder | Value |
|-------------|-------|
| `{workspace_root}` | Repository root absolute path |
| `{root}` | Domain-derived component root (from `root_depth`) |
| `{target_root}` | Same as `{root}`, falls back to `.` |
| `{domain}` | Domain id that triggered this pipeline |
| `{scope}` | Validation scope (`pre_commit` or `pre_push`) |

### Built-in processors

| Processor | Description | Config options |
|-----------|-------------|----------------|
| `passthrough` | Returns combined stdout/stderr, exit code 0 = success | — |
| `pytest` | Parses pytest output, extracts failure summaries | `max_failed_tests` (int, default: 10) |
| `gradle` | Parses Gradle build output, extracts error sections | `max_excerpt_lines` (int, default: 120), `max_error_sections` (int, default: 5) |
| `typescript_rush_tail` | Extracts last meaningful line from Rush/TS builds | — |
| `external_json` | Parses JSON from stdout with fields: `success`, `text`, `error_count` | — |

Use `passthrough` as the default. Reference built-in processors directly by name in `processor_id` — no registry entry needed.

---

## Git policy (`git_policy.yaml`)

Controls commit messages, PR formatting, and workflow behavior:

```yaml
version: 1
git_policy:
  commit:
    subject_max_chars: 70
    ticket_enabled: true
    ticket_prefix: JIRA
    risk_enabled: true
    risk_values: [nonprod, low, high]
    risk_prefix: risk
    require_co_authored_by: false
    template_file: templates/commit-message.txt
    pre_commit_guidance: false

  pr:
    title_template_file: templates/pr-title.txt
    body_template_file: templates/pr-body.md

  workflow:
    prefer_force_with_lease: true
    autosquash_unpublished_only: true

  enforcement:
    mode: "off"
```

**Commit policy fields:**

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `subject_max_chars` | int | 70 | Max commit subject length (10–200) |
| `ticket_enabled` | bool | `true` | Require/allow ticket trailers |
| `ticket_prefix` | string | `JIRA` | Trailer key for tickets (e.g., `JIRA: DX-123`) |
| `risk_enabled` | bool | `true` | Require risk level trailers |
| `risk_values` | list[string] | `[nonprod, low, high]` | Allowed risk levels |
| `risk_prefix` | string | `risk` | Trailer key for risk (e.g., `risk: low`) |
| `require_co_authored_by` | bool | `false` | Require co-author trailers |
| `template_file` | string | — | Path to commit message template (relative to `.aida/`) |
| `pre_commit_guidance` | bool | `false` | Show commit guidance during validation |

**Enforcement modes:**

| Mode | Behavior |
|------|----------|
| `off` | No enforcement — policy is advisory only |
| `advisory` | Warn on violations but allow commit |
| `strict` | Block commits that violate policy |

### Templates

Templates use `{placeholder}` syntax. Available variables:

**Commit message template:**

| Variable | Value |
|----------|-------|
| `{type}` | Commit type (`feat`, `fix`, `chore`, etc.) |
| `{scope}` | Optional scope name |
| `{repository_part}` | `(scope)` if scope is present, empty otherwise |
| `{title}` | Commit title text |
| `{subject}` | Full subject: `{type}{repository_part}: {title}` |
| `{body}` | Commit body |
| `{risk}` | Formatted risk trailer (e.g., `risk: low`) |
| `{risk_value}` | Raw risk value |
| `{ticket}` | Formatted ticket trailer (e.g., `JIRA: DX-123`) |
| `{ticket_values}` | Raw ticket IDs |
| `{co_authored_by}` | Formatted `Co-authored-by:` lines |
| `{co_authored_by_values}` | Raw co-author values |
| `{file:path/to/file}` | Inline content of a file (path relative to repo root) |

**PR body template:** Same as commit variables, plus:

| Variable | Value |
|----------|-------|
| `{summary}` | PR summary text |
| `{test_plan}` | Test plan text |
| `{ticket_id}` | Raw ticket ID |
| `{ticket_ref_prefix}` | `[TICKET] ` if ticket present, empty otherwise |

---

## Preferences

### Team defaults (`preferences.yaml`)

```yaml
version: 1
preferences:
  mode: full                # full (MCP server) | native (exported rules)
  validation:
    pre_commit: auto        # auto | ask | disabled
    pre_push: auto          # auto | ask | disabled
  commit:
    mode: auto              # auto | ask | disabled
  pr:
    mode: auto              # auto | ask | disabled
```

| Value | Behavior |
|-------|----------|
| `auto` | AI executes automatically |
| `ask` | AI asks user for confirmation |
| `disabled` | Feature is turned off |

### Developer overrides (`preferences.local.yaml`)

Gitignored. Merges with team defaults so individual developers can opt out of automation:

```yaml
preferences:
  validation:
    pre_commit: ask
  commit:
    mode: disabled
```

---

## Native mode (no MCP server)

For teams not yet running MCP, export rules as static files:

```bash
aida-mcp rules export
```

This writes self-contained rule files for each tool (`.claude/CLAUDE.md`, `.cursor/rules/aida.mdc`, etc.). Re-run after config changes. `aida-mcp doctor` warns when exports are stale.

Set `preferences.mode: native` to signal that the repo uses exported rules instead of MCP.

---

## MCP tools

The AIDA MCP server exposes these tools to AI assistants:

### `get_rules`

Retrieves task-relevant rules using hybrid scoring (BM25 + semantic).

| Parameter | Type | Description |
|-----------|------|-------------|
| `query` | string | Task description for relevance matching |
| `session_id` | string | Stable ID for the conversation (enables delta mode) |
| `delta_cursor` | string | Cursor from previous response (returns only new/changed rules) |
| `file_context` | list[string] | Active file paths for improved relevance |

### `validate_command`

Plans validation steps based on changed files and scope.

| Parameter | Type | Description |
|-----------|------|-------------|
| `scope` | `pre_commit` \| `pre_push` | Validation scope |
| `focus_paths` | list[string] | Optional paths to validate directly (skips git discovery) |
| `explain` | bool | Include routing details in response |
| `upstream_ref` | string | Upstream override for `pre_push` |

### `commit_command`

Creates a policy-compliant commit.

| Parameter | Type | Description |
|-----------|------|-------------|
| `commit_type` | string | Commit type prefix (`feat`, `fix`, `chore`, etc.) |
| `title` | string | Commit subject |
| `risk` | string | Risk level value |
| `scope` | string | Optional scope |
| `body` | string | Commit body |
| `ticket` | string \| list | Ticket ID(s) |
| `co_authored_by` | string \| list | Co-author(s) as `Name <email>` |
| `fixup_of` | string | SHA/ref for `git commit --fixup` |

Returns `status: "needs_more_input"` when policy-required fields are missing. The AI should infer values from context and retry.

### `pr_command`

Creates a policy-compliant pull request.

| Parameter | Type | Description |
|-----------|------|-------------|
| `base` | string | Base branch |
| `head` | string | Head branch |
| `title` | string | PR title |
| `summary` | string | PR body summary |
| `test_plan` | string | Test plan |
| `commit_type` | string | Conventional commit type |
| `scope` | string | Optional scope |
| `ticket` | string | Ticket ID |
| `risk` | string | Risk level |
| `draft` | bool | Create as draft PR |

### `github_comments`

Manages PR review threads: list, reply, resolve.

### `warmup_embeddings`

Pre-initializes the semantic model for faster first `get_rules` call.

---

## CLI reference

### `aida-mcp init`

Bootstrap AIDA in a repository. Auto-detects stack and enables matching profiles.

```
--force              Overwrite files where safe
--http               Use HTTP MCP transport (experimental)
--http-port PORT     HTTP port (default: 9910)
```

### `aida-mcp doctor`

Check repository health. Reports configuration errors, missing files, stale exports, and legacy conflicts.

```
--auto-fix           Apply safe fixes (create missing configs, update .gitignore, reconcile structure)
```

**Checks performed:**
- `.aida/` directory and required config files exist
- Config YAML syntax and schema validity
- Config version currency (detects needed migrations)
- `.aida/.gitignore` and workspace `.gitignore` completeness
- Legacy Cursor rule files (non-AIDA `.mdc` files)
- Conflicting `CLAUDE.md` files
- Native export staleness

### `aida-mcp migrate`

Upgrade from an older AIDA layout.

```
--client-only        Only migrate client rule files
--dry-run            Report without modifying
--force              Overwrite on conflict
```

### `aida-mcp validate`

Run validation on changed files.

```
--scope {pre_commit,pre_push}   Validation scope (default: pre_commit)
--focus-path PATH               Validate specific paths (repeatable, skips git discovery)
--dry-run                       Plan without executing
--explain                       Show routing and command details
--mode {important,errors}       Output filtering (default: important)
--upstream-ref REF              Upstream override for pre_push
--head-ref REF                  Head ref override (default: HEAD)
--timeout-sec SEC               Override command timeout
--stdout-max-lines N            Max output lines (default: 120)
--stdout-max-chars N            Max output chars (default: 12000)
```

### `aida-mcp commit`

Create a policy-compliant commit.

```
--type TYPE                     Commit type (default: chore)
--title TITLE                   Subject text (required)
--scope SCOPE                   Optional scope
--risk VALUE                    Risk level
--ticket ID                     Ticket ID (repeatable)
--co-authored-by "Name <email>" Co-author (repeatable)
--body TEXT                     Commit body
--body-file PATH                Read body from file
--body-stdin                    Read body from stdin
--fixup-of SHA                  Git fixup target
--plan                          Show plan instead of committing
--dry-run                       Print message without committing
--json                          Machine-readable output
```

### `aida-mcp check-commit-policy`

Validate commits in a ref range against policy.

```
--from-ref REF       Base ref, exclusive (e.g., @{u})
--to-ref REF         Head ref, inclusive (default: HEAD)
--json               Machine-readable output
```

### `aida-mcp check-pr-policy`

Validate a PR title and body against the PR template.

```
--title TITLE        PR title (required)
--body TEXT           PR body
--body-file PATH     Read body from file
--json               Machine-readable output
```

### `aida-mcp preview-commit-template`

Render a commit message from the template with sample values.

```
--type TYPE          Commit type (default: chore)
--title TITLE        Subject text (required)
--scope SCOPE        Optional scope
--risk RISK          Risk level (default: nonprod)
--ticket ID          Ticket ID (repeatable)
--json               Machine-readable output
```

### `aida-mcp preview-pr-template`

Render a PR title and body from templates with sample values.

```
--title TITLE        PR title (required)
--summary TEXT       PR summary
--test-plan TEXT     Test plan
--ticket ID          Ticket ID
--risk RISK          Risk level (default: nonprod)
--type TYPE          Commit type
--scope SCOPE        Optional scope
--json               Machine-readable output
```

### `aida-mcp rules export`

Export rules as static files for native mode.

```
--tool {claude,cursor,codex,jetbrains,junie,all}   Target tools (repeatable)
```

### `aida-mcp update-rules`

Regenerate client rule files (`.claude/CLAUDE.md`, `.cursor/rules/aida.mdc`, etc.) from the installed onboarding template.

### `aida-mcp install-mcp`

Install or refresh MCP wiring for AI tools.

```
--target {cursor,claude,codex,jetbrains,junie,all}  Target tools (repeatable)
--global             Write global config (required for JetBrains/Junie)
--http               Use HTTP transport instead of stdio
--http-port PORT     HTTP port (default: 9910)
```

JetBrains and Junie require global installation:

```bash
aida-mcp install-mcp --global --target jetbrains
aida-mcp install-mcp --global --target junie
```

### `aida-mcp serve-http`

Run the MCP server over HTTP (experimental).

```
--host HOST          Bind address (default: 127.0.0.1)
--port PORT          Port (default: 9910)
--path PATH          URL path (default: /mcp)
```

### `aida-mcp alias install`

Install an `aida` shell alias for convenience.

```
--name NAME          Alias name (default: aida)
--force              Install even if name exists on PATH
```

---

## Environment variables

| Variable | Description |
|----------|-------------|
| `AIDA_WORKSPACE_ROOT` | Override workspace root for the AIDA server |
| `AIDA_CONFIG_ROOT` | Override `.aida/` directory location |

---

## Feedback

We'd love to hear how AIDA works for your team. If something is missing, broken, or could be better — let us know on our [Slack community](https://www.gooddata.com/slack).
