mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-19 11:01:15 +00:00
feat(MCP Server Trigger Node): Add MCP Server Trigger node to expose tools to MCP clients (#14403)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
@@ -954,6 +954,8 @@ export interface RootState {
|
||||
endpointForm: string;
|
||||
endpointFormTest: string;
|
||||
endpointFormWaiting: string;
|
||||
endpointMcp: string;
|
||||
endpointMcpTest: string;
|
||||
endpointWebhook: string;
|
||||
endpointWebhookTest: string;
|
||||
endpointWebhookWaiting: string;
|
||||
|
||||
@@ -15,6 +15,8 @@ export const defaultSettings: FrontendSettings = {
|
||||
endpointForm: '',
|
||||
endpointFormTest: '',
|
||||
endpointFormWaiting: '',
|
||||
endpointMcp: '',
|
||||
endpointMcpTest: '',
|
||||
endpointWebhook: '',
|
||||
endpointWebhookTest: '',
|
||||
endpointWebhookWaiting: '',
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useToast } from '@/composables/useToast';
|
||||
import {
|
||||
CHAT_TRIGGER_NODE_TYPE,
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
MCP_TRIGGER_NODE_TYPE,
|
||||
OPEN_URL_PANEL_TRIGGER_NODE_TYPES,
|
||||
PRODUCTION_ONLY_TRIGGER_NODE_TYPES,
|
||||
} from '@/constants';
|
||||
@@ -31,7 +32,7 @@ const isMinimized = ref(
|
||||
props.nodeTypeDescription &&
|
||||
!OPEN_URL_PANEL_TRIGGER_NODE_TYPES.includes(props.nodeTypeDescription.name),
|
||||
);
|
||||
const showUrlFor = ref('test');
|
||||
const showUrlFor = ref<'test' | 'production'>('test');
|
||||
|
||||
const isProductionOnly = computed(() => {
|
||||
return (
|
||||
@@ -95,6 +96,18 @@ const baseText = computed(() => {
|
||||
copyMessage: i18n.baseText('nodeWebhooks.showMessage.message.formTrigger'),
|
||||
};
|
||||
|
||||
case MCP_TRIGGER_NODE_TYPE:
|
||||
return {
|
||||
toggleTitle: i18n.baseText('nodeWebhooks.webhookUrls.mcpTrigger'),
|
||||
clickToDisplay: i18n.baseText('nodeWebhooks.clickToDisplayWebhookUrls.mcpTrigger'),
|
||||
clickToHide: i18n.baseText('nodeWebhooks.clickToHideWebhookUrls.mcpTrigger'),
|
||||
clickToCopy: i18n.baseText('nodeWebhooks.clickToCopyWebhookUrls.mcpTrigger'),
|
||||
testUrl: i18n.baseText('nodeWebhooks.testUrl'),
|
||||
productionUrl: i18n.baseText('nodeWebhooks.productionUrl'),
|
||||
copyTitle: i18n.baseText('nodeWebhooks.showMessage.title.mcpTrigger'),
|
||||
copyMessage: undefined,
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
toggleTitle: i18n.baseText('nodeWebhooks.webhookUrls'),
|
||||
|
||||
@@ -27,6 +27,7 @@ import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import {
|
||||
EnterpriseEditionFeature,
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
MCP_TRIGGER_NODE_TYPE,
|
||||
STICKY_NODE_TYPE,
|
||||
UPDATE_WEBHOOK_ID_NODE_TYPES,
|
||||
WEBHOOK_NODE_TYPE,
|
||||
@@ -1071,7 +1072,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||
|
||||
// if it's a webhook and the path is empty set the UUID as the default path
|
||||
if (
|
||||
[WEBHOOK_NODE_TYPE, FORM_TRIGGER_NODE_TYPE].includes(node.type) &&
|
||||
[WEBHOOK_NODE_TYPE, FORM_TRIGGER_NODE_TYPE, MCP_TRIGGER_NODE_TYPE].includes(node.type) &&
|
||||
node.parameters.path === ''
|
||||
) {
|
||||
node.parameters.path = node.webhookId as string;
|
||||
|
||||
@@ -679,20 +679,26 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
|
||||
function getWebhookUrl(
|
||||
webhookData: IWebhookDescription,
|
||||
node: INode,
|
||||
showUrlFor?: string,
|
||||
showUrlFor: 'test' | 'production',
|
||||
): string {
|
||||
const { isForm, restartWebhook } = webhookData;
|
||||
const { nodeType, restartWebhook } = webhookData;
|
||||
if (restartWebhook === true) {
|
||||
return isForm ? '$execution.resumeFormUrl' : '$execution.resumeUrl';
|
||||
}
|
||||
|
||||
let baseUrl;
|
||||
if (showUrlFor === 'test') {
|
||||
baseUrl = isForm ? rootStore.formTestUrl : rootStore.webhookTestUrl;
|
||||
} else {
|
||||
baseUrl = isForm ? rootStore.formUrl : rootStore.webhookUrl;
|
||||
return nodeType === 'form' ? '$execution.resumeFormUrl' : '$execution.resumeUrl';
|
||||
}
|
||||
|
||||
const baseUrls = {
|
||||
test: {
|
||||
form: rootStore.formTestUrl,
|
||||
mcp: rootStore.mcpTestUrl,
|
||||
webhook: rootStore.webhookTestUrl,
|
||||
},
|
||||
production: {
|
||||
form: rootStore.formUrl,
|
||||
mcp: rootStore.mcpUrl,
|
||||
webhook: rootStore.webhookUrl,
|
||||
},
|
||||
} as const;
|
||||
const baseUrl = baseUrls[showUrlFor][nodeType ?? 'webhook'];
|
||||
const workflowId = workflowsStore.workflowId;
|
||||
const path = getWebhookExpressionValue(webhookData, 'path', true, node.name) ?? '';
|
||||
const isFullPath =
|
||||
|
||||
@@ -143,6 +143,7 @@ export const JIRA_TRIGGER_NODE_TYPE = 'n8n-nodes-base.jiraTrigger';
|
||||
export const MICROSOFT_EXCEL_NODE_TYPE = 'n8n-nodes-base.microsoftExcel';
|
||||
export const MANUAL_TRIGGER_NODE_TYPE = 'n8n-nodes-base.manualTrigger';
|
||||
export const MANUAL_CHAT_TRIGGER_NODE_TYPE = '@n8n/n8n-nodes-langchain.manualChatTrigger';
|
||||
export const MCP_TRIGGER_NODE_TYPE = '@n8n/n8n-nodes-langchain.mcpTrigger';
|
||||
export const CHAT_TRIGGER_NODE_TYPE = '@n8n/n8n-nodes-langchain.chatTrigger';
|
||||
export const AGENT_NODE_TYPE = '@n8n/n8n-nodes-langchain.agent';
|
||||
export const OPEN_AI_NODE_TYPE = '@n8n/n8n-nodes-langchain.openAi';
|
||||
@@ -239,6 +240,7 @@ export const OPEN_URL_PANEL_TRIGGER_NODE_TYPES = [
|
||||
WEBHOOK_NODE_TYPE,
|
||||
FORM_TRIGGER_NODE_TYPE,
|
||||
CHAT_TRIGGER_NODE_TYPE,
|
||||
MCP_TRIGGER_NODE_TYPE,
|
||||
];
|
||||
|
||||
export const SINGLE_WEBHOOK_TRIGGERS = [
|
||||
|
||||
@@ -1495,23 +1495,28 @@
|
||||
"nodeWebhooks.clickToCopyWebhookUrls": "Click to copy webhook URLs",
|
||||
"nodeWebhooks.clickToCopyWebhookUrls.formTrigger": "Click to copy Form URL",
|
||||
"nodeWebhooks.clickToCopyWebhookUrls.chatTrigger": "Click to copy Chat URL",
|
||||
"nodeWebhooks.clickToCopyWebhookUrls.mcpTrigger": "Click to copy MCP URL",
|
||||
"nodeWebhooks.clickToDisplayWebhookUrls": "Click to display webhook URLs",
|
||||
"nodeWebhooks.clickToDisplayWebhookUrls.formTrigger": "Click to display Form URL",
|
||||
"nodeWebhooks.clickToDisplayWebhookUrls.chatTrigger": "Click to display Chat URL",
|
||||
"nodeWebhooks.clickToDisplayWebhookUrls.mcpTrigger": "Click to display MCP URL",
|
||||
"nodeWebhooks.clickToHideWebhookUrls": "Click to hide webhook URLs",
|
||||
"nodeWebhooks.clickToHideWebhookUrls.formTrigger": "Click to hide Form URL",
|
||||
"nodeWebhooks.clickToHideWebhookUrls.chatTrigger": "Click to hide Chat URL",
|
||||
"nodeWebhooks.clickToHideWebhookUrls.mcpTrigger": "Click to hide MCP URL",
|
||||
"nodeWebhooks.invalidExpression": "[INVALID EXPRESSION]",
|
||||
"nodeWebhooks.productionUrl": "Production URL",
|
||||
"nodeWebhooks.showMessage.title": "URL copied",
|
||||
"nodeWebhooks.showMessage.title.formTrigger": "Form URL copied",
|
||||
"nodeWebhooks.showMessage.title.chatTrigger": "Chat URL copied",
|
||||
"nodeWebhooks.showMessage.title.mcpTrigger": "MCP URL copied",
|
||||
"nodeWebhooks.showMessage.message.formTrigger": "Form submissions made via this URL will trigger the workflow when it's activated",
|
||||
"nodeWebhooks.showMessage.message.chatTrigger": "Chat submissions made via this URL will trigger the workflow when it's activated",
|
||||
"nodeWebhooks.testUrl": "Test URL",
|
||||
"nodeWebhooks.webhookUrls": "Webhook URLs",
|
||||
"nodeWebhooks.webhookUrls.formTrigger": "Form URLs",
|
||||
"nodeWebhooks.webhookUrls.chatTrigger": "Chat URL",
|
||||
"nodeWebhooks.webhookUrls.mcpTrigger": "MCP URL",
|
||||
"openWorkflow.workflowImportError": "Could not import workflow",
|
||||
"openWorkflow.workflowNotFoundError": "Could not find workflow",
|
||||
"parameterInput.expressionResult": "e.g. {result}",
|
||||
|
||||
@@ -7,7 +7,7 @@ import { computed, ref } from 'vue';
|
||||
const { VUE_APP_URL_BASE_API } = import.meta.env;
|
||||
|
||||
export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||
const state = ref({
|
||||
const state = ref<RootState>({
|
||||
baseUrl: VUE_APP_URL_BASE_API ?? window.BASE_PATH,
|
||||
restEndpoint:
|
||||
!window.REST_ENDPOINT || window.REST_ENDPOINT === '{{REST_ENDPOINT}}'
|
||||
@@ -17,6 +17,8 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||
endpointForm: 'form',
|
||||
endpointFormTest: 'form-test',
|
||||
endpointFormWaiting: 'form-waiting',
|
||||
endpointMcp: 'mcp',
|
||||
endpointMcpTest: 'mcp-test',
|
||||
endpointWebhook: 'webhook',
|
||||
endpointWebhookTest: 'webhook-test',
|
||||
endpointWebhookWaiting: 'webhook-waiting',
|
||||
@@ -49,10 +51,18 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||
|
||||
const webhookUrl = computed(() => `${state.value.urlBaseWebhook}${state.value.endpointWebhook}`);
|
||||
|
||||
const webhookTestUrl = computed(
|
||||
() => `${state.value.urlBaseEditor}${state.value.endpointWebhookTest}`,
|
||||
);
|
||||
|
||||
const webhookWaitingUrl = computed(
|
||||
() => `${state.value.urlBaseEditor}${state.value.endpointWebhookWaiting}`,
|
||||
);
|
||||
|
||||
const mcpUrl = computed(() => `${state.value.urlBaseWebhook}${state.value.endpointMcp}`);
|
||||
|
||||
const mcpTestUrl = computed(() => `${state.value.urlBaseEditor}${state.value.endpointMcpTest}`);
|
||||
|
||||
const pushRef = computed(() => state.value.pushRef);
|
||||
|
||||
const binaryDataMode = computed(() => state.value.binaryDataMode);
|
||||
@@ -67,10 +77,6 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||
|
||||
const OAuthCallbackUrls = computed(() => state.value.oauthCallbackUrls);
|
||||
|
||||
const webhookTestUrl = computed(
|
||||
() => `${state.value.urlBaseEditor}${state.value.endpointWebhookTest}`,
|
||||
);
|
||||
|
||||
const restUrl = computed(() => `${state.value.baseUrl}${state.value.restEndpoint}`);
|
||||
|
||||
const executionTimeout = computed(() => state.value.executionTimeout);
|
||||
@@ -164,7 +170,7 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||
state.value.defaultLocale = locale;
|
||||
};
|
||||
|
||||
const setBinaryDataMode = (binaryDataMode: string) => {
|
||||
const setBinaryDataMode = (binaryDataMode: RootState['binaryDataMode']) => {
|
||||
state.value.binaryDataMode = binaryDataMode;
|
||||
};
|
||||
|
||||
@@ -175,6 +181,8 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||
formUrl,
|
||||
formTestUrl,
|
||||
formWaitingUrl,
|
||||
mcpUrl,
|
||||
mcpTestUrl,
|
||||
webhookUrl,
|
||||
webhookTestUrl,
|
||||
webhookWaitingUrl,
|
||||
|
||||
Reference in New Issue
Block a user