Skip to main content
Glama

teadata-mcp

teadata-mcp is a Model Context Protocol server and ChatGPT-ready web app for exploring Texas public school data. It wraps the teadata data engine in a transport and UI layer that works well for ChatGPT apps, internal assistants, and browser-based workflows.

The project is built around a simple idea: make Texas education data usable in conversational interfaces without sacrificing provenance, structure, or operator control.

What the Repository Provides

  • An MCP server with streamable HTTP, WebSocket, and legacy SSE transports

  • A React frontend for ChatGPT-style exploration and inline tool workflows

  • Tools for campus search, district detail, geospatial lookup, comparisons, and transfer insights

  • Widget assets for map and boundary visualization inside supported clients

  • SSO-aware deployment options for pairing a public assistant with a private website workflow

Related MCP server: uk-schools-mcp

Core Use Cases

  • Build a Texas school-data assistant for ChatGPT or another MCP-capable client

  • Embed school search and boundary workflows in a browser-based assistant UI

  • Support internal research or public-interest analysis with structured TEA data access

  • Pair teadata-app and teadata-mcp so the public website and assistant share the same data model

Highlights

  • Rich campus and district retrieval backed by teadata

  • Fuzzy search across district names, campus names, and identifiers

  • Geospatial tools for nearby campuses and boundary-driven analysis

  • Side-by-side comparison flows for campuses and districts

  • Transfer and mobility insights suitable for charts and narrative summaries

  • Response shaping controls that keep large payloads usable in conversational clients

  • Widget rendering for boundary and explorer workflows in compatible MCP surfaces

Architecture

At a high level, the repository is split into three layers:

  1. teadata data access and domain logic

  2. MCP/server transport and assistant middleware

  3. React frontend and widget assets for browser and ChatGPT experiences

Project Layout

.
├── src/teadata_mcp/
│   ├── logic.py                 # Domain logic built on teadata
│   ├── router.py                # Tool definitions and request routing
│   ├── server.py                # MCP server wiring
│   ├── sse_server.py            # Starlette app / HTTP entrypoint
│   ├── assistant_auth.py        # Shared-token assistant auth helpers
│   ├── assistant_middleware.py  # Browser/API auth behavior
│   ├── tooling_guide.py         # Prompt-to-tool guidance
│   └── widget_assets/           # Inline HTML widget assets
├── frontend/                    # React application for the browser UI
├── tests/                       # Router, logic, auth, and transport tests
├── run_dev.sh                   # Local development launcher
└── Dockerfile                   # Container deployment

Local Development

Fastest Path

./run_dev.sh

This script:

  • syncs Python dependencies with uv

  • installs/builds frontend assets

  • starts the application server

By default the service exposes:

  • http://localhost:<port>/mcp for streamable HTTP MCP

  • ws://localhost:<port>/ws for WebSocket MCP

  • http://localhost:<port>/sse for legacy SSE MCP

  • http://localhost:<port>/ for the browser UI

Manual Backend Setup

uv sync
uv run uvicorn teadata_mcp.sse_server:app --reload --port 8000

Manual Frontend Setup

cd frontend
npm install --legacy-peer-deps
npm run dev

Testing

Run the Python test suite with:

uv run pytest

If you are modifying the frontend or widget behavior, also build the frontend to confirm the static bundle still compiles cleanly.

Deployment

The repository is structured to deploy cleanly to container-friendly platforms such as Render.

Important environment variables:

  • TEADATA_SNAPSHOT: path to a local snapshot

  • TEADATA_SNAPSHOT_URL: remote snapshot URL when the bundled artifact is unavailable

  • TEADATA_MAX_RESPONSE_BYTES: soft response-size cap for list-heavy results

  • PORT: server port supplied by the platform

In production, build the frontend before starting the Python service so the static_dist/ assets are available for browser traffic.

Pairing With teadata-app

teadata-mcp can run as a companion assistant service for teadata-app.

Recommended pattern:

  • keep a website-assistant deployment behind shared-signing-key SSO

  • keep a separate public MCP deployment for ChatGPT or other external clients

This split lets you protect browser access for website users without forcing the same auth model onto non-browser MCP clients.

teadata-app Environment

Set these on the Django application:

  • TEADATA_ASSISTANT_ENABLED=1

  • TEADATA_ASSISTANT_URL

  • TEADATA_ASSISTANT_SSO_SECRET

  • TEADATA_ASSISTANT_COOKIE_DOMAIN

  • TEADATA_ASSISTANT_COOKIE_NAME

  • TEADATA_ASSISTANT_SSO_TTL_SECONDS

teadata-mcp Environment

Set these on the assistant service:

  • TEADATA_ASSISTANT_SSO_SECRET

  • TEADATA_ASSISTANT_COOKIE_NAME

  • TEADATA_ASSISTANT_LAUNCH_URL

  • TEADATA_ASSISTANT_ENFORCE_SSO

  • TEADATA_ASSISTANT_SSO_SKEW_SECONDS

  • TEADATA_DEBUG or DEBUG

Auth Behavior

  • browser UI requests redirect unauthenticated users to the configured launch URL

  • API and MCP transport endpoints return 401 when SSO enforcement is enabled

  • non-browser clients may supply a bearer token instead of a cookie

Working With Large Responses

Texas school data gets large quickly, especially for map and boundary workflows. This repo includes several controls to keep responses useful inside assistant clients:

  • response_profile to choose map-only, list-only, or combined payloads

  • campus_meta_fields / meta_fields to request only the metrics you need

  • campus_list_format to keep list payloads compact

  • pagination plus next_tool_call guidance for follow-up requests

  • export resources for CSV/JSON retrieval when full result sets are too large

If you are designing prompts or integrating a client, use the tooling guide and follow-up calls rather than falling back to web search.

Files Worth Knowing

  • SAMPLE_QUERIES.md: example requests and assistant flows

  • PLAN.md: implementation roadmap / planning notes

  • TODO.md: active task inventory

  • src/teadata_mcp/tooling_guide.py: prompt-routing guidance

Data and Privacy Notes

  • The underlying data is public Texas education data.

  • The service is designed to expose structured public information, not user-specific private records.

  • If you enable SSO or bearer-token protection, secrets should live in environment variables or platform secret stores, never in committed config.

  • teadata: core Texas education data toolkit

  • teadata-app: companion Django website and SSO launcher

License

Apache License 2.0. See LICENSE.

Available Tools

16 tools
compare_campusesA
Read-onlyIdempotent

Compare 2+ campuses side-by-side on rating, enrollment, staffing, and demographics. Use for benchmarking or 'compare these campuses' prompts after collecting campus identifiers via search. Optionally include meta_fields for extra comparison dimensions. Responses include payload.table and payload.exports for deterministic tables and CSV/JSON export.

Inputs: identifiers (required), meta_fields.

find_campuses_in_district_boundaryA
Read-onlyIdempotent

Spatial containment query using teadata boundary methods; returns GeoJSON (district polygon + campus points) with overall_rating_2025 in properties. Use for 'within Austin ISD boundaries' or 'show on a map' prompts; apply status or campus_query filters (e.g., IDEA, KIPP). Default boundary_delivery is 'reference' to avoid huge payloads; this returns a Census TIGERweb download_url so ChatGPT can fetch the boundary directly. response_profile controls payload size: 'map' returns GeoJSON only, 'list' returns campus list only, 'both' returns both. campus_list_format controls list compactness (id/id_name/full). Responses paginate via cursor/next_cursor to avoid truncation; set include_total=true to return total_matches. Responses include payload.table and payload.exports for deterministic tables and CSV/JSON export when lists are returned; if payload.completeness.needs_follow_up or pagination.has_more is true, follow next_tool_call before finalizing. Use campus_meta_fields to include specific meta keys without dumping full meta. Example: "Find campuses within Austin ISD boundaries".

Inputs: district_identifier (required), limit, cursor, status, +9 more.

find_charter_campuses_within_districtA
Read-onlyIdempotent

Shortcut for charter-only campuses within a district boundary; returns GeoJSON for interactive maps. Example: district_identifier 'Austin ISD' with campus_query 'IDEA'. Default boundary_delivery is 'reference' to avoid huge payloads; this returns a Census TIGERweb download_url so ChatGPT can fetch the boundary directly. response_profile controls payload size ('map', 'list', 'both'). campus_list_format controls how list outputs are compacted. Responses paginate via cursor/next_cursor. When lists are returned, payload.table and payload.exports provide deterministic tables and CSV/JSON export; if payload.completeness.needs_follow_up is true, follow next_tool_call before finalizing.

Inputs: district_identifier (required), limit, cursor, campus_query, +8 more.

get_campus_aggregatesA
Read-onlyIdempotent

Compute aggregate statistics (total enrollment, rating distribution, etc.) for campuses matching specific filters. Use this for questions like "Total enrollment of all charter schools" or "How many A-rated campuses are there?". Supports the same filters as search_campuses (query, status, rating, grade_level).

Inputs: query, rating, status, grade_level.

get_campus_detailA
Read-onlyIdempotent

Return a campus profile with rich data: Staffing, Class Sizes, Demographics, Transfers Out, Location, and Ratings. DO NOT search the web for these metrics; they are provided locally. Use get_data_fields to discover other available metrics (e.g., test scores) if needed. Example: "Show details for campus 227901001".

Inputs: identifier (required), meta_fields.

get_data_fieldsA
Read-onlyIdempotent

Inspect available data fields (metrics) for a campus or district. Use this tool to discover what specific data points are available in the local database before searching the web. It returns a list of keys (e.g., 'campus_2025_sat_average') that can be requested via meta_fields in other tools.

Inputs: identifier (required), entity_type (required).

get_districtA
Read-onlyIdempotent

Look up a Texas school district by TEA number or name (wildcards like 'ALDINE*' or 'AUSTIN%' are supported). Use this to anchor district-level questions or to supply identifiers for detail, boundary, or campus list queries. Optionally request specific meta_fields for additional metrics. DO NOT search the web for basic district info; this tool provides it locally. Example: "Find Austin ISD" or "Get district 227901".

Inputs: identifier (required), meta_fields.

get_district_detailA
Read-onlyIdempotent

Return district summary plus its campuses (useful for 'show all campuses in Houston ISD' style prompts). Includes district-wide stats. DO NOT search the web for this info; it is provided locally. Results paginate with cursor/next_cursor and can include total_matches when include_total=true. You can request meta_fields for the district or campus_meta_fields for each campus. Responses include payload.table and payload.exports for deterministic tables and CSV/JSON export; if payload.completeness.needs_follow_up or pagination.has_more is true, follow next_tool_call before finalizing. Example: "List campuses in Austin ISD".

Inputs: identifier (required), limit, cursor, meta_fields, +2 more.

get_entity_geometryA
Read-onlyIdempotent

Fetch campus or district geometry/location data from the local teadata snapshot, including geometry_fields to show which attributes are available. Use this for map/boundary questions or to confirm geometry before drawing. Example: "What geometry fields does Austin ISD expose?"

Inputs: identifier (required), entity_type (required).

get_nearby_campusesA
Read-onlyIdempotent

Find campuses within a radius (miles) of a campus or coordinates; returns distance_miles plus charter/private flags so you can filter. Example: coords (-95.36, 29.83) with radius 10 to find nearby charters; filter results by charter/is_private in the response. Results paginate with cursor/next_cursor and can include total_matches when include_total=true. Responses include payload.table and payload.exports for deterministic tables and CSV/JSON export; if payload.completeness.needs_follow_up or pagination.has_more is true, follow next_tool_call before finalizing.

Inputs: limit, cursor, latitude, longitude, +3 more.

get_staffing_dashboardA
Read-onlyIdempotent

Return campus-level staffing metrics for dashboard analysis, including teacher experience, turnover rate, student-teacher ratio, enrollment, ratings, and coordinates for mapping. Use this to compare staffing differences between charter and traditional campuses without web search.

Inputs: no named parameters.

get_tooling_guideA
Read-onlyIdempotent

Return a prompt-to-tool guide with recommended tool calls for common intents. Use this when the user asks for a map, boundary, comparison, or spatial query and you want the canonical tool choice.

Inputs: topic.

get_transfer_insightsA
Read-onlyIdempotent

Analyze outbound student transfer flows across campuses. Returns Sankey nodes/links, map-ready flow lines, charter vs traditional shares, rating shifts, and distance patterns. Optionally scope to a district or campus query. Use this for transfer dynamics, school choice patterns, or charter share questions.

Inputs: top_sources, campus_query, top_destinations, min_transfer_count, +2 more.

map_campuses_within_districtA
Read-onlyIdempotent

Map-focused alias for within-boundary queries. Use this when the user asks to show campuses on an interactive map; supports status filters (charter/isd/private). Returns campus points plus a boundary_reference download_url. response_profile defaults to 'map' for compact GeoJSON-only responses. campus_list_format controls how list outputs are compacted. Responses paginate via cursor/next_cursor. When lists are returned, payload.table and payload.exports provide deterministic tables and CSV/JSON export; if payload.completeness.needs_follow_up is true, follow next_tool_call before finalizing. Example: "Show campuses within Austin ISD on a map".

Inputs: district_identifier (required), limit, cursor, status, +9 more.

map_charter_campuses_within_districtA
Read-onlyIdempotent

Map-focused alias for charter-only campuses within a district boundary. Use this for prompts like "Find all charter school campuses located within Austin ISD's boundaries and show them on an interactive map". response_profile defaults to 'map' for compact GeoJSON-only responses. campus_list_format controls how list outputs are compacted. Responses paginate via cursor/next_cursor. When lists are returned, payload.table and payload.exports provide deterministic tables and CSV/JSON export; if payload.completeness.needs_follow_up is true, follow next_tool_call before finalizing.

Inputs: district_identifier (required), limit, cursor, campus_query, +8 more.

search_campusesA
Read-onlyIdempotent

Search campuses by name/number/district with filters for status (all/charter/isd/private), rating (A-F, NR), and grade_level (Elementary/Middle/High). Examples: query 'IDEA', 'KIPP', or 'Austin ISD' to fetch identifiers before detail/compare/map calls. This tool returns basic info; use get_campus_detail for full stats. Results paginate with cursor/next_cursor; set include_total=true to receive total_matches. You can request meta_fields to pull specific metrics without returning full meta blobs. Responses include payload.table and payload.exports for deterministic tables and CSV/JSON export; if payload.completeness.needs_follow_up or pagination.has_more is true, follow next_tool_call before finalizing.

Inputs: limit, query, cursor, rating, +4 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 16 tools

    Disambiguation3/5

    Several tools overlap significantly, particularly the four boundary/map tools (find_campuses_in_district_boundary, find_charter_campuses_within_district, map_campuses_within_district, map_charter_campuses_within_district) which are essentially variations of the same query. Descriptions help clarify differences, but the redundancy can still confuse an agent selecting a tool.

    Naming Consistency4/5

    Naming generally follows a consistent verb_noun pattern (get_, search_, find_, map_, compare_). Minor inconsistencies exist, such as 'in_district_boundary' vs 'within_district' and the mix of 'get_' with nouns like 'staffing_dashboard' and 'entity_geometry', but the overall pattern is clear and readable.

    Tool Count4/5

    At 16 tools, the count is slightly above the ideal range. Most tools cover distinct data views, but the boundary/map aliases could be consolidated into fewer parameterized tools, which would reduce redundancy without losing functionality.

    Completeness4/5

    The server covers a wide range of read-only TEA data operations: search, detail, aggregation, comparison, spatial queries, staffing, transfers, and geometry. Minor gaps exist, such as lack of a direct district comparison tool, but core domain needs are well addressed.

    Maintenance

    ActivityMaintained
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      A Model Context Protocol server that provides AI assistants with plain-English access to official Dallas-area public data (weather alerts, school ratings, and 311 service requests) without requiring API keys or logins.
      4
      1
      Apache 2.0