Browse Integrate
Integrate · Agent Tool Integrations

Custom Connection Tools

Create narrow agent tools on supported BotDojo connections using the provider's API without exposing credentials or a general request client to the agent.

7 min read

Custom connection tools let a project administrator add purpose-built actions to an existing connection. The administrator defines each tool in TypeScript, while the agent sees only its published name, description, and input schema.

Salesforce, HubSpot, Oracle Fusion, and Zendesk Chat support custom connection tools. Oracle Fusion custom tools are read-only and accept only approved Fusion Financials, Supply Chain Management, and CRM REST resource paths. Other integrations may add the same capability as their provider-specific request policies become available.

How Custom Connection Tools Work

A custom connection tool has two separate interfaces:

  • Administrator interface: TypeScript code can call the connected provider through connection.http.request(...).
  • Agent interface: The agent can invoke only the published tool and provide values allowed by its input schema.

BotDojo supplies the provider base URL and current connection authentication. The program cannot choose another host or read the credentials, and BotDojo does not expose the request client itself as a general agent tool. Each provider also controls the allowed HTTP methods, paths, headers, request sizes, response sizes, and execution limits.

Custom tool programs run in BotDojo's restricted-network code sandbox. They can reach the selected integration only through the bound request client.

File downloads and uploads

The public surface is BotDojo's constrained HTTP client, not a general-purpose request library. connection.http.request({ method, path, params, headers, data, responseType }) returns { ok, status, headers, data }; JSON is the default. Every request goes to the connected provider's origin with the connection's credentials. The required path is relative to that origin; absolute URLs are not accepted, and BotDojo rejects unsupported options. Non-2xx responses fail with a bounded provider error.

For providers that support file transfer, set responseType: "file" on a GET. Its response.data is an opaque, backend-held file object with readable filename, mimeType, and sizeBytes metadata and reserved transport data. It is not a Buffer or ArrayBuffer: authored code cannot read or modify the file bytes. To upload the original file, pass the entire, unchanged object as a later request's data, using any method other than GET and any path the provider allows. BotDojo keeps file bytes in backend memory only; they never enter the custom tool sandbox or model context and are not durably stored. The server derives the upload's MIME type from the file metadata; an authored Content-Type header must match it. Return only the metadata the agent needs, never the opaque object.

This Zendesk Chat example copies a ticket attachment into a new upload token. Pass the path and query of the attachment's content_url; BotDojo always sends the request to the connected Zendesk subdomain.

const tool = defineConnectionTool({
	name: "copy_zendesk_attachment",
	description: "Copy a Zendesk ticket attachment into a new upload token.",
	inputSchema: {
		type: "object",
		properties: {
			contentUrl: { type: "string", description: "The attachment content_url." },
		},
		required: ["contentUrl"],
		additionalProperties: false,
	},
	handler: async (input, { connection }) => {
		const [, path, query = ""] =
			/^https:\/\/[^/]+(\/[^?#]*)(?:\?([^#]*))?/.exec(input.contentUrl) || [];
		if (!path) throw new Error("A Zendesk attachment content_url is required");
		// Split each pair on its first "=" so signed values containing "=" survive.
		const decode = (value: string) => decodeURIComponent(value.replace(/\+/g, " "));
		const params: Record<string, string> = {};
		for (const pair of query.split("&")) {
			if (!pair) continue;
			const separator = pair.indexOf("=");
			if (separator < 0) params[decode(pair)] = "";
			else params[decode(pair.slice(0, separator))] = decode(pair.slice(separator + 1));
		}

		const downloaded = await connection.http.request({
			method: "GET",
			path,
			params,
			responseType: "file",
		});

		const uploaded = await connection.http.request({
			method: "POST",
			path: "/api/v2/uploads.json",
			params: { filename: downloaded.data.filename },
			data: downloaded.data,
		});

		return {
			uploadToken: uploaded.data.upload.token,
			filename: downloaded.data.filename,
			sizeBytes: downloaded.data.sizeBytes,
		};
	},
});

The opaque file capability is scoped to the same invocation and connection, expires when the invocation completes, aborts, or times out, and is single-use. An upload attempt consumes it even when the attempt fails or its result is uncertain; do not retry it. A new invocation must download the file again.

Custom tools never see or control redirects. For file downloads, a provider may follow redirects to that vendor's own storage hosts; Zendesk Chat follows up to five redirects to Zendesk attachment hosts and sends credentials only to the connected subdomain. Zendesk Chat is currently the only provider that supports file transfer; Salesforce, HubSpot, and Oracle Fusion custom tools remain JSON-only.

Each file is limited to 50 MiB, and host-retained plus in-flight file data shares a 50 MiB budget. Binary downloads and uploads together are limited to 100 MiB per invocation. Each invocation is limited to 60 seconds and 10 requests. JSON responses are limited to 2 MiB cumulatively and 1 MiB per response or final result; JSON request bodies are limited to 256 KiB.

Before You Start

You need:

  • project administrator access;
  • a supported integration connection in the project;
  • the provider's API documentation;
  • permission in the connected system to perform the intended operation; and
  • a narrow definition of the values the agent may choose.

Start write actions against a provider sandbox or test tenant when one is available.

Create A Tool

  1. Open the project and select Connections.
  2. Open a connection that supports custom tools.
  3. In Tools, select Add Custom Tool.
The Salesforce connection editor with the Add Custom Tool button
Screenshot previewThe Salesforce connection editor with the Add Custom Tool button
The Salesforce connection editor with the Add Custom Tool button
  1. Edit the starter source in the TypeScript editor.
  2. Review the Tool definition preview. This is the interface the agent will receive after publication.
The custom tool editor showing Salesforce request code and the agent-facing tool definition
Screenshot previewThe custom tool editor showing Salesforce request code and the agent-facing tool definition
The custom tool editor showing Salesforce request code and the agent-facing tool definition
  1. Select Validate to check the source and schema.
  2. Select Save draft to save work without changing the published tool.
  3. Select Publish when the exact draft is ready for agents.
  4. Enable the tool and choose Deny, Ask, or Allow under Tool Configuration, then save the connection.

Only a published, enabled tool is available to agents. A tool's first publication defaults to Ask. Publishing a later revision preserves its current permission.

The connection's tool settings control in-app use. An external MCP client is controlled by its resource-token policy.

Define The Agent Interface

Each source file declares exactly one tool. The path below illustrates the request shape; use the provider guide for a valid API path.

const tool = defineConnectionTool({
	name: "one_specific_action",
	description: "Explain exactly when the agent should use this action.",
	inputSchema: {
		type: "object",
		properties: {
			recordId: {
				type: "string",
				description: "The confirmed record ID.",
			},
		},
		required: ["recordId"],
		additionalProperties: false,
	},
	handler: async (input, { connection }) => {
		const response = await connection.http.request({
			method: "GET",
			path: "/provider/relative/path/" + encodeURIComponent(input.recordId),
		});

		return { record: response.data };
	},
});

The top-level name, description, and inputSchema must be literal values so BotDojo can validate the agent interface without running the program. Tool names may contain 1–64 letters, numbers, underscores, and hyphens.

Use a closed object schema with additionalProperties: false. Add constraints such as pattern, enum, minimum, and maxLength when they reduce the chance of an invalid or overly broad operation.

Build A Narrow Action

The safest tool keeps policy decisions in administrator-authored code:

  • Let the agent provide only values that must vary for the task.
  • Keep the provider object, method, fields, and fixed state changes in code.
  • Return only the data the agent needs for its next decision.
  • Give write tools explicit names and descriptions, such as add_case_comment, instead of generic names such as api_request.
  • Use Ask while testing and for actions that need human approval.

Do not accept a URL, HTTP method, arbitrary request body, object API name, or field API name from the agent unless that variability is a deliberate part of the action's policy.

Drafts, Publishing, And Changes

  • Validate checks the current editor contents without saving or publishing.
  • Save draft creates a new saved revision. Agents continue using the last published revision.
  • Publish makes the exact saved draft current and refreshes the connection's discoverable tools.
  • Renaming a published tool moves its existing permission to the new name.
  • Archive removes the custom tool and its saved permission from the connection.

If another administrator changes the same connection while you are editing, reload it before saving or publishing. This prevents one editor from overwriting a newer revision.

Troubleshooting

The connection does not show Add Custom Tool

The integration provider has not enabled code-authored tools. Salesforce, HubSpot, and Oracle Fusion currently support them. Oracle Fusion allows only read-only requests to approved Fusion REST resource paths.

The tool is not available to an agent

Confirm that the tool is published, enabled, present in the connection's current tool snapshot, and permitted for the invoking user or agent. A saved draft alone is not discoverable.

Validation rejects the source

Keep one top-level const tool = defineConnectionTool({...}) declaration. Use literal tool metadata and a closed object input schema. Remove imports and ambient runtime APIs such as fetch, process, and require.

The provider rejects the request

Check the provider documentation for the HTTP method, relative path, query, and JSON body. Also verify that the connected account can perform the action. Provider permissions continue to apply even when BotDojo allows the tool.

The published tool still behaves like an older version

Open the tool and confirm its status is Published, with no Unpublished changes. Refresh the connection if publication reports that tool discovery did not refresh.

Provider Guides