Faostat Mcp Server

io.github.cyanheads由 GitHub 账号 cyanheads 发布v0.6.0更新于 Nov 4, 2026

通过MCP,在本地SQLite镜像上使用联合国FAOSTAT全球粮食与农业统计数据。

AI 翻译由 AI 模型根据下方的原文描述翻译,可能有误。

原文描述

UN FAOSTAT global food & agriculture statistics over a local SQLite mirror, via MCP.

已验证Streamable HTTP可网页运行数据库数据与分析

概览

Faostat 的托管 MCP 服务(faostat.caseyjhand.com);无需登录;Streamable HTTP;6 个工具。

暂无 AI 概览:以下信息来自注册中心条目和服务本身。

端点
faostat.caseyjhand.com
最近检查

安装

在 SourceWeft 中

  1. 打开 控制台中的 Faostat Mcp Server,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/io.github.cyanheads%2Ffaostat-mcp-server/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "faostat": {
      "type": "http",
      "url": "https://faostat.caseyjhand.com/mcp"
    }
  }
}

README

@cyanheads/faostat-mcp-server

Global food & agriculture statistics from the UN FAOSTAT bulk-download corpus, served from a local SQLite mirror with a DataCanvas SQL surface, via MCP. STDIO or Streamable HTTP.

7 Tools • 1 Skill Resource • 0 Prompts

Public Hosted Server: https://faostat.caseyjhand.com/mcp


Overview

Global food and agriculture statistics from the UN's FAOSTAT: production, trade, food balances, food security, land use, fertilizer, and emissions for 245+ countries and territories since 1961. Resolve commodity and country codes, query the data, and run SQL over large results.

Tools

ToolDescription
faostat_list_domainsDiscover FAOSTAT statistical domains with codes, descriptions, last-update date, upstream row count, and local index status. The entry point — every query keys on a domain code.
faostat_resolve_codesResolve human terms to the codes a query needs (areas, items, elements), flagging each area as a country or an aggregate region.
faostat_query_observationsQuery a domain's cube by area(s), item(s), element(s), and year range. Inline preview for small results; large sets spill to a DataCanvas table.
faostat_commodity_profileWorkflow: assemble top producers, the production trend, and trade flows for one commodity from the production and trade domains in a single call.
faostat_dataframe_queryRun a read-only SQL SELECT against the canvas tables staged by the analytical tools.
faostat_dataframe_describeList the canvas tables staged this session, each with provenance, row count, and column schema.
faostat_dataframe_dropDrop one staged canvas table before its 2-hour TTL. Opt-in — callable only with FAOSTAT_DATAFRAME_DROP_ENABLED=true.

Capability reference

faostat_list_domains tool

  • Full FAOSTAT catalog (~69 domains) read live from the bulk manifest, annotated with local mirror status
  • code for an exact domain lookup; topic substring filter over code/name/topic; indexed_only to list only domains queryable from the local mirror
  • offset + limit (max 200, default 20) page the catalog — response reports totalMatches, truncated, and nextOffset
  • Each entry reports indexed / index_ready flags, local row count, and last completed sync

faostat_resolve_codes tool

  • FTS5 full-text query, substring name_contains, or exact code lookup within a dimension: area, item, or element; code_text looks up alphanumeric items
  • Item/element matches are scoped to codes present in the given domain's cube; area codes are shared across domains
  • Element matches include observed units; item_codes or item_codes_text restrict element discovery and units to selected items. Item/element lookups require that domain to be ready
  • Every area match is flagged country or aggregate (codes ≥ 5000, plus curated sub-threshold roll-ups such as China=351)
  • Verified item groups are marked aggregate by domain. Classification is incomplete: kind: null does not mean items can safely be summed together
  • limit (max 200, default 50) + offset page the match set
  • Typed errors: unknown_domain, index_not_ready, invalid_text_selector, invalid_item_scope, query_timeout

faostat_query_observations tool

  • Filters by area_codes / item_codes / item_codes_text / element_codes / source_codes and an inclusive year_start / year_end range
  • Alphanumeric items retain their exact item_code_text with item_code: null; numeric items retain their integer code
  • Period observations retain period (e.g. 2000-2002) and use its midpoint as year for filtering. Censored values retain the numeric bound plus value_qualifier (e.g. < with 2.5)
  • Source-specific rows carry source_code and source; omitting source_codes returns all sources, which must be compared separately rather than summed
  • An empty query with explicit item and element selections returns available_elements with labels and units for those items; this does not establish coverage for the requested areas, years, or sources
  • Aggregate regions excluded by default (include_aggregates: false); explicit area_codes bypass the exclusion
  • limit caps the inline page (default 200, max 1000); a match that exceeds it spills in full to a DataCanvas table (50,000-row staging cap) — faostat_dataframe_describe for its columns, then SQL via faostat_dataframe_query
  • Every row carries its data-quality flag (A/E/I/B/M/T/X, others per domain) — never dropped
  • Typed errors: domain_not_indexed, index_not_ready, canvas_disabled, canvas_not_found, invalid_year_range, conflicting_item_selectors, query_timeout

faostat_commodity_profile tool

  • Selects one QCL item with item_code or item_query (code takes precedence), then ranks top producers/exporters/importers and returns an annual production trend
  • Query selection prefers a unique exact label after trimming, collapsing whitespace, and case folding; otherwise requires a unique relevance match. For ambiguous terms such as maize or milk, call faostat_resolve_codes with domain: "QCL", dimension: "item", and the query, then pass the selected item_code (e.g. 56 for maize grain)
  • Rankings are per-country sums grouped by unit, each country at its own latest reporting year; countries only (aggregates excluded)
  • Returns a partial, production-only profile with a notice — rather than failing — when the trade domain (TCL) isn't indexed or still syncing
  • top_n caps each ranked list (max 50); when the merged observation set is too large to inline it spills to a DataCanvas table — faostat_dataframe_describe for its columns, then faostat_dataframe_query for further SQL
  • Every staging outcome is stated in the notice: the staged table, a set that fit inline, a staging failure, or a disabled canvas
  • Typed errors: missing_item, ambiguous_item, no_match, domain_not_indexed, index_not_ready, invalid_year_range, canvas_not_found, query_timeout

faostat_dataframe_query tool

  • Single-statement read-only SELECT over staged faostat_xxxxxxxx tables — joins, aggregates, window functions, and CTEs all work
  • Writes, DDL, DROP, COPY, PRAGMA, ATTACH, external-file functions, and system catalogs (information_schema, sqlite_master, duckdb_*) are rejected
  • Engine-setting introspection, query_table, dynamic query() SQL, and file table references are rejected; query() with a single literal read-only SELECT remains supported
  • row_limit caps the response (default 1000, max 10000); truncated means more rows exist, with no exact total computed on this path
  • Typed errors: canvas_disabled, canvas_not_found, missing_table, system_catalog_access, invalid_sql

faostat_dataframe_describe tool

  • Lists staged tables with source tool, query params, row count, column schema, and recorded creation/expiry timestamps
  • name describes one table outright; otherwise offset + limit (max 100, default 20) page the listing newest-first
  • Typed errors: canvas_disabled, canvas_not_found, missing_table

faostat_dataframe_drop tool

  • Opt-in: registered disabled — listed on the landing page with its enable hint, absent from tools/list — unless FAOSTAT_DATAFRAME_DROP_ENABLED=true
  • Drops one staged faostat_xxxxxxxx table and its provenance from the session canvas, or from the canvas a canvas_id names
  • Idempotent: a name not staged on that canvas returns dropped: false with a notice and changes nothing
  • Typed errors: canvas_disabled, canvas_not_found

Features

The faostat-guide covers reporting periods, censored values, source estimates, country versus regional totals, units, coverage, and complete SQL analysis across staged results. It ships with plugin, npm, Docker, and MCPB installs and is served through the MCP Skills extension as a skill resource.

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

FAOSTAT-specific:

  • Persistent local SQLite mirror of the FAOSTAT bulk corpus via the framework MirrorService, with FTS5 over the dimension labels driving code resolution
  • Streaming bulk-ZIP ingester — skips domains whose upstream update date hasn't advanced, and stream-parses the normalized CSV into SQLite without materializing the full file in memory
  • Config-driven domain selection (FAOSTAT_DOMAINS) — the indexed set can grow without code changes, and the full catalog stays browsable regardless
  • DataCanvas SQL surface (DuckDB) for GROUP BY, ranking, and time-series analysis over spilled result sets

Agent-friendly output:

  • Country-vs-aggregate classification on every area, with aggregates excluded from sums by default — guards against double-counting World/continent rows with their member countries
  • Data-quality provenance — every observation carries its FAOSTAT flag (A/E/I/B/M/T/X, others per domain), never dropped from output
  • Graceful partial results — faostat_commodity_profile returns a production-only profile with a notice, rather than failing, when the trade domain isn't indexed
  • Typed error contracts — index_not_ready, domain_not_indexed, canvas_disabled, and others each carry a concrete recovery hint

Getting started

Connect to the hosted instance, no install:

sh
claude mcp add --transport http faostat-mcp-server https://faostat.caseyjhand.com/mcp

Or run it locally after building the mirror:

sh
claude mcp add faostat-mcp-server -- npx -y @cyanheads/faostat-mcp-server

For other clients, add this to the MCP config file:

json
{  "mcpServers": {    "faostat-mcp-server": {      "command": "npx",      "args": ["-y", "@cyanheads/faostat-mcp-server"]    }  }}

The bulk-download service is public and keyless. For Docker or a source build, see Running the server.

Configuration

VariableDescriptionDefault
FAOSTAT_DOMAINSComma-separated FAOSTAT domain codes to index into the local mirror. Domains outside this set appear in faostat_list_domains but are not queryable until added and re-synced.QCL,TCL,FBS,FS,RL,GLE,RFN,QV
FAOSTAT_MIRROR_PATHDirectory holding the per-domain SQLite stores and the shared dimension database. Created if absent../.faostat-mirror
FAOSTAT_BULK_BASE_URLFAOSTAT bulk-download service base URL (manifest + per-domain ZIPs).https://bulks-faostat.fao.org/production
FAOSTAT_REFRESH_CRONCron for the in-process incremental refresh (HTTP transport only). Omit to disable and run mirror:refresh out-of-band.—
CANVAS_PROVIDER_TYPEDataCanvas engine. duckdb enables the SQL surface; set none to disable analytical staging (the dataframe_* tools then report canvas_disabled and large queries refuse to spill).duckdb
FAOSTAT_DATAFRAME_DROP_ENABLEDSet true to make faostat_dataframe_drop callable. Off, the tool is registered disabled: absent from tools/list, shown on the landing page with this hint.false
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto (resolves to stateful). The server declares stateless in src/index.ts — no tool asks the caller for input mid-handler — and setting this overrides that declaration.stateless
MCP_HTTP_PORTPort for the HTTP server.3010
MCP_AUTH_MODEAuth mode: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (RFC 5424).info
LOGS_DIRDirectory for log files (Node.js only).<project-root>/logs

See .env.example for the full list of optional overrides.

Running the server

Local development

Requires Bun v1.4.0 or higher (or Node.js v24+), plus disk for the mirror. The default domain set (QCL,TCL,FBS,FS,RL,GLE,RFN,QV, ∼37M rows) needs a few GB; drop TCL (∼17M rows) from FAOSTAT_DOMAINS on a constrained host.

sh
git clone https://github.com/cyanheads/faostat-mcp-server.gitcd faostat-mcp-server && bun installcp .env.example .env# Override the default domain set, mirror path, or refresh cron in .env.
  • Build and run:

    sh
    # One-time buildbun run rebuild
    # Run the built serverbun run start:stdio# orbun run start:http
  • Run checks and tests:

    sh
    bun run devcheck   # Lint, format, typecheck, securitybun run test       # Vitest suite under Bun, then under Nodebun run lint:mcp   # Validate MCP definitions against spec

Building the mirror

The corpus is not bundled. Before the data tools can answer queries, sync the selected domains into the local mirror:

sh
bun run mirror:init      # one-time bootstrap — downloads and indexes the FAOSTAT_DOMAINS setbun run mirror:refresh   # re-sync domains whose upstream update date has advanced; build missing planner statisticsbun run mirror:verify    # report sync status, local row counts, checkpoints, and dimension population

mirror:init is idempotent and resumable per domain — re-running after an interrupt re-streams only the unfinished domain ZIP. FAOSTAT_DOMAINS selects which domains are indexed; everything else in the catalog shows in faostat_list_domains with indexed: false until added and re-synced. On HTTP transport, set FAOSTAT_REFRESH_CRON to refresh in-process on a schedule; on stdio, run mirror:refresh out-of-band. The read tools return index_not_ready until the first sync completes.

After upgrading an older mirror, run mirror:refresh. FS and GLE rebuild once to recover period/text-item rows and separate source estimates, even when the upstream date is unchanged; they remain unready until that rebuild succeeds. Compatible numeric domains stay readable and gain the new indexes during refresh without a forced download. Until those indexes are prepared, domain-wide element/unit discovery can be slow; scope it with item_codes or item_codes_text when possible.

mirror:refresh also builds query-planner statistics (ANALYZE) for any selected domain that has none, even when its data is unchanged upstream — a mirror synced by an older release, or one whose last build failed. Otherwise the server builds them itself, in the background on a worker thread, the first time a data read touches that domain, and keeps answering meanwhile.

Docker

sh
docker run --rm -p 3010:3010 -v faostat-mirror:/usr/src/app/.faostat-mirror ghcr.io/cyanheads/faostat-mcp-server:latest

Build locally with docker build -t faostat-mcp-server .. For stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "-v", "faostat-mirror:/usr/src/app/.faostat-mirror", "ghcr.io/cyanheads/faostat-mcp-server:latest"]. The image defaults to HTTP transport, stateless session mode, and logs to /var/log/faostat-mcp-server. A dedicated dependency stage installs the production tree for the target platform (DuckDB's prebuilt native binding included), so the slim runtime image carries no build toolchain and a multi-arch build never runs Bun under emulation. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them. Mount a volume at the mirror path to persist the corpus across container recreations, and bootstrap it inside the container:

sh
docker exec <container> bun run mirror:init      # one-time bootstrapdocker exec <container> bun run mirror:verify    # sync status, row counts, checkpoints, and dimension populationdocker exec <container> bun run mirror:refresh   # re-sync updated domains; build missing planner statistics

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers the seven tools (faostat_dataframe_drop behind its opt-in flag), wires the mirror and canvas in setup(), schedules the HTTP refresh.
src/configServer-specific environment variable parsing and validation with Zod.
skills/faostat-guidePublished agent guide, also served as an MCP skill resource.
src/mcp-server/tools/definitionsTool definitions (*.tool.ts).
src/services/faostat-mirrorThe bulk-download mirror service — manifest discovery, streaming ZIP ingester, CSV parsing, dimension store, SQLite-backed MirrorService wiring, and the two-worker read pool (read-pool.ts, read-worker.ts) that runs cube reads and statistics builds off the main thread.
src/services/canvas-accessor.ts, canvas-staging.tsDataCanvas accessor and the spill/query/describe/drop staging layer.
scripts/faostat-mirror-*.tsmirror:init / mirror:refresh / mirror:verify CLIs.
tests/Unit and integration tests mirroring src/.

Development guide

See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools in the createApp() array in src/index.ts
  • Wrap external data: validate raw → normalize to domain type → return output schema; never fabricate missing fields, and carry the data-quality flag through

Data attribution

Data is sourced from FAOSTAT, the statistics division of the Food and Agriculture Organization of the United Nations (FAO). FAOSTAT data is published under CC BY-4.0; cite FAO as the source in downstream use. This project is not affiliated with or endorsed by the FAO.

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheckbun run test

License

Apache-2.0 — see LICENSE for details.

来源:README.md,提交 c5a4fea

工具

6

faostat_list_domains

faostat-mcp-server: list domains

Discover FAOSTAT statistical domains (production, trade, food balances, food security, land use, agri-emissions, prices, value) with their codes, descriptions, last-update date, upstream row count, and local index status. Every query keys on a domain code from here. The `indexed` flag tells you which domains are queryable right now; un-indexed domains exist in the catalog but must be added to FAOSTAT_DOMAINS and re-synced before faostat_query_observations can read them. The catalog runs to ~69 domains with long descriptions, so responses are paged: narrow with `topic` / `indexed_only`, pass `code` to fetch one domain outright, or page with `offset` + `limit` — when the response reports `truncated`, pass the returned `nextOffset` to fetch the rest.

输入参数codelimittopicoffsetindexed_only

faostat_resolve_codes

faostat-mcp-server: resolve codes

Resolve labels to the numeric codes or exact alphanumeric item identities faostat_query_observations needs: areas (countries/regions), items (commodities), and elements (metrics). Use query for full-text matching, name_contains for substring matching, code or code_text for exact identity, or omit them to list. Item and element matches occur in the selected domain; areas use shared vocabulary. For elements, optionally pass item_codes or item_codes_text to discover metrics and distinct units observed for those items; this does not establish area/year/source coverage. Empty item selectors match nothing. Page with offset and limit using nextOffset when truncated. Areas carry country/aggregate classification. Verified domain-specific item groups carry aggregate; null means unclassified, not safe-to-sum. Item groups can overlap their members even with matching units. Area aggregates include codes ≥ 5000 and curated roll-ups such as China=351; exclude overlapping regional and country rows when summing.

输入参数codelimitquerydomain必填offsetcode_textdimension必填item_codesname_containsitem_codes_text

faostat_query_observations

faostat-mcp-server: query observations

Query a FAOSTAT domain's data cube by area(s), item(s), element(s), sources, and year range, returning values with units and data-quality flags. Resolve numeric codes or exact alphanumeric item identities with faostat_resolve_codes; element resolution accepts item selectors and returns units. Empty queries with explicit items and elements list available elements for those items across the domain, without establishing area/year/source coverage. Aggregate regions are excluded by default; include_aggregates=true returns roll-ups, and explicit area_codes are honored verbatim. Item groups can overlap their members even with matching units; they remain included, and kind=null does not establish safe-to-sum. Keep source methodologies separate. Small results return inline; large ones spill to a DataCanvas table (canvas_id + table_name); call faostat_dataframe_describe for columns, then faostat_dataframe_query for analysis. Honor every row's flag: commonly A=Official, B=time-series break, E=Estimated, I=Imputed, M=Missing, T=Unofficial, X=international organization; unrecognized flags never establish official status.

输入参数limitdomain必填year_endcanvas_idarea_codesitem_codesyear_startsource_codeselement_codesitem_codes_textinclude_aggregates

faostat_commodity_profile

faostat-mcp-server: commodity profile

Assemble a global profile for one QCL item in a single call: top-producing countries, the annual production trend, and trade flows (top exporters and importers). Supply item_code or item_query; code wins when both are supplied. A query selects a unique normalized exact label, otherwise a unique relevance match; ambiguous queries require an explicit code. Each ranking uses the selected item at each country's own latest year with data, grouped by unit. The trend is returned inline as year/value points. Country-level only (aggregates excluded). QCL is required; unavailable TCL yields a production-only profile with a notice. When the merged observation set is too large to inline, it spills to a DataCanvas table (canvas_id + table_name) — call faostat_dataframe_describe for its columns, then faostat_dataframe_query for deeper SQL.

输入参数top_nyear_endcanvas_iditem_codeitem_queryyear_start

faostat_dataframe_query

faostat-mcp-server: dataframe query

Run a single-statement SELECT against canvas tables staged by faostat_query_observations and faostat_commodity_profile (faostat_xxxxxxxx). Use DuckDB joins, aggregates, window functions, and CTEs for rankings and time-series analysis. Item groups can overlap their members even with matching units; do not sum them together, and resolver kind=null does not establish safe-to-sum. Separate source methodologies and respect bounded values and multi-year periods. Read-only: writes, DDL, DROP, COPY, PRAGMA, ATTACH, and external-file functions are rejected; system catalogs are denied. List tables and columns with faostat_dataframe_describe. Preserve data-quality flags in projections: commonly A=Official, B=time-series break, E=Estimated, I=Imputed, M=Missing, T=Unofficial, X=international organization; unrecognized flags never establish official status.

输入参数sql必填canvas_idrow_limit

faostat_dataframe_describe

faostat-mcp-server: dataframe describe

List the canvas tables (faostat_xxxxxxxx) staged by faostat_query_observations and faostat_commodity_profile, each with its source tool, the query parameters that produced it, creation/expiry timestamps, row count, and column schema. Call this before faostat_dataframe_query to discover the exact table and column names to reference in SQL. Tables are listed newest-first and paged: pass `name` to describe one table outright, or page with `offset` + `limit` — when the response reports `truncated`, pass the returned `nextOffset` to fetch the rest.

输入参数namelimitoffsetcanvas_id

版本历史

8
  1. v0.6.0最新Nov 4, 2026
  2. v0.5.0Oct 29, 2026
  3. v0.4.1Oct 25, 2026
  4. v0.4.0Oct 18, 2026
  5. v0.3.0Oct 4, 2026
  6. v0.2.5Sep 30, 2026
  7. v0.2.4Sep 19, 2026
  8. v0.2.3Sep 16, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分