ipt-mcp
# ipt-mcp
> **Experimental proof of concept.** It shows what could be done with a [GBIF IPT](https://www.gbif.org/ipt) with the
> help of an AI agent: create a resource, fill in its metadata, validate and add data, map it to Darwin Core and publish
> it, all by asking in plain language. It is not production software: expect rough edges, and try it on a test IPT first.
An MCP server that lets an AI assistant manage an IPT **from resource creation up to publication**, validating data
before it reaches the IPT.
## Setup
You need [Node.js 20 or newer](https://nodejs.org) and [git](https://git-scm.com/downloads).
**Quick way (Linux, macOS, Windows)**: the installer checks Node, installs the dependencies, saves your IPT(s) in a small
config file (without the password) and connects the server to your MCP client.
```bash
git clone https://github.com/vjrj/ipt-mcp.git
cd ipt-mcp
node scripts/install.mjs
```
It asks for a name (for example `demo`), the IPT URL, your login email and your MCP client (`claude-desktop`,
`claude-code`, or `print` to just show the configuration). Then set the password in the environment that launches the
client; the installer tells you the exact variable (for `demo`: `IPT_DEMO_PASSWORD`):
```bash
export IPT_DEMO_PASSWORD='your-password' # Linux / macOS (Windows: setx IPT_DEMO_PASSWORD "your-password")
```
Non-interactive, and how to add more IPTs (run it again with another name):
```bash
node scripts/install.mjs --name demo --url https://ipt-demo.example.org/ipt --email me@example.org --client claude-desktop
node scripts/install.mjs --name prod --url https://ipt.example.org/ipt --email me@example.org --client claude-desktop --readonly
```
### Several IPTs (demo, production, …)
The installer writes `~/.config/ipt-mcp/instances.json` (Windows: `%APPDATA%\ipt-mcp\instances.json`); you can also edit
it by hand. `${VAR}` takes the value from an environment variable, so no password lives in the file:
```json
{
"default": "demo",
"instances": {
"demo": { "url": "https://ipt-demo.example.org/ipt", "email": "me@example.org", "password": "${IPT_DEMO_PASSWORD}" },
"prod": { "url": "https://ipt.example.org/ipt", "email": "me@example.org", "password": "${IPT_PROD_PASSWORD}", "readonly": true }
}
}
```
Every tool accepts an `instance` argument, so you can say *"list the datasets on prod"* or *"create the resource on
demo"*; without it the `default` IPT is used. `ipt_list_instances` shows what is configured (never the credentials).
`"readonly": true` makes every write operation refuse to run on that IPT.
### Manual configuration
If you prefer to write the MCP client configuration yourself (one IPT, no config file):
```json
{ "mcpServers": { "ipt": {
"command": "node", "args": ["--import", "tsx", "/path/to/ipt-mcp/src/server.ts"],
"env": { "IPT_URL": "https://my-ipt.example.org/ipt", "IPT_EMAIL": "me@example.org", "IPT_PASSWORD": "…" }
} } }
```
- Without credentials only the public queries work. `IPT_URL` defaults to `https://ipt.gbif.org`.
- `IPT_INSTANCES` (a JSON file path, or inline JSON) replaces `IPT_URL`/`IPT_EMAIL`/`IPT_PASSWORD` when you have several IPTs.
- `IPT_READONLY=1` makes every write operation refuse to run on all IPTs.
- Publishing, changing visibility, deleting and replacing the EML **do nothing without explicit confirmation**: the
assistant explains what will happen (and on which IPT) and asks you first.
## What to ask (prompts by use case)
The examples assume a test IPT; replace names and paths. You can give everything in one message or go step by step.
### 1. See what is there
> Which resources can I manage on the IPT and what state is each one in (published version, visibility, valid metadata)?
> List the public datasets on my IPT that mention "plants" and tell me how many records each has.
> Which IPTs can you use? Now list the public datasets on `prod`.
### 2. Create a resource from scratch
> Create an occurrence resource with shortname `flora_valencia_2025`.
> Create a checklist resource called `bryophyte_catalogue`.
### 3. Create a resource from an existing DwC-A
> Create the resource `herbarium_uv` by importing `/data/herbarium_uv.zip`, and tell me whether it is ready to publish.
### 4. Fill in the metadata
> In `flora_valencia_2025` set the title "Vascular flora of the province of Valencia", language Spanish, licence CC0 and
> this description: "…". Contact, creator and metadata provider: Ana Perez, Valencia Botanical Garden, ana@example.org.
> Add geographic coverage (lat 38–40, lon −1 to 0.5, "Province of Valencia"), temporal coverage from 2019-01-01 to
> 2023-12-31 and taxonomic coverage for *Quercus ilex* and *Pinus halepensis*.
> Add the keywords "flora" and "Valencian Community" and these methods: study extent "province of Valencia", sampling
> "random transects", quality control "manual review by a botanist".
> Replace the resource metadata with the contents of this EML file: `/data/revised_eml.xml`.
> Show me the fields of the project section and fill in the title and funding with …
### 5. Add the data (with validation)
> Validate `/data/occurrences.txt` before uploading it and tell me what problems it has.
> Upload `/data/occurrences.txt` as a source of `flora_valencia_2025`. If it has problems, do not upload it and explain them.
> Add the URL `https://example.org/export.csv` as a source named `export`, comma delimiter, UTF-8, and show me the first
> rows as the IPT understands them.
> My large file has records split by line breaks: tell me how many rows are broken and on which lines.
### 6. Map to Darwin Core
> Map the source `occurrences` to Darwin Core Occurrence and tell me which columns were left unmapped.
> In the Occurrence mapping, assign `obs_date` to `eventDate` and `sp` to `scientificName`, and set `kingdom` = Plantae
> and `basisOfRecord` = HumanObservation as fixed values.
> Show me the current mapping and which required terms are missing.
### 7. Check before publishing
> Check whether `flora_valencia_2025` is ready to publish and tell me everything that is missing, section by section.
### 8. Publish
> Publish `flora_valencia_2025` with the summary "First version" and tell me when it finishes, how many records were
> generated and whether there were warnings.
> Publish only the metadata of `bryophyte_catalogue` (no data).
### 9. Make it public
> Make `flora_valencia_2025` public and publish a new version so the change takes effect. Then check that it appears in
> the public list with its record count.
### 10. Update a version
> I changed `/data/occurrences.txt`. Validate it, upload it again as the source `occurrences`, re-analyse it, check that
> the mapping is still complete and publish the next version.
### 11. Automatic publication
> Configure `flora_valencia_2025` to publish automatically every Friday at 12:00.
### 12. Diagnosis and clean-up
> The last publication of `flora_valencia_2025` failed: show me its status and the end of the log.
> Delete the source `test` and the resource `draft_test`.
### The whole flow in one message
> Using `/data/occurrences.txt`, create an occurrence resource `flora_valencia_2025`. Title "…", description "…",
> licence CC-BY, contact and creator Ana Perez (Botanical Garden, ana@example.org). Validate and upload the file, map it
> to Occurrence, check that it is ready and, if it is, tell me which version will be created and ask me to confirm
> before publishing.
## Tips
- Always ask to **"validate first"** when working with new files: the assistant uses `validate_tsv` and will not upload broken data.
- If something fails, the error includes the IPT's own message; ask the assistant to fix it and retry.
- For actions that need confirmation, answer "yes, go ahead" once it has described what it is going to do.
## Security
- The password is never returned to the model: every tool result and error is scrubbed of `IPT_PASSWORD`, URL
credentials and any field named password/secret/token/API key.
- Local files are only read or uploaded when they are real data files (`.txt .tsv .csv .xls .xlsx .zip .gz` for data,
`.xml` for EML, `.zip` for a DwC-A) and not inside hidden paths (`~/.ssh`, `~/.config`, `~/.mcp.json`, …).
Set `IPT_ALLOWED_DIRS=/data:/home/me/exports` to restrict them to specific directories.
- Prefer keeping passwords out of files the assistant can read: use `${VAR}` in the instances file (the installer does)
and export the variables from your secrets manager in the environment that launches the MCP client.
Technical reference, tests and CI: [DEVELOPMENT.md](DEVELOPMENT.md).
TDQS
Scored across 32 tools
Tools are broadly distinct: reads vs writes, resources vs mappings vs metadata, and validation stages are clearly separated. The main ambiguity is that several specialized metadata setters (basic, contacts, coverage, keywords/methods) overlap with the generic ipt_set_metadata_fields, so an agent could choose either route.
The dominant pattern is ipt_<verb>_<noun>, with list for collections and get for single items. Minor deviations include `ipt_health` with no verb, `validate_tsv` lacking the ipt_ prefix, and `ipt_publish` being a bare verb.
At 32 tools, the server is above the 25+ threshold and feels heavy for an agent to navigate. Several specialized metadata setters duplicate the generic ipt_set_metadata_fields and could be consolidated without losing functionality.
The surface covers the full publish lifecycle: create resources, add/configure sources, map terms, validate, publish, change visibility, and delete resources. Minor gaps exist, such as refreshing an existing URL source or directly downloading a published archive, but agents can work around them.