@n8n/frontend-module-mcp 0.1.0 → 0.2.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/LICENSE_EE.md ADDED
@@ -0,0 +1,27 @@
1
+ # The n8n Enterprise License (the “Enterprise License”)
2
+
3
+ Copyright (c) 2022-present n8n GmbH.
4
+
5
+ With regard to the n8n Software:
6
+
7
+ This software and associated documentation files (the "Software") may only be used in production, if
8
+ you (and any entity that you represent) hold a valid n8n Enterprise license corresponding to your
9
+ usage. Subject to the foregoing sentence, you are free to modify this Software and publish patches
10
+ to the Software. You agree that n8n and/or its licensors (as applicable) retain all right, title and
11
+ interest in and to all such modifications and/or patches, and all such modifications and/or patches
12
+ may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid n8n
13
+ Enterprise license for the corresponding usage. Notwithstanding the foregoing, you may copy and
14
+ modify the Software for development and testing purposes, without requiring a subscription. You
15
+ agree that n8n and/or its licensors (as applicable) retain all right, title and interest in and to
16
+ all such modifications. You are not granted any other rights beyond what is expressly stated herein.
17
+ Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, and/or
18
+ sell the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
21
+ NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
23
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ For all third party components incorporated into the n8n Software, those components are licensed
27
+ under the original license provided by the owner of the applicable component.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n8n/frontend-module-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.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.43.0",
12
- "@n8n/composables": "1.33.0",
13
- "@n8n/design-system": "2.42.0",
14
- "@n8n/frontend-constants": "0.9.0",
15
- "@n8n/frontend-module-sdk": "0.13.0",
16
- "@n8n/i18n": "2.43.0",
17
- "@n8n/permissions": "0.80.0",
18
- "@n8n/rest-api-client": "2.43.0",
19
- "@n8n/stores": "2.43.0",
20
- "@n8n/telemetry": "1.11.0",
21
- "@n8n/utils": "1.50.0",
11
+ "@n8n/api-types": "1.44.0",
12
+ "@n8n/composables": "1.34.0",
13
+ "@n8n/design-system": "2.43.0",
14
+ "@n8n/frontend-constants": "0.10.0",
15
+ "@n8n/frontend-module-sdk": "0.14.0",
16
+ "@n8n/i18n": "2.44.0",
17
+ "@n8n/permissions": "0.81.0",
18
+ "@n8n/rest-api-client": "2.44.0",
19
+ "@n8n/stores": "2.44.0",
20
+ "@n8n/telemetry": "1.12.0",
21
+ "@n8n/utils": "1.51.0",
22
22
  "element-plus": "2.4.3",
23
23
  "lodash": "4.18.1",
24
- "n8n-workflow": "2.43.0",
24
+ "n8n-workflow": "2.44.0",
25
25
  "pinia": "^2.2.4",
26
26
  "reka-ui": "^2.5.0",
27
27
  "vue": "^3.5.13",
@@ -55,7 +55,16 @@
55
55
  "vitest-mock-extended": "^3.1.0",
56
56
  "vue-tsc": "^2.2.8"
57
57
  },
58
- "license": "LicenseRef-n8n-sustainable-use",
58
+ "license": "SEE LICENSE IN LICENSE.md",
59
+ "homepage": "https://n8n.io",
60
+ "author": {
61
+ "name": "Jan Oberhauser",
62
+ "email": "jan@n8n.io"
63
+ },
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/n8n-io/n8n.git"
67
+ },
59
68
  "scripts": {
60
69
  "clean": "rimraf .turbo",
61
70
  "typecheck": "vue-tsc --noEmit",
@@ -0,0 +1 @@
1
+ <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Antigravity</title><mask height="23" id="antigravity-mask" maskUnits="userSpaceOnUse" width="24" x="0" y="1"><path d="M21.751 22.607c1.34 1.005 3.35.335 1.508-1.508C17.73 15.74 18.904 1 12.037 1 5.17 1 6.342 15.74.815 21.1c-2.01 2.009.167 2.511 1.507 1.506 5.192-3.517 4.857-9.714 9.715-9.714 4.857 0 4.522 6.197 9.714 9.715z" fill="#fff"/></mask><g mask="url(#antigravity-mask)"><g filter="url(#antigravity-blur-1)"><path d="M-1.018-3.992c-.408 3.591 2.686 6.89 6.91 7.37 4.225.48 7.98-2.043 8.387-5.633.408-3.59-2.686-6.89-6.91-7.37-4.225-.479-7.98 2.043-8.387 5.633z" fill="#FFE432"/></g><g filter="url(#antigravity-blur-2)"><path d="M15.269 7.747c1.058 4.557 5.691 7.374 10.348 6.293 4.657-1.082 7.575-5.653 6.516-10.21-1.058-4.556-5.691-7.374-10.348-6.292-4.657 1.082-7.575 5.653-6.516 10.21z" fill="#FC413D"/></g><g filter="url(#antigravity-blur-3)"><path d="M-12.443 10.804c1.338 4.703 7.36 7.11 13.453 5.378 6.092-1.733 9.947-6.95 8.61-11.652C8.282-.173 2.26-2.58-3.833-.848-9.925.884-13.78 6.1-12.443 10.804z" fill="#00B95C"/></g><g filter="url(#antigravity-blur-4)"><path d="M-12.443 10.804c1.338 4.703 7.36 7.11 13.453 5.378 6.092-1.733 9.947-6.95 8.61-11.652C8.282-.173 2.26-2.58-3.833-.848-9.925.884-13.78 6.1-12.443 10.804z" fill="#00B95C"/></g><g filter="url(#antigravity-blur-5)"><path d="M-7.608 14.703c3.352 3.424 9.126 3.208 12.896-.483 3.77-3.69 4.108-9.459.756-12.883C2.69-2.087-3.083-1.871-6.853 1.82c-3.77 3.69-4.108 9.458-.755 12.883z" fill="#00B95C"/></g><g filter="url(#antigravity-blur-6)"><path d="M9.932 27.617c1.04 4.482 5.384 7.303 9.7 6.3 4.316-1.002 6.971-5.448 5.93-9.93-1.04-4.483-5.384-7.304-9.7-6.301-4.316 1.002-6.971 5.448-5.93 9.93z" fill="#3186FF"/></g><g filter="url(#antigravity-blur-7)"><path d="M2.572-8.185C.392-3.329 2.778 2.472 7.9 4.771c5.122 2.3 11.042.227 13.222-4.63 2.18-4.855-.205-10.656-5.327-12.955-5.122-2.3-11.042-.227-13.222 4.63z" fill="#FBBC04"/></g><g filter="url(#antigravity-blur-8)"><path d="M-3.267 38.686c-5.277-2.072 3.742-19.117 5.984-24.83 2.243-5.712 8.34-8.664 13.616-6.592 5.278 2.071 11.533 13.482 9.29 19.195-2.242 5.713-23.613 14.298-28.89 12.227z" fill="#3186FF"/></g><g filter="url(#antigravity-blur-9)"><path d="M28.71 17.471c-1.413 1.649-5.1.808-8.236-1.878-3.135-2.687-4.531-6.201-3.118-7.85 1.412-1.649 5.1-.808 8.235 1.878s4.532 6.2 3.119 7.85z" fill="#749BFF"/></g><g filter="url(#antigravity-blur-10)"><path d="M18.163 9.077c5.81 3.93 12.502 4.19 14.946.577 2.443-3.612-.287-9.727-6.098-13.658-5.81-3.931-12.502-4.19-14.946-.577-2.443 3.612.287 9.727 6.098 13.658z" fill="#FC413D"/></g><g filter="url(#antigravity-blur-11)"><path d="M-.915 2.684c-1.44 3.473-.97 6.967 1.05 7.804 2.02.837 4.824-1.3 6.264-4.772 1.44-3.473.97-6.967-1.05-7.804-2.02-.837-4.824 1.3-6.264 4.772z" fill="#FFEE48"/></g></g><defs><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="17.587" id="antigravity-blur-1" width="19.838" x="-3.288" y="-11.917"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="1.117"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="38.565" id="antigravity-blur-2" width="38.9" x="4.251" y="-13.493"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="5.4"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="36.517" id="antigravity-blur-3" width="40.955" x="-21.889" y="-10.592"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.591"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="36.517" id="antigravity-blur-4" width="40.955" x="-21.889" y="-10.592"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.591"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="36.595" id="antigravity-blur-5" width="36.632" x="-19.099" y="-10.278"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.591"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="34.087" id="antigravity-blur-6" width="33.533" x=".981" y="8.758"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="4.363"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="35.276" id="antigravity-blur-7" width="35.978" x="-6.143" y="-21.659"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.954"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="46.523" id="antigravity-blur-8" width="45.114" x="-11.96" y="-.46"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.531"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="24.054" id="antigravity-blur-9" width="25.094" x="10.485" y=".58"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.159"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="30.007" id="antigravity-blur-10" width="33.508" x="5.833" y="-12.467"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="2.669"/></filter><filter color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="26.151" id="antigravity-blur-11" width="22.194" x="-8.355" y="-8.876"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_977_115" stdDeviation="3.303"/></filter></defs></svg>
@@ -0,0 +1 @@
1
+ <svg fill="#000000" role="img" viewBox="49 48 329 329" xmlns="http://www.w3.org/2000/svg"><title>Devin</title><path d="M70 159.333V91.3471C70 88.3592 71.594 85.5983 74.1816 84.1044L133.043 50.1205C135.631 48.6265 138.819 48.6265 141.407 50.1205L200.269 84.1044C202.856 85.5983 204.45 88.3592 204.45 91.3471V126.068C204.708 137.606 210.806 148.734 221.531 154.926C232.256 161.117 244.942 160.834 255.063 155.289L285.132 137.929C287.719 136.435 290.907 136.435 293.495 137.929L352.357 171.913C354.944 173.406 356.538 176.167 356.538 179.155V247.123C356.538 250.111 354.944 252.872 352.357 254.366L293.495 288.35C290.907 289.844 287.719 289.844 285.132 288.35L255.306 271.13C245.146 265.456 232.344 265.117 221.534 271.358C210.809 277.55 204.711 288.678 204.453 300.215V334.926C204.453 337.914 202.859 340.675 200.271 342.169L141.41 376.153C138.822 377.647 135.634 377.647 133.046 376.153L74.1845 342.169C71.5969 340.675 70.0028 337.914 70.0028 334.926V266.959C70.0029 263.971 71.5969 261.21 74.1845 259.716L133.046 225.732C135.634 224.238 138.822 224.238 141.41 225.732L171.547 243.132C181.656 248.638 194.306 248.906 205.005 242.729C215.815 236.488 221.922 225.231 222.088 213.595C221.83 202.057 215.732 189.737 205.008 183.545C194.283 177.353 181.597 177.636 171.476 183.181L141.269 200.72C138.67 202.229 135.461 202.228 132.864 200.716L74.1576 166.562C71.5835 165.065 70 162.311 70 159.333Z"/></svg>
@@ -155,21 +155,26 @@ describe('MCPWorkflowsSelect', () => {
155
155
  data: [],
156
156
  });
157
157
 
158
- const { getByTestId } = createComponent({ pinia });
159
- await nextTick();
158
+ const { getAllByRole, findByRole, emitted } = createComponent({ pinia });
160
159
 
161
- // Wait for the fetch to complete
160
+ // The ready event follows the loading indicator delay.
162
161
  await waitFor(() => {
163
- expect(mcpStore.getMcpEligibleWorkflows).toHaveBeenCalled();
162
+ expect(emitted('ready')).toHaveLength(1);
164
163
  });
165
164
 
166
- const select = getByTestId('mcp-workflows-select');
167
- await userEvent.click(select);
165
+ const [input] = getAllByRole('combobox').filter(
166
+ (combobox) => !combobox.hasAttribute('readonly'),
167
+ );
168
+ expect(input).toHaveAttribute('aria-expanded', 'false');
169
+ await userEvent.click(input);
168
170
 
169
171
  await waitFor(() => {
170
- const emptyText = document.querySelector('.el-select-dropdown__empty');
171
- expect(emptyText).toBeInTheDocument();
172
+ expect(input).toHaveAttribute('aria-expanded', 'true');
172
173
  });
174
+
175
+ const emptyOption = await findByRole('option', { name: 'No available workflows' });
176
+ expect(emptyOption).toBeVisible();
177
+ expect(emptyOption).toHaveAttribute('aria-disabled', 'true');
173
178
  });
174
179
  });
175
180
 
@@ -69,6 +69,50 @@ describe('McpConnectClientDialog', () => {
69
69
  expect(body().queryByText('Authenticate')).not.toBeInTheDocument();
70
70
  });
71
71
 
72
+ it('should show the shared Antigravity config file and entry', async () => {
73
+ renderComponent({ pinia });
74
+
75
+ await waitFor(() => {
76
+ expect(body().getByTestId('mcp-connect-client-picker-trigger')).toBeInTheDocument();
77
+ });
78
+ await userEvent.click(body().getByTestId('mcp-connect-client-picker-trigger'));
79
+ const menuLabel = await body().findByText('Antigravity');
80
+ expect(
81
+ menuLabel.closest('[role="menuitem"]')?.querySelector('svg.brand-icon'),
82
+ ).toBeInTheDocument();
83
+ await userEvent.click(menuLabel);
84
+
85
+ expect(
86
+ body().getByTestId('mcp-connect-client-picker-trigger').querySelector('svg.brand-icon'),
87
+ ).toBeInTheDocument();
88
+ expect(body().getByText(/~\/\.gemini\/config\/mcp_config\.json/)).toHaveTextContent(
89
+ 'IDE, desktop app, and CLI (agy) share this file',
90
+ );
91
+ expect(body().getByTestId('mcp-config-snippet')).toHaveTextContent('mcpServers');
92
+ expect(body().getByTestId('mcp-config-snippet')).toHaveTextContent('serverUrl');
93
+ expect(body().queryByTestId('mcp-connect-one-click')).not.toBeInTheDocument();
94
+ });
95
+
96
+ it('should show the shared Devin config file and entry', async () => {
97
+ renderComponent({ pinia });
98
+
99
+ await waitFor(() => {
100
+ expect(body().getByTestId('mcp-connect-client-picker-trigger')).toBeInTheDocument();
101
+ });
102
+ await userEvent.click(body().getByTestId('mcp-connect-client-picker-trigger'));
103
+ const menuLabel = await body().findByText('Devin');
104
+ expect(
105
+ menuLabel.closest('[role="menuitem"]')?.querySelector('svg.brand-icon'),
106
+ ).toBeInTheDocument();
107
+ await userEvent.click(menuLabel);
108
+
109
+ expect(body().getByText(/~\/\.config\/devin\/mcp_config\.json/)).toHaveTextContent(
110
+ 'Devin desktop app and CLI share this file',
111
+ );
112
+ expect(body().getByTestId('mcp-config-snippet')).toHaveTextContent('"transport": "http"');
113
+ expect(body().queryByTestId('mcp-connect-one-click')).not.toBeInTheDocument();
114
+ });
115
+
72
116
  it('should show only the server URL (no one-click) for web clients without a connector URL', async () => {
73
117
  renderComponent({ pinia });
74
118
 
@@ -134,6 +134,16 @@ const addButtonLabel = computed(() =>
134
134
  interpolate: { client: activeClient.value?.name ?? '' },
135
135
  }),
136
136
  );
137
+ const sharedConfigFileDescriptionKeys: Partial<Record<string, BaseTextKey>> = {
138
+ antigravity: 'settings.mcp.connectDialog.configure.antigravity.description',
139
+ devin: 'settings.mcp.connectDialog.configure.devin.description',
140
+ };
141
+ const configureDescription = computed(() =>
142
+ i18n.baseText(
143
+ sharedConfigFileDescriptionKeys[activeClient.value?.id ?? ''] ??
144
+ 'settings.mcp.connectDialog.configure.description',
145
+ ),
146
+ );
137
147
  const serverUrlDescription = computed(() =>
138
148
  i18n.baseText('settings.mcp.connectDialog.serverUrl.description', {
139
149
  interpolate: { client: activeClient.value?.name ?? '' },
@@ -354,7 +364,7 @@ const serverUrlDescription = computed(() =>
354
364
  layout="vertical"
355
365
  :show-divider="false"
356
366
  :title="i18n.baseText('settings.mcp.connectDialog.configure.title')"
357
- :description="i18n.baseText('settings.mcp.connectDialog.configure.description')"
367
+ :description="configureDescription"
358
368
  >
359
369
  <template #action>
360
370
  <McpConfigSnippet
@@ -11,10 +11,27 @@ describe('getMcpClientCatalog', () => {
11
11
  expect(catalog.map((group) => group.clients.map((client) => client.id))).toEqual([
12
12
  ['claude-code', 'codex', 'gemini-cli'],
13
13
  ['claude-ai', 'chatgpt', 'mistral-vibe'],
14
- ['cursor', 'vscode', 'windsurf'],
14
+ ['antigravity', 'cursor', 'devin', 'vscode'],
15
15
  ]);
16
16
  });
17
17
 
18
+ it('should give Antigravity an HTTP server entry and keep Gemini CLI available', () => {
19
+ const antigravity = clients.find((client) => client.id === 'antigravity');
20
+ expect(JSON.parse(antigravity!.configSnippet!)).toEqual({
21
+ mcpServers: { n8n: { serverUrl: SERVER_URL } },
22
+ });
23
+ expect(clients.find((client) => client.id === 'gemini-cli')?.installCommand).toContain(
24
+ 'gemini mcp add',
25
+ );
26
+ });
27
+
28
+ it('should give Devin a streamable HTTP server entry', () => {
29
+ const devin = clients.find((client) => client.id === 'devin');
30
+ expect(JSON.parse(devin!.configSnippet!)).toEqual({
31
+ mcpServers: { n8n: { url: SERVER_URL, transport: 'http' } },
32
+ });
33
+ });
34
+
18
35
  it('should embed the server URL in every install command and config snippet', () => {
19
36
  for (const client of clients) {
20
37
  if (client.installCommand) expect(client.installCommand).toContain(SERVER_URL);
@@ -1,8 +1,10 @@
1
1
  import type { Component } from 'vue';
2
2
 
3
+ import AntigravityIcon from './assets/client-icons/antigravity.svg?component';
3
4
  import ClaudeIcon from './assets/client-icons/claude.svg?component';
4
5
  import CodexIcon from './assets/client-icons/codex.svg?component';
5
6
  import CursorIcon from './assets/client-icons/cursor.svg?component';
7
+ import DevinIcon from './assets/client-icons/devin.svg?component';
6
8
  import GeminiIcon from './assets/client-icons/gemini.svg?component';
7
9
  import MistralIcon from './assets/client-icons/mistral.svg?component';
8
10
  import OpenAiIcon from './assets/client-icons/openai.svg?component';
@@ -15,7 +17,7 @@ export interface McpClientSetup {
15
17
  /** Brand name, not translatable. */
16
18
  name: string;
17
19
  category: McpSetupCategory;
18
- /** Brand mark; null falls back to the neutral MCP icon (Windsurf has no official inlineable mark). */
20
+ /** Brand mark; null falls back to the neutral MCP icon. */
19
21
  icon: Component | null;
20
22
  installCommand?: string;
21
23
  /** Snippet for the client's MCP config file (JSON, or TOML for Codex). */
@@ -80,7 +82,15 @@ url = "${serverUrl}"`;
80
82
  }
81
83
  }
82
84
  }`;
83
- const windsurfSnippet = `{\n "mcpServers": {\n "n8n": {\n "serverUrl": "${serverUrl}"\n }\n }\n}`;
85
+ const serverUrlSnippet = `{\n "mcpServers": {\n "n8n": {\n "serverUrl": "${serverUrl}"\n }\n }\n}`;
86
+ const devinSnippet = `{
87
+ "mcpServers": {
88
+ "n8n": {
89
+ "url": "${serverUrl}",
90
+ "transport": "http"
91
+ }
92
+ }
93
+ }`;
84
94
 
85
95
  // One-click deep links, exactly as each editor expects: Cursor takes base64 of
86
96
  // `{"url": …}` in a `config` query param, VS Code a URL-encoded JSON object.
@@ -154,6 +164,13 @@ url = "${serverUrl}"`;
154
164
  {
155
165
  id: 'ide',
156
166
  clients: [
167
+ {
168
+ id: 'antigravity',
169
+ name: 'Antigravity',
170
+ category: 'ide',
171
+ icon: AntigravityIcon,
172
+ configSnippet: serverUrlSnippet,
173
+ },
157
174
  {
158
175
  id: 'cursor',
159
176
  name: 'Cursor',
@@ -162,6 +179,13 @@ url = "${serverUrl}"`;
162
179
  deepLink: cursorDeepLink,
163
180
  configSnippet: cursorSnippet,
164
181
  },
182
+ {
183
+ id: 'devin',
184
+ name: 'Devin',
185
+ category: 'ide',
186
+ icon: DevinIcon,
187
+ configSnippet: devinSnippet,
188
+ },
165
189
  {
166
190
  id: 'vscode',
167
191
  name: 'VS Code',
@@ -170,13 +194,6 @@ url = "${serverUrl}"`;
170
194
  deepLink: vscodeDeepLink,
171
195
  configSnippet: vscodeSnippet,
172
196
  },
173
- {
174
- id: 'windsurf',
175
- name: 'Windsurf',
176
- category: 'ide',
177
- icon: null,
178
- configSnippet: windsurfSnippet,
179
- },
180
197
  ],
181
198
  },
182
199
  ];
package/src/mcp.module.ts CHANGED
@@ -71,6 +71,7 @@ export const MCPModule = defineFrontendModule({
71
71
  settingsPages: [
72
72
  {
73
73
  id: 'settings-mcp',
74
+ order: 200,
74
75
  icon: 'mcp',
75
76
  get label() {
76
77
  return useI18n().baseText('settings.mcp');