Configuration¶
edi-mcp runs with no configuration at all: the defaults serve stdio, cache upstream
responses in memory, keep tool results small and confine downloads to
~/edi-downloads. Settings come from four layers; a higher layer wins:
- Command-line flags (only a handful of common settings).
- Environment variables named
EDI_MCP_<SECTION>__<FIELD>, with a double underscore descending into nested sections at any depth, for exampleEDI_MCP_EDI__RETRIES__MAX_ATTEMPTS=5. List settings take a comma-separated value:EDI_MCP_SERVER__ALLOWED_HOSTS=edi.example.org,alt.example.org. The empty string,noneandnullmean "unset". - A YAML file passed with
--config path.yaml, using the same section and field names. - Built-in defaults (the table below).
Unknown keys in the YAML file or the environment are ignored with a warning, so a typo does not stop the server but is visible in its log. The loader only checks the shape of the settings; it never contacts EDI.
The EDI API key¶
The key is the only secret, and it is read from the environment only. Set
EDI_API_KEY, the name EDI's own guides use. EDI_MCP_EDI__API_KEY, when set, takes
precedence (for giving edi-mcp a different key from your other EDI tools). Look-alike
names (EDI_KEY, EDI_TOKEN, EDI_AUTH_TOKEN) are not read; edi-mcp logs a
warning if one of them is set without EDI_API_KEY. There is deliberately no
command-line flag for the key: arguments are visible to every user in the process
list and end up in shell history. edi-mcp does not load .env files itself; if you
keep the key in one, export it into the environment before starting the server. See
EDI API key for creating a key and checking that EDI accepts it.
The key decides the backend: with one, edi-mcp reads PASTA; without one, it reads the anonymous DataONE replicas (Backends).
Command-line flags¶
| Flag | Sets |
|---|---|
--config PATH |
YAML file to load |
--transport {stdio,http} |
server.transport |
--bind-address, --bind-port |
server.bind_address, server.bind_port |
--log-level |
server.log_level |
--download-dir PATH |
downloads.directory |
--no-downloads |
downloads.enabled = false |
--print-config |
print the effective configuration as YAML (key shown as ***) and exit |
--check |
load the configuration, call edi_ping in-process (no network request), print a JSON report, exit 0 or 1 |
--version |
print the version |
A YAML example¶
# edi-mcp.yaml — pass with: edi-mcp --config edi-mcp.yaml
edi:
max_concurrency: 4
retries:
max_attempts: 3
cache:
ttl_s:
search: 600
limits:
max_result_bytes: 50000
downloads:
directory: /data/edi-downloads
server:
log_level: info
Never put the API key in a YAML file you share or commit; use EDI_API_KEY.
Automatic values¶
A few settings default to "unset" and resolve per transport:
downloads.enabled: downloads exist only on stdio; over HTTP the download tools are never listed, whatever this says.--no-downloadsturns them off on stdio too. (The download tools themselves are still being built; see the Roadmap.)server.dns_rebinding_protection: unset follows the MCP SDK default, which is on for loopback binds or whenallowed_hosts/allowed_originsare set.server.public_base_url: when set, its host and origin join the allow-lists, and anhttps://URL enables per-caller keys in theX-EDI-Keyheader (see Hosted HTTP deployment).
All settings¶
| Setting (YAML path) | Environment variable | Type | Default | Description |
|---|---|---|---|---|
edi.pasta_base_url |
EDI_MCP_EDI__PASTA_BASE_URL |
str | https://pasta.lternet.edu |
PASTA+ base URL (package and audit services). |
edi.auth_base_url |
EDI_MCP_EDI__AUTH_BASE_URL |
str | https://auth.edirepository.org/auth/v1 |
EDI Identity and Access Manager API (key -> edi-token exchange). |
edi.dataone_cn_url |
EDI_MCP_EDI__DATAONE_CN_URL |
str | https://cn.dataone.org/cn/v2 |
DataONE coordinating node (Solr search, resolve) used without a key. |
edi.gmn_lter_url |
EDI_MCP_EDI__GMN_LTER_URL |
str | https://gmn.lternet.edu/mn/v2 |
DataONE member node serving knb-lter-* packages without a key. |
edi.gmn_edi_url |
EDI_MCP_EDI__GMN_EDI_URL |
str | https://gmn.edirepository.org/mn/v2 |
DataONE member node serving edi.* and other non-LTER packages without a key. |
edi.cite_url |
EDI_MCP_EDI__CITE_URL |
str | https://cite.edirepository.org/cite |
EDI citation service (anonymous). |
edi.portal_url |
EDI_MCP_EDI__PORTAL_URL |
str | https://portal.edirepository.org/nis |
EDI Data Portal (landing pages are built from it). |
edi.api_key |
EDI_MCP_EDI__API_KEY(or EDI_API_KEY) |
SecretStr | null | unset | EDI API access key (https://auth.edirepository.org/auth/ui/key). Enables the PASTA backend; without it edi-mcp uses DataONE. Prefer the EDI_API_KEY env var; never commit it. |
edi.user_agent_suffix |
EDI_MCP_EDI__USER_AGENT_SUFFIX |
str | null | unset | Text appended to the User-Agent header. |
edi.connect_timeout_s |
EDI_MCP_EDI__CONNECT_TIMEOUT_S |
float | 10.0 |
TCP/TLS connect timeout. |
edi.read_timeout_s |
EDI_MCP_EDI__READ_TIMEOUT_S |
float | 60.0 |
Default read timeout. |
edi.download_read_timeout_s |
EDI_MCP_EDI__DOWNLOAD_READ_TIMEOUT_S |
float | 300.0 |
Read timeout for data-entity downloads. |
edi.max_concurrency |
EDI_MCP_EDI__MAX_CONCURRENCY |
int | 4 |
Maximum simultaneous upstream requests. |
edi.token_refresh_margin_s |
EDI_MCP_EDI__TOKEN_REFRESH_MARGIN_S |
float | 3600.0 |
Refresh the 8-hour edi-token once fewer than this many seconds remain. |
edi.auth_fallback_s |
EDI_MCP_EDI__AUTH_FALLBACK_S |
float | 300.0 |
After the EDI auth service fails, send ?key= for this long before retrying it. |
edi.max_response_bytes |
EDI_MCP_EDI__MAX_RESPONSE_BYTES |
int | 20971520 |
Largest upstream response body read into memory. |
edi.retries.max_attempts |
EDI_MCP_EDI__RETRIES__MAX_ATTEMPTS |
int | 3 |
Attempts per upstream call, including the first. |
edi.retries.backoff_base_s |
EDI_MCP_EDI__RETRIES__BACKOFF_BASE_S |
float | 0.5 |
Base of the exponential backoff (0.5 * 2^n seconds). |
edi.retries.backoff_max_s |
EDI_MCP_EDI__RETRIES__BACKOFF_MAX_S |
float | 8.0 |
Cap on a single backoff sleep. |
edi.retries.retry_after_default_s |
EDI_MCP_EDI__RETRIES__RETRY_AFTER_DEFAULT_S |
float | 1.0 |
Wait used on HTTP 429 when no Retry-After header is sent. |
cache.enabled |
EDI_MCP_CACHE__ENABLED |
bool | true |
Cache upstream responses in memory. |
cache.max_entries |
EDI_MCP_CACHE__MAX_ENTRIES |
int | 1024 |
Overall entry cap across families. |
cache.stale_if_error_s |
EDI_MCP_CACHE__STALE_IF_ERROR_S |
int | 86400 |
How long an expired entry may be served when a refresh fails. |
cache.ttl_s.listings |
EDI_MCP_CACHE__TTL_S__LISTINGS |
int | 3600 |
Scope, identifier and revision listings. |
cache.ttl_s.search |
EDI_MCP_CACHE__TTL_S__SEARCH |
int | 600 |
Search results. |
cache.ttl_s.metadata |
EDI_MCP_CACHE__TTL_S__METADATA |
int | 86400 |
EML and entity facts of a given revision (immutable upstream). |
cache.ttl_s.reports |
EDI_MCP_CACHE__TTL_S__REPORTS |
int | 86400 |
Quality reports and provenance. |
cache.ttl_s.feeds |
EDI_MCP_CACHE__TTL_S__FEEDS |
int | 300 |
Change and upload feeds, read counts. |
cache.ttl_s.citations |
EDI_MCP_CACHE__TTL_S__CITATIONS |
int | 86400 |
Formatted citations. |
limits.default_limit |
EDI_MCP_LIMITS__DEFAULT_LIMIT |
int | 25 |
Default page size where a tool does not set its own. |
limits.max_limit |
EDI_MCP_LIMITS__MAX_LIMIT |
int | 200 |
Largest page size any tool accepts. |
limits.max_result_bytes |
EDI_MCP_LIMITS__MAX_RESULT_BYTES |
int | 50000 |
Tool results are trimmed (with page.truncated) to fit this many bytes of compact JSON. |
limits.hard_max_result_bytes |
EDI_MCP_LIMITS__HARD_MAX_RESULT_BYTES |
int | 200000 |
A result still larger than this after trimming fails with result_too_large. |
limits.text_budget_summary |
EDI_MCP_LIMITS__TEXT_BUDGET_SUMMARY |
int | 300 |
Characters kept of long text fields (abstracts) in summaries. |
limits.text_budget_detail |
EDI_MCP_LIMITS__TEXT_BUDGET_DETAIL |
int | 4000 |
Default characters kept of long text fields in detail views. |
limits.max_eml_bytes |
EDI_MCP_LIMITS__MAX_EML_BYTES |
int | 20971520 |
Largest EML document parsed (larger ones fail with result_too_large). |
limits.preview_max_bytes |
EDI_MCP_LIMITS__PREVIEW_MAX_BYTES |
int | 262144 |
Most bytes edi_preview_entity reads from an entity. |
limits.tools_list_max_bytes |
EDI_MCP_LIMITS__TOOLS_LIST_MAX_BYTES |
int | 60000 |
Conformance bound on the serialized tools/list result; tightened once every tool lands. |
downloads.enabled |
EDI_MCP_DOWNLOADS__ENABLED |
bool | null | unset | Offer the download tools. Unset: true on stdio; never available over http. |
downloads.directory |
EDI_MCP_DOWNLOADS__DIRECTORY |
Path | ~/edi-downloads |
Directory every download is confined to (created on first use). |
downloads.max_files_per_call |
EDI_MCP_DOWNLOADS__MAX_FILES_PER_CALL |
int | 50 |
Most entities one edi_download_entities call transfers. |
downloads.max_bytes_per_call |
EDI_MCP_DOWNLOADS__MAX_BYTES_PER_CALL |
int | 2147483648 |
Most bytes one download call transfers. |
downloads.max_file_bytes |
EDI_MCP_DOWNLOADS__MAX_FILE_BYTES |
int | 1073741824 |
Largest single file accepted. |
downloads.verify_checksums |
EDI_MCP_DOWNLOADS__VERIFY_CHECKSUMS |
bool | true |
Verify the SHA-1 checksums EDI publishes. |
downloads.allowed_hosts |
EDI_MCP_DOWNLOADS__ALLOWED_HOSTS |
list[str] | [pasta.lternet.edu, gmn.lternet.edu, gmn.edirepository.org, cn.dataone.org] |
Hosts downloads (and their redirects) may come from; '*.' is a subdomain wildcard. |
server.transport |
EDI_MCP_SERVER__TRANSPORT |
stdio | http |
stdio |
stdio (local clients) or http (stateless Streamable HTTP at /mcp). |
server.bind_address |
EDI_MCP_SERVER__BIND_ADDRESS |
str | 127.0.0.1 |
HTTP bind address. |
server.bind_port |
EDI_MCP_SERVER__BIND_PORT |
int | 8080 |
HTTP bind port. |
server.public_base_url |
EDI_MCP_SERVER__PUBLIC_BASE_URL |
str | null | unset | Public URL of a hosted deployment; its host/origin join the allow-lists and https:// enables per-request keys. |
server.allowed_hosts |
EDI_MCP_SERVER__ALLOWED_HOSTS |
list[str] | [] |
Host header allow-list (DNS-rebinding protection). |
server.allowed_origins |
EDI_MCP_SERVER__ALLOWED_ORIGINS |
list[str] | [] |
Origin header allow-list (DNS-rebinding protection). |
server.dns_rebinding_protection |
EDI_MCP_SERVER__DNS_REBINDING_PROTECTION |
bool | null | unset | Force DNS-rebinding protection on/off. Unset: SDK default (on for loopback binds or when allow-lists are set). |
server.json_response |
EDI_MCP_SERVER__JSON_RESPONSE |
bool | false |
Answer POST /mcp with application/json instead of a single SSE frame. |
server.max_request_body_size |
EDI_MCP_SERVER__MAX_REQUEST_BODY_SIZE |
int | 1048576 |
Largest accepted request body in bytes. |
server.accept_header_key |
EDI_MCP_SERVER__ACCEPT_HEADER_KEY |
bool | true |
HTTP: honour a per-request EDI API key header (subject to the TLS gate). |
server.request_key_header |
EDI_MCP_SERVER__REQUEST_KEY_HEADER |
str | X-EDI-Key |
Header carrying a caller's EDI API key in HTTP mode. |
server.allow_insecure_header_key |
EDI_MCP_SERVER__ALLOW_INSECURE_HEADER_KEY |
bool | false |
Accept header keys when public_base_url is not https:// (development only). |
server.share_config_key_over_http |
EDI_MCP_SERVER__SHARE_CONFIG_KEY_OVER_HTTP |
bool | false |
Lend the operator's configured key to HTTP callers without one (private deployments only). |
server.tools_list_ttl_ms |
EDI_MCP_SERVER__TOOLS_LIST_TTL_MS |
int | 300000 |
ttlMs advertised on tools/list. |
server.log_level |
EDI_MCP_SERVER__LOG_LEVEL |
debug | info | warning | error | critical |
info |
Log verbosity. |
server.instructions_extra |
EDI_MCP_SERVER__INSTRUCTIONS_EXTRA |
str | null | unset | Text appended to the server instructions. |