# FamilySearch (pypi · familysearch-mcp)

Genealogical research on FamilySearch: historical places, indexed records, page images, the tree.

- Trust score: 81/100 (high trust)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-01

## Components

- pypi · `familysearch-mcp`: 81/100 (this document), [markdown](https://verifymcp.io/servers/ianderso-familysearch-mcp/familysearch-mcp.md), [page](https://verifymcp.io/servers/ianderso-familysearch-mcp/familysearch-mcp)

## Channel facts

- Registry: `pypi`
- Package: `familysearch-mcp`
- Version: `1.0.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-10-01.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised build step with no custom scripting around it.
  - 1 of 32 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to ianderso/familysearch-mcp).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 1 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 71/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 4824 tokens (~192/item across 25 items; 25 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 25 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 26 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the FamilySearch MCP server?

FamilySearch runs locally as a PyPI package, launched with uvx familysearch-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add ianderso-familysearch-mcp -- uvx familysearch-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "ianderso-familysearch-mcp": {
      "command": "uvx",
      "args": [
        "familysearch-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "ianderso-familysearch-mcp": {
      "command": "uvx",
      "args": [
        "familysearch-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add ianderso-familysearch-mcp -- uvx familysearch-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ianderso-familysearch-mcp": {
      "type": "local",
      "command": [
        "uvx",
        "familysearch-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add ianderso-familysearch-mcp --command uvx --arg familysearch-mcp
```

### Hermes

```yaml
mcp_servers:
  ianderso-familysearch-mcp:
    command: "uvx"
    args: ["familysearch-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "ianderso-familysearch-mcp": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "familysearch-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add ianderso-familysearch-mcp -t stdio -c uvx -a familysearch-mcp
```

### Other

```json
{
  "mcpServers": {
    "ianderso-familysearch-mcp": {
      "command": "uvx",
      "args": [
        "familysearch-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-10-01 (score 81, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-09-30 (score 66)

First indexed and scored.

## MCP tools (25)

### `search_places` (~144 tokens)

Look up a place in the FamilySearch gazetteer.

Resolves a bare place name to its full jurisdictional form and
coordinates, which is what a properly-formed place record needs. Each
result carries a place id you can pass to get_place or
get_place_jurisdictions.

If the place name comes from a record with a date on it, prefer
search_places_at_date: jurisdictions change, and the modern answer is
often the wrong one.

Works without credentials.

Input parameters:

- `count` (integer): Maximum results to return (1-50).
- `name` (string, required): Place name to look up, e.g. 'Kaskaskia'.

### `search_places_at_date` (~252 tokens)

Resolve a place as it existed in a particular year.

Jurisdictions are not stable. Counties are created, split, renamed and
abolished, so a record naming a county that no longer exists is ordinary
rather than an error. Filing that record under the modern county that now
covers the ground is a common mistake, and an invisible one: the place
name still looks plausible, but it sends the next search to the wrong
courthouse and the wrong record set.

Give it the name as the record spells it and the year of the record. Each
result carries the span over which that jurisdiction existed, so you can
see whether it was the right one at the time.

Works without credentials.

Input parameters:

- `count` (integer): Maximum results to return (1-50).
- `name` (string, required): Place name as the record spells it.
- `within_place_id` (string): Optional id of a jurisdiction to search inside, from a previous place lookup. Narrows an ambiguous name to one region.
- `year` (integer, required): The year to resolve the place as of, e.g. 1850. Use the year of the record the place name came from, not today.

### `get_place` (~105 tokens)

Read one place: its jurisdictional chain, type, coordinates and dates.

The dates are the ones that matter for research. A place description
records the span over which that jurisdiction existed, so you can check
whether the county a record names was the county in being on the date the
record was made.

Works without credentials.

Input parameters:

- `place_id` (string, required): FamilySearch place description id, e.g. '7344697'. Place lookups return one on every result.

### `get_place_jurisdictions` (~105 tokens)

Walk a place's containment chain upward to the country.

This is what turns "Kaskaskia" into "Kaskaskia, Randolph, Illinois, United States".
The chain is returned innermost first, each level carrying its own id,
type and the span over which it existed, so you can see at which level
the naming changed.

Works without credentials.

Input parameters:

- `place_id` (string, required): FamilySearch place description id to walk upward from.

### `search_records` (~536 tokens)

Search historical records by name, events, relatives, type or collection.

Beyond a person's own name and dates, two kinds of criteria matter:

Relationship criteria. Searching for a man by his wife's or his father's
name is how you find him when his own name was misindexed, mis-spelled
or abbreviated to an initial. An indexer who mangled "Chesebrough" often
got the wife's "Mary" right.

Scoping. Restricting to a record type or a single collection turns a
search of the whole archive into a search of one register, which is what
you want once you know which register should hold the entry.

Pass at least one name. Everything else narrows.

Requires an access token.

Input parameters:

- `birth_place` (string): Birth place, free text.
- `birth_year`: Approximate birth year.
- `collection_id` (string): Restrict to one collection, by the id search_collections returns. Scoping to a collection is how you search a specific register rather than the whole archive.
- `count` (integer): Maximum results to return (1-100).
- `death_place` (string): Death place, free text.
- `death_year`: Approximate death year.
- `exact` (boolean): Require names and places to match exactly. Off by default, because indexed spellings vary and fuzzy matching is usually what you want. Turn it on when a common name returns noise.
- `father_given` (string): Father's given name(s).
- `father_surname` (string): Father's surname.
- `given` (string): Given name(s) of the person sought.
- `loose` (boolean): Rank by similarity instead of requiring every criterion to match. Off by default: FamilySearch treats a search term as a scoring hint unless told otherwise, so a filter that does not filter is the su…
- `marriage_place` (string): Marriage place, free text.
- `marriage_year`: Approximate marriage year.
- `mother_given` (string): Mother's given name(s).
- `mother_surname` (string): Mother's surname, usually her maiden name.
- `offset` (integer): Results to skip, for paging.
- `record_type` (string): Restrict to one kind of record: birth, marriage, death, census, immigration, military, probate or other.
- `residence_place` (string): A place the person is known to have lived.
- `spouse_given` (string): Spouse's given name(s).
- `spouse_surname` (string): Spouse's surname.
- `surname` (string): Surname of the person sought.

### `get_record` (~179 tokens)

Read one indexed record in full.

A search returns a persona: one person's summary of what a record said.
This returns the record, which is more: every person named on it, and
the indexed fields behind each of them, labelled with the box on the
original form each value was read from.

The fields are where a search summary loses things -- the informant, the
witness, the enumerator's spelling, the age that contradicts the
birth year.

This is still the index, not the document. Use get_record_image to reach
what was actually written.

Requires an access token.

Input parameters:

- `ark` (string, required): Record ark or id, e.g. '1:1:XXXX-YYY' or the full 'ark:/61903/1:1:XXXX-YYY'. Record searches return one per hit.

### `get_record_image` (~182 tokens)

Find the document image an indexed record was taken from.

The persona is somebody's reading of the record. The image is the
record. Indexers mis-read hands, skip columns, normalise spellings and
guess at ages, so anything that matters should be checked against the
film.

Returns whatever the record offers as a route to the image: image and
waypoint links, and the digital film (DGS) and image numbers indexed
against it. Many records carry no image link at all, in which case this
says so rather than inventing one -- a great deal of the archive was
indexed from microfilm that has never been published, and the answer is
then to read the citation and order the film.

Requires an access token.

Input parameters:

- `ark` (string, required): Record ark or id whose source image you want to reach.

### `search_collections` (~304 tokens)

Find a record collection, so a search can be scoped to one.

A collection is one register, census or index -- "Connecticut Church
Records, 1630-1920" rather than the whole archive. Once you know which
collection should hold an entry, scoping search_records to its
id turns a fishing expedition into a lookup, and turns a nil result into
something that means anything.

Each result carries a coverage statement: which record types, which
place, which years. Read it. A collection covering 1850 to 1900 cannot
answer a question about 1840, and the difference between "no record
exists" and "I searched a collection that could not contain it" is the
whole of the reasoning.

FamilySearch offers no search over the catalogue, so this walks it and
matches your words against the titles. The catalogue runs to thousands of
collections and the API pages it in blocks of roughly ninety, so this
makes several calls the first time; the result is cached.

Works without a token.

Input parameters:

- `count` (integer): Maximum collections to return (1-200).
- `query` (string): Words to match against collection titles, e.g. 'connecticut church'. Leave empty to browse what is there.
- `refresh` (boolean): Re-fetch the catalogue rather than use the cached copy. Takes about ninety seconds; only needed when looking for a collection published since the cache was built.

### `get_collection` (~104 tokens)

Read one record collection: what it covers, and how much of it there is.

Worth reading before trusting a nil result. The counts say how many
records, people and images the collection holds, and a collection whose
image count is far below its record count was indexed from film that was
largely never published.

Works without a token.

Input parameters:

- `collection_id` (string, required): Record collection id, e.g. '2178'. search_collections returns one per result.

### `get_person` (~139 tokens)

Read one person from the FamilySearch shared tree.

Returns names, sex and facts. Use it to find records: get_person_sources
on the same id is usually the next call, because it leads out of the
tree towards a document.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `person_id` (string, required): FamilySearch person id, e.g. 'K2ZP-VY1'.

### `get_person_relatives` (~178 tokens)

Read a tree person's parents, spouses, children and siblings.

One call for the whole immediate family, which is what you need to judge
whether a profile is the person you are looking for. A family that does
not fit -- a child born before the marriage, a wife with the wrong
surname, parents twenty years too young -- is the usual first sign of a
conflation of two same-named people.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `person_id` (string, required): FamilySearch person id, e.g. 'K2ZP-VY1'.

### `get_ancestry` (~228 tokens)

Walk a tree person's pedigree back through the generations.

Each person carries an Ahnentafel position: 1 is the person asked about,
2 and 3 their father and mother, 4 to 7 their grandparents, and so on --
so a flat list reads as a tree, and a gap is visible as a missing number.

The further back a pedigree runs the less of it is sourced. Lines beyond
about five generations are frequently copied rather than researched, and
a long unbroken pedigree is a reason for more suspicion rather than less.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `generations` (integer): Generations to return, 1 to 8. The person is generation 1, their parents 2, grandparents 3.
- `person_id` (string, required): FamilySearch person id to walk back from.

### `get_descendancy` (~168 tokens)

Walk a tree person's descendants forward through the generations.

Useful for the sideways search: when a person's own record cannot be
found, a descendant's obituary, probate or pension file often names him.

Living descendants are withheld and come back marked as restricted.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `generations` (integer): Generations to return, 1 to 4. FamilySearch limits this one more tightly than ancestry, because a descendancy fans out.
- `person_id` (string, required): FamilySearch person id to walk forward from.

### `get_person_sources` (~241 tokens)

Read the sources attached to a tree person.

This is the most useful thing in the tree, because it is the way out of
it. Each attached source carries a citation and usually an ark pointing
at an indexed record, so a profile that seemed unsupported becomes a
list of documents to read.

Each source also carries what it is said to support -- Name, Birth,
Death -- which distinguishes "this person has sources" from "this
person's death date has a source". A profile with ten sources, none of
which touch the fact you care about, has told you nothing about it.

A profile with no sources at all is not evidence of anything. It is
somebody's assertion, and should be treated as one.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `person_id` (string, required): FamilySearch person id, e.g. 'K2ZP-VY1'.

### `get_person_memories` (~182 tokens)

Read the photographs and documents attached to a tree person.

Memories are uploads: a headstone photograph, a scanned letter, a family
Bible page, a typed story. Some are primary documents worth citing;
others are a relative's recollection written down eighty years later.
What each one is depends entirely on what was uploaded, so look before
relying on it.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `count` (integer): Maximum memories to return (1-100).
- `person_id` (string, required): FamilySearch person id, e.g. 'K2ZP-VY1'.

### `get_person_changes` (~195 tokens)

Read the change log of a tree profile: who edited it, when and why.

This is how you judge what you are looking at. A profile assembled in
one sitting last month by one contributor is a different kind of claim
from one built over years by several. A name or a parent that changed
recently, with no reason given, is where a conflation of two same-named
people usually enters.

Each entry carries the contributor, the timestamp, what changed and any
reason they typed.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `person_id` (string, required): FamilySearch person id, e.g. 'K2ZP-VY1'.

### `get_matches` (~274 tokens)

Read FamilySearch's own candidate matches for a tree person.

With collection 'tree' these are profiles the system thinks may be the
same person -- the duplicates behind most conflations, and the reason a
person appears twice with two different sets of parents.

With collection 'records' they are indexed records that may belong to
this person, which is a lead towards a document.

These are the system's guesses, scored by its own confidence. A high
score is a reason to look, never a reason to conclude.

Record matches are restricted in production to applications FamilySearch
has certified; an uncertified one gets a refusal here rather than
results.

The FamilySearch shared tree is community-edited: anyone can change this profile, and conflations of two same-named people are common. What this returns is a hint about where to look, not evidence. Follow it to an underlying record and cite that instead.

Requires an access token.

Input parameters:

- `collection` (string): Where to look for candidates: 'tree' for duplicate profiles in the shared tree, 'records' for indexed records that may be the same person.
- `count` (integer): Maximum candidates to return (1-100).
- `person_id` (string, required): FamilySearch person id, e.g. 'K2ZP-VY1'.

### `get_place_children` (~92 tokens)

List the places directly inside a jurisdiction.

The downward walk, complementing get_place_jurisdictions' upward one. Use
it to find the right sub-jurisdiction when a record names a town you
cannot place, or to see what a county contained at the time.

Works without a token.

Input parameters:

- `place_id` (string, required): Place id whose immediate children you want, e.g. '442'.

### `browse_waypoints` (~154 tokens)

Browse a collection's structure — its volumes, date ranges and films.

The way to reach a page the index never covered. Indexing is incomplete
across most of the archive, so a record you cannot find by searching may
still be sitting on an image you can browse to: collection, then volume
or date range, then film, then pages.

Works without a token. Pass a collection_id to start, then a waypoint_id
from the children to descend.

Input parameters:

- `collection_id` (string): Collection id to browse from the top, e.g. '1916078'.
- `waypoint_id` (string): Waypoint id to browse one level further down. Take it from a previous call's children.

### `get_records_on_image` (~115 tokens)

List every record indexed from one image.

The reverse of get_record_image. A passenger manifest page carries thirty
people and a census page forty; finding one of them tells you where the
others are. Use it to pick up a household, or to check whether the person
you want was indexed at all from a page you are already reading.

Requires an access token.

Input parameters:

- `image_ark` (string, required): A DigitalArtifact ark, e.g. '3:1:33SQ-G5LD-93NY'.

### `get_collection_fields` (~137 tokens)

Decode the field codes a collection's indexed records use.

An indexed record labels its values with codes rather than words —
\`PR_FTHR_NAME`, `EVENT_PLACE`, `PR_NAME_SURN_ORIG`. The codes are
per-collection, and this is the dictionary that turns them into "Father's
Name", "Event Place" and so on. Read it before interpreting a record from
a collection you have not worked with.

Works without a token.

Input parameters:

- `collection_id` (string, required): Numeric collection id, e.g. '1417683' for the 1880 US census. Find one with search_collections.

### `get_image_links` (~151 tokens)

Resolve a document image to its actual, fetchable URLs.

A record read does not carry image links; the image resource does, and
this is it. Returns the storage node, the deep-zoom descriptor, thumbnails
at several sizes, and `dist` — the full-resolution page.

Also returns the neighbouring pages. A pension file or a passenger
manifest runs to many images, and the entry you want is often not the one
the index pointed at.

Input parameters:

- `image_ark` (string, required): A DigitalArtifact ark, e.g. '3:1:33SQ-G5LD-93NY'. Take it from get_record_image's 'sources' entry whose resource_type is DigitalArtifact.

### `get_film_image` (~208 tokens)

Reach a page image by film and image number instead of by ark.

Citations often name a film and an image rather than an ark — those are
the `FS_DIGITAL_FILM_NBR` and `FS_IMAGE_NBR` fields on an indexed record,
and `get_record_image` reports them. This addresses the image directly on
the storage host, where `get_image_links` cannot help because there is no
ark to look up.

Checks the thumbnail first and says plainly whether the image exists, so
a wrong film or image number is a clear answer rather than a URL that
fails later. The thumbnail is readable without a token; the full page
needs one.

Input parameters:

- `film_number` (string, required): Digital film (DGS) number, e.g. '004893581'. Keep the leading zeros — they are part of the number.
- `image_number` (integer, required): Image number within the film, 1-based, as a citation gives it.

### `download_image` (~214 tokens)

Download a document image to a local file so it can be read.

This is the step that turns a citation into evidence. The image is
written to disk rather than returned inline: a full page scan runs to
megabytes, which is not something to push through a tool result.

Only FamilySearch image URLs are fetched, because the request carries
your access token.

Images are copyrighted or access-restricted in some collections. Treat a
downloaded file as a working copy for reading, not as something to
redistribute.

Input parameters:

- `destination` (string, required): Where to write the file, e.g. '/tmp/1880-census-p12.jpg'. It must be an image or PDF file name; the directory must already exist, and the file must not: an existing file is never overwritten.
- `image_url` (string, required): An image URL from get_image_links or get_film_image — normally 'full_image' for the readable page, or a thumbnail to check which page you have before spending the bandwidth.

### `auth_status` (~82 tokens)

Report whether credentials are configured, and what is missing.

With a token configured, also asks FamilySearch whether it is still
accepted (`token_accepted`): `authenticated` only means one is set, and
a token lasts about an hour.

This server ships no client id. Production access needs your own
registered FamilySearch application; see docs/AUTH.md.

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/ianderso-familysearch-mcp/familysearch-mcp#diagnostics

## Score history

- 2026-10-01: 81
- 2026-09-30: 66

## Common questions

### What is the FamilySearch MCP server?

FamilySearch is an MCP server listed in the public MCP registry as io.github.ianderso/familysearch-mcp. Genealogical research on FamilySearch: historical places, indexed records, page images, the tree. This page covers its PyPI package (familysearch-mcp).

### Is the FamilySearch MCP server safe to use?

FamilySearch scores 81 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 1 October 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the FamilySearch MCP server expose?

FamilySearch exposes 25 tools: search_places, search_places_at_date, get_place, get_place_jurisdictions, search_records, and 20 more. Their descriptions and schemas cost roughly 4,669 tokens of context every time the server is loaded.

### Is the FamilySearch MCP server still maintained?

FamilySearch is still listed as active in the MCP registry. We last reached this channel on 1 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the FamilySearch MCP server under?

FamilySearch declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- PyPI project: https://pypi.org/project/familysearch-mcp/
- Socket report: https://socket.dev/pypi/package/familysearch-mcp
- Repository: https://github.com/ianderso/familysearch-mcp
- Changelog RSS feed: https://verifymcp.io/servers/ianderso-familysearch-mcp/familysearch-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/ianderso-familysearch-mcp/familysearch-mcp.json
- HTML version of this page: https://verifymcp.io/servers/ianderso-familysearch-mcp/familysearch-mcp
