n8n
Version:
n8n Workflow Automation Tool
286 lines • 12.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const n8n_workflow_1 = require("n8n-workflow");
const node_description_transform_1 = require("./node-description-transform");
const mock_servers_1 = require("./registry/mock-servers");
const baseDescription = {
displayName: 'MCP Registry Client (internal)',
name: 'mcpRegistryClientTool',
hidden: true,
group: ['output'],
version: 1,
description: 'Runtime backing for MCP registry-derived nodes',
defaults: { name: 'MCP Registry Client' },
codex: {
categories: ['AI'],
subcategories: { AI: ['Model Context Protocol'] },
alias: ['MCP', 'Model Context Protocol'],
},
inputs: [],
outputs: [],
credentials: [{ name: 'mcpOAuth2Api', required: true }],
properties: [
{ displayName: 'Endpoint URL', name: 'endpointUrl', type: 'hidden', default: '' },
{
displayName: 'Server Transport',
name: 'serverTransport',
type: 'hidden',
default: 'httpStreamable',
},
{
displayName: 'Tools to Include',
name: 'include',
type: 'options',
default: 'all',
options: [],
},
],
};
describe('serverToNodeDescription', () => {
it('returns a description tailored to the Notion mock server', () => {
const description = (0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
expect(description).not.toBeNull();
expect(description).toMatchObject({
name: 'notion',
displayName: 'Notion MCP',
description: mock_servers_1.notionMockServer.description,
iconUrl: mock_servers_1.notionMockServer.icons[0].src,
defaults: { name: 'Notion MCP' },
version: 1,
});
expect(description?.hidden).toBeUndefined();
});
it('prefers streamable-http when both remotes are available', () => {
const description = (0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
const endpointUrl = description?.properties.find((p) => p.name === 'endpointUrl');
const serverTransport = description?.properties.find((p) => p.name === 'serverTransport');
expect(serverTransport?.default).toBe('httpStreamable');
expect(endpointUrl?.default).toBe('https://mcp.notion.com/mcp');
});
it('falls back to sse when only sse is available', () => {
const sseOnlyServer = {
...mock_servers_1.notionMockServer,
remotes: [{ type: 'sse', url: 'https://mcp.notion.com/sse' }],
};
const description = (0, node_description_transform_1.serverToNodeDescription)(sseOnlyServer, baseDescription);
const endpointUrl = description?.properties.find((p) => p.name === 'endpointUrl');
const serverTransport = description?.properties.find((p) => p.name === 'serverTransport');
expect(serverTransport?.default).toBe('sse');
expect(endpointUrl?.default).toBe('https://mcp.notion.com/sse');
});
it('returns null when no supported remote is available', () => {
const unsupportedServer = {
...mock_servers_1.notionMockServer,
remotes: [],
};
expect((0, node_description_transform_1.serverToNodeDescription)(unsupportedServer, baseDescription)).toBeNull();
});
it('marks deprecated servers as hidden so the node creator skips them', () => {
const deprecatedServer = { ...mock_servers_1.notionMockServer, status: 'deprecated' };
const description = (0, node_description_transform_1.serverToNodeDescription)(deprecatedServer, baseDescription);
expect(description?.hidden).toBe(true);
});
it('returns a themed iconUrl when both light and dark variants are available', () => {
const themedServer = {
...mock_servers_1.notionMockServer,
icons: [
{ src: 'https://example.com/light.svg', theme: 'light' },
{ src: 'https://example.com/dark.svg', theme: 'dark' },
],
};
const description = (0, node_description_transform_1.serverToNodeDescription)(themedServer, baseDescription);
expect(description?.iconUrl).toEqual({
light: 'https://example.com/light.svg',
dark: 'https://example.com/dark.svg',
});
});
it('falls back to the first icon when only one theme is provided', () => {
const lightOnlyServer = {
...mock_servers_1.notionMockServer,
icons: [{ src: 'https://example.com/light.svg', theme: 'light' }],
};
const description = (0, node_description_transform_1.serverToNodeDescription)(lightOnlyServer, baseDescription);
expect(description?.iconUrl).toBe('https://example.com/light.svg');
});
it('prefers SVG over PNG over JPG when multiple formats are available', () => {
const multiFormatServer = {
...mock_servers_1.notionMockServer,
icons: [
{ src: 'https://example.com/icon.jpg', mimeType: 'image/jpeg' },
{ src: 'https://example.com/icon.png', mimeType: 'image/png' },
{ src: 'https://example.com/icon.svg', mimeType: 'image/svg+xml' },
],
};
const description = (0, node_description_transform_1.serverToNodeDescription)(multiFormatServer, baseDescription);
expect(description?.iconUrl).toBe('https://example.com/icon.svg');
});
it('prefers PNG over JPG when SVG is not available', () => {
const noSvgServer = {
...mock_servers_1.notionMockServer,
icons: [
{ src: 'https://example.com/icon.jpg', mimeType: 'image/jpeg' },
{ src: 'https://example.com/icon.png', mimeType: 'image/png' },
],
};
const description = (0, node_description_transform_1.serverToNodeDescription)(noSvgServer, baseDescription);
expect(description?.iconUrl).toBe('https://example.com/icon.png');
});
it('applies mime type preference within each theme', () => {
const themedMultiFormatServer = {
...mock_servers_1.notionMockServer,
icons: [
{ src: 'https://example.com/light.png', mimeType: 'image/png', theme: 'light' },
{ src: 'https://example.com/light.svg', mimeType: 'image/svg+xml', theme: 'light' },
{ src: 'https://example.com/dark.jpg', mimeType: 'image/jpeg', theme: 'dark' },
{ src: 'https://example.com/dark.png', mimeType: 'image/png', theme: 'dark' },
],
};
const description = (0, node_description_transform_1.serverToNodeDescription)(themedMultiFormatServer, baseDescription);
expect(description?.iconUrl).toEqual({
light: 'https://example.com/light.svg',
dark: 'https://example.com/dark.png',
});
});
it('extends codex.alias with the title and displayName', () => {
const description = (0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
expect(description?.codex?.alias).toEqual([
'MCP',
'Model Context Protocol',
'Notion',
'Notion MCP',
]);
});
it('sets primaryDocumentation when websiteUrl is present', () => {
const description = (0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
expect(description?.codex?.resources).toEqual({
primaryDocumentation: [{ url: mock_servers_1.notionMockServer.websiteUrl }],
});
});
it('does not mutate the input baseDescription', () => {
const snapshot = (0, n8n_workflow_1.deepCopy)(baseDescription);
(0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
expect(baseDescription).toEqual(snapshot);
});
it('leaves properties other than endpointUrl and serverTransport untouched', () => {
const description = (0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
const include = description?.properties.find((p) => p.name === 'include');
expect(include).toEqual(baseDescription.properties.find((p) => p.name === 'include'));
});
it('matches inline snapshot for Notion mock server', () => {
const description = (0, node_description_transform_1.serverToNodeDescription)(mock_servers_1.notionMockServer, baseDescription);
expect(description).toMatchInlineSnapshot(`
{
"codex": {
"alias": [
"MCP",
"Model Context Protocol",
"Notion",
"Notion MCP",
],
"categories": [
"AI",
],
"resources": {
"primaryDocumentation": [
{
"url": "https://developers.notion.com/docs/mcp",
},
],
},
"subcategories": {
"AI": [
"Model Context Protocol",
],
},
},
"credentials": [
{
"name": "notionMcpOAuth2Api",
"required": true,
},
],
"defaults": {
"name": "Notion MCP",
},
"description": "Notion's official MCP server lets you use your Notion workspace as a system of record for knowledge work and software development. Search questions about the codebase and business, fetch links to pages such as tech specs and PRDs, and track tasks with your team.",
"displayName": "Notion MCP",
"group": [
"output",
],
"iconUrl": "https://mcp.notion.com/notion-logo-block-main.svg",
"inputs": [],
"name": "notion",
"outputs": [],
"properties": [
{
"default": "https://mcp.notion.com/mcp",
"displayName": "Endpoint URL",
"name": "endpointUrl",
"type": "hidden",
},
{
"default": "httpStreamable",
"displayName": "Server Transport",
"name": "serverTransport",
"type": "hidden",
},
{
"default": "all",
"displayName": "Tools to Include",
"name": "include",
"options": [],
"type": "options",
},
],
"version": 1,
}
`);
});
});
describe('serverToCredentialDescription', () => {
it('returns a description for servers with OAuth2 auth type', () => {
const description = (0, node_description_transform_1.serverToCredentialDescription)(mock_servers_1.notionMockServer);
expect(description).not.toBeNull();
expect(description).toEqual({
name: 'notionMcpOAuth2Api',
displayName: 'Notion MCP OAuth2',
extends: ['mcpOAuth2Api'],
icon: 'node:@n8n/mcp-registry.notion',
properties: [
{
displayName: 'Use Dynamic Client Registration',
name: 'useDynamicClientRegistration',
type: 'hidden',
default: true,
},
{
displayName: 'Server URL',
name: 'serverUrl',
type: 'hidden',
default: 'https://mcp.notion.com/mcp',
},
{
displayName: 'Allowed HTTP Request Domains',
name: 'allowedHttpRequestDomains',
type: 'hidden',
default: 'none',
},
],
});
});
it('returns null when the auth type is not supported', () => {
const unsupportedServer = {
...mock_servers_1.notionMockServer,
authType: 'foo',
};
expect((0, node_description_transform_1.serverToCredentialDescription)(unsupportedServer)).toBeNull();
});
it('returns null when no remote is available', () => {
const noRemoteServer = {
...mock_servers_1.notionMockServer,
remotes: [],
};
expect((0, node_description_transform_1.serverToCredentialDescription)(noRemoteServer)).toBeNull();
});
});
//# sourceMappingURL=node-description-transform.test.js.map