MCP server for nonprofit financials via ProPublica: IRS Form 990 data for 1.8M+ nonprofits.
One address for any MCP client. No sign-in needed on this server.
3 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Search and explore 1.8M+ US nonprofits, fetch Form 990 financials, and access IRS filing history via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://nonprofit-explorer.caseyjhand.com/mcp
IRS Form 990 data for 1.8M+ tax-exempt organizations, via the ProPublica Nonprofit Explorer API. Search by name, fetch an org's financial snapshot, and pull its full filing history.
| Tool | Description |
|---|---|
nonprofit_search_organizations | Search 1.8M+ IRS-recognized tax-exempt organizations by name, keyword, city, or phrase with optional state, NTEE sector, and 501(c) filters |
nonprofit_get_organization | Full profile for one org by EIN: legal identity, IRS classification, ruling date, and a financial snapshot from the most recent Form 990 |
nonprofit_get_filings | All Form 990 filings for an org by EIN: year-by-year financials, revenue breakdown, executive compensation, and source PDF links |
nonprofit_search_organizations tool"Red Cross"), required terms (+evanston), excluded terms (-dental)ZZ for foreign entities; a code outside that set is rejected rather than silently returning national results), NTEE major sector (1–10), 501(c) subsection code (3 covers both public charities and private foundations — see foundation_type on nonprofit_get_organization to tell them apart)page, zero-indexed); num_pages, per_page, and page_offset track positiontotal_results === 10000 means the actual count may be higher, and a page whose offset reaches that ceiling is refused (pagination_ceiling)organizations array and a notice explaining which case it isnonprofit_get_organization or nonprofit_get_filings for detailsnonprofit_get_organization tool530196605) or string with or without hyphen ("53-0196605"); use nonprofit_search_organizations first if you only have an org namefiling_count counts filings with extracted data; filing_count_pdf_only separately counts PDFs without financial extracts. Use nonprofit_get_filings for those links; PDF-only histories have no financial snapshottax_prd_yr in the snapshot is the fiscal year of the filing, not the current yearnonprofit_get_filings toolgrants_paid, distributable_amount, fair_market_value_assets, and net_investment_income. The distributable amount is a payout requirement, not actual payments or a compliance measureprogram_expense_ratio is always null — ProPublica returns no Form 990 Part IX program-service expense total, so the program/management/fundraising split is only in the source PDFfilings_pdf_only lists older filings with a PDF but no extracted datafilings array plus a notice, not an errortax_prd_yr (fiscal year) when presenting figuresBuilt 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.
ProPublica Nonprofit Explorer / IRS-specific:
pdf_url is still nullAgent-friendly output:
data_source attribution on all tool outputs, ProPublica URL for direct verificationtax_prd_yr prominently labeled as fiscal year with explicit lag caveat in every financial responsestructuredContent keeps an explicit null rather than omitting the keyfield_name and form_type exposed so agents know exactly which IRS field was readA public instance is available at https://nonprofit-explorer.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP, with this client config:
{
"mcpServers": {
"nonprofit-explorer-mcp-server": {
"type": "streamable-http",
"url": "https://nonprofit-explorer.caseyjhand.com/mcp"
}
}
}
Add the following to your MCP client configuration file.
{
"mcpServers": {
"nonprofit-explorer-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/nonprofit-explorer-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"nonprofit-explorer-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/nonprofit-explorer-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"nonprofit-explorer-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/nonprofit-explorer-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/nonprofit-explorer-mcp-server.git
cd nonprofit-explorer-mcp-server
bun install
cp .env.example .env
# edit .env as needed (no required vars — server works out of the box)
All configuration is validated at startup. Key environment variables:
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_HOST | HTTP server hostname | 127.0.0.1 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path | /mcp |
MCP_PUBLIC_URL | Public origin override for TLS-terminating reverse-proxy deployments | — |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.) | info |
LOGS_DIR | Directory for log files (Node.js only) | <project-root>/logs |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1 | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation | false |
No server-specific required variables. ProPublica Nonprofit Explorer is a keyless public API.
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t nonprofit-explorer-mcp-server .
docker run --rm -p 3010:3010 nonprofit-explorer-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/nonprofit-explorer-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and inits the service. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Three tools for search and financial data. |
src/services/nonprofit-explorer | ProPublica Nonprofit Explorer API client and domain types. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagecreateApp() in src/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.