The Gramps Web MCP Server exposes the Gramps Web HTTP API as a set of MCP tools so AI assistants can inspect and edit a Gramps family tree over HTTP.[web:8][web:10] It targets a self-hosted Gramps Web API instance (usually via Docker) and focuses on core genealogy objects: people, families, events, and places.[web:10][web:12]
The server is stateless, configured via environment variables for base URL and auth token, and acts purely as an HTTP–JSON proxy with light validation and ergonomic argument shapes.
The gramps server should also produce a CLI tool that is easily compatible with OpenClaw as a skill.
- Provide a safe well-typed MCP interface over the Gramps Web API (read/write).[web:10]
- Cover the 80% workflows: searching, retrieving, and creating/updating people, families, events, and places.[web:10][web:19]
- Make person-centric workflows easy (e.g., get a person with families, ancestors, and descendants in one call via composition tools).[web:10][web:17]
- Keep configuration minimal (base URL, API token, tree ID).
- No direct DB access; all operations go through the Gramps Web HTTP API.[web:10]
- No schema migration or Gramps core plugin features.[web:17]
- No UI; this is MCP only, for consumption by AI clients.
- Backend: Python + Flask REST API for Gramps.[web:8][web:12]
- Typical base URL:
https://example.com/apiorhttp://localhost:5000/api.[web:10][web:20] - Standard REST endpoints include (names vary slightly by version):[web:10][web:19]
/meta– API metadata and version./people,/people/{handle}/families,/families/{handle}/events,/events/{handle}/places,/places/{handle}
- Authentication: typically bearer token or session cookie, configured on the API.[web:10][web:20]
- Acts as a thin adapter:
- Translates MCP tool calls → HTTP requests to Gramps Web API.
- Validates and normalizes arguments (e.g., person handle vs Gramps ID).
- Returns structured JSON responses suitable for LLM consumption.
The server is configured via environment variables and optional JSON config.
GRAMPS_WEB_BASE_URL(required)- Base URL of Gramps Web API, e.g.,
https://my-gramps/api.
- Base URL of Gramps Web API, e.g.,
GRAMPS_WEB_API_TOKEN(optional but recommended)- Bearer token or API key, sent as
Authorization: Bearer <token>.
- Bearer token or API key, sent as
GRAMPS_WEB_TREE_ID(optional)- Default tree ID or name if the API instance supports multiple trees.
GRAMPS_WEB_REQUEST_TIMEOUT_MS(optional, default 15000)- HTTP timeout in milliseconds.
GRAMPS_WEB_VERIFY_TLS(optional, defaulttrue)- When
false, disables TLS verification for self-signed dev setups.
- When
All tools must:
- Map HTTP 4xx/5xx into structured MCP errors with:
error_code(NOT_FOUND,VALIDATION,AUTH,SERVER).http_status.details(string with concise human message).
- Surface Gramps API validation errors as part of
details.[web:10]
These are logical JSON models exposed to MCP clients; internally they map 1:1 or very closely to Gramps Web API payloads.[web:10][web:17]
{
"handle": "string",
"gramps_id": "string",
"primary_name": {
"first_name": "string",
"surname": "string",
"suffix": "string | null"
},
"gender": "UNKNOWN | MALE | FEMALE | OTHER",
"birth": {
"event_handle": "string | null",
"date": "string | null",
"place_title": "string | null"
},
"death": {
"event_handle": "string | null",
"date": "string | null",
"place_title": "string | null"
},
"family_handles": {
"as_child": ["string"],
"as_spouse": ["string"]
},
"tags": ["string"],
"private": "boolean"
}{
"handle": "string",
"gramps_id": "string",
"father_handle": "string | null",
"mother_handle": "string | null",
"parent_handles": ["string"],
"child_handles": ["string"],
"marriage_event_handle": "string | null",
"tags": ["string"],
"private": "boolean"
}{
"handle": "string",
"gramps_id": "string",
"type": "string",
"date": "string | null",
"place_handle": "string | null",
"description": "string | null",
"citation_handles": ["string"],
"note_handles": ["string"],
"tags": ["string"],
"private": "boolean"
}{
"handle": "string",
"gramps_id": "string",
"title": "string",
"latitude": "number | null",
"longitude": "number | null",
"code": "string | null",
"place_type": "string | null",
"tags": ["string"],
"private": "boolean"
}This section defines the MCP tools, not the raw HTTP paths, but each tool includes the underlying HTTP method and URL pattern for implementation.
- Purpose: Check connectivity, authentication, and basic metadata.
- HTTP:
GET /meta[web:10] - Input:
{} - Output:
{ "status": "ok | degraded | error", "api_version": "string | null", "backend_version": "string | null", "tree_id": "string | null", "message": "string" } - Behavior:
- If HTTP 200:
status = "ok". - If 401/403:
status = "error",messageexplains auth issue. - If timeout or network error:
status = "error".
- If HTTP 200:
- Purpose: List people with simple filters and pagination.
- HTTP:
GET /people[web:10][web:19] - Input:
{ "query": "string | null", "limit": "number | null", "offset": "number | null", "order_by": "name | gramps_id | change | null", "ascending": "boolean | null" } - HTTP mapping:
/people?filter=<query>&limit=<limit>&offset=<offset>&order_by=<order_by>&ascending=<true|false>(parameter names adjusted to match API once confirmed).[web:10]
- Output:
{ "total": "number", "items": ["Person"] } - Notes:
queryis passed as a server-side filter/search parameter if supported; otherwise ignored.[web:19]
- Purpose: Fetch a person by handle or Gramps ID.
- HTTP:
GET /people/{handle}orGET /people?gramps_id=...depending on API.[web:10] - Input:
{ "handle": "string | null", "gramps_id": "string | null" } - Rules:
- Exactly one of
handleorgramps_idmust be non-null.
- Exactly one of
- Output:
{ "person": "Person" }
- Purpose: Full-text search across people fields (name, ID, notes where supported).
- HTTP:
GET /peoplewithsearchor similar query param (adjusted to actual API).[web:10][web:19] - Input:
{ "text": "string", "limit": "number | null" } - Output:
{ "items": ["Person"] }
- Purpose: Create a new person record.
- HTTP:
POST /people[web:10] - Input:
{ "primary_name": { "first_name": "string", "surname": "string" }, "gender": "UNKNOWN | MALE | FEMALE | OTHER | null", "birth": { "date": "string | null", "place_handle": "string | null" }, "death": { "date": "string | null", "place_handle": "string | null" }, "tags": ["string"] } - HTTP body mapping:
- Convert to Gramps Web
PersonJSON structure (fields likename,gender,event_ref_list, etc., following the OpenAPI/spec).[web:10]
- Convert to Gramps Web
- Output:
{ "person": "Person" }
- Purpose: Update existing person fields.
- HTTP:
PATCH /people/{handle}orPUTdepending on API.[web:10] - Input:
{ "handle": "string", "primary_name": { "first_name": "string | null", "surname": "string | null" }, "gender": "UNKNOWN | MALE | FEMALE | OTHER | null", "birth": { "date": "string | null", "place_handle": "string | null" }, "death": { "date": "string | null", "place_handle": "string | null" }, "tags": ["string"] } - Behavior:
- Only non-null fields will be updated where the API supports partial updates; otherwise, fetch-and-merge then
PUT.
- Only non-null fields will be updated where the API supports partial updates; otherwise, fetch-and-merge then
- Output:
{ "person": "Person" }
- Purpose: Delete a person by handle.
- HTTP:
DELETE /people/{handle}[web:10] - Input:
{ "handle": "string" } - Output:
{ "success": "boolean", "message": "string" }
- Purpose: List family units with pagination.
- HTTP:
GET /families[web:10][web:19] - Input:
{ "limit": "number | null", "offset": "number | null" } - Output:
{ "total": "number", "items": ["Family"] }
- Purpose: Fetch family by handle or Gramps ID.
- HTTP:
GET /families/{handle}orGET /families?gramps_id=...[web:10] - Input:
{ "handle": "string | null", "gramps_id": "string | null" } - Output:
{ "family": "Family" }
- Purpose: Create a new family unit linking spouses and children.
- HTTP:
POST /families[web:10] - Input:
{ "parent_handles": ["string"], "child_handles": ["string"], "marriage_event": { "date": "string | null", "place_handle": "string | null" } } - Output:
{ "family": "Family" }
- Purpose: Add/remove children or change parents for an existing family.
- HTTP:
PATCH /families/{handle}[web:10] - Input:
{ "handle": "string", "parent_handles": ["string | null"], "child_handles": ["string | null"], "marriage_event_handle": "string | null" } - Output:
{ "family": "Family" }
- Purpose: Paginated list of events.
- HTTP:
GET /events[web:10][web:19] - Input:
{ "type": "string | null", "limit": "number | null", "offset": "number | null" } - Output:
{ "total": "number", "items": ["Event"] }
- Purpose: Fetch event by handle or Gramps ID.
- HTTP:
GET /events/{handle}orGET /events?gramps_id=...[web:10] - Input:
{ "handle": "string | null", "gramps_id": "string | null" } - Output:
{ "event": "Event" }
- Purpose: Create a new event (birth, death, marriage, etc.).
- HTTP:
POST /events[web:10] - Input:
{ "type": "string", "date": "string | null", "place_handle": "string | null", "description": "string | null", "tags": ["string"] } - Output:
{ "event": "Event" }
- Purpose: Paginated list of places.
- HTTP:
GET /places[web:10] - Input:
{ "query": "string | null", "limit": "number | null", "offset": "number | null" } - Output:
{ "total": "number", "items": ["Place"] }
- Purpose: Fetch place by handle or Gramps ID.
- HTTP:
GET /places/{handle}orGET /places?gramps_id=...[web:10] - Input:
{ "handle": "string | null", "gramps_id": "string | null" } - Output:
{ "place": "Place" }
- Purpose: Create a new place record.
- HTTP:
POST /places[web:10] - Input:
{ "title": "string", "latitude": "number | null", "longitude": "number | null", "code": "string | null", "place_type": "string | null", "tags": ["string"] } - Output:
{ "place": "Place" }
These tools are composed operations that the MCP server performs by calling multiple underlying HTTP endpoints.
- Purpose: Given a person handle, fetch the person and all their immediate family records.
- HTTP:
GET /people/{handle}GET /families/{handle}for each family.[web:10]
- Input:
{ "handle": "string" } - Output:
{ "person": "Person", "families_as_child": ["Family"], "families_as_spouse": ["Family"] }
- Purpose: Fetch ancestors up to
max_generationsdepth. - HTTP:
- Repeated
GET /people/{handle}andGET /families/{handle}to walk parents.[web:10][web:17]
- Repeated
- Input:
{ "handle": "string", "max_generations": "number" } - Output:
{ "root_handle": "string", "generations": [ { "generation_index": "number", "person_handles": ["string"] } ], "people": { "<handle>": "Person" }, "families": { "<handle>": "Family" } }
- Purpose: Fetch descendants up to
max_generations. - HTTP:
- Similar to ancestors, but walking children via family links.[web:10][web:17]
- Input:
{ "handle": "string", "max_generations": "number" } - Output:
{ "root_handle": "string", "generations": [ { "generation_index": "number", "person_handles": ["string"] } ], "people": { "<handle>": "Person" }, "families": { "<handle>": "Family" } }
- All outgoing HTTP requests must attach
Authorizationheader ifGRAMPS_WEB_API_TOKENis set. - The MCP server must never log full tokens or API responses in plaintext logs.
- Optionally support a
read_onlymode:- When
GRAMPS_WEB_READ_ONLYenv istrue, disable all mutation tools:gramps_create_*,gramps_update_*,gramps_delete_person, etc.
- When
- Language: TypeScript or Python recommended.
- HTTP client: must support:
- Configurable timeout.
- TLS verification toggles.
- JSON encoding/decoding with robust error paths.
- The MCP manifest will describe:
- Each tool name and JSON schema for arguments/results.
- A clear description string matching the purpose described above.
This spec should be sufficient to implement a first version of the Gramps Web MCP server and keep the mapping to the GitHub-hosted Gramps Web HTTP API explicit and maintainable.[web:8][web:10][web:12]