diff --git a/packages/components/credentials/TeradataBearerToken.credential.ts b/packages/components/credentials/TeradataBearerToken.credential.ts new file mode 100644 index 000000000..d0a863041 --- /dev/null +++ b/packages/components/credentials/TeradataBearerToken.credential.ts @@ -0,0 +1,26 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class TeradataBearerTokenCredential implements INodeCredential { + label: string + name: string + description: string + version: number + inputs: INodeParams[] + + constructor() { + this.label = 'Teradata Bearer Token' + this.name = 'teradataBearerToken' + this.version = 1.0 + this.description = + 'Refer to official guide on how to get Teradata Bearer Token' + this.inputs = [ + { + label: 'Token', + name: 'token', + type: 'password' + } + ] + } +} + +module.exports = { credClass: TeradataBearerTokenCredential } diff --git a/packages/components/credentials/TeradataTD2.credential.ts b/packages/components/credentials/TeradataTD2.credential.ts new file mode 100644 index 000000000..ae3d8f042 --- /dev/null +++ b/packages/components/credentials/TeradataTD2.credential.ts @@ -0,0 +1,28 @@ +import { INodeParams, INodeCredential } from '../src/Interface' + +class TeradataTD2Credential implements INodeCredential { + label: string + name: string + version: number + inputs: INodeParams[] + + constructor() { + this.label = 'Teradata TD2 Auth' + this.name = 'teradataTD2Auth' + this.version = 1.0 + this.inputs = [ + { + label: 'Teradata TD2 Auth Username', + name: 'tdUsername', + type: 'string' + }, + { + label: 'Teradata TD2 Auth Password', + name: 'tdPassword', + type: 'password' + } + ] + } +} + +module.exports = { credClass: TeradataTD2Credential } diff --git a/packages/components/nodes/tools/MCP/Teradata/TeradataMCP.ts b/packages/components/nodes/tools/MCP/Teradata/TeradataMCP.ts new file mode 100644 index 000000000..3ccb49115 --- /dev/null +++ b/packages/components/nodes/tools/MCP/Teradata/TeradataMCP.ts @@ -0,0 +1,147 @@ +import { Tool } from '@langchain/core/tools' +import { ICommonObject, INode, INodeData, INodeOptionsValue, INodeParams } from '../../../../src/Interface' +import { getCredentialData, getCredentialParam } from '../../../../src/utils' +import { MCPToolkit } from '../core' +import hash from 'object-hash' + +class Teradata_MCP implements INode { + label: string + name: string + version: number + description: string + type: string + icon: string + category: string + baseClasses: string[] + documentation: string + credential: INodeParams + inputs: INodeParams[] + + constructor() { + this.label = 'Teradata MCP' + this.name = 'teradataMCP' + this.version = 1.0 + this.type = 'Teradata MCP Tool' + this.icon = 'teradata.svg' + this.category = 'Tools (MCP)' + this.description = 'MCP Server for Teradata (remote HTTP streamable)' + this.documentation = 'https://github.com/Teradata/teradata-mcp-server' + this.credential = { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['teradataTD2Auth', 'teradataBearerToken'], + description: 'Needed when using Teradata MCP server with authentication' + } + this.inputs = [ + { + label: 'MCP Server URL', + name: 'mcpUrl', + type: 'string', + placeholder: 'http://teradata-mcp-server:8001/mcp', + description: 'URL of your Teradata MCP server', + optional: false + }, + { + label: 'Bearer Token', + name: 'bearerToken', + type: 'string', + optional: true, + description: 'Optional to override Default set credentials' + }, + { + label: 'Available Actions', + name: 'mcpActions', + type: 'asyncMultiOptions', + loadMethod: 'listActions', + refresh: true + } + ] + this.baseClasses = ['Tool'] + } + + //@ts-ignore + loadMethods = { + listActions: async (nodeData: INodeData, options: ICommonObject): Promise => { + try { + const toolset = await this.getTools(nodeData, options) + toolset.sort((a: any, b: any) => a.name.localeCompare(b.name)) + return toolset.map(({ name, ...rest }) => ({ + label: name.toUpperCase(), + name: name, + description: rest.description || name + })) + } catch (error) { + console.error('Error listing actions:', error) + return [ + { + label: 'No Available Actions', + name: 'error', + description: 'No available actions, please check your MCP server URL and credentials, then refresh.' + } + ] + } + } + } + + async init(nodeData: INodeData, _: string, options: ICommonObject): Promise { + const tools = await this.getTools(nodeData, options) + const _mcpActions = nodeData.inputs?.mcpActions + let mcpActions = [] + if (_mcpActions) { + try { + mcpActions = typeof _mcpActions === 'string' ? JSON.parse(_mcpActions) : _mcpActions + } catch (error) { + console.error('Error parsing mcp actions:', error) + } + } + return tools.filter((tool: any) => mcpActions.includes(tool.name)) + } + + async getTools(nodeData: INodeData, options: ICommonObject): Promise { + const credentialData = await getCredentialData(nodeData.credential ?? '', options) + const mcpUrl = nodeData.inputs?.mcpUrl || 'http://teradata-mcp-server:8001/mcp' + if (!mcpUrl) { + throw new Error('Missing MCP Server URL') + } + // Determine auth method from credentials + let serverParams: any = { + url: mcpUrl, + headers: {} + } + // Get Bearer token from node input (from agent flow) or credential store + const bearerToken = nodeData.inputs?.bearerToken || getCredentialParam('token', credentialData, nodeData) + const username = getCredentialParam('tdUsername', credentialData, nodeData) + const password = getCredentialParam('tdPassword', credentialData, nodeData) + + if (bearerToken) { + serverParams.headers['Authorization'] = `Bearer ${bearerToken}` + } else if (username && password) { + serverParams.headers['Authorization'] = 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64') + } else { + throw new Error('Missing credentials: provide Bearer token from flow/credentials OR username/password from credentials') + } + const workspaceId = options?.searchOptions?.workspaceId?._value || options?.workspaceId || 'tdws_default' + let sandbox: ICommonObject = {} + const cacheKey = hash({ workspaceId, serverParams, sandbox }) + if (options.cachePool) { + const cachedResult = await options.cachePool.getMCPCache(cacheKey) + if (cachedResult) { + if (cachedResult.tools.length > 0) { + return cachedResult.tools + } + } + } + + // Use SSE for remote HTTP MCP servers + const toolkit = new MCPToolkit(serverParams, 'sse') + await toolkit.initialize() + const tools = toolkit.tools ?? [] + if (options.cachePool) { + await options.cachePool.addMCPCache(cacheKey, { toolkit, tools }) + } + return tools as Tool[] + } +} + +module.exports = { nodeClass: Teradata_MCP } diff --git a/packages/components/nodes/tools/MCP/Teradata/teradata.svg b/packages/components/nodes/tools/MCP/Teradata/teradata.svg new file mode 100644 index 000000000..38c343532 --- /dev/null +++ b/packages/components/nodes/tools/MCP/Teradata/teradata.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + +