datazen-api-mcp
v1.2.0
Published
Spec-driven MCP server exposing the DataZen DZFunctionAppCore HTTP API to LLM clients
Maintainers
Readme
DataZen API MCP
A spec-driven stdio MCP server that exposes the DataZen DZFunctionAppCore HTTP API as MCP
tools. Tool definitions live in dz-api-spec.json; the server
(src/index.ts) is generic and loads that spec at startup — regenerating the
spec never requires editing the server.
This build exposes the 17 approved endpoints listed in
datazen-tools.csv: agent health/status, job listing, history, logs and
output, variables, connection metadata, and job start/stop/activate. Everything else is
intentionally excluded — dashboard, job versions, change-log reads, dependencies, statistics,
/connections (returns secrets), agent-wide /output, and the dynamic-job endpoints (dynamic
jobs are not supported yet).
Configure
Set these as env vars (see .env.example) or in your MCP client config:
| Var | Required | Meaning |
|---|---|---|
| DZ_FUNCTIONS_BASE_URL | yes | Function app base URL, e.g. https://<funcapp>.azurewebsites.net/api. A trailing /api is tolerated and stripped — spec paths already start /api/{agentId}/.... |
| DZ_DEPLOYMENT | no | cloud (default) or selfhosted — see Cloud vs self-hosted. |
| DZ_AGENT_ID | cloud only | Agent GUID — substituted into every route and sent as X-API-DZ-AgentId. Required for cloud; ignored when self-hosted. |
| DZ_CLIENT_ID | yes | Client id for Basic auth. |
| DZ_SECRET | yes | Secret (or 32-char service token) for Basic auth. |
| DZ_SPEC_PATH | no | Path to the spec JSON. Default: dz-api-spec.json next to package.json. |
| DZ_TIMEOUT_MS | no | Per-call HTTP timeout. Default 120000. |
| DZ_LOG_DEBUG | no | 1 adds X-API-DZ-LogDebug: 1 for 401 diagnostics. |
These are the same vars used by datazen-pipeline-mcp, so one set of values
drives both — provided DZ_FUNCTIONS_BASE_URL ends in /api. That form works for both: this
server strips the trailing /api and re-adds it from the spec path, while datazen-pipeline-mcp
requires it. Omitting /api works here but breaks datazen-pipeline-mcp.
Missing a required var exits at startup with
datazen-api-mcp: missing required environment variable <NAME> on stderr.
Cloud vs self-hosted
DataZen is reachable two ways, and they differ in whether an agent id means anything. Cloud hosts many agents, so a call has to say which one it is for. A self-hosted deployment is the agent — there is nothing to address, and it needs no agent id:
| | DZ_DEPLOYMENT=cloud (default) | DZ_DEPLOYMENT=selfhosted |
|---|---|---|
| Route | /api/{agentId}/ping | /api/ping |
| X-API-DZ-AgentId header | sent | not sent |
| DZ_AGENT_ID | required | ignored (warns if set) |
| DZ_CLIENT_ID / DZ_SECRET | required | required |
Self-hosted still authenticates — only the agent addressing goes away:
{ "mcpServers": { "datazen-api": {
"command": "npx",
"args": ["-y", "datazen-api-mcp"],
"env": {
"DZ_FUNCTIONS_BASE_URL": "http://localhost:7071/api",
"DZ_DEPLOYMENT": "selfhosted",
"DZ_CLIENT_ID": "...",
"DZ_SECRET": "..."
}
} } }DZ_DEPLOYMENT is validated: any value other than cloud or selfhosted exits at startup rather
than falling back to a default, so a typo like self-hosted fails immediately instead of becoming
a puzzling 404 on the first tool call.
Renamed vars.
DZ_AGENT_ID,DZ_CLIENT_IDandDZ_SECRETwere previouslyENZO_AGENT_ID,ENZO_CLIENT_IDandENZO_SECRET. The old names still work but log a deprecation warning on startup.
This server does not load
.env— there is nodotenvdependency..env.exampledocuments the vars; supply them via the real environment or your MCP client'senvblock.
The secret ends up in your client config file — use a dedicated service token, not an admin clientId:secret, when possible. With Hermes, prefer
${VAR}interpolation (see below) so no secret is written toconfig.yaml.
Build & test
npm install
npm run build
npm run smoke # self-contained: no live function app needed
node smoke.mjs --spec dz-api-spec.json # validate the generated specRegister
Published to npm as datazen-api-mcp. Run it via
npx -y datazen-api-mcp (no local build/checkout required), or launch it by absolute path to
dist/index.js after npm run build if you're working from a clone of this repo.
Hermes agent
Hermes agent registers MCP
servers in an mcp_servers: block in $HERMES_HOME/config.yaml (or ~/.hermes/config.yaml when
HERMES_HOME is unset):
mcp_servers:
datazen-api:
command: "npx"
args: ["-y", "datazen-api-mcp"]
env:
DZ_AGENT_ID: ${DZ_AGENT_ID}
DZ_CLIENT_ID: ${DZ_CLIENT_ID}
DZ_SECRET: ${DZ_SECRET}
DZ_FUNCTIONS_BASE_URL: ${DZ_FUNCTIONS_BASE_URL}(Use command: "node", args: ["<absolute-path>/dist/index.js"] instead if you're running from a
local clone after npm run build.)
Keep credentials out of config.yaml: ${VAR} placeholders are interpolated from the
environment, including $HERMES_HOME/.env, which Hermes loads at startup. Put the real values
there:
# $HERMES_HOME/.env
DZ_AGENT_ID=...
DZ_CLIENT_ID=...
DZ_SECRET=...
DZ_FUNCTIONS_BASE_URL=https://<funcapp>.azurewebsites.net/apiAn unset variable is not an error — Hermes leaves the literal string
${DZ_AGENT_ID}in place, which surfaces later as a confusing401from the function app. If auth fails, check.envfirst.
Or add it from the CLI. --args consumes the rest of the line, so it must come last:
hermes mcp add datazen-api --command npx --args -y datazen-api-mcpThis registers command/args only. hermes mcp add does accept --env KEY=VALUE (which must
come before --args), but that puts credentials in your shell history — add the env: block
with ${VAR} placeholders as above instead.
Then verify it connects, and reload after any config.yaml change:
hermes mcp test datazen-api # confirm it connects and lists the tools
# ...and inside `hermes chat`:
/reload-mcp # re-read config.yaml without restarting the sessiondz_job_start, dz_job_stop and dz_job_active change agent state. To expose only the read-only
tools, add a tools.include whitelist. Prefer include over exclude here: regenerating the spec
can add tools, and a whitelist won't surface them to the model until you opt in.
tools:
include: [dz_ping, dz_version, dz_status, dz_jobs, dz_jobs_summary, dz_job_status,
dz_job_history, dz_job_log, dz_job_output, dz_job_info, dz_job_changelogs,
dz_variables, dz_variable_get, dz_connections_info]If your Hermes install was built without MCP support, enable it once with
cd $HERMES_HOME/hermes-agent && uv pip install -e ".[mcp]".
Claude Code
claude mcp add datazen-api \
-e DZ_FUNCTIONS_BASE_URL=https://... -e DZ_AGENT_ID=... -e DZ_CLIENT_ID=... -e DZ_SECRET=... \
-- npx -y datazen-api-mcpClaude Desktop
claude_desktop_config.json:
{ "mcpServers": { "datazen-api": {
"command": "npx",
"args": ["-y", "datazen-api-mcp"],
"env": { "DZ_FUNCTIONS_BASE_URL": "https://...", "DZ_AGENT_ID": "...", "DZ_CLIENT_ID": "...", "DZ_SECRET": "..." }
} } }Regenerating the spec
Re-run the /datazen-api-mcp skill. It rereads the HTTP-triggered functions under
DZiPaaS/DZFunctionAppCore/Functions/, overwrites dz-api-spec.json, and re-runs the smoke test.
Do not hand-edit the server for spec changes.
