create_custom_tool
Create a new custom tool by its caller-relative name. A
standalone personal credential uses a bare name. In organization scope, use me/name for the
represented member's private space or <team>/name for a bound team. An unprefixed
organization-scoped name denotes the shared organization catalogue, which is currently read-only
over MCP. To change an existing tool, use update_custom_tool.
You define the connector and its authentication type, but not the secret. A tool that needs authentication is created disabled; a person adds its credential and enables it in the dashboard (see secrets stay in the dashboard).
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Caller-relative address: bare with a standalone personal credential, me/name in private organization space, or <team>/name for a bound team. |
description | string | No | What the tool does — shown to the agent that uses it. |
connector | object | Yes | The HTTP connector definition (see below). No secret — set that in the dashboard. |
The connector object:
| Field | Type | Required | Description |
|---|---|---|---|
http_method | string | Yes | GET, POST, PUT, PATCH, or DELETE. |
url_template | string | Yes | URL template. {name} placeholders are filled from the tool's arguments (one path segment each). |
input_schema | object | Yes | JSON Schema (an object) describing the tool's arguments. |
headers | object | No | Header name → value template. Defaults to none. |
body_template | string | No | Request-body template. |
auth | object | No | Authentication scheme (type only). Defaults to { "type": "none" }. |
The auth object is one of: { "type": "none" }, { "type": "bearer" }, { "type": "basic" },
or { "type": "header", "header_name": "X-Api-Key" }. The secret itself is added in the dashboard.
Response
| Field | Type | Description |
|---|---|---|
name | string | Canonical caller-relative address of the created tool. |
id | string | The new tool's identifier. |
enabled | boolean | Whether the tool is active. false when it needs a secret you haven't set yet. |
Example
{
"name": "weather",
"description": "Fetch the current weather for a city",
"connector": {
"http_method": "GET",
"url_template": "https://api.example.com/weather/{city}",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
},
"auth": { "type": "bearer" }
}
}
Result (created disabled — it uses bearer auth and has no secret yet):
{
"name": "weather",
"id": "018f...c2a1",
"enabled": false
}
Errors
Each failure carries a precise code in the response. A domain rejection — a name clash, a limit, or a permission denial — is returned as a tool result with isError: true, so the agent can read the code and self-correct; malformed arguments and server faults are JSON-RPC protocol errors.
| Code | Cause | Delivered as |
|---|---|---|
custom_tool_name_taken | A custom tool with this name already exists in the target space. | tool error (isError) |
invalid_custom_tool_name | The name is invalid. | tool error (isError) |
name_too_long | The name exceeds its maximum length. | tool error (isError) |
description_too_long | The description exceeds its maximum length. | tool error (isError) |
invalid_connector | The connector is invalid (bad method, URL template, header, or input schema). | tool error (isError) |
custom_tool_limit_reached | Your plan's custom-tool limit has been reached. | tool error (isError) |
team_not_found | No team with that name is bound to this credential. | tool error (isError) |
forbidden | The credential lacks create authority, uses me/ outside organization scope, or targets the read-only organization catalogue. | tool error (isError) |
empty_name | The name is empty after removing the team prefix. | tool error (isError) |
personal_cannot_address_team | You used a team/ prefix (or team field) with a personal credential. | tool error (isError) |
no_personal_space | A userless organization credential used me/name. | tool error (isError) |
invalid_arguments | The arguments are missing or don't match the input schema. | protocol error (invalid_params) |
internal_error | An unexpected error occurred. | protocol error (internal_error) |
Notes
- A tool using
auth: { "type": "none" }needs no dashboard secret; its initial enabled state follows the owner's default setting. Any other auth type creates the tool disabled; a person must add its secret and enable it in the dashboard. A credential can call either kind only when the access model makes it reachable. - Creating a custom tool counts as one tool call against your plan's tool-invocation allowance.