n8n-mcp-community 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ All notable changes to this project will be documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.1] - Frozen release candidate
10
+
11
+ Version 0.2.1 is a frozen release candidate. Availability is established only
12
+ by matching npm provenance, GitHub Release assets and signatures, and MCP
13
+ Registry readbacks; this immutable entry is not rewritten after publication.
14
+
15
+ ### Changed
16
+
17
+ - Forty tool definitions now state their exact selector semantics, pagination
18
+ behavior, authorization or configuration boundary, side effects, and return
19
+ contract in shorter agent-facing language. The four definitions already
20
+ rated 5.0 by Glama remain unchanged.
21
+ - The durable 44-tool definition matrix now checks parameter, authorization,
22
+ configuration, return, annotation, and task-support invariants independently.
23
+ Controlled mutations prove that every invariant rejects semantic drift.
24
+ - Execution-stop documentation now names every stoppable state and the
25
+ terminal-race mapping. Health documentation now states the explicit opt-in
26
+ required for remote plaintext HTTP.
27
+
9
28
  ## [0.2.0] - Frozen release candidate
10
29
 
11
30
  Version 0.2.0 is a frozen release candidate. Availability is established only
@@ -285,6 +304,7 @@ Registry readbacks; this immutable entry is not rewritten after publication.
285
304
  publishable history; authenticated public release receipts remain supported.
286
305
 
287
306
  [Unreleased]: #unreleased
307
+ [0.2.1]: #021---frozen-release-candidate
288
308
  [0.2.0]: #020---frozen-release-candidate
289
309
  [0.1.4]: #014---frozen-release-candidate
290
310
  [0.1.3]: #013---frozen-release-candidate
package/README.md CHANGED
@@ -104,7 +104,7 @@ how to check the download.
104
104
  The current source candidate has been verified with:
105
105
 
106
106
  - exactly **44 tools**, **5 resources**, and **4 prompts** over real stdio;
107
- - **296 passing tests** locally on Node.js 24.18.1; CI and release gates require
107
+ - **297 passing tests** locally on Node.js 24.18.1; CI and release gates require
108
108
  the pinned Node.js 22.23.1 and 24.18.0 matrix;
109
109
  - zero findings from Gitleaks, Semgrep, Trivy, and both project-root
110
110
  production/full `npm audit` runs; all three source scanners are reproduced in
@@ -170,13 +170,13 @@ failure, and security reference.
170
170
 
171
171
  ### Executions
172
172
 
173
- | Tool | Mode | Purpose |
174
- | -------------------------------------------------------------- | --------- | ------------------------------------------------------------------ |
175
- | [`n8n_executions_list`](docs/tools.md#n8n_executions_list) | Read-only | List execution metadata with value-free data-presence summaries. |
176
- | [`n8n_executions_get`](docs/tools.md#n8n_executions_get) | Read-only | Read one execution's metadata without returning workflow payloads. |
177
- | [`n8n_executions_delete`](docs/tools.md#n8n_executions_delete) | Unsafe | Permanently delete a saved execution. |
178
- | [`n8n_executions_retry`](docs/tools.md#n8n_executions_retry) | Unsafe | Retry an eligible saved execution. |
179
- | [`n8n_executions_stop`](docs/tools.md#n8n_executions_stop) | Unsafe | Stop one running execution and bind identity from validated input. |
173
+ | Tool | Mode | Purpose |
174
+ | -------------------------------------------------------------- | --------- | -------------------------------------------------------------------- |
175
+ | [`n8n_executions_list`](docs/tools.md#n8n_executions_list) | Read-only | List execution metadata with value-free data-presence summaries. |
176
+ | [`n8n_executions_get`](docs/tools.md#n8n_executions_get) | Read-only | Read one execution's metadata without returning workflow payloads. |
177
+ | [`n8n_executions_delete`](docs/tools.md#n8n_executions_delete) | Unsafe | Permanently delete a saved execution. |
178
+ | [`n8n_executions_retry`](docs/tools.md#n8n_executions_retry) | Unsafe | Retry an eligible saved execution. |
179
+ | [`n8n_executions_stop`](docs/tools.md#n8n_executions_stop) | Unsafe | Stop one stoppable execution and bind identity from validated input. |
180
180
 
181
181
  ### Credentials
182
182
 
@@ -291,7 +291,7 @@ API. The project does not redistribute the `n8n-nodes-base` catalog and does not
291
291
  use browser cookies, interactive session routes, or runtime package downloads to
292
292
  construct one.
293
293
 
294
- The v0.2.0 candidate surface intentionally excludes arbitrary workflow execution,
294
+ The v0.2.1 candidate surface intentionally excludes arbitrary workflow execution,
295
295
  credential/workflow transfer, folders, data tables, beta evaluation endpoints,
296
296
  and execution annotations. See the [roadmap](ROADMAP.md) for the annotation
297
297
  proposal retained outside the release target.
package/ROADMAP.md CHANGED
@@ -18,7 +18,7 @@ maintainer approval.
18
18
 
19
19
  ## Accepted backlog proposal
20
20
 
21
- Execution annotations remain a coherent two-tool proposal outside v0.2.0:
21
+ Execution annotations remain a coherent two-tool proposal outside v0.2.1:
22
22
 
23
23
  - `n8n_executions_get_tags`
24
24
  - `n8n_executions_update_tags`
@@ -59,7 +59,7 @@ export const credentialTools = Object.freeze([
59
59
  defineTool({
60
60
  name: "n8n_credentials_create",
61
61
  title: "Create credential",
62
- description: "Create and persist one credential as an additive write. Use n8n_credentials_schema first; use n8n_credentials_update when the credential exists. type selects the schema the caller should use to construct data, and isResolvable is sent only when supplied. Requires write/unsafe mode plus credential-create permission. Secret values belong only in input and are excluded from logs and output; returns metadata only.",
62
+ description: "Create and persist one credential as an additive write. First call n8n_credentials_schema with the planned type, then build data from its field contract; name is only the stored label. isResolvable is forwarded only when supplied. Use n8n_credentials_update for an existing credential. Requires write/unsafe mode plus create permission. Secret values belong only in data input and are excluded from logs and output; returns metadata only.",
63
63
  operation: "write",
64
64
  outputDataDescription: "Validated credential metadata with id, name, type, optional timestamps, managed/global flags, and resolvability. Stored credential values are never returned.",
65
65
  destructive: false,
@@ -89,7 +89,7 @@ export const credentialTools = Object.freeze([
89
89
  defineTool({
90
90
  name: "n8n_credentials_delete",
91
91
  title: "Delete credential",
92
- description: "Permanently delete one stored credential, which can break every referencing workflow. Use n8n_credentials_usage across all pages first; use n8n_credentials_update when replacement is sufficient. No secret or rollback is returned. Requires unsafe mode plus credential-delete permission; returns the request-bound ID with deleted=true.",
92
+ description: "Permanently delete one stored credential, which can break every referencing workflow. credentialId must be the stable ID from n8n_credentials_list, get, or create; a credential name or type is not accepted. Scan all n8n_credentials_usage pages first, or update when replacement is enough. No secret or rollback is returned. Requires unsafe mode plus credential-delete permission; returns the request-bound credentialId with deleted=true.",
93
93
  operation: "unsafe",
94
94
  outputDataDescription: "Object with the validated input credentialId and deleted=true. Identity is bound to the request because n8n does not consistently return the deleted credential ID.",
95
95
  input: { credentialId: identifier("Stable ID of the credential to delete.") },
@@ -103,7 +103,7 @@ export const credentialTools = Object.freeze([
103
103
  defineTool({
104
104
  name: "n8n_credentials_schema",
105
105
  title: "Get credential schema",
106
- description: "Get n8n's Public API field schema for one credential type. Use it before n8n_credentials_create or replacement-data updates; use n8n_credentials_get for stored metadata. credentialType is a route selector, not a stored credential ID, so this call never reads credential values. Requires schema-read permission; returns the validated upstream field contract without testing credentials.",
106
+ description: "Get n8n's Public API field contract for one credential type. credentialType is the public type name used by create/update, not a stored credential ID, name, or secret. Its result tells the caller which keys belong in data; it does not read or test a stored credential. Use n8n_credentials_get for stored metadata. Requires schema-read permission; returns the validated upstream field contract.",
107
107
  operation: "read-only",
108
108
  outputDataDescription: "Validated Public API schema object for the requested credential type. It describes accepted fields and constraints but never contains stored credential values.",
109
109
  input: {
@@ -116,7 +116,7 @@ export const credentialTools = Object.freeze([
116
116
  defineTool({
117
117
  name: "n8n_credentials_list",
118
118
  title: "List credentials",
119
- description: "List one Public API page of credential metadata using the endpoint supported from n8n Community 2.30.5. Use it for discovery; use n8n_credentials_get when the ID is known. cursor resumes a prior page and limit bounds that single request; this call never auto-paginates. Requires credential-list permission; returns metadata and nextCursor, never stored values.",
119
+ description: "List one Public API page of credential metadata; the endpoint is supported from n8n Community 2.30.5. Omit cursor for page one. For later pages, pass nextCursor back unchanged; limit sizes only that request, and the tool never auto-paginates. Use n8n_credentials_get when an ID is known. Requires credential-list permission; returns metadata and nextCursor, never stored values.",
120
120
  operation: "read-only",
121
121
  outputDataDescription: "Object with data (up to 100 credential metadata records) and nextCursor (string or null). Records contain allowlisted metadata only; credential values are rejected.",
122
122
  input: { limit: pageLimit(), cursor: cursor.optional() },
@@ -131,7 +131,7 @@ export const credentialTools = Object.freeze([
131
131
  defineTool({
132
132
  name: "n8n_credentials_get",
133
133
  title: "Get credential",
134
- description: "Get public metadata for one stored credential by stable ID. Use it for a known target; use n8n_credentials_list for discovery and n8n_credentials_schema for type fields. credentialId identifies stored metadata only: the endpoint and output contract do not retrieve credential values or test the external service. Requires credential-read permission; returns validated identity, type, flags, and timestamps.",
134
+ description: "Get public metadata for one stored credential. credentialId is the stable ID from n8n_credentials_list or create, not its name, type, or secret data. Use n8n_credentials_schema for type fields. The endpoint does not retrieve credential values or test the external service. Requires credential-read permission; returns validated identity, type, flags, and timestamps.",
135
135
  operation: "read-only",
136
136
  outputDataDescription: "One validated credential metadata record with id, name, type, optional timestamps, managed/global flags, and resolvability; no stored credential values.",
137
137
  input: { credentialId: identifier("Stable ID of the credential metadata to retrieve.") },
@@ -140,7 +140,7 @@ export const credentialTools = Object.freeze([
140
140
  defineTool({
141
141
  name: "n8n_credentials_update",
142
142
  title: "Update credential",
143
- description: "Update selected metadata or values on one stored credential, potentially breaking dependent workflows. Use n8n_credentials_schema for data fields and n8n_credentials_create for a new credential. Supply at least one field; changing type also requires data. isPartialData=false treats data as replacement, while true requests a partial merge. Requires write/unsafe mode plus update permission; returns metadata without secret values.",
143
+ description: "Update one stored credential, potentially breaking dependent workflows. credentialId selects the record; omit fields to keep them and supply at least one change. If type changes, data is also required and must match n8n_credentials_schema. With data, isPartialData=false replaces the saved field set, while true requests a partial merge. Use create for a new credential. Requires write/unsafe mode plus update permission; returns metadata without secret values.",
144
144
  operation: "write",
145
145
  outputDataDescription: "Updated credential metadata with id, name, type, optional timestamps, managed/global flags, and resolvability. Supplied credential values are never echoed.",
146
146
  destructive: true,
@@ -201,7 +201,7 @@ export const credentialTools = Object.freeze([
201
201
  defineTool({
202
202
  name: "n8n_credentials_test",
203
203
  title: "Test credential",
204
- description: "Test one stored credential by allowing n8n to contact its external service, which receives and may log the attempt. Use n8n_credentials_get for metadata-only inspection and do not call this when network contact is unwanted. Requires unsafe mode plus test permission; returns only the target-bound OK/Error outcome and withholds the upstream diagnostic message because it may contain secrets.",
204
+ description: "Test one already stored credential by allowing n8n to contact its external service, which receives and may log the attempt. credentialId is the stable ID from n8n_credentials_list or get; inline credential data, names, and types are not accepted. Use get for metadata-only inspection and skip this tool when network contact is unwanted. Requires unsafe mode plus test permission; returns only the target-bound OK/Error outcome and withholds the upstream diagnostic message.",
205
205
  operation: "unsafe",
206
206
  outputDataDescription: "Object with credentialId, status OK or Error, and a server-authored message that discloses only whether the test succeeded. The untrusted upstream diagnostic message is never returned.",
207
207
  openWorld: true,
@@ -221,7 +221,7 @@ export const credentialTools = Object.freeze([
221
221
  defineTool({
222
222
  name: "n8n_credentials_usage",
223
223
  title: "Find credential usage",
224
- description: "Scan one bounded workflow page for exact references to a credential ID. Use it before n8n_credentials_update or deletion; use n8n_credentials_get for metadata only. active filters upstream, cursor resumes the scan, and unresolved legacy references are counted but never matched; coverage is complete only when nextCursor is null. Requires workflow-list permission; returns bounded workflow/node matches and omission counts.",
224
+ description: "Scan one workflow page for exact references to a credential. credentialId is the stable ID from n8n_credentials_list or get. Omit cursor for page one; keep credentialId, active, and limit unchanged when passing nextCursor. Unresolved legacy references are counted but never matched, and coverage is complete only when nextCursor is null. Use this before update/delete. Requires workflow-list permission; returns bounded workflow/node matches and omission counts.",
225
225
  operation: "read-only",
226
226
  outputDataDescription: "Object with credentialId, workflowsExamined, matchingWorkflowCount, workflows with at most 200 total node details and 20 per workflow, nextCursor, scanComplete, truncation counts, referencesScanned, and referencesUnresolved.",
227
227
  input: {
@@ -90,7 +90,7 @@ export const executionTools = Object.freeze([
90
90
  defineTool({
91
91
  name: "n8n_executions_get",
92
92
  title: "Get execution",
93
- description: "Get metadata for one saved execution by stable ID. Use it for a known target; use n8n_executions_list to discover or filter executions. includeData changes only whether n8n is asked about payload presence—the tool never returns node inputs or outputs. Requires permission to read the execution; returns identity, status, timing, retry fields, and value-free dataPolicy.",
93
+ description: "Get metadata for one saved execution. executionId is the execution record ID from n8n_executions_list, not its workflowId. includeData=true asks n8n whether saved payload data exists, but the MCP still withholds every node input and output; false skips that request detail. Use list for discovery. Requires execution-read permission; returns identity, status, timing, retry fields, and value-free dataPolicy.",
94
94
  operation: "read-only",
95
95
  outputDataDescription: "One allowlisted execution metadata record with identity, status, mode, workflow/timing/retry fields when present, and value-free dataPolicy. Raw execution values are never returned.",
96
96
  input: {
@@ -111,7 +111,7 @@ export const executionTools = Object.freeze([
111
111
  defineTool({
112
112
  name: "n8n_executions_delete",
113
113
  title: "Delete execution",
114
- description: "Permanently delete one saved execution and its retained history. Use it only after n8n_executions_get confirms the target; use read tools for inspection. The server provides no recovery or rollback. Requires unsafe mode plus execution-delete permission; returns the request-bound ID with deleted=true.",
114
+ description: "Permanently delete one saved execution and its retained history. executionId must be the execution record ID from n8n_executions_list or get; a workflow ID is not accepted. Confirm the target with get first. The server provides no recovery or rollback. Requires unsafe mode plus execution-delete permission; returns the request-bound executionId with deleted=true.",
115
115
  operation: "unsafe",
116
116
  outputDataDescription: "Object with the validated input executionId and deleted=true. Identity is bound to the request and does not rely on an upstream response body.",
117
117
  input: { executionId },
@@ -125,7 +125,7 @@ export const executionTools = Object.freeze([
125
125
  defineTool({
126
126
  name: "n8n_executions_retry",
127
127
  title: "Retry execution",
128
- description: "Retry one eligible saved execution, potentially repeating every external effect already reached. Use it only after n8n_executions_get review; use n8n_executions_stop for a running execution. loadWorkflow=true uses the currently saved workflow, while false uses the original execution snapshot. Requires unsafe mode and retry permission; returns metadata without payload values.",
128
+ description: "Retry one saved execution that has retry data and did not finish successfully; n8n rejects queued/new executions and successful finished executions. executionId comes from n8n_executions_get or list. loadWorkflow=true uses the currently saved workflow, while false uses the original execution snapshot. A retry may repeat earlier external effects; use stop for a running execution. Requires unsafe mode and retry permission; returns metadata without payload values.",
129
129
  operation: "unsafe",
130
130
  outputDataDescription: "Allowlisted metadata for the new or retried execution, including scalar identity/status/timing/retry fields when supplied by n8n; raw execution values are omitted.",
131
131
  input: {
@@ -144,7 +144,7 @@ export const executionTools = Object.freeze([
144
144
  defineTool({
145
145
  name: "n8n_executions_stop",
146
146
  title: "Stop execution",
147
- description: "Request cancellation of one running execution without rolling back completed external effects. Use n8n_executions_get for inspection or n8n_executions_retry for an eligible saved failure. A successful HTTP response alone does not prove cancellation. Requires unsafe mode plus stop permission; returns stopped, already_finished, or unknown from validated state.",
147
+ description: "Request cancellation of one stoppable execution: new, unknown, waiting, or running. executionId must be its record ID from n8n_executions_list or get, not a workflow ID. n8n rejects an execution that is already terminal. From a successful response, canceled maps to stopped, another terminal status maps to already_finished, and an unclear body maps to unknown; HTTP 200 alone never proves cancellation. Completed external effects remain. Requires unsafe mode and stop permission; returns the mapped state.",
148
148
  operation: "unsafe",
149
149
  outputDataDescription: "Object with executionId, stopped, state (stopped, already_finished, or unknown), and optional finished/status/stoppedAt metadata. HTTP success alone never asserts that a stop occurred.",
150
150
  input: { executionId },
@@ -160,10 +160,10 @@ export const executionTools = Object.freeze([
160
160
  method: "POST",
161
161
  path: `/executions/${pathSegment(input.executionId)}/stop`,
162
162
  }));
163
- // n8n answers 200 even when the execution already finished in the race between the
164
- // operator's check and this call. Derive the outcome from the validated body instead of
165
- // asserting a stop that never happened: only a "canceled" terminal state means we stopped
166
- // it; other terminal states mean it finished on its own; anything else is unknown.
163
+ // Supported n8n versions normally reject an already-terminal target. For any successful
164
+ // body, derive the outcome instead of trusting HTTP 200: only "canceled" proves this call
165
+ // stopped it; another terminal state maps defensively to already_finished; anything else
166
+ // is unknown.
167
167
  const normalizedStatus = upstream.status?.toLowerCase();
168
168
  const outcome = normalizedStatus === "canceled"
169
169
  ? { stopped: true, state: "stopped" }
@@ -18,7 +18,7 @@ export const tagTools = Object.freeze([
18
18
  defineTool({
19
19
  name: "n8n_tags_list",
20
20
  title: "List tags",
21
- description: "List one Public API page of workflow tags. Use it to discover stable tag IDs; use n8n_tags_get when the ID is already known. cursor resumes the prior page and limit bounds only this request, so continue nextCursor until null when complete coverage matters. Requires tag-list permission and never changes assignments; returns validated metadata and nextCursor.",
21
+ description: "List one Public API page of workflow tags. Omit cursor for page one; for later pages, pass nextCursor back unchanged. limit sizes only the current page, so continue until nextCursor is null for complete coverage. Use n8n_tags_get when the stable tag ID is known. Requires tag-list permission and never changes assignments; returns validated metadata and nextCursor.",
22
22
  operation: "read-only",
23
23
  outputDataDescription: "Object with data (up to 100 validated tag records) and nextCursor (string or null). Each tag includes id, name, and optional createdAt/updatedAt timestamps.",
24
24
  input: { limit: pageLimit(), cursor: cursor.optional() },
@@ -33,7 +33,7 @@ export const tagTools = Object.freeze([
33
33
  defineTool({
34
34
  name: "n8n_tags_get",
35
35
  title: "Get tag",
36
- description: "Get one workflow tag by stable ID. Use it to verify a known target before assigning, renaming, or deleting it; use n8n_tags_list for discovery. tagId identifies the reusable tag itself, not a workflow assignment, so this call neither lists nor changes assigned workflows. Requires tag-read permission; returns validated ID, name, and optional timestamps.",
36
+ description: "Get one workflow tag. tagId is the stable tag identity returned by n8n_tags_list or create; a tag name or workflow ID is not accepted. Use it before assigning, renaming, or deleting the tag. The ID identifies the reusable tag itself, not an assignment, so this call neither lists nor changes assigned workflows. Requires tag-read permission; returns validated ID, name, and optional timestamps.",
37
37
  operation: "read-only",
38
38
  outputDataDescription: "One validated tag record with id, name, and optional createdAt/updatedAt timestamps.",
39
39
  input: { tagId: identifier("Stable ID of the workflow tag to retrieve.") },
@@ -42,7 +42,7 @@ export const tagTools = Object.freeze([
42
42
  defineTool({
43
43
  name: "n8n_tags_create",
44
44
  title: "Create tag",
45
- description: "Create and persist one workflow tag as an additive write. Use it for a new reusable label; use n8n_tags_update when the tag exists and n8n_workflows_update_tags to assign it. name must already be trimmed, and duplicate-name handling belongs to n8n; creation never changes any workflow assignment. Requires write/unsafe mode plus tag-create permission; returns validated metadata.",
45
+ description: "Create and persist one reusable workflow label as an additive write. name is the literal display label, not a tag ID or workflow assignment; it must already be trimmed. Duplicate-name handling belongs to n8n, and creation never assigns the tag to a workflow. Use n8n_tags_update for an existing tag and n8n_workflows_update_tags to assign it. Requires write/unsafe mode plus create permission; returns validated metadata.",
46
46
  operation: "write",
47
47
  outputDataDescription: "Created tag record with validated id, name, and optional createdAt/updatedAt timestamps.",
48
48
  destructive: false,
@@ -54,7 +54,7 @@ export const tagTools = Object.freeze([
54
54
  defineTool({
55
55
  name: "n8n_tags_update",
56
56
  title: "Update tag",
57
- description: "Rename one existing workflow tag while preserving its stable ID and current assignments. Use n8n_tags_create for a new label and n8n_workflows_update_tags to change assignments. tagId selects the existing record and name is its complete replacement; duplicate-name acceptance belongs to n8n. Requires write/unsafe mode plus tag-update permission; returns validated metadata.",
57
+ description: "Rename one existing workflow tag while preserving its ID and assignments. tagId is the stable selector from n8n_tags_list or get; name is the complete new display label, not another ID. Duplicate-name acceptance belongs to n8n. Use n8n_tags_create for a new label and n8n_workflows_update_tags to change assignments. Requires write/unsafe mode plus tag-update permission; returns validated metadata.",
58
58
  operation: "write",
59
59
  outputDataDescription: "Updated tag record with validated id, name, and optional createdAt/updatedAt timestamps.",
60
60
  input: { tagId: identifier("Stable ID of the workflow tag to rename."), name: tagName },
@@ -67,7 +67,7 @@ export const tagTools = Object.freeze([
67
67
  defineTool({
68
68
  name: "n8n_tags_delete",
69
69
  title: "Delete tag",
70
- description: "Permanently delete one workflow tag, which can remove that label from multiple workflows. Use n8n_tags_get plus affected-workflow review first; use n8n_tags_update when a rename is sufficient. No rollback is provided. Requires unsafe mode plus tag-delete permission; returns the request-bound ID with deleted=true.",
70
+ description: "Permanently delete one workflow tag, which can remove that label from many workflows. tagId must be the stable ID from n8n_tags_list or get; a tag name or workflow ID is not accepted. Review the target and affected workflows first, or use n8n_tags_update when a rename is enough. No rollback is provided. Requires unsafe mode plus tag-delete permission; returns the request-bound tagId with deleted=true.",
71
71
  operation: "unsafe",
72
72
  outputDataDescription: "Object with the validated input tagId and deleted=true. Identity is bound to the request and does not rely on an upstream response body.",
73
73
  input: { tagId: identifier("Stable ID of the workflow tag to delete.") },
@@ -71,7 +71,7 @@ export const userTools = Object.freeze([
71
71
  defineTool({
72
72
  name: "n8n_users_get",
73
73
  title: "Get user",
74
- description: "Get one user by stable ID or exact email. Use it for targeted verification before an administrative action; use n8n_users_list for discovery. userIdOrEmail chooses ID lookup or an exact percent-encoded email lookup; includeRole=true requests but cannot guarantee role visibility. Requires user-read permission; returns redaction-protected identity and account state without mutation.",
74
+ description: "Get one user by stable ID or exact email. userIdOrEmail routes ID-shaped input to ID lookup and other input to an exact percent-encoded email lookup; partial email matching is not used. includeRole=true asks n8n for the global role, but permissions may still hide it. Use n8n_users_list for discovery. Requires user-read permission; returns redaction-protected identity and account state without mutation.",
75
75
  operation: "read-only",
76
76
  outputDataDescription: "One validated user record with id and optional email, firstName, lastName, role, disabled/pending status, and timestamps. Recognized personal values may be redacted.",
77
77
  input: {
@@ -89,7 +89,7 @@ export const userTools = Object.freeze([
89
89
  defineTool({
90
90
  name: "n8n_users_create",
91
91
  title: "Invite user",
92
- description: "Create a pending non-owner invitation as an additive write that may send email. Use n8n_users_get first when the address may exist; this is not an update or project-membership tool. role defaults to global:member. Requires unsafe mode plus invite permission. An inconclusive response may still mean a pending user exists; returns delivery state but never the acceptance URL.",
92
+ description: "Create a pending invitation as an additive write that may send email. email is the future account login; use n8n_users_get first when it may exist. role is a global account role, not project membership, and omission selects global:member; owner invitations are rejected. Requires unsafe mode plus invite permission. An inconclusive response may still mean a pending user exists; returns delivery state but never the acceptance URL.",
93
93
  operation: "unsafe",
94
94
  outputDataDescription: "Confirmed invitation outcome with userCreated, invited, userId, email, requestedRole, roleConfirmedByResponse, emailSent, delivery, and inviteAcceptUrlReturned=false. Acceptance URLs are never returned.",
95
95
  destructive: false,
@@ -134,7 +134,7 @@ export const userTools = Object.freeze([
134
134
  defineTool({
135
135
  name: "n8n_users_delete",
136
136
  title: "Delete user",
137
- description: "Permanently delete one API-eligible user. Use n8n_users_get to verify the target first; use read tools for inspection. userId accepts no transfer target, so ownership handling remains entirely with n8n. Requires unsafe mode plus user-delete permission; returns the request-bound ID with deleted=true and provides no rollback claim.",
137
+ description: "Permanently delete one API-eligible user. userId must be the stable ID from n8n_users_list or get; an email address is not accepted. The input has no transfer target, so workflow or credential ownership handling remains entirely with n8n. Verify the target with get first. Requires unsafe mode plus user-delete permission; returns the request-bound userId with deleted=true and provides no rollback claim.",
138
138
  operation: "unsafe",
139
139
  outputDataDescription: "Object with the validated input userId and deleted=true. The tool makes no ownership-transfer claim and accepts n8n's successful empty response.",
140
140
  input: { userId: identifier("Stable ID of the API-eligible user to delete.") },
@@ -65,7 +65,7 @@ export const utilityTools = Object.freeze([
65
65
  defineTool({
66
66
  name: "n8n_health",
67
67
  title: "Check n8n health",
68
- description: "Perform one redirect-free same-origin /healthz request with a 10-second timeout. Use it for HTTP reachability only; use an authenticated read tool such as n8n_workflows_list to validate API-key capability. The shared client still requires configured URL/key values, but no workflow data is read or changed. Returns ok=true and the successful status, never the upstream body.",
68
+ description: "Perform one redirect-free same-origin /healthz request with a 10-second timeout. This tool accepts no arguments and requires the configured n8n URL and API key; remote plaintext HTTP also requires N8N_ALLOW_INSECURE_HTTP=1. Use it for HTTP reachability, then use n8n_workflows_list to test API-key capability. No workflow data is read or changed. Returns ok=true and the successful status, never the upstream body.",
69
69
  operation: "read-only",
70
70
  outputDataDescription: "Object with ok=true and the successful n8n health endpoint HTTP status. It proves bounded reachability, not authenticated Public API capability.",
71
71
  input: {},
@@ -112,7 +112,7 @@ export const utilityTools = Object.freeze([
112
112
  defineTool({
113
113
  name: "n8n_audit_generate",
114
114
  title: "Generate security audit",
115
- description: "Run n8n's broad, non-destructive instance security scan. Use it for an owner-approved audit; use n8n_introspect for local analysis of one workflow. Omitting categories lets n8n choose its complete default, while daysAbandonedWorkflow changes only the inactive-workflow threshold. Requires unsafe mode and owner-authorized API access; returns a sanitized but untrusted report without changing instance configuration.",
115
+ description: "Run n8n's broad, non-destructive instance security scan. Omit categories to use n8n's complete default, or supply the exact risk areas to include. daysAbandonedWorkflow is independent: it changes only the inactive-workflow age threshold and does not select categories. Use n8n_introspect for one workflow. Requires unsafe mode and owner-authorized API access; returns a sanitized but untrusted report without changing configuration.",
116
116
  operation: "unsafe",
117
117
  preserveValidatedRootRecordValues: true,
118
118
  outputDataDescription: "Map of upstream report titles to validated reports. Each report has one official risk category and bounded sections with recommendations plus typed locations or instance details; all content is sanitized and untrusted.",
@@ -149,7 +149,7 @@ export const utilityTools = Object.freeze([
149
149
  defineTool({
150
150
  name: "n8n_search_workflows",
151
151
  title: "Search workflows",
152
- description: "Search one workflow page locally with a case-insensitive substring. Use it for name, node-type, or tag-name matching; use n8n_workflows_list for an unsearched API page. searchIn selects local fields after active filters upstream; cursor and limit select one page, not a global index, so continue nextCursor for coverage. Requires workflow-list permission; returns at most 50 value-free matches and explicit scan state.",
152
+ description: "Search one workflow page for a case-insensitive substring. query is matched locally only in the fields named by searchIn; active filters upstream first. Omit cursor for page one, then keep query, searchIn, active, and limit unchanged when passing nextCursor. This is not a global index; use n8n_workflows_list for an unsearched page. Requires workflow-list permission; returns at most 50 value-free matches and explicit scan state.",
153
153
  operation: "read-only",
154
154
  outputDataDescription: "Object with query, workflowsExamined, matches (up to 50 identity/state records with matchedIn scopes), nextCursor, scanComplete, and truncated. It covers one requested workflow page, not a global index.",
155
155
  input: {
@@ -215,7 +215,7 @@ export const utilityTools = Object.freeze([
215
215
  defineTool({
216
216
  name: "n8n_get_node_docs",
217
217
  title: "Get node documentation",
218
- description: "Return one immutable bundled reference selected by the four-value node key. Use it for offline orientation; use n8n_list_node_types to inventory observed types and official docs for any other node. Requires no n8n URL, API key, network, or elevated mode; it never follows the included URL. Returns source/fetched provenance, canonical type, title, summary, guidance, and official URL.",
218
+ description: "Return one immutable bundled reference. node is an exact local key—webhook, code, http-request, or if—not an arbitrary n8n node-type string. Use n8n_list_node_types to inventory observed types and official docs for anything else. Requires no n8n URL, API key, network, or elevated mode; it never follows the included URL. Returns source/fetched provenance, canonical type, title, summary, guidance, and official URL.",
219
219
  operation: "read-only",
220
220
  outputDataDescription: "Bundled reference with source=bundled_offline_reference, fetched=false, node type, title, concise summary/guidance, and official documentation URL. No network fetch occurs.",
221
221
  openWorld: false,
@@ -233,7 +233,7 @@ export const utilityTools = Object.freeze([
233
233
  defineTool({
234
234
  name: "n8n_list_node_types",
235
235
  title: "List observed node types",
236
- description: "Inventory node-type strings observed across bounded workflow pages. Use it for usage evidence, not installed availability; use n8n_get_node_docs for bundled references or n8n_community_packages_list for package metadata. cursor selects the start, maxPages bounds continuation, and active filters upstream; a complete result requires starting at the first page and reaching the end. Requires workflow-list permission; returns counts and exact coverage under 30-second/20,000-node budgets.",
236
+ description: "Inventory node types observed across workflow pages; this proves usage, not installation. Omit cursor to start at page one. maxPages limits how many pages this call follows; active and limit apply to every page, so keep them unchanged when continuing from nextCursor. Complete coverage requires starting at page one and reaching the end. Use n8n_get_node_docs for bundled references. Requires workflow-list permission; returns counts and exact coverage, capped at 30 seconds or 20,000 nodes.",
237
237
  operation: "read-only",
238
238
  outputDataDescription: "Observed-workflow inventory with scope/availabilityStatement, up to 500 sorted types and counts, page/workflow/node counters, coverage booleans, nextCursor, resultComplete, truncated, and omittedTypeCount.",
239
239
  input: {
@@ -399,7 +399,7 @@ export const utilityTools = Object.freeze([
399
399
  defineTool({
400
400
  name: "n8n_community_packages_list",
401
401
  title: "List community packages",
402
- description: "List bounded metadata for installed n8n community packages. Use it for package inventory; use n8n_list_node_types for types actually observed in workflows. This zero-input call retains at most 100 of the endpoint's records and reports exact total/omitted counts; it does not download or inspect package contents. Requires package-list permission, never installs, updates, or removes packages, and returns untrusted metadata with author emails redacted.",
402
+ description: "List bounded metadata for installed n8n community packages. This call accepts no arguments, filters, cursor, or limit; it keeps at most 100 endpoint records and reports exact omissions. Use n8n_list_node_types for types observed in workflows. It does not download or inspect contents. Requires package-list permission, never installs, updates, or removes packages, and returns untrusted metadata with author emails redacted.",
403
403
  operation: "read-only",
404
404
  outputDataDescription: "Object with data (at most 100 package metadata records), totalCount, truncated, and exact omittedCount. Records may include packageName, installedVersion, author metadata, and timestamps; author emails are redacted.",
405
405
  input: {},
@@ -651,7 +651,7 @@ export const workflowTools = Object.freeze([
651
651
  defineTool({
652
652
  name: "n8n_workflows_list",
653
653
  title: "List workflows",
654
- description: "List one Public API page of workflows visible to the configured API key. Use it for unsearched discovery; use n8n_search_workflows for local substring matching or n8n_workflows_get when the ID is known. Filters are applied upstream, cursor resumes a prior page, and this call never auto-paginates. Requires workflow-list permission; returns projected workflows and nextCursor, never pin/static values.",
654
+ description: "List one Public API page of workflows visible to the configured API key. Use n8n_search_workflows for substring matching or n8n_workflows_get for a known ID. Omit cursor for page one. active, tags, and name are combined upstream; keep those filters and excludePinnedData unchanged when passing nextCursor. limit sizes only that page, and the tool never auto-paginates. Requires workflow-list permission; returns projected workflows and nextCursor, never pin/static values.",
655
655
  operation: "read-only",
656
656
  outputDataDescription: "Object with data (up to 100 projected workflows) and nextCursor (string or null). Each workflow includes structure/state plus pinDataPresent and staticDataPresent presence markers, but never pinned or static values.",
657
657
  input: {
@@ -698,7 +698,7 @@ export const workflowTools = Object.freeze([
698
698
  defineTool({
699
699
  name: "n8n_workflows_get",
700
700
  title: "Get workflow",
701
- description: "Get one current workflow by stable ID for inspection or reviewed editing. Use n8n_workflows_get_version for retained history or n8n_workflows_list for discovery. excludePinnedData controls only the upstream request and presence reporting; values are always withheld. Requires permission to read the target; returns structure, state, settings, and presence flags.",
701
+ description: "Get one current workflow for inspection or reviewed editing. workflowId is the stable ID returned by n8n_workflows_list or create, not a workflow name or version ID. Use n8n_workflows_get_version for history. excludePinnedData changes only the upstream request and the pinDataPresent/staticDataPresent flags; pinned values are withheld whether true or false. Requires permission to read the target; returns structure, state, settings, and presence flags.",
702
702
  operation: "read-only",
703
703
  outputDataDescription: "Current workflow projection with id, optional versionId/description/state, name, nodes, connections, settings, and pinDataPresent/staticDataPresent markers. Pinned and static values are never returned.",
704
704
  input: {
@@ -716,7 +716,7 @@ export const workflowTools = Object.freeze([
716
716
  defineTool({
717
717
  name: "n8n_workflows_create",
718
718
  title: "Create workflow",
719
- description: "Create and persist one workflow as an additive write without activating its triggers. Use it for a new workflow; use n8n_workflows_update for an existing one and n8n_workflows_activate separately for triggers. nodes and connections must describe one complete consistent graph; optional pin/static inputs are accepted but never returned. Requires write or unsafe mode plus create permission; returns the validated projection.",
719
+ description: "Create and persist one workflow as an additive write without activating its triggers. name, nodes, and connections define a new record; nodes and connections must form one complete graph, while omitting settings uses an empty object. Optional pinData, staticData, description, and nodeGroups are forwarded only when supplied. Use n8n_workflows_update for an existing workflow and activate for triggers. Requires write or unsafe mode plus create permission; returns the validated projection without pin/static values.",
720
720
  operation: "write",
721
721
  outputDataDescription: "Created workflow projection with id, optional versionId/description/state, name, nodes, connections, settings, and withheld pin/static presence markers. Pinned and static values are never returned.",
722
722
  destructive: false,
@@ -764,7 +764,7 @@ export const workflowTools = Object.freeze([
764
764
  defineTool({
765
765
  name: "n8n_workflows_update",
766
766
  title: "Update workflow",
767
- description: "Update selected top-level workflow fields through a non-atomic full PUT. Use n8n_update_node for one node property and n8n_workflows_update_tags for tag assignments. expectedVersionId must match both pre-write reads; omitted writable fields are preserved, while supplied nodes or other composite fields replace that field completely. Requires write/unsafe mode plus read and update permission; returns the confirmed projection.",
767
+ description: "Update selected top-level fields through a non-atomic full PUT. workflowId selects the current record; copy expectedVersionId from its latest read, and supply at least one writable field. The version must match both pre-write reads. Omitted fields are preserved, but each supplied array or object replaces that whole field. Use n8n_update_node for one node property and n8n_workflows_update_tags for tags. Requires write/unsafe mode plus read and update permission; returns the confirmed projection.",
768
768
  operation: "write",
769
769
  outputDataDescription: "Confirmed updated workflow projection with id, version/state, name, optional description, nodes, connections, settings, and withheld pin/static presence markers. Omitted writable fields come from the immediate pre-write read.",
770
770
  destructive: true,
@@ -838,7 +838,7 @@ export const workflowTools = Object.freeze([
838
838
  defineTool({
839
839
  name: "n8n_update_node",
840
840
  title: "Update one workflow node",
841
- description: "Update one validated path on one workflow node while preserving sibling data. Use it instead of n8n_workflows_update for a single-node change; use the full update for top-level or multi-node edits. path selects the mutable root whose contract validates value, and both version/risk guards must match before the non-atomic PUT. Requires write/unsafe mode plus read and update permission; returns the changed path and residual race risk.",
841
+ description: "Update one path on one node while preserving sibling data. workflowId selects the current workflow, nodeId selects exactly one node inside it, and path selects the mutable field whose contract validates value. Copy expectedVersionId from the latest workflow read and set acknowledgeNonAtomicRisk=true; both guards are required before the full PUT. Use n8n_workflows_update for top-level or multi-node edits. Requires write/unsafe mode plus read and update permission; returns the changed path and residual race risk.",
842
842
  operation: "write",
843
843
  outputDataDescription: "Object with workflowId, versionId, nodeId, path, updated=true, atomic=false, and residualRisk describing the Public API's non-atomic full-workflow PUT limitation.",
844
844
  destructive: true,
@@ -916,7 +916,7 @@ export const workflowTools = Object.freeze([
916
916
  defineTool({
917
917
  name: "n8n_workflows_delete",
918
918
  title: "Delete workflow",
919
- description: "Permanently delete one workflow and its saved definition. Use n8n_workflows_archive when reversible removal is sufficient and n8n_workflows_get for inspection. This action has no rollback or transfer. Requires unsafe mode plus delete permission; returns the request-bound ID with deleted=true.",
919
+ description: "Permanently delete one workflow and its saved definition. workflowId must be the current stable ID from n8n_workflows_list or get; a name or version ID is not accepted. Use n8n_workflows_archive for reversible removal and n8n_workflows_get for inspection. This action has no rollback or transfer. Requires unsafe mode plus delete permission; returns the request-bound workflowId with deleted=true.",
920
920
  operation: "unsafe",
921
921
  outputDataDescription: "Object with the validated input workflowId and deleted=true. Identity is bound to the request and does not rely on an upstream response body.",
922
922
  input: { workflowId: identifier("Stable ID of the workflow to delete.") },
@@ -931,8 +931,8 @@ export const workflowTools = Object.freeze([
931
931
  name: `n8n_workflows_${action}`,
932
932
  title: `${action === "activate" ? "Activate" : "Deactivate"} workflow`,
933
933
  description: action === "activate"
934
- ? "Activate one workflow so production triggers can accept future events; this does not execute it immediately. Use it only after n8n_workflows_get review; use n8n_workflows_deactivate to stop future triggers. Requires unsafe mode plus activation permission; returns target-validated active state metadata."
935
- : "Deactivate one workflow's future production triggers without deleting saved data or stopping executions already running. Use n8n_workflows_activate to reverse this state or n8n_workflows_archive for lifecycle removal. Requires unsafe mode plus deactivation permission; returns target-validated inactive state metadata.",
934
+ ? "Activate one workflow so production triggers can accept future events; this does not execute it now. workflowId must be the current stable ID from n8n_workflows_list or get, not a name or version ID. Review the workflow first; use n8n_workflows_deactivate to stop future triggers. Requires unsafe mode plus activation permission; returns target-validated active state metadata."
935
+ : "Deactivate one workflow's future production triggers without deleting saved data or stopping runs already in progress. workflowId must be the current stable ID from n8n_workflows_list or get, not a name or version ID. Use n8n_workflows_activate to reverse the state or archive for lifecycle removal. Requires unsafe mode plus deactivation permission; returns target-validated inactive state metadata.",
936
936
  operation: "unsafe",
937
937
  outputDataDescription: action === "activate"
938
938
  ? "Target-bound workflow metadata with required id and active=true; optional name, type, archive state, versionId, and timestamps are included when supplied by n8n, while all other fields are omitted."
@@ -948,7 +948,7 @@ export const workflowTools = Object.freeze([
948
948
  defineTool({
949
949
  name: "n8n_workflows_get_version",
950
950
  title: "Get workflow version",
951
- description: "Get one retained historical workflow snapshot selected by workflowId and versionId. Use it when exact historical nodes/connections are needed; use n8n_workflows_get for current state or n8n_workflows_diff for value-free comparison. Both returned IDs must match the selectors; a missing endpoint and a pruned version share an explicit ambiguous 404 outcome. Requires history-read permission; returns validated structure subject to retention.",
951
+ description: "Get one retained workflow snapshot. workflowId is the stable workflow ID; versionId is that workflow's separate retained-version ID. Do not swap them or pass a workflow name. Use n8n_workflows_get for current state or n8n_workflows_diff for comparison. Both returned IDs must match the selectors; a missing endpoint and a pruned version share an explicit ambiguous 404 outcome. Requires history-read permission; returns validated nodes and connections subject to retention.",
952
952
  operation: "read-only",
953
953
  outputDataDescription: "Historical snapshot with workflowId, versionId, optional name, nodes, and connections. Historical settings, pin data, and static data are unavailable from the supported Public API.",
954
954
  input: {
@@ -964,7 +964,7 @@ export const workflowTools = Object.freeze([
964
964
  defineTool({
965
965
  name: "n8n_workflows_get_tags",
966
966
  title: "Get workflow tags",
967
- description: "List the tags currently assigned to one workflow. Use it before n8n_workflows_update_tags when assignments must be preserved; use n8n_tags_list to discover all available tags. This endpoint has no cursor: the tool retains at most 100 records and reports totalCount plus exact omissions. Requires permission to read the workflow's assignments; returns validated tag metadata.",
967
+ description: "List tags currently assigned to one workflow. workflowId selects the workflow from n8n_workflows_list or get; it is not a tag ID and this call does not list unassigned tags. Use it before n8n_workflows_update_tags when assignments must be preserved; use n8n_tags_list for all tags. The endpoint has no cursor, so the tool keeps at most 100 records and reports exact omissions. Requires assignment-read permission; returns validated tag metadata.",
968
968
  operation: "read-only",
969
969
  outputDataDescription: "Object with data (at most 100 validated tag records), totalCount, truncated, and exact omittedCount. Each tag may include id, name, createdAt, and updatedAt.",
970
970
  input: {
@@ -977,7 +977,7 @@ export const workflowTools = Object.freeze([
977
977
  defineTool({
978
978
  name: "n8n_workflows_update_tags",
979
979
  title: "Update workflow tags",
980
- description: "Replace the complete tag assignment for one workflow; this is not a merge. Use n8n_workflows_get_tags first to preserve IDs and n8n_tags_create for a missing tag. tagIds is the entire desired set, so omitted IDs are removed and an empty array clears all tags. Requires write/unsafe mode plus assignment permission; returns n8n's validated replacement.",
980
+ description: "Replace one workflow's complete tag assignment; this is not a merge. workflowId selects the workflow, while every tagIds item must be a stable tag ID from n8n_tags_list or get; tag names are not accepted. tagIds is the whole desired set, so omitted IDs are removed and [] clears all tags. Use n8n_workflows_get_tags first to preserve assignments. Requires write/unsafe mode plus assignment permission; returns n8n's validated replacement.",
981
981
  operation: "write",
982
982
  outputDataDescription: "Array of at most 100 validated tag records returned by n8n after replacing the workflow's complete assignment. An empty returned array represents no assigned tags.",
983
983
  destructive: true,
@@ -1001,8 +1001,8 @@ export const workflowTools = Object.freeze([
1001
1001
  name: `n8n_workflows_${action}`,
1002
1002
  title: `${action === "archive" ? "Archive" : "Unarchive"} workflow`,
1003
1003
  description: action === "archive"
1004
- ? "Archive one workflow without deleting its saved definition. Use it instead of n8n_workflows_delete for reversible lifecycle removal; use n8n_workflows_deactivate only to stop triggers. The availability change can disrupt callers. Requires unsafe mode plus archive permission; returns target-validated archive state."
1005
- : "Restore one archived workflow without activating its triggers. Use it to reverse n8n_workflows_archive; call n8n_workflows_activate separately only if triggers should resume. Current active state is not inferred. Requires unsafe mode plus unarchive permission; returns target-validated archive state.",
1004
+ ? "Archive one workflow without deleting its saved definition. workflowId must be the current stable ID from n8n_workflows_list or get, not a name or version ID. Use this instead of delete for reversible removal; use deactivate only to stop triggers. The availability change can disrupt callers. Requires unsafe mode plus archive permission; returns target-validated metadata with isArchived=true."
1005
+ : "Restore one archived workflow without activating its triggers. workflowId must identify the archived workflow itself, not a name or version ID. Use this to reverse n8n_workflows_archive; call activate separately only if triggers should resume. Current active state is not inferred. Requires unsafe mode plus unarchive permission; returns target-validated metadata with isArchived=false.",
1006
1006
  operation: "unsafe",
1007
1007
  outputDataDescription: action === "archive"
1008
1008
  ? "Target-bound workflow metadata with required id and isArchived=true; optional name, type, active state, versionId, and timestamps are included when supplied by n8n, while all other fields are omitted."
@@ -1018,7 +1018,7 @@ export const workflowTools = Object.freeze([
1018
1018
  defineTool({
1019
1019
  name: "n8n_workflows_diff",
1020
1020
  title: "Compare workflow versions",
1021
- description: "Compare one retained workflow version with another or current state without returning changed values. Use it for review; use n8n_workflows_get_version when raw historical structure is needed. Omitting toVersionId selects current, while ignoreLayout=true suppresses position-only changes; selectors must differ. Requires workflow/history read permission; returns coverage, counts, and a byte-bounded prefix of up to 200 changes.",
1021
+ description: "Compare two states of one workflow without returning changed values. workflowId selects the workflow; fromVersionId is the retained baseline, and toVersionId is a different retained target. Omit toVersionId to compare with current state. ignoreLayout=true hides position-only changes. Use n8n_workflows_get_version for raw history. Requires workflow/history read permission; returns coverage, counts, and a byte-bounded prefix of up to 200 changes.",
1022
1022
  operation: "read-only",
1023
1023
  outputDataDescription: "Value-free comparison with workflowId, from/to selectors, comparisonCoverage, summary counts, byte-budgeted changes (up to 200), truncated, and exact omittedDetails. Modified nodes name changed fields; parameterChanges expose only sanitized paths, presence, value types, changed=true, and exact truncation metadata, while credential changes expose only referenceChanged=true. Raw values and webhook identifiers are never returned.",
1024
1024
  input: {
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = "0.2.0";
1
+ export const PACKAGE_VERSION = "0.2.1";
@@ -18,7 +18,7 @@ release evidence from the client-matrix gates that remain open.
18
18
 
19
19
  The package declares Node.js `>=22.0.0`. That engine range is an acceptance
20
20
  floor, not evidence that every later major has completed the release matrix.
21
- The supported release lines for v0.2.0 are Node.js 22 and 24.
21
+ The supported release lines for v0.2.1 are Node.js 22 and 24.
22
22
 
23
23
  ## Upgrading from 0.1.x
24
24
 
@@ -36,7 +36,7 @@ update and rollback steps.
36
36
 
37
37
  ## n8n Community Edition
38
38
 
39
- The v0.2.0 floor candidate is n8n Community Edition 2.30.5, with 2.30.7 as the
39
+ The v0.2.1 floor candidate is n8n Community Edition 2.30.5, with 2.30.7 as the
40
40
  current comparison target. The compiled public 44-tool candidate passed fresh,
41
41
  disposable lifecycles on both versions with synthetic fixtures, API-key
42
42
  revocation, resource cleanup, enforced candidate-process egress isolation, and
@@ -48,7 +48,7 @@ browser cookies, interactive `/rest` routes, paid project transfers, arbitrary
48
48
  workflow execution, or runtime node-catalog downloads.
49
49
 
50
50
  `n8n_credentials_list` is supported only from n8n 2.30.5 onward. Project
51
- selectors are intentionally absent because v0.2.0 does not present paid project
51
+ selectors are intentionally absent because v0.2.1 does not present paid project
52
52
  capabilities as Community features. Other tools may also depend on endpoint
53
53
  availability and the permissions assigned to the API key; an HTTP 403 does not
54
54
  by itself mean the endpoint is absent.
@@ -87,7 +87,7 @@ surface.
87
87
 
88
88
  ## Deliberate exclusions
89
89
 
90
- v0.2.0 does not include:
90
+ v0.2.1 does not include:
91
91
 
92
92
  - folders or data tables;
93
93
  - beta evaluation endpoints;
@@ -1,6 +1,6 @@
1
1
  # Security model
2
2
 
3
- This document describes the controls and residual risks of the v0.2.0 source
3
+ This document describes the controls and residual risks of the v0.2.1 source
4
4
  candidate. It is a threat model, not a claim that the software or the connected
5
5
  n8n instance is invulnerable.
6
6
 
package/docs/tools.md CHANGED
@@ -431,19 +431,23 @@ Asks n8n to retry one eligible saved execution.
431
431
 
432
432
  ## n8n_executions_stop
433
433
 
434
- Stops one currently running execution.
434
+ Stops one execution whose state is `new`, `unknown`, `waiting`, or `running`.
435
435
 
436
436
  - **Policy and endpoint:** unsafe; `POST /executions/{executionId}/stop`;
437
437
  `RO=false, D=true, I=false, OW=true`.
438
438
  - **Requirements:** Requires mode `unsafe` and API-key permission to stop the execution.
439
- - **Community Edition:** Verified on Community 2.30.5 and 2.30.7. Only currently stoppable executions succeed; completed external effects are not rolled back.
439
+ - **Community Edition:** Verified on Community 2.30.5 and 2.30.7. Stoppable
440
+ states are `new`, `unknown`, `waiting`, and `running`; completed external
441
+ effects are not rolled back.
440
442
  - **Inputs:** required `executionId`.
441
443
  - **Returns:** the validated input ID, a `stopped` boolean derived from the
442
- validated upstream `status`/`finished` body fields (n8n answers HTTP 200 even
443
- for executions that already finished, so HTTP success alone never asserts a
444
- stop), a `state` of `stopped`, `already_finished`, or `unknown`, passthrough
445
- `finished` when present, and allowlisted upstream status/timestamp when
446
- present. Identity never depends on the upstream body.
444
+ validated upstream `status`/`finished` body fields. Supported n8n versions
445
+ reject an already-terminal target. For any successful body, only `canceled`
446
+ maps to `stopped`; another terminal status maps defensively to
447
+ `already_finished`, and an unclear body maps to `unknown`. HTTP success alone
448
+ never asserts a stop. The tool also returns `finished` when present and
449
+ allowlisted upstream status/timestamp metadata. Identity never depends on the
450
+ upstream body.
447
451
  - **Failures and privacy:** a denied mode issues zero requests. n8n
448
452
  may reject a terminal, missing, or non-stoppable execution; stopping may leave
449
453
  external side effects already performed by earlier nodes.
@@ -771,10 +775,14 @@ Performs a short same-origin health check against the configured n8n instance.
771
775
 
772
776
  - **Policy and endpoint:** read-only; `GET /healthz` outside the Public API
773
777
  prefix with a 10-second timeout; `RO=true, D=false, I=true, OW=true`.
774
- - **Requirements:** Requires valid connected configuration because the shared HTTP boundary supplies the API key, even though `/healthz` itself is a root health route.
778
+ - **Requirements:** Requires valid connected configuration because the shared
779
+ HTTP boundary supplies the API key, even though `/healthz` itself is a root
780
+ health route. A non-loopback plaintext HTTP URL also requires
781
+ `N8N_ALLOW_INSECURE_HTTP=1`.
775
782
  - **Community Edition:** Verified on Community 2.30.5 and 2.30.7. It proves HTTP reachability only, not workflow, queue, or credential health.
776
783
  - **Inputs:** none. Connected configuration still requires `N8N_API_URL` and
777
- `N8N_API_KEY` because the shared client owns the request boundary.
784
+ `N8N_API_KEY` because the shared client owns the request boundary; the
785
+ plaintext-HTTP exception above remains explicit opt-in.
778
786
  - **Returns:** `ok: true` and the successful HTTP status.
779
787
  - **Failures and privacy:** invalid configuration, DNS/TLS/connectivity failure,
780
788
  redirect, timeout, oversize, or non-success status returns a fixed sanitized
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-mcp-community",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "A security-focused MCP server for self-hosted n8n Community Edition.",
5
5
  "private": false,
6
6
  "releaseState": "candidate",
package/server.json CHANGED
@@ -8,13 +8,13 @@
8
8
  "url": "https://github.com/drzamarian/n8n-mcp-community",
9
9
  "source": "github"
10
10
  },
11
- "version": "0.2.0",
11
+ "version": "0.2.1",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "n8n-mcp-community",
17
- "version": "0.2.0",
17
+ "version": "0.2.1",
18
18
  "transport": {
19
19
  "type": "stdio"
20
20
  },