ancestry-com-mcp
This server lets an AI assistant manage Ancestry.com accounts entirely through MCP tools—reading and editing trees, people, facts, sources, notes, tags, media, DNA, messages, and historical records, plus searching records and handling hints, without a browser (except for one-time login).
Session: check login status, trigger Chrome-based login to save cookies.
Trees: list, view, create, update, set privacy, export/import GEDCOM, delete (with confirmation).
People: list, find, get full profiles, family views, relationships, add/update/link/unlink/delete people, change relationships.
Facts: add/edit/delete events, attach citations and media.
Sources & Citations: manage sources, repositories, citations, web links.
Notes & Tags: read/replace/append notes, manage MyTreeTags and custom tags.
Record Search: full search form (names, dates, places, relatives, collections) with fuzzy matching and wildcards.
Records: get record details, image indexes, download images, save records to people (with relatives), suggest places, manage shoebox.
Hints: summary, list, compare, set state (rejected/deferred/pending); accept via record save or media copy.
Media: list/get/download/upload photos & documents, update metadata, set profile photo, copy from other trees, delete.
DNA: list tests, filter matches (cM, side, trees, groups, notes), view match details, ethnicity, set notes, manage groups.
Messaging: list conversations, read messages, view member profiles, send messages (real mail, requires confirmation).
Raw Requests: escape hatch for any endpoint not covered.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ancestry-com-mcp search census records for my ancestor John Miller born 1842 in Ohio"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ancestry-com-mcp
An MCP server that lets an AI assistant do everything you would do on Ancestry.com by hand: search records, read and edit your trees (people, facts, sources, citations, notes, tags, media), review hints, attach records, look at DNA matches, and read or send messages.
Ancestry has no public API. This server drives the site's own internal JSON endpoints, reverse-engineered
and documented in docs/endpoints.md and docs/writes.md.
Companion server: familysearch-org-mcp. Both can be attached at once;
their tools are prefixed ancestry_ and familysearch_ so they never collide.
Unofficial. This project is not affiliated with or endorsed by Ancestry. It uses undocumented endpoints that may change or break at any time, and automated access may be against Ancestry's terms of service. Use it only with your own account, on your own trees, at your own risk.
How it works
Cloudflare rejects ordinary Node/curl TLS on ancestry.com even with valid cookies, but it accepts a client that
impersonates Chrome's TLS/HTTP2 fingerprint (impit) carrying the site's two
login cookies (ATT, SecureATT). So the server runs without a browser: cookies live in .session.json
(mode 600, git-ignored) and every request goes straight from Node. A real Chrome is opened only to sign in:
pnpm login (or the ancestry_session tool's login action) launches a dedicated Chrome profile on the sign-in
page, waits for you to log in, saves the cookies, and closes the window again.
Requirements: Node 26+ (runs TypeScript directly), pnpm, Google Chrome.
Related MCP server: Gramps MCP
Setup
git clone https://github.com/ball2jh/ancestry-com-mcp
cd ancestry-com-mcp
pnpm install
pnpm login # one-time: opens Chrome to sign in, saves .session.json, closes Chrome
pnpm mcp-test # optional: read-only check of the connection and every read toolAdd to Claude Code / Claude Desktop (stdio):
{
"mcpServers": {
"ancestry": {
"command": "node",
"args": ["/absolute/path/to/ancestry-com-mcp/src/server.ts"]
}
}
}If the saved session is missing or expired, tools return an error telling you to run ancestry_session login
(it opens Chrome for a one-time sign-in and closes it afterwards). Ancestry's login cookie lasts a long time, so
this is rare; the server also persists any rotated cookies it receives.
Tools
Tools are grouped by resource and split into a read tool and an edit tool, so an MCP client can
auto-approve reads and ask before writes based on the tool annotations (readOnlyHint, destructiveHint,
idempotentHint, openWorldHint, all set honestly on every tool). Most tools take an action parameter; every
parameter is described in the schema, and a missing parameter for an action comes back as an error naming it.
The whole definition set is about 12K tokens (pnpm mcp-test prints the exact figure); the extra weight over a
bare schema is the per-parameter documentation, which is what lets the model call these tools correctly the
first time.
Tool | Actions | Writes? |
| status, login | opens Chrome, saves cookies |
| list, get (with Tree Checker totals), checker (the flagged people: duplicates, no sources, tree-only sources, issues) | no |
| create, update, set_privacy, import_gedcom, delete (needs | yes |
| list, find, get, family_view, relationship_to_home, fact_types, issues (duplicate candidates and data issues) | no |
| add, link_existing, update, change_relationship, remove_relationship, delete (needs | yes |
| add, edit, delete, attach_citation, detach_citation, attach_media, detach_media | yes |
| list_sources, get_citation | no |
| create_source, update_source, create_repository, link_repository, unlink_repository, create_citation, update_citation, remove_citation, add_web_link, remove_web_link | yes |
| get_notes, list_tags | no |
| save_notes (replaces the note), append_notes (keeps it), add_tags, remove_tags, create_custom_tag | yes |
| single action: the full website search form (names, per-event year/place, relatives, exactness, collection) | no |
| get, image_index (a whole census page, paged and filterable by name/household/fields), catalog (which collections exist for a keyword/place/year), browse (a collection's images by place, for unindexed collections), suggest_places, shoebox_list | no |
| save_to_person (update/alternate/name/relatives like the Review page), shoebox_save, shoebox_remove | yes |
| record_image, media, gedcom | writes a local file, or returns the image inline |
| summary, list, compare | no |
| rejected / deferred / pending | yes (reversible) |
| list, get | no |
| upload, update, set_profile_photo, copy_from_tree, delete | yes |
| tests, matches (all website filters: cM, parent side, trees, groups, name, unviewed/notes/new, sort), groups, match_detail, ethnicity | no |
| set_note, add_to_group, remove_from_group | yes |
| list_conversations, get_messages, member_profile, notifications | no |
| send a message to a member | yes, irreversible |
| any ancestry.com request with the saved session | can |
Files: ancestry_download writes record images, media files and GEDCOM exports to a local outPath; without
outPath, images come back inline (scaled to maxSide, default 1600 px) so the assistant can look at them.
ancestry_media_edit upload and ancestry_tree_edit import_gedcom read from a local filePath.
Ids: treeId from ancestry_tree list; personId is the plain numeric id (not the pid:1030:tree form);
assertionId, citationId, webLinkId come from ancestry_person get; collectionId + recordId from
ancestry_search_records or ancestry_hint list; imageId from ancestry_record get.
Results: JSON, trimmed to what a caller acts on. The heavy reads (ancestry_person get/family_view, ancestry_hint list,
ancestry_media list, ancestry_dna matches, ancestry_notes_tags list_tags, ancestry_source list_sources) take a
responseFormat of concise (default) or detailed; concise drops bulky secondary fields such as per-fact citation id
lists, download URLs and field-by-field hint comparisons, and each description states the expected size. A result over 36K characters (about 9K tokens, under the 10K-token warning and 25K-token cap Claude Code applies to tool results; override with
MCP_MAX_RESULT_CHARS) is cut at an item boundary so it stays valid JSON, and carries a _truncated field with
the count shown, the total, and how to get the rest. Errors, including validation errors and upstream HTTP
failures, come back as tool results with isError: true and a message that says what to do next. A 429 from
Ancestry is retried once after its Retry-After; if it persists the error says how long to wait.
Tests
pnpm test— unit tests, no network. Besides the result helpers, the search query builder and the image-index and checker projections,test/writes.test.tsruns the real client againsttest/fake-http.ts(a transport double that serves declared routes and records every call) and checks the body each replace-style write sends when given one field.test/conventions.test.tsandtest/tools.test.tsare static checks: no write method swallows a failed read, every replace-style write merges throughmergeGiven, every tool action has a handler and every handler an action, every parameter a description.pnpm mcp-test— read-only end-to-end run over stdio against your account: checks that every tool has a title, full annotations and described parameters, calls every read action, fails if any default read comes back truncated, downloads one image inline and one to disk, and checks that error paths come back asisErrorresults. SetANCESTRY_TEST_TREEto choose the tree; else your first tree is used. Prints definition size and per-call latency.pnpm mcp-write-test— every write action on a throwaway tree that it creates and deletes afterward (deletion runs in afinally, so a failing step does not leave the tree behind). Also briefly sets and clears a note on one DNA match that had none.
Evals
evals/ holds a small harness for measuring tool-layout changes with real agents rather than intuition:
evals/run.tsruns read-only research tasks (tasks.json) or editing tasks on a throwaway tree (write-tasks.json) through headless Claude Code against any server layout described by anmcp-<name>.json, records tool calls, errors, writes, tokens, cost and time, and has an Opus judge grade each answer against a rubric.node evals/run.ts --layouts old,new --tasks-file write-tasks.json. Costs real money; results are gitignored.evals/transcript-stats.pysummarises MCP usage in Claude Code session transcripts: calls, error rates, result sizes and every distinct error message per tool and action.evals/field-usage.pymeasures, per returned field, how often agents actually used the value afterwards. The concise/detailed split of the read tools was chosen from its output.
Repository layout
src/http.ts— Chrome-impersonating HTTP client (impit), CSRF tokens, JSONP, throttling, Cloudflare and rate-limit handlingsrc/session.ts— cookie jar persisted in.session.jsonsrc/browser.ts— Chrome bootstrap used only bylogin(sign in, export cookies, close)src/client.ts— typed client for every endpointsrc/result.ts— tool-result helpers: JSON-safe truncation, inline images, errors, action dispatchsrc/tools.ts— MCP tool definitionssrc/server.ts— stdio entry point and server instructionsscripts/—login.ts, the Chrome launcher, the traffic recorder used during reverse engineering, the two end-to-end tests, andmeasure-limits.ts(field length limits, measured on a throwaway tree)test/— unit tests andfake-http.ts, the transport double they run the client againstCLAUDE.md— the rules for changing this code, one per bug class found in the fieldevals/— tool-layout eval harness and transcript analysis scriptstest-fixtures/— tiny image and GEDCOM used by the testsdocs/— research notes and the endpoint contracts
Not covered
Stories (a separate slide-editor app), tree sharing and invitations, merging duplicate people, and comments. Everything
else visible in the Ancestry UI has a tool; ancestry_raw_request covers the rest.
Caveats
Ancestry's terms of service restrict automated access. This is a personal tool for your own account and trees; use it accordingly. Writes are serialized with a small delay to stay gentle.
ancestry_search_recordsmirrors the website's full form: per-name match level, per-event year tolerance and place scope, exact relatives/keyword, collection focus, and a passthrough for collection-specific fields.Ancestry's name parameters are positional (
{given}_{surname}), so a surname-only search has to send the empty given slot. Sending the bare surname returns people whose given name matches, which reads like a real result —test/search.test.tsand the surname assertion inpnpm mcp-testguard against that regressing.Replace-style writes refill what the caller left out. Several of Ancestry's save forms are full replaces (person quick-edit, fact save, source, media), so a call that gives only one field would blank the others. Every such write in
client.tsreads the current values first and merges the given fields in; an explicit empty string still clears a field, and a read that fails refuses the write with a message naming what it would have blanked. Where the endpoint keeps fields it is not sent (tree basic info, tree privacy, citation) only the given fields are sent, never empty defaults.scripts/mcp-write-test.tshas a partial-edit check for each. Any new write must be classified the same way before it ships.Free-text fields have ceilings that Ancestry mostly enforces by silently truncating on save. The tools reject over-long values instead, and carry the limit as
maxLengthin their schema: fact description 1024, source title 256, citation detail 256, media title 128, web link title 64 (HTTP 500 above it), custom tag name 64. Person notes, citation transcription and otherInfo, media description and source note took 16k without complaint. Re-measure withnode scripts/measure-limits.ts, which probes a throwaway tree and deletes it.Ancestry's image index (
index-panel-data) returns every record on an image in one response and ignores paging parameters, and each record lists every indexed column of the collection even when empty (a 50-line census page is 120–190K characters raw).ancestry_record image_indexdrops the empty columns and pages, filters (name, household, record ids) and projects fields client-side;test/image-index.test.tscovers that.The Tree Checker list has no total count;
ancestry_tree checkerreportshasMorefrom a full page. Duplicate pairs come from Ancestry's per-person check, one request per flagged person. There is no endpoint to merge or dismiss a pair (the guessed ones are recorded as dead ends indocs/endpoints.md).Tool schemas are strict: a parameter the tool does not have is rejected by name. Recorded research sessions passed an
actiontoancestry_search_records147 times without ever learning it was ignored; a misspelled real parameter would have vanished the same way.ancestry_search_recordsacceptsaction: "search"for uniformity.A refused fact save is an HTTP 200 whose body says
statusCode: 400(custom events do this); the tool reports it as an error rather than a saved fact with id "0".Cloudflare's firewall rejects some long person notes with a 403 on an otherwise healthy session; the error says so and how to shorten, instead of telling the agent to log in again.
The research project that uses these servers should run them from a separate stable clone, not from a checkout that is being edited: a running research session lost every record search for two hours after a code session edited
src/client.tsin the checkout its.mcp.jsonpointed at.The endpoints are internal and can change without notice.
pnpm mcp-testis a quick regression check..session.json,.profile/andcaptures/hold live session cookies and are gitignored. Never commit them.The server logs only to stderr, so it is safe on stdio.
Available Tools
13 toolsancestry_sessionARead-only
Ancestry login session (cookies in .session.json; no browser needed for normal use). "status" reports whether the session is valid and who the user is. "login" opens the sign-in page in a dedicated Chrome window, waits for the user to sign in, saves the cookies, and closes Chrome — run it only when status says not logged in.
Inputs: action (required), timeoutSeconds.
dnaB
AncestryDNA. Actions: tests (testGuid per kit) · matches (the website match list with all its filters: minCm/maxCm, parentalSide maternal|paternal|both|unassigned, trees public|private|unlinked, groupIds (2 = starred; custom ids from groups), nameSearch (match name or username), unviewedOnly, notesOnly, newOnly, sharedWithMatchGuid = shared matches with that match; sortBy RELATIONSHIP (default) | DATE; pageSize ≤ 50) · groups (starred/custom group ids and match counts) · add_to_group / remove_from_group (matchGuids + groupId) · match_detail (shared cM/segments, predicted relationship, trees) · set_note (private note on a match; empty clears) · ethnicity (estimate regions).
Inputs: action (required), note, page, maxCm, +15 more.
factADestructive
Facts/events on a person (birth, death, residence, occupation, census, burial, marriage, custom…). Actions: list_types (valid eventType keys) · add · edit (all fields replaced; needs assertionId from person.get) · delete · attach_citation / detach_citation (existing citationId; pass databaseId+recordId for record citations) · attach_media / detach_media (mediaId). Relationship events (marriage, divorce, engagement…) require spouseId. Dates are free text that Ancestry parses: "12 Mar 1850", "Mar 1850", "1850", and the GEDCOM qualifiers "abt 1850", "bef 1850", "aft 1850", "bet 1850 and 1860"; anything else ("circa", "1850s", "early 1800s") is stored unparsed and never matches records or hints. Place is free text, most specific first, like "Cocke County, Tennessee, USA".
Inputs: action (required), date, place, title, +11 more.
hintA
Ancestry hints (suggested records, photos, member-tree people). Actions: summary (counts) · list (pending hints across a tree, newest first; filter by firstName/lastName; page/pageSize) · compare (record vs tree person side by side — what the Review page shows; do this before accepting) · set_state (rejected = No/ignore, deferred = Maybe, pending = undo ignore). To ACCEPT a record hint use record.save_to_person; to accept a photo hint use media.copy_from_tree.
Inputs: action (required), page, state, hintId, +7 more.
mediaADestructive
Photos, documents and stories in a tree. Actions: list (for a person, or the whole tree when personId is omitted) · get · download (full-size file to outPath, or base64; maxSide scales) · upload (local file → person gallery) · update (title/date/description/location; omitted fields kept) · set_profile_photo · copy_from_tree (copy a photo from another member's public tree or a photo hint into destPersonIds) · delete. Attaching media to a fact is fact.attach_media.
Inputs: action (required), date, page, cropX, +15 more.
messagesA
Ancestry member messaging and notifications. Actions: list_conversations · get_messages (conversationId) · member_profile (public profile by userId) · send (reply in conversationId, or start a new one with toUserId — sends real mail to a real person; confirm with the user first) · notifications.
Inputs: action (required), limit, userId, content, +2 more.
notes_tagsB
Research notes and MyTreeTags on a person. Actions: get_notes · save_notes (replaces the whole note; read first to append) · list_tags (tag ids by category, includes custom tags) · add_tags / remove_tags (tagIds; set custom=true for custom tag ids) · create_custom_tag.
Inputs: action (required), name, note, custom, +4 more.
personADestructive
People in a tree (also works read-only on other members' public trees). Actions: list (100/page, optional name filter; brief rows — use get for details) · find (name autocomplete → PID, years) · get (full profile: facts with assertionIds, citations, sources, relationships with modifiers, notes, web links; set includeSourceDetails for full source bibliographies) · family_view (ancestors/descendants graph) · relationship_to_home · add (NEW person related to anchorPersonId as Father/Mother/Spouse/Child/Sibling) · link_existing (link two existing people) · update (quick edit name/gender/living/birth/death; omitted fields kept) · change_relationship (modifier codes: parent-child pcb biological, pca adopted, pcs step, pcf foster; spouse spu spouse, spx ex, spp partner) · remove_relationship (unlink, keeps both people) · delete (permanent; displayName must equal "First Last"). Living people: add/update default to deceased; a person marked living is hidden from everyone the tree is shared with and from public view, and Ancestry auto-marks people born within ~100 years with no death as living in some views — set living explicitly. type codes for relationship actions are the related person's role seen from personId: F father, M mother, C child, H husband, W wife.
Inputs: action (required), name, page, type, +23 more.
raw_requestADestructive
Escape hatch: perform any ancestry.com request through the logged-in browser (same-origin fetch with cookies; CSRF added for hintsui/DNA APIs). Only when no action above fits. path like "/api/treesui-list/trees?rights=own". See docs/endpoints.md for known endpoints.
Inputs: path (required), method, jsonBody, dnaContext.
recordA
Historical records and places. Actions: get (indexed fields, image id, which of my trees have it, suggested records, other people on the same image) · image_index (every record indexed on one image, e.g. a census page) · download_image (full-res JPEG; maxSide to scale; outPath to write a file) · save_to_person (attach record to a tree person exactly like the site Review page: creates the citation and merges events — events "new" (default) | "all" | list like ["Residence"]; updateExisting=true replaces tree facts that differ (site default), saveAsAlternate=true keeps both, updateName=true takes the record name; includeRelatives adds household/parents/spouse from the record as new people or links matched ones — run once without it and read availableRelatives, then pass their ids or "all") · suggest_places (place autocomplete → gpid) · shoebox_list / shoebox_save / shoebox_remove.
Inputs: action (required), term, title, events, +11 more.
search_recordsARead-only
Search Ancestry historical records (census, vital, military, immigration…), optionally member trees, stories and photos — the full website search form. Names are "First Last"; years are 4-digit; places are free text ("Cocke County, Tennessee, USA") and should carry a gpid from record.suggest_places when a placeScope is used. Default matching is fuzzy everywhere (results are ranked, not filtered). Tighten per field: firstNameMatch/lastNameMatch (cumulative levels like the site slider: exact ⊂ similar ⊂ phonetic ⊂ initials|soundex); each event object takes year, yearEnd (range), yearTolerance (0 = that year only, 1/2/5/10 = ± years, omit = fuzzy), place, gpid (from record.suggest_places) and placeScope (exact, county, county_adjacent, state, state_adjacent, country; omit = fuzzy); exactRelatives, exactKeyword, exactRace. Name wildcards: "?" = one character, "" = 0–5 characters; a wildcarded name needs at least 3 literal letters and must start or end with a letter (Sm?th, Johson) and only applies with exact name matching. Start broad (name + birth year), then narrow. collectionFocus re-ranks toward a country/ethnicity; collectionId restricts to one collection; extraParams passes any other site query parameter (collection-specific fields, e.g. f-SelfMilitaryRank). Returns up to 50 hits per page with indexed fields and collection/record ids for record.get and record.save_to_person.
Inputs: page, race, birth, child, +28 more.
sourceADestructive
Sources, repositories, citations and web links. A source is the work (book, deed book, website); a citation is a person-level reference into it, which fact.attach_citation then links to facts. Actions: list_sources · create_source (title only; then update_source for author/publisher/etc.) · update_source · create_repository · link_repository / unlink_repository · create_citation (sourceId + detail, optional url) · update_citation · get_citation (with the facts it supports and, for record citations, the indexed record) · remove_citation · add_web_link · remove_web_link (webLinkId from person.get).
Inputs: action (required), url, date, name, +22 more.
treeADestructive
Family trees the user owns. Actions: list (own or shared trees) · get (home person, counts, quality stats) · create (new tree with a root person) · update (name/description) · set_privacy · export_gedcom (writes ZIP with .ged to outPath, or returns base64) · import_gedcom (local .ged file → NEW tree, waits for completion) · delete (PERMANENT; requires confirmName = exact tree name; confirm with the user first). Required: get/update/set_privacy/export_gedcom/delete need treeId; create needs name + root* fields; import_gedcom needs name + filePath.
Inputs: action (required), name, rights, treeId, +12 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Each tool targets a distinct domain: session management, hints, trees, people, facts, sources, notes/tags, record search, record details, media, DNA, messages, and a raw API escape hatch. There is minimal overlap; even where hint and record interact, their roles are clearly separated and complementary.
Most tool names are concise lowercase nouns (person, tree, fact, source, record, media, dna) which is a consistent pattern. Minor deviations like search_records, notes_tags, and raw_request break the pure-noun convention but remain readable and predictable.
With 13 tools, the server is well-scoped for a comprehensive genealogy platform. Each tool represents a major functional area (trees, people, records, DNA, messaging, etc.) and earns its place without bloat or excessive granularity.
The tool surface covers the core ancestry workflows: tree CRUD, person management with relationships, facts, sourcing/citations, record search and attachment, media handling, DNA insights, hints review, notes/tags, and messaging. There are no obvious dead ends for typical genealogy research tasks.
Related MCP Connectors
Family history and genealogy in your AI: your tree and FamilySearch, with a source for every fact.
Read-only A2Me family context tools for AI assistants (members, dates, activity, relationships)
Let AI agents use your real, signed-in Chrome: tabs, clicks, forms, screenshots and video.
Connect AI assistants to your approved computer for files, commands, browser and desktop tasks.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to create, edit, and query genealogical data from GEDCOM files. Supports complex genealogy searches, automatic data enrichment from web sources, relationship analysis, and biography generation for individuals and families.17MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to interact with Gramps genealogy databases for intelligent family tree research and management. Provides comprehensive tools for searching family data, creating records, analyzing relationships, and tracking genealogy research through natural language.51AGPL 3.0
- AlicenseAqualityDmaintenanceEnables AI assistants to load and query local GEDCOM genealogy files, supporting search, browsing, and family tree traversal.22AGPL 3.0
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to query genealogy data from GEDCOM files with 24 tools for search, navigation, relationship analysis, and timeline events.1MIT