GedFire 4.1.18

dotnet tool install --global GedFire --version 4.1.18
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local GedFire --version 4.1.18
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=GedFire&version=4.1.18
                    
nuke :add-package GedFire --version 4.1.18
                    

GedFire

CI License: MIT .NET 10

GedFire is a command-line tool that lets an AI agent help with your family history research without giving it write access to your GEDCOM file. The agent writes its findings as a JSON proposal; you review it, approve the items you want, and GedFire applies them, keeping each citation attached to the claim it supports. It can then generate a static family-page site from the result.

agent research -> JSON proposal -> your review -> verified GEDCOM -> static HTML

GedFire works only with local files and makes no network requests. Your chosen AI client controls where tool results are processed.

Why not just let the agent edit the file? GEDCOM looks like plain text, but the level hierarchy, cross-record pointers, and continuation rules are easy to get wrong — agents asked to rewrite raw GEDCOM tend to produce broken levels, dangling references, or files that no longer validate. GedFire instead applies typed operations to a parsed document and refuses to write anything invalid, so a valid input file stays valid. As a bonus, the agent doesn't have to read and reproduce whole GEDCOM records, which saves a lot of tokens.

Browse a live generated family-page site → Generated from the synthetic family in docs/demo, rebuilt by CI on every push. Facts retain their source citations, available as hover-popover footnotes in the HTML.

MCP server

GedFire also runs as a Model Context Protocol server, so clients including Claude Desktop, Claude Code, Cursor, Windsurf, Gemini CLI, and Codex can query your GEDCOM directly, in conversation, instead of shelling out to the CLI:

gedfire mcp --input family.ged

Install the global .NET tool before configuring a client. The command above starts a long-running stdio server, so waiting silently for a client connection is normal.

Most MCP clients accept the same local stdio server entry. Add this block to the client's MCP configuration, replacing the GEDCOM path with an absolute path:

{
  "mcpServers": {
    "gedfire": {
      "command": "gedfire",
      "args": [
        "mcp",
        "--input",
        "/absolute/path/to/family.ged"
      ]
    }
  }
}

Use that mcpServers entry in the location your client supports:

Client Configuration
Claude Desktop Open Settings → Developer → Edit Config and add it to claude_desktop_config.json.
Claude Code Add it to .mcp.json in the project root, or run claude mcp add --scope project --transport stdio gedfire -- gedfire mcp --input <absolute-path>.
Cursor Add it to .cursor/mcp.json for the project or the client's global mcp.json.
Windsurf Add it to ~/.codeium/windsurf/mcp_config.json.
Gemini CLI Add it to .gemini/settings.json for the project or ~/.gemini/settings.json.

Codex uses TOML instead of the JSON wrapper above. Add this to .codex/config.toml in the project or ~/.codex/config.toml:

[mcp_servers.gedfire]
command = "gedfire"
args = ["mcp", "--input", "/absolute/path/to/family.ged"]

Three optional flags follow --input in args (or after --input <path> on the claude mcp add / TOML command lines above):

Flag Effect
--policy <json> Enforce a policy file on every changeset: validate_changeset reports each violation as an error at the dry run, and apply_changeset refuses a changeset that has any.
--read-only Disable apply_changeset: every call to it is refused with an error, and the bound file is never written. validate_changeset and every other (already read-only) tool stay available — an agent can still preview changesets, it just can't apply them. Use this for a client you trust to look and propose, but not to write, even after review.
--enforce-privacy Run every tool's view of the document through the same privacy filter generate applies before publishing a site: individuals with an RESN of CONFIDENTIAL or PRIVACY, and individuals plausibly still living (no death-class fact, born within the last 100 years), are reduced to a "Living <Surname>" placeholder — no dates, places, notes, or media. Use this when the MCP client is one you don't want seeing the living branches of the file.

On Windows, JSON paths use escaped backslashes such as C:\\Users\\me\\family.ged; forward slashes also work. The gedfire command must be available on the environment PATH inherited by the client, or command must contain the absolute path of the executable. GedFire needs no environment variables, API keys, or other credentials.

Restart or reload the client after changing its configuration, approve the local server if prompted, and confirm that it discovers find_person, date_calc, get_document_stats, get_record, get_records, list_people, list_unanchored_people, find_family, select_targets, find_source, describe_changeset_ops, draft_changeset, validate_changeset, check_plausibility, validate_document, and apply_changeset.

A call with a missing required parameter or an unrecognized one is refused for every tool, and the error names each offending parameter and lists the accepted ones, for example find_person: missing required parameter "query"; unrecognized parameter "nme". Accepted parameters: query (required), hints, maxResults.

As a smoke test, ask "How many people and families are in this file?" The client should call get_document_stats and report both counts.

The server binds to one document over stdio and exposes sixteen tools. Fourteen are read-only; apply_changeset is the only one that writes to the file, and only after validation and in-memory verification both pass (or not at all, if the server was started with --read-only); draft_changeset never touches the file either, but with outPath it creates a new changeset file. The server also watches the bound file and reloads automatically if it changes on disk — including a change apply_changeset itself just wrote — no restart needed:

Tool What it does
date_calc Normalize a dual-dated year, add or subtract a genealogical age, or calculate elapsed years/months/days. Uses exact Gregorian dates supplied in the call and never reads or changes the bound document.
find_person Resolve a name the agent heard in conversation — "my great-grandfather Fred Morrill" — to scored candidates, a confident match when one exists, and family handoff identifiers. Optional structured hints distinguish birth from death, father from mother, and one marriage from another. Set maxResults to an integer from 1 through 20 (default 8) without changing the matcher's confidence decision. A name part written Unknown (Mary Unknown, Unknown Pike) is a wildcard that adds no score; people recorded with an Unknown part rank below exact name matches, and each result carries surnameUnknown.
find_family Find people through a relative whose name is all you know: relation spouse ("the widow of Joseph"), child (the parents of someone named X), or parent (the children of someone named X), with optional birth/death hints describing the person sought. Each result gives the person, the connecting family xref, and the matched relative.
find_source Learn whether a source already exists before a changeset creates one: title, author, and url are case-insensitive substrings (supply at least one; a source must match all supplied). Returns each match as xref, title, author, and url (the web address recorded in the source note, or null), or an empty sources array when none match.
get_document_stats Report person/family counts, the declared GEDCOM version, and the running gedfire version, for a quick orientation before other work.
get_record Fetch the full detail of a specific person, family, or source by xref. Every citation carries its source xref, page, quoted text (dataText), and quality (quay); a person's burial, baptism, and other individual events come back in otherEvents, each with its tag and citations, and a family's own citations come back in citations; set includeSources to also get the full text of each source cited on a person or family. Looking up a source lists every structure that cites it (citedBy).
get_records Fetch several people, families, or sources in one call, each in the shape get_record returns, in the order requested (includeSources adds one top-level sources array covering the whole call; fields such as ["name"] narrows each record to recordType, xref and just those fields, and an unknown field name is an error that lists the accepted ones). An xref that does not exist comes back as a not_found record in its own slot (up to 500 xrefs per call).
list_people List every person — xref, primary name, surname, birth and death year — in a stable order (surname, given name, xref), optionally limited to a list of surnames, in pages (pageSize 1–2000, default 500; pass each result's nextCursor back as cursor).
list_unanchored_people List people the tree cannot place: no dated event, no place, and no parent, spouse, or child who is placed (family ties only to other unplaced people count as unplaced). Reports each person's fact count and unplaced relatives. People hidden by --enforce-privacy are never listed.
select_targets The gedfire select-targets command as a tool: detect research gaps for the given surnames and draw count targets, returned as the wanted.json document without the GEDCOM path. An Enrich person target is one card per person: enrichments lists every birth, marriage and death whose date or place is not exact, nominalPoints is one per missing date or place across all of them, and the difficulty bonus is added once (score is nominalPoints plus the bonus). To price a changeset against it, pass validate_changeset a card with gap enrich-fact, points 1 and bonus equal to score minus nominalPoints. Each target lists possibleDuplicateOf: the people the check_plausibility GEN301 duplicate test pairs with it ([] when none); at most three are listed, best first, and possibleDuplicateCount gives the full number; such a target is still drawn. A person with an Unknown name part is paired only through an agreeing parent or spouse name. Seeded from the clock, like the command.
describe_changeset_ops Return the changeset envelope shape and the full v2 op dialect (every createOrUpdate/delete/merge op, its required and optional fields, and one worked example) — so an agent can compose a valid changeset without external documentation or trial-and-error against validate_changeset's error text. Takes no arguments.
check_plausibility Preview a proposal changeset and return only the chronological and biological plausibility findings it would introduce, structured for routing by severity. Name characters are checked too: digits and symbols (other than apostrophe, hyphen and period) are errors that block a changeset, an ordinal suffix such as 3rd is allowed, and punctuation standing in for a name (----) is a warning to use Unknown. Always available, even under --read-only.
draft_changeset Compile findings into a changeset in the op dialect (see Drafting a changeset). Reuses an existing source or mints one per collection, merges each (source, page, grade) group's extracts into one dataText, copies the locator verbatim into the citation page, adds a verification note for every fact graded at or below the policy ceiling, and refuses a QUAY the source and information class cannot support, a target name that differs from the recorded one, and prose the policy bans. Returns the changeset, a decisions list, and errors; outPath writes it to a new file, replacing an existing one only with overwrite. New people and families come from people and relations.
validate_changeset Dry-run a proposal changeset (changesetPath, items) against the bound document: every op is validated exactly as apply_changeset would validate it, but nothing is written. Policy violations are reported as errors. Pass cards to get a pointsPreview (see Points preview). Always available, even under --read-only.
validate_document Run the same GEDCOM 7 conformance checks as gedfire validate against the whole bound document — independent of any changeset — and return the findings structured instead of as plain-text lines. Optional warningsAsErrors mirrors the CLI flag. Always available, even under --read-only.
apply_changeset Validate, apply, and verify a proposal changeset, then write the file — the same safety model as gedfire apply (dry-run-equivalent validation, byte-stable round-trip check, pointer resolution, record-count deltas) reached over MCP instead of the CLI. Refuses to run under --read-only.

date_calc, find_person, get_document_stats, and get_record also have a one-shot CLI mirror — find-person, get-record, get-document-stats, and date-calc — that runs the same engine and prints the same JSON without starting a server. validate_changeset and apply_changeset mirror the CLI's own apply --dry-run and apply, and validate_document mirrors validate, described under "Command reference" below, rather than having a same-named CLI counterpart of their own.

Changeset policy file

--policy <json> loads editorial rules the server enforces on every changeset. Every key is optional, an absent rule is not enforced, and an unknown key stops the server from starting. docs/policy.example.json shows all four:

Key Rule
bannedPhrases Case-insensitive phrases that may not appear in note text or a citation page.
bannedPatterns Regular expressions that may not match note text or a citation page (for example, record ids such as @S00123@).
verificationNote {"maxQuay": 1, "notePrefix": "needs independent verification:"}. Every record that receives a fact cited at maxQuay or lower must be named by a note containing the prefix: a note on that record, a note naming the person's given name, or for a marriage a note mentioning marriage.
deathPlaceEvidence A regular expression. A DEAT fact with a place needs a citation whose page or dataText matches it, so a burial record alone cannot supply a death place.

Violations read policy: item 2: .... Only the selected items are checked.

Points preview

validate_changeset takes an optional cards array, each {"xref", "class", "gap"?, "name"?, "points"}, where class is enrich-person, new-parent, new-spouse or new-child (a select_targets card type) and gap is enrich-birth, enrich-death or enrich-marriage, optionally ending in -date or -place. A card with no gap, or the queue gap enrich-fact, names no single fact: it closes on any improved fact written on the subject and is one card with many dates and places to find: points is what each improved date or place earns, bonus is the difficulty bonus added once, and closed.points is points times the improvements plus bonus (wrote lists each fact and which parts improved). The result then carries pointsPreview, found by diffing the touched records before and after:

  • closed: cards the changeset closes, each with the wrote fact or relationship. An enrich card closes only when the fact it names (and a date or place, if the gap says so) is written on the subject; a new card closes only when the subject's family gains that relative. A citation alone, or a restated identical fact, writes nothing.
  • notClosed: the other cards, each with a reason such as no DEAT place written on the subject (wrote BURI).
  • subjectMismatch: cards left open by a name that differs. For an enrich card, the changeset renamed the subject. For a new-parent, new-spouse or new-child card, name is the person to mint, and a person the changeset creates under it must carry that name exactly (case and spelling), so a one-letter drift keeps the card open.
  • total: the closed cards' points.

Drafting a changeset

draft_changeset takes findings, each with an id, extract, source (name, locator, class, information, optional author, url, accessed), target (xref, optional card, name), facts (record, fact, optional date and place, or a text value such as a NAME change, a per-fact quay, optional excerpt, class, information) and optional notes. The values and grades are yours; the compiler does the rest the same way every time. Findings with the same target become one item, and one source record is made per collection and author. The QUAY ceiling follows the source class and information class: original or image copy 3 for primary information and 2 otherwise, derivative 2 for primary and 1 otherwise, authored 1, user-contributed 0. Unrecognized fields are rejected by path.

A repeatable tag such as CENS or RESI never overwrites by accident: several values for one record are each added, a value whose date the record already holds updates that instance, and any other value is added beside the existing ones. A single-valued tag (BIRT, DEAT, NAME, ...) given two different values is left as an open decision.

A finding can also create people and families. List each new person under people (xref as a placeholder such as @NewI1@, name, sex, which a partner needs) and link them under relations: {"kind": "spouse", "person", "spouse", "family", "quay"}, {"kind": "child", "family", "child", "husb", "wife", "quay"} or {"kind": "parent", "person", "role", "parent", "quay"}. Each relation is cited with the finding's source at its own quay. Facts about a new person or a new family (a MARR) use the placeholder as their record. husb and wife seed a family only where a relation creates it; later relations on that family leave them off and a dropped-seed decision says so.

A fact can carry substructures ([{"tag": "TYPE", "value": "Pension Application"}] on an EVEN, which is then added rather than matched by date), a replacedCitations disposition (keep, drop or moveToNote) for the citations of a value it replaces, or citeOnly: true to attach this finding's citation to a fact the record already holds (date and place pick the instance). A spouse or child relation takes notDuplicateOf, a list of existing people confirmed not to be the new person. A note takes match, the exact text of an existing note to rewrite, and cites, a list of {"finding", "quay", "excerpt"} citations of findings in the same item (the note's own finding when finding is omitted).

With outPath the changeset is written to a new file; an existing file is replaced only when overwrite is true.

For example, an MCP client can call find_person with:

{
  "query": "Frederick Morrill",
  "hints": {
    "birth": { "year": 1841, "place": "New Hampshire" },
    "parents": { "father": "Wyman Morrill" },
    "spouse": {
      "name": "Sarah Blake",
      "marriage": { "year": 1865, "place": "Maine" }
    }
  },
  "maxResults": 20
}

Every hint leaf is optional, but each supplied object must contain at least one fact. sex ("M" or "F") ranks a candidate whose recorded sex differs lower; a candidate with no recorded sex is neither helped nor penalized, and none is removed (CLI: --sex). Birth and death places are event-specific; census or otherwise unclassified places are not hints. Parent names require a known father or mother role. All fields under spouse describe one marriage and are never combined across different marriages. Hints rank only people already recalled by the name query, and missing candidate data is not penalized.

Every result has the same top-level fields: matchType, confidentMatchXref, confidentMatchScore, person, candidates, suggestions, totalMatches, and truncated. Candidates and suggestions always include matchScore. Scores rank evidence within this matcher; they are not statistical probabilities. Use matchType and confidentMatchXref to decide whether the lookup resolved one person. maxResults changes only the returned comparison-list length, never recall, ranking, or confidence classification.

apply_changeset is the only tool that writes to the file, and it applies the same numbered JSON changeset you'd review by hand — an agent proposing a change still writes a changeset for you to see, not a second, unreviewed path to your data; apply_changeset is how that reviewed changeset gets applied without leaving the conversation, instead of shelling out to gedfire apply. Start the server with --read-only if you want an agent to look and propose but never write, regardless of what it's asked to do. GedFire itself makes no network requests and sends no telemetry; the MCP client you choose is responsible for what it does with tool results. Every returned xref belongs to the one GEDCOM bound by --input; do not reuse it against another file.

If the server does not start, run gedfire --version in a new terminal to confirm that the installed command is available, then verify the input path exists. Check the client's MCP status or server log for startup errors. An absolute executable path in command avoids PATH differences between a terminal and a desktop application.

Why should I use GedFire?

  • GedFire keeps the agent's hands off your master file. The agent writes a numbered JSON proposal instead of editing the GEDCOM, so nothing changes until you've reviewed it.
  • GedFire applies changes as typed operations against a parsed document rather than raw text edits, so it can't produce broken levels, dangling pointers, or a file that no longer validates.
  • You can dry-run a proposal with --dry-run and then apply all of its numbered items or only the ones you agree with.
  • Citations stay attached to the claims they support, from the agent's finding all the way into the GEDCOM and the generated pages.
  • GedFire generates a static family-page site directly from the reviewed GEDCOM, so the published pages and the evidence never drift apart.
  • Because the agent works with compact operations instead of reading and reproducing whole GEDCOM records, the workflow uses considerably fewer agent tokens.
  • GedFire is also a scriptable GEDCOM 7 validator, a GEDCOM 5.5 converter, a GEDZIP packer, and a JSON name-index exporter, so it can anchor a genealogy build pipeline on its own.
  • GedFire runs as an MCP server (gedfire mcp), so agents like Claude Desktop or Claude Code can look up people and records in your GEDCOM, and validate or apply a reviewed changeset, directly in conversation instead of shelling out to the CLI — or start it with --read-only to keep the agent to lookups and previews only.

In other words, use GedFire if you want AI help with your research but final say over your family history.

Installation

Download a release

Download the archive for your platform from GitHub Releases, extract it, and run the binary directly:

.\GedFire.exe help

On Linux and macOS, run ./GedFire help from the extracted directory.

Install as a .NET tool

With the .NET 10 SDK installed:

dotnet tool install -g gedfire
gedfire --version

Demo

The repository includes a fictional Ash family and a proposed set of findings. From the repository root, copy the GEDCOM, dry-run the proposal, apply it, and generate the site:

cp docs/demo/ash-whitfield.ged family.ged
gedfire apply --input family.ged --changes docs/demo/proposed-findings.json --items all --dry-run
gedfire apply --input family.ged --changes docs/demo/proposed-findings.json --items all
gedfire generate --input family.ged --output-dir site --template docs/demo/template.html

The dry run confirms exactly what would be accepted:

validation OK (3 ops, items 1,2)

The real apply reports each approved operation and verifies the written file:

applied: createOrUpdateSource @S00003@: created
applied: createOrUpdateVital DEAT on @I2@: updated (cited @S00003@)
applied: item 1: applied (@I2@ Mercy Whitfield -> cite her death, currently uncited)
...
verify OK: round-trip byte-stable, pointers resolve, deltas {SOUR +1}

Open site/index.html to browse the generated family pages. The input family, records, places, and sources are entirely fictional.

Safety model

  • --dry-run validates selected items without touching the GEDCOM.
  • You can apply all numbered items or just the ones you accept.
  • If any operation fails validation, nothing is written.
  • A path-based apply holds an exclusive file lock from its initial read through validation and verified write, preventing two GedFire writers from racing to allocate the same record identity.
  • Before creating a person, validation uses the same identity matcher as find_person and rejects a high-confidence duplicate. Creating families, sources, and media does not use person duplicate detection.
  • Multiple marriages between the same people are allowed, but creating a second marriage to the same person on the same exact date is rejected.
  • After a successful write, the file is reparsed and checked: the round trip must be byte-stable, pointers must resolve, and record counts must change by exactly the expected amounts.
  • GedFire works on local files and sends no family data or telemetry anywhere.

How changesets work

Suppose an agent finds a death certificate for Mercy Whitfield, whose death is already recorded but uncited. Instead of editing the GEDCOM, the agent proposes a source and a numbered claim in JSON:

{
  "proposal": "mercy-whitfield-death-record",
  "newSources": [
    { "xref": "@NewSource1@", "ops": [
      { "op": "createOrUpdateSource", "xref": "@NewSource1@",
        "auth": "New Hampshire Bureau of Vital Records (fictional)",
        "title": "Death certificate, Mercy (Whitfield) Ash, 1870",
        "accessed": "2026-07-22" } ] }
  ],
  "items": [
    { "item": 1,
      "target": "@I2@ Mercy Whitfield -> cite her death, currently uncited",
      "ops": [
        { "op": "createOrUpdateVital", "record": "@I2@", "fact": "DEAT",
          "value": { "date": "19 JAN 1870", "place": "Salisbury, Hartwell County, New Hampshire" },
          "citation": { "source": "@NewSource1@", "page": "cert. 1870-0114",
                        "dataText": "Mercy Ash, widow of Nathaniel, died 19 Jan 1870, aged 75",
                        "quay": 2 } } ] }
  ]
}

A citation attached to a family record itself (what the spouse, child, and parent ops write) can be corrected or removed with createOrUpdateCitation / deleteCitation by omitting fact, and the citations under a note by naming it with note (its exact text).

Prose a changeset writes (note text, citation page and quoted text, source titles and authors, merge notes) must refer to people and sources by name; text containing an internal id such as @S00223@ is refused at validation and apply.

New records use reserved @New<token>@ placeholders rather than caller-selected GEDCOM xrefs. The token can contain letters, digits, and underscores. Its first creating operation fixes the record kind, and every later use of that placeholder in the changeset resolves to the same real xref. Here, apply allocates @S00003@ for @NewSource1@, creates the source once, and resolves the citation to it. Library callers receive the complete placeholder-to-xref map in ApplyResult.MintedXrefs; the CLI names the minted xrefs in its operation log.

A new source that describes a document already in the file — same title and author, no publication, compared ignoring case and extra spaces — is not created again: the placeholder resolves to the existing source, which is left unchanged. Updating a source that more than one fact cites never changes those other facts: the update is applied to a copy (or to an existing source that is exactly the updated one), and only the facts the same changeset item cites it on point to the copy. The item must therefore also cite the source on the fact that needs the change. ApplyResult.CopiedSources maps each original to the source used in its place. A citation's page is part of the citation, not of the source, so correcting one fact's page never affects another fact. To cite one source on several pages of a fact, give one citation per page; a citation naming no matching page is added, and one whose page matches is updated (a source cited once on a fact simply has its page corrected). deleteCitation takes an optional page to choose among several.

Creating a new person is additionally checked against existing and other planned people using the find_person matcher. A high-confidence match is a validation error, so an already-applied person-creation proposal cannot silently create another copy on retry. Ambiguous candidates do not block a reviewed creation.

The complete demo proposal adds a second item as well. More examples covering relationships, notes, media, deletion, and person merges live in GedCore.Tests/TestData/Changesets.

Date arithmetic

date-calc performs genealogical calendar arithmetic without reading a GEDCOM file:

gedfire date-calc --op normalize --date "11 FEB 1691/2"
gedfire date-calc --op add --date "27 SEP 1777" --age "63y 4m 2d"
gedfire date-calc --op sub --date "29 JAN 1841" --age "63y 4m 2d"
gedfire date-calc --op diff --from "27 SEP 1777" --to "29 JAN 1841"

The commands print 11 FEB 1692, 29 JAN 1841, 27 SEP 1777, and 63y 4m 2d, respectively. Arithmetic requires exact Gregorian dates; qualified, partial, BCE, and non-Gregorian dates are rejected rather than having uncertainty invented or discarded. GEDCOM reading, writing, and HTML generation continue to preserve original date text such as 1860, BEF 1860, and dual dates.

The MCP server exposes the same operations through date_calc. Its operation is normalize, add, sub, or diff; the advertised schema describes which fields each operation requires. For example:

{
  "operation": "diff",
  "from": "27 SEP 1777",
  "to": "29 JAN 1841"
}

The result is { "operation": "diff", "date": null, "age": "63y 4m 2d" }. The other operations return the canonical result in date and set age to null.

Command reference

Command What it does
create Create a seeded GEDCOM 7 document.
upgrade Upgrade a GEDCOM 5.5 file to GEDCOM 7.
downgrade Write a GEDCOM 7 document in GEDCOM 5.5 format.
validate Report GEDCOM 7 conformance diagnostics.
apply Validate and apply selected JSON changeset items.
generate Generate static family-page HTML, with optional media staging.
export-index Export a JSON person-name index.
select-targets Detect research gaps for given surnames, score them, and draw a self-contained wanted.json pack.
date-calc Normalize dual-dated years, add or subtract a genealogical age, or calculate elapsed years/months/days.
mcp Start the stdio MCP server bound to one GEDCOM document. Watches the file and reloads if it changes on disk. Optional --read-only (disable the apply_changeset write tool) and --enforce-privacy (redact living/restricted individuals).
find-person One-shot mirror of the mcp server's find_person tool: resolve a name to scored candidates or a confident match.
get-record One-shot mirror of the mcp server's get_record tool: fetch a person, family, or source by xref.
get-document-stats One-shot mirror of the mcp server's get_document_stats tool: person/family counts, GEDCOM version, gedfire version.
pack Create a GEDZIP archive from GEDCOM and referenced media.
unpack Extract a GEDZIP archive.

Run gedfire help for syntax, then gedfire <command> with the options above.

When to use it

Use GedFire if you want an AI agent helping with family history research without handing it the master file, if you need a scriptable GEDCOM validation or publishing pipeline, or if you want your published pages to be reproducible from the GEDCOM itself. It's a data-processing tool — not a graphical family-tree editor, a research provider, or a hosted genealogy service.

Roadmap

  • GEDCOM interoperability report — the next patch release will add a read-only gedfire audit command. It will summarize GEDCOM 7 conformance, declared and undeclared extensions, unresolved pointers, and GEDZIP/media sharing readiness so researchers can understand a file before sending it to another tool or collaborator.
  • MCP write support — shipped. gedfire mcp now exposes validate_changeset and apply_changeset, extending the MCP server through the same reviewed changeset-and-approval path the CLI's apply/apply --dry-run already enforce, rather than opening a second way to mutate a GEDCOM. Start the server with --read-only to keep apply_changeset disabled for a given run — validate_changeset and every lookup tool stay available either way. describe_changeset_ops ships alongside them so the changeset format itself is discoverable over MCP — no external documentation needed to compose one from scratch.
  • Agent skills — companion Claude Code skills for the full research workflow (evidence grading, identity correlation, record harvesting, and driving GedFire safely) are being prepared for publication as a separate repository.
  • GedCore library package — publish the parser/apply engine on NuGet for embedding in other .NET projects, if there is interest.

GedFire is newly public — if you're interested in the agent skills, watch or star the repo to follow along.

Building from source

GedFire requires the .NET 10 SDK.

git clone https://github.com/morrillonline/GedFire.git
cd GedFire
dotnet build --nologo
dotnet test --nologo
.\GedFire\bin\Debug\net10.0\GedFire.exe help

The solution contains GedCore (the GEDCOM engine), GedFire (the CLI), and GedCore.Tests (the test suite).

Versions use 4.0.<build>, beginning with 4.0.1. Local builds default to build 1; CI supplies its increasing workflow run number, and release builds take the build number from their v4.0.<build> tag. To reproduce another build locally, pass -p:BuildNumber=<build> to dotnet build, test, or publish.

Standards

GedFire is an independent implementation designed to work with FamilySearch GEDCOM 7, the current GEDCOM specification stewarded by FamilySearch. It is not affiliated with or endorsed by FamilySearch or The Church of Jesus Christ of Latter-day Saints.

For specification questions, proposed standard changes, and registered extensions, see the official GEDCOM repository and GEDCOM registries.

Contributing

Bug reports and pull requests are welcome — edge-case GEDCOM fixtures especially. Please don't include private family data in an issue, fixture, or pull request, and run dotnet test --nologo before opening a PR.

License

GedFire is released under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
4.1.18 0 10/12/2026
4.1.17 46 10/11/2026
4.1.16 42 10/11/2026
4.1.15 53 10/8/2026
4.1.14 67 10/7/2026
4.1.13 65 10/7/2026
4.1.12 103 10/4/2026
4.1.11 89 10/4/2026
4.1.10 90 10/4/2026
4.1.9 90 10/4/2026
4.1.8 99 9/30/2026
4.1.7 176 9/14/2026
4.1.6 120 9/11/2026
4.1.5 128 9/7/2026
4.1.4 118 9/3/2026
4.1.3 115 9/3/2026
4.1.2 131 8/25/2026
4.1.1 127 8/24/2026
4.0.8 131 8/23/2026
4.0.7 122 8/23/2026
Loading failed