Skip to main content
Glama
README.md
<!-- mcp-name: io.github.SauceTaster/assemblyline-mcp -->

# AssemblyLine MCP

A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for
[AssemblyLine 4](https://cybercentrecanada.github.io/assemblyline4_docs/) — the
Canadian Centre for Cyber Security's open-source **file triage and malware
analysis** platform. It lets MCP-compatible AI clients (Claude Desktop, Claude
Code, Cursor, VS Code, …) submit files, hashes, and URLs for analysis, retrieve
results, search across AssemblyLine indices, and triage alerts.

[![PyPI version](https://img.shields.io/pypi/v/assemblyline-mcp.svg)](https://pypi.org/project/assemblyline-mcp/)
[![Python versions](https://img.shields.io/pypi/pyversions/assemblyline-mcp.svg)](https://pypi.org/project/assemblyline-mcp/)
[![CI](https://github.com/SauceTaster/assemblyline-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/SauceTaster/assemblyline-mcp/actions/workflows/ci.yml)
[![E2E](https://github.com/SauceTaster/assemblyline-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/SauceTaster/assemblyline-mcp/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/SauceTaster/assemblyline-mcp/branch/main/graph/badge.svg)](https://codecov.io/gh/SauceTaster/assemblyline-mcp)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![MCP](https://img.shields.io/badge/MCP-server-blue)](https://modelcontextprotocol.io)

---

## Features

- **Built for agents, not just API parity** — `al_analyze` submits and waits, then
  returns a concise **verdict digest** (verdict, flagged services, heuristics, AV
  hits, network IOCs, children) instead of a raw blob; `al_submission_iocs` and
  `al_find_related` cover IOC extraction and pivoting. Guided **MCP prompts**
  (`triage_file`, `investigate_hash`, `review_alert`) ship the AssemblyLine triage
  workflow and scoring semantics **inside the server — no skill install required**.
- **Full AssemblyLine 4 coverage** — submit/ingest, inspect submissions and
  per-file results, Lucene search across every index, alert triage, and system
  metadata, built on the official [`assemblyline-client`](https://github.com/CybercentreCanada/assemblyline_client).
- **Safe by default** — destructive/admin operations (deletes, workflow runs,
  user/service/system management) are **not even registered** unless you opt in
  with `AL_ALLOW_ADMIN=true`, and every tool carries MCP `readOnlyHint` /
  `destructiveHint` annotations so clients can prompt before risky actions.
- **Two transports** — `stdio` for local desktop clients and **streamable HTTP**
  for networked/containerized deployments (with optional `admin`-scope auth).
- **Built-in end-to-end self-test** — `assemblyline-mcp selftest` boots an
  in-process mock AssemblyLine, exercises every tool, and prints a PASS/FAIL
  report. No live server, no pytest, any architecture.
- **Production-ready packaging** — `uvx`-installable, typed (`py.typed`),
  multi-arch Docker image, and a published [MCP registry](https://github.com/modelcontextprotocol/registry) manifest.

## Available tools

All tools are prefixed `al_`. Read-only tools are always available; write tools
change state but are non-destructive; **admin** tools require `AL_ALLOW_ADMIN=true`.

| Group | Tools |
|-------|-------|
| **Analysis (agentic)** | `al_analyze` (submit + wait + verdict digest), `al_submission_digest`, `al_submission_iocs`, `al_find_related` |
| **Submission** | `al_submit`, `al_ingest`, `al_ingest_get_messages`, `al_submission`, `al_submission_full`, `al_submission_summary`, `al_submission_tree`, `al_submission_report`, `al_submission_file`, `al_submission_is_completed`, `al_submission_list`, `al_submission_set_verdict` |
| **Search** | `al_search`, `al_search_facet`, `al_search_stats`, `al_search_histogram`, `al_search_grouped`, `al_search_fields` |
| **Files / results** | `al_file_info`, `al_file_result`, `al_file_score`, `al_file_children`, `al_file_strings`, `al_file_hex`, `al_file_ascii`, `al_file_ai_summary`, `al_file_download`, `al_result`, `al_error`, `al_hash_search` |
| **Alerts** | `al_alert`, `al_alert_list`, `al_alert_grouped`, `al_alert_statistics`, `al_alert_label`, `al_alert_set_priority`, `al_alert_set_status`, `al_alert_set_verdict`, `al_alert_take_ownership` |
| **System** | `al_whoami`, `al_user_quotas`, `al_help_configuration`, `al_help_constants`, `al_help_classification`, `al_heuristics` |
| **Admin** *(gated)* | `al_submission_delete`, `al_file_delete_from_filestore`, `al_alert_remove_label`, `al_workflow_run`, `al_workflow_delete`, `al_signature_change_status`, `al_signature_delete`, `al_badlist_delete`, `al_safelist_delete`, `al_service_delete`, `al_system_set_message`, `al_system_clear_message` |

### Guided workflows (MCP prompts — no skill required)

The server registers MCP **prompts** that encode the AssemblyLine triage workflow
and scoring semantics, so the agent interprets results correctly out of the box.
These surface natively in any MCP client (Claude Desktop/Code, Cursor, …):

- **`triage_file`** — triage a file/hash and report a verdict.
- **`investigate_hash`** — known results, IOCs, and related activity for a hash.
- **`review_alert`** — assess an alert as true/false positive.

A richer, Claude-specific **Agent Skill** is also available under
[`skills/assemblyline-triage/`](skills/assemblyline-triage/) — but it's optional;
the server is fully usable without installing anything.

## Installation

You need a running [AssemblyLine 4](https://cybercentrecanada.github.io/assemblyline4_docs/installation/overview/)
instance and an API key (or username/password).

```bash
# Run without installing (recommended)
uvx assemblyline-mcp

# Or with pipx / pip
pipx install assemblyline-mcp
pip install assemblyline-mcp
```

### Docker

```bash
docker run --rm -i \
  -e AL_URL=https://al.example.org \
  -e AL_APIKEY_USER=analyst \
  -e AL_APIKEY=your-keyname:secret \
  ghcr.io/SauceTaster/assemblyline-mcp:latest
```

## Configuration

Configuration is via `AL_`-prefixed environment variables (or a `.env` file).

| Variable | Required | Default | Description |
|----------|:--------:|---------|-------------|
| `AL_URL` | ✅ | – | Base URL of the AssemblyLine instance (e.g. `https://al.example.org`). |
| `AL_APIKEY_USER` | ✅¹ | – | Username paired with `AL_APIKEY`. |
| `AL_APIKEY` | ✅¹ | – | API key secret (`keyname:secret`). |
| `AL_USERNAME` | ✅¹ | – | Username (for password auth). |
| `AL_PASSWORD` | ✅¹ | – | Password (for password auth). |
| `AL_VERIFY_SSL` | | `true` | Verify the AssemblyLine TLS certificate. |
| `AL_TIMEOUT` | | `60` | Per-request timeout (seconds). |
| `AL_RETRIES` | | `3` | Finite retry count (never infinite). |
| `AL_ALLOW_ADMIN` | | `false` | Register admin/destructive tools. |
| `AL_MAX_DOWNLOAD_BYTES` | | `10485760` | Cap on `al_file_download` output. |
| `AL_TRANSPORT` | | `stdio` | `stdio` or `http`. |
| `AL_HOST` / `AL_PORT` / `AL_PATH` | | `127.0.0.1` / `8000` / `/mcp/` | HTTP transport bind settings. |
| `AL_ALLOW_INSECURE_BIND` | | `false` | Permit binding http to a non-loopback host (no built-in auth). |
| `AL_MASK_ERROR_DETAILS` | | `true` | Hide internal exception details from clients. |

¹ Provide **either** `AL_APIKEY_USER` + `AL_APIKEY` **or** `AL_USERNAME` + `AL_PASSWORD`.

### Claude Code

```bash
claude mcp add assemblyline \
  --env AL_URL=https://al.example.org \
  --env AL_APIKEY_USER=analyst \
  --env AL_APIKEY=your-keyname:secret \
  -- uvx assemblyline-mcp
```

### Claude Desktop

Add to `claude_desktop_config.json`
([see example](examples/claude_desktop_config.json)):

```json
{
  "mcpServers": {
    "assemblyline": {
      "command": "uvx",
      "args": ["assemblyline-mcp"],
      "env": {
        "AL_URL": "https://al.example.org",
        "AL_APIKEY_USER": "analyst",
        "AL_APIKEY": "your-keyname:secret"
      }
    }
  }
}
```

### VS Code

See [examples/vscode_mcp.json](examples/vscode_mcp.json).

## Usage

```bash
assemblyline-mcp                    # serve over stdio (default)
assemblyline-mcp serve -t http -p 8000   # serve over streamable HTTP
assemblyline-mcp selftest          # run the built-in end-to-end self-test
assemblyline-mcp doctor            # validate config + connectivity to a real AL
assemblyline-mcp version
```

`doctor` performs the AssemblyLine connection handshake and reports the
authenticated user — useful as a container `HEALTHCHECK` and for first-run setup.

## Development

```bash
git clone https://github.com/SauceTaster/assemblyline-mcp
cd assemblyline-mcp
uv sync                       # creates .venv with Python 3.12 and all dev deps

uv run ruff check .           # lint
uv run ruff format --check .  # formatting
uv run mypy                   # type-check
uv run pytest                 # full test suite (unit + integration + e2e, mock-backed)
uv run assemblyline-mcp selftest   # the shippable e2e diagnostic
```

The whole test suite runs against an in-process **mock AssemblyLine** server, so
no live instance is required and it works on any architecture (including Apple
Silicon). An opt-in suite exercises a real instance — see
[CONTRIBUTING.md](CONTRIBUTING.md).

### Debugging with the MCP Inspector

```bash
uv run fastmcp dev src/assemblyline_mcp/server.py
```

### End-to-end with Docker Compose

```bash
docker compose -f docker-compose.e2e.yml up --build
# MCP server (http) on http://localhost:8000/mcp/, backed by the mock AssemblyLine
```

## Security

- Credentials are read only from the environment and never logged.
- Destructive tools are gated behind `AL_ALLOW_ADMIN`; on the HTTP transport they
  additionally require an `admin`-scoped token.
- `al_file_download` returns base64 bytes only — samples are **never executed**;
  oversized files (`AL_MAX_DOWNLOAD_BYTES`) are refused without being fetched.
- **The HTTP transport ships with no built-in authentication for read/write
  tools.** It refuses to bind a non-loopback address unless you set
  `AL_ALLOW_INSECURE_BIND=true`; before exposing it, front the server with an
  auth-enforcing reverse proxy or a FastMCP `AuthProvider`.

See [SECURITY.md](SECURITY.md) for the disclosure policy.

## Acknowledgements

- [AssemblyLine 4](https://github.com/CybercentreCanada/assemblyline) and the
  [`assemblyline-client`](https://github.com/CybercentreCanada/assemblyline_client)
  by the Canadian Centre for Cyber Security.
- Built with [FastMCP](https://gofastmcp.com).

## License

[MIT](LICENSE)