PluginWorld
Op

open-hydra-mcp

MCP

Enables an AI assistant to read raw source text and propose FollowTheMoney records into a hydra archive, checking them against the archive's schema and existing entities, linking rather than duplicating, and storing the text as the cited evidence. Supports drafting, dry runs, saving, read-back verification, image hints and archive creation while leaving uncertain judgments to human review.

@meridiangray · AGPL 3.0 · updated today

SECURITY

B

SCORE

60

STARS

▲ 0

PLUG IN

git clone https://github.com/meridiangray/open-hydra-mcp.git

See the README to configure this MCP server

README

open-hydra-mcp

Support me on Ko-fi

An MCP server that lets an AI assistant add facts it finds in raw text to a hydra archive: an article, a court filing, a press release, notes from an interview. The assistant reads the text and proposes FollowTheMoney records. The server tells it what the archive accepts, checks its proposal, and only then writes it, with the text stored as the source every entity cites.

It doesn't guess. Anything uncertain is left for a person: a name that might be someone already in the archive becomes a merge question in hydra-ctl review, a link to someone the archive doesn't know stays a name, and a role the hierarchy doesn't rank is reported, not rewritten.

What an ingestion writes

Each call to save adds one new collection and nothing else:

collections/<collection>.json                 the records
.hydra/sources/entries/<collection>.json      the raw text, as one Document (an Article when it has a URL)
.hydra/sources/files/<collection>/<file>      the original file, when the source is one

Every entity (person, organization, company…) cites that document in sources, so hydra-ctl claims and hydra-ctl sources trace each value back to the text. Relationships have no sources field in FtM: they trace back through their collection, which holds that one source only.

The archive is rebuilt after writing, and the result reports the new review questions. A batch with errors is never written, an existing collection is never overwritten unless asked, and a build that fails puts the files back as they were. To undo a save, delete the files listed in its undo result, then run hydra-ctl build.

Tools

Tool What it does
labelling_guidelines How to write records so hydra places them correctly, with this archive's relationship fields and position ladder
read_file A file's text exactly as it will be stored as evidence, in parts; says whether the archive already holds it
describe_schema With no name, the entity types and relationships; with one, every property it accepts, its type, which are required, and what a link takes
find_entities Existing entities closest to a name, so new records link instead of duplicating
check_records Dry run of save: errors, warnings, how links and roles resolve, likely duplicates. Writes nothing
save Checks, writes the batch and its evidence, rebuilds, and reports what changed
get_records Reads a saved collection back with each property's type, what every link resolved to after the build, and the checks repeated on what was written
link_image Previews, then on confirm adds, a hints.yaml rule giving matching entities a portrait, an icon (thumb) or a logo from a local file
create_archive Previews, then on confirm creates, a new empty archive in a new or empty folder (as hydra-ctl init)

The intended order: guidelines, read_file, describe_schema and find_entities while drafting, check_records, save, then get_records to verify.

Labelling guidelines

The server doesn't try to catch every badly structured record after the fact. It tells the assistant how to write good ones in the first place, from the rules in the hierarchy guide and hydra's data model. labelling_guidelines returns them, and the server's instructions tell the assistant to read them before proposing a record:

  • only what the text states: no facts from memory, no inferred relationships
  • one record per entity, its fullest name as written; other full names in alias, surnames and nicknames in weakAlias; two records when unsure, since review merges them
  • each end of a relationship in the right field and of the right kind, never pointing at itself
  • roles in the text's own words, left out when the text gives none, never changed to fit the hierarchy
  • Family's word describes the relative from the person's side ("father" puts the relative above)
  • an organization inside another takes a role like "affiliate", "unit" or "branch", never a person's rank; a subsidiary is an Ownership
  • dates only as given, the start never after the end
  • a public office is a Position plus an Occupancy: the holder sits under the office's organization, titled by the office; events link their participants through involved; addresses stay in address unless shared
  • images, the hierarchy and review decisions left to the archive's owner

The relationship table (which field is which end, what kind of entity each takes, which end hydra draws above) and the position ladder are read from the archive itself: its schema, direction.yaml and hierarchy. With a custom positions.yaml the guidelines show its positions; without one, hydra's defaults.

Records

Records use hydra's FtM dialect: links are written as the linked entity's name, not an id.

[
  {"schema": "Person", "properties": {"name": ["Rob Kerr"], "weakAlias": ["Kerr"]}},
  {"schema": "PublicBody", "properties": {"name": ["Port Authority"]}},
  {"schema": "Employment", "properties": {"employee": ["Rob Kerr"], "employer": ["Port Authority"],
                                          "role": ["deputy"], "startDate": ["2019"]}}
]

id is optional; one is made from the schema and name (person-rob-kerr). Values can be a string or a list. Dates are YYYY, YYYY-MM or YYYY-MM-DD.

check_records blocks on: an unknown or abstract schema, a property the schema doesn't have, a malformed date, a relationship missing one of its ends, an entity without a name, an image field (images are linked in hints.yaml, never in records), and two records sharing an id.

With or without a custom hierarchy

Hydra ranks people inside an organization by the role on their relationship (or their own position), matched against the titles of the positions in effect. labelling_guidelines reports which ladder is in effect:

  • defaults only: hydra's four positions (Leader, Second-in-command, Member, Associate). Only leadership roles rank: "deputy" ranks as Second-in-command, "administrator" ranks no one.
  • custom and defaults: the archive's positions.yaml adds its own positions (a corporate chain, an agency's ranks) to the defaults.
  • custom only: the defaults are switched off and positions.yaml is the whole ladder.

In every mode the assistant keeps each role in the source's own words. check_records and save report the position each role ranks as, and for a role nothing takes, the title to add to positions.yaml if the archive's owner wants it ranked. The data stays faithful to the text, and the hierarchy catches up. See the hierarchy guide.

Changes the owner approves

Images aren't facts from a text, so the assistant never puts them in records. link_image adds a rule to .hydra/rules/hints.yaml that gives matching entities a portrait, an icon (thumb) or a logo. It works in two calls: without confirm it only previews the rule, and the assistant calls it with confirm=true only after you approve. The match is schema, position, titled and/or name, all of which must hold. Local files only, never a URL; the file is copied into .hydra/resources/, a rule that hydra-ctl check flags is not kept, and the archive is rebuilt to say how many entities it reached.

The position ladder is not changed through this server. Roles are kept in the source's words, and save reports the ones that rank no one; the owner edits .hydra/rules/positions.yaml (see the hierarchy guide).

Install

hydra-mcp needs open-hydra (the open-hydra package, which gives hydra-ctl), and Python 3.11 or later. Neither is on PyPI yet, so install both from GitHub:

uv tool install git+https://github.com/meridiangray/open-hydra-mcp --with git+https://github.com/meridiangray/open-hydra

Or with pip, into any environment:

pip install git+https://github.com/meridiangray/open-hydra git+https://github.com/meridiangray/open-hydra-mcp

To update, run the same command again with uv tool install --reinstall or pip install --force-reinstall, and restart your client.

Add it to your client

Every MCP client needs the command hydra-mcp and, optionally, HYDRA_ARCHIVE set to the archive's folder.

Claude Code:

claude mcp add hydra --env HYDRA_ARCHIVE=/path/to/case -- hydra-mcp

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "hydra": {
      "command": "hydra-mcp",
      "env": {"HYDRA_ARCHIVE": "/path/to/case"}
    }
  }
}

If the client can't find the command, give its full path (which hydra-mcp or where hydra-mcp prints it).

Windows: give the full path and double every backslash:

"command": "C:\\Users\\you\\.local\\bin\\hydra-mcp.exe",
"env": {"HYDRA_ARCHIVE": "C:\\cases\\case"}

Installed in WSL, client on Windows:

"command": "wsl",
"args": ["-d", "Debian", "--", "bash", "-lc", "HYDRA_ARCHIVE=/mnt/c/cases/case hydra-mcp"]

Running it

The archive is each tool's archive argument, else HYDRA_ARCHIVE, else an archive activated (hydra-ctl activate) in the folder the server starts in or above it, else that folder if it is an archive.

With no archive

If none of those gives an archive, nothing is written anywhere:

  • Tools that need an archive say none is selected, and tell the assistant to ask you for one or offer create_archive.
  • labelling_guidelines, describe_schema and check_records still work, on hydra's defaults, and say so. The assistant can read the text and check its records before an archive exists. find_entities finds nothing, since there is nothing to find.
  • save and get_records refuse with the same advice.
  • create_archive takes the full path of a new or empty folder you choose, a name (default: the folder's) and an id prefix (default: made from the name, none for one per collection). It previews first and creates the archive only with confirm=true. The assistant then passes that folder as archive. To make it the default, set HYDRA_ARCHIVE in the server's configuration.

An archive that is named but isn't one (a wrong path) is an error, never a quiet fallback to the defaults.

Tests

git clone https://github.com/meridiangray/open-hydra
git clone https://github.com/meridiangray/open-hydra-mcp
cd open-hydra-mcp
python3 -m venv .venv && .venv/bin/pip install pytest -e ../open-hydra -e .
.venv/bin/python -m pytest -q

License

Copyright (c) 2026 Meridian Gray. hydra-mcp is free software under the GNU Affero General Public License, version 3 only, as is open-hydra. If you run a modified version as a network service, the AGPL requires you to offer its users the source of your changes.

Contributing

See CONTRIBUTING.md. Before a pull request can merge, you agree once to the Contributor Agreement.

SIMILAR PLUGINS