{"id":50993838,"url":"https://github.com/cyanheads/faostat-mcp-server","last_synced_at":"2026-06-20T06:32:35.252Z","repository":{"id":365058836,"uuid":"1268109166","full_name":"cyanheads/faostat-mcp-server","owner":"cyanheads","description":"Global food \u0026 agriculture statistics from the UN FAOSTAT bulk-download corpus, served from a local SQLite mirror with a DataCanvas SQL surface, over MCP. STDIO \u0026 Streamable HTTP.","archived":false,"fork":false,"pushed_at":"2026-06-15T16:54:55.000Z","size":471,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T18:23:35.628Z","etag":null,"topics":["agriculture","ai-agents","bun","cyanheads","duckdb","fao","faostat","food","food-security","mcp","mcp-server","model-context-protocol","open-data","sqlite","statistics","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@cyanheads/faostat-mcp-server","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cyanheads.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-13T06:38:15.000Z","updated_at":"2026-06-15T16:54:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cyanheads/faostat-mcp-server","commit_stats":null,"previous_names":["cyanheads/faostat-mcp-server"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/cyanheads/faostat-mcp-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Ffaostat-mcp-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Ffaostat-mcp-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Ffaostat-mcp-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Ffaostat-mcp-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyanheads","download_url":"https://codeload.github.com/cyanheads/faostat-mcp-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyanheads%2Ffaostat-mcp-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34560265,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-20T02:00:06.407Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agriculture","ai-agents","bun","cyanheads","duckdb","fao","faostat","food","food-security","mcp","mcp-server","model-context-protocol","open-data","sqlite","statistics","typescript"],"created_at":"2026-06-20T06:32:34.602Z","updated_at":"2026-06-20T06:32:35.222Z","avatar_url":"https://github.com/cyanheads.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003e@cyanheads/faostat-mcp-server\u003c/h1\u003e\n  \u003cp\u003e\u003cb\u003eGlobal food \u0026 agriculture statistics from the UN FAOSTAT bulk-download corpus, served from a local SQLite mirror with a DataCanvas SQL surface, over MCP. STDIO \u0026 Streamable HTTP.\u003c/b\u003e\n  \u003cdiv\u003e6 Tools • 0 Resources • 0 Prompts\u003c/div\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Version](https://img.shields.io/badge/Version-0.1.4-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/faostat-mcp-server?style=flat-square\u0026logo=npm\u0026logoColor=white)](https://www.npmjs.com/package/@cyanheads/faostat-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.11-blueviolet.svg?style=flat-square)](https://bun.sh/)\n\n[![Install in Claude Desktop](https://img.shields.io/badge/Install_in-Claude_Desktop-D97757?style=for-the-badge\u0026logo=anthropic\u0026logoColor=white)](https://github.com/cyanheads/faostat-mcp-server/releases/latest/download/faostat-mcp-server.mcpb) [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=faostat-mcp-server\u0026config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvZmFvc3RhdC1tY3Atc2VydmVyIl19) [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=for-the-badge\u0026logo=visualstudiocode\u0026logoColor=white)](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22faostat-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads%2Ffaostat-mcp-server%22%5D%7D)\n\n[![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-67E8F9?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n**Public Hosted Server:** [https://faostat.caseyjhand.com/mcp](https://faostat.caseyjhand.com/mcp)\n\n\u003c/div\u003e\n\n---\n\n## Overview\n\n[FAOSTAT](https://www.fao.org/faostat/) is the UN Food and Agriculture Organization's authoritative global statistics service — crop and livestock production, agricultural trade, food balances, food security and nutrition, land use, fertilizer use, and agrifood-systems emissions for 245+ countries and territories from 1961 to the present. Each domain is a data cube of **area** (country/region) × **item** (commodity) × **element** (metric) × **year**, with a data-quality flag on every observation.\n\nThis server does not call the FAOSTAT REST query API — that endpoint is auth-gated (`HTTP 401` keyless). Instead it syncs FAOSTAT's keyless **bulk-download service** (per-domain ZIPs of normalized CSVs plus their dimension code lists) into a persistent local **SQLite mirror** (embedded, with FTS5 over the dimension labels) and serves every query from that mirror — fast, offline-capable, and free of per-request rate limits. Analytical query results spill to a **DataCanvas** (DuckDB-backed) so an agent can run SQL `GROUP BY`, rankings, joins, and time-series analysis over the full result set.\n\n\u003e [!IMPORTANT]\n\u003e **First run requires a mirror build.** The corpus is not bundled. Run `bun run mirror:init` once to download and index the selected FAOSTAT domains before querying. The read tools return `index_not_ready` until the first sync completes. See [Building the mirror](#building-the-mirror).\n\n## Tools\n\nSix tools organized around the mirror's discover → resolve → query flow, with a DataCanvas pair for SQL over large result sets:\n\n| Tool | Description |\n|:---|:---|\n| `faostat_list_domains` | Discover 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. |\n| `faostat_resolve_codes` | Resolve human terms to the opaque integer codes a query needs (areas, items, elements), flagging each area as a country or an aggregate region. |\n| `faostat_query_observations` | Query 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. |\n| `faostat_commodity_profile` | Workflow: assemble top producers, the production trend, and trade flows for one commodity from the production and trade domains in a single call. |\n| `faostat_dataframe_query` | Run a read-only SQL `SELECT` against the canvas tables staged by the analytical tools. |\n| `faostat_dataframe_describe` | List the canvas tables staged this session, each with provenance, row count, and column schema. |\n\n### `faostat_list_domains`\n\nDiscover the catalog and what's queryable right now.\n\n- Full FAOSTAT catalog read live from the bulk manifest, annotated with local mirror status\n- Per-domain `indexed` / `index_ready` flags, local row count, and last completed sync\n- `topic` substring filter over code, name, and topic (e.g. `\"trade\"`, `\"emissions\"`, `\"QCL\"`)\n- `indexed_only` to list just the domains queryable from the local mirror\n\n---\n\n### `faostat_resolve_codes`\n\nTurn names into the integer codes the cube requires — FAOSTAT is unqueryable without code resolution.\n\n- FTS5 full-text matching (`query`, e.g. `\"maize\"` → item 56), substring filter (`name_contains`), or exact-code lookup (`code`)\n- Resolves within a `dimension`: `area` (countries/regions), `item` (commodities), or `element` (metrics like production, yield, import quantity)\n- Every area match is flagged `country` or `aggregate` (World, continents, economic groupings; codes ≥ 5000) so an agent can avoid summing a region with its member countries\n- Surfaces the CPC crosswalk code for items where available\n- Omit all of `query` / `name_contains` / `code` to list the whole dimension\n\n---\n\n### `faostat_query_observations`\n\nThe core data tool — query a domain's cube and get observations with their data-quality flag.\n\n- Filter by `area_codes`, `item_codes`, `element_codes` (resolve them first), and a `year_start` / `year_end` range\n- **Aggregate regions are excluded by default** (`include_aggregates: false`) so a naive `SUM` does not double-count a region with its members — set `include_aggregates: true` for World/continent roll-ups, or pass explicit `area_codes` to query exactly what you name\n- Small result sets return inline; large ones spill to a DataCanvas table (returned `canvas_id` + `table_name`) for SQL aggregation\n- Every row carries its flag (`A`=Official, `E`=Estimated, `I`=Imputed, `B`=break, `X`=external) — honor it; never treat estimated/imputed values as official\n\n---\n\n### `faostat_commodity_profile`\n\nA workflow tool that assembles a global profile for one commodity in a single call.\n\n- Accepts a commodity name, resolves it to item codes, then queries the production (`QCL`) and trade (`TCL`) domains and merges the results\n- Returns top-producing countries, top exporters, and top importers (ranked by the latest year present), plus a production-trend point count — countries only, aggregates excluded\n- Returns a **partial profile** with a notice naming the gap when a required domain (e.g. trade) is not indexed, rather than failing\n- The full merged production + trade observation set spills to a DataCanvas table for deeper SQL\n\n---\n\n### `faostat_dataframe_query` / `faostat_dataframe_describe`\n\nSQL analytics over the canvas tables (`faostat_xxxxxxxx`) that `faostat_query_observations` and `faostat_commodity_profile` stage. Call `faostat_dataframe_describe` first to discover table and column names, then `faostat_dataframe_query` for cross-country and cross-item aggregation, `GROUP BY` rankings, joins, window functions, and CTEs — standard DuckDB SQL.\n\n- **Read-only.** Writes, DDL, `DROP`, `COPY`, `PRAGMA`, `ATTACH`, and external-file table functions are rejected by the framework SQL gate. System catalogs (`information_schema`, `sqlite_master`, `duckdb_*`) are denied so a caller can't enumerate staged tables it doesn't hold a handle for — list them via `faostat_dataframe_describe`.\n- Staged-table columns: `area_code`, `area`, `item_code`, `item`, `element_code`, `element`, `year`, `unit`, `value`, `flag`. Keep `flag` in projections and honor it in interpretation.\n- `canvas_id` is optional on both tools — omit it to operate on the tables staged in the current session (the common case).\n\nAll tool output is also rendered as human-readable markdown (`content[]`) alongside the structured payload, so tool-only MCP clients reach the same data.\n\n## Features\n\nBuilt on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):\n\n- Declarative tool definitions — single file per tool, framework handles registration and validation\n- Unified error handling — handlers throw, the framework catches, classifies, and formats; typed error contracts give agents actionable recovery hints\n- Pluggable auth: `none`, `jwt`, `oauth`\n- Structured logging with optional OpenTelemetry tracing\n- STDIO and Streamable HTTP transports\n\nFAOSTAT-specific:\n\n- **Persistent local SQLite mirror** of the FAOSTAT bulk corpus via the framework `MirrorService` — one indexed table per selected domain plus shared dimension tables, with FTS5 over the dimension labels driving code resolution\n- **Streaming bulk-ZIP ingester** — fetches the manifest, compares each domain's update date against the stored checkpoint to skip unchanged domains, and stream-parses the normalized CSV (∼18× decompression ratio) into SQLite without materializing the full file in memory\n- **Config-driven domain selection** (`FAOSTAT_DOMAINS`) — the indexed set can grow without code changes; `faostat_list_domains` always shows the full catalog and which domains are locally queryable\n- **DataCanvas SQL surface** (DuckDB) — analytical cube queries spill to a staged table for ad-hoc `GROUP BY` / ranking / time-series analysis\n\nAgent-friendly output:\n\n- **Country-vs-aggregate classification** on every area, plus aggregate exclusion by default — guards against the double-counting hazard of summing World/continent rows with their member countries\n- **Data-quality flags carried through** on every observation (`A`/`E`/`I`/`B`/`X`) — never dropped, so downstream rigor can honor official-vs-estimated distinctions\n- **Graceful partial results** — `faostat_commodity_profile` returns a production-only profile with a notice when trade is not indexed, rather than failing the request\n- **Typed error contracts** — `index_not_ready`, `domain_not_indexed`, `empty_result`, `canvas_disabled`, and `no_match` each carry a concrete recovery hint (run the init script, pick an indexed domain, widen the query, enable the canvas)\n\n## Getting started\n\n### Public Hosted Instance\n\nA public instance is available at `https://faostat.caseyjhand.com/mcp` — no installation required. Point any MCP client at it via Streamable HTTP:\n\n```json\n{\n  \"mcpServers\": {\n    \"faostat-mcp-server\": {\n      \"type\": \"streamable-http\",\n      \"url\": \"https://faostat.caseyjhand.com/mcp\"\n    }\n  }\n}\n```\n\n### Self-Hosted / Local\n\nAdd the following to your MCP client configuration file. The server runs entirely on a local mirror, so [build the mirror](#building-the-mirror) once before querying.\n\n```json\n{\n  \"mcpServers\": {\n    \"faostat-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"bunx\",\n      \"args\": [\"@cyanheads/faostat-mcp-server@latest\"],\n      \"env\": {\n        \"MCP_TRANSPORT_TYPE\": \"stdio\",\n        \"MCP_LOG_LEVEL\": \"info\"\n      }\n    }\n  }\n}\n```\n\nOr with npx (no Bun required):\n\n```json\n{\n  \"mcpServers\": {\n    \"faostat-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@cyanheads/faostat-mcp-server@latest\"],\n      \"env\": {\n        \"MCP_TRANSPORT_TYPE\": \"stdio\",\n        \"MCP_LOG_LEVEL\": \"info\"\n      }\n    }\n  }\n}\n```\n\nOr with Docker:\n\n```json\n{\n  \"mcpServers\": {\n    \"faostat-mcp-server\": {\n      \"type\": \"stdio\",\n      \"command\": \"docker\",\n      \"args\": [\n        \"run\", \"-i\", \"--rm\",\n        \"-e\", \"MCP_TRANSPORT_TYPE=stdio\",\n        \"-v\", \"faostat-mirror:/usr/src/app/.faostat-mirror\",\n        \"ghcr.io/cyanheads/faostat-mcp-server:latest\"\n      ]\n    }\n  }\n}\n```\n\nFor Streamable HTTP, set the transport and start the server:\n\n```sh\nMCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http\n# Server listens at http://localhost:3010/mcp\n```\n\nNo API key is required — the FAOSTAT bulk-download service is public and keyless.\n\n### Prerequisites\n\n- [Bun v1.3.0](https://bun.sh/) or higher (or Node.js v24+).\n- Disk for the local mirror. The default domain set (`QCL,TCL,FBS,FS,RL,GLE,RFN,QV`, ∼37M rows) needs a few GB; `TCL` (∼17M rows) dominates and can be dropped from `FAOSTAT_DOMAINS` on a constrained host.\n\n### Installation\n\n1. **Clone the repository:**\n\n```sh\ngit clone https://github.com/cyanheads/faostat-mcp-server.git\n```\n\n2. **Navigate into the directory:**\n\n```sh\ncd faostat-mcp-server\n```\n\n3. **Install dependencies:**\n\n```sh\nbun install\n```\n\n4. **Configure environment:**\n\n```sh\ncp .env.example .env\n# edit .env to override the default domain set, mirror path, or refresh cron\n```\n\n### Building the mirror\n\nThe corpus is not bundled. Before the data tools can answer queries, sync the selected domains into the local mirror:\n\n```sh\nbun run mirror:init      # one-time bootstrap — downloads and indexes the FAOSTAT_DOMAINS set\nbun run mirror:refresh   # re-sync domains whose upstream update date has advanced\nbun run mirror:verify    # report sync status, local row counts, and sample reads\n```\n\n`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.\n\n## Configuration\n\n| Variable | Description | Default |\n|:---------|:------------|:--------|\n| `FAOSTAT_DOMAINS` | Comma-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` |\n| `FAOSTAT_MIRROR_PATH` | Directory holding the per-domain SQLite stores and the shared dimension database. Created if absent. | `./.faostat-mirror` |\n| `FAOSTAT_BULK_BASE_URL` | FAOSTAT bulk-download service base URL (manifest + per-domain ZIPs). | `https://bulks-faostat.fao.org/production` |\n| `FAOSTAT_REFRESH_CRON` | Cron for the in-process incremental refresh (HTTP transport only). Omit to disable and run `mirror:refresh` out-of-band. | — |\n| `CANVAS_PROVIDER_TYPE` | DataCanvas 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` |\n| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |\n| `MCP_HTTP_PORT` | Port for the HTTP server. | `3010` |\n| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |\n| `MCP_LOG_LEVEL` | Log level (RFC 5424). | `info` |\n| `LOGS_DIR` | Directory for log files (Node.js only). | `\u003cproject-root\u003e/logs` |\n\nSee [`.env.example`](./.env.example) for the full list of optional overrides.\n\n## Running the server\n\n### Local development\n\n- **Build and run:**\n\n  ```sh\n  # One-time build\n  bun run rebuild\n\n  # Run the built server\n  bun run start:stdio\n  # or\n  bun run start:http\n  ```\n\n- **Run checks and tests:**\n\n  ```sh\n  bun run devcheck   # Lint, format, typecheck, security\n  bun run test       # Vitest test suite\n  bun run lint:mcp   # Validate MCP definitions against spec\n  ```\n\n### Docker\n\n```sh\ndocker build -t faostat-mcp-server .\ndocker run --rm -p 3010:3010 -v faostat-mirror:/usr/src/app/.faostat-mirror faostat-mcp-server\n```\n\nThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/faostat-mcp-server`. The build stage compiles the native dependencies (`@duckdb/node-api`, `better-sqlite3`) and the production stage reuses the prebuilt `node_modules`, so the slim runtime image carries no build toolchain. 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:\n\n```sh\ndocker exec \u003ccontainer\u003e bun run mirror:init      # one-time bootstrap\ndocker exec \u003ccontainer\u003e bun run mirror:verify    # sync status + sample reads\ndocker exec \u003ccontainer\u003e bun run mirror:refresh   # re-sync when FAO has updated a domain\n```\n\n## Project structure\n\n| Directory | Purpose |\n|:----------|:--------|\n| `src/index.ts` | `createApp()` entry point — registers the six tools, wires the mirror and canvas in `setup()`, schedules the HTTP refresh. |\n| `src/config` | Server-specific environment variable parsing and validation with Zod. |\n| `src/mcp-server/tools/definitions` | Tool definitions (`*.tool.ts`). |\n| `src/services/faostat-mirror` | The bulk-download mirror service — manifest discovery, streaming ZIP ingester, CSV parsing, dimension store, SQLite-backed `MirrorService` wiring. |\n| `src/services/canvas-accessor.ts`, `canvas-staging.ts` | DataCanvas accessor and the spill/query/describe staging layer. |\n| `scripts/faostat-mirror-*.ts` | `mirror:init` / `mirror:refresh` / `mirror:verify` CLIs. |\n| `tests/` | Unit and integration tests mirroring `src/`. |\n\n## Development guide\n\nSee [`CLAUDE.md`/`AGENTS.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:\n\n- Handlers throw, framework catches — no `try/catch` in tool logic\n- Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage\n- Register new tools in the `createApp()` array in `src/index.ts`\n- Wrap external data: validate raw → normalize to domain type → return output schema; never fabricate missing fields, and carry the data-quality flag through\n\n## Data attribution\n\nData is sourced from [FAOSTAT](https://www.fao.org/faostat/), the statistics division of the Food and Agriculture Organization of the United Nations (FAO). FAOSTAT data is published under [CC BY-4.0](https://creativecommons.org/licenses/by/4.0/); cite FAO as the source in downstream use. This project is not affiliated with or endorsed by the FAO.\n\n## Contributing\n\nIssues and pull requests are welcome. Run checks and tests before submitting:\n\n```sh\nbun run devcheck\nbun run test\n```\n\n## License\n\nApache-2.0 — see [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyanheads%2Ffaostat-mcp-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyanheads%2Ffaostat-mcp-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyanheads%2Ffaostat-mcp-server/lists"}