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

# AE-MCP-002

> Every remote MCP server origin must be known — present in the repo's trusted-remote allowlist. Unknown remote origins are flagged (OWASP MCP Top 10 beta, MCP09 Shadow MCP Servers).

**Rule ID:** AE-MCP-002 · **Severity:** <Badge color="orange">High</Badge> · **Category:** MCP Safety · **Auto-fixable:** No

## Why this rule

A coding agent should not be pointed at arbitrary remote infrastructure without a team review. Unknown public MCP origins are a supply-chain and data-exfiltration risk — tool calls travel to infrastructure outside the team's visibility, and there is no way to audit what the server receives or returns. See the [MCP safety model](/docs/concepts/mcp-safety-model) for the trust boundary Charter enforces.

## What triggers it

Charter scans MCP configuration files for server entries that use a remote `url`, Gemini CLI's `httpUrl` (streamable HTTP), or a transport type of `http` or `sse`. For each remote server, Charter extracts the URL host and compares it against two sources:

1. The built-in catalog of vendor-operated hosts (known, reviewed third-party MCP providers)
2. The repo's [`charter.yaml`](/docs/config/charter-yaml) `mcp.trustedRemotes` list

Any public host not present in either source fires High.

**Scanned files:** `.mcp.json`, `mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, `.gemini/settings.json`

**Always exempt (never fire):**

| Origin type                | Examples                                   |
| -------------------------- | ------------------------------------------ |
| Loopback                   | `localhost`, `127.0.0.1`, `::1`            |
| RFC 1918 private ranges    | `192.168.x.x`, `10.x.x.x`, `172.16–31.x.x` |
| Link-local                 | `169.254.x.x`, `fe80::`                    |
| Internal TLDs              | `*.localhost`, `*.local`, `*.internal`     |
| Dynamic / unresolvable URL | `${API_URL}` — no parseable host, skipped  |

<Note>
  If no `mcp.trustedRemotes` allowlist exists in `charter.yaml`, every non-local remote server is flagged as unverifiable. The finding's summary makes the remediation clear: add an allowlist entry for any intentional remote server.
</Note>

## Examples

<Tabs>
  <Tab title="Failing">
    ```json .mcp.json theme={null}
    {
      "mcpServers": {
        "custom": {
          "type": "http",
          "url": "https://unknown-startup.com/mcp"
        }
      }
    }
    ```

    ```
    # unknown-startup.com not in catalog or charter.yaml trustedRemotes
    # Charter flags: unknown remote origin — fires High
    ```
  </Tab>

  <Tab title="Passing">
    ```json .mcp.json theme={null}
    {
      "mcpServers": {
        "internal-tool": {
          "type": "http",
          "url": "https://api.your-internal-tool.com/mcp"
        }
      }
    }
    ```

    ```yaml charter.yaml theme={null}
    mcp:
      trustedRemotes:
        - "api.your-internal-tool.com"
    ```
  </Tab>
</Tabs>

## How to fix

If the remote origin is intentional, add its hostname to `charter.yaml` under `mcp.trustedRemotes` and commit the change. This signals that your team has reviewed the server.

```yaml charter.yaml theme={null}
mcp:
  trustedRemotes:
    - "api.your-internal-tool.com"
    - "mcp.your-vendor.com"
```

If the origin is unknown or unintended, replace it with a trusted catalog host or a local alternative.

There is no auto-fixer for this rule — the trust decision requires a human review before it can be encoded in config.

<Tip>
  The effective allowlist is: built-in catalog hosts + your `mcp.trustedRemotes`. Local origins (loopback, RFC 1918 private ranges, `.local`, `.internal`) are always exempt without any listing. Allowlist matching is host-only — no scheme or path component is compared.
</Tip>

## Score impact

`High` (−10 per finding). No hard cap — caps are reserved for raw-secret and Blocker findings. See [Scoring and caps](/docs/concepts/scoring-and-caps).

## Edge cases

Local and internal origins are always exempt without any listing — loopback (`localhost`, `127.0.0.1`, `::1`), RFC 1918 private ranges, link-local addresses, and internal TLDs (`*.localhost`, `*.local`, `*.internal`). A dynamic `${VAR}` URL has no parseable host and is skipped. Allowlist matching is host-only — no scheme or path component is compared.

## Related rules

<CardGroup cols={2}>
  <Card title="AE-MCP-001" icon="shield" href="/rules/AE-MCP-001">
    MCP servers must be pinned to exact versions.
  </Card>

  <Card title="AE-MCP-003" icon="shield" href="/rules/AE-MCP-003">
    Remote MCP servers must declare auth.
  </Card>
</CardGroup>

## CLI

```bash theme={null}
charter explain AE-MCP-002
```
