# @cognium-ai/mcp-server

An [MCP](https://modelcontextprotocol.io) server exposing Cognium's security
scanning and spec-conformance tools over **stdio**. Backed by the
[`circle-ir-ai`](https://www.npmjs.com/package/circle-ir-ai) engine.

## Tools Overview

| Tool | Category | Description |
|------|----------|-------------|
| `find_credential_exposure` | Pillar I | Detect hardcoded secrets (AWS, GitHub, OpenAI, etc.) |
| `check_license_compliance` | Pillar I | Flag copyleft licenses in dependencies |
| `find_stale_dependencies` | Pillar I | Check npm registry for outdated packages |
| `find_install_script_risk` | Pillar I | Detect malicious install script patterns |
| `find_typosquats` | Pillar I | Detect typosquat package names |
| `find_excessive_permissions` | Pillar I | Audit MCP config permissions |
| `verify_spec_conformance` | Spec | Analyze code-to-spec alignment |
| `find_spec_drift` | Spec | Report spec/code drift |
| `find_pattern` | Utility | Regex search over code |

## Install & run

```bash
npm install -g @cognium-ai/mcp-server
cognium-mcp          # speaks MCP JSON-RPC over stdin/stdout
```

Or wire it into an MCP client config:

```json
{
  "mcpServers": {
    "cognium": {
      "command": "npx",
      "args": ["-y", "@cognium-ai/mcp-server"]
    }
  }
}
```

Inspect it interactively:

```bash
npx @modelcontextprotocol/inspector cognium-mcp
```

## Pillar I Security Tools

All Pillar I tools return a common envelope:

```jsonc
{
  "findings": [{ "kind": "...", "severity": "...", "location": {...}, "evidence": {...}, "suggested_action": "..." }],
  "summary": { "total": N, "by_severity": {...}, "truncated": false }
}
```

- `location.span` is **1-indexed line numbers** `[start_line, end_line]`
- All tools accept `severity_floor` to filter results

### `find_credential_exposure`

Detect hardcoded secrets using SAST pattern matching.

| Input | Type | Description |
|-------|------|-------------|
| `code_root` | string | Absolute path to scan |
| `include_git_history` | boolean | Scan git history (default: false) |
| `severity_floor` | enum | Minimum severity: info/low/medium/high/critical |

**Evidence:** `rule_id`, `match_snippet` (redacted), `confidence`

### `check_license_compliance`

Flag copyleft licenses in npm/Cargo dependencies.

| Input | Type | Description |
|-------|------|-------------|
| `code_root` | string | Absolute path to scan |
| `include_dev_deps` | boolean | Include devDependencies (default: false) |
| `severity_floor` | enum | Minimum severity |

**Severity:** AGPL/SSPL=critical, GPL=high, LGPL/MPL=medium, EUPL/CPAL=low

### `find_stale_dependencies`

Check npm dependencies for staleness and known vulnerabilities.

| Input | Type | Description |
|-------|------|-------------|
| `code_root` | string | Absolute path to scan |
| `include_dev_deps` | boolean | Include devDependencies (default: false) |
| `severity_floor` | enum | Minimum severity |
| `check_vulnerabilities` | boolean | Query OSV for CVEs (default: true) |

**Staleness severity:** >3 years=high, >2 years=medium, >1 year=low

**Vulnerability severity:** CVSS ≥9.0=critical, ≥7.0=high, ≥4.0=medium, <4.0=low

**Evidence:**
- Staleness: `dependency`, `current_version`, `latest_version`, `days_since_publish`, `last_publish_date`
- Vulnerability: `dependency`, `current_version`, `vuln_id`, `cve`, `cvss`, `summary`

### `find_install_script_risk`

Detect malicious patterns in npm lifecycle scripts (preinstall, postinstall, etc.).

| Input | Type | Description |
|-------|------|-------------|
| `code_root` | string | Absolute path to scan |
| `severity_floor` | enum | Minimum severity |

**Rules:** piped downloads (critical), eval/exec (critical), base64 decode (high), env exfiltration (high), network downloads (medium), hidden file writes (medium)

### `find_typosquats`

Detect typosquat package names using Levenshtein distance and homoglyph detection.

| Input | Type | Description |
|-------|------|-------------|
| `code_root` | string | Absolute path to scan |
| `include_dev_deps` | boolean | Include devDependencies (default: false) |
| `severity_floor` | enum | Minimum severity |
| `check_homoglyphs` | boolean | Detect visual deception attacks (default: true) |
| `check_popularity` | boolean | Query npm for download counts (default: false) |

**Detection methods:**
- **Levenshtein:** distance=1 is high, distance=2 is medium
- **Homoglyph:** detects visual deception (`rn`→`m`, `1`→`l`, `0`→`o`, etc.) - always high severity

**Evidence:** `dependency`, `similar_to`, `detection_method`, `distance`, `homoglyph`, `target_downloads`

### `find_excessive_permissions`

Audit MCP server configurations for overly broad permissions.

| Input | Type | Description |
|-------|------|-------------|
| `code_root` | string | Absolute path to scan |
| `severity_floor` | enum | Minimum severity |
| `deep_analysis` | boolean | Run CircleIR to detect undisclosed capabilities (default: true) |

**Config formats:** skill bundle (mcp.json), Claude Desktop, Cursor

**Static rules:** broad filesystem paths, secret env vars, unrestricted network, dangerous commands

**Deep analysis:** Cross-references declared permissions against actual code behavior. Detects undisclosed network, filesystem, or process execution capabilities.

---

## Spec Tools

### `verify_spec_conformance`

Analyze how well code conforms to its [Specifica](https://www.npmjs.com/package/circle-ir-ai)
spec.

**Input**

| field       | type   | description                                   |
|-------------|--------|-----------------------------------------------|
| `spec_dir`  | string | Path to the `.specifica/` directory (spec.md) |
| `code_root` | string | Path to the code root to analyze              |

**Output**

```jsonc
{
  "alignment_score": 100,           // 0–100
  "requirements": [
    { "id": "REQ-001", "status": "covered",   "evidence": { "function": "calculateTotal" } },
    { "id": "REQ-002", "status": "partial",   "evidence": { "function": "applyDiscount" } },
    { "id": "REQ-003", "status": "uncovered", "evidence": {} }
  ],
  "summary": "2/3 requirements covered (67%); 0 undocumented behaviors"
}
```

`status` is derived from the engine's match confidence:

- `covered` — confidence **≥ 0.7**
- `partial` — matched but confidence **< 0.7**
- `uncovered` — no matching code behavior

> The engine has no native "partial" state; it is a boundary heuristic at
> confidence 0.7 over the engine's already-filtered match set
> (min confidence 0.3).
>
> **Empty spec:** if no requirements parse from `spec_dir` (missing, empty, or
> malformed `spec.md`), `alignment_score` is reported as **0** with an
> explanatory `summary` — never a misleading 100 — so a `>= N` gate fails
> safely instead of passing on a spec that was never read.

### `find_spec_drift`

Report drift in both directions.

**Input:** same as above (`spec_dir`, `code_root`).

**Output**

```jsonc
{
  "drifts": [
    { "kind": "code_without_spec", "location": "deleteUser",  "suggested_action": "Document deleteUser: ..." },
    { "kind": "spec_without_code", "location": "REQ-004",     "suggested_action": "Implement REQ-004: ..." }
  ]
}
```

- `code_without_spec` — an undocumented public behavior (engine
  `undocumentedBehaviors`).
- `spec_without_code` — a requirement with no matching code (engine
  `uncoveredRequirements`).

### `find_pattern`

Search file contents under a code root with a JavaScript regular expression.

**Input**

| field       | type   | description                                       |
|-------------|--------|---------------------------------------------------|
| `pattern`   | string | JS `RegExp` source (applied with the global flag) |
| `code_root` | string | Path to the code root to search                   |

**Output**

```jsonc
{
  "matches": [
    { "file": "src/cart.ts", "span": [7, 7], "snippet": "return unitPrice * quantity;" }
  ]
}
```

> `span` is **line-based** `[startLine, endLine]` (1-based), **not** character
> offsets. Files larger than 1 MiB, files detected as binary (NUL byte in the
> leading window), and common build/vendor directories are skipped; results are
> capped at 1000 matches.
>
> The pattern is validated once up front (an invalid regex errors even over an
> empty tree). A 2 s wall-clock budget bounds total scanning between matches;
> it does not interrupt a single `exec`, so the 1 MiB per-file cap is the real
> bound against a catastrophic-backtracking pattern.

## Field mapping (engine → MCP boundary)

The engine emits camelCase; this server renames to snake_case at the boundary.

| MCP field                  | engine source (`SpecGapResult`)                         |
|----------------------------|---------------------------------------------------------|
| `alignment_score`          | `specAlignmentScore`                                    |
| `requirements[].id`        | `coveredRequirements[].requirement.id` / `uncoveredRequirements[].requirement.id` |
| `requirements[].status`    | derived from `coveredRequirements[].confidence` (≥0.7)  |
| `requirements[].evidence.function` | `coveredRequirements[].matchedFunction`         |
| `drifts` (code_without_spec) | `undocumentedBehaviors[]` → `{functionName, suggestion}` |
| `drifts` (spec_without_code) | `uncoveredRequirements[]` → `{requirement.id, reason}`   |

## Schema stability

Tool input/output field names are **draft-stable through 2026-08-31**. Additive
fields are non-breaking; renames or removals are breaking changes (major bump).

## Runtime support

Works under **Node** (≥18) and **Bun**. circle-ir's WASM is resolved via
`createRequire(import.meta.url)` from `node_modules/circle-ir`, so no symlink is
required under either runtime. Both are exercised by the CI stdio smoke matrix.

## Development

```bash
npm install        # installs deps and builds (prepare → tsc)
npm run typecheck
npm test           # vitest: mapper unit tests
npm run smoke      # spawn dist/index.js over stdio against the demo fixture
```

## License

PolyForm Noncommercial License 1.0.0 — see [LICENSE](./LICENSE).
