familysearch-mcp
An MCP server for genealogical research on FamilySearch: a historical place gazetteer, indexed record search, the page images behind the records, and reads of the shared family tree.
Nothing here writes to FamilySearch. The shared tree is community-edited and conflations of same-named people are common, so a tree result is a hint: follow it to the underlying record and cite that.
This is an independent project. It is not made, endorsed or supported by FamilySearch.
Credentials
This package ships no client id and never handles a FamilySearch password. Records, images and the tree need an access token from your own registered FamilySearch application; docs/AUTH.md explains why, and how to get one.
The gazetteer, the collection catalogue and the film browser answer without a token, so they work on a fresh install with no setup at all.
Install
uvx familysearch-mcp
That runs the server over stdio, which is how an MCP client starts it. You normally put it in the client's configuration rather than running it yourself.
Claude Desktop
{
"mcpServers": {
"familysearch": {
"command": "uvx",
"args": ["familysearch-mcp"],
"env": { "FS_ENV_FILE": "/path/to/familysearch.env" }
}
}
}
Point at an env file rather than pasting the token into env. A token that
lives in the client's config can only be replaced by editing it and
restarting; a token in the file is picked up mid-session. With no token at
all, leave env out and the anonymous tools still work.
Claude Code
claude mcp add familysearch -e FS_ENV_FILE=/path/to/familysearch.env -- uvx familysearch-mcp
Configuration
| Variable | Meaning |
|---|---|
FS_ACCESS_TOKEN |
Bearer token from your application's OAuth flow. |
FS_ENV_FILE |
The env file to read these settings from, and to re-read a refreshed token from. Default: the nearest .env from the working directory upward. |
FS_CLIENT_ID |
Your registered application's client id, reported by auth_status. |
FS_ENVIRONMENT |
production (default) or integration for the FamilySearch sandbox. |
FS_TIMEOUT |
HTTP timeout in seconds. Default 60. |
.env.example lists them with comments.
Tools
Twenty-five tools. All of them read; download_image also writes the page
it fetches to a local file.
Places
FamilySearch's Places API answers anonymously.
| Tool | Needs a token | Purpose |
|---|---|---|
search_places |
no | Resolve a place name to its full jurisdictional form and coordinates. |
search_places_at_date |
no | Resolve a place as it was in a given year. A record naming a county that no longer exists is normal; filing it under the modern one is an invisible error. |
get_place |
no | Read one place: jurisdictional chain, type, coordinates, and the dates that jurisdiction existed. |
get_place_jurisdictions |
no | Walk the containment chain upward — what turns "Kaskaskia" into "Kaskaskia, Randolph, Illinois, United States". |
get_place_children |
no | The places directly inside a jurisdiction — the downward walk. |
Records and collections
| Tool | Needs a token | Purpose |
|---|---|---|
search_records |
yes | Search historical records by name, life events, parents, spouse, record type or collection. Every criterion filters. Returns one hit per record, with the others named on it. |
get_record |
yes | Read one indexed record in full: every person on it and the labelled fields behind each. |
get_record_image |
yes | Find the document image a record came from, or the film number when no image was published. |
get_records_on_image |
yes | Every record indexed from one image. A census page carries forty people. |
search_collections |
no | Find a record collection by title, with its coverage. |
get_collection |
no | Read one collection: what it covers and how much of it there is. |
get_collection_fields |
no | Decode a collection's indexed field codes (PR_FTHR_NAME → "Father's Name"). |
browse_waypoints |
no | Browse a collection's volumes and films, to reach pages the index never covered. |
Page images
| Tool | Needs a token | Purpose |
|---|---|---|
get_image_links |
for the page | Resolve an image ark to fetchable URLs: full page, deep zoom, thumbnails, neighbouring pages. Without a token only the navigation comes back. |
get_film_image |
for the page | Reach a page by film and image number when a citation gives those instead of an ark. Checking the page exists needs no token. |
download_image |
yes | Download a page image to a new local file so it can be read. |
Shared tree — a lead, never a source
Every tool here reads a community-edited profile, and says so in its own description.
| Tool | Needs a token | Purpose |
|---|---|---|
get_person |
yes | Read a shared-tree person: names, sex, facts. |
get_person_relatives |
yes | Parents, spouses, children and siblings in one call. |
get_person_sources |
yes | What the tree attaches as sources, and which facts each supports. The fastest route out of the tree. |
get_ancestry |
yes | Pedigree walk back, up to 8 generations, numbered by Ahnentafel. |
get_descendancy |
yes | Pedigree walk forward, up to 4 generations. |
get_person_memories |
yes | Attached photographs, documents and stories. |
get_person_changes |
yes | The change log: who edited this profile, when, and why. |
get_matches |
yes | FamilySearch's own duplicate and record-match candidates. |
Setup
| Tool | Needs a token | Purpose |
|---|---|---|
auth_status |
no | Report what is configured, what is missing, and whether FamilySearch still accepts the token. |
How it behaves
- The tree is not evidence. The tree tools read profiles anyone can
edit. Use them to find records.
get_person_sourcesis the most useful of them because it leads out of the tree towards a document. - A persona is not the record. A search returns one person's summary of
what a record said;
get_recordreturns the indexed fields behind it, andget_record_imagethe document itself. Read down that chain before citing. - Jurisdictions move.
search_places_at_dateresolves a place as it was in a given year. Filing an 1820 record under the county that covers the ground today is a common and hard-to-spot error. - Record search uses the website's search service. The API's own record
search answers from a partial index that is almost all immigration
records.
search_recordsasks the service the FamilySearch website uses instead, with your token and a browser User-Agent, which that service requires. It is undocumented and FamilySearch can change or close it; docs/API-NOTES.md has the comparison. - Search criteria filter. FamilySearch treats a search term as a ranking
hint unless told otherwise, so adding a death year to a name search only
reorders it. This server asks for every criterion to match;
loose=Truegoes back to ranking. - Tokens expire, and a refreshed one is picked up. A token lasts about an
hour. On a 401 the server re-reads
FS_ACCESS_TOKENfrom the env file and retries once, so refreshing the file is enough.auth_statusreportstoken_accepted: falsewhen it is not. - Throttling is retried once. A 429 asking for a wait of up to 15 seconds
is waited out and retried. A longer wait, or a second 429, comes back as
rate_limitedwith the server'sRetry-After. - Unknown parameters are refused. A misspelt or invented argument is an error that lists the parameters the tool does take. It is not silently dropped, which would make a filtered search quietly return unfiltered results.
download_imageis careful with what it is given. It fetches only HTTPS URLs on FamilySearch hosts, because the request carries your token. It creates only image and PDF files, and never overwrites one.- Some routes are not publicly documented. FamilySearch's Historical
Records API is behind a login wall, so those routes and response shapes
were confirmed by live probing instead. A comment beside the code says
when, and docs/API-NOTES.md records what was found.
tests/live_check.pyasks again.
Development
git clone https://github.com/ianderso/familysearch-mcp
cd familysearch-mcp
uv sync --extra dev
uv run pytest # mocked with respx; no token, no network
uv run ruff check .
uv run ruff format --check .
uv run python -m tests.live_check re-asks FamilySearch the questions only
the live API can answer, with the token from your env file. See
CONTRIBUTING.md for what a change is expected to carry.
License
MIT.
Release files for familysearch-mcp 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| familysearch_mcp-1.0.0.tar.gz | 160.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| familysearch_mcp-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 208.0 kB
Release files / familysearch_mcp-1.0.0.tar.gz
| Download URL | familysearch_mcp-1.0.0.tar.gz |
|---|---|
| Size | 160.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
48b0cf8d82b7e7a4097504a07f13bdc16315661bdb41e79f9b82e35fa1894d2a
|
|
BLAKE2b-256 checksum How to use checksums |
9a0b246f0b6162053f9180c114ca09848082ef98603076c11a9c7bd8f0291fac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / familysearch_mcp-1.0.0-py3-none-any.whl
| Download URL | familysearch_mcp-1.0.0-py3-none-any.whl |
|---|---|
| Size | 47.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d35afb2740c89d2e5fd1fe1b8cb33590188e7812d3c446ee32981b2352fb4dd2
|
|
BLAKE2b-256 checksum How to use checksums |
81a3d88dd50c843f75fdd7c892bcdd25231169cb4d57bb38c4b80fdf40229272
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log