Skip to main content
Glama
SK-CERT

Taranis NG MCP

Official
by SK-CERT
README.md
# Taranis NG MCP

[Taranis NG](https://github.com/SK-CERT/Taranis-NG) is an open-source platform for OSINT collection, analysis,
reporting, team collaboration, and asset-based vulnerability awareness, developed by SK-CERT with the wider CSIRT
community.

Taranis NG MCP lets AI assistants work with that platform through a typed, safety-oriented interface over the
Taranis NG REST API. It translates user goals into semantic operations instead of exposing the API as an
unstructured collection of HTTP calls.

Taranis NG implements the workflow:

1. **Assess** — collectors crawl sources and produce unstructured news items.
2. **Analyze** — analysts turn news items into structured report items.
3. **Publish** — report items become products, presenters render them (especially as PDF), and publishers deliver them.

This MCP exposes both day-to-day workflow tools and administrative configuration tools. They remain visibly
separate so that an MCP client can grant and reason about their permissions independently, while Taranis NG remains
authoritative for permissions and ACLs.

## Why use this MCP?

- **Workflow-native operations:** work with sources, news aggregates, report items, products, assets, and delivery
  presets using Taranis concepts rather than raw REST routes.
- **Safety at consequential boundaries:** mutations use typed inputs, explicit consent where appropriate,
  stable identifiers, bounded results, secret projection, read-after-write verification, and no blind mutation
  retries.
- **Small or direct tool catalogs:** use a six-tool discovery gateway for compact model context, or expose focused
  direct tools for clients that prefer a flat catalog.
- **Broad operational coverage:** support Assess, Analyze, Publish, customer assets, identity and ACL
  administration, authentication providers, MFA/passkey policy, nodes, automation, and federation.

## What you can ask it to do

### Build an ICS/IoT source portfolio

> Find and set up the best sources on IoT security, preferably RSS. Make them download articles into a group ICS/IoT
> that you create. Wait until Taranis actually collects the articles before reporting back.

### Group today's news by CVE

> Group today's news items by CVE.

### Create vulnerability reports

> Find all the Critical vulnerabilities in news items and create a vulnerability report for each. Also include
> items with High criticality when they affect widely used software such as Windows, WordPress, or Postfix.

### Generate a Microsoft vulnerability PDF

> Make me a PDF Vulnerability Report product with all the Microsoft vulnerability reports from today.

### Build a daily analyst brief

> Find the five news developments from today that could have the greatest operational impact on European public
> sector organizations. Create a daily intelligence report with the supporting news attached.

### Correlate a threat campaign

> Find news items that appear to describe the same threat campaign under different names. Group the evidence and
> create a campaign report covering the aliases, actors, targets, timeline, techniques, and indicators you can infer.

### Reconcile conflicting vulnerability intelligence

> Find sources that disagree about the affected versions, severity, or mitigation for the same CVE. Compare their
> claims and create a draft report that separates confirmed facts, contradictions, and unanswered questions.

### Find urgent exploitation gaps

> Find vulnerabilities that are reportedly being exploited but do not yet have a usable vendor fix. Mark the
> supporting news as important and create a watchlist report for each affected product.

### Discover an emerging trend

> Review the last seven days of news and find an important recurring pattern that no individual article names
> directly. Create a trend report explaining the pattern and link the evidence that led you to it.

You need a deployed [Taranis NG](https://github.com/SK-CERT/Taranis-NG) Core instance and a least-privilege user
identity. If you are new to Taranis NG, start with its
[Docker deployment guide](https://github.com/SK-CERT/Taranis-NG/blob/master/docker/README.md). For unattended MCP
use, a user-owned API key is recommended.

## Installation

Taranis NG MCP requires Python 3.12. On Debian or Ubuntu, install `python3.12-venv` if `python -m venv` reports that
`ensurepip` is unavailable.

```bash
git clone https://github.com/SK-CERT/Taranis-NG-MCP.git
cd Taranis-NG-MCP
python3 -m venv .venv
.venv/bin/pip install -r requirements.lock
.venv/bin/pip install -e . --no-deps
```

The lock file provides a reproducible dependency set. `pyproject.toml` retains compatible package ranges.

## Quick start

Point the MCP at the `/api/v1` URL of a running Taranis NG Core. A user-owned API key is the recommended credential
for unattended use:

```bash
export TARANIS_BASE_URL='https://taranis.example/api/v1'
export TARANIS_AUTH_MODE='api_key'
export TARANIS_API_KEY='...'
.venv/bin/python -m taranis_mcp
```

Password authentication is also available:

```bash
export TARANIS_BASE_URL='https://taranis.example/api/v1'
export TARANIS_AUTH_MODE='password'
export TARANIS_USERNAME='mcp-user'
export TARANIS_PASSWORD='...'
.venv/bin/python -m taranis_mcp
```

The default `gateway` catalog is the best starting point. It gives the client six tools: check the connection, list
domains, find and inspect semantic operations, then execute a read or an explicit mutation. The server supplies the
workflow and safety guidance through MCP, so the client does not need this README in its context.

Taranis NG MCP is a standard STDIO server. Configure your MCP client to launch the installed `taranis-mcp`
executable—or the absolute path to `.venv/bin/taranis-mcp`—and forward the authentication environment variables.
When launched manually, the process waits silently for MCP messages on standard input; that is normal.

## MCP client setup

Use the absolute path to this repository in the examples below. They use API-key authentication; for password mode,
replace `TARANIS_API_KEY` with `TARANIS_USERNAME` and `TARANIS_PASSWORD`, then set `TARANIS_AUTH_MODE=password`.
Never commit a configuration containing real credentials.

### Codex

Codex reads MCP configuration from `~/.codex/config.toml` or a trusted project's `.codex/config.toml`. Export the
three `TARANIS_*` variables from the API-key quick start, then add:

```toml
[mcp_servers.taranis]
command = "/absolute/path/to/Taranis-NG-MCP/.venv/bin/taranis-mcp"
env_vars = ["TARANIS_BASE_URL", "TARANIS_AUTH_MODE", "TARANIS_API_KEY"]
startup_timeout_sec = 20
tool_timeout_sec = 120
default_tools_approval_mode = "writes"
required = true
```

The fuller [codex-config.toml.example](codex-config.toml.example) forwards every supported optional setting. Run
`codex mcp list` to check the connection or use `/mcp` inside Codex. See the
[official Codex MCP documentation](https://developers.openai.com/codex/mcp).

### Claude Code and Claude Desktop

Add a local STDIO server to Claude Code:

```bash
claude mcp add \
  --env TARANIS_BASE_URL="$TARANIS_BASE_URL" \
  --env TARANIS_AUTH_MODE="$TARANIS_AUTH_MODE" \
  --env TARANIS_API_KEY="$TARANIS_API_KEY" \
  --transport stdio \
  taranis -- /absolute/path/to/Taranis-NG-MCP/.venv/bin/taranis-mcp
```

Run `claude mcp list` or use `/mcp` inside Claude Code to check the connection. For Claude Desktop, add the same
server to `claude_desktop_config.json` and restart the application:

```json
{
  "mcpServers": {
    "taranis": {
      "type": "stdio",
      "command": "/absolute/path/to/Taranis-NG-MCP/.venv/bin/taranis-mcp",
      "env": {
        "TARANIS_BASE_URL": "https://taranis.example/api/v1",
        "TARANIS_AUTH_MODE": "api_key",
        "TARANIS_API_KEY": "replace-me"
      }
    }
  }
}
```

See the [official Claude MCP documentation](https://code.claude.com/docs/en/mcp). These examples place the supplied
values in Claude's local configuration; protect that file as a credential-bearing file.

### OpenCode

Add the local server under `mcp` in `opencode.json` or `opencode.jsonc`:

```jsonc
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "taranis": {
      "type": "local",
      "command": [
        "/absolute/path/to/Taranis-NG-MCP/.venv/bin/taranis-mcp"
      ],
      "enabled": true,
      "environment": {
        "TARANIS_BASE_URL": "https://taranis.example/api/v1",
        "TARANIS_AUTH_MODE": "api_key",
        "TARANIS_API_KEY": "replace-me"
      }
    }
  }
}
```

Start OpenCode and ask it to use the `taranis` tools. See the
[official OpenCode MCP documentation](https://opencode.ai/docs/mcp-servers/). Protect this configuration if it
contains credentials.

## Capabilities

The MCP provides a lightweight six-tool gateway over the registered
semantic-operation catalog, optional direct catalogs, password/JWT or user API-key authentication, safe HTTP
behavior, and managed binary/PDF artifacts. The generated [operation inventory](OPERATIONS.md) reports the current
count. Its Core compatibility contract targets upstream commit
[`871f5aace3e781d7c1a45365c12b5ad50e68b2eb`](https://github.com/SK-CERT/Taranis-NG/commit/871f5aace3e781d7c1a45365c12b5ad50e68b2eb)
(`26.05.1-532-g871f5aac`).

| Area | MCP surface |
| --- | --- |
| Shared transport/auth | Provider-selected password/JWT, user API keys, precise MFA gates, safe retries, TLS, bounds, and compatibility reporting |
| Assess | Collector/source/group/word-list administration; bounded news navigation, intake, triage, evidence, grouping, and deletion |
| Analyze administration | Authentication/security, report schemas, AI/data providers, workflows, identity/ACL, bots, federation, settings, and maintenance |
| Analyze usage | Report CRUD, values, evidence links, locks, attachments, workflow discovery, dashboards, and guarded LLM drafts |
| Customer assets | Asset/group/template CRUD, CPE lookup, vulnerability matching context, and review state |
| Publish | Presenter/publisher infrastructure, product/preset configuration, products, preflight, preview artifacts, and explicit delivery |

See [OPERATIONS.md](OPERATIONS.md) for the generated executable catalog, [COVERAGE.md](COVERAGE.md) for current
coverage and boundaries, [CALLER-DOCUMENTATION.md](CALLER-DOCUMENTATION.md) for the
model-facing documentation standard, [TOOL-ORGANIZATION.md](TOOL-ORGANIZATION.md) for operation classification and
relationship rules, and [SOURCE-GUIDE.md](SOURCE-GUIDE.md) for the source-of-truth map.

## Tool catalogs

Domain handlers register semantic operations once; the server can present them through a compact discovery gateway,
as direct operation-specific tools, or through both interfaces.

The default gateway exposes exactly six tools:

- `system_check`
- `domains_list`
- `operations_list`
- `operation_describe`
- `query_execute`
- `action_execute`

Semantic operation names use `<domain>.<audience>.<intent>`, for example `assess.admin.create_source`. The gateway is complete but compact: discover an operation, inspect its schema, then run it through the read-only or mutation executor. A direct catalog generates tools such as `assess_admin_create_source` from the same definitions and adds `documentation_read` so tool-only clients can open focused guides without duplicating guide bodies in every operation description.

Usage/admin is an organization and context boundary, not an authorization boundary. Taranis permissions and ACLs remain authoritative.

## Configuration

Configuration is read from environment variables first. The catalog and capability-selection settings can then be
overridden by command-line arguments. Boolean environment values accept `1`, `true`, `yes`, or `on` and `0`,
`false`, `no`, or `off`, case-insensitively. Comma-separated values are trimmed and empty entries are ignored.

Authentication and Core connection:

| Variable | Default | Meaning |
| --- | --- | --- |
| `TARANIS_BASE_URL` | Required | Absolute HTTP(S) Core URL. It must end exactly once with `/api/v1`; a trailing slash is accepted and removed. |
| `TARANIS_AUTH_MODE` | `password` | Authentication mode: `password` or `api_key`. |
| `TARANIS_USERNAME` | Required in password mode | Core username. |
| `TARANIS_PASSWORD` | Required in password mode | Core password. |
| `TARANIS_AUTH_PROVIDER_ID` | Unset | Positive ID of an enabled local or LDAP form provider to select for password login. When omitted, Core uses its local provider only; a password is never tried against every LDAP provider. |
| `TARANIS_API_KEY` | Required in API-key mode | User-owned Core API key. |
| `TARANIS_VERIFY_TLS` | `true` | Whether to verify the Core server certificate. |
| `TARANIS_CA_BUNDLE` | Unset | Path to an existing CA bundle. `~` is expanded. When set, this bundle is used for TLS verification regardless of `TARANIS_VERIFY_TLS`. |
| `TARANIS_REQUEST_TIMEOUT` | `30` | General HTTP request timeout in seconds; accepts a number. |
| `TARANIS_ARTIFACT_TIMEOUT` | `120` | HTTP timeout in seconds for artifact transfers; accepts a number. |
| `TARANIS_MAX_CONCURRENCY` | `8` | Maximum concurrent requests; must be at least `1`. |
| `TARANIS_MAX_CONNECTIONS` | `16` | HTTP connection-pool limit; must be at least `TARANIS_MAX_CONCURRENCY`. |
| `TARANIS_GET_RETRIES` | `2` | Additional GET/HEAD retries after the initial attempt; must be `0` or greater. |
| `TARANIS_CORE_VERSION` | Unset | Deployed Core version or commit asserted from deployment evidence and reported by `system_check`. |

Result bounds, caching, artifacts, and logging:

| Variable | Default | Meaning |
| --- | --- | --- |
| `TARANIS_DEFAULT_PAGE_SIZE` | `25` | Default number of records returned by paged operations. |
| `TARANIS_MAX_PAGE_SIZE` | `200` | Maximum permitted page size. Page sizes must satisfy `1 <= default <= maximum <= 200`. |
| `TARANIS_METADATA_CACHE_TTL` | `30` | Metadata cache lifetime in seconds; accepts a number. |
| `TARANIS_ARTIFACT_DIR` | Runtime temp directory | Artifact storage directory. `~` is expanded. The generated default is `$XDG_RUNTIME_DIR/taranis-mcp-<uid>` when `XDG_RUNTIME_DIR` is set, otherwise the platform temporary directory with the same suffix. |
| `TARANIS_ARTIFACT_TTL` | `3600` | Artifact lifetime in seconds; must be at least `1`. |
| `TARANIS_ARTIFACT_MAX_BYTES` | `26214400` | Maximum artifact size in bytes (25 MiB by default); must be at least `1`. |
| `TARANIS_RESPONSE_MAX_BYTES` | `5242880` | Maximum ordinary response body size in bytes (5 MiB by default); must be at least `1`. |
| `TARANIS_LOG_LEVEL` | `INFO` | Logging threshold: `DEBUG`, `INFO`, `WARNING`, `ERROR`, or `CRITICAL` (case-insensitive). |

Catalog and capability selection:

| Variable | Default | Meaning |
| --- | --- | --- |
| `TARANIS_MCP_CATALOG` | `gateway` | Tool catalog profile: `gateway`, `direct`, or `hybrid`. |
| `TARANIS_MCP_MODULES` | Empty | Comma-separated legacy coarse domain/audience selectors, such as `assess.usage,assess.admin`. Retained during the 0.x series. |
| `TARANIS_MCP_TOOLSETS` | Empty | Comma-separated capability toolsets to enable. |
| `TARANIS_MCP_OPERATIONS` | Empty | Comma-separated semantic operation names to add. |
| `TARANIS_MCP_EXCLUDE_OPERATIONS` | Empty | Comma-separated semantic operation names to remove; exclusions take precedence over other selectors. |
| `TARANIS_MCP_READ_ONLY` | `false` | Remove every mutation from the effective catalog. |
| `TARANIS_MCP_ALLOW_LARGE_CATALOG` | `false` | Allow direct/hybrid profiles to expose more than 25 tools. |
| `TARANIS_MCP_DEVELOPER` | `false` | Add the read-only `raw_get` developer tool. |

Toolsets and individually selected operations are additive. With no capability filter, the selected catalog exposes
its normal default set.

Supported command-line arguments:

| Argument | Meaning |
| --- | --- |
| `-h` / `--help` | Print command usage and exit. |
| `--version` | Print the package version and exit. |
| `--catalog {gateway,direct,hybrid}` | Override `TARANIS_MCP_CATALOG`. |
| `--modules LIST` | Override `TARANIS_MCP_MODULES` with a comma-separated list. |
| `--toolsets LIST` | Override `TARANIS_MCP_TOOLSETS` with a comma-separated list. |
| `--operations LIST` | Override `TARANIS_MCP_OPERATIONS` with a comma-separated list. |
| `--exclude-operations LIST` | Override `TARANIS_MCP_EXCLUDE_OPERATIONS` with a comma-separated list. |
| `--read-only` / `--no-read-only` | Override `TARANIS_MCP_READ_ONLY`. |
| `--allow-large-catalog` / `--no-allow-large-catalog` | Override `TARANIS_MCP_ALLOW_LARGE_CATALOG`. |
| `--developer` / `--no-developer` | Override `TARANIS_MCP_DEVELOPER`. |

CLI list arguments replace their corresponding environment lists, including when an explicitly empty value is
passed. Connection, authentication, bounds, artifact, cache, and logging settings are environment-only. The
canonical defaults and validation are implemented in `src/taranis_mcp/config.py`.

`TARANIS_CORE_VERSION` may declare the deployed Core version or commit from deployment evidence. `system_check`
compares it with the audited baseline (`26.05.1-532-g871f5aac` /
[commit `871f5aac...`](https://github.com/SK-CERT/Taranis-NG/commit/871f5aace3e781d7c1a45365c12b5ad50e68b2eb)). When it is unset,
compatibility is reported as `UNVERIFIED` because the pinned Core `/isalive` response contains no version metadata;
reachability alone is never presented as version compatibility.

### Catalog examples

The default is `--catalog gateway`. Direct and hybrid catalogs should normally be narrowed to a coherent toolset:

```bash
.venv/bin/python -m taranis_mcp --catalog direct --toolsets assess-triage
.venv/bin/python -m taranis_mcp --catalog hybrid --toolsets publish-products
.venv/bin/python -m taranis_mcp --catalog direct --toolsets auth-security
.venv/bin/python -m taranis_mcp --catalog gateway --toolsets assets-inventory,assets-notifications
.venv/bin/python -m taranis_mcp --catalog gateway --read-only
.venv/bin/python -m taranis_mcp --catalog gateway --developer
```

The developer flag adds only `raw_get`, restricted to relative `/api/v1/...` GET requests. There is no arbitrary write escape hatch.

### Authentication behavior

API-key mode sends the user-owned core API key through the centralized transport and does not log in or refresh. It
is the recommended mode for unattended MCP use. Password mode can explicitly select a local or LDAP form provider
with `TARANIS_AUTH_PROVIDER_ID`; OIDC, OAuth2, and SAML require a browser redirect and are not MCP transport modes.
If Core requires MFA completion or enrollment, password authentication stops with a precise error: this MCP does
not collect TOTP codes or drive passkey ceremonies. Complete those workflows in a trusted interactive client, then
use a least-privilege user-owned API key for unattended access. See
`taranis-docs://authentication-providers` for provider lifecycle, provisioning, and MFA policy guidance.

PDF preview-ticket generation is unavailable in API-key mode because the pinned core endpoint unusually requires a
JWT in its JSON body; ordinary permissioned API calls still use the API key normally.

## Design goals

- Provide safe, typed MCP tools for the complete assess → analyze → publish workflow.
- Keep **usage** and **admin** capabilities in separate modules/tool namespaces and allow either set to be disabled.
- Match the deployed Taranis NG API, including its permission and ACL behavior.
- Cover Core/GUI functionality through verified semantic operations rather than route-shaped wrappers.
- Treat PDF presentation as a first-class publish workflow, not an afterthought.
- Make destructive tools unmistakable and require exact stable identifiers.
- Return concise, model-friendly results while retaining pagination metadata and server errors.

## Supported scope

The MCP covers Taranis NG functionality exposed through Core and used by the GUI: daily Assess, Analyze, Assets, and Publish workflows; configuration families; node registration; user and access administration; dashboards and state management; import/export; remote synchronization; and AI/data providers. Composite or bulk operations are added only for an observed user task, not merely because a GUI issues repeated requests.

Each operation records its permission, effect, stable identifiers, and payload semantics. Higher-impact operations receive typed validation, explicit authorization, bounded results, reconciliation, and contract tests.

## Architecture

The server is a modular Python monolith. Domain handlers register typed operations once; the gateway, direct tools,
and hybrid catalog are generated from that shared registry.

## MCP boundary

The MCP talks to the **core** REST API. Core in turn talks to collector, presenter, and publisher nodes using node API keys.

```text
Codex / MCP client
        |
        | MCP tools
        v
Taranis MCP server
        |
        | /api/v1/* + bearer JWT or user API key
        v
Taranis NG core
   |          |             |
   v          v             v
collector   presenter     publisher
nodes       nodes         nodes

assess --> report items --> product --> PDF presenter --> publisher preset
```

The MCP should not call node services directly for normal user workflows. Core owns database state, permission/ACL checks, node selection, preview caching, and the presentation/publishing orchestration.

The configured HTTP base should end at the versioned core prefix, normally `https://<host>/api/v1`. Tool adapters then use relative paths such as `/assess/osint-source-groups`. Configuration must reject ambiguous double-prefixes such as `/api/v1/api/v1` and must not silently downgrade HTTPS.

### Capability separation

Each registry operation carries a domain, `usage`/`admin` audience, effect, permissions, one or more capability
toolsets, optional search aliases, and sparse workflow relationships. The default gateway exposes the effective
registry through progressive discovery while keeping the initial tool catalog small. Direct and hybrid profiles
must be narrowed to at most 25 visible tools unless the operator explicitly opts into a larger catalog. See
[TOOL-ORGANIZATION.md](TOOL-ORGANIZATION.md) for the classification contract.

The separation is not a security mechanism. A process uses one Taranis identity, and core permissions/ACLs decide what that identity can do. Separate Codex entries with different credentials remain possible if a deployment later wants identity isolation.

Read-only administration discovery (for example attached collector nodes) still belongs to the admin family because it can expose infrastructure, endpoint, and parameter metadata. “Read-only” and “safe for ordinary users” are different classifications.

### MCP primitives

- **Tools** perform bounded reads and explicit mutations.
- **Resources/artifacts** carry generated or downloaded binary content such as a PDF preview; ordinary tool results should return metadata and a resource reference.
- **Prompts** are not exposed. Workflow guidance belongs in tool descriptions and focused resources until a repeatable multi-step interaction clearly benefits from a prompt template.
- Long-running collection should not be faked as a synchronous MCP tool. Source CRUD triggers collector refresh, but ingestion completion is asynchronous and needs separate health/status observation.

### Documentation presented to MCP clients

Client-facing guidance uses progressive disclosure so it remains useful to smaller models without filling their context with the whole repository README:

- Server instructions give a compact definition of Taranis NG, the Assess → Analyze → Publish flow, the Assets boundary, the safe discovery sequence, and the most important ID/retry warnings.
- `taranis-docs://quickstart` explains the operational flow and how to use the six gateway tools in plain language.
- `taranis-docs://data-model` explains the entity and stable-ID distinctions that commonly cause unsafe calls.
- `taranis-docs://web-collector` explains web-selector scope, inline article containers, publication-date handling,
  duplicate identity, and how to verify a saved web source.
- `taranis-docs://acl` explains independent grant dimensions, protected-object ID discovery, safe structural
  changes, last-entry deletion, inheritance, and the limits of direct-policy diagnosis.
- Direct and hybrid catalogs expose `documentation_read`; its `uri` choices are generated from the same static
  resources, and it returns exactly the corresponding Markdown. Gateway clients continue to use MCP resources.
- `domains_list` returns a short purpose, workflow position, and identity note for each domain.
- `operations_list` provides ranked natural-language search, capability filters, compact summaries, and at most
  three high-signal related operations.
- `operation_describe` adds domain, audience, toolsets, effect, identity/safety context, complete curated
  relationships, and exact schemas only after an operation is selected.
- Direct tools include the relevant domain identity note because they may be exposed without the discovery gateway.
  Shared operation descriptions only name focused guide URIs where they matter; guide content is not unfolded into
  the flat catalog.

Keep startup instructions under 800 characters, the quick start under 3,000 characters, and the data-model guide
under 4,000 characters. Prefer one clear definition in a shared resource over repeating background text across the
catalog. Put critical ID meaning directly on the corresponding input-schema field. Follow
[CALLER-DOCUMENTATION.md](CALLER-DOCUMENTATION.md) and [TOOL-ORGANIZATION.md](TOOL-ORGANIZATION.md) for every new or
revised operation.

## Sources of truth

The supported Core baseline does not provide a generated OpenAPI/Swagger contract. “API documentation” therefore means route docstrings and registrations in `src/core/api/`, augmented by schemas and implementation. Route docstrings frequently describe intent but not complete payloads or response bodies.

Every tool contract must be checked against all three sources:

1. **REST surface:** resource methods and `api.add_resource(...)` registrations under `Taranis-NG/src/core/api/`.
2. **Backend behavior:** managers, models, and Marshmallow schemas under `src/core/` and `src/shared/shared/schema/`; node behavior under `src/collectors`, `src/presenters`, and `src/publishers`.
3. **GUI behavior:** request construction under `src/gui-v3/src/api/` and payload construction in the corresponding views/components. The older `src/gui/` is useful as secondary compatibility evidence.

When the three disagree, tests against a pinned Taranis NG version decide the MCP contract. Record the disagreement rather than silently copying the GUI.

### Confidence levels

Each contract entry should carry one of these labels in implementation notes/tests:

- **Observed** — captured from the target running deployment.
- **Implemented** — established directly from backend code and schemas at the pinned commit.
- **GUI-derived** — inferred from the current v3 client payload or workflow.
- **Assumed** — not yet verified; assumptions must not drive a destructive tool.

Compatibility findings and contract tests preserve the evidence level so assumptions cannot silently become mutation contracts.

## Domain model and identity boundaries

- A **collector node** advertises collector definitions. A **collector** is a node-owned capability such as `WEB_COLLECTOR`; a **source** is a core-owned configuration bound to a collector ID.
- A **source group** contains sources, but assess lists **news item aggregates** by group. An aggregate can contain underlying **news items**, which have different IDs and deletion semantics.
- A configurable **attribute** is the reusable data type/field definition. A **report item type** embeds ordered attribute groups and group items that reference attributes.
- A **report item** is filled analyze data. A **product type** selects a presenter and presenter parameter values. A **product** selects report items and a product type.
- A **presenter** renders product data. A **publisher preset** configures a publisher that delivers already-rendered data. “PDF publisher” in conversation normally means the PDF presenter plus, optionally, a separate delivery publisher.

The MCP must preserve these IDs and terms in results even when it uses friendlier display labels.

## Implementation layout

```text
taranis-mcp/
├── README.md, COVERAGE.md, SOURCE-GUIDE.md, OPERATIONS.md
├── src/taranis_mcp/
│   ├── transport/          # base URL, auth, TLS, timeouts, decoding, redaction
│   ├── contracts/          # normalized semantic input/output models
│   ├── operations/         # assess, analyze, publish, and assets handlers
│   ├── registry/           # semantic definitions, discovery, validation, dispatch
│   ├── artifacts/          # bounded binary/PDF lifecycle
│   ├── observability/      # structured body-free logging
│   └── server/             # MCP tools, resources, and catalog profiles
├── tests/                  # unit, contract-shaped, and protocol tests
└── scripts/                # generated inventory and live validation
```

Usage/admin modules can be exposed as different profiles without moving domain adapters. No tool module constructs authentication headers or owns an HTTP client; transport, retries, JWT refresh, and redaction stay centralized.

### HTTP and authentication policy

All calls to the Taranis core API—including login, token refresh, JSON reads and writes, developer-mode reads, and binary preview downloads—must go through the process-wide `TaranisClient` in `src/taranis_mcp/transport/client.py`. Operation and server modules must never instantiate an HTTP library, build an `Authorization` header, or implement their own retry/reauthentication loop.

The client logs in on demand, refreshes shortly before JWT expiry, and performs one refresh-and-replay when an API call returns HTTP 401. Authentication is single-flight: concurrent initial calls share one login, and concurrent 401 responses share one refresh. If refresh fails, the client clears the stale session and performs a fresh login.

Retries are bounded by `TARANIS_GET_RETRIES` (default `2`, meaning at most three total attempts) with capped exponential backoff. Login and refresh may be retried because repeating them only issues a token. Ordinary automatic retries are limited to GET/HEAD transport failures and HTTP 502/503/504 responses. POST, PUT, and DELETE are not automatically replayed: a disconnect or timeout after a mutation may mean that the server applied it, so the operation must reconcile state explicitly instead. A 401 replay is separate from transient-server retries and happens at most once per request.

When adding an API adapter, use `runtime.client.json`, `json_with_status`, `cached_json`, or `binary`; add missing cross-cutting behavior to `TaranisClient` rather than locally. Request bodies, credentials, tokens, and binary data must not be logged. The runtime lifespan closes the shared client so pooled connections and in-flight resources do not linger after MCP shutdown.

## Design rules

- Stable IDs, not names, drive mutations; return both in discovery results.
- List tools accept bounded pagination. Never auto-fetch an unbounded collection unless a deliberately named helper does so.
- Do not expose every REST parameter simply because it exists. Tools omit advanced filters and mutations unless they support an observed user outcome.
- Admin tools accept semantic inputs. For example, source parameters should be keyed by collector parameter key, and the adapter should resolve them to the API's `parameter_values` representation.
- Return backend permission/ACL failures distinctly from validation, network, and node-service failures.
- Destructive calls should describe the target immediately before execution and must not cascade beyond the exact API operation requested.
- Tests create uniquely named disposable objects and clean them up in reverse dependency order.
- Empty success bodies are success only when the HTTP status is successful; use read-after-write verification before reporting the resulting state.
- PUT adapters use fetch → normalize → merge requested change → replace → verify. They must not send arbitrary response-only fields back to Marshmallow loaders.
- Word-list updates are explicit full replacements of the nested categories and entries. Callers must first list the current word list and include every category/entry they intend to retain.
- Retry GET/HEAD only by default. POST, PUT, and DELETE require operation-specific idempotency evidence; a network timeout after a mutation is an “outcome unknown” state followed by reconciliation, not an automatic replay.
- Never log bearer tokens, API keys, node keys, web basic-auth parameters, proxy credentials, publisher credentials, report contents, or generated document bytes.

## Error and consistency model

Tool errors should identify both layer and certainty:

| Category | Examples | MCP behavior |
| --- | --- | --- |
| Client validation | Unsupported source type, malformed selector prefix, invalid pagination | Reject before REST call with the exact field and accepted values. |
| Authentication/authorization | 401, missing permission, ACL denial | Distinguish invalid credentials from insufficient permission where server evidence allows it. |
| Conflict/dependency | Default group mutation, referenced attribute, stale replacement input | Preserve the object; explain the dependency or refresh requirement. |
| Core validation/defect | Marshmallow error, unexpected empty/malformed response, 500 | Return sanitized server evidence and record a reproducible compatibility finding. |
| Node failure | Collector refresh, presenter generation, publisher delivery unavailable | Explain that core was reached and name the downstream stage without exposing node keys. |
| Outcome unknown | Timeout/disconnect after a mutation was sent | Do not retry blindly; reconcile by listing/fetching state and report uncertainty if unresolved. |
| Partial success | Some publisher presets succeed and others fail | Return every result and make successful external side effects explicit; never auto-repeat all publishers. |

Core mutations and node effects are not one transaction. Source configuration can commit before collector refresh fails, and product rendering can succeed before one delivery fails. The MCP therefore reports stages rather than a single undifferentiated success flag.

## Validation and testing

Run the test suite and generated-catalog check with:

```bash
.venv/bin/python -m pytest -q
.venv/bin/python scripts/generate_operations.py --check
.venv/bin/ruff check src tests scripts
```

Run the live harness in its default read-only mode with:

```bash
export TARANIS_BASE_URL='https://taranis.example/api/v1'
export TARANIS_USERNAME='mcp-validator'
export TARANIS_PASSWORD='...'
.venv/bin/python -u scripts/live_validate.py
```

The harness uses the same authentication and TLS environment variables as the server, including API-key mode,
`TARANIS_CA_BUNDLE`, and `TARANIS_VERIFY_TLS`. If an HTTP proxy is configured, add the Core hostname to `NO_PROXY`
when it must be reached directly.

`--mutations` is only for an explicitly authorized validation window on a disposable or non-production instance.
The harness does not mutate pre-existing production news, delete production news items, or attempt delivery by
default. It never prints secrets or response content, uses uniquely named disposable records, and performs cleanup
in `finally`.

The test strategy comprises:

- **Unit tests:** URL/auth handling, payload projection, selector parsing, pagination bounds, redaction, and response normalization.
- **Mock contract tests:** pinned JSON/binary response shapes for registered operations, including empty bodies and known quirks.
- **Live integration tests:** uniquely named disposable objects against a non-production instance, with explicit cleanup and a retained cleanup manifest if interrupted.
- **Permission tests:** usage-only, admin-only, missing permission, and ACL-limited identities.
- **Workflow tests:** source → group → news navigation; attribute → report type; report item → product → PDF preview → test publisher.
- **Template regression tests:** exercise MCP previews against representative presenter templates using visual or golden comparisons.
- **Upgrade tests:** rerun contract capture and diff normalized schemas before changing the supported Taranis NG commit/range.