Skip to content
Conduit

mcp server

pyodkmcp

Chat with Your ODK Data - MCP Server for ODK Central

joybindroo/pyodkmcpcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 27, 2026

🚀 Python MCP Server for ODK

A Model Context Protocol (MCP) Server for ODK Central, enabling AI agents to interact with your data collection projects, forms, submissions, entities, and app users using the powerful PyODK Python library.

This server acts as the Ingestion Layer, fetching data from ODK Central and storing it in a local SQLite database, which is then queried by a dedicated Database MCP Server (like pyMCP) for high-performance analysis. It also exposes the full PyODK management API plus a raw HTTP bridge to any ODK Central API endpoint.


🛠️ Tools and Dual-Server Workflow

1. ODK MCP Server

Read tools (safe, no changes to Central)

ToolDescription
list_projects()List all projects available to the configured user.
get_project(project_id)Full details of one project.
list_forms(project_id)List forms in a project.
get_form(project_id, form_id)Form metadata.
get_form_xml(project_id, form_id)The XForms XML definition of a form.
list_submissions(project_id, form_id)Submission metadata (no data values).
get_submission(project_id, form_id, instance_id)One submission's metadata.
get_data(project_id, form_id, ...)Fetch submission data (with optional OData filter/top/skip/select/expand/count/wkt), flatten it with pandas, and store it in a local SQLite table.
list_submission_comments(project_id, form_id, instance_id)Comments on a submission.
list_entity_lists(project_id)Entity lists (datasets) in a project, with properties.
get_entity_list(project_id, entity_list_name)Details of one entity list.
list_entities(project_id, entity_list_name)Entity metadata in an entity list.
get_entity_data(project_id, entity_list_name, ...)Fetch entity data (with optional OData params) and store it in a local SQLite table.
list_synced_tables()Tables synced into the local SQLite database, with sync timestamps.

Write tools (modify data in Central)

ToolDescription
create_app_users(display_names, project_id, form_ids)Create app users, optionally assigning forms.
create_form(...) / update_form(...)Create a form from an XLSForm/XML file or raw XML; publish new versions.
create_submission(...) / edit_submission(...)Create a submission from XML (with optional attachments); edit an existing one.
review_submission(..., review_state, comment)Set a submission's review state (approved/hasIssues/rejected).
add_submission_comment(...)Comment on a submission.
create_entity_list(...) / add_entity_list_property(...)Create datasets and their properties.
create_entities(...) / update_entity(...) / delete_entity(...) / merge_entities(...)Create, update, delete, or bulk merge entities.
central_api_request(method, path, ...)Raw bridge: call any Central API endpoint not covered by the tools above (form drafts, assignments, admin, ...).

How syncing works: get_data flattens nested submission JSON into a tabular format and stores it in a SQLite table named data_pid_{project_id}__fid_{form_id} (replacing any previous contents). get_entity_data stores entity data in entity_pid_{project_id}__el_{entity_list_name}. Both log the sync timestamp in the table_log table and return a preview of up to 50 rows.

2. Database MCP Server (The Analyst)

Once get_data (or get_entity_data) has synced the information, the AI agent transitions to the Database MCP Server (pyMCP) to perform natural language SQL queries against the local SQLite file.


🪴 Installation & Setup

1. Prerequisites

2. Installation

# Clone the repository
git clone https://github.com/joybindroo/pyodkmcp.git
cd pyodkmcp

# Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate  # Linux/MacOS
# venv\Scripts\activate   # Windows

# Install dependencies (PyODK 1.3.0+, MCP SDK, pandas)
pip install -r requirements.txt

3. Configuration

The server uses an environment variable to determine where to store the SQLite database.

  • Variable: ODK_MCP_DB_PATH
  • Default: odk_mcp_server.db (local directory)

Credentials are read from pyodk_config.toml in the project root when it exists (copy pyodk_config.toml.template and fill in real values). Otherwise the standard PyODK resolution is used: the PYODK_CONFIG_FILE environment variable, then ~/.pyodk_config.toml. Setting PYODK_CONFIG_FILE always takes precedence over the project-level file.

4. Running the server

The server speaks three MCP transports. stdio is the default and is what most desktop clients expect; sse and streamable-http let clients connect to an already-running server over the network.

# stdio (default) - the MCP client launches the server as a subprocess
python odk_mcp_server.py

# SSE over HTTP - any MCP client supporting the SSE transport can connect
python odk_mcp_server.py --transport sse --host 127.0.0.1 --port 8000

# streamable-http - the newer HTTP transport
python odk_mcp_server.py --transport streamable-http --host 127.0.0.1 --port 8000
FlagDefaultDescription
--transportstdioTransport protocol: stdio, sse, or streamable-http.
--host127.0.0.1Host to bind for sse / streamable-http.
--port8000Port to bind for sse / streamable-http.

With --transport sse, the server exposes http://<host>:<port>/sse (event stream) and http://<host>:<port>/messages/ (client messages). Bind to 0.0.0.0 to accept connections from other machines — the server has write access to ODK Central, so expose it deliberately.


🔌 MCP Client Configuration

stdio (client launches the server)

Example configuration for Claude Desktop or other MCP clients (point command at the project venv Python). The client starts its own server subprocess, so no separate launch step is needed:

{
  "odk_mcp_server": {
    "command": "/path/to/pyodkmcp/venv/bin/python",
    "args": ["/path/to/pyodkmcp/odk_mcp_server.py"],
    "env": {
      "ODK_MCP_DB_PATH": "/path/to/odk_mcp_server.db"
    }
  },
  "odk_mcp_sqlite_data": {
    "command": "python",
    "args": [
      "/path/to/pymcp/db_mcp_server.py",
      "sqlite:// /path/to/odk_mcp_server.db"
    ],
    "env": {
      "PYTHONPATH": "/path/to/pymcp"
    }
  }
}

HTTP / SSE (client connects to a running server)

Start the server first with --transport sse (see Running the server), then point clients at its URL instead of a command:

{
  "odk_mcp_server": {
    "url": "http://127.0.0.1:8000/sse"
  }
}

This is handy when several agents should share one server instance, or when the client cannot spawn subprocesses. If a client only supports the newer HTTP transport, use --transport streamable-http and its /messages/ endpoint instead.


⚠️ Notes

  • Write tools change data in ODK Central. The most destructive operations (delete_entity, merge_entities(delete_not_matched=True), non-GET central_api_request calls) should be used deliberately.
  • Tool failures are surfaced as MCP tool errors (isError), e.g. ODK Client not initialized. when the PyODK configuration is missing, or No submissions found for this form. for empty forms.

🛣️ Roadmap

  • Data Export: Export synced tables to CSV/Excel.
  • Async DB: Migration to aiosqlite for non-blocking I/O.
  • Version Monitoring: Integration with PyXComparer to detect schema changes between syncs.

🤝 Community

If you find this server useful, please consider giving the repository a star!

↑↓ navigate openesc close