n8n
Version:
n8n Workflow Automation Tool
193 lines • 9.63 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentsToolsService = exports.isAgentToolNodeType = exports.isExecutableNodeType = void 0;
const agents_1 = require("@n8n/agents");
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const workflow_sdk_1 = require("@n8n/workflow-sdk");
const n8n_workflow_1 = require("n8n-workflow");
const zod_1 = require("zod");
const node_execution_1 = require("../../node-execution");
const node_catalog_1 = require("../../node-catalog");
const isExecutableNodeType = (nodeId) => !(0, n8n_workflow_1.isTriggerNodeType)(nodeId);
exports.isExecutableNodeType = isExecutableNodeType;
const isAgentToolNodeType = (nodeId) => (0, exports.isExecutableNodeType)(nodeId) &&
((0, n8n_workflow_1.isToolType)(nodeId, { includeHitl: false }) || (0, node_execution_1.isAgentProviderNode)(nodeId));
exports.isAgentToolNodeType = isAgentToolNodeType;
const searchNodesInputSchema = zod_1.z.object({
queries: zod_1.z.array(zod_1.z.string()).min(1).describe('Search queries (e.g., ["gmail", "slack", "http"])'),
});
const nodeVersionSchema = zod_1.z.number().describe('Tool node type version from search_nodes');
const getNodeTypesInputSchema = zod_1.z.object({
nodeIds: zod_1.z
.array(zod_1.z.union([
zod_1.z.string(),
zod_1.z.object({
nodeId: zod_1.z.string(),
version: nodeVersionSchema.optional(),
resource: zod_1.z.string().optional(),
operation: zod_1.z.string().optional(),
mode: zod_1.z.string().optional(),
}),
]))
.min(1)
.describe('Tool node IDs from search_nodes (e.g., ["n8n-nodes-base.gmailTool"])'),
});
const listCredentialsInputSchema = zod_1.z.object({
types: zod_1.z
.array(zod_1.z.string())
.optional()
.describe('Optional credential types to filter by (e.g., ["gmailOAuth2", "httpHeaderAuth"]). ' +
'When omitted, returns all credentials. Use the credential types declared in the ' +
'node schema from get_node_types to narrow the results.'),
});
const runNodeInputSchema = zod_1.z.object({
nodeType: zod_1.z.string().describe('Tool node type identifier from search_nodes'),
nodeTypeVersion: nodeVersionSchema,
nodeParameters: zod_1.z
.record(zod_1.z.unknown())
.optional()
.describe('Static node config. Use expressions like ={{ $json.url }} to reference inputData fields.'),
credentials: zod_1.z
.record(zod_1.z.object({ id: zod_1.z.string(), name: zod_1.z.string() }))
.optional()
.describe('Credential slot → { id, name }. Copy from list_credentials results.'),
inputData: zod_1.z
.record(zod_1.z.unknown())
.optional()
.describe('Runtime input, available as $json inside nodeParameters expressions.'),
});
let AgentsToolsService = class AgentsToolsService {
constructor(logger, nodeCatalogService, ephemeralNodeExecutor) {
this.logger = logger;
this.nodeCatalogService = nodeCatalogService;
this.ephemeralNodeExecutor = ephemeralNodeExecutor;
}
getSharedTools(credentialProvider, listCredentialsUsageHint) {
return [
this.buildSearchNodesTool(),
this.buildGetNodeTypesTool(),
this.buildListCredentialsTool(credentialProvider, listCredentialsUsageHint),
];
}
getRuntimeTools(credentialProvider, projectId) {
return [
...this.getSharedTools(credentialProvider, 'Call this before run_node_tool to know which credential to pass.'),
this.buildRunNodeTool(projectId),
];
}
buildSearchNodesTool() {
return new agents_1.Tool('search_nodes')
.description('Search for n8n nodes by name or service. Use this to find nodes that can be executed. ' +
'Returns tool node IDs, display names, versions, and descriptions. ' +
'After finding a node, call get_node_types to get its parameter schema.')
.input(searchNodesInputSchema)
.handler(async ({ queries }) => {
const { results } = await this.nodeCatalogService.searchNodes(queries, {
nodeFilter: exports.isAgentToolNodeType,
});
return { results };
})
.build();
}
buildGetNodeTypesTool() {
return new agents_1.Tool('get_node_types')
.description('Get detailed parameter schema for specific n8n nodes. Use the node IDs returned ' +
'by search_nodes. Returns parameter definitions needed to configure a node for execution. ' +
'Use the tool node IDs from search_nodes. ' +
'You can optionally filter by resource/operation/mode.')
.input(getNodeTypesInputSchema)
.handler(async ({ nodeIds }) => {
const results = await this.nodeCatalogService.getNodeTypes(nodeIds.map(normalizeNodeRequestForCatalog));
return { results };
})
.build();
}
buildListCredentialsTool(credentialProvider, usageHint) {
return new agents_1.Tool('list_credentials')
.description('List the credentials available to the user. Returns an array of credential names and types. ' +
'Accepts an optional `types` filter to return only credentials matching the given types. ' +
usageHint)
.input(listCredentialsInputSchema)
.handler(async ({ types }) => {
const creds = await credentialProvider.list();
if (!types || types.length === 0)
return { credentials: creds };
const allowed = new Set(types);
return { credentials: creds.filter((c) => allowed.has(c.type)) };
})
.build();
}
buildRunNodeTool(projectId) {
return new agents_1.Tool('run_node_tool')
.description('Execute an n8n node for the current request. ' +
'Use the tool nodeType and nodeTypeVersion from search_nodes. ' +
'Call get_node_types first to understand what nodeParameters the node accepts. ' +
'nodeParameters holds static node config; use n8n expressions like ={{ $json.url }} to map inputData fields. ' +
'credentials maps slot names to { id, name } — copy from the list_credentials results. ' +
'inputData is the runtime payload available as $json inside expressions. ' +
'Parameters are validated against the node schema before execution.')
.input(runNodeInputSchema)
.handler(async ({ nodeType, nodeTypeVersion, nodeParameters, credentials, inputData }) => {
if (!(0, exports.isExecutableNodeType)(nodeType)) {
return {
status: 'error',
message: `Node type "${nodeType}" cannot be executed directly — trigger nodes are not supported here.`,
};
}
if (nodeParameters) {
const { valid, errors } = (0, workflow_sdk_1.validateNodeConfig)(nodeType, nodeTypeVersion, {
parameters: nodeParameters,
}, { isToolNode: true });
if (!valid) {
return {
status: 'error',
message: `Invalid nodeParameters: ${errors.map((e) => e.message).join('; ')}`,
};
}
}
try {
return await this.ephemeralNodeExecutor.executeInline({
nodeType,
nodeTypeVersion,
nodeParameters: (nodeParameters ?? {}),
credentialDetails: credentials,
inputData: [{ json: (inputData ?? {}) }],
projectId,
});
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
this.logger.warn('run_node_tool execution failed', { nodeType, error });
return {
status: 'error',
message: `Node execution failed: ${message}`,
};
}
})
.build();
}
};
exports.AgentsToolsService = AgentsToolsService;
exports.AgentsToolsService = AgentsToolsService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger,
node_catalog_1.NodeCatalogService,
node_execution_1.EphemeralNodeExecutor])
], AgentsToolsService);
function normalizeNodeRequestForCatalog(req) {
if (typeof req === 'string')
return req;
const { version, ...rest } = req;
return version === undefined ? rest : { ...rest, version: String(version) };
}
//# sourceMappingURL=agents-tools.service.js.map