Skip to main content
Glama
coreprocess

Periscope MCP

Official
by coreprocess

Periscope MCP

Periscope is an MCP server that gives AI agents strictly read-only query access to data sources:

Source

Query language

Result format

PostgreSQL

SQL (one SELECT)

Parquet

Oracle Database (19c+)

SQL (one SELECT)

Parquet

SQL Server / Azure SQL

T-SQL (one SELECT)

Parquet

Azure Cosmos DB for NoSQL

Cosmos NoSQL query language

NDJSON (gzip)

Azure Blob Storage

Glob or prefix, optional tag filter

NDJSON listing; contents referenced in place

Azure Log Analytics / Application Insights

KQL

Parquet

Redis (standalone or cluster)

One read-only Redis command; SCAN as key export

NDJSON (gzip)

KIM

Node or record query language

NDJSON (gzip); offloaded contents referenced in place

Any HTTP API (rest)

One HTTP request: request line, headers, body

NDJSON (gzip); binary responses referenced, fetched again on read

Agents get the full read capability of each source. Writes cannot get through: on the API-based sources, write operations are never offered and are blocked on the wire; on the query-language sources they are filtered at several independent layers; for HTTP APIs, only the requests the operator declares as reads are sent. The guarantee holds even when the configured credential could write.

A query never returns data inline. It produces a result that the agent either pages through in small windows (read_result) or downloads over HTTP and processes with its own tools (curl, DuckDB, jq, pandas). The flow is the same for five rows or fifty million.

The full design is in docs/CONCEPT.md.

Tools

Tool

Purpose

list_sources

Sources you may query, with query language, dialect hints, options and limits

describe

Metadata, drilled down step by step (schemas → tables → columns, containers → sampled schemas, prefixes, …)

query

Starts a query and returns a resultId immediately; non-read-only queries are rejected synchronously

get_result

Status, schema, row counts, window limits and one manifest URL for downloads

read_result

One window of rows, or (blob, KIM and REST results) lines of a text file or an image

cancel_query

Stops a running query

A typical session:

list_sources()                                         → appdata (cosmos), erp (oracle), …
describe("appdata", "main/claims")                     → partition key, inferred schema
query("appdata", "SELECT * FROM c WHERE c.year = @y",
      { "@y": 2026 }, { "container": "claims" })       → r_7Hq2kV…
read_result("r_7Hq2kV…", limit: 20)                    → first rows (already while running)
get_result("r_7Hq2kV…", wait: 10)                      → succeeded, 1.8M rows, 19 files, 412 MB
M="<download.manifestUrl>"; B="${M%/manifest.json}"
curl -fsS "$M" | jq -r '.files[].path' | xargs -P4 -I{} curl -fsS --create-dirs -o {} "$B/{}"
duckdb -c "SELECT status, count(*) FROM read_json('part-*.ndjson.gz') GROUP BY 1"

Related MCP server: QuestLLens

Run locally (stdio)

Requires Node.js 24 and pnpm.

pnpm install
cp .env.example .env.periscope   # add your sources
node src/index.ts --env-file .env.periscope

Register it with Claude Code:

claude mcp add periscope -- node /path/to/periscope-mcp/src/index.ts --env-file /path/to/.env.periscope

In stdio mode, results are stored in ./.periscope/results and download URLs point to a small HTTP endpoint on 127.0.0.1 that the server starts alongside.

With Docker (the published image runs the HTTP transport by default):

docker run -i --rm --env-file .env.periscope \
  -e PERISCOPE_TRANSPORT=stdio -e PERISCOPE_HOST=0.0.0.0 -e PERISCOPE_PORT=8765 \
  -e PERISCOPE_PUBLIC_URL=http://127.0.0.1:8765 -p 127.0.0.1:8765:8765 \
  ghcr.io/coreprocess/periscope-mcp:0.1.0

Deploy

Periscope is built to run as one container per project and environment, inside the network where the data sources are reachable, protected by Microsoft Entra ID (OAuth 2.1, MCP authorization spec): directly when the host is on a domain verified in the tenant, otherwise through Periscope's built-in authorization server, which signs users in at Entra (PERISCOPE_AUTH=entra-broker). See docs/deployment.md and the Terraform module in terraform/.

Configuration

Everything is configured through environment variables; sources are an indexed list:

PERISCOPE_SOURCE_N=2
PERISCOPE_SOURCE_0_NAME=shop
PERISCOPE_SOURCE_0_TYPE=postgres
PERISCOPE_SOURCE_0_URL=postgres://reader:secret@db:5432/shop
PERISCOPE_SOURCE_1_NAME=cache
PERISCOPE_SOURCE_1_TYPE=redis
PERISCOPE_SOURCE_1_URL=rediss://default:secret@cache:6380

The full reference is in docs/configuration.md.

How read-only is enforced

Source

Layers

Cosmos DB

Only read/query operations implemented; HTTP guard on the SDK lets only GET/HEAD and query POSTs through

Blob Storage

Only list/read operations implemented; pipeline guard lets only GET/HEAD through; downloads pinned to ETag/version/snapshot

Log Analytics

Query API only (it has no write operations); control commands rejected

PostgreSQL

READ ONLY transaction, always rolled back; syntax-tree guard with the Postgres parser (single SELECT, no locking, no data-modifying CTEs); side-effect function denylist; inventory that parses view definitions and switches to strict mode when dblink-style functions are reachable

Oracle

SET TRANSACTION READ ONLY, always rolled back; strict lexer (single SELECT, no PL/SQL, no DML/DDL keywords, no FOR UPDATE, no NEXTVAL); system package allowlist; routine inventory against autonomous transactions and dynamic SQL

SQL Server

Transaction that is always rolled back; strict T-SQL lexer (single SELECT, no EXEC, no DML/DDL, no SELECT … INTO, no NEXT VALUE FOR, no remote rowsets or locking hints); function inventory against extended procedures, OLE Automation, CLR and linked servers, with strict mode for sysadmin logins and risky server settings; optional ApplicationIntent=ReadOnly

Redis

Static allowlist of read-only, non-blocking commands; guard on every command the client writes to the socket; optional replica reads

KIM

Service account key with only the Reader role; allowlist on every HTTP request (query, collection listing, type definitions, byte reads; no drafts, sessions, agents or exports); query check (no reveal unless allowed, no foreign origins)

REST

Operator-declared allowlist of METHOD pattern rules, checked on every request, page, redirect hop and file read; no method override headers or parameters, no dot segments or encoded slashes; agents cannot set credentials; optional script check. Holds as far as the allowed requests are reads for the API.

Each layer is tested on its own against real databases with write-capable credentials (see test/integration).

Development

pnpm lint          # Biome
pnpm typecheck     # TypeScript
pnpm test          # unit tests
pnpm test:integration   # needs Docker: Postgres, Oracle, SQL Server, Cosmos emulator, Azurite, Redis; KIM and REST run against mocks
pnpm build

License

MIT

Available Tools

6 tools
cancel_queryCancel a running queryC
Read-onlyIdempotent

Stops a running query. Parts already written stay readable until the result expires.

NameRequiredDescriptionDefault
resultIdYes

TDQS

C2.6/5.0
Behavior1/5

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

The description says the tool "stops a running query" while annotations declare readOnlyHint=true, implying no state change. That is inconsistent: cancellation is a state mutation. The added note about partial results remaining readable is useful, but the core safety profile is contradictory.

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

Conciseness4/5

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

Two tight sentences with the action front-loaded and the retention behavior following. Every sentence carries information, though a routing hint would improve it.

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

Completeness3/5

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

For a single-parameter tool with no output schema, explaining the retention and expiration of partial results is good context. But the undocumented resultId and the annotation contradiction leave real gaps 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?

One required parameter (resultId) has 0% schema description coverage, and the description never explains what a resultId is or where to obtain it. The description adds no meaning beyond the parameter name at all.

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

Purpose4/5

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

States a specific verb and resource: "Stops a running query." An agent can grasp the action immediately. It does not, however, differentiate itself from siblings like get_result or read_result, which handle query output.

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

Usage Guidelines2/5

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

No guidance on when to cancel versus using get_result/read_result, no mention of prerequisites such as needing an active query or a valid resultId, and no note on what happens after cancellation (e.g., how to fetch remaining results).

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

describeDescribe metadataA
Read-onlyIdempotent

Browses metadata of a source step by step (schemas/tables/columns, databases/containers with sampled schemas, blob prefixes, Log Analytics tables, Redis key prefixes). Use path to drill down and filter (with * wildcards) to narrow.

NameRequiredDescriptionDefault
pathNoLevel to describe, e.g. "public.orders", "main/claims", "invoices/2026/"
filterNoName pattern, * and ? wildcards
sourceYesSource name from list_sources

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already cover readOnly, idempotent, non-destructive, and closed-world, so the safety profile is known. The description adds genuinely useful behavior beyond that: drill-down is hierarchical/stepwise and schemas are 'sampled' rather than exhaustive, which affects how the agent should interpret results.

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

Conciseness4/5

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

Two dense sentences with the core action front-loaded and the operational hint second. The parenthetical source-type list is long but earns its place by clarifying breadth; nothing is redundant.

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 three-parameter read-only discovery tool with no output schema, the definition conveys enough: what it enumerates, how to drill down, and that schemas are sampled. Minor gap in not describing the shape of returned results, but that is largely inferable from the enumerated metadata kinds.

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 'path', 'filter', and 'source' are already documented with examples and wildcard syntax in the schema itself. The description restates the drill-down and wildcard concepts without adding format or semantics beyond that baseline.

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

Purpose4/5

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

States a specific verb (browses) and resource (metadata of a source) and enumerates the concrete metadata kinds it can return across source types. It doesn't explicitly differentiate itself from siblings like query or list_sources, though the exploratory 'step by step' framing implies discovery rather than querying.

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

Usage Guidelines3/5

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

'Use path to drill down and filter ... to narrow' explains how to operate the tool but not when to choose it over query, list_sources, or read_result. Usage is implied as an exploratory/discovery step, but no explicit when/when-not or alternative routing is given.

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

get_resultGet result status and metadataB
Read-onlyIdempotent

Status, progress, schema, row counts, window limits and download information (one manifest URL; all files are listed in the manifest). Use wait (seconds, max 10) to wait for a status change.

NameRequiredDescriptionDefault
waitNoSeconds to wait for a status change
resultIdYes

TDQS

B3.3/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), and the description adds genuinely non-obvious context: downloads resolve through a single manifest URL and all files are listed in that manifest. It does not describe the status values or what happens on repeated polling, but that gap is smaller given the annotation coverage.

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

Conciseness4/5

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

Two sentences with no filler, and the payload summary is front-loaded ahead of the wait hint. The first sentence is a compressed noun list rather than a statement of action, which costs a little readability but no space.

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 no output schema, the description carries the full return-value burden and does so well, covering status, progress, counts, limits and download mechanics. The remaining gaps—resultId provenance and the possible status values—are meaningful for a polling tool but not fatal.

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?

The description's wait clause restates the schema verbatim ('Seconds to wait for a status change', max 10) and adds no semantics. resultId, the required parameter, is undocumented in both the schema and the description, so the agent has no hint about where a resultId comes from.

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

Purpose4/5

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

The description enumerates exactly what the tool returns for a result: status, progress, schema, row counts, window limits and download info, so an agent knows the resource and payload. It stops short of naming the sibling read_result, so there is no explicit differentiation between polling metadata and fetching actual data.

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

Usage Guidelines2/5

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

The only guidance is 'Use wait (seconds, max 10) to wait for a status change', which repeats the schema constraint rather than explaining when to poll versus calling read_result or list_sources. There are no exclusions or alternatives named, so tool selection is left entirely to inference.

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

list_sourcesList data sourcesA
Read-onlyIdempotent

Lists the data sources you may query: name, type, query language, dialect hints, options, limits and the active read-only layers.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive/closed-world, so the safety profile is covered. The description adds real context beyond that by naming what is returned, including dialect hints, limits and active read-only layers, which tells the agent what it can learn before querying.

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

Conciseness4/5

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

One sentence, front-loaded with the verb, followed by an enumerated payload list. Slightly list-heavy but every item conveys what the agent can extract.

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 no parameters, no output schema and fully covered annotations, the description carries the remaining burden and does so by describing the returned content. Minor gaps: no mention of ordering, permissions or whether results are scoped to the caller's workspace.

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

Parameters4/5

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

The tool takes zero parameters, so the baseline is 4; the description correctly implies no filtering arguments are needed.

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

Purpose4/5

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

States a specific verb and resource ('Lists the data sources you may query') and enumerates the fields returned, which makes it distinguishable from query/get_result. It does not explicitly contrast itself with the sibling 'describe', but the discovery framing is clear.

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

Usage Guidelines3/5

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

'you may query' hints that this is a pre-query discovery step, but there is no explicit when-to-use or when-not-to-use guidance, and no routing against the sibling 'describe' tool.

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

queryStart a queryA
Read-only

Starts a read-only query and returns a resultId immediately. Query language depends on the source (see list_sources). Invalid or non-read-only queries are rejected right away with a reason. Then call get_result / read_result.

NameRequiredDescriptionDefault
queryYesSQL, Cosmos SQL, KQL, a blob glob or a Redis command
paramsNoBind parameters (format depends on the source)
sourceYesSource name from list_sources
optionsNoType-specific options (see list_sources)

TDQS

A4.2/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, yet the description still adds real value: the call returns immediately with a resultId rather than results, and invalid/non-read-only queries fail fast with a rejection reason. Missing detail on size limits or timeouts, but the async-vs-sync behavior is the critical disclosure and it is present.

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 tight sentences with the return behavior front-loaded, then the language dependency, then the follow-up call. No filler and no repetition of the title.

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 no output schema, the description correctly supplies the return value (resultId) and the continuation path, which is the main thing an agent needs. It could say more about result lifetimes or options, but for a 4-parameter read-only launcher this is close to complete.

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% and the schema even enumerates the query dialects, so the baseline is 3. The description's only added semantics is that the applicable language is determined by the chosen source via list_sources, which is useful but marginal; nothing is said about `params` or `options` formats beyond the schema.

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?

States a specific verb and resource (starts a read-only query) plus the exact return contract (a resultId, immediately), which signals the async pattern. It also distinguishes itself from the follow-up siblings get_result/read_result by naming them as the next step.

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?

Gives clear context: query language depends on the source (see list_sources), and the agent is told to follow up with get_result/read_result. It stops short of stating when NOT to use this tool (e.g. re-reading an existing resultId, or cancelling via cancel_query), so it is strong but not fully routing-aware.

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

read_resultRead a window of a resultA
Read-onlyIdempotent

Returns one window of a result inline: rows (offset, limit, optional columns), or for blob results the content of a referenced text file by line range (file, lineOffset, lineLimit) or an image (file). Long values are truncated; downloads keep full values.

NameRequiredDescriptionDefault
fileNoBlob results: the file path from a listing row
limitNo
offsetNo
columnsNoColumns or JSON paths (documents) to return
resultIdYes
lineLimitNo
lineOffsetNo

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already cover the read-only/idempotent/non-destructive profile, yet the description still adds non-obvious behavior: values are truncated when read inline but complete in downloads, and the returned payload shape depends on the result kind. It stops short of describing pagination boundaries or what happens past the end of a window.

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

Conciseness4/5

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

A single front-loaded sentence with the general case first, then the mode-specific exceptions, then the truncation caveat. Dense but every clause carries information; slightly cramped to read in one pass.

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 7-parameter, no-output-schema read tool, the description covers both result modes, the column selection option, and the truncation tradeoff — enough to invoke correctly. Gaps are minor: no hint about where resultId originates or what an out-of-range window returns.

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

Parameters4/5

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

Schema coverage is only 29%, so the description carries real weight — it maps offset/limit/columns to the row mode and file/lineOffset/lineLimit to the blob text mode, and clarifies that 'file' comes from a listing row. The one required parameter, resultId, gets no explanation in either place.

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

Purpose4/5

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

States a specific verb and resource ('Returns one window of a result inline') and immediately distinguishes the two payload shapes: tabular rows vs. blob files/images. It does not name a sibling tool, so an agent must infer the boundary with get_result and describe on its own.

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

Usage Guidelines3/5

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

Usage is implied by the mode enumeration — use offset/limit for row results, file + line range for blob text, file alone for images — and 'downloads keep full values' hints at when to prefer a download instead. However, no alternative tool is named and there is no explicit 'use this when' statement.

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. 6 tool updatesv0.0.0
    • First observedcancel_query
    • First observeddescribe
    • First observedget_result
    • First observedlist_sources
    • First observedquery
    • First observedread_result

TDQS

A3.6/5.0

Scored across 6 tools

Disambiguation4/5

The tools split cleanly along the query lifecycle: discovery (list_sources, describe), execution (query, cancel_query), monitoring (get_result) and retrieval (read_result), and each description explains its role in the sequence. The only mild overlap is get_result vs read_result, which sound similar (status/manifest vs inline rows) and rely on the descriptions to separate.

Naming Consistency4/5

Most names are consistent snake_case verb_noun (list_sources, get_result, read_result, cancel_query), which is predictable. The two bare verbs 'describe' and 'query' deviate slightly from the pattern but remain readable and verb-first.

Tool Count5/5

Six tools is well-scoped for a read-only federated query service; each one maps to a distinct lifecycle stage (list, describe, start, poll, read, cancel) with no filler. Nothing feels thin or bloated.

Completeness5/5

The full async query lifecycle is covered: source discovery, metadata drill-down, query start, status/progress polling, windowed row/blob retrieval with download manifest, and cancellation. No obvious dead ends for a read-only query server.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Gives AI agents read-only access to any QuestDB instance, enabling schema discovery, time-series querying, and monitoring of partitions, WAL state, symbols, and ingestion health through MCP.
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Provides fail-closed, read-only PostgreSQL and MongoDB access for AI agents via MCP, enabling structured data inspection and bounded queries without mutation capabilities.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables analytics agents to run validated read-only SQL against a warehouse API with enforced row limits, required JSON responses, and secret-safe audit metadata.
    MIT