UNPKG

n8n

Version:

n8n Workflow Automation Tool

384 lines (327 loc) 22.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.externalServicesSkill = externalServicesSkill; const api_types_1 = require("@n8n/api-types"); const zod_to_json_schema_1 = require("zod-to-json-schema"); const schema_text_serializer_1 = require("../../json-config/schema-text-serializer"); const initial_build_prompt_1 = require("../prompts/initial-build.prompt"); const mcpServerSchemaText = (0, schema_text_serializer_1.jsonSchemaToCompactText)((0, zod_to_json_schema_1.zodToJsonSchema)(api_types_1.McpServerConfigSchema)); function externalServicesSkill() { return { id: 'agent-builder-external-services', name: 'Agent Builder External Services', description: 'Use when connecting the target agent to any external product: deciding whether Slack, Discord, Linear, Telegram, or another platform is a chat integration/trigger versus an MCP, node, or workflow tool; adding, removing, or updating chat integrations or MCP servers; and wiring n8n node-backed tools (search_nodes/get_node_types discovery, nodeParameters, node credential slots, $fromAI usage, n8n expressions).', recommendedTools: [ 'resolve_integration', 'list_integration_types', 'configure_channel', 'search_nodes', 'get_node_types', 'ask_credential', 'verify_mcp_server', 'read_config', 'patch_config', ], allowedTools: [ 'resolve_integration', 'list_integration_types', 'configure_channel', 'search_mcp_servers', 'search_nodes', 'get_node_types', 'ask_credential', 'verify_mcp_server', 'get_resource_locator_options', 'ask_questions', 'read_config', 'patch_config', 'write_config', 'load_skill', ], instructions: `\ ## Purpose Use this to connect the target agent to external products across all three surfaces: chat integrations (the \`integrations\` array), MCP servers (\`mcpServers\`), and n8n node tools (entries in \`tools[]\` with \`nodeParameters\`). Decide the right surface first, then follow that section. ## Integration vs Callable Tool Decision Use an integration when the product is the agent's conversation or trigger surface: humans will mention, message, comment to, or resume the agent there, or the agent needs to respond in that same platform conversation context. Use an MCP, node, or workflow tool when the product is only something the agent operates on: searching records, creating tickets, updating objects, or sending a business-process notification while the conversation happens elsewhere. When building an agent that should interact with Slack, Discord, or Telegram, prefer the matching chat integration over an MCP, node, or workflow tool, even when a callable tool could perform the same messaging action. Use a callable tool instead only when the user explicitly asks for one or the requested operation is not supported by the chat integration. Examples: - Slack integration: the agent should be chatted with in Slack, respond in Slack threads, DM users, message channels, add reactions, or render rich UI to Slack users. - Discord integration: the agent should be mentioned or messaged in Discord, respond in Discord threads or DMs, or render approval buttons there. - Telegram integration: the agent should receive or send Telegram messages, continue conversations there, or render supported interactive messages. - Linear integration: the agent should be triggered from Linear issues/comments, understand the current Linear subject, or reply in the same Linear conversation. - Linear callable tools: the agent is triggered from Slack, Preview, a task, or a workflow and only needs to search/create/update Linear tickets via MCP or node tools. For callable (non-chat) services, call \`resolve_integration\` separately per service and follow the returned \`kind\`: \`"mcp"\` -> MCP Servers section below, \`"node"\` -> Node Tools section below. ## Web Search vs Direct HTTP Requests Generic web search, browsing, research, current-information, and source-finding requests must use \`config.webSearch\` according to the system prompt's web-search rules. Do not call \`resolve_integration\` or \`search_nodes\` for these requests. Never add \`n8n-nodes-base.httpRequestTool\` or \`@n8n/n8n-nodes-langchain.toolHttpRequest\` unless the user explicitly requests the HTTP Request Tool or direct HTTP, API, or specific-page fetching. ## Chat Integrations The \`integrations\` array controls how the target agent is triggered. - These are configured external chat platforms, not built-in Preview chat. - Call \`list_integration_types\` first. - Read the returned \`capabilities\`, \`useIntegrationWhen\`, and \`useNodeToolWhen\` fields before deciding to add an integration. - Pick one returned \`type\` and pass it to \`configure_channel\` as \`integrationType\`. ALWAYS use \`configure_channel\` for chat-channel credentials — never \`ask_credential\` or a raw config write. The setup UI it shows creates and persists the channel configuration without publishing the agent; do not follow up with \`patch_config\`/\`write_config\` to write the credential. - ${initial_build_prompt_1.INITIAL_BUILD_NOTE} Instead of \`configure_channel\`: after \`list_integration_types\` returns the matching type, \`read_config()\` then \`patch_config\` adding \`{ "type": "<integrationType>", "credentialId": "" }\` to \`/integrations/-\` (include a minimal valid draft \`settings\` object for telegram) so the channel appears in the agent panel as needing setup. Pass the same \`integrationType\` in the trailing \`finish_setup\` call's \`channels\` array — its card configures or skips the channel itself. Do not call \`configure_channel\` again after \`finish_setup\` handles the card. If skipped, list it in the closing setup checklist pointing at the channel chip in the agent panel. - Preserve existing chat integrations unless the user asked to remove them. - To remove an existing chat integration, call \`read_config\` and inspect \`config.integrations\`. - If exactly one existing integration matches the requested platform, remove that entry with \`patch_config\` by index (or replace \`/integrations\` with a filtered array when clearer). - If multiple existing integrations match the requested platform, ask which one to remove before editing \`integrations\`. - Removing a chat integration means deleting its entry from \`integrations[]\`. Do not call \`configure_channel\` to remove a channel. ### Gotchas - Chat integration types must come from \`list_integration_types\`. - Do not add a chat integration just because the agent needs CRUD or notifications for that product. Resolve the callable capability through \`resolve_integration\` unless the product itself is the chat/trigger context. - For recurring or scheduled runs, create a task (\`create_tasks\`), not an integration. - Omitting \`integrations\` from a config write preserves the current channels. To remove one, write an explicit filtered array or remove the exact array entry. ## MCP Servers MCP servers expose external tool catalogs to the target agent over HTTP. They live on the top-level \`mcpServers\` array, and each entry maps 1:1 to a connected MCP server. Use this section when \`resolve_integration\` returned \`kind: "mcp"\`, the user explicitly asks to add or edit an MCP server, or the user provides or asks to configure a custom MCP server. ### Discovery and setup For a generic external-service request, \`resolve_integration\` must select the integration type before MCP setup. If no resolver result is available yet, call \`resolve_integration\` with queries matching the requested service. Resolve one requested service per call; use \`queries\` only for alternative search terms for that service. - If it returns \`kind: "node"\` for a generic service request, follow the Node Tools section with the returned node results. Stop this MCP workflow. - If it returns \`kind: "node"\` but the user explicitly requested an MCP server, do not silently substitute a node tool. Continue with manual MCP setup by asking for the URL and transport/authentication decision through \`${api_types_1.ASK_QUESTIONS_TOOL_NAME}\`. - \`resolve_integration\` returns \`{ kind: "mcp", results: [...] }\` for MCP matches. Never read server fields from the wrapper; select a result first: - If \`results[]\` contains one entry, use it as \`selectedResult\`. - If the request uniquely identifies one entry by \`name\` or \`title\`, use that entry as \`selectedResult\`. - If multiple candidates remain, call \`ask_questions\` with the candidate titles and descriptions; never choose by array order. During an initial build, do not call \`ask_questions\` for this: pick the best candidate by title/description relevance yourself, and list the pick as an assumption in your summary. Use the chosen entry as \`selectedResult\`. If \`ask_questions\` returns \`{ answered: false }\`, stop MCP setup without selecting a server, asking for credentials, verifying a connection, or mutating config. Do not re-present the question. - Use \`name\`, \`url\`, \`transport\`, \`authentication\`, \`credentialType\`, \`tools\`, and optional \`metadata\` only from \`selectedResult\`. Follow these steps for the selected MCP result: 1. Credential: call \`ask_credential\` with a short \`purpose\`, using \`selectedResult.credentialType\` as \`credentialType\`. Never invent credential IDs. 2. Verify: call \`verify_mcp_server\` with the selected result's \`name\`, \`url\`, \`transport\`, and \`authentication\`, plus the returned \`credentialId\` as \`credential\` when authentication is required. 3. Capability check: confirm the verified tool names and descriptions cover the capability the user requested. 4. Write config: call \`read_config\`, then \`patch_config\` to add the entry to \`mcpServers[]\` using the patch pattern below. When the entry already exists and verify returned \`credentialApplied: true\`, skip this step — the credential is already persisted. ${initial_build_prompt_1.INITIAL_BUILD_NOTE} For MCP that means: pick the best candidate as an assumption (above), then \`read_config()\` and \`patch_config\` a draft \`/mcpServers/-\` entry using \`name\`, \`url\`, \`transport\`, \`authentication\`, and \`metadata.nodeTypeName\` from \`selectedResult\` with \`credential\` omitted, and skip \`verify_mcp_server\` — there is nothing to authenticate yet. Include the credential in the trailing \`finish_setup\` call; verify with the returned credential id — on success the tool writes the credential into the matching entry itself (\`credentialApplied: true\`); no \`read_config\`/\`patch_config\` follow-up for the credential. Existing-agent additions keep the immediate ask + verify flow above unchanged. If verification succeeds but the tools do not cover the requested capability for a generic service request, switch to the Node Tools section, call \`search_nodes\` with the same service queries, and continue with node setup. Do not add the MCP server merely because its registry entry matched. Full schema reference: ${mcpServerSchemaText} ### Tool exposure and approval - Expose every available MCP tool by default: omit \`toolFilter\` unless the user explicitly asks to restrict which tools are exposed. Do not infer an allowlist from the requested capability. - For an explicit filter or selected approval list, use only exact, unprefixed \`name\` values from \`selectedResult.tools\` or a successful \`verify_mcp_server\` result. - Never prepend the server name. Never invent MCP tool names. \`toolFilter.tools\` and \`approval.tools\` match original MCP names; the SDK adds the server prefix only when exposing tools to the model. ### Credential flow - For \`bearerAuth\`, call \`ask_credential\` with \`credentialType: "httpBearerAuth"\`. - For \`headerAuth\`, call \`ask_credential\` with \`credentialType: "httpHeaderAuth"\`. - For \`multipleHeadersAuth\`, call \`ask_credential\` with \`credentialType: "httpMultipleHeadersAuth"\`. - For \`mcpOAuth2Api\`, call \`ask_credential\` with \`credentialType: "mcpOAuth2Api"\`. ### Testing the connection Before writing to config, call \`verify_mcp_server\` with server \`name\`, \`url\`, \`transport\`, and (if applicable) the credential id from \`ask_credential\`. - Success returns \`{ ok: true, tools: [{ name, description }] }\`, and when a matching \`mcpServers\` entry exists, also \`credentialApplied: true, configMutated: true, agentId\` — the credential is written automatically; do not follow with \`read_config\`/\`patch_config\` for the credential. - When verify succeeds but \`credentialApplied: false\` and the entry already exists, fall back to \`read_config\` then \`patch_config\` for the credential. - For an explicitly requested filter or selected approval list, copy exact names from the returned tool list following Tool exposure and approval above. - Failure returns \`{ ok: false, error: "..." }\`. - If verification fails, explain the error and ask the user to check the URL or credentials before proceeding. ### Incomplete setup The user can skip the credential prompt, the URL question, or both. Never invent a credential ID or a placeholder URL to fill the gap, and never abort the server addition — always persist what is known and let the user finish setup later: - Credential skipped (\`ask_credential\` returned \`{ skipped: true }\`): omit only the \`credential\` field. - URL skipped: persist \`url: ""\`. - Either case: skip \`verify_mcp_server\` (there is nothing to authenticate or connect to), then \`read_config\` and \`patch_config\` the entry, preserving every other known field — \`name\`, \`transport\`, \`authentication\`, an already-selected credential, and registry \`metadata\`. ### Selecting credentials When using a registry-backed server, always use the \`credentialType\` returned by \`selectedResult\`. For custom MCP servers, if credential type is unknown, ask the user which credential type to use (OAuth2, Bearer Token, Header Auth, Multiple Headers Auth, or None) via \`${api_types_1.ASK_QUESTIONS_TOOL_NAME}\`. Then map to: - \`bearerAuth\` -> \`ask_credential\` with \`credentialType: "httpBearerAuth"\` - \`headerAuth\` -> \`ask_credential\` with \`credentialType: "httpHeaderAuth"\` - \`multipleHeadersAuth\` -> \`ask_credential\` with \`credentialType: "httpMultipleHeadersAuth"\` - \`mcpOAuth2Api\` -> \`ask_credential\` with \`credentialType: "mcpOAuth2Api"\` ### Patch pattern 1. Initialize the array if missing: \`{ "op": "add", "path": "/mcpServers", "value": [] }\` 2. Append each server: \`{ "op": "add", "path": "/mcpServers/-", "value": { ... } }\` ### Gotchas - Server \`name\` must be unique across \`mcpServers\` within an agent. - Never fabricate \`metadata.nodeTypeName\`. - When \`selectedResult\` includes \`metadata.nodeTypeName\`, include \`metadata: { nodeTypeName: <selectedResult.metadata.nodeTypeName> }\` in the entry so the UI can render the correct server form. - A registry match proves server availability, not support for the requested capability; use the verified live tool list for that decision. ## Node Tools Use this section to discover, configure, and wire node tools into the target agent's \`tools[]\`, including \`nodeParameters\` and n8n expressions. ### Workflow - For a generic external-service request, call \`resolve_integration\` before node discovery unless a resolver result is already available. - If it returns \`kind: "mcp"\`, follow the MCP Servers section instead and stop this node-tool workflow. - If it returns \`kind: "node"\`, use its returned node results and call \`get_node_types\`; do not repeat the same search with \`search_nodes\`. - Call \`search_nodes\` directly only when the user explicitly requests an n8n node, when refining node results, or when a verified MCP server lacks the requested capability. - Never guess node type names. - Use the tool node id from discovery, usually ending in \`Tool\`. - Put fixed values in \`nodeParameters\`; use complete n8n expressions for values the agent should decide at runtime: \`={{ $fromAI('url', 'The URL to inspect', 'string') }}\`. - For stable dynamic selectors, load \`agent-builder-resource-locators\` and follow it. - Never write literal \`"$fromAI"\` or bare \`$fromAI\`; the node will treat it as the actual value. - Do not pipe AI-chosen fields through \`$json\`. - Do not include \`inputSchema\` or \`toolDescription\` for node tools. - n8n Connect (\`n8n credits\`) covers many services, including some community nodes. Adding a node tool with its credential slot omitted triggers server-side assignment: for a covered service the server attaches the managed \`n8n credits\` credential (\`{ id: null, name: "n8n credits", __aiGatewayManaged: true }\`) to each required, eligible slot on write — but only when the project has no credential of that type; an existing credential of the type wins and the slot stays empty for the normal credential flow below. Add the tool with the credential slot omitted, then \`read_config\`. - Exception — when the user explicitly asks to run a tool on n8n credits, write \`{ "id": null, "name": "n8n credits", "__aiGatewayManaged": true }\` into that credential slot yourself: the server keeps it when the service is covered (even if the user has their own credential of the type) and removes it when not covered — check \`read_config\` after the write and resolve a real credential if it was removed. - The \`n8n credits\` managed credential IS the real, working credential — the tool executes through n8n's gateway on n8n credits, so NO separate API key is needed. It is NOT a placeholder and NOT "invalid for the service", even for a community node. For a slot \`read_config\` shows populated with it: the slot is fully connected and the tool WILL run. Do NOT call \`ask_credential\` for it; do NOT include it in \`finish_setup\`; NEVER clear, remove, or replace it via \`patch_config\`; and NEVER seek a "real" API key to swap in for it. Report the tool as ready, running on n8n credits — exactly like a managed model. Never tell the user the credential is "not connected"/"not set up" or that the tool "won't run until a credential is added". - Only for a required slot that \`read_config\` shows still empty after the write (a service n8n Connect does not cover) do you resolve a real credential: call \`ask_credential\` once before the config mutation for an addition to an existing agent. ${initial_build_prompt_1.INITIAL_BUILD_NOTE} After the trailing \`finish_setup\` resolves the credential, copy the returned credentials into \`node.credentials\` via \`patch_config\`; for resource-locator resolution follow \`agent-builder-resource-locators\` then. Pass the node's credential key as \`credentialSlot\`. On success, copy the returned \`credentials\` object directly to \`node.credentials\`. If skipped, still add the tool and omit only that credential slot. - When the agent already has a chat channel configured and the tool needs the same credential type, \`ask_credential\` reuses the channel's credential automatically — do not ask the user to pick a different one. ### n8n Expressions Node tool parameters inside \`nodeParameters\` can use n8n expressions. Prefer \`$fromAI\` whenever the target agent should decide a value at runtime. Do not use \`$fromAI\` for stable resource IDs that the target agent cannot know at runtime, such as Linear \`teamId\`, project IDs, channel IDs, calendar IDs, database IDs, table IDs, or other dynamic "Name or ID" selectors. Resolve those with the \`agent-builder-resource-locators\` skill, \`ask_credential\`, and \`get_resource_locator_options\`; write the returned \`parameterValue\` into \`nodeParameters\`. - \`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('fieldName', 'What value to provide', 'string') }}\` - \`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('count', 'How many items', 'number') }}\` - \`={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('enabled', 'Whether to enable this option', 'boolean') }}\` - \`={{ $now.toISO() }}\` for current date/time. - \`={{ $today }}\` for the start of today. Always wrap expressions in \`={{ }}\`. Never pipe AI-chosen node-tool fields through \`$json\`; use \`$fromAI\` for those fields instead. ### Gotchas - Do not include \`inputSchema\` or \`toolDescription\` for node tools. - \`$fromAI(...)\` placeholders define the node tool input schema; do not add it manually. - Follow \`agent-builder-resource-locators\` for dynamic selector lookup, credentials, and \`parameterValue\` handling. - If a required node-tool credential is skipped, add the tool and omit only that credential slot. - Node tools execute inline, so never use waiting operations such as \`sendAndWait\` or \`dispatchAndWait\`. When the user requests human approval, configure the intended non-waiting operation and set \`requireApproval: true\` on the tool. ## Verify - Configured chat integrations were set up through \`configure_channel\` or the initial-build \`finish_setup\` channel card, not \`ask_credential\` or a manual config write. - The chosen integration matches \`useIntegrationWhen\`; otherwise resolve the callable capability through \`resolve_integration\` and use MCP, node, or workflow tools. - Generic non-chat external services were routed through \`resolve_integration\` before MCP or node setup. - The final \`integrations\` array keeps unrelated integrations intact and removes only the requested channel entries. - Node tools use discovered tool node ids and valid node parameters.`, }; } //# sourceMappingURL=external-services.skill.js.map