Metadata-Version: 2.5
Name: amselect-mcp
Version: 1.2.7
Summary: MCP server for AirMettle Select
Project-URL: Homepage, https://airmettle.com/select
Author-email: "AirMettle Inc." <support@airmettle.com>
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: mcp<3,>=2.2
Requires-Dist: pyamselect>=1.2.7
Description-Content-Type: text/markdown

# amselect-mcp

An [MCP](https://modelcontextprotocol.io) server for AirMettle Select. Ask Claude Code, Grok CLI, Codex CLI, Claude Desktop, Cursor, VS Code or any other MCP host to query CSV, JSON lines and Parquet data in your Azure Blob Storage with SQL, including gzip-compressed CSV and JSON lines, without moving or ingesting it.

> **Requires an active Azure AirMettle Select subscription.** `amselect-mcp` runs your queries through the AirMettle Select service, so set up your subscription first: it provides the endpoints and API key used below.

- [amselect-mcp](#amselect-mcp)
  - [How It Works](#how-it-works)
  - [Before You Start](#before-you-start)
  - [Setup](#setup)
    - [1. Get the Package](#1-get-the-package)
    - [2. Your AirMettle Select Details](#2-your-airmettle-select-details)
    - [3. Register It With Your AI Tool](#3-register-it-with-your-ai-tool)
  - [Tools](#tools)
  - [Result Size](#result-size)
  - [Troubleshooting](#troubleshooting)
  - [License](#license)

## How It Works

Your MCP host starts `amselect-mcp` on your machine and talks to it over standard input and output. When the model decides to run a query, `amselect-mcp` sends the request to your AirMettle Select endpoints with your subscription credentials and hands the rows back to the model. Nothing else is installed or hosted.

```
MCP host (Claude Code, ...) ──stdio──► amselect-mcp ──HTTPS──► AirMettle Select
                                       (on your machine)        query + metadata services
```

Your subscription ID and API key stay in the host's configuration on your machine. The model never sees them: they are not tool arguments, and `amselect-mcp` removes the key from any error text it returns.

## Before You Start

You need:

- **An active Azure AirMettle Select subscription.** It provides:
  - your **subscription ID** and **API key**
  - the **query endpoint host**
  - the **metadata service endpoint** URL
- Data in an Azure storage account that is registered under your subscription.
- [uv](https://docs.astral.sh/uv/getting-started/installation/), or Python 3.10 or later with pip.

## Setup

`amselect-mcp` is a Python package on PyPI. Your AI tool starts it on demand, so setup takes three steps: get the package, provide your AirMettle Select details, and register it with your tool.

### 1. Get the Package

| Option | Command | Notes |
|---|---|---|
| Run with uv (recommended) | `uvx amselect-mcp` | uv fetches the package from PyPI and runs it on demand. |
| Install with pip | `pip install amselect-mcp` | Python 3.10 or later. Use `amselect-mcp` wherever this guide says `uvx amselect-mcp`. |

### 2. Your AirMettle Select Details

| Variable | Value |
|---|---|
| `AMSELECT_HOST` | Query endpoint host |
| `AMSELECT_SUBSCRIPTION_ID` | Subscription ID |
| `AMSELECT_API_KEY` | Subscription API key |
| `AMSELECT_METADATA_ENDPOINT` | Metadata service URL, used by `prepare` |

### 3. Register It With Your AI Tool

#### Option A: Export Once in Your Shell

Set the values in the shell you start the AI tool from, then register the server with no credentials in the command. The tool passes your shell's environment on to `amselect-mcp`. Tested with Claude Code:

```bash
export AMSELECT_HOST="<query-host>"
export AMSELECT_METADATA_ENDPOINT="https://<metadata-endpoint>"
export AMSELECT_SUBSCRIPTION_ID="<subscription-id>"
export AMSELECT_API_KEY="<api-key>"

claude mcp add amselect -- uvx amselect-mcp
claude
```

Add the exports to `~/.bashrc` or `~/.zshrc` to have them in every new terminal. Desktop apps read their own configuration, so use Option B for them.

#### Option B: Save the Values With the Tool

Each tool keeps the values in its own configuration, so the server works from any terminal and in desktop apps.

**Claude Code**

```bash
claude mcp add amselect \
    -e AMSELECT_HOST="<query-host>" \
    -e AMSELECT_METADATA_ENDPOINT="https://<metadata-endpoint>" \
    -e AMSELECT_SUBSCRIPTION_ID="<subscription-id>" \
    -e AMSELECT_API_KEY="<api-key>" \
    -- uvx amselect-mcp
```

Add `--scope user` to make it available in every project. Run `/mcp` inside Claude Code to check that the `amselect` server is connected.

To share the setup with a team through a project `.mcp.json` file, reference the credentials from each person's environment instead of writing them into the file:

```json
{
  "mcpServers": {
    "amselect": {
      "command": "uvx",
      "args": ["amselect-mcp"],
      "env": {
        "AMSELECT_HOST": "<query-host>",
        "AMSELECT_METADATA_ENDPOINT": "https://<metadata-endpoint>",
        "AMSELECT_SUBSCRIPTION_ID": "${AMSELECT_SUBSCRIPTION_ID}",
        "AMSELECT_API_KEY": "${AMSELECT_API_KEY}"
      }
    }
  }
}
```

**Grok CLI**

```bash
grok mcp add amselect \
    -e AMSELECT_HOST="<query-host>" \
    -e AMSELECT_METADATA_ENDPOINT="https://<metadata-endpoint>" \
    -e AMSELECT_SUBSCRIPTION_ID="<subscription-id>" \
    -e AMSELECT_API_KEY="<api-key>" \
    uvx amselect-mcp
```

**Codex CLI**

```bash
codex mcp add amselect \
    --env AMSELECT_HOST="<query-host>" \
    --env AMSELECT_METADATA_ENDPOINT="https://<metadata-endpoint>" \
    --env AMSELECT_SUBSCRIPTION_ID="<subscription-id>" \
    --env AMSELECT_API_KEY="<api-key>" \
    -- uvx amselect-mcp
```

**Claude Desktop and Cursor**

Add the server to `claude_desktop_config.json` (Claude Desktop: Settings → Developer → Edit Config) or `~/.cursor/mcp.json` (Cursor):

```json
{
  "mcpServers": {
    "amselect": {
      "command": "uvx",
      "args": ["amselect-mcp"],
      "env": {
        "AMSELECT_HOST": "<query-host>",
        "AMSELECT_METADATA_ENDPOINT": "https://<metadata-endpoint>",
        "AMSELECT_SUBSCRIPTION_ID": "<subscription-id>",
        "AMSELECT_API_KEY": "<api-key>"
      }
    }
  }
}
```

Restart the app after editing the file. If it cannot find `uvx`, use the full path that `which uvx` prints.

**VS Code**

Add the server to `.vscode/mcp.json`. VS Code prompts for the API key the first time the server starts and keeps it in its secret storage rather than in the file:

```json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "amselect-api-key",
      "description": "AirMettle Select API key",
      "password": true
    }
  ],
  "servers": {
    "amselect": {
      "type": "stdio",
      "command": "uvx",
      "args": ["amselect-mcp"],
      "env": {
        "AMSELECT_HOST": "<query-host>",
        "AMSELECT_METADATA_ENDPOINT": "https://<metadata-endpoint>",
        "AMSELECT_SUBSCRIPTION_ID": "<subscription-id>",
        "AMSELECT_API_KEY": "${input:amselect-api-key}"
      }
    }
  }
}
```

**Other Hosts**

Any MCP host that can launch a local (stdio) server works: run the command `uvx amselect-mcp` with the variables from step 2.

## Tools

**`query`** runs a SQL `SELECT` on one object and returns the rows. The table is always named `Object` (`SELECT * FROM Object LIMIT 5`). Supported objects are CSV, gzip-compressed CSV, JSON lines (one JSON record per line), gzip-compressed JSON lines, and Parquet. The format is inferred from the blob extension (`.csv`, `.tsv`, `.jsonl`, `.ndjson`, `.json`, `.parquet`, with `.gz` marking gzip compression) and can also be passed explicitly, together with CSV header and delimiter settings. Results come back as CSV or JSON lines.

**`prepare`** generates the query metadata an object needs. Run it once per object, and again with `overwrite` after the object's contents change. It waits up to a minute by default; if the prepare is still running after that, calling it again reports whether the object is ready.

**`export`** runs a query like `query` but saves the complete result to a file on your machine instead of returning it to the model, for when you want every row. Files are created in the export directory (`AMSELECT_EXPORT_DIR`, default `~/amselect-exports`) under the name the model picks; existing files are never overwritten, and names that point outside the directory are rejected. The model gets back the file path, the row count and a short preview.

## Result Size

Query results returned to the model are capped at 64 KiB by default (see `AMSELECT_MAX_RESULT_BYTES`). Longer output is cut at the last whole record and marked as truncated. A truncated query still runs to completion on the service, so ask for `LIMIT`, filters and aggregates (`COUNT`, `SUM`, `GROUP BY`) rather than whole objects. To keep a full result, ask for an export: it goes to a file rather than into the conversation (up to 2 GiB by default, see `AMSELECT_MAX_EXPORT_BYTES`).

## Troubleshooting

| Symptom | What to check |
|---|---|
| The server does not start or shows as failed | A required variable is missing or invalid; the host's MCP log shows a line such as `amselect-mcp: AMSELECT_HOST is required`. Check that `uvx` is on the host's `PATH`. |
| "AMSelect rejected the credentials (HTTP 401)" | `AMSELECT_SUBSCRIPTION_ID` and `AMSELECT_API_KEY` |
| "Object is not prepared for querying" | Prepare the object first (the model normally does this itself) |
| "prepare is not configured" | Set `AMSELECT_METADATA_ENDPOINT` |
| HTTP 403 or 404 | The storage account must be registered under your subscription, and the subscription must be active |
| "Could not reach the AMSelect query endpoint" | `AMSELECT_HOST`, `AMSELECT_PORT` and your network or proxy settings |

## License

MIT — see the LICENSE file. Use of the AirMettle Select service itself is
governed by your service agreement.
