Skip to main content
Glama
kshitiz305

analytics-mcp-server

by kshitiz305

analytics-mcp-server

A Model Context Protocol (MCP) server, built with FastMCP, that lets an LLM safely explore and analyse a SQLite database through well-designed tools — list tables, inspect schema, run guarded read-only SQL, compute aggregations, and import CSVs.

It ships with a seeded sample e-commerce dataset, so you can clone and run it in under a minute with zero API keys or external services.

  • Language: Python 3.10+

  • Framework: FastMCP (fastmcp)

  • Data: SQLite (stdlib) + pandas

  • Transport: stdio (local) — the standard for desktop MCP clients

  • Tested: 16 pytest cases, incl. read-only safety and pagination


Why this exists

MCP servers expose tools that an LLM can call. The hard parts are (1) safety — never letting a model mutate or exfiltrate data it shouldn't — and (2) ergonomics — tools with clear schemas, pagination, and actionable errors so the model uses them correctly. This project demonstrates both.


Related MCP server: mcp-sqlite-tools

Quick start

git clone https://github.com/kshitiz305/analytics-mcp-server.git
cd analytics-mcp-server

python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt
pip install -e .

python scripts/seed_data.py     # generates sample.db

Run the server over stdio:

analytics-mcp            # console script
# or:  python -m analytics_mcp.server

Try it without an MCP client

Use the built-in MCP Inspector:

npx @modelcontextprotocol/inspector analytics-mcp

Register it with Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "analytics": {
      "command": "analytics-mcp",
      "env": { "ANALYTICS_DB_PATH": "/absolute/path/to/sample.db" }
    }
  }
}

Point ANALYTICS_DB_PATH at any SQLite file to analyse your own data.


Tools

Tool

Purpose

Write?

analytics_list_tables

List tables with row counts

read-only

analytics_describe_table

Column schema, row count, sample rows

read-only

analytics_run_query

Run a guarded, paginated SELECT

read-only

analytics_aggregate

Group-by + count/sum/avg/min/max (no SQL needed)

read-only

analytics_import_csv

Load a CSV into a table (validated via pandas)

write

Every tool supports response_format="markdown" (default, human-readable) or "json" (machine-readable, full precision), carries MCP annotations (readOnlyHint, destructiveHint, …), and returns actionable Error: … messages.

Example output

analytics_list_tables:

### Tables

| table | rows |
| --- | --- |
| customers | 200 |
| order_items | 2420 |
| orders | 800 |
| products | 40 |

analytics_aggregate(table="orders", group_by="status", agg="count"):

### count(*) by status in orders

| status | value |
| --- | --- |
| completed | 379 |
| shipped | 175 |
| processing | 119 |
| cancelled | 87 |
| returned | 40 |

analytics_run_query with a join + pagination (top customers by spend):

Returned 5 of 196 rows (offset 0, next_offset 5)

| name | country | spend |
| --- | --- | --- |
| Arjun Khan | Japan | 22462.72 |
| Hiro Gupta | Japan | 21656.45 |
| Fatima Lee | Japan | 21249.44 |
| Liam Gupta | Canada | 19013.48 |
| Liam Brown | India | 18822.85 |

Attempting a write is rejected:

analytics_run_query(sql="DROP TABLE customers")
→ Error: Only read-only queries are permitted. The statement must start with SELECT or WITH.

Safety model

User-supplied SQL is treated as untrusted and guarded on three independent layers:

  1. Read-only connection — queries execute over a file:…?mode=ro SQLite URI, so writes are rejected at the storage engine level.

  2. Authorizer callback — an allow-list set_authorizer permits only read actions (SELECT/READ/FUNCTION), blocking ATTACH, PRAGMA writes, etc.

  3. Statement validationanalytics_run_query accepts a single SELECT/WITH statement only, with fast, clear errors before touching the database.

Tools that build SQL internally (list_tables, describe_table, aggregate) never interpolate raw user text — table/column names are validated against the live schema and quoted, so they are injection-safe. The only write path, analytics_import_csv, validates the destination name against an identifier allow-list.


Sample dataset

scripts/seed_data.py generates a deterministic (seeded) e-commerce dataset:

  • customers (200) — id, name, email, country, signup_date

  • products (40) — id, name, category, price

  • orders (800) — id, customer_id, order_date, status

  • order_items (2420) — id, order_id, product_id, quantity, unit_price

Because the RNG is seeded, the numbers above are reproducible on any machine.


Testing

pip install -e ".[dev]"
pytest

The suite (tests/test_server.py) covers schema discovery, pagination, aggregation, CSV import, rejection of write/multi-statement SQL, and an end-to-end call through FastMCP's in-memory client.


Docker

docker build -t analytics-mcp .
docker run --rm -i analytics-mcp        # serves MCP over stdio

The image installs the package and bundles a freshly seeded sample.db.


Project structure

analytics-mcp-server/
├── src/analytics_mcp/
│   ├── server.py        # FastMCP server + tool definitions
│   ├── database.py      # SQLite access layer (read-only safety)
│   ├── models.py        # Enums for tool inputs
│   ├── formatting.py    # JSON / Markdown formatting + pagination
│   └── sample_data.py   # Deterministic dataset generator
├── scripts/seed_data.py # CLI to (re)build sample.db
├── tests/test_server.py # pytest suite
├── Dockerfile
└── pyproject.toml

License

MIT © 2026 Kshitiz Gupta

Available Tools

5 tools
analytics_aggregateAggregate By ColumnA
Read-onlyIdempotent

Group a table by a column and compute an aggregate — no SQL required.

A convenience workflow tool over the most common analytics pattern. Column and table names are validated against the schema, so it is safe from injection. For anything more complex, use analytics_run_query.

ParametersJSON Schema
NameRequiredDescriptionDefault
aggNoOne of ``count``, ``sum``, ``avg``, ``min``, ``max`` (default count).count
limitNoMaximum groups to return (1-200).
orderNoSort groups by the aggregate value, ``desc`` (default) or ``asc``.desc
tableYesTable to aggregate.
metricNoNumeric column to aggregate. Required for sum/avg/min/max.
group_byYesColumn to group rows by.
response_formatNo``markdown`` (default) or ``json``.markdown

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, establishing safety. The description adds that column/table names are validated against the schema and safe from injection, providing useful behavioral context beyond the annotations. However, it does not elaborate on other behavioral aspects like error handling or performance.

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, front-loaded with the core purpose. Every sentence adds value: purpose, convenience context, safety note and alternative. No filler or redundancy.

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?

With rich annotations, a full output schema, and a simple use case, the description covers the essential aspects: purpose, safety, and alternative. It could mention the output format or that it returns aggregated data, but the output schema likely covers that. Overall it is complete for an AI agent to correctly select and invoke the tool.

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?

The input schema has 100% description coverage, so the schema fully documents each parameter. The description adds no additional parameter information, meaning it neither enhances nor detracts from the schema's explanations. Baseline of 3 is appropriate.

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 'Group a table by a column and compute an aggregate — no SQL required,' providing a specific verb and resource. It also implicitly distinguishes from siblings by indicating this is a convenience wrapper for simple aggregations, while 'analytics_run_query' is for more complex queries.

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?

The description says 'A convenience workflow tool over the most common analytics pattern' and explicitly directs to 'analytics_run_query' for anything more complex, offering clear context and an alternative. It lacks explicit 'when not to use' cases but the guidance is sufficient for an AI agent.

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

analytics_describe_tableDescribe TableA
Read-onlyIdempotent

Show a table's column schema, total row count and a few sample rows.

Use this after analytics_list_tables to understand a table's columns (names, types, nullability, primary keys) before writing a query.

ParametersJSON Schema
NameRequiredDescriptionDefault
tableYesExact table name (see analytics_list_tables).
sample_limitNoNumber of sample rows to preview (0-50).
response_formatNo``markdown`` (default) or ``json``.markdown

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?

Annotations already declare readOnlyHint=true and destructiveHint=false. The description adds that the tool returns column schema, row count, and sample rows, which gives behavioral context beyond annotations. No contradictions, but the annotations already cover safety.

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 consists of two sentences: the first defines purpose, the second states usage. No extraneous words, well front-loaded, and efficient.

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 the tool simplicity (3 parameters, 1 required) and the presence of an output schema, the description sufficiently covers what the tool does and when to use it. No gaps.

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 description coverage is 100%, so each parameter is documented in the schema. The description does not add significant new meaning beyond reminding to use exact table name from analytics_list_tables. Baseline 3 is appropriate.

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 shows a table's column schema, total row count, and sample rows. It uses a specific verb-resource combination ('Show a table's column schema...') and distinguishes from siblings by advising to use after analytics_list_tables.

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?

The description explicitly says to use this tool after analytics_list_tables to understand columns before writing a query. This provides clear context and implicitly guides not to use it for other purposes like running queries or aggregates.

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

analytics_import_csvImport CSV Into TableA
Destructive

Load a CSV file into a SQLite table (validated via pandas).

This is the only WRITE tool. Column types are inferred by pandas. The destination table name must be a valid SQL identifier.

ParametersJSON Schema
NameRequiredDescriptionDefault
tableYesDestination table name (letters, digits, underscores).
csv_pathYesPath to a readable .csv file on disk.
if_existsNo``fail`` (default — error if the table exists), ``replace`` (drop and recreate) or ``append`` (add rows).fail

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

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

Adds value beyond annotations by specifying pandas column type inference and requiring valid SQL identifiers. Annotations already mark destructiveHint true, but the description enriches understanding without contradiction.

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?

Three sentences, front-loaded with core action, each sentence adding critical information without waste. Highly efficient.

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?

Covers key aspects: write action, validation, rule for table name, and if_exists behavior (via schema). Output schema exists, so return values are covered. Slightly more detail on validation process would be ideal, but overall sufficient.

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 100%, so baseline is 3. The description does not add significant new meaning for parameters beyond what's in the schema (e.g., valid SQL identifier is already in schema description).

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 'Load a CSV file into a SQLite table', specifying verb, resource, and validation via pandas. Distinguishes itself from siblings as 'the only WRITE tool', making its purpose unambiguous.

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?

Explicitly notes it's the only write tool, providing clear context for when to use it. Does not directly list when not to use alternatives, but this is strongly implied by the write-vs-read distinction.

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

analytics_list_tablesList TablesA
Read-onlyIdempotent

List all user tables in the database together with their row counts.

Use this first to discover what data is available before describing or querying a specific table. It does not read row contents, only metadata.

ParametersJSON Schema
NameRequiredDescriptionDefault
response_formatNo``markdown`` (human-readable, default) or ``json``.markdown

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true and destructiveHint=false. The description adds context beyond this by specifying it reads only metadata (not row contents) and lists row counts. No contradictions.

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?

Two sentences, front-loaded with purpose, then usage guidance. Every sentence serves a purpose; no fluff. Highly efficient.

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 the tool's simplicity (one optional parameter, output schema present, annotations clear), the description fully covers purpose, usage context, and behavioral limitations. No gaps.

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 description coverage is 100%, so baseline is 3. The description does not mention the response_format parameter, adding no additional semantic information beyond what the schema provides.

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 it lists all user tables with row counts, using a specific verb ('List') and resource ('user tables'). It distinguishes from sibling tools like analytics_describe_table (describes specific table) and analytics_run_query (runs arbitrary queries).

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 advises 'Use this first to discover what data is available before describing or querying a specific table,' providing clear when-to-use guidance. It also clarifies it does not read row contents, implying when not to use (if row contents are needed).

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

analytics_run_queryRun Read-Only SQLA
Read-onlyIdempotent

Run a guarded, read-only SQL SELECT query with pagination.

The query is executed over a read-only connection protected by a SQLite authorizer, and is validated to be a single SELECT/WITH statement, so it cannot modify data. Results are paginated via limit/offset.

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYesA single read-only SQL statement. Must start with SELECT or WITH. Writes and multiple statements are rejected.
limitNoMaximum rows to return (1-500).
offsetNoRows to skip, for pagination.
response_formatNo``markdown`` (default) or ``json``.markdown

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint. The description adds valuable context: the query runs on a read-only connection with a SQLite authorizer, is validated as a single SELECT/WITH, and results are paginated. This fully discloses behavior beyond annotations.

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 two sentences, front-loaded with the core purpose, and every sentence adds value. No wasted words.

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?

With an output schema present, return values are covered. The description addresses purpose, safety, pagination, and validation. It could mention error handling or result format, but overall it is complete for this tool.

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 100%, so parameter descriptions are already present. The description mentions pagination via limit/offset but does not add new meaning beyond the schema. Baseline of 3 is appropriate.

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 explicitly states 'Run a guarded, read-only SQL SELECT query with pagination,' clearly identifying the verb and resource. It distinguishes from sibling tools (like analytics_import_csv or analytics_describe_table) by emphasizing read-only execution and pagination.

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?

The description explains that the tool is for read-only queries and validates statements, implying it is safe to run. It does not explicitly list alternatives or when-not-to-use, but the context is clear enough for an agent to choose appropriately.

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. Dates show when Glama detected each change.

  1. 5 tool updatesv0.1.0
    • First observedanalytics_aggregate
    • First observedanalytics_describe_table
    • First observedanalytics_import_csv
    • First observedanalytics_list_tables
    • First observedanalytics_run_query

TDQS

A4.3/5.0
Disambiguation5/5

Each tool has a clear, distinct purpose: listing tables, describing schemas, importing data, running custom queries, and performing common aggregations. No overlap or ambiguity.

Naming Consistency4/5

All tools use the 'analytics_' prefix and snake_case. Most follow a verb_noun pattern (e.g., describe_table, list_tables), but 'analytics_aggregate' lacks a noun, which is a minor inconsistency.

Tool Count5/5

With 5 tools covering exploration (list, describe), querying (run, aggregate), and data loading (import), the count is well-scoped for an analytics server. Not too few, not too many.

Completeness4/5

Covers core analytics workflows: schema discovery, custom queries, common aggregations, and data import. Minor gaps include lack of an export tool or more advanced statistical functions, but these can be addressed via custom queries.

Maintenance

ActivityStale
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    A
    maintenance
    Provides comprehensive SQLite database operations for LLMs with security features, transaction support, and separation of read-only and destructive operations.
    22
    133
    19
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to explore and query SQLite databases through read-only tools, with defense-in-depth sandboxing preventing any data modifications.
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kshitiz305/analytics-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server