ew-mcp
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ew-mcpWhich counties have the lowest high school graduation rates?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
education-to-workforce-mcp
An MCP server over the Education-to-Workforce (E-W) Indicator Framework — 20 essential questions and 99 indicators covering how people progress from early education through the workforce, for the US, states, counties, and school districts.
The framework is Mathematica's, written with the Bill & Melinda Gates Foundation. The data tool that compiles federal data against it is the Urban Institute's. This server ships a compiled, pinned snapshot of that data — 11.8M observations in ~24 MB of Parquet — so it answers locally and makes no network calls.
No API key required. Read-only.
Prerequisites
Python 3.11+
Related MCP server: FRED MCP Server
Installation
git clone https://github.com/UrbanInstitute/education-to-workforce-mcp.git
cd education-to-workforce-mcp
uv syncTools
Tool | Purpose |
| Browse essential questions, or find an indicator/metric by concept |
| Explain one metric, indicator, essential question, or disaggregate — or the framework itself |
| Find the state/county/district geoid used by the data, from a name |
| Fetch metric values for places, or rank places on a metric |
Typical workflow: search → describe → resolve_place → get_data.
Tool reference
search
Browse the framework, or find a metric by concept.
Parameter | Type | Description |
| string (optional) | A concept — |
describe
Explain one metric, indicator, essential question, disaggregate, or the framework overview.
Parameter | Type | Description |
| string |
|
resolve_place
Find the geoid for a state, county, or school district, from a name.
Parameter | Type | Description |
| string | Place name — |
| string (optional) |
|
get_data
Fetch metric values for places, or rank places on a metric.
Parameter | Type | Description |
| string | Comma-separated ids: |
| string |
|
| string (optional) | Comma-separated geoids from |
| string (optional) | 2-digit state FIPS — every place at |
| string (optional) | Comma-separated years: |
| string (optional) | A dimension ( |
| string (optional) |
|
| integer (default: 10) | How many places to return when ranking |
Running it
MCP Inspector (interactive testing)
uv run mcp dev src/ew_mcp/server.pyOpens a browser at http://localhost:6274 — connect, open Tools, and run
any tool with parameters.
Claude Desktop / Claude Code / VS Code / Copilot CLI
{
"mcpServers": {
"ew-mcp": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/education-to-workforce-mcp", "ew-mcp"]
}
}
}Client | Where it goes |
Claude Desktop |
|
Claude Code |
|
VS Code (Copilot) |
|
stdio (direct)
uv run ew-mcpStreamable HTTP (hosted)
MCP_TRANSPORT=streamable-http PORT=8080 uv run ew-mcpMCP is served at POST /mcp; GET /health is a plain unauthenticated health
check for a load balancer or orchestrator.
Variable | Default | Purpose |
|
|
|
|
| Listen port |
|
| Bind address |
| (unset) | Comma-separated |
| (unset) | Comma-separated |
Set MCP_ALLOWED_HOSTS to the public hostname before exposing this beyond a
private network. Both are unset by default, which leaves the SDK's
DNS-rebinding protection off — setting either turns it on. Note that enabling it
with an allow-list that omits the real hostname rejects every request.
Hosted
Also available as a hosted streamable-HTTP server: https://educationdata.urban.org/mcp/ew
Tests
uv run pytest -qTests run against the compiled data in src/ew_mcp/data/ — no network. If the
store is absent they skip rather than fail.
Available Tools
4 toolsdescribeDescribe a metric or indicatorARead-onlyIdempotent
Explain one Education-to-Workforce metric, indicator, or essential question, with the years and geographies it actually covers and a link to the framework's own page for it.
Coverage is derived from the data rather than upstream metadata, which disagrees with reality for 22 metrics.
LINK PEOPLE TO THE SOURCE: every metric and indicator described here carries the framework's own page for it at educationtoworkforce.org. Pass that link on when someone wants the definition, the evidence or the measurement guidance rather than a number — it beats paraphrasing, and it is where the framework lives.
Args: target: "m47" (metric), "i5" (indicator), "eq12" (essential question); a bare number is read as a metric id. Also "framework" for the overview and citation, or "disaggregates" for the queryable breakdown dimensions.
| Name | Required | Description | Default |
|---|---|---|---|
| target | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safe-read profile (readOnly, idempotent, non-destructive, closed-world), and the description adds genuinely non-obvious behavior: coverage is derived from the data, which disagrees with upstream metadata for 22 metrics. That caveat is real value beyond the annotations, but error handling and response shape for bad targets are unaddressed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded purpose sentence, then the LINK PEOPLE TO THE SOURCE block, then arg documentation — well ordered and mostly tight. Minor padding in "it beats paraphrasing, and it is where the framework lives" and the all-caps emphasis costs a little economy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, this is complete: purpose, the coverage-accuracy caveat that affects trust in the answer, full target syntax, and guidance on relaying the source link. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage and no enums, the description carries the full burden and does: it documents the mNN / iN / eqN prefixes, the bare-number default (metric), and the two special targets "framework" and "disaggregates". An agent can construct a valid target without guessing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ("Explain") and a precise resource ("one Education-to-Workforce metric, indicator, or essential question") plus the returned scope (years, geographies, framework link). It implicitly separates itself from get_data by framing the answer as definition/evidence "rather than a number", though no sibling is named outright.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear use condition — pass the framework link along when someone wants the definition, evidence or measurement guidance rather than a number — which implicitly routes numeric requests to get_data. No explicit exclusions or prerequisites (e.g., unknown target behavior), so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_dataGet metric valuesARead-onlyIdempotent
Fetch Education-to-Workforce metric values for places, or rank places on
a metric. Choose places one of three ways: geo_ids for specific ones,
state for every place at that level in a state, or rank for the extremes.
State and national benchmarks are added automatically for a handful of places; demographic context is added for a whole state.
Coverage is uneven: districts carry ~35 metrics, counties ~62, and many places have no data for a given metric. "No data here" is a real finding — report it rather than substituting a different geography.
Every result carries its own reading notes and caveats. Use them: values are rendered for you, some metrics are signed representation gaps rather than rates, and there are NO margins of error, sample sizes or denominators anywhere in this dataset, so never call a difference significant.
Args: metric_ids: Comma-separated ids — "47" or "47,51". geo_level: "national", "state", "county", or "district". geo_ids: Comma-separated geoids from resolve_place. state: 2-digit state FIPS — every place at geo_level in that state. years: Comma-separated years — "2022" or "2013,2022". Omit for all. disaggregate: A dimension ("race", "gender", "disability", "ell", "income") for every group, or one group code ("d1_hispanic"). Ranking requires a single group code. rank: "highest" or "lowest" to rank places instead of listing values. limit: How many places to return when ranking.
| Name | Required | Description | Default |
|---|---|---|---|
| rank | No | ||
| limit | No | ||
| state | No | ||
| years | No | ||
| geo_ids | No | ||
| geo_level | Yes | ||
| metric_ids | Yes | ||
| disaggregate | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only/idempotent annotations, the description discloses substantial behavior: benchmarks added automatically, demographic context for states, uneven coverage by geography, and the critical caveat that there are NO margins of error, sample sizes or denominators so differences must never be called significant. This is exactly the kind of context annotations cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and selection options are front-loaded, and every section (benchmarks, coverage caveats, args) earns its place. It is somewhat long, but the length is justified by the 0% schema coverage and the genuine nuance in the dataset.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description need not explain return values, and it fills the remaining gaps: parameter meanings, auto-added benchmarks, coverage unevenness, and the statistical-misuse warning. Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the full burden and does so thoroughly: it explains each of the 8 parameters, gives comma-separated syntax examples for metric_ids/years/geo_ids, clarifies state as 2-digit FIPS, and specifies that disaggregate accepts a dimension or a single group code.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('Fetch Education-to-Workforce metric values for places, or rank places on a metric') and covers both listing and ranking modes. It also distinguishes itself from sibling resolve_place by explicitly directing geoid sourcing there.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly lays out three mutually exclusive ways to select places (geo_ids, state, rank) and adds constraints like 'Ranking requires a single group code' and 'Omit for all' on years. It routes to resolve_place for geoids, though it doesn't explicitly state when to prefer this tool over siblings like search.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_placeResolve a place name to a geoidARead-onlyIdempotent
Find the state, county, or school-district geoid used by get_data.
Returns ranked candidates and does NOT auto-pick: ~30 states have a "Washington County", and a silently wrong pick is indistinguishable from a right one once it reaches a number. Name the place and geoid you chose in your answer. When candidates span geo levels (a county and a district of the same name), ASK — they are different grains with different coverage, so the wrong pick changes what is answerable, not just the number.
For every place in a state, skip this and use get_data(state="12").
Args: name: Place name — "Cook County", "Illinois", "Chicago Public Schools". District names are official NCES names. geo_level: "state", "county", or "district".
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| geo_level | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered; the description adds genuinely new behavior — it returns ranked candidates, deliberately does NOT auto-pick, and explains the ambiguity risk (duplicate county names, wrong-grain picks). It stops short of describing candidate payload shape, but the output schema exists for that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then the no-auto-pick behavior, then the routing escape hatch, then args. Every sentence earns its place, though the Washington County illustration is slightly verbose and the mid-paragraph 'ASK' instruction interrupts the flow.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape needn't be explained, and the description still tells the agent how to use results (name the chosen place and geoid in the answer). For a two-parameter read-only resolver, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the load, and it does: it defines 'name' with example formats ('Cook County', 'Chicago Public Schools') and the NCES-naming convention, and enumerates the three legal 'geo_level' values. It adds real meaning beyond the bare schema, though it does not state what happens when geo_level is omitted.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+outcome: 'Find the state, county, or school-district geoid used by get_data.' It also implicitly distinguishes itself from the get_data sibling by identifying itself as the name-to-geoid resolution step that feeds it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing rules: skip this tool entirely for statewide queries and use get_data(state="12") instead, and ASK the user when candidates span geo levels. Both the when-to-use and when-not-to-use cases are named with concrete conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchSearch the frameworkARead-onlyIdempotent
Browse the Education-to-Workforce Framework, or find its metrics by concept. Searches metric and indicator names plus the framework's own definitions, so conceptual terms match even when a metric is worded differently. Matches per-word and ignores word order, so "neighborhood poverty" and "poverty in the neighborhood" hit the same records — try a short multi-word phrase before assuming something isn't covered.
NOT COVERED anywhere in this dataset: individual schools, individual colleges, and local labour market data. It stops at school district. Say so rather than answering from a different grain.
Args: query: A concept — "gifted", "student debt", "chronic absence". Omit to list the 20 essential questions, which is the best starting point.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/closed-world, so the bar is lower, yet the description adds genuinely non-obvious behavior: per-word matching that ignores word order, definition-level matching so differently-worded metrics still hit, and an explicit coverage boundary (no individual schools, colleges, or local labour market data; stops at district). This beyond-annotation context is exactly what prevents silent misuse.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and matching behavior, then coverage limits, then the argument semantics. Every sentence either defines capability, prevents a misuse, or documents the parameter; nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need no explanation, and the description covers purpose, matching semantics, coverage boundaries, defaults, and the sole parameter's meaning. An agent has everything needed to select and call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and there is a single parameter, so the description must carry it — and it does, defining query as 'a concept' with concrete examples ('gifted', 'student debt', 'chronic absence') and stating that omitting it returns the 20 essential questions. This is far more than the bare schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource — browsing the Education-to-Workforce Framework and finding metrics by concept — and even explains the matching mechanism (metric/indicator names plus framework definitions). It does not explicitly contrast itself with siblings like describe or get_data, but the purpose is unambiguous on its own.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit guidance on how to invoke ('try a short multi-word phrase before assuming something isn't covered'), a recommended default ('omit query to list the 20 essential questions, which is the best starting point'), and a when-not-to-answer rule ('NOT COVERED... say so rather than answering from a different grain'). That is when/when-not/alternatives all in one place.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
describe - First observed
get_data - First observed
resolve_place - First observed
search
TDQS
Scored across 4 tools
The four tools form a clear pipeline: search finds metrics by concept, describe explains a specific metric/indicator/essential question, resolve_place turns a name into a geoid, and get_data fetches values. There is mild overlap between search and describe (both surface framework content), but the args and outputs make the boundary clear in practice.
Two tools use bare verbs (search, describe) while two use a verb_noun pattern (resolve_place, get_data). Individually readable and unambiguous, but the conventions are mixed rather than a single predictable pattern.
Four tools is a tight, well-scoped read-only set for a data framework: discover, explain, geocode, fetch. Nothing feels redundant, though it sits near the thin end where one more operation (e.g. an explicit metric enumeration) might round it out.
The surface covers the full read lifecycle for the domain — find a metric, understand it, resolve a place, retrieve or rank values, including automatic benchmarks and disaggregation options. Being a read-only data server, missing write ops are not gaps; the main absence is a direct way to enumerate all available metrics/geographies without a query.
Maintenance
Related MCP Connectors
Query 4,300+ ranked metros across sixteen dimensions: profiles, search, and head-to-head comparisons
Query 29,500+ World Bank development indicators for 200+ countries across 60+ years.
Search, sample and query open reproducible datasets published as immutable Parquet with schemas.
341Curated gateway to snapshot-versioned Canadian public data services with source provenance.
Related MCP Servers
- AlicenseCqualityDmaintenanceProvides access to Urban Institute's Education Data API through Claude, enabling users to query and analyze detailed educational data from schools, districts, and universities.22MIT
- FlicenseNot gradedqualityDmaintenanceProvides access to over 800,000 economic time series from the Federal Reserve Bank of St. Louis, including data on GDP, inflation, and employment. It enables users to search for, retrieve, and analyze various economic indicators and state-level statistics.1-
- AlicenseAqualityCmaintenanceProvides curated US economic data from Treasury, FRED, BLS, BEA, and other sources through an MCP interface. Enables querying economic series, fetching data with provenance tracking, and accessing cached artifacts.9MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying Sweden's municipal and regional key-performance-indicator (KPI) database, providing access to a wide range of public sector metrics via natural language.1MIT