@n8n/n8n-nodes-langchain
Version:

228 lines • 8.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAllTools = getAllTools;
exports.mapToNodeOperationError = mapToNodeOperationError;
exports.connectMcpClient = connectMcpClient;
exports.getAuthHeaders = getAuthHeaders;
exports.tryRefreshOAuth2Token = tryRefreshOAuth2Token;
exports.isStructuredContent = isStructuredContent;
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
const n8n_workflow_1 = require("n8n-workflow");
const ai_utilities_1 = require("@n8n/ai-utilities");
async function getAllTools(client, cursor) {
const { tools, nextCursor } = await client.listTools({ cursor });
if (nextCursor) {
return tools.concat(await getAllTools(client, nextCursor));
}
return tools;
}
function safeCreateUrl(url, baseUrl) {
try {
return (0, n8n_workflow_1.createResultOk)(new URL(url, baseUrl));
}
catch (error) {
return (0, n8n_workflow_1.createResultError)(error);
}
}
function normalizeAndValidateUrl(input) {
const withProtocol = !/^https?:\/\//i.test(input) ? `https://${input}` : input;
const parsedUrl = safeCreateUrl(withProtocol);
if (!parsedUrl.ok) {
return (0, n8n_workflow_1.createResultError)(parsedUrl.error);
}
return parsedUrl;
}
function errorHasCode(error, code) {
return (!!error &&
typeof error === 'object' &&
(('code' in error && Number(error.code) === code) ||
('message' in error &&
typeof error.message === 'string' &&
error.message.includes(code.toString()))));
}
function isUnauthorizedError(error) {
return errorHasCode(error, 401);
}
function isForbiddenError(error) {
return errorHasCode(error, 403);
}
function mapToNodeOperationError(node, error) {
switch (error.type) {
case 'invalid_url':
return new n8n_workflow_1.NodeOperationError(node, error.error, {
message: 'Could not connect to your MCP server. The provided URL is invalid.',
});
case 'auth':
return new n8n_workflow_1.NodeOperationError(node, error.error, {
message: 'Could not connect to your MCP server. Authentication failed.',
description: error.error.message,
});
case 'connection':
default:
return new n8n_workflow_1.NodeOperationError(node, error.error, {
message: 'Could not connect to your MCP server',
description: error.error.message,
});
}
}
async function connectMcpClient({ headers, serverTransport, endpointUrl, name, version, onUnauthorized, }) {
const endpoint = normalizeAndValidateUrl(endpointUrl);
if (!endpoint.ok) {
return (0, n8n_workflow_1.createResultError)({ type: 'invalid_url', error: endpoint.error });
}
const authFetch = createAuthFetch(headers, onUnauthorized);
const client = new index_js_1.Client({ name, version: version.toString() }, { capabilities: {} });
if (serverTransport === 'httpStreamable') {
try {
const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(endpoint.result, {
fetch: authFetch,
});
await client.connect(transport);
return (0, n8n_workflow_1.createResultOk)(client);
}
catch (error) {
if (isUnauthorizedError(error) || isForbiddenError(error)) {
return (0, n8n_workflow_1.createResultError)({ type: 'auth', error: error });
}
else {
return (0, n8n_workflow_1.createResultError)({ type: 'connection', error: error });
}
}
}
try {
const sseTransport = new sse_js_1.SSEClientTransport(endpoint.result, {
eventSourceInit: {
fetch: async (url, init) => await authFetch(url, {
...init,
headers: {
...headersToRecord(init?.headers),
Accept: 'text/event-stream',
},
}),
},
fetch: authFetch,
});
await client.connect(sseTransport);
return (0, n8n_workflow_1.createResultOk)(client);
}
catch (error) {
if (isUnauthorizedError(error) || isForbiddenError(error)) {
return (0, n8n_workflow_1.createResultError)({ type: 'auth', error: error });
}
else {
return (0, n8n_workflow_1.createResultError)({ type: 'connection', error: error });
}
}
}
function headersToRecord(headers) {
if (!headers)
return {};
if (headers instanceof Headers)
return Object.fromEntries(headers.entries());
if (Array.isArray(headers))
return Object.fromEntries(headers);
return headers;
}
function createAuthFetch(initialHeaders, onUnauthorized) {
let headers = initialHeaders;
return async (input, init) => {
const response = await (0, ai_utilities_1.proxyFetch)(input, {
...init,
headers: {
...headersToRecord(init?.headers),
...headers,
},
});
if (response.status !== 401 || !onUnauthorized) {
return response;
}
const refreshedHeaders = await onUnauthorized(headers);
if (!refreshedHeaders) {
return response;
}
headers = refreshedHeaders;
return await (0, ai_utilities_1.proxyFetch)(input, {
...init,
headers: {
...headersToRecord(init?.headers),
...headers,
},
});
};
}
async function getAuthHeaders(ctx, authentication) {
switch (authentication) {
case 'headerAuth': {
const header = await ctx
.getCredentials('httpHeaderAuth')
.catch(() => null);
if (!header)
return {};
return { headers: { [header.name]: header.value } };
}
case 'bearerAuth': {
const result = await ctx
.getCredentials('httpBearerAuth')
.catch(() => null);
if (!result)
return {};
return { headers: { Authorization: `Bearer ${result.token}` } };
}
case 'mcpOAuth2Api': {
const result = await ctx
.getCredentials('mcpOAuth2Api')
.catch(() => null);
if (!result)
return {};
return { headers: { Authorization: `Bearer ${result.oauthTokenData.access_token}` } };
}
case 'multipleHeadersAuth': {
const result = await ctx
.getCredentials('httpMultipleHeadersAuth')
.catch(() => null);
if (!result)
return {};
return {
headers: result.headers.values.reduce((acc, cur) => {
acc[cur.name] = cur.value;
return acc;
}, {}),
};
}
case 'none':
default: {
return {};
}
}
}
async function tryRefreshOAuth2Token(ctx, authentication, headers) {
if (authentication !== 'mcpOAuth2Api') {
return null;
}
let access_token = null;
try {
const result = (await ctx.helpers.refreshOAuth2Token.call(ctx, 'mcpOAuth2Api'));
access_token = result?.access_token;
}
catch (error) {
return null;
}
if (!access_token) {
return null;
}
if (!headers) {
return {
Authorization: `Bearer ${access_token}`,
};
}
return {
...headers,
Authorization: `Bearer ${access_token}`,
};
}
function isStructuredContent(value) {
return (value !== undefined && value !== null && typeof value === 'object' && !Array.isArray(value));
}
//# sourceMappingURL=utils.js.map