Skip to main content
Glama

nifi-mcp

An MCP server and a CLI for the Apache NiFi REST API, sharing one async client.

  • nifi-mcp — exposes NiFi to an LLM agent over stdio (59 tools).

  • nifi — the same capabilities from a terminal, with tables by default and --json for scripting.

Built and verified against NiFi 1.15.3. See SPEC.md for the design decisions.

Install

uv sync
source .venv/bin/activate
nifi doctor          # verify connectivity, auth, host header, and version

Related MCP server: n8n MCP Server

Configuration

Settings resolve in this order: environment → .env → defaults. Copy .env.example to .env to start. The important ones:

Variable

Default

Meaning

NIFI_BASE_URL

https://172.21.80.1:8443

The NiFi instance

NIFI_HOST_HEADER

localhost:8443

Host header override — see below

NIFI_ALLOW_WRITE

false

Gates every mutating MCP tool

NIFI_VERIFY_SSL

false

TLS verification (dev server is self-signed)

Credentials

Credentials come from the environment, and only from the environment:

Variable

Meaning

NIFI_USERNAME

Login identity

NIFI_PASSWORD

Login password

Set them however your environment does it — exported in the shell, in the MCP server's env block, or in a .env file next to pyproject.toml (gitignored). The process is handed its secret; it never goes looking for one on disk, so what it authenticates with does not depend on the directory it was launched from.

export NIFI_USERNAME='7090b632-6bb7-4a11-b6bc-c308a331e559'
export NIFI_PASSWORD='...'
nifi doctor

NiFi in single-user mode generates the pair on its first run and logs it once, to logs/nifi-app.log on the NiFi host:

grep 'Generated \(Username\|Password\)' logs/nifi-app.log
... o.a.n.a.s.SingleUserCredentialsGenerator Generated Username [<uuid>]
... o.a.n.a.s.SingleUserCredentialsGenerator Generated Password [<password>]

Copy those two values into the variables above. If the line has already been rotated out of the log, set a pair of your own instead:

./bin/nifi.sh set-single-user-credentials <username> <password>   # on the NiFi host

The values are exchanged for a JWT at POST /access/token; the token is cached, its exp is decoded, and it is refreshed about 60s before expiry (and once more on any 401). The password itself is sent only at login, is held in a SecretStr, and never appears in logs or repr().

The host header

NiFi validates the Host header against nifi.web.proxy.host. Reaching the instance by an address that is not in that allowlist — for example from WSL at 172.21.80.1 when NiFi runs on the Windows host — gets this:

The request contained an invalid host header [172.21.80.1:8443]

NIFI_HOST_HEADER rewrites the header without changing where the connection goes. Set it to an empty value if your NiFi accepts the real host. nifi doctor diagnoses this case.

CLI

nifi doctor                       # connectivity + auth + version diagnostics
nifi about                        # version and identity
nifi status                       # flow health at a glance
nifi bulletins                    # recent warnings and errors

nifi pg tree                      # the flow, as a tree
nifi pg list|get|create|delete|start|stop
nifi processor list|get|create|update|start|stop|run-once|terminate|delete
nifi connection list|get|create|delete
nifi queue list|peek|empty
nifi cs list|get|create|enable|disable|delete
nifi param list|get|create|set|delete
nifi types search|show
nifi provenance query|lineage
nifi version status|registries|commit
nifi system diagnostics|counters|cluster

Global flags: --json (raw JSON), --yes (skip destructive prompts), --dry-run (show what would happen), --verbose (log HTTP to stderr).

Exit codes: 0 ok, 1 NiFi error, 2 usage error, 3 connection or auth failure.

Example: build and run a flow

GID=$(nifi --json pg create demo | jq -r .id)
GEN=$(nifi --json processor create GenerateFlowFile --pg $GID --name gen \
        --period "1 sec" --prop "File Size=64B" | jq -r .id)
LOG=$(nifi --json processor create LogAttribute --pg $GID --name log \
        --y 300 --auto-terminate success | jq -r .id)
nifi connection create $GEN $LOG -r success --pg $GID
nifi pg start $GID
nifi connection list $GID          # watch the queue
nifi pg stop $GID
nifi --yes pg delete $GID

Component types accept short names (GenerateFlowFile); they are resolved against the instance's catalogue. nifi types search kafka finds what is installed.

MCP server

Register with Claude Code:

claude mcp add nifi -- /home/alexsaez/projects/nifi-mcp/.venv/bin/nifi-mcp

Or add to your MCP client config directly:

{
  "mcpServers": {
    "nifi": {
      "command": "/home/alexsaez/projects/nifi-mcp/.venv/bin/nifi-mcp",
      "env": {
        "NIFI_BASE_URL": "https://172.21.80.1:8443",
        "NIFI_HOST_HEADER": "localhost:8443",
        "NIFI_USERNAME": "7090b632-6bb7-4a11-b6bc-c308a331e559",
        "NIFI_PASSWORD": "...",
        "NIFI_ALLOW_WRITE": "false"
      }
    }
  }
}

Note that env values in this config file are stored in plain text, so it should be readable only by you. To keep the password out of the file entirely, export NIFI_USERNAME / NIFI_PASSWORD in the shell that launches the client and reference them as "${NIFI_PASSWORD}" if your client expands variables, or omit them here and let the server inherit them.

If the configuration is wrong the server still starts and each tool call returns a structured error naming the problem. It does not exit, because a process that dies before the transport is established shows up in the client as nothing more than "connection closed". nifi doctor diagnoses the same things from a terminal.

Write safety

Two layers, because the caller is a model:

  1. NIFI_ALLOW_WRITE — when false (the default), mutating tools are not registered. The model sees 28 read tools and cannot attempt a write at all. Set it to true to register all 59.

  2. confirm — the 9 destructive tools (deletes, nifi_empty_queue, nifi_terminate_processor, nifi_stop_version_control) take confirm. Called without it, they report exactly what would be affected and change nothing.

Tools also carry MCP annotations (read_only_hint, destructive_hint) so clients can apply their own policy.

The CLI is deliberately not gated by NIFI_ALLOW_WRITE — a human at a terminal is already the confirmation. Destructive commands prompt instead; --yes skips the prompt.

Development

pytest                              # 49 unit tests, no server needed
NIFI_INTEGRATION=1 pytest           # + 5 live tests against NIFI_BASE_URL
ruff check src tests && mypy src/nifi_mcp

Integration tests build a real flow inside a scratch process group and tear it down afterwards; they never touch anything outside it.

Layout

src/nifi_mcp/
├── config.py       settings from environment and .env
├── errors.py       typed exceptions with actionable hints
├── client.py       async client: auth, host header, revisions, retries
├── compat.py       version detection, 1.x/2.x differences
├── summaries.py    trim NiFi entities to the useful fields
├── api/            one module per resource family
├── tools/          MCP tool definitions (read_tools / write_tools)
├── server.py       MCP entry point
└── cli.py          Typer CLI

Notes on the NiFi API

Three things the client handles so callers don't have to:

  • Revisions. Every mutating call must echo the revision it read (optimistic locking). update_with_revision fetches, merges, and retries once on a 409.

  • Async request endpoints. Queue listings, drop requests, provenance queries, and parameter updates are submitted, polled, then deleted. Some of these are POSTs that only read; those bypass the write gate explicitly.

  • Display strings. NiFi returns queuedCount as a formatted string ("1,234"). The summaries expose numeric count/bytes alongside the _display variants.

Known gaps

  • NiFi 2.x auth (OIDC/SAML) — the compat layer detects the version, but 2.x is untested.

  • Version-control writes are implemented but unexercised: no registry is configured on the dev server.

  • Cluster management, tenants/policies, and templates are out of scope (SPEC.md §11).

Available Tools

28 tools
nifi_aboutA
Read-onlyIdempotent

Version, build, and identity information for the connected NiFi instance.

Backed by GET /flow/about and /flow/current-user. Use this first to confirm connectivity and learn which NiFi generation you are talking to.

Inputs: no named parameters. Returns: object

nifi_countersB
Read-onlyIdempotent

Flow counters and their current values.

Backed by GET /counters.

Inputs: no named parameters. Returns: result

nifi_find_component_typesA
Read-onlyIdempotent

Search the catalogue of installable component types by name, tag, or description.

Backed by GET /flow/processor-types and /flow/controller-service-types. kind is "processor", "controller_service", or "both". Use this to find the correct type before calling nifi_create_processor.

Inputs: kind, limit, query. Returns: result

nifi_flow_statusA
Read-onlyIdempotent

Aggregate controller status: running/stopped/invalid counts, queued data, bulletins.

Backed by GET /flow/status and /flow/bulletin-board. The fastest way to answer "is anything wrong right now?".

Inputs: no named parameters. Returns: object

nifi_flow_treeA
Read-onlyIdempotent

Recursive process-group tree with processors and connections.

Backed by GET /flow/process-groups/{id}. depth bounds the recursion; groups beyond it are listed by id and name only. Start here to understand a flow's shape.

Inputs: depth, pg_id, include_components. Returns: object

nifi_get_connectionC
Read-onlyIdempotent

One connection: endpoints, routed relationships, queue depth, prioritizers.

Backed by GET /connections/{id}.

Inputs: connection_id (required), verbose. Returns: object

nifi_get_controller_serviceB
Read-onlyIdempotent

One controller service: properties, state, and what references it.

Backed by GET /controller-services/{id}. Sensitive values are masked.

Inputs: service_id (required), verbose. Returns: object

nifi_get_flowfileB
Read-onlyIdempotent

Metadata and attributes for one queued flowfile.

Backed by GET /flowfile-queues/{id}/flowfiles/{uuid}.

Inputs: connection_id (required), flowfile_uuid (required). Returns: object

nifi_get_flowfile_contentA
Read-onlyIdempotent

Content of one queued flowfile, truncated to max_bytes.

Backed by GET /flowfile-queues/{id}/flowfiles/{uuid}/content. Binary content is returned base64-encoded, indicated by the encoding field.

Inputs: connection_id (required), flowfile_uuid (required), max_bytes. Returns: object

nifi_get_lineageA
Read-onlyIdempotent

Lineage graph for a flowfile, as nodes and links.

Backed by POST /provenance/lineage, polled to completion. Supply either flowfile_uuid or event_id.

Inputs: event_id, direction, flowfile_uuid. Returns: object

nifi_get_parameter_contextB
Read-onlyIdempotent

One parameter context with its parameters and bound process groups.

Backed by GET /parameter-contexts/{id}.

Inputs: context_id (required). Returns: object

nifi_get_process_groupB
Read-onlyIdempotent

A process group's identity, component-state counts, and version-control state.

Backed by GET /process-groups/{id}. Pass "root" for the top-level group.

Inputs: pg_id, verbose. Returns: object

nifi_get_process_group_contentsA
Read-onlyIdempotent

The direct contents of one process group: children, processors, connections, ports.

Backed by GET /flow/process-groups/{id}. Unlike nifi_flow_tree this does not recurse, so it is the cheaper choice when inspecting a single group.

Inputs: pg_id, verbose. Returns: object

nifi_get_processorA
Read-onlyIdempotent

One processor in full: properties, scheduling, relationships, validation errors.

Backed by GET /processors/{id}. Sensitive property values are masked.

Inputs: processor_id (required), verbose. Returns: object

nifi_get_property_descriptorA
Read-onlyIdempotent

Descriptor for one processor property: type, default, and allowable values.

Backed by GET /processors/{id}/descriptors. Use this before setting a property whose valid values you are unsure of.

Inputs: processor_id (required), property_name (required). Returns: object

nifi_get_provenance_eventB
Read-onlyIdempotent

One provenance event with its full before/after attribute set.

Backed by GET /provenance-events/{id}.

Inputs: event_id (required). Returns: object

nifi_get_version_infoB
Read-onlyIdempotent

Version-control state of a process group: registry, bucket, flow, and version.

Backed by GET /process-groups/{id}.

Inputs: pg_id. Returns: object

nifi_list_bulletinsA
Read-onlyIdempotent

Recent bulletins — NiFi's warning and error notices.

Backed by GET /flow/bulletin-board. The first place to look when a processor is failing but its configuration looks correct.

Inputs: limit. Returns: result

nifi_list_connectionsA
Read-onlyIdempotent

Connections in a process group with queue depth and backpressure settings.

Backed by GET /process-groups/{id}/connections. queued.count and queued.bytes are numeric; the _display variants are formatted strings.

Inputs: pg_id. Returns: result

nifi_list_controller_servicesB
Read-onlyIdempotent

Controller services visible to a process group, with state and referencing components.

Backed by GET /flow/process-groups/{id}/controller-services.

Inputs: pg_id, include_ancestors. Returns: result

nifi_list_parameter_contextsA
Read-onlyIdempotent

All parameter contexts and their parameters. Sensitive values are masked.

Backed by GET /flow/parameter-contexts.

Inputs: no named parameters. Returns: result

nifi_list_processorsB
Read-onlyIdempotent

Processors in a process group with run status, validity, and throughput.

Backed by GET /process-groups/{id}/processors.

Inputs: pg_id, include_descendants. Returns: result

nifi_list_registry_clientsB
Read-onlyIdempotent

NiFi Registry clients configured on this instance.

Backed by GET /controller/registry-clients.

Inputs: no named parameters. Returns: result

nifi_process_group_statusA
Read-onlyIdempotent

Throughput and queue statistics for a process group, recursively.

Backed by GET /flow/process-groups/{id}/status.

Inputs: pg_id. Returns: object

nifi_provenance_queryA
Read-onlyIdempotent

Search provenance events to trace what happened to data.

Backed by POST /provenance, polled to completion. Dates use NiFi's MM/dd/yyyy HH:mm:ss format. This reads history only.

Inputs: end_date, filename, event_type, start_date, +3 more. Returns: object

nifi_queue_listingB
Read-onlyIdempotent

List the flowfiles currently queued on a connection.

Backed by POST /flowfile-queues/{id}/listing-requests, polled to completion. This reads state only; it does not modify the queue.

Inputs: connection_id (required), limit. Returns: object

nifi_system_diagnosticsA
Read-onlyIdempotent

JVM heap, thread counts, repository disk usage, and uptime.

Backed by GET /system-diagnostics. Use when diagnosing performance or memory pressure rather than flow logic.

Inputs: verbose. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.2/5.0

    Scored across 28 tools

    Disambiguation4/5

    Most tools target distinct NiFi resources and the descriptions explicitly differentiate overlapping process-group views (tree vs contents vs status). However several status/diagnostic tools (flow_status, process_group_status, get_process_group) and provenance/lineage tools share enough conceptual ground that misselection is possible for less careful agents.

    Naming Consistency4/5

    All tools use a consistent nifi_ prefix and snake_case. The dominant pattern is verb_noun (get_, list_, search_, find_, query_), with a few noun-only deviations (about, counters, flow_status, flow_tree, system_diagnostics) that are still readable.

    Tool Count2/5

    28 tools exceed the 25-tool threshold for 'too many' per the rubric, especially given the apparent read-only scope. While they cover many subresources, the set could be consolidated or split into focus modes.

    Completeness2/5

    The surface is almost entirely read-only: no create/update/delete/start/stop tools for processors, connections, controller services, or process groups. Descriptions even reference nifi_create_processor and setting processor properties, which are absent, indicating significant gaps for flow management.

    Maintenance

    ActivityStale
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      B
      quality
      C
      maintenance
      Enables AI assistants to interact with n8n workflow automation instances through the REST API. Supports workflow management, execution control, tag organization, execution history monitoring, and webhook management.
      19
      148 npm
      5
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI agents to manage n8n workflow automation instances through tools for workflow CRUD operations, execution monitoring, and webhook triggering. It facilitates programmatic interaction with n8n instances via the n8n API with AI-optimized descriptions and error handling.
      62
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      Enables LLMs to programmatically control n8n via natural language for automated workflow creation, modification, and execution management.
      1
      -