What is collected, and why
Telemetry is recorded per HTTP request and per tool call. Recording is best-effort and never blocks a live call: a record is placed on a bounded in-memory queue and written by a background thread. If the queue is full the line is dropped and counted; if the disk write then fails, that line is lost silently. So “recorded” below means a record is enqueued for the call, subject to these rare losses.
Tool arguments
The arguments you pass to a tool (for example a search_grants query, a text_search, or a pi_name) are recorded after MCP protocol validation: each validated non-null value is converted to its Python text form (str()) and truncated to its first 200 characters, while arguments you left unset or passed as null are omitted entirely. This is a faithful record of what you searched for, but it is not your raw JSON and not lexically exact — a JSON true is stored as True, a list as ['a', 'b'], and anything past 200 characters is cut.
Why: Know which tools are used and with what shape of input, so broken or missing capabilities are visible.
Salted hash of your IP address
Client IP addresses are never stored raw. Each address is salted-SHA256-hashed to a 16-character digest before it reaches disk. The raw IP exists only inside the in-memory rate limiter and is never persisted.
Why: Distinguish one caller from another for abuse control and usage counts, without holding an identifier that points back to you.
Country
Whatever the CF-IPCountry request header carries, bounded to 200 characters. In production Cloudflare stamps this header at the edge with a two-letter country code; the server stores the header value as received and does not itself geolocate a stored IP address.
Why: Understand where the service is used so capacity and coverage decisions are informed.
User agent
The User-Agent header you send, bounded to its first 400 characters.
Why: Identify which clients and versions call the service, and spot automated abuse.
MCP session id and origin
The per-connection MCP session id and the Origin header, each bounded to 200 characters. The server has no accounts and assigns you no identifier of its own, but it does persist the session id your client sends.
Why: Group the calls of one connection together when diagnosing a failed session.
Allowlisted request headers
Only request headers whose lowercase name appears in a fixed allowlist are recorded — accept, accept-encoding, accept-language, cache-control, cf-ipcountry, connection, content-length, content-type, host, mcp-protocol-version, mcp-session-id, origin, te, transfer-encoding, user-agent, via, x-forwarded-proto — each value bounded to 200 characters. Every other header is dropped whole: neither its name nor its value is written. A header dropped for not being on the allowlist increments a single other_header_names_count tally; a header dropped earlier by the credential/client-IP denylist is discarded without being counted at all.
Why: Diagnose protocol and transport problems without letting caller-controlled text into the log through a header name.
Request and result metadata
The HTTP method; the request path and query string, each bounded to 200 characters; scheme; HTTP version; status code; response byte count; latency in milliseconds; a success/failure flag; the error class name and a bounded error message; the tool name; result-shape counts; a bounded result-metadata summary; a cache-hit flag; and your MCP client's reported name, version, protocol version, and capabilities.
Why: Measure health, latency, error rates, and rate-limit rejections on an endpoint with no other observability.
Server-generated record stamps
Every stored line also carries a UTC timestamp, the OpenNIH version that served the request, a per-process boot id that groups lines by container generation, and a flag marking the line as a request record or a tool-call record. These are generated by the server, not taken from you.
Why: Order events, attribute a problem to a specific deploy, and make server restarts visible.
Children
OpenNIH is a research-data tool intended for a general professional audience. It is not directed at children, it has no registration, and it asks no user of any age for their name or age. It does record the pseudonymous and client-supplied values listed above — a salted IP hash, user agent, origin, MCP session id, and whatever text is passed in a query — for every caller alike, and the correction form has an optional contact field that stores whatever address is typed into it.