In-depth architectural comparison of the MCP Server Malcolm and MCP Server MCP servers. Compare execution transports, security boundaries, tool capabilities, quality scores, and ready-to-paste client installation snippets for Claude, Cursor, Windsurf, and VS Code.
At a Glance & Executive Verdict
MCP Server Malcolm
Security · Local stdio
Quality: 57/100 (Good) | Auth: other
MCP Server
Security · Local stdio
Quality: 63/100 (Good) | Auth: No auth required
Verdict Summary: Choose MCP Server Malcolm if you need specialized Security tools running via a local process. Choose MCP Server if your workspace requires Security integration with local subprocess execution. Both servers can be configured concurrently in your client's mcpServers manifest.
Which MCP Server Should You Choose?
Choose MCP Server Malcolm when:
You need dedicated capabilities in the Security domain.
You prefer local stdio subprocess transport architecture.
Your security boundary fits: other (Free / Open Source).
The first MCP server for Malcolm, the open-source network traffic analysis suite (Zeek + Suricata + Arkime + OpenSearch + NetBox). Gives AI agents structured, threat-hunting access: search and aggregate traffic, discover fields, query Suricata alerts, browse Arkime sessions, and resolve NetBox assets. Read-only by default; opt-in, audited write classes for alerts, tagging, hunts, and PCAP upload. pip install mcp-server-malcolm
55 tools for verified public data lookups — CVE/SBOM security audits, licence compliance, patents, federal contracts, NPI provider lookups, nonprofit 990 filings, and domain intelligence. No API key required.
Tools & Capabilities Breakdown
MCP Server Malcolm Tools (51)
search_dsl
Run a raw OpenSearch DSL query and return its hits plus aggregations.
Use this for full DSL control over the query and aggregation bodies. When you
only need a match count and not the documents, use count. For Malcolm's
simpler field-filter syntax instead of raw DSL, use malcolm_search.
Aggregations honor the time filter inside the DSL body, so there is no hidden
default time window. Returns the raw OpenSearch _search response.
Both input guards run before any request leaves this server: malformed
query_dsl, and an index containing /, ? or .., are refused as input
errors rather than costing an upstream scan. When the query is easier to
say as an Arkime expression, compile it with arkime_build_query and hand
the index and query_dsl it returns straight to this tool — serialise its
query_dsl object to a JSON string first, which is what this parameter
declares.
count
Count documents matching a DSL query clause, without returning the documents.
Use this instead of search_dsl when you only need the number of matches, not
the documents themselves. Note the query_dsl shape differs from search_dsl's —
the schema says how. Returns the raw OpenSearch _count response
({"count": N, ...}).
This tool takes no time arguments and applies no default window, so a
bare call counts everything the index still holds, which on any real
capture is millions of documents. Bound it with a range clause inside
query_dsl, use malcolm_search when you want a human-readable time range,
or arkime_sessions_summary when you want byte and packet totals beside
the count.
Ready-to-Paste Client Configurations
Paste either (or both) of these JSON server blocks into your client config file (e.g. claude_desktop_config.json or ~/.cursor/mcp.json).
MCP Server Malcolm is categorized under Security and uses a local stdio subprocess. In contrast, MCP Server belongs to Security using local stdio subprocess. Select MCP Server Malcolm when you need capabilities focused on security and MCP Server when you require tools for security.
List indices with their health, status, and document count.
Use this to discover which indices exist before querying one. For the field
schema (field names and types) of a single index, use index_mapping instead;
for cluster-wide health rather than per-index status, use cluster_health.
Returns a JSON array, one object per index, with name, health, status, and doc
count.
This reads OpenSearch's index list directly, so Malcolm's own internals
come back beside the traffic, and most of what is listed holds no
network data at all (.kibana_1, .opendistro_security, the arkime_*_v*
config indices, top_queries-*). The traffic is in the arkime_sessions3-*
indices alone; Arkime opens a new one per day, so their number grows and
the newest is usually still empty — read "docs.count" rather than the
name to find the one carrying the capture. A pattern matching nothing
returns an empty array, not an error. "health" is a shard-replication
fact and says nothing about whether capture is still arriving —
malcolm_data_coverage answers that.
index_mapping
Return one index's field mapping: every field name and its OpenSearch type.
Use this to learn what fields an index holds and how they are typed before
writing a DSL query against it. To list which indices exist rather than inspect
one index's schema, use list_indices. For Malcolm's non-standard field names
across all indices, malcolm_field_search is easier than reading raw mappings.
Returns the raw OpenSearch _mapping response; a non-existent index yields an
OpenSearch error in the response body.
A wildcard returns one mapping block per matching index rather than a
merged one, and each block repeats the whole schema: "arkime_sessions3-*"
costs roughly a megabyte of JSON, growing by another block every day
Arkime opens a new index. Name ONE index when you only need the schema —
the blocks are near-identical. The types it reports are OpenSearch's own
(keyword, long, text), while malcolm_field_search reports Malcolm's names
for the same fields (string, integer) — so come here only when the
OpenSearch type is what you need.
cluster_health
Report OpenSearch cluster health: green/yellow/red status plus node and shard counts.
This checks the storage backend (OpenSearch) itself, cluster-wide. To check
whether the Malcolm API is reachable, use malcolm_ping; for the readiness of
Malcolm's individual services, use malcolm_service_status; for per-index
status rather than the whole cluster, use list_indices. Returns the raw
OpenSearch _cluster/health document.
This is a storage-layer answer only: every shard allocated says nothing
about whether packets are still being captured or parsed. Measured on
Malcolm v26.07.1 (single node) the steady state is green with
number_of_nodes=1 and unassigned_shards=0, so treat yellow as something
to explain rather than as normal. For whether data is still arriving use
malcolm_data_coverage; for whether a capture node is dropping packets use
arkime_node_stats.
malcolm_search
Search Malcolm's indexed network traffic using Malcolm's simple filter dict.
Use this for field-based filtering with human-readable time ranges. To
search with Arkime expression syntax instead, or when you need a session
id to feed arkime_session_pcap / arkime_add_tags afterward, use
arkime_sessions (only its rows carry that id). For raw OpenSearch DSL,
use search_dsl. Confirm field names with malcolm_field_search first —
Malcolm uses non-standard names. Returns the raw Malcolm /mapi/document
response (matching documents); when nothing matched and a filter names a
field Malcolm does not index, the correct field name is reported above
the response.
Two defaults to know before the first call: with no time_from this
searches ALL retained history, where malcolm_aggregate covers only the
last 24 hours; and filter values are matched exactly, so any wildcard or
substring has to go to search_dsl instead.
malcolm_aggregate
Aggregate network traffic into top-N value buckets for one or more fields.
Use this to count distinct values (top talkers, protocol distribution)
rather than fetch documents — for the documents themselves use
malcolm_search. For distinct values of a single field with less setup,
malcolm_field_values is simpler. Returns the raw Malcolm /mapi/agg
response (bucket keys with doc counts); when no buckets came back and an
aggregated or filtered field is not one Malcolm indexes, the correct
field name is reported above the response.
With no time_from this covers only the LAST 24 HOURS, unlike
malcolm_search which covers all history. Against a capture older than a
day that returns an empty bucket list, which reads as "no such traffic"
when it means "nothing in the last day" — suspect the window before the
data.
malcolm_alerts
Search Suricata alerts with structured parameters, no field knowledge needed.
Use this instead of malcolm_search when hunting Suricata alerts: it maps
each argument to the correct Malcolm field for you (you don't need to
know whether it's suricata.alert.signature or rule.name). It always
filters event.dataset=alert. These are Suricata IDS alerts, signature
matches on the wire; three other things on this server are also called
alerts and are different mechanisms — malcolm_alerting_monitors and
malcolm_alerting_alerts are the OpenSearch alerting plugin's standing
rules and their firings, malcolm_anomaly_detectors is its machine-learning
baseline, and malcolm_create_alert (alerting write class) records a
finding of your own.
Behavior: `signature` and `category` are substring searches, which Malcolm
cannot express in a filter (its filters are exact terms), so this tool
resolves the substring against the field's 500 most common values first
and filters on the matches. A substring that matches no recorded value
returns a message saying so rather than an empty result set — that is the
difference between "no such signature here" and "no alerts fired". That
pre-scan is the one place the time range bites: it reads only the last 24
hours, while the alert search itself covers ALL history when time_from is
empty, so on a capture older than a day every signature reads as
unrecorded until you pass time_from.
Returns the raw Malcolm /mapi/document response (matching alert documents).
malcolm_field_search
Discover which field NAMES exist in Malcolm's index, by keyword, prefix, or type.
Use this first, before any query, to confirm a field name exists — Malcolm uses
non-standard names (e.g. http.useragent, NOT http.user_agent). To then see the
VALUES a field holds, use malcolm_field_values; to see which datasets contain
it, use malcolm_field_profile. Do NOT source an arkime_* argument from here:
these are the names malcolm_* and search_dsl take, and Arkime has its own
spelling for the same field (ip.src, srcIp) that arkime_field_search reports.
Pass at least one argument. Returns a text list of "name (type)" lines,
sorted alphabetically.
Arguments narrow (AND), they never widen, and the mapping is big enough
that one keyword rarely lands: it runs to thousands of fields, and a
keyword as common as "ip" matches over a thousand of them on its own.
The header line counts every match but only the first 100 names are
printed, so add a prefix or a field_type rather than reading the printed
list as the whole answer.
malcolm_field_values
List a single field's distinct VALUES with per-value document counts.
Use this to see what values a field actually holds before filtering on it, so
you don't invent values. To confirm the field NAME exists first, use
malcolm_field_search; to see which datasets carry the field, use
malcolm_field_profile. For multi-field or nested bucketing, use
malcolm_aggregate. A "-" in the output is Malcolm's placeholder for
documents where the field is absent, not a value you can filter on.
Returns a text list of "value (N docs)" lines.
With no time range this reads only the last 24 hours, so a value that
exists only in older data is missing here and reads as invalid —
measured on Malcolm v26.07.1, network.protocol lists nothing at the
default window while its top value carries millions of documents once
time_from reaches the capture. Pass time_from before concluding a value
is not in this Malcolm.
malcolm_field_profile
Show which event.dataset types actually contain a given field, with doc counts.
Use this to learn where a field lives (e.g. whether it only appears in SSL or DNS
records) before scoping a query. To confirm the field NAME first, use
malcolm_field_search; to list its distinct VALUES, use malcolm_field_values.
Behavior: first resolves the name against the index mapping, then aggregates over
event.dataset. Three distinct text outcomes — (1) unknown field → a "not found"
message with close-name suggestions (no profile); (2) known field but no matching
documents in the time window → an "exists but no documents" message; (3) a
per-dataset "event.dataset=<name> (N docs)" list. The dataset counts honor the
time window: with no range it uses the last 24 hours, so a field that only has
old data can resolve as known yet profile as empty — pass time_from/time_to to
reach historical data. Returns plain text, not JSON.
malcolm_service_status
Report readiness of each Malcolm service plus Malcolm version and OpenSearch health.
Call this before a hunt to confirm the whole stack is up. For a bare
is-the-API-alive check use malcolm_ping; for the OpenSearch cluster's
green/yellow/red detail alone use cluster_health; for data freshness and
per-dataset counts use malcolm_data_coverage. Returns a JSON summary with
malcolm_version, mode, opensearch_health, a per-service readiness map, and an
"N/total services ready" line. One probe failing adds an `errors` entry and
keeps the rest; both failing is reported as an error, since there is then no
status at all to report.
The readiness map is also where the optional subsystems declare
themselves — measured on Malcolm v26.07.1, 15 keys, netbox, filescan and
extracted_files among them. Read the relevant key here before taking an
empty answer from malcolm_netbox_lookup or malcolm_file_scans as "no
such asset" when it may mean "that subsystem is not deployed".
+39 more tools listed on main page
MCP Server Tools (55)
report_feedback
Report a data quality issue or agent intent gap for a DataNexus tool response.
tool_id: e.g. "T10" or "security_fetch_cve_detail".
query_hash: From the query_hash field of the response.
signal: incorrect_data | missing_field | stale_data | not_useful | wrong_entity | data_quality.
comment: Issue description. Max 500 chars.
missing_fields: Absent or wrong field names.
feedback_type: "user_feedback" (default) or "agent_gap".
intended_query: Agent's goal. Max 256 chars.
gap_description: What was missing. Max 256 chars.
Example: report_feedback(tool_id="T10", query_hash="abc123", signal="incorrect_data")
report_mcpize_link
Check MCPize subscription status for a DataNexus tool.
tool_id: DataNexus tool identifier e.g. "T10". Pass the tool the user is asking about.
Returns: status ("free" | "subscription_required" | "not_configured"), message, tool_id, and upgrade_url when subscription is required.
Example: report_mcpize_link(tool_id="T10")
validate_tool_output
Validate a DataNexus tool response for data quality issues using two-layer validation: deterministic rules first, then AI review for ambiguous cases. Read-only. Never blocks. tool_id: DataNexus tool identifier e.g. T04, T10, T22. Required. Find in the tool_id field of any response. query_hash: Hash from the response you are validating. Required. Enables feedback correlation. response_json: Full tool response serialised as a JSON string. Required. Returns pass or issues_found, with issues from each layer and whether feedback was auto-filed. Both layers must agree before feedback is filed. Use validate_tool_output to check data quality. Use report_feedback instead to manually report an issue you have already identified. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="validate_tool_output", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
search_datanexus_tools
Find the right DataNexus tool by describing your task in plain English. Read-only. No side effects. Call this before any other DataNexus tool to reduce context load from 40000 to 800 tokens. query: Plain English description of your task e.g. check if a Python package has CVEs or look up a UK charity by name. Required. domain: Restrict results to one sub-server: nonprofit, security, compliance, domain, legal, govcon, or regulatory. Optional. Returns matching tool names and parameter hints you can call directly. Do not call this recursively or to validate results — use validate_tool_output for that. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="search_datanexus_tools", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
nonprofit_fetch_nonprofit_by_ein
Fetch IRS 990 filing data for any US nonprofit by EIN. Read-only. No side effects. Idempotent. US only. ein: 9-digit Employer ID with or without dash, e.g. 46-5734087 or 465734087. Required. Returns name, revenue, expenses, assets, NTEE code, and mission from the most recent 990 filing. Use this when you have the exact EIN. Use nonprofit_search_nonprofits_by_name instead when you only have a name. Verified source: IRS EO BMF + IRS TEOS. 7-day cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="nonprofit_fetch_nonprofit_by_ein", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
nonprofit_search_nonprofits_by_name
Search US nonprofits by name with optional state filter. Read-only. No side effects. Idempotent. US only. Returns up to 25 matches. name: Full or partial organisation name. Required. state: Two-letter US state code e.g. CA, NY. Optional, defaults to all states. Returns EIN, name, state, revenue, and NTEE code for each match. Use this when you have a name but not the EIN. Use nonprofit_fetch_nonprofit_by_ein instead when you have the exact EIN for a precise single lookup. Verified source: IRS EO BMF. 7-day cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="nonprofit_search_nonprofits_by_name", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
nonprofit_fetch_charity_uk
Fetch UK registered charity details by charity number or organisation name. Read-only. No side effects. Idempotent. UK only. charity_number_or_name: UK registered charity number (7 digits, e.g. 1234567) or full/partial organisation name. Required. Returns registration status, income, expenditure, activities, and trustee count. Use this for UK charities. Use nonprofit_fetch_nonprofit_by_ein or nonprofit_search_nonprofits_by_name for US nonprofits. Verified source: UK Charity Commission OGL v3. 24-hour cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="nonprofit_fetch_charity_uk", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
security_fetch_package_vulnerabilities
Fetch all known CVEs for an open source package version or a batch of packages. Read-only. No side effects. Idempotent. Single-package mode: package (e.g. requests), version (e.g. 2.28.0), ecosystem (PyPI/npm/Maven/Go/Cargo/NuGet/RubyGems). Batch mode: packages array of {name, version, ecosystem} objects — max 50 per call. If packages array is provided and non-empty, batch mode is used and package/version/ecosystem are ignored. Batch returns {results: [...], partial: bool, failed_count: int}. Each result has vuln_count and vulnerabilities list. Returns CVE ID, severity, CVSS score, affected range, and fixed version. Use security_fetch_cve_detail for full detail by CVE ID. Use security_audit_sbom_vulnerabilities for SBOM files. Verified source: Google OSV.dev. 1-hour cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="security_fetch_package_vulnerabilities", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
security_fetch_dependency_graph
Fetch the full dependency tree for a package version including transitive dependencies. Read-only. No side effects. Idempotent. Hard 8-second timeout — large dependency trees may return partial results. package: Package name. Required. version: Exact version string e.g. 1.2.3. Required. ecosystem: One of PyPI, npm, Maven, Go, Cargo, NuGet, RubyGems. Required. Returns all direct and transitive dependencies with version constraints. Use this to understand full supply chain exposure. Use security_fetch_package_vulnerabilities instead when you only need CVEs for a single package. Verified source: deps.dev (Google). 1-hour cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="security_fetch_dependency_graph", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
security_fetch_cve_detail
Fetch full detail for a specific CVE by ID. Read-only. No side effects. Idempotent. cve_id: CVE identifier in format CVE-YYYY-NNNNN e.g. CVE-2021-44228. Required. Returns description, CVSS base score, affected products, patch references, and publish date. Use this when you have a CVE ID and need complete detail beyond what a package scan returns. Use security_fetch_package_vulnerabilities instead when you want all CVEs for a package version. Verified source: NIST NVD. 1-hour cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="security_fetch_cve_detail", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
security_audit_sbom_vulnerabilities
Audit a Software Bill of Materials for known vulnerabilities across all listed packages. Read-only. No side effects. Idempotent. sbom_json: CycloneDX or SPDX SBOM as a JSON string. Required. Large SBOMs (100+ packages) may take up to 10 seconds. Returns CVEs grouped by package with severity and fixed versions. Use this when you have a full SBOM to audit. Use security_fetch_package_vulnerabilities instead when checking a single package version. Verified source: Google OSV.dev batch API. 1-hour cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="security_audit_sbom_vulnerabilities", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".
security_fetch_package_licence
Fetch the SPDX licence identifier for an open source package version. Read-only. No side effects. Idempotent. package: Package name e.g. flask. Required. version: Exact version string e.g. 2.3.0. Required. ecosystem: One of PyPI, npm, Maven, Go, Cargo, NuGet, RubyGems. Required. Returns the SPDX licence identifier e.g. MIT, Apache-2.0, GPL-3.0. Use this to verify licence compatibility before including a dependency. Use security_fetch_package_vulnerabilities instead when checking for security issues not licences. Verified source: deps.dev (Google). 1-hour cache. If this tool's response does not serve the user's need, call report_feedback with feedback_type="agent_gap", tool_id="security_fetch_package_licence", intended_query="{what the user needed}", gap_description="{what was missing or wrong in the result}".