Programmatic access (API · MCP · CLI)¶
Beyond the Windows review app and the Fabric dashboard, the curated triple is reachable from any tool, interface, or harness through three headless surfaces — all built on one shared client.
flowchart TD
subgraph clients [Any consumer]
web[Web / Power BI / Excel / curl]
ai[AI agents & harnesses<br/>Claude · Copilot · Cursor]
sh[Scripts · pipelines · terminals]
win[WinForms review app]
end
web --> api[FdaObservability.Api<br/>REST + OpenAPI]
ai --> mcp[FdaObservability.Mcp<br/>MCP stdio server]
sh --> cli[FdaObservability.Cli<br/>fda-obs]
win --> core
api --> core[(FdaObservability.Core<br/>FdaKustoClient)]
mcp --> core
cli --> core
core --> eh[(Eventhouse · FdaInteractions<br/>stored KQL functions)]
All four projects live under src/. Every read goes through the same stored functions
(SearchFdaInteractions, GetFdaInteraction, FdaTuningSummary, FdaFailures, FdaTopQuestions)
plus an inline KPI summarize — so the API, MCP, CLI, and desktop app return identical data.
Shared core — FdaObservability.Core¶
A cross-platform (net8.0) library holding the models, the FdaKustoClient, and a pluggable
IKustoTokenProvider. Authentication is injected, so the same client serves:
| Host | Token provider |
|---|---|
| Desktop review app | interactive MSAL (the reviewer's identity) |
| API / MCP / CLI | DefaultAzureCredentialTokenProvider |
DefaultAzureCredential resolves — in order — an environment service principal, managed/workload identity, Azure
CLI, Azure PowerShell, and Visual Studio. That single chain runs unchanged in Azure, containers, CI, and on a dev
box. The caller (or its host) needs Viewer on the observability KQL database.
Common configuration
All headless surfaces read the same settings — Eventhouse Query URI and database — from environment variables or config. See the configuration reference.
REST API — FdaObservability.Api¶
An ASP.NET Core minimal API with OpenAPI/Swagger. Consumable by web UIs, Power BI, Excel, Postman, curl, or any language.
Endpoints¶
| Method & path | Description |
|---|---|
GET /health |
Connectivity + schema probe |
GET /api/interactions |
Search — term, from, to, agent, user, status, onlyMatched, maxRows |
GET /api/interactions/{id} |
One interaction by id (404 if absent) |
GET /api/kpis |
KPI roll-up — from, to |
GET /api/tuning-summary |
Per-agent × day tuning KPIs — from, to |
GET /api/failures |
Interactions needing attention — from, to |
GET /api/top-questions |
Top question patterns — from, to, topN |
Interactive docs at /swagger; the OpenAPI document at /swagger/v1/swagger.json lets any client generate a typed
SDK.
Run¶
docker build -t fda-observability-api -f src/FdaObservability.Api/Dockerfile .
docker run -p 8080:8080 \
-e FdaObservability__EventhouseQueryUri=https://<cluster>.<region>.kusto.fabric.microsoft.com \
-e FdaObservability__Database=FDA_Observability_EH \
-e AZURE_TENANT_ID=... -e AZURE_CLIENT_ID=... -e AZURE_CLIENT_SECRET=... \
fda-observability-api
Securing it¶
Set Api:ApiKey (config or Api__ApiKey env). When present, every route except /health and /swagger
requires a matching X-Api-Key header. When not set, the API logs a warning and stays open — front it with a
gateway / APIM / Entra-auth proxy, or set the key, before exposing it externally. CORS is open by default for
browser clients; tighten it for production.
MCP server — FdaObservability.Mcp¶
A Model Context Protocol stdio server (protocol 2024-11-05) exposing each query as a tool, so AI agents and
harnesses (Claude Desktop/Code, Copilot, Cursor, …) can query interactions natively. It is dependency-light —
hand-rolled JSON-RPC, no preview packages — and logs only to stderr so the protocol stream stays clean.
Tools¶
search_interactions · get_interaction · kpis · tuning_summary · failures · top_questions
Register (Claude Desktop / Code example)¶
{
"mcpServers": {
"fda-observability": {
"command": "dotnet",
"args": ["C:/path/to/FdaObservability.Mcp.dll"],
"env": {
"FDA_EVENTHOUSE_URI": "https://<cluster>.<region>.kusto.fabric.microsoft.com",
"FDA_DATABASE": "FDA_Observability_EH"
}
}
}
}
Then ask the agent things like "top failing FDA questions this week" or "show the executed DAX for interaction <id>" and it calls the tools directly.
CLI — FdaObservability.Cli (fda-obs)¶
A cross-platform dotnet tool for scripts, pipelines, and terminals. JSON output by default (pipe to jq);
--table gives a human view for search/failures.
# install (after packing / from a feed)
dotnet tool install -g FdaObservability.Cli
export FDA_EVENTHOUSE_URI=https://<cluster>.<region>.kusto.fabric.microsoft.com
fda-obs ping
fda-obs search --term revenue --status Error --table
fda-obs kpis --from 2026-06-01 --to 2026-06-20
fda-obs failures --from 2026-06-13 --table
fda-obs top-questions --top 25 | jq '.[].Example'
| Command | Purpose |
|---|---|
ping |
Connectivity + schema probe |
search |
Search interactions (--term --from --to --agent --user --status --matched --max --table) |
get <id> |
One interaction by id |
kpis |
KPI roll-up (--from --to) |
tuning |
Per-agent × day tuning KPIs (--from --to) |
failures |
Interactions needing attention (--from --to --table) |
top-questions |
Top question patterns (--from --to --top) |
Connection comes from --cluster/--database flags or FDA_EVENTHOUSE_URI/FDA_DATABASE.
Governance¶
These surfaces expose prompt/answer text and DAX, which may be sensitive. The same rules as the review app apply: authenticate callers, scope KQL-database Viewer to authorized identities, and prefer the API's key/gateway or per-user (on-behalf-of) auth when surfacing data to a broad audience. RLS on the Eventhouse, if configured, applies to whichever identity the token represents.