Skip to main content
Glama
parsiya

Trailmark MCP Server

by parsiya
README.md
# Trailmark MCP Server
Trailmark MCP Server is a standalone MCP wrapper around [railofbits/trailmark][trail].

While I do understand the ToB's usage with [Claude skills][skill], my usecase
requires an MCP server that can analyze and server multiple graphs. The server
can scan multiple repositories and the LLM can request information from each
separately.

[trail]: https://github.com/trailofbits/skills/tree/main/plugins/trailmark
[skill]: https://github.com/trailofbits/skills/tree/main/plugins/trailmark

Mostly created with OpenAI GPT-5.5 via Github Copilot in VS Code. Point your LLM
to the [ai-docs](/ai-docs/) directory for documentation and development support.

## Requirements

* Python 3.12+
* `uv`

Project metadata:

* package name: `trailmark-mcp`
* CLI command: `trailmark-mcp`

## Install
Install runtime and development dependencies:

```bash
uv sync --group dev
```

## Quick Start
Start server over stdio:

```bash
uv run trailmark-mcp serve --transport stdio
```

Smoke-test direct scan path without an MCP client:

```bash
uv run trailmark-mcp scan /path/to/repo
```

Skip preanalysis during scan when needed:

```bash
uv run trailmark-mcp scan /path/to/repo --skip-preanalysis
```

## How The Server Works
Primary lifecycle entrypoint is `open_repository(...)`.

Behavior summary:

* if no snapshot exists, the server scans source, optionally runs preanalysis, and saves the first snapshot
* if a snapshot exists and `rescan=False`, the server reloads the latest snapshot into a live session
* if `rescan=True`, the server rebuilds from source and saves a fresh snapshot

This means the common flow is:

1. call `open_repository`
2. use graph tools against returned session
3. call `save_snapshot` after meaningful in-memory mutations when you want persistence

## Session Model
`session_id` is MCP wrapper state, not Trailmark core state.

Current semantics:

* each `open_repository(...)` call creates a new session id
* multiple live sessions can coexist
* tools accept `session_id` to target a specific graph
* omitted `session_id` uses the most recently opened still-open session
* closing the default session promotes the most recently opened remaining session

Use `current_repository(session_id=...)` to verify which repository a session points to.

## Public MCP Tools
Lifecycle:

* `open_repository`
* `current_repository`
* `close_repository`
* `save_snapshot`

Navigation:

* `graph_summary`
* `diff_graphs`
* `search_nodes`
* `callers_of`
* `callees_of`
* `ancestors_of`
* `reachable_from`
* `paths_between`
* `entrypoint_paths_to`
* `attack_surface`
* `complexity_hotspots`
* `functions_that_raise`

Context and mutation:

* `subgraph`
* `annotations_of`
* `findings`
* `nodes_with_annotation`
* `run_preanalysis`
* `annotate_node`
* `clear_annotations`
* `augment_findings`

Notes:

* `diff_graphs(before_session_id, after_session_id)` treats `after` as the new state
* `search_nodes` supports `contains`, `exact`, and `suffix`
* removed helper surfaces like `scan_repository` and `tool_manifest` are intentionally not part of the public runtime anymore

## Snapshot Behavior
Snapshots are written under the analyzed repository, not under this server repository:

```text
<target-repo>/.trailmark/snapshots/<timestamp>/
```

Current snapshot artifacts include:

* `graph.json`
* `summary.json`
* `entrypoints.json`
* `hotspots.json`
* `subgraphs.json`
* `scan-metadata.json`

Snapshots support reload into a live session. Use `rescan=True` when you explicitly need a fresh rebuild from source.

## Repository Layout
Key files:

* `src/trailmark_mcp/cli.py`: CLI entrypoint for `scan` and `serve`
* `src/trailmark_mcp/mcp_app.py`: MCP tool registration
* `src/trailmark_mcp/tool_catalog.py`: declarative metadata for exposed tools
* `src/trailmark_mcp/services/registry.py`: session tracking
* `src/trailmark_mcp/services/runtime.py`: main Trailmark-backed runtime behavior

## Development
Run focused test suite:

```bash
uv run --group dev pytest tests/test_tool_catalog.py tests/test_registry.py tests/test_stdio_server.py
```

Current CI runs that same focused suite on Python 3.12.

Extension rule:

1. add or change runtime behavior
2. register tool in `mcp_app.py`
3. update metadata in `tool_catalog.py`
4. update tests
5. update docs if public behavior changed

## Use In VS Code
VS Code can launch this server directly through MCP using a workspace-level `mcp.json` file.

Typical setup:

1. open this repository in VS Code
2. make sure dependencies are installed with `uv sync --group dev`
3. keep the server definition in `.vscode/mcp.json`
4. let the MCP client start the server over `stdio`

This repository already includes `.vscode/mcp.json` for local use.

Example `mcp.json`:

```json
{
  "servers": {
    "trailmark-mcp": {
      "type": "stdio",
      "command": "uv",
      "args": [
        "run",
        "trailmark-mcp",
        "serve",
        "--transport",
        "stdio"
      ]
    }
  }
}
```

If you use this server from a larger multi-project workspace, copy the same definition into that workspace root's `.vscode/mcp.json` and make sure the command runs in an environment where `uv` and this project are available.

TDQS

B3/5.0

Scored across 24 tools

Disambiguation5/5

Each tool targets a distinct operation: graph traversal (ancestors_of, callees_of, callers_of, reachable_from), annotation management (annotate_node, annotations_of, clear_annotations, nodes_with_annotation), repository lifecycle (open_repository, close_repository, current_repository, save_snapshot), and analysis (attack_surface, complexity_hotspots, findings, etc.). No two tools have overlapping purposes.

Naming Consistency5/5

Tool names follow two predictable patterns: query-like operations use noun_of (e.g., ancestors_of, callees_of, callers_of) and action operations use verb_noun (e.g., annotate_node, open_repository, save_snapshot). This pattern is consistent throughout the set, with no mixing of casing styles or ambiguous verbs.

Tool Count4/5

With 24 tools, the count is slightly above the typical well-scoped range (3-15) but still appropriate for a comprehensive code analysis server covering graph traversal, annotations, repository management, and analysis features. The tools are logically grouped and each serves a distinct purpose, so the number does not feel excessive.

Completeness4/5

The tool surface covers core workflows: opening/closing repositories, scanning, traversing the call graph, managing annotations, and querying findings. Minor gaps exist, such as no tool to list all repositories (only current_repository) or to delete nodes/annotations, but these do not critically hinder typical use cases for a static analysis tool.

Maintenance

ActivityInactive
ResponsivenessNo issues