Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
30fee12
feat(mcp): bound tool responses with pageable artifacts
LevSky22 Aug 4, 2026
986c9ef
fix(mcp): preserve artifact payloads across utf8 pages
LevSky22 Aug 4, 2026
d26f12d
docs(mcp): clarify optional artifact persistence
LevSky22 Aug 4, 2026
528926b
test(mcp): cover artifact lifecycle and reader integration
LevSky22 Aug 4, 2026
59254b8
test(mcp): close response bounding coverage gaps
LevSky22 Aug 4, 2026
a0ab321
feat(mcp): add structured artifact queries
LevSky22 Aug 5, 2026
cc73aae
fix(test): pass SDK v2 input-required ctx accessor to direct tools/ca…
LevSky22 Aug 6, 2026
ed15606
fix(mcp): never report a lossy artifact result as complete
LevSky22 Aug 6, 2026
ee55532
feat(mcp): add response artifact contract v2
LevSky22 Aug 14, 2026
7304103
test(mcp): strengthen response artifact coverage
LevSky22 Aug 14, 2026
808f0c3
fix(mcp): infer unambiguous artifact envelopes
LevSky22 Aug 17, 2026
bf153e3
fix(mcp): infer selected response envelopes
LevSky22 Aug 17, 2026
b14ceba
fix(mcp): document the artifact wrappers and correct the tool count
Aug 11, 2026
d32cf49
fix(mcp): make artifact access query-only
LevSky22 Aug 19, 2026
00f36e4
docs: clarify artifact pagination contract
LevSky22 Aug 22, 2026
442ede1
chore(upstream): merge n8n-mcp v2.73.0
LevSky22 Aug 27, 2026
504cb3f
feat: unify response artifact contract v3
LevSky22 Aug 22, 2026
b4da8a6
docs: clarify artifact pagination contract
LevSky22 Aug 22, 2026
936f441
feat(mcp): add protocol-native artifact descriptors
LevSky22 Aug 23, 2026
8855bac
test(mcp): enforce camelCase tool inputs
LevSky22 Aug 24, 2026
e3790f2
fix(mcp): normalize response-root-relative paths
LevSky22 Aug 26, 2026
ed5a0d4
fix(mcp): structure artifact query errors
LevSky22 Aug 26, 2026
6b5f01b
fix(mcp): normalize inferred collection fields
LevSky22 Aug 28, 2026
21d555f
chore(upstream): merge n8n-mcp v2.77.0
LevSky22 Sep 2, 2026
023000c
chore(deps): sync lockfile after upstream merge
LevSky22 Sep 2, 2026
bede8d5
fix(mcp): clarify nested artifact field pointers
LevSky22 Sep 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,66 @@ Want to use n8n-MCP with your n8n instance? Check out our comprehensive [n8n Dep
- Cloud deployment on Hetzner, AWS, and other providers
- Troubleshooting and security best practices

### Bounded MCP responses

Compact JSON tool results stay inline up to a conservative 32 KiB budget. Larger workflow,
execution, documentation, and host-injected tool results return a compact
preview capped at 8 KiB plus `responseMeta.artifact`. Prefer `query_response_artifact` to
select, filter, project, search, and paginate structured JSON without loading the full
artifact into model context. Artifact query paths use RFC 6901. Exact document
pointers win; when one is missing and the advertised `responseRoot` is non-empty,
the same pointer is tried once beneath that root and the canonical path is reported
as `responseMeta.inferredResponsePath`. Projected fields accept either root names
such as `id` or pointers such as `/status/name`. Nested paths must begin with
`/`; `status/name` means one literal root key, not a nested lookup. When fields
or filters target an object containing exactly one array child,
the query selects that collection and reports its pointer as
`responseMeta.inferredResponsePath`; ambiguous objects still require a
more specific path. Use
`objectMode: "entries"` to query keyed objects (including native n8n connection
maps) as `{key, value}` rows; filters can then select several keys in one call.
`describe: true` pages shape metadata and returns absolute child pointers for
objects. `textSearch` performs bounded literal search across large string values.
Artifact-query arguments are strict camelCase: `artifactId`, `responsePath`,
`fields`, `filters`, `pageSize`, `cursor`, `describe`, `objectMode`, and
`textSearch`. `pageSize` accepts 1-100 only. To continue beyond 100 matching
items, pass `responseMeta.nextCursor` as `cursor` and keep every other query-view
argument unchanged; snake_case variants are rejected.
Query pages use response contract version 3 and do not repeat
the full artifact descriptor minted by the originating tool. The full serialized MCP result is capped at
128 KiB, artifacts are capped at 50 MiB, expire after 24 hours, and are pruned
at a 1 GiB quota.

`query_response_artifact` advertises an MCP output schema and returns the same bounded
JSON as compact text and `structuredContent`. When an originating call creates an
artifact, its result also includes an `artifact://n8n-mcp/{artifactId}` resource link.
Reading that link returns at most 8 KiB of metadata and query guidance; it never returns
the stored provider payload. Ephemeral artifacts are not added to `resources/list`.
Caller-correctable path, control, cursor, and handle failures also use the output
schema with `isError: true` and a stable error code. Internal storage, corruption,
and hard-limit failures remain server errors instead of being hidden as input mistakes.

Every bounded result reports `responseMeta.complete`, returned/total/remaining
counts, and an opaque next cursor. A compact preview is never exhaustive when
`complete` is false, even if an upstream payload says it is on its last page.
Request another semantic page only when the current page did not answer the question.
Structured cursors are signed and bound to
the artifact, instance scope, and exact query view.

`n8n_executions({action: "list"})` defaults to 20 records. Existing execution
detail modes and their explicit limits remain compatible; any oversized result
is moved behind an artifact reference. `n8n_get_workflow` still defaults to
`full`: small workflows stay inline, while large workflows return their topology
and an artifact reference. Explicit workflow modes remain backward compatible.

By default, artifacts use the container-local `/tmp/n8n-mcp-artifacts` directory.
This is usually appropriate for the 24-hour cache: a container restart discards
outstanding artifact IDs, and callers can repeat the original read. To preserve
artifacts across restarts, opt in with `MCP_RESPONSE_ARTIFACT_ROOT` pointing to a
private persistent directory and set `MCP_RESPONSE_CURSOR_KEY` to a stable secret
so existing page cursors remain valid. Persistence is not required for bounded
responses or artifact paging.

### Cloudflare Access Authentication

If your n8n instance sits behind Cloudflare Access (Zero Trust), provide your service token so n8n-MCP can authenticate:
Expand Down
952 changes: 748 additions & 204 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { logger } from './utils/logger';
import { AuthManager, buildBearerChallenge } from './utils/auth';
import { PROJECT_VERSION } from './utils/version';
import { isN8nApiConfigured } from './config/n8n-api';
import { serializeToolText } from './services/mcp-response-bounding';
import dotenv from 'dotenv';
import { readFileSync } from 'fs';
import { getStartupBaseUrl, formatEndpointUrls, detectBaseUrl } from './utils/url-detector';
Expand Down Expand Up @@ -438,9 +439,10 @@ export async function startFixedHTTPServer() {
const result = await mcpServer.executeTool(toolName, toolArgs);

// Convert result to JSON text for content field
let responseText = JSON.stringify(result, null, 2);
let responseText = serializeToolText(result);

// Build MCP-compliant response with structuredContent for validation tools
// Build MCP-compliant response. Schema-backed tools always include
// structuredContent; compact text remains the compatibility representation.
const mcpResult: MCPToolResponse = {
content: [
{
Expand All @@ -450,9 +452,11 @@ export async function startFixedHTTPServer() {
]
};

// Add structuredContent for validation tools (they have outputSchema)
// Apply 1MB safety limit to prevent memory issues (matches STDIO server behavior)
if (toolName.startsWith('validate_')) {
const toolDefinition = (mcpServer as any).findToolSchema?.(toolName);
const hasStructuredOutput = toolName.startsWith('validate_') || !!toolDefinition?.outputSchema;

// Apply 1MB safety limit to prevent memory issues (matches STDIO server behavior).
if (hasStructuredOutput) {
const resultSize = responseText.length;

if (resultSize > 1000000) {
Expand Down
2 changes: 1 addition & 1 deletion src/mcp/handlers-n8n-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2220,7 +2220,7 @@ export async function handleListExecutions(args: unknown, context?: InstanceCont
const input = listExecutionsSchema.parse(args || {});

const response = await client.listExecutions({
limit: input.limit || 100,
limit: input.limit || 20,
cursor: input.cursor,
workflowId: input.workflowId,
projectId: input.projectId,
Expand Down
Loading
Loading