n8n-workflow-builder-mcp 0.1.0
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/.cursor/rules/cursor_rules.mdc +53 -0
- package/.cursor/rules/dev_workflow.mdc +219 -0
- package/.cursor/rules/mcp.mdc +430 -0
- package/.cursor/rules/self_improve.mdc +72 -0
- package/.cursor/rules/taskmaster.mdc +382 -0
- package/.cursorignore +1 -0
- package/.cursorrules +4 -0
- package/.env.example +23 -0
- package/.eslintrc.json +38 -0
- package/.github/workflows/npm-publish-github-packages.yml +55 -0
- package/.prettierrc +9 -0
- package/.roo/rules/dev_workflow.md +219 -0
- package/.roo/rules/mcp.md +430 -0
- package/.roo/rules/roo_rules.md +53 -0
- package/.roo/rules/self_improve.md +72 -0
- package/.roo/rules/taskmaster.md +382 -0
- package/.roo/rules-architect/architect-rules +93 -0
- package/.roo/rules-ask/ask-rules +89 -0
- package/.roo/rules-boomerang/boomerang-rules +181 -0
- package/.roo/rules-code/code-rules +61 -0
- package/.roo/rules-debug/debug-rules +68 -0
- package/.roo/rules-test/test-rules +61 -0
- package/.roomodes +63 -0
- package/.taskmasterconfig +31 -0
- package/.windsurfrules +2382 -0
- package/LICENSE +21 -0
- package/README.md +210 -0
- package/config/credentials/credentials.json +1 -0
- package/config/default.js +41 -0
- package/package.json +56 -0
- package/scripts/demo-n8n-integration.js +161 -0
- package/scripts/demo-workflow-generator.js +102 -0
- package/scripts/init.sh +36 -0
- package/scripts/prd.txt +197 -0
- package/src/index.ts +1440 -0
- package/src/middleware/auth.js +273 -0
- package/src/middleware/authorize.js +183 -0
- package/src/middleware/logging.js +64 -0
- package/src/middleware/mcp.js +187 -0
- package/src/middleware/rateLimiter.js +82 -0
- package/src/middleware/validation.js +241 -0
- package/src/models/credential.js +359 -0
- package/src/models/llmService.js +236 -0
- package/src/models/n8nIntegration.js +542 -0
- package/src/models/storage.js +196 -0
- package/src/models/tool.js +148 -0
- package/src/models/user.js +164 -0
- package/src/models/workflow.js +229 -0
- package/src/routes/toolDefinitions.js +62 -0
- package/src/routes/toolExecution.js +79 -0
- package/src/tools/__index.js +242 -0
- package/src/tools/connectionManagement.js +500 -0
- package/src/tools/n8nIntegration.js +370 -0
- package/src/tools/nodeDiscovery.js +488 -0
- package/src/tools/nodeManagement.js +674 -0
- package/src/tools/toolDefinitions.js +660 -0
- package/src/tools/workflowCreation.js +100 -0
- package/src/tools/workflowGenerator.js +152 -0
- package/src/tools/workflowStorage.js +113 -0
- package/src/tools/workflowTesting.js +285 -0
- package/src/utils/encryption.js +164 -0
- package/src/utils/logger.js +84 -0
- package/src/utils/mcp.js +85 -0
- package/src/utils/securityLogger.js +109 -0
- package/tests/auth.test.js +402 -0
- package/tests/authorize.test.js +208 -0
- package/tests/run-memory-tests.js +55 -0
- package/tests/run-tests.js +55 -0
- package/tests/server.test.js +203 -0
- package/tests/unit/add-ai-connections.test.js +385 -0
- package/tests/unit/connectionManagement.test.js +309 -0
- package/tests/unit/langchain-llm-format.test.js +259 -0
- package/tests/unit/memory-connection.test.js +140 -0
- package/tests/unit/memory-integration.test.js +253 -0
- package/tests/unit/n8nIntegration.test.js +291 -0
- package/tests/unit/nodeDiscovery.test.js +270 -0
- package/tests/unit/nodeManagement.test.js +522 -0
- package/tests/unit/utils/mcp-test-utils.js +94 -0
- package/tests/unit/workflowCreation.test.js +110 -0
- package/tests/unit/workflowTesting.test.js +269 -0
- package/tests/user.test.js +181 -0
- package/tsconfig.json +20 -0
- package/workflow_nodes/Brandfetch.json +85 -0
- package/workflow_nodes/WorkflowTrigger.json +28 -0
- package/workflow_nodes/actionNetwork.json +218 -0
- package/workflow_nodes/activeCampaign.json +722 -0
- package/workflow_nodes/activeCampaignTrigger.json +52 -0
- package/workflow_nodes/acuitySchedulingTrigger.json +8 -0
- package/workflow_nodes/adalo.json +123 -0
- package/workflow_nodes/affinity.json +203 -0
- package/workflow_nodes/affinityTrigger.json +124 -0
- package/workflow_nodes/aggregate.json +119 -0
- package/workflow_nodes/agileCrm.json +503 -0
- package/workflow_nodes/aiTransform.json +17 -0
- package/workflow_nodes/airtable.json +226 -0
- package/workflow_nodes/airtableTrigger.json +120 -0
- package/workflow_nodes/airtop.json +10 -0
- package/workflow_nodes/amqp.json +62 -0
- package/workflow_nodes/amqpTrigger.json +8 -0
- package/workflow_nodes/apiTemplateIo.json +147 -0
- package/workflow_nodes/asana.json +446 -0
- package/workflow_nodes/asanaTrigger.json +52 -0
- package/workflow_nodes/automizy.json +195 -0
- package/workflow_nodes/autopilot.json +287 -0
- package/workflow_nodes/autopilotTrigger.json +8 -0
- package/workflow_nodes/awsCertificateManager.json +223 -0
- package/workflow_nodes/awsComprehend.json +125 -0
- package/workflow_nodes/awsDynamoDb.json +251 -0
- package/workflow_nodes/awsElb.json +174 -0
- package/workflow_nodes/awsLambda.json +69 -0
- package/workflow_nodes/awsRekognition.json +191 -0
- package/workflow_nodes/awsS3.json +32 -0
- package/workflow_nodes/awsSes.json +302 -0
- package/workflow_nodes/awsSns.json +110 -0
- package/workflow_nodes/awsSnsTrigger.json +47 -0
- package/workflow_nodes/awsSqs.json +140 -0
- package/workflow_nodes/awsTextract.json +43 -0
- package/workflow_nodes/awsTranscribe.json +217 -0
- package/workflow_nodes/azureCosmosDb.json +8 -0
- package/workflow_nodes/azureStorage.json +17 -0
- package/workflow_nodes/bambooHr.json +8 -0
- package/workflow_nodes/bannerbear.json +126 -0
- package/workflow_nodes/baserow.json +277 -0
- package/workflow_nodes/beeminder.json +146 -0
- package/workflow_nodes/bitbucketTrigger.json +62 -0
- package/workflow_nodes/bitly.json +130 -0
- package/workflow_nodes/bitwarden.json +224 -0
- package/workflow_nodes/box.json +457 -0
- package/workflow_nodes/boxTrigger.json +8 -0
- package/workflow_nodes/brevo.json +41 -0
- package/workflow_nodes/brevoTrigger.json +145 -0
- package/workflow_nodes/bubble.json +212 -0
- package/workflow_nodes/calTrigger.json +91 -0
- package/workflow_nodes/calendlyTrigger.json +71 -0
- package/workflow_nodes/chargebee.json +217 -0
- package/workflow_nodes/chargebeeTrigger.json +187 -0
- package/workflow_nodes/circleCi.json +89 -0
- package/workflow_nodes/ciscoWebex.json +593 -0
- package/workflow_nodes/ciscoWebexTrigger.json +159 -0
- package/workflow_nodes/clearbit.json +138 -0
- package/workflow_nodes/clickUp.json +793 -0
- package/workflow_nodes/clickUpTrigger.json +188 -0
- package/workflow_nodes/clockify.json +372 -0
- package/workflow_nodes/clockifyTrigger.json +26 -0
- package/workflow_nodes/cloudflare.json +103 -0
- package/workflow_nodes/cockpit.json +161 -0
- package/workflow_nodes/coda.json +242 -0
- package/workflow_nodes/code.json +40 -0
- package/workflow_nodes/coinGecko.json +363 -0
- package/workflow_nodes/compareDatasets.json +14 -0
- package/workflow_nodes/compression.json +66 -0
- package/workflow_nodes/contentful.json +29 -0
- package/workflow_nodes/convertKit.json +159 -0
- package/workflow_nodes/convertKitTrigger.json +109 -0
- package/workflow_nodes/convertToFile.json +64 -0
- package/workflow_nodes/copper.json +239 -0
- package/workflow_nodes/copperTrigger.json +8 -0
- package/workflow_nodes/cortex.json +348 -0
- package/workflow_nodes/crateDb.json +90 -0
- package/workflow_nodes/cron.json +23 -0
- package/workflow_nodes/crowdDev.json +8 -0
- package/workflow_nodes/crowdDevTrigger.json +8 -0
- package/workflow_nodes/crypto.json +147 -0
- package/workflow_nodes/customerIo.json +206 -0
- package/workflow_nodes/customerIoTrigger.json +185 -0
- package/workflow_nodes/dateTime.json +39 -0
- package/workflow_nodes/debughelper.json +162 -0
- package/workflow_nodes/deepL.json +103 -0
- package/workflow_nodes/demio.json +187 -0
- package/workflow_nodes/dhl.json +53 -0
- package/workflow_nodes/discord.json +81 -0
- package/workflow_nodes/discourse.json +319 -0
- package/workflow_nodes/disqus.json +254 -0
- package/workflow_nodes/drift.json +112 -0
- package/workflow_nodes/dropbox.json +258 -0
- package/workflow_nodes/dropcontact.json +154 -0
- package/workflow_nodes/e2eTest.json +70 -0
- package/workflow_nodes/editImage.json +132 -0
- package/workflow_nodes/egoi.json +220 -0
- package/workflow_nodes/elasticSecurity.json +401 -0
- package/workflow_nodes/elasticsearch.json +422 -0
- package/workflow_nodes/emailReadImap.json +88 -0
- package/workflow_nodes/emailSend.json +38 -0
- package/workflow_nodes/emelia.json +201 -0
- package/workflow_nodes/emeliaTrigger.json +57 -0
- package/workflow_nodes/erpNext.json +139 -0
- package/workflow_nodes/errorTrigger.json +15 -0
- package/workflow_nodes/evaluationMetrics.json +21 -0
- package/workflow_nodes/eventbriteTrigger.json +125 -0
- package/workflow_nodes/executeCommand.json +25 -0
- package/workflow_nodes/executeWorkflow.json +102 -0
- package/workflow_nodes/executeWorkflowTrigger.json +65 -0
- package/workflow_nodes/executionData.json +35 -0
- package/workflow_nodes/extractFromFile.json +71 -0
- package/workflow_nodes/facebookGraphApi.json +234 -0
- package/workflow_nodes/facebookLeadAdsTrigger.json +8 -0
- package/workflow_nodes/facebookTrigger.json +112 -0
- package/workflow_nodes/figmaTrigger.json +8 -0
- package/workflow_nodes/filemaker.json +268 -0
- package/workflow_nodes/filter.json +24 -0
- package/workflow_nodes/flow.json +323 -0
- package/workflow_nodes/flowTrigger.json +52 -0
- package/workflow_nodes/form.json +25 -0
- package/workflow_nodes/formIoTrigger.json +59 -0
- package/workflow_nodes/formTrigger.json +8 -0
- package/workflow_nodes/formstackTrigger.json +8 -0
- package/workflow_nodes/freshdesk.json +584 -0
- package/workflow_nodes/freshservice.json +899 -0
- package/workflow_nodes/freshworksCrm.json +772 -0
- package/workflow_nodes/ftp.json +127 -0
- package/workflow_nodes/function.json +22 -0
- package/workflow_nodes/functionItem.json +22 -0
- package/workflow_nodes/gSuiteAdmin.json +562 -0
- package/workflow_nodes/getResponse.json +310 -0
- package/workflow_nodes/getResponseTrigger.json +82 -0
- package/workflow_nodes/ghost.json +290 -0
- package/workflow_nodes/git.json +184 -0
- package/workflow_nodes/github.json +732 -0
- package/workflow_nodes/githubTrigger.json +317 -0
- package/workflow_nodes/gitlab.json +544 -0
- package/workflow_nodes/gitlabTrigger.json +61 -0
- package/workflow_nodes/gmail.json +62 -0
- package/workflow_nodes/gmailTrigger.json +127 -0
- package/workflow_nodes/goToWebinar.json +430 -0
- package/workflow_nodes/gong.json +22 -0
- package/workflow_nodes/googleAds.json +116 -0
- package/workflow_nodes/googleAnalytics.json +28 -0
- package/workflow_nodes/googleBigQuery.json +38 -0
- package/workflow_nodes/googleBooks.json +154 -0
- package/workflow_nodes/googleBusinessProfile.json +277 -0
- package/workflow_nodes/googleBusinessProfileTrigger.json +55 -0
- package/workflow_nodes/googleCalendar.json +474 -0
- package/workflow_nodes/googleCalendarTrigger.json +72 -0
- package/workflow_nodes/googleChat.json +187 -0
- package/workflow_nodes/googleCloudNaturalLanguage.json +171 -0
- package/workflow_nodes/googleCloudStorage.json +466 -0
- package/workflow_nodes/googleContacts.json +481 -0
- package/workflow_nodes/googleDocs.json +312 -0
- package/workflow_nodes/googleDrive.json +920 -0
- package/workflow_nodes/googleDriveTrigger.json +181 -0
- package/workflow_nodes/googleFirebaseCloudFirestore.json +156 -0
- package/workflow_nodes/googleFirebaseRealtimeDatabase.json +75 -0
- package/workflow_nodes/googlePerspective.json +94 -0
- package/workflow_nodes/googleSheets.json +98 -0
- package/workflow_nodes/googleSheetsTrigger.json +192 -0
- package/workflow_nodes/googleSlides.json +186 -0
- package/workflow_nodes/googleTasks.json +198 -0
- package/workflow_nodes/googleTranslate.json +80 -0
- package/workflow_nodes/gotify.json +110 -0
- package/workflow_nodes/grafana.json +155 -0
- package/workflow_nodes/graphql.json +165 -0
- package/workflow_nodes/grist.json +13 -0
- package/workflow_nodes/gumroadTrigger.json +8 -0
- package/workflow_nodes/hackerNews.json +100 -0
- package/workflow_nodes/haloPSA.json +286 -0
- package/workflow_nodes/harvest.json +699 -0
- package/workflow_nodes/helpScout.json +629 -0
- package/workflow_nodes/helpScoutTrigger.json +8 -0
- package/workflow_nodes/highLevel.json +8 -0
- package/workflow_nodes/homeAssistant.json +201 -0
- package/workflow_nodes/html.json +118 -0
- package/workflow_nodes/htmlExtract.json +87 -0
- package/workflow_nodes/httpRequest.json +472 -0
- package/workflow_nodes/hubspot.json +62 -0
- package/workflow_nodes/hubspotTrigger.json +138 -0
- package/workflow_nodes/humanticAi.json +82 -0
- package/workflow_nodes/hunter.json +168 -0
- package/workflow_nodes/iCal.json +20 -0
- package/workflow_nodes/if.json +24 -0
- package/workflow_nodes/intercom.json +335 -0
- package/workflow_nodes/interval.json +8 -0
- package/workflow_nodes/invoiceNinja.json +882 -0
- package/workflow_nodes/invoiceNinjaTrigger.json +11 -0
- package/workflow_nodes/itemLists.json +313 -0
- package/workflow_nodes/iterable.json +168 -0
- package/workflow_nodes/jenkins.json +172 -0
- package/workflow_nodes/jira.json +529 -0
- package/workflow_nodes/jiraTrigger.json +308 -0
- package/workflow_nodes/jotFormTrigger.json +44 -0
- package/workflow_nodes/jwt.json +195 -0
- package/workflow_nodes/kafka.json +132 -0
- package/workflow_nodes/kafkaTrigger.json +11 -0
- package/workflow_nodes/keap.json +915 -0
- package/workflow_nodes/keapTrigger.json +37 -0
- package/workflow_nodes/kitemaker.json +153 -0
- package/workflow_nodes/koBoToolbox.json +337 -0
- package/workflow_nodes/koBoToolboxTrigger.json +8 -0
- package/workflow_nodes/langchain_Summarization Chain.json +60 -0
- package/workflow_nodes/langchain_agent.json +145 -0
- package/workflow_nodes/langchain_allowFileUploads.json +180 -0
- package/workflow_nodes/langchain_chainLlm.json +16 -0
- package/workflow_nodes/langchain_chainSummarization.json +119 -0
- package/workflow_nodes/langchain_code.json +62 -0
- package/workflow_nodes/langchain_documentBinaryInputLoader.json +8 -0
- package/workflow_nodes/langchain_documentDefaultDataLoader.json +8 -0
- package/workflow_nodes/langchain_documentGithubLoader.json +8 -0
- package/workflow_nodes/langchain_documentJsonInputLoader.json +8 -0
- package/workflow_nodes/langchain_embeddingDimensions.json +17 -0
- package/workflow_nodes/langchain_embeddingsAwsBedrock.json +8 -0
- package/workflow_nodes/langchain_embeddingsAzureOpenAi.json +151 -0
- package/workflow_nodes/langchain_embeddingsCohere.json +8 -0
- package/workflow_nodes/langchain_embeddingsGoogleGemini.json +8 -0
- package/workflow_nodes/langchain_embeddingsGoogleVertex.json +8 -0
- package/workflow_nodes/langchain_embeddingsHuggingFaceInference.json +8 -0
- package/workflow_nodes/langchain_embeddingsMistralCloud.json +8 -0
- package/workflow_nodes/langchain_embeddingsOllama.json +8 -0
- package/workflow_nodes/langchain_informationExtractor.json +81 -0
- package/workflow_nodes/langchain_lmChatAwsBedrock.json +8 -0
- package/workflow_nodes/langchain_lmChatAzureOpenAi.json +151 -0
- package/workflow_nodes/langchain_lmChatDeepSeek.json +10 -0
- package/workflow_nodes/langchain_lmChatGoogleGemini.json +31 -0
- package/workflow_nodes/langchain_lmChatGoogleVertex.json +32 -0
- package/workflow_nodes/langchain_lmChatGroq.json +8 -0
- package/workflow_nodes/langchain_lmChatMistralCloud.json +8 -0
- package/workflow_nodes/langchain_lmChatOllama.json +8 -0
- package/workflow_nodes/langchain_lmChatOpenAi.json +155 -0
- package/workflow_nodes/langchain_lmChatOpenRouter.json +10 -0
- package/workflow_nodes/langchain_lmChatXAiGrok.json +10 -0
- package/workflow_nodes/langchain_lmCohere.json +8 -0
- package/workflow_nodes/langchain_lmOllama.json +8 -0
- package/workflow_nodes/langchain_lmOpenAi.json +251 -0
- package/workflow_nodes/langchain_lmOpenHuggingFaceInference.json +8 -0
- package/workflow_nodes/langchain_manualChatTrigger.json +11 -0
- package/workflow_nodes/langchain_mcpClientTool.json +86 -0
- package/workflow_nodes/langchain_mcpTrigger.json +8 -0
- package/workflow_nodes/langchain_memoryBufferWindow.json +13 -0
- package/workflow_nodes/langchain_memoryChatRetriever.json +22 -0
- package/workflow_nodes/langchain_memoryManager.json +106 -0
- package/workflow_nodes/langchain_memoryMongoDbChat.json +10 -0
- package/workflow_nodes/langchain_memoryMotorhead.json +13 -0
- package/workflow_nodes/langchain_memoryPostgresChat.json +13 -0
- package/workflow_nodes/langchain_memoryRedisChat.json +15 -0
- package/workflow_nodes/langchain_memoryXata.json +14 -0
- package/workflow_nodes/langchain_memoryZep.json +13 -0
- package/workflow_nodes/langchain_model.json +155 -0
- package/workflow_nodes/langchain_mongoCollection.json +16 -0
- package/workflow_nodes/langchain_notice.json +22 -0
- package/workflow_nodes/langchain_openAiAssistant.json +132 -0
- package/workflow_nodes/langchain_options.json +17 -0
- package/workflow_nodes/langchain_outputParserAutofixing.json +8 -0
- package/workflow_nodes/langchain_outputParserItemList.json +8 -0
- package/workflow_nodes/langchain_outputParserStructured.json +12 -0
- package/workflow_nodes/langchain_pineconeNamespace.json +16 -0
- package/workflow_nodes/langchain_queryName.json +16 -0
- package/workflow_nodes/langchain_retrieverContextualCompression.json +8 -0
- package/workflow_nodes/langchain_retrieverMultiQuery.json +8 -0
- package/workflow_nodes/langchain_retrieverVectorStore.json +8 -0
- package/workflow_nodes/langchain_retrieverWorkflow.json +103 -0
- package/workflow_nodes/langchain_sentimentAnalysis.json +52 -0
- package/workflow_nodes/langchain_systemPromptTemplate.json +47 -0
- package/workflow_nodes/langchain_tableName.json +23 -0
- package/workflow_nodes/langchain_textClassifier.json +66 -0
- package/workflow_nodes/langchain_textSplitterCharacterTextSplitter.json +8 -0
- package/workflow_nodes/langchain_textSplitterRecursiveCharacterTextSplitter.json +8 -0
- package/workflow_nodes/langchain_textSplitterTokenSplitter.json +8 -0
- package/workflow_nodes/langchain_toolCalculator.json +8 -0
- package/workflow_nodes/langchain_toolCode.json +12 -0
- package/workflow_nodes/langchain_toolHttpRequest.json +232 -0
- package/workflow_nodes/langchain_toolSearXng.json +8 -0
- package/workflow_nodes/langchain_toolSerpApi.json +8 -0
- package/workflow_nodes/langchain_toolThink.json +8 -0
- package/workflow_nodes/langchain_toolVectorStore.json +11 -0
- package/workflow_nodes/langchain_toolWikipedia.json +8 -0
- package/workflow_nodes/langchain_toolWolframAlpha.json +8 -0
- package/workflow_nodes/langchain_toolWorkflow.json +8 -0
- package/workflow_nodes/langchain_vectorStoreInMemoryInsert.json +29 -0
- package/workflow_nodes/langchain_vectorStoreInMemoryLoad.json +8 -0
- package/workflow_nodes/langchain_vectorStorePineconeInsert.json +37 -0
- package/workflow_nodes/langchain_vectorStorePineconeLoad.json +8 -0
- package/workflow_nodes/langchain_vectorStoreSupabaseInsert.json +32 -0
- package/workflow_nodes/langchain_vectorStoreSupabaseLoad.json +8 -0
- package/workflow_nodes/langchain_vectorStoreZepInsert.json +46 -0
- package/workflow_nodes/langchain_vectorStoreZepLoad.json +8 -0
- package/workflow_nodes/ldap.json +182 -0
- package/workflow_nodes/lemlist.json +44 -0
- package/workflow_nodes/lemlistTrigger.json +45 -0
- package/workflow_nodes/limit.json +26 -0
- package/workflow_nodes/line.json +95 -0
- package/workflow_nodes/linear.json +151 -0
- package/workflow_nodes/linearTrigger.json +71 -0
- package/workflow_nodes/lingvaNex.json +66 -0
- package/workflow_nodes/linkedIn.json +142 -0
- package/workflow_nodes/localFileTrigger.json +120 -0
- package/workflow_nodes/lonescale.json +171 -0
- package/workflow_nodes/lonescaleTrigger.json +8 -0
- package/workflow_nodes/magento2.json +164 -0
- package/workflow_nodes/mailcheck.json +46 -0
- package/workflow_nodes/mailchimp.json +507 -0
- package/workflow_nodes/mailchimpTrigger.json +100 -0
- package/workflow_nodes/mailerLite.json +24 -0
- package/workflow_nodes/mailerLiteTrigger.json +74 -0
- package/workflow_nodes/mailgun.json +81 -0
- package/workflow_nodes/mailjet.json +201 -0
- package/workflow_nodes/mailjetTrigger.json +8 -0
- package/workflow_nodes/mandrill.json +372 -0
- package/workflow_nodes/manualTrigger.json +8 -0
- package/workflow_nodes/markdown.json +376 -0
- package/workflow_nodes/marketstack.json +126 -0
- package/workflow_nodes/matrix.json +264 -0
- package/workflow_nodes/mattermost.json +8 -0
- package/workflow_nodes/mautic.json +564 -0
- package/workflow_nodes/mauticTrigger.json +54 -0
- package/workflow_nodes/medium.json +209 -0
- package/workflow_nodes/merge.json +125 -0
- package/workflow_nodes/messageBird.json +182 -0
- package/workflow_nodes/metabase.json +175 -0
- package/workflow_nodes/microsoftDynamicsCrm.json +100 -0
- package/workflow_nodes/microsoftEntra.json +51 -0
- package/workflow_nodes/microsoftExcel.json +35 -0
- package/workflow_nodes/microsoftGraphSecurity.json +113 -0
- package/workflow_nodes/microsoftOneDrive.json +232 -0
- package/workflow_nodes/microsoftOneDriveTrigger.json +80 -0
- package/workflow_nodes/microsoftOutlook.json +40 -0
- package/workflow_nodes/microsoftOutlookTrigger.json +24 -0
- package/workflow_nodes/microsoftSql.json +81 -0
- package/workflow_nodes/microsoftTeams.json +36 -0
- package/workflow_nodes/microsoftToDo.json +181 -0
- package/workflow_nodes/mindee.json +86 -0
- package/workflow_nodes/misp.json +399 -0
- package/workflow_nodes/mocean.json +103 -0
- package/workflow_nodes/mondayCom.json +290 -0
- package/workflow_nodes/mongoDb.json +16 -0
- package/workflow_nodes/monicaCrm.json +543 -0
- package/workflow_nodes/moveBinaryData.json +121 -0
- package/workflow_nodes/mqtt.json +67 -0
- package/workflow_nodes/mqttTrigger.json +47 -0
- package/workflow_nodes/msg91.json +65 -0
- package/workflow_nodes/mySql.json +111 -0
- package/workflow_nodes/n8n.json +75 -0
- package/workflow_nodes/n8nTrigger.json +27 -0
- package/workflow_nodes/nasa.json +310 -0
- package/workflow_nodes/netlify.json +87 -0
- package/workflow_nodes/netlifyTrigger.json +68 -0
- package/workflow_nodes/netscalerAdc.json +243 -0
- package/workflow_nodes/nextCloud.json +312 -0
- package/workflow_nodes/noOp.json +8 -0
- package/workflow_nodes/nocoDb.json +276 -0
- package/workflow_nodes/notion.json +8 -0
- package/workflow_nodes/notionTrigger.json +75 -0
- package/workflow_nodes/npm.json +64 -0
- package/workflow_nodes/odoo.json +344 -0
- package/workflow_nodes/okta.json +97 -0
- package/workflow_nodes/oneSimpleApi.json +281 -0
- package/workflow_nodes/onfleet.json +316 -0
- package/workflow_nodes/onfleetTrigger.json +8 -0
- package/workflow_nodes/openAi.json +154 -0
- package/workflow_nodes/openThesaurus.json +81 -0
- package/workflow_nodes/openWeatherMap.json +129 -0
- package/workflow_nodes/orbit.json +375 -0
- package/workflow_nodes/oura.json +74 -0
- package/workflow_nodes/paddle.json +403 -0
- package/workflow_nodes/pagerDuty.json +351 -0
- package/workflow_nodes/payPal.json +196 -0
- package/workflow_nodes/payPalTrigger.json +40 -0
- package/workflow_nodes/peekalink.json +41 -0
- package/workflow_nodes/phantombuster.json +172 -0
- package/workflow_nodes/philipsHue.json +177 -0
- package/workflow_nodes/pipedrive.json +860 -0
- package/workflow_nodes/pipedriveTrigger.json +11 -0
- package/workflow_nodes/plivo.json +91 -0
- package/workflow_nodes/postHog.json +122 -0
- package/workflow_nodes/postbin.json +60 -0
- package/workflow_nodes/postgres.json +109 -0
- package/workflow_nodes/postgresTrigger.json +8 -0
- package/workflow_nodes/postmarkTrigger.json +72 -0
- package/workflow_nodes/profitWell.json +305 -0
- package/workflow_nodes/pushbullet.json +186 -0
- package/workflow_nodes/pushcut.json +75 -0
- package/workflow_nodes/pushcutTrigger.json +8 -0
- package/workflow_nodes/pushover.json +159 -0
- package/workflow_nodes/questDb.json +94 -0
- package/workflow_nodes/quickChart.json +188 -0
- package/workflow_nodes/quickbase.json +205 -0
- package/workflow_nodes/quickbooks.json +550 -0
- package/workflow_nodes/rabbitmq.json +165 -0
- package/workflow_nodes/rabbitmqTrigger.json +8 -0
- package/workflow_nodes/raindrop.json +216 -0
- package/workflow_nodes/readBinaryFile.json +26 -0
- package/workflow_nodes/readBinaryFiles.json +26 -0
- package/workflow_nodes/readPDF.json +31 -0
- package/workflow_nodes/readWriteFile.json +27 -0
- package/workflow_nodes/reddit.json +309 -0
- package/workflow_nodes/redis.json +183 -0
- package/workflow_nodes/redisTrigger.json +8 -0
- package/workflow_nodes/removeDuplicates.json +8 -0
- package/workflow_nodes/renameKeys.json +67 -0
- package/workflow_nodes/respondToWebhook.json +126 -0
- package/workflow_nodes/rocketchat.json +216 -0
- package/workflow_nodes/rssFeedRead.json +28 -0
- package/workflow_nodes/rssFeedReadTrigger.json +17 -0
- package/workflow_nodes/rundeck.json +79 -0
- package/workflow_nodes/s3.json +425 -0
- package/workflow_nodes/salesforce.json +1137 -0
- package/workflow_nodes/salesforceTrigger.json +122 -0
- package/workflow_nodes/salesmate.json +467 -0
- package/workflow_nodes/scheduleTrigger.json +270 -0
- package/workflow_nodes/seaTable.json +8 -0
- package/workflow_nodes/seaTableTrigger.json +87 -0
- package/workflow_nodes/securityScorecard.json +459 -0
- package/workflow_nodes/segment.json +219 -0
- package/workflow_nodes/sendGrid.json +359 -0
- package/workflow_nodes/sendy.json +225 -0
- package/workflow_nodes/sentryIo.json +426 -0
- package/workflow_nodes/serviceNow.json +544 -0
- package/workflow_nodes/set.json +124 -0
- package/workflow_nodes/shopify.json +707 -0
- package/workflow_nodes/shopifyTrigger.json +8 -0
- package/workflow_nodes/signl4.json +133 -0
- package/workflow_nodes/simulate.json +30 -0
- package/workflow_nodes/simulateTrigger.json +8 -0
- package/workflow_nodes/slack.json +62 -0
- package/workflow_nodes/slackTrigger.json +135 -0
- package/workflow_nodes/sms77.json +121 -0
- package/workflow_nodes/snowflake.json +65 -0
- package/workflow_nodes/sort.json +57 -0
- package/workflow_nodes/splitInBatches.json +30 -0
- package/workflow_nodes/splitOut.json +62 -0
- package/workflow_nodes/splunk.json +40 -0
- package/workflow_nodes/spontit.json +123 -0
- package/workflow_nodes/spotify.json +285 -0
- package/workflow_nodes/spreadsheetFile.json +8 -0
- package/workflow_nodes/sseTrigger.json +8 -0
- package/workflow_nodes/ssh.json +105 -0
- package/workflow_nodes/stackby.json +85 -0
- package/workflow_nodes/start.json +15 -0
- package/workflow_nodes/stickyNote.json +36 -0
- package/workflow_nodes/stopAndError.json +8 -0
- package/workflow_nodes/storyblok.json +138 -0
- package/workflow_nodes/strapi.json +138 -0
- package/workflow_nodes/strava.json +427 -0
- package/workflow_nodes/stravaTrigger.json +79 -0
- package/workflow_nodes/stripe.json +357 -0
- package/workflow_nodes/stripeTrigger.json +775 -0
- package/workflow_nodes/summarize.json +124 -0
- package/workflow_nodes/supabase.json +136 -0
- package/workflow_nodes/surveyMonkeyTrigger.json +160 -0
- package/workflow_nodes/switch.json +91 -0
- package/workflow_nodes/syncroMsp.json +8 -0
- package/workflow_nodes/taiga.json +340 -0
- package/workflow_nodes/taigaTrigger.json +81 -0
- package/workflow_nodes/tapfiliate.json +241 -0
- package/workflow_nodes/telegram.json +612 -0
- package/workflow_nodes/telegramTrigger.json +142 -0
- package/workflow_nodes/theHive.json +497 -0
- package/workflow_nodes/theHiveProject.json +8 -0
- package/workflow_nodes/theHiveProjectTrigger.json +162 -0
- package/workflow_nodes/theHiveTrigger.json +101 -0
- package/workflow_nodes/timescaleDb.json +95 -0
- package/workflow_nodes/todoist.json +285 -0
- package/workflow_nodes/togglTrigger.json +24 -0
- package/workflow_nodes/totp.json +86 -0
- package/workflow_nodes/travisCi.json +142 -0
- package/workflow_nodes/trello.json +609 -0
- package/workflow_nodes/trelloTrigger.json +8 -0
- package/workflow_nodes/twake.json +76 -0
- package/workflow_nodes/twilio.json +95 -0
- package/workflow_nodes/twilioTrigger.json +46 -0
- package/workflow_nodes/twist.json +376 -0
- package/workflow_nodes/twitter.json +40 -0
- package/workflow_nodes/typeformTrigger.json +62 -0
- package/workflow_nodes/unleashedSoftware.json +154 -0
- package/workflow_nodes/uplead.json +72 -0
- package/workflow_nodes/uproc.json +26 -0
- package/workflow_nodes/uptimeRobot.json +453 -0
- package/workflow_nodes/urlScanIo.json +113 -0
- package/workflow_nodes/venafiTlsProtectCloud.json +310 -0
- package/workflow_nodes/venafiTlsProtectCloudTrigger.json +38 -0
- package/workflow_nodes/venafiTlsProtectDatacenter.json +491 -0
- package/workflow_nodes/vero.json +158 -0
- package/workflow_nodes/vonage.json +125 -0
- package/workflow_nodes/wait.json +71 -0
- package/workflow_nodes/webflow.json +38 -0
- package/workflow_nodes/webflowTrigger.json +8 -0
- package/workflow_nodes/webhook.json +55 -0
- package/workflow_nodes/wekan.json +460 -0
- package/workflow_nodes/whatsApp.json +476 -0
- package/workflow_nodes/whatsAppTrigger.json +103 -0
- package/workflow_nodes/wise.json +330 -0
- package/workflow_nodes/wiseTrigger.json +8 -0
- package/workflow_nodes/wooCommerce.json +812 -0
- package/workflow_nodes/wooCommerceTrigger.json +8 -0
- package/workflow_nodes/wordpress.json +500 -0
- package/workflow_nodes/workableTrigger.json +51 -0
- package/workflow_nodes/writeBinaryFile.json +34 -0
- package/workflow_nodes/wufooTrigger.json +37 -0
- package/workflow_nodes/xero.json +530 -0
- package/workflow_nodes/xml.json +129 -0
- package/workflow_nodes/youTube.json +578 -0
- package/workflow_nodes/yourls.json +71 -0
- package/workflow_nodes/zammad.json +406 -0
- package/workflow_nodes/zendesk.json +526 -0
- package/workflow_nodes/zendeskTrigger.json +187 -0
- package/workflow_nodes/zohoCrm.json +721 -0
- package/workflow_nodes/zoom.json +507 -0
- package/workflow_nodes/zulip.json +371 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node Management Tools
|
|
3
|
+
*
|
|
4
|
+
* Implements tools for managing nodes in n8n workflows, including:
|
|
5
|
+
* - Adding new nodes to existing workflows
|
|
6
|
+
* - Validating node parameters against node type definitions
|
|
7
|
+
* - Generating unique node IDs
|
|
8
|
+
* - Managing node positioning in workflow canvas
|
|
9
|
+
* - Replacing existing nodes while maintaining connections
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { createTool } = require('../models/tool');
|
|
13
|
+
const { workflowStorage } = require('../models/storage');
|
|
14
|
+
const { getNodesFromSource } = require('./nodeDiscovery');
|
|
15
|
+
const { logger } = require('../utils/logger');
|
|
16
|
+
const { v4: uuidv4 } = require('uuid');
|
|
17
|
+
|
|
18
|
+
// In-memory cache for node type case mapping
|
|
19
|
+
let NODE_TYPE_CASE_MAP = {};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build the node type case mapping from node definitions
|
|
23
|
+
*
|
|
24
|
+
* Dynamically creates a mapping of lowercase node types to their properly cased versions
|
|
25
|
+
* based on the node definitions in the workflow_nodes directory.
|
|
26
|
+
*
|
|
27
|
+
* @returns {Promise<Object>} Mapping of lowercase node types to properly cased versions
|
|
28
|
+
*/
|
|
29
|
+
const buildNodeTypeCaseMap = async () => {
|
|
30
|
+
try {
|
|
31
|
+
// Get all nodes from the node discovery service
|
|
32
|
+
const nodes = await getNodesFromSource();
|
|
33
|
+
if (!Array.isArray(nodes) || nodes.length === 0) {
|
|
34
|
+
logger.warn('No nodes found for building case map');
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const caseMap = {};
|
|
39
|
+
|
|
40
|
+
// Process each node to build the case map
|
|
41
|
+
for (const node of nodes) {
|
|
42
|
+
if (node.originalNodeType) {
|
|
43
|
+
// Handle full node type (with prefix)
|
|
44
|
+
const lowerCaseType = node.originalNodeType.toLowerCase();
|
|
45
|
+
caseMap[lowerCaseType] = node.originalNodeType;
|
|
46
|
+
|
|
47
|
+
// Also add entry for the node name without prefix
|
|
48
|
+
if (lowerCaseType.startsWith('n8n-nodes-base.')) {
|
|
49
|
+
const shortName = lowerCaseType.split('n8n-nodes-base.')[1];
|
|
50
|
+
caseMap[shortName] = node.originalNodeType;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add entry for node ID if it exists
|
|
55
|
+
if (node.id) {
|
|
56
|
+
const nodeId = node.id.toLowerCase();
|
|
57
|
+
const nodeTypeWithPrefix = `n8n-nodes-base.${node.id}`;
|
|
58
|
+
|
|
59
|
+
// If we don't already have this mapping (original node type has precedence)
|
|
60
|
+
if (!caseMap[nodeId]) {
|
|
61
|
+
caseMap[nodeId] = nodeTypeWithPrefix;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Also map the full prefixed version
|
|
65
|
+
const prefixedLowerCase = `n8n-nodes-base.${nodeId}`;
|
|
66
|
+
if (!caseMap[prefixedLowerCase]) {
|
|
67
|
+
caseMap[prefixedLowerCase] = nodeTypeWithPrefix;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
logger.info(`Built node type case map with ${Object.keys(caseMap).length} entries`);
|
|
73
|
+
return caseMap;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
logger.error('Error building node type case map:', error);
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the properly cased node type for special cases
|
|
82
|
+
*
|
|
83
|
+
* This function handles both simple node type names (like "openai")
|
|
84
|
+
* and full node types (like "n8n-nodes-base.openai"), ensuring
|
|
85
|
+
* the proper casing is used in the workflow.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} nodeType - Node type to check (with or without prefix)
|
|
88
|
+
* @returns {string|null} - Properly cased node type or null if no special case
|
|
89
|
+
*/
|
|
90
|
+
const getSpecialCaseNodeType = async (nodeType) => {
|
|
91
|
+
if (!nodeType) return null;
|
|
92
|
+
|
|
93
|
+
// Ensure the case map is populated
|
|
94
|
+
if (Object.keys(NODE_TYPE_CASE_MAP).length === 0) {
|
|
95
|
+
NODE_TYPE_CASE_MAP = await buildNodeTypeCaseMap();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Normalize to lowercase for lookup
|
|
99
|
+
const normalizedType = nodeType.toLowerCase();
|
|
100
|
+
|
|
101
|
+
// Check direct mapping first
|
|
102
|
+
if (NODE_TYPE_CASE_MAP[normalizedType]) {
|
|
103
|
+
return NODE_TYPE_CASE_MAP[normalizedType];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// If no direct match and no prefix, try adding the prefix
|
|
107
|
+
const prefix = 'n8n-nodes-base.';
|
|
108
|
+
if (!normalizedType.includes(prefix)) {
|
|
109
|
+
const withPrefix = prefix + normalizedType;
|
|
110
|
+
if (NODE_TYPE_CASE_MAP[withPrefix]) {
|
|
111
|
+
return NODE_TYPE_CASE_MAP[withPrefix];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// For backwards compatibility: try to extract node name and apply casing
|
|
116
|
+
if (normalizedType.startsWith(prefix)) {
|
|
117
|
+
const nodeName = normalizedType.replace(prefix, '');
|
|
118
|
+
if (NODE_TYPE_CASE_MAP[nodeName]) {
|
|
119
|
+
return NODE_TYPE_CASE_MAP[nodeName];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// If we don't have a specific mapping, check if we need to add the prefix
|
|
124
|
+
if (!normalizedType.startsWith(prefix)) {
|
|
125
|
+
return prefix + normalizedType;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Return the original if all else fails
|
|
129
|
+
return nodeType;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Load the node type case map on module initialization
|
|
133
|
+
buildNodeTypeCaseMap().then(caseMap => {
|
|
134
|
+
NODE_TYPE_CASE_MAP = caseMap;
|
|
135
|
+
}).catch(error => {
|
|
136
|
+
logger.error('Failed to initialize node type case map:', error);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate a unique ID for a node within a workflow
|
|
141
|
+
*
|
|
142
|
+
* @param {Object} workflow - The workflow object
|
|
143
|
+
* @returns {string} Unique node ID
|
|
144
|
+
*/
|
|
145
|
+
const generateUniqueNodeId = (workflow) => {
|
|
146
|
+
// Base ID on node type or use a random string
|
|
147
|
+
const baseId = 'node';
|
|
148
|
+
|
|
149
|
+
// Find the highest numbered ID with this base
|
|
150
|
+
let maxNumber = 0;
|
|
151
|
+
if (workflow.nodes && Array.isArray(workflow.nodes)) {
|
|
152
|
+
workflow.nodes.forEach(node => {
|
|
153
|
+
if (node.id && node.id.startsWith(baseId)) {
|
|
154
|
+
const numStr = node.id.replace(baseId, '');
|
|
155
|
+
const num = parseInt(numStr, 10);
|
|
156
|
+
if (!isNaN(num) && num > maxNumber) {
|
|
157
|
+
maxNumber = num;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Generate the next available ID
|
|
164
|
+
return `${baseId}${maxNumber + 1}`;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Find node type definition from available nodes
|
|
169
|
+
*
|
|
170
|
+
* @param {string} nodeType - Type of node to find
|
|
171
|
+
* @returns {Promise<Object|null>} Node type definition or null if not found
|
|
172
|
+
*/
|
|
173
|
+
const getNodeTypeDefinition = async (nodeType) => {
|
|
174
|
+
const nodesData = await getNodesFromSource();
|
|
175
|
+
const nodes = Array.isArray(nodesData) ? nodesData : []; // Ensure we have an array
|
|
176
|
+
const filenameMap = nodesData.filenameMap || {}; // Get the filename map if available
|
|
177
|
+
|
|
178
|
+
// Handle case insensitive matching and potential camelCase vs lowercase inconsistencies
|
|
179
|
+
// First convert to lowercase for node type comparison
|
|
180
|
+
const normalizedNodeType = nodeType.toLowerCase();
|
|
181
|
+
|
|
182
|
+
// Check if we're looking for a node with just the node name or the full n8n-nodes-base prefix
|
|
183
|
+
const hasPrefix = normalizedNodeType.startsWith('n8n-nodes-base.');
|
|
184
|
+
let nodeNameOnly = normalizedNodeType;
|
|
185
|
+
|
|
186
|
+
if (hasPrefix) {
|
|
187
|
+
// If it has the prefix, extract just the node name for lookups
|
|
188
|
+
nodeNameOnly = normalizedNodeType.split('n8n-nodes-base.')[1];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check if we have this node name in our filename map, which preserves original casing
|
|
192
|
+
const originalCaseNodeName = filenameMap[nodeNameOnly];
|
|
193
|
+
|
|
194
|
+
// First try to find by exact ID match (the filename minus extension)
|
|
195
|
+
let matchedNode = nodes.find(node => node.id.toLowerCase() === nodeNameOnly);
|
|
196
|
+
|
|
197
|
+
// Next, if we have a prefix, try to match by the originalNodeType which has exact casing
|
|
198
|
+
if (!matchedNode && hasPrefix) {
|
|
199
|
+
matchedNode = nodes.find(node =>
|
|
200
|
+
node.originalNodeType && node.originalNodeType.toLowerCase() === normalizedNodeType);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// If still no match, try nodeType matching with originals
|
|
204
|
+
if (!matchedNode) {
|
|
205
|
+
matchedNode = nodes.find(node => {
|
|
206
|
+
// Check both the node ID and the file name for matches
|
|
207
|
+
const nodeIdMatch = node.id.toLowerCase() === nodeNameOnly;
|
|
208
|
+
|
|
209
|
+
// Check if the normalized version of the node type matches
|
|
210
|
+
const normalizedTypeMatch = node.normalizedType &&
|
|
211
|
+
nodeNameOnly === node.normalizedType;
|
|
212
|
+
|
|
213
|
+
// Check if the type fields match (case insensitive)
|
|
214
|
+
const nodeTypeMatch = (
|
|
215
|
+
(node.type && node.type.toLowerCase() === normalizedNodeType) ||
|
|
216
|
+
(node.originalNodeType && node.originalNodeType.toLowerCase() === normalizedNodeType) ||
|
|
217
|
+
(hasPrefix && node.id.toLowerCase() === nodeNameOnly)
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return nodeIdMatch || normalizedTypeMatch || nodeTypeMatch;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// If we found a match at this point, return it
|
|
225
|
+
if (matchedNode) {
|
|
226
|
+
// Log for debugging
|
|
227
|
+
logger.debug(`Found node match for ${nodeType}:`, {
|
|
228
|
+
id: matchedNode.id,
|
|
229
|
+
type: matchedNode.type,
|
|
230
|
+
originalNodeType: matchedNode.originalNodeType
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return matchedNode;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// If all else fails, do a broader search that's less precise
|
|
237
|
+
matchedNode = nodes.find(node => {
|
|
238
|
+
return node.id.toLowerCase().includes(nodeNameOnly) ||
|
|
239
|
+
(node.type && node.type.toLowerCase().includes(nodeNameOnly)) ||
|
|
240
|
+
(node.originalNodeType && node.originalNodeType.toLowerCase().includes(nodeNameOnly));
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return matchedNode || null;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Validate node parameters against the node type definition
|
|
248
|
+
*
|
|
249
|
+
* @param {Object} nodeTypeDef - Node type definition
|
|
250
|
+
* @param {Object} parameters - Node parameters to validate
|
|
251
|
+
* @returns {boolean} True if valid, throws error if invalid
|
|
252
|
+
*/
|
|
253
|
+
const validateNodeParameters = (nodeTypeDef, parameters) => {
|
|
254
|
+
if (!parameters) return true;
|
|
255
|
+
|
|
256
|
+
// For now, perform basic validation
|
|
257
|
+
// A more comprehensive validation would check against parameter schema
|
|
258
|
+
|
|
259
|
+
// Check that all required parameters are present
|
|
260
|
+
const requiredParams = nodeTypeDef.parameters
|
|
261
|
+
.filter(param => param.required)
|
|
262
|
+
.map(param => param.name);
|
|
263
|
+
|
|
264
|
+
const missingParams = requiredParams.filter(name =>
|
|
265
|
+
!parameters.hasOwnProperty(name)
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (missingParams.length > 0) {
|
|
269
|
+
throw new Error(`Missing required parameters: ${missingParams.join(', ')}`);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return true;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Check if connections between two node types are compatible
|
|
277
|
+
*
|
|
278
|
+
* @param {Object} originalNodeDef - Original node definition
|
|
279
|
+
* @param {Object} newNodeDef - New node definition
|
|
280
|
+
* @returns {Object} Compatibility assessment with input and output compatibility
|
|
281
|
+
*/
|
|
282
|
+
const checkConnectionCompatibility = (originalNodeDef, newNodeDef) => {
|
|
283
|
+
// Default to uncertain compatibility if we can't determine
|
|
284
|
+
const defaultCompatibility = {
|
|
285
|
+
inputCompatible: true,
|
|
286
|
+
outputCompatible: true,
|
|
287
|
+
warnings: []
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// If either definition is missing, we can't make a determination
|
|
291
|
+
if (!originalNodeDef || !newNodeDef) {
|
|
292
|
+
return {
|
|
293
|
+
inputCompatible: true, // Assume compatible for safety
|
|
294
|
+
outputCompatible: true, // Assume compatible for safety
|
|
295
|
+
warnings: ['Could not determine connection compatibility due to missing node definitions']
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Start with simple category compatibility checks
|
|
300
|
+
const originalCategories = new Set(originalNodeDef.categories || []);
|
|
301
|
+
const newCategories = new Set(newNodeDef.categories || []);
|
|
302
|
+
|
|
303
|
+
const compatibility = {
|
|
304
|
+
inputCompatible: true,
|
|
305
|
+
outputCompatible: true,
|
|
306
|
+
warnings: []
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// If original was a trigger and new is not, that's an incompatibility
|
|
310
|
+
if (originalCategories.has('Trigger') && !newCategories.has('Trigger')) {
|
|
311
|
+
compatibility.inputCompatible = false;
|
|
312
|
+
compatibility.warnings.push('Original node was a Trigger but new node is not');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Parameter structure incompatibility check (basic)
|
|
316
|
+
const originalParamCount = originalNodeDef.parameters?.length || 0;
|
|
317
|
+
const newParamCount = newNodeDef.parameters?.length || 0;
|
|
318
|
+
|
|
319
|
+
if (originalParamCount > newParamCount + 5) {
|
|
320
|
+
compatibility.warnings.push('New node has significantly fewer parameters than original node');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return compatibility;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Update connections for replaced node based on compatibility
|
|
328
|
+
*
|
|
329
|
+
* @param {Object} workflow - The workflow object
|
|
330
|
+
* @param {string} nodeId - ID of the replaced node
|
|
331
|
+
* @param {string} originalNodeType - Original node type
|
|
332
|
+
* @param {string} newNodeType - New node type
|
|
333
|
+
* @returns {Object} Updated workflow with adjusted connections
|
|
334
|
+
*/
|
|
335
|
+
const updateConnectionsForReplacedNode = async (workflow, nodeId, originalNodeType, newNodeType) => {
|
|
336
|
+
// Get node definitions for checking compatibility using the updated case-insensitive matching
|
|
337
|
+
// The original node type might have camelCase that needs to be handled
|
|
338
|
+
const originalNodeDef = await getNodeTypeDefinition(originalNodeType);
|
|
339
|
+
const newNodeDef = await getNodeTypeDefinition(newNodeType);
|
|
340
|
+
|
|
341
|
+
// Check connection compatibility
|
|
342
|
+
const compatibility = checkConnectionCompatibility(originalNodeDef, newNodeDef);
|
|
343
|
+
|
|
344
|
+
// If no connections to modify or we're keeping all, return as is
|
|
345
|
+
if (!workflow.connections || !Array.isArray(workflow.connections)) {
|
|
346
|
+
return {
|
|
347
|
+
workflow,
|
|
348
|
+
compatibility
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Check each connection
|
|
353
|
+
if (!compatibility.inputCompatible || !compatibility.outputCompatible) {
|
|
354
|
+
// Filter connections that need adjustment
|
|
355
|
+
workflow.connections = workflow.connections.filter(connection => {
|
|
356
|
+
// If this node is the source and output compatibility is false, remove
|
|
357
|
+
if (connection.source.node === nodeId && !compatibility.outputCompatible) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// If this node is the target and input compatibility is false, remove
|
|
362
|
+
if (connection.target.node === nodeId && !compatibility.inputCompatible) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return true;
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
workflow,
|
|
372
|
+
compatibility
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Add a node to an existing workflow
|
|
378
|
+
*
|
|
379
|
+
* @param {Object} params - The parameters for the operation
|
|
380
|
+
* @param {string} params.workflowId - ID or path of the workflow to modify
|
|
381
|
+
* @param {string} params.nodeType - Type of node to add
|
|
382
|
+
* @param {Object} params.position - Position on the canvas {x, y}
|
|
383
|
+
* @param {Object} params.parameters - Node parameters
|
|
384
|
+
* @returns {Promise<Object>} Operation result with updated workflow
|
|
385
|
+
*/
|
|
386
|
+
const addNode = async (params) => {
|
|
387
|
+
try {
|
|
388
|
+
const { workflowId, nodeType, position, parameters, nodeName } = params;
|
|
389
|
+
logger.info('Adding node to workflow', { workflowId, nodeType });
|
|
390
|
+
|
|
391
|
+
// Load workflow
|
|
392
|
+
const workflow = await workflowStorage.loadWorkflow(workflowId);
|
|
393
|
+
if (!workflow) {
|
|
394
|
+
throw new Error(`Workflow with ID ${workflowId} not found`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Initialize nodes array if it doesn't exist
|
|
398
|
+
if (!workflow.nodes) {
|
|
399
|
+
workflow.nodes = [];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Get node type definition with case-insensitive matching
|
|
403
|
+
const nodeTypeDef = await getNodeTypeDefinition(nodeType);
|
|
404
|
+
if (!nodeTypeDef) {
|
|
405
|
+
throw new Error(`Node type ${nodeType} not found`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Log for debugging
|
|
409
|
+
logger.info('Node definition found:', {
|
|
410
|
+
id: nodeTypeDef.id,
|
|
411
|
+
type: nodeTypeDef.type,
|
|
412
|
+
originalNodeType: nodeTypeDef.originalNodeType,
|
|
413
|
+
normalizedType: nodeTypeDef.normalizedType,
|
|
414
|
+
fileName: nodeTypeDef.fileName
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Validate parameters against node type definition
|
|
418
|
+
validateNodeParameters(nodeTypeDef, parameters);
|
|
419
|
+
|
|
420
|
+
// Generate unique node ID
|
|
421
|
+
const nodeId = generateUniqueNodeId(workflow);
|
|
422
|
+
|
|
423
|
+
// Use the original node type format from the definition when available
|
|
424
|
+
// This preserves the exact casing as in the node definition file
|
|
425
|
+
// which is important for proper node type resolution in n8n
|
|
426
|
+
let actualNodeType;
|
|
427
|
+
|
|
428
|
+
// First check for special case node types that need specific casing
|
|
429
|
+
const specialCaseNodeType = await getSpecialCaseNodeType(nodeType);
|
|
430
|
+
if (specialCaseNodeType) {
|
|
431
|
+
actualNodeType = specialCaseNodeType;
|
|
432
|
+
logger.info(`Special case handling for ${nodeType}, using: ${actualNodeType}`);
|
|
433
|
+
}
|
|
434
|
+
// Check if the nodeType is directly found in the node definition
|
|
435
|
+
else if (nodeTypeDef.originalNodeType) {
|
|
436
|
+
actualNodeType = nodeTypeDef.originalNodeType; // Use exact casing from JSON file
|
|
437
|
+
logger.info(`Using originalNodeType: ${actualNodeType}`);
|
|
438
|
+
} else if (nodeTypeDef.type) {
|
|
439
|
+
actualNodeType = nodeTypeDef.type;
|
|
440
|
+
logger.info(`Using type from node definition: ${actualNodeType}`);
|
|
441
|
+
} else if (nodeTypeDef.id) {
|
|
442
|
+
// If we have node ID but no type, reconstruct it with prefix
|
|
443
|
+
actualNodeType = `n8n-nodes-base.${nodeTypeDef.id}`;
|
|
444
|
+
logger.info(`Reconstructed from ID: ${actualNodeType}`);
|
|
445
|
+
} else {
|
|
446
|
+
// Fallback to user provided type
|
|
447
|
+
actualNodeType = nodeType;
|
|
448
|
+
logger.info(`Using original nodeType: ${actualNodeType}`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Create node object
|
|
452
|
+
const newNode = {
|
|
453
|
+
id: nodeId,
|
|
454
|
+
name: nodeName || nodeTypeDef.name || nodeType,
|
|
455
|
+
type: actualNodeType,
|
|
456
|
+
position: position || { x: 100, y: 100 },
|
|
457
|
+
parameters: parameters || {},
|
|
458
|
+
typeVersion: nodeTypeDef.typeVersion || 1
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
// Add node to workflow
|
|
462
|
+
workflow.nodes.push(newNode);
|
|
463
|
+
|
|
464
|
+
// Update workflow timestamp
|
|
465
|
+
workflow.updatedAt = new Date().toISOString();
|
|
466
|
+
|
|
467
|
+
// Save updated workflow to the same file
|
|
468
|
+
// The filePath should be extracted from the workflowId if it's already a path
|
|
469
|
+
const filePath = workflowId.includes('/') || workflowId.includes('\\') ?
|
|
470
|
+
workflowId : `${workflowId}.json`;
|
|
471
|
+
|
|
472
|
+
await workflowStorage.saveWorkflow(workflow.id || 'workflow', workflow, filePath);
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
success: true,
|
|
476
|
+
nodeId,
|
|
477
|
+
workflow,
|
|
478
|
+
message: `Node ${nodeId} added successfully to workflow`
|
|
479
|
+
};
|
|
480
|
+
} catch (error) {
|
|
481
|
+
logger.error('Error adding node to workflow', { error: error.message });
|
|
482
|
+
throw new Error(`Failed to add node: ${error.message}`);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Replace a node in an existing workflow
|
|
488
|
+
*
|
|
489
|
+
* @param {Object} params - The parameters for the operation
|
|
490
|
+
* @param {string} params.workflowId - ID or path of the workflow to modify
|
|
491
|
+
* @param {string} params.targetNodeId - ID of the node to replace
|
|
492
|
+
* @param {string} params.newNodeType - Type of the new node
|
|
493
|
+
* @param {Object} params.parameters - Parameters for the new node
|
|
494
|
+
* @param {string} [params.nodeName] - Optional custom name for the node
|
|
495
|
+
* @returns {Promise<Object>} Operation result with updated workflow
|
|
496
|
+
*/
|
|
497
|
+
const replaceNode = async (params) => {
|
|
498
|
+
try {
|
|
499
|
+
const { workflowId, targetNodeId, newNodeType, parameters, nodeName } = params;
|
|
500
|
+
logger.info('Replacing node in workflow', { workflowId, targetNodeId, newNodeType });
|
|
501
|
+
|
|
502
|
+
// Load workflow
|
|
503
|
+
const workflow = await workflowStorage.loadWorkflow(workflowId);
|
|
504
|
+
if (!workflow) {
|
|
505
|
+
throw new Error(`Workflow with ID ${workflowId} not found`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Find target node
|
|
509
|
+
const targetNodeIndex = workflow.nodes.findIndex(node => node.id === targetNodeId);
|
|
510
|
+
if (targetNodeIndex === -1) {
|
|
511
|
+
throw new Error(`Node with ID ${targetNodeId} not found in workflow`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Get node type definition with case-insensitive matching
|
|
515
|
+
const nodeTypeDef = await getNodeTypeDefinition(newNodeType);
|
|
516
|
+
if (!nodeTypeDef) {
|
|
517
|
+
throw new Error(`Node type ${newNodeType} not found`);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Store original node for compatibility checking
|
|
521
|
+
const originalNode = workflow.nodes[targetNodeIndex];
|
|
522
|
+
const originalNodeType = originalNode.type;
|
|
523
|
+
|
|
524
|
+
// Validate parameters against node type definition
|
|
525
|
+
validateNodeParameters(nodeTypeDef, parameters);
|
|
526
|
+
|
|
527
|
+
// Use the original node type format from the definition when available
|
|
528
|
+
// This preserves the exact casing as in the node definition file
|
|
529
|
+
let actualNodeType;
|
|
530
|
+
|
|
531
|
+
// First check for special case node types that need specific casing
|
|
532
|
+
const specialCaseNodeType = await getSpecialCaseNodeType(newNodeType);
|
|
533
|
+
if (specialCaseNodeType) {
|
|
534
|
+
actualNodeType = specialCaseNodeType;
|
|
535
|
+
logger.info(`Special case handling for ${newNodeType}, using: ${actualNodeType}`);
|
|
536
|
+
}
|
|
537
|
+
// Check if the nodeType is directly found in the node definition
|
|
538
|
+
else if (nodeTypeDef.originalNodeType) {
|
|
539
|
+
actualNodeType = nodeTypeDef.originalNodeType; // Use exact casing from JSON file
|
|
540
|
+
logger.info(`Using originalNodeType: ${actualNodeType}`);
|
|
541
|
+
} else if (nodeTypeDef.type) {
|
|
542
|
+
actualNodeType = nodeTypeDef.type;
|
|
543
|
+
logger.info(`Using type from node definition: ${actualNodeType}`);
|
|
544
|
+
} else if (nodeTypeDef.id) {
|
|
545
|
+
// If we have node ID but no type, reconstruct it with prefix
|
|
546
|
+
actualNodeType = `n8n-nodes-base.${nodeTypeDef.id}`;
|
|
547
|
+
logger.info(`Reconstructed from ID: ${actualNodeType}`);
|
|
548
|
+
} else {
|
|
549
|
+
// Fallback to user provided type
|
|
550
|
+
actualNodeType = newNodeType;
|
|
551
|
+
logger.info(`Using original nodeType: ${actualNodeType}`);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Create new node object, maintaining position and ID
|
|
555
|
+
const newNode = {
|
|
556
|
+
id: targetNodeId, // Keep same ID to maintain connections
|
|
557
|
+
name: nodeName || nodeTypeDef.name || newNodeType,
|
|
558
|
+
type: actualNodeType,
|
|
559
|
+
position: originalNode.position,
|
|
560
|
+
parameters: parameters || {},
|
|
561
|
+
typeVersion: nodeTypeDef.typeVersion || 1
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// Replace node in workflow
|
|
565
|
+
workflow.nodes[targetNodeIndex] = newNode;
|
|
566
|
+
|
|
567
|
+
// Update workflow timestamp
|
|
568
|
+
workflow.updatedAt = new Date().toISOString();
|
|
569
|
+
|
|
570
|
+
// Check connection compatibility and adjust connections if needed
|
|
571
|
+
const { workflow: updatedWorkflow, compatibility } =
|
|
572
|
+
await updateConnectionsForReplacedNode(workflow, targetNodeId, originalNodeType, newNodeType);
|
|
573
|
+
|
|
574
|
+
// Save updated workflow
|
|
575
|
+
const filePath = workflowId.includes('/') || workflowId.includes('\\') ?
|
|
576
|
+
workflowId : `${workflowId}.json`;
|
|
577
|
+
|
|
578
|
+
await workflowStorage.saveWorkflow(updatedWorkflow.id || 'workflow', updatedWorkflow, filePath);
|
|
579
|
+
|
|
580
|
+
return {
|
|
581
|
+
success: true,
|
|
582
|
+
nodeId: targetNodeId,
|
|
583
|
+
workflow: updatedWorkflow,
|
|
584
|
+
compatibility: compatibility.warnings, // Include compatibility warnings in response
|
|
585
|
+
message: `Node ${targetNodeId} replaced successfully with ${newNodeType}`
|
|
586
|
+
};
|
|
587
|
+
} catch (error) {
|
|
588
|
+
logger.error('Error replacing node in workflow', { error: error.message });
|
|
589
|
+
throw new Error(`Failed to replace node: ${error.message}`);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Tool definition for the add_node MCP tool
|
|
595
|
+
*/
|
|
596
|
+
const addNodeTool = createTool(
|
|
597
|
+
'Add a node to an existing workflow',
|
|
598
|
+
{
|
|
599
|
+
workflowId: {
|
|
600
|
+
type: 'string',
|
|
601
|
+
description: 'ID or path of the workflow to add the node to'
|
|
602
|
+
},
|
|
603
|
+
nodeType: {
|
|
604
|
+
type: 'string',
|
|
605
|
+
description: 'Type of node to add (e.g., "gmail", "slack", "openai"). Use the list_available_nodes tool to see all available nodes with their correct casing. The system will automatically handle proper casing and prefixing for you.'
|
|
606
|
+
},
|
|
607
|
+
nodeName: {
|
|
608
|
+
type: 'string',
|
|
609
|
+
description: 'Custom display name for the node in the workflow',
|
|
610
|
+
optional: true
|
|
611
|
+
},
|
|
612
|
+
position: {
|
|
613
|
+
type: 'object',
|
|
614
|
+
description: 'Position of the node in the workflow canvas',
|
|
615
|
+
properties: {
|
|
616
|
+
x: { type: 'number' },
|
|
617
|
+
y: { type: 'number' }
|
|
618
|
+
},
|
|
619
|
+
optional: true
|
|
620
|
+
},
|
|
621
|
+
parameters: {
|
|
622
|
+
type: 'object',
|
|
623
|
+
description: 'Parameters for the node',
|
|
624
|
+
optional: true
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
addNode
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Tool definition for the replace_node MCP tool
|
|
632
|
+
*/
|
|
633
|
+
const replaceNodeTool = createTool(
|
|
634
|
+
'Replace an existing node in a workflow with a new node type while maintaining connections where possible',
|
|
635
|
+
{
|
|
636
|
+
workflowId: {
|
|
637
|
+
type: 'string',
|
|
638
|
+
description: 'ID or path of the workflow containing the node'
|
|
639
|
+
},
|
|
640
|
+
targetNodeId: {
|
|
641
|
+
type: 'string',
|
|
642
|
+
description: 'ID of the node to replace'
|
|
643
|
+
},
|
|
644
|
+
newNodeType: {
|
|
645
|
+
type: 'string',
|
|
646
|
+
description: 'Type of node to replace with (e.g., "gmail", "slack", "openai"). Use the list_available_nodes tool to see all available nodes with their correct casing. The system will automatically handle proper casing and prefixing for you.'
|
|
647
|
+
},
|
|
648
|
+
nodeName: {
|
|
649
|
+
type: 'string',
|
|
650
|
+
description: 'Custom display name for the new node in the workflow',
|
|
651
|
+
optional: true
|
|
652
|
+
},
|
|
653
|
+
parameters: {
|
|
654
|
+
type: 'object',
|
|
655
|
+
description: 'Parameters for the new node',
|
|
656
|
+
optional: true
|
|
657
|
+
}
|
|
658
|
+
},
|
|
659
|
+
replaceNode
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
module.exports = {
|
|
663
|
+
addNodeTool,
|
|
664
|
+
replaceNodeTool,
|
|
665
|
+
addNode,
|
|
666
|
+
replaceNode,
|
|
667
|
+
generateUniqueNodeId,
|
|
668
|
+
getNodeTypeDefinition,
|
|
669
|
+
validateNodeParameters,
|
|
670
|
+
checkConnectionCompatibility,
|
|
671
|
+
updateConnectionsForReplacedNode,
|
|
672
|
+
getSpecialCaseNodeType,
|
|
673
|
+
buildNodeTypeCaseMap
|
|
674
|
+
};
|