@n8n/frontend-module-mcp 0.2.1 → 0.3.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/package.json +13 -13
- package/src/SettingsMCPAgentsView.vue +1 -1
- package/src/SettingsMCPWorkflowsView.vue +1 -1
- package/src/components/ConnectionParameter.vue +1 -13
- package/src/components/MCPEmptyState.test.ts +36 -0
- package/src/components/MCPEmptyState.vue +63 -35
- package/src/components/McpAccessTokenTab.vue +1 -1
- package/src/components/McpClientLogoCards.vue +11 -124
- package/src/components/McpConnectClientDialog.vue +1 -1
- package/src/components/McpEmptyStateCard.vue +5 -27
- package/src/components/OAuthClientDetailsModal.vue +1 -1
- package/src/components/tabs/AgentsTable.vue +1 -1
- package/src/components/tabs/WorkflowsTable.vue +1 -1
- package/src/mcp.clients.catalog.ts +15 -1
- package/src/mcp.constants.ts +2 -0
- package/src/modals/MCPConnectAgentsModal.vue +1 -1
- package/src/modals/MCPConnectWorkflowsModal.vue +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@n8n/frontend-module-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"exports": {
|
|
@@ -8,20 +8,20 @@
|
|
|
8
8
|
"./mcp.module": "./src/mcp.module.ts"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@n8n/api-types": "1.
|
|
12
|
-
"@n8n/composables": "1.
|
|
13
|
-
"@n8n/design-system": "2.
|
|
14
|
-
"@n8n/frontend-constants": "0.
|
|
15
|
-
"@n8n/frontend-module-sdk": "0.
|
|
16
|
-
"@n8n/i18n": "2.
|
|
17
|
-
"@n8n/permissions": "0.
|
|
18
|
-
"@n8n/rest-api-client": "2.
|
|
19
|
-
"@n8n/stores": "2.
|
|
20
|
-
"@n8n/telemetry": "1.
|
|
21
|
-
"@n8n/utils": "1.
|
|
11
|
+
"@n8n/api-types": "1.45.0",
|
|
12
|
+
"@n8n/composables": "1.35.0",
|
|
13
|
+
"@n8n/design-system": "2.44.0",
|
|
14
|
+
"@n8n/frontend-constants": "0.11.0",
|
|
15
|
+
"@n8n/frontend-module-sdk": "0.15.0",
|
|
16
|
+
"@n8n/i18n": "2.45.0",
|
|
17
|
+
"@n8n/permissions": "0.82.0",
|
|
18
|
+
"@n8n/rest-api-client": "2.45.0",
|
|
19
|
+
"@n8n/stores": "2.45.0",
|
|
20
|
+
"@n8n/telemetry": "1.13.0",
|
|
21
|
+
"@n8n/utils": "1.52.0",
|
|
22
22
|
"element-plus": "2.4.3",
|
|
23
23
|
"lodash": "4.18.1",
|
|
24
|
-
"n8n-workflow": "2.
|
|
24
|
+
"n8n-workflow": "2.45.0",
|
|
25
25
|
"pinia": "^2.2.4",
|
|
26
26
|
"reka-ui": "^2.5.0",
|
|
27
27
|
"vue": "^3.5.13",
|
|
@@ -72,7 +72,7 @@ const handleCopy = async (value: string) => {
|
|
|
72
72
|
>
|
|
73
73
|
<N8nButton
|
|
74
74
|
v-if="props.allowCopy && isSupported"
|
|
75
|
-
variant="
|
|
75
|
+
variant="outline"
|
|
76
76
|
icon-only
|
|
77
77
|
:icon="copied ? 'check' : 'copy'"
|
|
78
78
|
:class="$style['copy-button']"
|
|
@@ -171,18 +171,7 @@ const handleCopy = async (value: string) => {
|
|
|
171
171
|
|
|
172
172
|
button {
|
|
173
173
|
height: auto;
|
|
174
|
-
border: none;
|
|
175
174
|
border-radius: 0;
|
|
176
|
-
box-shadow: none;
|
|
177
|
-
|
|
178
|
-
&:hover {
|
|
179
|
-
border-color: inherit;
|
|
180
|
-
box-shadow: none;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
&:active {
|
|
184
|
-
box-shadow: none;
|
|
185
|
-
}
|
|
186
175
|
}
|
|
187
176
|
|
|
188
177
|
button + button {
|
|
@@ -207,7 +196,6 @@ const handleCopy = async (value: string) => {
|
|
|
207
196
|
}
|
|
208
197
|
|
|
209
198
|
.copy-button {
|
|
210
|
-
border: none;
|
|
211
199
|
border-radius: 0;
|
|
212
200
|
}
|
|
213
201
|
</style>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import userEvent from '@testing-library/user-event';
|
|
2
|
+
import { createComponentRenderer } from '@n8n/frontend-test-utils';
|
|
3
|
+
import MCPEmptyState from './MCPEmptyState.vue';
|
|
4
|
+
|
|
5
|
+
const renderComponent = createComponentRenderer(MCPEmptyState);
|
|
6
|
+
|
|
7
|
+
describe('MCPEmptyState', () => {
|
|
8
|
+
it('should render the design-system empty state with the MCP icon cards and both actions', () => {
|
|
9
|
+
const { getByTestId, container } = renderComponent();
|
|
10
|
+
|
|
11
|
+
const emptyState = getByTestId('mcp-empty-state-container');
|
|
12
|
+
expect(emptyState).toHaveClass('n8n-empty-state');
|
|
13
|
+
expect(emptyState).toHaveTextContent('Connect AI assistants to build and run workflows');
|
|
14
|
+
// The centre card carries the MCP mark; the side cards render the client brand marks.
|
|
15
|
+
expect(container.querySelector('[data-icon="mcp"]')).toBeInTheDocument();
|
|
16
|
+
expect(getByTestId('mcp-empty-state-learn-more')).toHaveAttribute('target', '_blank');
|
|
17
|
+
expect(getByTestId('enable-mcp-access-button')).toBeEnabled();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should emit turnOnMcp when the enable button is clicked', async () => {
|
|
21
|
+
const { getByTestId, emitted } = renderComponent();
|
|
22
|
+
|
|
23
|
+
await userEvent.click(getByTestId('enable-mcp-access-button'));
|
|
24
|
+
|
|
25
|
+
expect(emitted('turnOnMcp')).toHaveLength(1);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it.each([
|
|
29
|
+
['disabled', { disabled: true }],
|
|
30
|
+
['loading', { loading: true }],
|
|
31
|
+
])('should disable the enable button when %s', (_, props) => {
|
|
32
|
+
const { getByTestId } = renderComponent({ props });
|
|
33
|
+
|
|
34
|
+
expect(getByTestId('enable-mcp-access-button')).toBeDisabled();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue';
|
|
3
|
-
import { N8nButton, N8nIcon, N8nTooltip } from '@n8n/design-system';
|
|
4
|
-
import {
|
|
3
|
+
import { N8nButton, N8nEmptyState, N8nIcon, N8nTooltip } from '@n8n/design-system';
|
|
4
|
+
import type { EmptyStateIconCards } from '@n8n/design-system';
|
|
5
5
|
import { useI18n } from '@n8n/i18n';
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
import { MCP_CLIENT_LOGO_CYCLE } from '../mcp.clients.catalog';
|
|
8
|
+
import { MCP_DOCS_PAGE_URL } from '../mcp.constants';
|
|
7
9
|
|
|
8
10
|
type Props = {
|
|
9
11
|
disabled?: boolean;
|
|
@@ -24,52 +26,78 @@ const emit = defineEmits<{
|
|
|
24
26
|
const i18n = useI18n();
|
|
25
27
|
|
|
26
28
|
const buttonDisabled = computed(() => props.disabled || props.loading);
|
|
29
|
+
|
|
30
|
+
// The MCP mark, flanked by side cards cycling through the client brand marks.
|
|
31
|
+
const emptyStateIcon: EmptyStateIconCards = {
|
|
32
|
+
type: 'cards',
|
|
33
|
+
center: 'mcp',
|
|
34
|
+
sides: MCP_CLIENT_LOGO_CYCLE,
|
|
35
|
+
};
|
|
27
36
|
</script>
|
|
28
37
|
|
|
29
38
|
<template>
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
:class="$style.card"
|
|
39
|
+
<N8nEmptyState
|
|
40
|
+
:class="$style.emptyState"
|
|
33
41
|
data-test-id="mcp-empty-state-container"
|
|
34
|
-
:
|
|
42
|
+
:icon="emptyStateIcon"
|
|
43
|
+
:heading="i18n.baseText('settings.mcp.actionBox.heading')"
|
|
35
44
|
:description="i18n.baseText('settings.mcp.emptyState.description')"
|
|
36
45
|
>
|
|
37
|
-
<template #
|
|
38
|
-
<
|
|
39
|
-
variant="ghost"
|
|
40
|
-
size="medium"
|
|
41
|
-
:href="MCP_DOCS_PAGE_URL"
|
|
42
|
-
target="_blank"
|
|
43
|
-
data-test-id="mcp-empty-state-learn-more"
|
|
44
|
-
>
|
|
45
|
-
{{ i18n.baseText('generic.learnMore') }} <N8nIcon icon="arrow-up-right" size="small" />
|
|
46
|
-
</N8nButton>
|
|
47
|
-
<N8nTooltip :disabled="!buttonDisabled">
|
|
48
|
-
<template #content>
|
|
49
|
-
<span v-if="props.loading">{{ i18n.baseText('generic.loading') }}...</span>
|
|
50
|
-
<span v-else-if="props.managedByEnv">
|
|
51
|
-
{{ i18n.baseText('settings.mcp.managedByEnv.tooltip') }}
|
|
52
|
-
</span>
|
|
53
|
-
<span v-else>
|
|
54
|
-
{{ i18n.baseText('settings.mcp.toggle.disabled.tooltip') }}
|
|
55
|
-
</span>
|
|
56
|
-
</template>
|
|
46
|
+
<template #additionalContent>
|
|
47
|
+
<div :class="$style.actions">
|
|
57
48
|
<N8nButton
|
|
58
|
-
variant="
|
|
49
|
+
variant="ghost"
|
|
59
50
|
size="medium"
|
|
60
|
-
:
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
:href="MCP_DOCS_PAGE_URL"
|
|
52
|
+
target="_blank"
|
|
53
|
+
data-test-id="mcp-empty-state-learn-more"
|
|
63
54
|
>
|
|
64
|
-
{{ i18n.baseText('
|
|
55
|
+
{{ i18n.baseText('generic.learnMore') }}
|
|
56
|
+
<N8nIcon icon="arrow-up-right" size="small" />
|
|
65
57
|
</N8nButton>
|
|
66
|
-
|
|
58
|
+
<N8nTooltip :disabled="!buttonDisabled">
|
|
59
|
+
<template #content>
|
|
60
|
+
<span v-if="props.loading">{{ i18n.baseText('generic.loading') }}...</span>
|
|
61
|
+
<span v-else-if="props.managedByEnv">
|
|
62
|
+
{{ i18n.baseText('settings.mcp.managedByEnv.tooltip') }}
|
|
63
|
+
</span>
|
|
64
|
+
<span v-else>
|
|
65
|
+
{{ i18n.baseText('settings.mcp.toggle.disabled.tooltip') }}
|
|
66
|
+
</span>
|
|
67
|
+
</template>
|
|
68
|
+
<N8nButton
|
|
69
|
+
variant="solid"
|
|
70
|
+
size="medium"
|
|
71
|
+
:disabled="buttonDisabled"
|
|
72
|
+
data-test-id="enable-mcp-access-button"
|
|
73
|
+
@click="emit('turnOnMcp')"
|
|
74
|
+
>
|
|
75
|
+
{{ i18n.baseText('settings.mcp.actionBox.button.label') }}
|
|
76
|
+
</N8nButton>
|
|
77
|
+
</N8nTooltip>
|
|
78
|
+
</div>
|
|
67
79
|
</template>
|
|
68
|
-
</
|
|
80
|
+
</N8nEmptyState>
|
|
69
81
|
</template>
|
|
70
82
|
|
|
71
83
|
<style lang="scss" module>
|
|
72
|
-
|
|
84
|
+
@use '@n8n/design-system/css/mixins/motion';
|
|
85
|
+
|
|
86
|
+
.emptyState {
|
|
73
87
|
margin-top: var(--spacing--xl);
|
|
88
|
+
/* The DS card is transparent by default; keep the page's surface so it reads as a panel. */
|
|
89
|
+
background: var(--background--surface);
|
|
90
|
+
/* Fixed white tiles so the dark brand marks stay visible on the dark theme, as in the client rows. */
|
|
91
|
+
--empty-state-icon-cards--tile-background: var(--color--neutral-white);
|
|
92
|
+
--empty-state-icon-cards--tile-color: var(--color--neutral-black);
|
|
93
|
+
/* Gentle entrance for the enable/disable swap (empty state ⇄ full page). */
|
|
94
|
+
@include motion.fade-in-up;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.actions {
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
gap: var(--spacing--2xs);
|
|
74
102
|
}
|
|
75
103
|
</style>
|
|
@@ -1,134 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import type { Component } from 'vue';
|
|
4
|
-
import { N8nIcon } from '@n8n/design-system';
|
|
2
|
+
import { N8nEmptyStateIconCards } from '@n8n/design-system';
|
|
5
3
|
|
|
6
|
-
import
|
|
7
|
-
import CursorIcon from '../assets/client-icons/cursor.svg?component';
|
|
8
|
-
import OpenAiIcon from '../assets/client-icons/openai.svg?component';
|
|
9
|
-
import VsCodeIcon from '../assets/client-icons/vscode.svg?component';
|
|
10
|
-
|
|
11
|
-
const CLIENT_ICONS: Component[] = [ClaudeIcon, CursorIcon, VsCodeIcon, OpenAiIcon];
|
|
12
|
-
|
|
13
|
-
// Staggered fade/blur cycle through the known client logos on the side cards,
|
|
14
|
-
// same animation as the secrets-providers empty state.
|
|
15
|
-
const leftIconIndex = ref(0);
|
|
16
|
-
const rightIconIndex = ref(2);
|
|
17
|
-
const leftFading = ref(false);
|
|
18
|
-
const rightFading = ref(false);
|
|
19
|
-
let animationInterval: ReturnType<typeof setInterval> | null = null;
|
|
20
|
-
|
|
21
|
-
function animateLeft() {
|
|
22
|
-
leftFading.value = true;
|
|
23
|
-
setTimeout(() => {
|
|
24
|
-
leftIconIndex.value = (leftIconIndex.value + 1) % CLIENT_ICONS.length;
|
|
25
|
-
leftFading.value = false;
|
|
26
|
-
}, 300);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function animateRight() {
|
|
30
|
-
rightFading.value = true;
|
|
31
|
-
setTimeout(() => {
|
|
32
|
-
rightIconIndex.value = (rightIconIndex.value + 1) % CLIENT_ICONS.length;
|
|
33
|
-
rightFading.value = false;
|
|
34
|
-
}, 300);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
onMounted(() => {
|
|
38
|
-
animationInterval = setInterval(() => {
|
|
39
|
-
animateLeft();
|
|
40
|
-
setTimeout(() => {
|
|
41
|
-
animateRight();
|
|
42
|
-
}, 1500);
|
|
43
|
-
}, 3000);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
onUnmounted(() => {
|
|
47
|
-
if (animationInterval) {
|
|
48
|
-
clearInterval(animationInterval);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
4
|
+
import { MCP_CLIENT_LOGO_CYCLE } from '../mcp.clients.catalog';
|
|
51
5
|
</script>
|
|
52
6
|
|
|
53
7
|
<template>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/>
|
|
60
|
-
</div>
|
|
61
|
-
<div :class="$style.iconCard">
|
|
62
|
-
<N8nIcon icon="mcp" :class="$style.mcpIcon" />
|
|
63
|
-
</div>
|
|
64
|
-
<div :class="$style.iconCard">
|
|
65
|
-
<component
|
|
66
|
-
:is="CLIENT_ICONS[rightIconIndex]"
|
|
67
|
-
:class="[$style.clientLogo, { [$style.fading]: rightFading }]"
|
|
68
|
-
/>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
8
|
+
<N8nEmptyStateIconCards
|
|
9
|
+
:class="$style.cards"
|
|
10
|
+
center-icon="mcp"
|
|
11
|
+
:side-icons="MCP_CLIENT_LOGO_CYCLE"
|
|
12
|
+
/>
|
|
71
13
|
</template>
|
|
72
14
|
|
|
73
15
|
<style lang="scss" module>
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.iconCard {
|
|
81
|
-
width: 2.5rem;
|
|
82
|
-
height: 2.5rem;
|
|
83
|
-
border: var(--border);
|
|
84
|
-
display: flex;
|
|
85
|
-
justify-content: center;
|
|
86
|
-
align-items: center;
|
|
87
|
-
/* fixed white tile so dark brand marks stay visible on the dark theme */
|
|
88
|
-
background: var(--color--neutral-white);
|
|
89
|
-
box-shadow: var(--shadow--xs);
|
|
90
|
-
border-radius: var(--radius);
|
|
91
|
-
overflow: hidden;
|
|
92
|
-
|
|
93
|
-
&:nth-child(1) {
|
|
94
|
-
transform: rotate(-8deg);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
&:nth-child(2) {
|
|
98
|
-
z-index: 1;
|
|
99
|
-
margin-top: -0.3rem;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
&:nth-child(3) {
|
|
103
|
-
transform: rotate(8deg);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
.clientLogo {
|
|
108
|
-
width: var(--spacing--lg);
|
|
109
|
-
height: var(--spacing--lg);
|
|
110
|
-
object-fit: contain;
|
|
111
|
-
opacity: 1;
|
|
112
|
-
filter: blur(0);
|
|
113
|
-
transition:
|
|
114
|
-
opacity 300ms ease-in-out,
|
|
115
|
-
filter 300ms ease-in-out;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.mcpIcon {
|
|
119
|
-
font-size: var(--spacing--lg);
|
|
120
|
-
/* the tile is always white, so the MCP glyph must stay dark in both themes */
|
|
121
|
-
color: var(--color--neutral-black);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.fading {
|
|
125
|
-
opacity: 0;
|
|
126
|
-
filter: blur(4px);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@media (prefers-reduced-motion: reduce) {
|
|
130
|
-
.clientLogo {
|
|
131
|
-
transition: none;
|
|
132
|
-
}
|
|
16
|
+
.cards {
|
|
17
|
+
/* Fixed white tiles so the dark brand marks stay visible on the dark theme, as in the client rows. */
|
|
18
|
+
--empty-state-icon-cards--tile-background: var(--color--neutral-white);
|
|
19
|
+
--empty-state-icon-cards--tile-color: var(--color--neutral-black);
|
|
133
20
|
}
|
|
134
21
|
</style>
|
|
@@ -424,7 +424,7 @@ const serverUrlDescription = computed(() =>
|
|
|
424
424
|
display: block;
|
|
425
425
|
padding-block: var(--spacing--5xs);
|
|
426
426
|
font-size: var(--font-size--3xs);
|
|
427
|
-
font-weight: var(--font-weight--
|
|
427
|
+
font-weight: var(--font-weight--medium);
|
|
428
428
|
letter-spacing: 0.06em;
|
|
429
429
|
text-transform: uppercase;
|
|
430
430
|
color: var(--color--text--tint-1);
|
|
@@ -6,16 +6,14 @@ withDefaults(
|
|
|
6
6
|
defineProps<{
|
|
7
7
|
title: string;
|
|
8
8
|
description: string;
|
|
9
|
-
/** White surface instead of the page background (for the standalone disabled state). */
|
|
10
|
-
surface?: boolean;
|
|
11
9
|
dataTestId?: string;
|
|
12
10
|
}>(),
|
|
13
|
-
{
|
|
11
|
+
{ dataTestId: undefined },
|
|
14
12
|
);
|
|
15
13
|
</script>
|
|
16
14
|
|
|
17
15
|
<template>
|
|
18
|
-
<div :class="
|
|
16
|
+
<div :class="$style.card" :data-test-id="dataTestId">
|
|
19
17
|
<McpClientLogoCards :class="$style.cards" />
|
|
20
18
|
<div :class="$style.copy">
|
|
21
19
|
<N8nText bold size="large" color="text-dark">{{ title }}</N8nText>
|
|
@@ -28,6 +26,8 @@ withDefaults(
|
|
|
28
26
|
</template>
|
|
29
27
|
|
|
30
28
|
<style lang="scss" module>
|
|
29
|
+
@use '@n8n/design-system/css/mixins/motion';
|
|
30
|
+
|
|
31
31
|
.card {
|
|
32
32
|
display: flex;
|
|
33
33
|
flex-direction: column;
|
|
@@ -38,29 +38,7 @@ withDefaults(
|
|
|
38
38
|
border: var(--border-width) dashed var(--border-color);
|
|
39
39
|
border-radius: var(--radius--lg);
|
|
40
40
|
/* Gentle entrance, matching the prototype's empty states. */
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.surface {
|
|
45
|
-
background: var(--background--surface);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@keyframes mcp-reveal-in {
|
|
49
|
-
from {
|
|
50
|
-
opacity: 0;
|
|
51
|
-
transform: translateY(var(--spacing--2xs));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
to {
|
|
55
|
-
opacity: 1;
|
|
56
|
-
transform: translateY(0);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@media (prefers-reduced-motion: reduce) {
|
|
61
|
-
.card {
|
|
62
|
-
animation: none;
|
|
63
|
-
}
|
|
41
|
+
@include motion.fade-in-up;
|
|
64
42
|
}
|
|
65
43
|
|
|
66
44
|
.cards {
|
|
@@ -237,7 +237,7 @@ const projectLink = (agent: McpAgent) =>
|
|
|
237
237
|
>
|
|
238
238
|
<template #actions>
|
|
239
239
|
<N8nButton
|
|
240
|
-
variant="
|
|
240
|
+
variant="outline"
|
|
241
241
|
data-test-id="mcp-bulk-remove-agent-access-button"
|
|
242
242
|
:label="i18n.baseText('settings.mcp.agents.table.action.removeMCPAccess')"
|
|
243
243
|
@click="onBulkRemoveMcpAccess"
|
|
@@ -291,7 +291,7 @@ const onConnectClick = () => {
|
|
|
291
291
|
>
|
|
292
292
|
<template #actions>
|
|
293
293
|
<N8nButton
|
|
294
|
-
variant="
|
|
294
|
+
variant="outline"
|
|
295
295
|
data-test-id="mcp-bulk-remove-access-button"
|
|
296
296
|
:label="i18n.baseText('settings.mcp.workflows.table.action.removeMCPAccess')"
|
|
297
297
|
@click="onBulkRemoveMcpAccess"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { markRaw, type Component } from 'vue';
|
|
2
2
|
|
|
3
3
|
import AntigravityIcon from './assets/client-icons/antigravity.svg?component';
|
|
4
4
|
import ClaudeIcon from './assets/client-icons/claude.svg?component';
|
|
@@ -10,6 +10,20 @@ import MistralIcon from './assets/client-icons/mistral.svg?component';
|
|
|
10
10
|
import OpenAiIcon from './assets/client-icons/openai.svg?component';
|
|
11
11
|
import VsCodeIcon from './assets/client-icons/vscode.svg?component';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Brand marks the decorative empty-state cards cycle through, interleaved so consecutive
|
|
15
|
+
* marks read clearly different (coral wordmark, blue ribbon, purple blossom, near-black
|
|
16
|
+
* cube, gradient spark, black blossom).
|
|
17
|
+
*/
|
|
18
|
+
export const MCP_CLIENT_LOGO_CYCLE: Component[] = [
|
|
19
|
+
ClaudeIcon,
|
|
20
|
+
VsCodeIcon,
|
|
21
|
+
CodexIcon,
|
|
22
|
+
CursorIcon,
|
|
23
|
+
GeminiIcon,
|
|
24
|
+
OpenAiIcon,
|
|
25
|
+
].map((icon) => markRaw(icon));
|
|
26
|
+
|
|
13
27
|
export type McpSetupCategory = 'cli' | 'web' | 'ide';
|
|
14
28
|
|
|
15
29
|
export interface McpClientSetup {
|
package/src/mcp.constants.ts
CHANGED
|
@@ -9,6 +9,7 @@ export const MCP_SCOPE_GROUPS: Array<{ key: string; resources: readonly string[]
|
|
|
9
9
|
{ key: 'workflows', resources: ['workflow', 'tag'] },
|
|
10
10
|
{ key: 'executions', resources: ['execution'] },
|
|
11
11
|
{ key: 'agents', resources: ['agent'] },
|
|
12
|
+
{ key: 'skills', resources: ['skill'] },
|
|
12
13
|
{ key: 'credentials', resources: ['credential'] },
|
|
13
14
|
{ key: 'dataTables', resources: ['dataTable'] },
|
|
14
15
|
{ key: 'projectsAndFolders', resources: ['project'] },
|
|
@@ -21,6 +22,7 @@ export const MCP_SCOPE_RESOURCE_ICONS: Record<string, IconName> = {
|
|
|
21
22
|
workflow: 'workflow',
|
|
22
23
|
execution: 'history',
|
|
23
24
|
agent: 'robot',
|
|
25
|
+
skill: 'graduation-cap',
|
|
24
26
|
credential: 'key-round',
|
|
25
27
|
dataTable: 'table',
|
|
26
28
|
project: 'folder',
|