Skip to content

Creating a Tool

A tool is a reusable workflow that receives inputs, runs request or transform steps in sequence, and returns a result. Saved tools can run on demand, on a schedule, through a webhook, from the SDK, or as MCP tools.

Agent

Describe the outcome you need. The agent selects systems, proposes the steps, tests the workflow, and lets you review it before saving.

Tool editor

Build or refine the workflow in the Tools area. Configure inputs, request and transform steps, test data, and the final output.

SDK or REST API

Execute a saved tool from an application or script. Tool creation and editing are usually done with the agent or tool editor.

For example, ask the agent:

Create a tool that finds active Stripe customers and returns their IDs and email addresses.

For a multi-step workflow, describe the sequence and desired result:

Find HubSpot contacts whose email domain is company.com, update their lifecycle stage to customer,
and return the contacts that were changed.

To execute an existing tool with the JavaScript SDK:

Terminal window
npm install @superglue/client
import { configure, getTool, listTools, runTool } from "@superglue/client";
configure({
baseUrl: "https://api.superglue.ai/v1",
apiKey: "YOUR_API_KEY",
});
const tools = await listTools({ page: 1, limit: 50 });
const tool = await getTool("customer-sync");
const run = await runTool(tool.id, {
inputs: {
customerId: "cus_123",
},
});
console.log(tools.data);
console.log(run.status, run.data);

Contract

The instruction explains the tool’s purpose. The input schema describes accepted inputs, and the output schema defines the required final result.

Steps

Request steps call HTTP APIs, databases, or file servers. Transform steps reshape data without making an external request.

Output

An output transform selects and reshapes the accumulated step data before the runtime validates it against the output schema.

  1. Initialize the execution context

    Tool inputs become top-level properties of sourceData. Uploaded files are available through the runtime file context.

  2. Run steps in order

    Each step receives the original inputs plus results from earlier steps. Request steps resolve stored credentials only when the call executes.

  3. Store each step result under its ID

    A normal step produces one result envelope. A step whose data selector returns an array produces an array of envelopes.

  4. Build and validate the final result

    The output transform receives the complete sourceData object. If an output schema is configured, a mismatch fails the run.

A request step identifies the system and contains the protocol-specific request configuration:

const getCustomerStep = {
id: "getCustomer",
instruction: "Fetch one Stripe customer",
config: {
type: "request",
systemId: "stripe",
method: "GET",
url: "https://api.stripe.com/v1/customers/<<customerId>>",
headers: {
Authorization: "Bearer <<stripe_apiKey>>",
},
},
};

type: "request" is optional for stored request steps and is added during normalization. systemId connects the step to the system used for credential and access resolution.

For HTTP requests, headers and queryParams are objects, while body is a string. Encode JSON request bodies with JSON.stringify:

const updateCustomerStep = {
id: "updateCustomer",
config: {
type: "request",
systemId: "stripe",
method: "POST",
url: "https://api.stripe.com/v1/customers/<<customerId>>",
headers: {
Authorization: "Bearer <<stripe_apiKey>>",
"Content-Type": "application/json",
},
body: JSON.stringify({
metadata: {
requestedBy: "<<requestedBy>>",
},
}),
},
};

Templates support direct top-level variables and arrow-function expressions:

Value Syntax
Tool input <<customerId>>
System secret <<stripe_apiKey>>
Nested step result <<(sourceData) => sourceData.getCustomer.data.id>>
Current loop item <<(sourceData) => sourceData.currentItem.id>>
Computed value <<(sourceData) => sourceData.items.length>>

Direct lookup works only for top-level keys. Use (sourceData) => ... for nested properties and computed values.

A data selector runs before its step. When it returns an array, the runtime executes the request once for each item and exposes that item as sourceData.currentItem:

const updateCustomersStep = {
id: "updateCustomers",
dataSelector: "(sourceData) => sourceData.customerIds",
failureBehavior: "continue",
config: {
type: "request",
systemId: "crm",
method: "PATCH",
url: "https://crm.example.com/customers/<<(sourceData) => sourceData.currentItem>>",
body: JSON.stringify({ status: "active" }),
},
};

If a selector returns a single non-array value, the step runs once with that value as currentItem. Prefer a provider’s batch endpoint over a large client-side loop when one is available.

The next step and the output transform see accumulated results in this shape:

{
"getCustomer": {
"currentItem": {},
"data": {
"id": "cus_123",
"email": "ada@example.com"
},
"success": true
},
"updateCustomers": [
{
"currentItem": "cus_123",
"data": {
"status": "active"
},
"success": true
}
]
}

Use .data for a non-loop step. Loop results are arrays, so filter or map their individual envelopes.

Transform steps run JavaScript without calling a system:

const formatCustomerStep = {
id: "formatCustomer",
config: {
type: "transform",
transformCode: `(sourceData) => ({
id: sourceData.getCustomer.data.id,
email: sourceData.getCustomer.data.email.toLowerCase(),
})`,
},
};

Their results use the same envelope as request steps, so a later step reads sourceData.formatCustomer.data.

Transform steps contain type: "transform" and transformCode; they do not contain request fields such as systemId, url, method, headers, body, or pagination.

The output transform receives the complete execution context and returns the tool’s public result:

const outputTransform = `(sourceData) => {
const updates = sourceData.updateCustomers || [];
return {
updated: updates.filter((result) => result.success).map((result) => result.currentItem),
failed: updates.filter((result) => !result.success).map((result) => ({
id: result.currentItem,
error: result.error,
})),
};
}`;

Without an output transform or output schema, the runtime returns the last successful step’s data.

Request steps select their runtime strategy from the resolved URL scheme. These steps use the same result envelopes as HTTP requests, but their JSON-encoded bodies describe queries, commands, or file operations.

PostgreSQL

postgres:// and postgresql:// URLs accept a body with query and optional params. Use $1, $2, and other parameter placeholders.

SQL Server and ODBC

mssql://, sqlserver://, and odbc:// URLs accept a body with query and optional params. The configured driver determines connection behavior.

Redis

redis:// and rediss:// URLs accept one { command, args } object or an array of commands, which runs as a pipeline.

MongoDB

mongodb:// and mongodb+srv:// URLs accept one operation or an array of operations. Bodies support MongoDB Extended JSON values.

FTP and SFTP

ftp://, ftps://, and sftp:// URLs support list, get, put, delete, rename, mkdir, rmdir, exists, and stat operations.

SMB

smb:// URLs use the same file-operation model. The URL must include a share name; credentials may also include a Windows domain.

PostgreSQL example
const queryUsersStep = {
id: "queryUsers",
config: {
type: "request",
systemId: "warehouse",
url: "postgres://<<warehouse_username>>:<<warehouse_password>>@<<warehouse_hostname>>:5432/<<warehouse_database>>",
body: JSON.stringify({
query: "SELECT id, email FROM users WHERE status = $1 AND created_at > $2",
params: ["active", "<<startDate>>"],
}),
},
};

Always parameterize user-controlled values instead of interpolating them into SQL.

Redis example
const getUserProfileStep = {
id: "getUserProfile",
config: {
type: "request",
systemId: "redisCache",
url: "rediss://<<redisCache_username>>:<<redisCache_password>>@<<redisCache_hostname>>:6379/0",
body: JSON.stringify({
command: "HGETALL",
args: ["user:<<userId>>"],
}),
},
};

Pass an array of command objects to execute a pipeline in one round-trip.

MongoDB example
const findUsersStep = {
id: "findUsers",
config: {
type: "request",
systemId: "mongo",
url: "mongodb://<<mongo_username>>:<<mongo_password>>@<<mongo_hostname>>:27017/<<mongo_database>>?authSource=admin",
body: JSON.stringify({
collection: "users",
operation: "find",
filter: { status: "active" },
options: { limit: 50, sort: { createdAt: -1 } },
}),
},
};

Supported operations include find, findOne, aggregate, countDocuments, estimatedDocumentCount, distinct, the insert/update/replace/delete variants, the find-and-modify variants, bulkWrite, and listCollections.

SFTP example
const listReportsStep = {
id: "listReports",
config: {
type: "request",
systemId: "fileServer",
url: "sftp://<<fileServer_username>>:<<fileServer_password>>@<<fileServer_hostname>>:22/data",
body: JSON.stringify({
operation: "list",
path: "/reports",
}),
},
};

get operations parse supported file types into step data and retain the produced file for later steps. Set outputFile: true on a producing step when the file should become a downloadable tool output.

Retry eligible HTTP failures

HTTP requests retry rate limits, server errors, and selected transport failures according to the run’s retry settings. Protocol-backed database and file steps do not retry by default.

Stop or continue

A failed step stops the run by default. Set failureBehavior: "continue" when later steps can safely handle missing data or per-item errors.

Validate final output

outputSchema validates the value after the output transform. A mismatch fails the run with an output-validation error.

inputSchema describes the arguments a tool accepts and is used by interfaces such as MCP to validate calls. Raw REST callers should still send a conforming input object. outputSchema is enforced by the runtime after the output transform.

const toolContract = {
inputSchema: {
type: "object",
properties: {
customerId: { type: "string" },
requestedBy: { type: "string" },
},
required: ["customerId"],
},
outputSchema: {
type: "object",
properties: {
id: { type: "string" },
email: { type: "string" },
},
required: ["id", "email"],
},
};