Periscope MCP
OfficialPeriscope MCP lets you inspect configured data sources and run strictly read-only queries whose results you page through or download.
List available data sources with their query languages, options, limits, and active read-only protections.
Browse metadata step by step: schemas, tables, columns, containers, blob prefixes, Log Analytics tables, and Redis key prefixes.
Start read-only queries against PostgreSQL, Oracle, SQL Server/Azure SQL, Cosmos DB, Blob Storage, Log Analytics/Application Insights, Redis, KIM, or declared REST APIs; non-read-only queries are rejected.
Get result status, progress, schema, row counts, window limits, and a download manifest URL.
Read one result window inline, including rows, text-file line ranges, or images for blob results.
Cancel running queries; already-written result parts remain readable until expiry.
Download full result files for processing with tools such as curl, DuckDB, jq, or pandas.
Provides strictly read-only query access to PostgreSQL databases, allowing agents to run a single SELECT statement and retrieve paged or downloadable results in Parquet format.
Provides strictly read-only query access to Redis standalone or cluster instances, allowing agents to run one read-only Redis command per query or use SCAN as a key export, with results returned as gzipped NDJSON.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Periscope MCPquery appdata for 2026 claims and show the first 20 rows"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 | Parquet |
Oracle Database (19c+) | SQL (one | Parquet |
SQL Server / Azure SQL | T-SQL (one | 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; | NDJSON (gzip) |
KIM | Node or record query language | NDJSON (gzip); offloaded contents referenced in place |
Any HTTP API ( | 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 |
| Sources you may query, with query language, dialect hints, options and limits |
| Metadata, drilled down step by step (schemas → tables → columns, containers → sampled schemas, prefixes, …) |
| Starts a query and returns a |
| Status, schema, row counts, window limits and one manifest URL for downloads |
| One window of rows, or (blob, KIM and REST results) lines of a text file or an image |
| 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 MBM="<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.periscopeRegister it with Claude Code:
claude mcp add periscope -- node /path/to/periscope-mcp/src/index.ts --env-file /path/to/.env.periscopeIn 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.0Deploy
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:6380The 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 |
Blob Storage | Only list/read operations implemented; pipeline guard lets only |
Log Analytics | Query API only (it has no write operations); control commands rejected |
PostgreSQL |
|
Oracle |
|
SQL Server | Transaction that is always rolled back; strict T-SQL lexer (single |
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 |
REST | Operator-declared allowlist of |
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 buildLicense
MIT
Available Tools
6 toolscancel_queryCancel a running queryCRead-onlyIdempotent
Stops a running query. Parts already written stay readable until the result expires.
| Name | Required | Description | Default |
|---|---|---|---|
| resultId | Yes |
TDQS
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.
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.
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.
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.
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.
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 metadataARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Level to describe, e.g. "public.orders", "main/claims", "invoices/2026/" | |
| filter | No | Name pattern, * and ? wildcards | |
| source | Yes | Source name from list_sources |
TDQS
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.
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.
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.
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.
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.
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 metadataBRead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| wait | No | Seconds to wait for a status change | |
| resultId | Yes |
TDQS
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.
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.
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.
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.
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.
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 sourcesARead-onlyIdempotent
Lists the data sources you may query: name, type, query language, dialect hints, options, limits and the active read-only layers.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 queryARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | SQL, Cosmos SQL, KQL, a blob glob or a Redis command | |
| params | No | Bind parameters (format depends on the source) | |
| source | Yes | Source name from list_sources | |
| options | No | Type-specific options (see list_sources) |
TDQS
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.
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.
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.
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.
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.
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 resultARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| file | No | Blob results: the file path from a listing row | |
| limit | No | ||
| offset | No | ||
| columns | No | Columns or JSON paths (documents) to return | |
| resultId | Yes | ||
| lineLimit | No | ||
| lineOffset | No |
TDQS
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.
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.
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.
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.
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.
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.
6 tool updates
v0.0.0- First observed
cancel_query - First observed
describe - First observed
get_result - First observed
list_sources - First observed
query - First observed
read_result
TDQS
Scored across 6 tools
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.
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.
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.
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
Related MCP Connectors
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
Query 40 databases from Claude, ChatGPT, or Cursor — on any device. Read-only, encrypted, audited.
Read-only finance and operations controls for AI agents with evidence and safe next actions.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to securely query VAST Data databases for schema, metadata, and sample data via read-only SQL and MCP resources.MIT
- AlicenseNot gradedqualityBmaintenanceGives 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
- AlicenseNot gradedqualityAmaintenanceProvides fail-closed, read-only PostgreSQL and MongoDB access for AI agents via MCP, enabling structured data inspection and bounded queries without mutation capabilities.MIT
- AlicenseNot gradedqualityCmaintenanceEnables 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