> ## Documentation Index
> Fetch the complete documentation index at: https://docs.keldyn.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Resolve architecture risks (CWEs) with MCP

> Use the Keldyn MCP server to work through every Threagile security risk (CWE) on a use case's architecture and give each one a documented disposition: fixed, accepted, or out of scope.

Running Threagile analysis on an architecture surfaces a list of **security risks** — each tagged with a CWE, a STRIDE category, a severity, and the affected asset. "Resolving" that list doesn't just mean making the count go down; it means every risk ends up with a **documented disposition** — fixed in code/architecture, formally accepted, or marked out of scope — and, for anything you didn't fix, a record of **who decided and why**. This guide walks through that loop with an AI agent connected to the [Keldyn MCP server](/keldyn-mcp-server).

<Note>
  Every MCP tool call runs **as you**, scoped to the team you selected at sign-in. Recording a disposition or editing the architecture writes to your live Keldyn workspace — review the agent's proposed dispositions (especially justifications) before it saves them.
</Note>

## What you'll use

| Tool                                | Role                                                                                                                                          |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **`analyze-use-case-architecture`** | Runs (or fetches) Threagile analysis and returns the open risks, most-severe first, with a `proposedEdit` for each.                           |
| **`update-use-case-architecture`**  | Saves an architecture fix (e.g. add authentication, encrypt a link, add a trust boundary) that removes a risk at the source.                  |
| **`update-risk-tracking`**          | Records the disposition for a risk by `riskId` — `mitigated`, `accepted`, `false-positive`, `in-progress`, or `in-discussion` — plus who/why. |

<Tip>
  `update-risk-tracking` writes to the same audit-trail table that Pulse alerts, Review Packs, Portfolio dashboards, and the web app all read to know a risk is tracked. It's also kept in sync with the `risk_tracking` block inside the architecture model itself, so tracking a risk either way (via this tool, or by editing `risk_tracking` directly through `update-use-case-architecture`) shows up consistently everywhere.
</Tip>

## Before you start

<Steps>
  <Step title="Connect the MCP server">
    Follow [Keldyn MCP server](/keldyn-mcp-server) to connect Claude Code, Cursor, or another MCP client, and sign in to the team that owns the use case.
  </Step>

  <Step title="Have an architecture saved">
    Risk resolution needs a Threagile model to analyze. If the use case doesn't have one yet, run through [Generate an architecture from your codebase](/guides/generate-architecture) first.
  </Step>

  <Step title="Open your repository">
    Run the agent from the repository behind the use case so it can search for real implementation evidence (auth middleware, encryption config, rate limiting, etc.) before dispositioning anything.
  </Step>
</Steps>

## The three outcomes for a risk

Every Threagile risk should land in exactly one of these buckets before you're done:

| Outcome          | `update-risk-tracking` status | Meaning                                                                                                                                                                   |
| ---------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Fixed**        | `mitigated`                   | The code or architecture was changed so the risk is genuinely closed (e.g. added auth, encrypted a link, added a WAF).                                                    |
| **Accepted**     | `accepted`                    | The risk is real but the team has decided, with a documented reason, to live with it (e.g. compensating controls, low actual exposure, planned future work).              |
| **Out of scope** | `false-positive`              | Threagile's generic rule doesn't actually apply here (e.g. the "datastore" in question isn't queryable, or the link is between two internal, non-adversarial components). |

<Warning>
  `accepted` and `false-positive` dispositions **must** include a `justification` that says who decided and why — that's what makes the disposition defensible in a review or audit later. Don't accept or dismiss a risk just to make the count drop.
</Warning>

## 1. Pull the open risks

Run `analyze-use-case-architecture` scoped to security so RCM compliance findings don't crowd out the Threagile risks:

```text theme={null}
Using the Keldyn MCP server, run analyze-use-case-architecture for use
case <USE_CASE_ID> with source=security. List every risk with its
severity, CWE, and affected asset.
```

| Argument           | Purpose                                                                                                       |
| ------------------ | ------------------------------------------------------------------------------------------------------------- |
| `source`           | Set to `security` (or `threagile`) to return only Threagile risks — skips RCM compliance findings entirely.   |
| `run`              | `true` (default) runs a **fresh** analysis (up to \~3 minutes). Set `false` to read the last stored analysis. |
| `limit` / `offset` | Page through risks (default `limit` 32, max 100) when there are more than fit in one batch.                   |

<Tip>
  If a use case has a lot of both RCM findings and Threagile risks, high-severity RCM findings can otherwise fill an entire unfiltered batch before any Threagile risk shows up. `source=security` avoids that — use it whenever you're specifically working the CWE list.
</Tip>

The result's `summary.truncated` and `summary.remainingIssues` tell you if more risks remain; call the tool again with `offset` set to the previous `offset` plus the number of risks returned to get the next page.

## 2. Investigate before you disposition

For each risk, have the agent check the codebase for concrete evidence before deciding fixed / accepted / out of scope — don't let it guess.

```text theme={null}
For each risk above, search this repository for evidence relevant to
it (e.g. auth middleware, encryption config, input validation,
rate limiting) and tell me whether it looks already mitigated in code,
genuinely needs a fix, or looks like a false positive for our setup —
citing the specific files and line ranges you found.
```

<Tip>
  For a large batch, ask the agent to explore in parallel — e.g. one pass over auth/authorization code, one over network/encryption config, one over data validation — and report file paths plus short excerpts per risk. This is much faster than investigating risks one at a time.
</Tip>

## 3. Disposition each risk

### Fix it (→ `mitigated`)

If the risk is genuinely open, use the risk's `proposedEdit` to make a real change — either in application code, or as an architecture-model edit (e.g. adding an authentication method, an encrypted communication link, or a trust boundary) — then save the model if it changed and re-run the analysis to confirm the risk cleared.

```text theme={null}
For risk <RISK_ID>, apply the proposed edit: add authentication to the
link between the API and the internal worker service. Update the
architecture model with update-use-case-architecture, then re-run
analyze-use-case-architecture to confirm the risk is gone.
```

If the fix is purely in application code (not the architecture model itself — e.g. you added parameterized queries, so a SQL-injection risk is already mitigated in practice even though the model doesn't change), record it directly:

```text theme={null}
Risk <RISK_ID> (SQL/NoSQL injection on the Database link) is already
mitigated: all queries use parameterized bindings in the data access
layer. Call update-risk-tracking with status "mitigated" and a
justification citing that.
```

### Accept it (→ `accepted`)

```text theme={null}
Risk <RISK_ID> (Missing Authentication from API to internal worker) is
accepted: the worker is internal-only (internet:false) and only
reachable from the API on the private network; a service token is
planned but not yet built. Call update-risk-tracking with status
"accepted", and a justification that says this is accepted pending
that future work.
```

### Mark it out of scope (→ `false-positive`)

```text theme={null}
Risk <RISK_ID> (SQL/NoSQL injection on Object Storage) is a false
positive: Object Storage is an S3-compatible store with no SQL/NoSQL
query interface, so this is a generic rule match on the "datastore"
asset type, not a real query surface. Call update-risk-tracking with
status "false-positive" and that justification.
```

`update-risk-tracking` accepts:

| Field           | Purpose                                                                                                            |
| --------------- | ------------------------------------------------------------------------------------------------------------------ |
| `riskId`        | The Threagile risk id from `analyze-use-case-architecture` (e.g. `sql-nosql-injection@api@postgres@api>database`). |
| `status`        | `mitigated`, `accepted`, `false-positive`, `in-progress`, `in-discussion`, or `unchecked`.                         |
| `justification` | Who decided and why. Required in practice for `accepted` / `false-positive` — this is the audit trail.             |
| `ticket`        | Optional ticket/reference number for planned follow-up work.                                                       |
| `date`          | Optional ISO date the decision was made (defaults to now).                                                         |

<Tip>
  Keep justifications specific and file-referenced where possible — "accepted because the datastore is only reachable from the API service account and authorization is enforced in the application layer (see `src/services/...`)" is defensible months later; "not a big deal" is not.
</Tip>

## 4. Verify nothing is left unmarked

Re-run `analyze-use-case-architecture` with `source=security` until it reports zero remaining risks, or until every risk returned already carries a non-`unchecked` `status` (Threagile reflects tracked risks in the `status` field of each returned risk, e.g. `Accepted` / `Mitigated`).

```text theme={null}
Run analyze-use-case-architecture again for use case <USE_CASE_ID> with
source=security, run=true. Confirm every risk returned already has a
status other than Unchecked. List any that don't so we can disposition
them next.
```

<Note>
  A risk that's fully fixed in the architecture model (e.g. you added the missing authentication) simply **stops appearing** in the analysis — Threagile no longer finds it. A risk that's `accepted` or `false-positive` still appears (the underlying condition is still true), but its `status` reflects the disposition, and the web app's CWE views hide it from the "open issues" list once it's tracked.
</Note>

## Putting it together

A single prompt can drive the whole loop end to end; the agent will pause for your approval before writing anything back to Keldyn:

```text theme={null}
Using the Keldyn MCP server for use case <USE_CASE_ID>:
1. Run analyze-use-case-architecture with source=security to list every
   Threagile risk.
2. For each one, search this repository for concrete evidence and
   decide: fixed, accepted, or out of scope.
3. For anything you can fix now, apply the change (code or
   architecture via update-use-case-architecture) and re-verify.
4. For everything else, call update-risk-tracking with status
   "accepted" or "false-positive" plus a specific justification citing
   the evidence you found.
5. Re-run analyze-use-case-architecture with source=security once more
   and confirm nothing is left with status Unchecked.
Show me your proposed disposition for each risk before you call
update-risk-tracking.
```

<Tip>
  Acceptance criteria worth holding the agent to: every CWE ends up marked fixed, accepted, or out of scope; every accepted/out-of-scope disposition names who decided and why; and a fresh analysis run leaves nothing unmarked.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Generate an architecture from your codebase" icon="network" href="/guides/generate-architecture" arrow={true}>
    Infer and save the Threagile model this workflow analyzes.
  </Card>

  <Card title="From controls to code" icon="git-compare-arrows" href="/guides/mcp-controls-to-code" arrow={true}>
    Run the equivalent close-the-gap loop for RCM compliance controls.
  </Card>

  <Card title="Keldyn MCP server" icon="plug" href="/keldyn-mcp-server" arrow={true}>
    Connect your AI tool and see the full catalog of tools and built-in prompts.
  </Card>

  <Card title="Working with a use case" icon="box" href="/working-with-use-cases" arrow={true}>
    See tracked risks and mitigation coverage in the Architecture Workspace.
  </Card>
</CardGroup>
