Skip to main content
Glama
mustafa-ahmadov

opendata-az-mcp

Opendata.az MCP Server

License: MIT

Model Context Protocol (MCP) server that allows AI chatbots (Claude, ChatGPT, Cursor, etc.) to search, explore, and get download links for datasets from opendata.az, the Republic of Azerbaijan's Open Data Portal, directly through conversation.

Note: This server uses STDIO transport only (no public HTTP endpoint). You run it locally; your chatbot starts the server process and talks to it via standard input/output. Replace PATH_TO_OPENDATA_AZ_MCP in the configs below with the full path to your cloned repo.

Connect your chatbot to the MCP server

Configuration depends on your client. Use the format that matches your tool. Every config runs the server with uv and stdio; the only variable is the path to the project.

Quick reference: Command is uv, arguments are --directory, PATH_TO_OPENDATA_AZ_MCP, run, main.py.

Claude Desktop | Cursor | Claude Code | VS Code | Windsurf | AnythingLLM | ChatGPT | Gemini CLI | HuggingChat | IBM Bob | Kiro CLI | Kiro IDE | Le Chat (Mistral) | Mistral Vibe

Claude Desktop

Add this to your Claude Desktop config file:

  • Linux: ~/.config/Claude/claude_desktop_config.json

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "opendata-az": {
      "command": "uv",
      "args": [
        "--directory",
        "PATH_TO_OPENDATA_AZ_MCP",
        "run",
        "main.py"
      ]
    }
  }
}

Replace PATH_TO_OPENDATA_AZ_MCP with the full path to your cloned repo. Restart Claude Desktop; the tools will appear (hammer icon).

Cursor

  1. Open Cursor Settings.

  2. Search for MCP or Model Context Protocol.

  3. Add a server with stdio and:

  • Command: uv

  • Arguments: --directory PATH_TO_OPENDATA_AZ_MCP run main.py

(Use your actual path for PATH_TO_OPENDATA_AZ_MCP.)

Claude Code

If your Claude Code setup supports stdio MCP servers, add the server with command uv and args --directory, PATH_TO_OPENDATA_AZ_MCP, run, main.py.

VS Code

Add to your VS Code MCP config (run MCP: Open User Configuration from the Command Palette to open it):

  • Linux: ~/.config/Code/User/mcp.json

  • macOS: ~/Library/Application Support/Code/User/mcp.json

  • Windows: %APPDATA%\Code\User\mcp.json

{
  "servers": {
    "opendata-az": {
      "command": "uv",
      "args": [
        "--directory",
        "PATH_TO_OPENDATA_AZ_MCP",
        "run",
        "main.py"
      ]
    }
  }
}

Windsurf

Add to ~/.codeium/windsurf/mcp_config.json (Windows: %USERPROFILE%\.codeium\windsurf\mcp_config.json):

{
  "mcpServers": {
    "opendata-az": {
      "command": "uv",
      "args": [
        "--directory",
        "PATH_TO_OPENDATA_AZ_MCP",
        "run",
        "main.py"
      ]
    }
  }
}

AnythingLLM

If AnythingLLM supports stdio (command + args), use command uv and args: --directory, PATH_TO_OPENDATA_AZ_MCP, run, main.py. Check AnythingLLM MCP documentation for the exact schema.

ChatGPT

Available for paid plans (Plus, Pro, Team, Enterprise).

ChatGPT connectors typically use a URL. This server does not expose an HTTP endpoint by default. To use it with ChatGPT you would need to run the server with HTTP transport (see "Run locally" below for code) and expose it at a URL, or use a bridge that runs the stdio server and exposes HTTP.

Gemini CLI

Add to ~/.gemini/settings.json (Windows: %USERPROFILE%\.gemini\settings.json). If Gemini supports stdio:

{
  "mcpServers": {
    "opendata-az": {
      "command": "uv",
      "args": [
        "--directory",
        "PATH_TO_OPENDATA_AZ_MCP",
        "run",
        "main.py"
      ]
    }
  }
}

HuggingChat

If HuggingChat allows adding an MCP server by command + args, use command uv and args --directory, PATH_TO_OPENDATA_AZ_MCP, run, main.py. If it only accepts a URL, this server would need to be run with HTTP (see "Run locally").

IBM Bob

Edit global or project MCP config and add:

{
  "mcpServers": {
    "opendata-az": {
      "command": "uv",
      "args": [
        "--directory",
        "PATH_TO_OPENDATA_AZ_MCP",
        "run",
        "main.py"
      ]
    }
  }
}

Kiro CLI

Add to ~/.kiro/settings/mcp.json (Windows: %USERPROFILE%\.kiro\settings\mcp.json):

{
  "mcpServers": {
    "opendata-az": {
      "command": "uv",
      "args": [
        "--directory",
        "PATH_TO_OPENDATA_AZ_MCP",
        "run",
        "main.py"
      ]
    }
  }
}

Kiro IDE

Use .kiro/settings/mcp.json in your workspace or the global Kiro config; same structure as Kiro CLI above.

Le Chat (Mistral)

If Le Chat supports custom MCP with command/args, use command uv and args --directory, PATH_TO_OPENDATA_AZ_MCP, run, main.py. If it only supports a connector URL, you would need an HTTP endpoint (see "Run locally").

Mistral Vibe CLI

Edit your Vibe config (e.g. ~/.vibe/config.toml, Windows: %USERPROFILE%\.vibe\config.toml). If Vibe supports stdio:

[[mcp_servers]]
name = "opendata-az"
command = "uv"
args = ["--directory", "PATH_TO_OPENDATA_AZ_MCP", "run", "main.py"]

See Mistral Vibe MCP configuration.


Summary: All configs use uv as the command and --directory PATH_TO_OPENDATA_AZ_MCP run main.py as the arguments. No API key is required; the server only exposes read-only tools.

Related MCP server: Toronto MCP Server

Run locally

Prerequisites

  • uv (recommended) or Python 3.13+ with pip

1. Clone and install

git clone https://github.com/your-org/opendata-az-mcp.git
cd opendata-az-mcp
uv sync

2. Optional environment

Copy the example env and adjust if needed:

cp .env.example .env

Supported variable:

  • LOG_LEVEL – Python logging level (default: INFO). Values: DEBUG, INFO, WARNING, ERROR, CRITICAL.

3. Run the server

The server runs over stdio and waits for MCP client messages. In a terminal:

uv run main.py

Leave this running; connect with MCP Inspector or your chatbot using the configs above (with the path set to this directory).

4. Docker (optional)

If you prefer Docker, the image runs the same stdio server. Note: most MCP clients expect to start the process themselves, so Docker is mainly for consistency or CI. To build and run:

docker compose up -d

The server process runs inside the container; for local chatbot use, the stdio config pointing at a local clone (with uv run main.py) is usually simpler.

Transport support

This server uses the official Python SDK for MCP and supports STDIO transport only. There is no HTTP or SSE endpoint unless you add one yourself.

Available tools

The server exposes three read-only tools for the opendata.az CKAN API.

Datasets

  • search_datasets – Search the opendata.az catalog by keyword. Returns dataset titles, IDs, short descriptions, organization, and tags.

    Parameters: query (required), limit (optional, default: 10)

  • get_dataset_info – Get detailed metadata for a dataset: organization, full description, tags, and the list of attached resources (files) with their IDs and formats.

    Parameters: dataset_id (required)

  • get_resource_info – Get format, size, and the direct download URL for a specific resource (file). Use this so the user or LLM can download the file; do not load large files into context.

    Parameters: resource_id (required)

Suggested workflow: 1) search_datasets to find datasets, 2) get_dataset_info to see resources, 3) get_resource_info to get the download URL for a file.

Architecture note

This project is a thin middleware between the LLM and the opendata.az CKAN API. It uses only the public API at https://opendata.az/api/3/action/ (package_search, package_show, resource_show). No heavy data parsing, no analytics or tracking—just async HTTP with a 15-second timeout.

Tests

Automated tests (pytest)

uv run pytest
uv run pytest -v

Interactive testing (MCP Inspector)

  1. Install Node.js and ensure npx is available.

  2. Start the MCP server in one terminal: uv run main.py.

  3. In another terminal, start the Inspector and connect via stdio with:

    • Command: uv

    • Arguments: --directory <path-to-this-repo> run main.py

Or, if your Inspector can spawn the process: npx @modelcontextprotocol/inspector and configure it to run uv --directory <path> run main.py.

Contributing

Contributions are welcome. Please:

  • Keep changes small (one feature or fix per PR).

  • Ensure code is reviewed and tested before submission.

Linting and formatting

This project uses Ruff for linting and formatting:

uv run ruff check --fix && uv run ruff format

Optional type checking with ty:

uv run ty check

License

This project is licensed under the MIT License — see the LICENSE file for details.

Available Tools

3 tools
get_dataset_infoA

Get detailed metadata for a specific dataset on opendata.az.

Use this after search_datasets when the user wants details about a dataset. Returns organization name, full description, tags, and the list of attached resources (files). Use the resource IDs with get_resource_info(resource_id) to get the direct download URL for each file.

ParametersJSON Schema
NameRequiredDescriptionDefault
dataset_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description must cover behavioral aspects. It lists returned fields (organization, description, tags, resources) and implies read-only behavior. It could mention idempotency or safety more explicitly, but it's clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four well-structured sentences with front-loaded purpose, no unnecessary words. Clear flow of information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given output schema exists, description covers key return fields. Tool is simple with one parameter. Workflow guidance compensates for missing schema descriptions. Complete enough for agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% (no parameter descriptions). The description mentions dataset_id implicitly by referencing search_datasets output, but does not specify its format or origin explicitly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the verb 'Get' and resource 'detailed metadata for a specific dataset'. Distinguishes itself from siblings by placing it in a workflow: after search_datasets and before get_resource_info.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs to use after search_datasets when details are needed, and names get_resource_info as the next step for download URLs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_resource_infoA

Get file format, size, and direct download URL for a specific data file on opendata.az.

Use this when the user needs to download a file or get the link. Returns the resource format (e.g. CSV, XML, JSON), file size if available, and the URL to download the file. Do not load large files into context—instruct the user to download from the URL or use the URL in code to fetch locally.

ParametersJSON Schema
NameRequiredDescriptionDefault
resource_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses returns (format, size, URL) and warns about large files. It does not mention authentication or rate limits, but these are likely irrelevant for a public open data platform. Overall, behavior is sufficiently transparent for a read-only operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is three sentences, all essential. First sentence states purpose, second gives usage and return list, third adds important caution. No redundant information; front-loaded with key action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one required parameter, output schema exists), the description covers return values, usage, and a caution. It lacks explanation of the parameter's source, which is a minor gap. Overall, it provides nearly complete context for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so description must explain the sole parameter 'resource_id'. However, it only states 'for a specific data file' without clarifying what a resource_id is or how to obtain it (e.g., from get_dataset_info or search_datasets). This lack of parameter context reduces usability.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states verb 'Get' and resource 'file format, size, and direct download URL for a specific data file on opendata.az'. It distinguishes from sibling tools (get_dataset_info, search_datasets) by focusing on file-level details rather than dataset metadata or search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use: 'Use this when the user needs to download a file or get the link.' Also provides a critical usage warning: 'Do not load large files into context—instruct the user to download from the URL or use the URL in code to fetch locally.' This gives clear context for appropriate use.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_datasetsA

Search the Azerbaijan Open Data Portal (opendata.az) for datasets by keyword.

Use this when the user asks for datasets on a topic (e.g. transport, health, economy). Returns a list of dataset titles, IDs, and short descriptions so you can suggest get_dataset_info(dataset_id) next for details and resource list.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description bears full responsibility. It describes the return format (titles, IDs, descriptions) and suggests a follow-up action (get_dataset_info), but does not disclose any side effects, rate limits, or authentication needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences, efficient, and front-loaded with the main purpose. Every sentence adds value without unnecessary detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a search tool with two parameters and an output schema, the description covers the purpose, usage context, and return content. It lacks parameter details but the output schema likely provides return structure. The suggestion for next steps adds completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%. The description does not explain the 'query' and 'limit' parameters. While 'query' is somewhat implied, 'limit' is not clarified. The description fails to add meaning beyond the schema's basic titles.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's action: search the Azerbaijan Open Data Portal for datasets by keyword. It specifies the resource (datasets) and differentiates from sibling tools like get_dataset_info by explaining the return type and suggesting next steps.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance is provided: 'Use this when the user asks for datasets on a topic'. It also suggests the next tool to call, but does not explicitly state when not to use or compare to alternatives.

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.

  1. 3 tool updatesv0.1.0
    • First observedget_dataset_info
    • First observedget_resource_info
    • First observedsearch_datasets

TDQS

A4.3/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a distinct purpose: searching datasets, getting metadata, and retrieving file download info. They form a clear sequential workflow with no overlap.

Naming Consistency5/5

All tools use consistent verb_noun snake_case naming (search_datasets, get_dataset_info, get_resource_info), making the pattern predictable.

Tool Count5/5

Three tools are perfectly scoped for a read-only open data portal: search, metadata, and resource details. No unnecessary tools.

Completeness4/5

The set covers the core workflow—search, get details, get download URL. A minor gap is the lack of a browse/list all datasets without a keyword, but the search tool effectively addresses discovery.

Maintenance

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that connects AI chatbots directly to data.gouv.fr, France’s national open data platform. It allows users to search, explore, and analyze thousands of public datasets simply by asking questions in natural language.
    10
    1,589
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that provides tools for intelligently querying, analyzing, and retrieving datasets from Toronto's CKAN-powered open data portal. It enables AI assistants to perform natural language searches, inspect data structures, and track dataset update frequencies across the city's open data catalog.
    12
    -
  • A
    license
    A
    quality
    D
    maintenance
    An MCP server for discovering, downloading, querying, and analyzing datasets from Ontario's open data portals, allowing natural language questions and high-performance analytics via DuckDB.
    23
    1
    MIT