# Tools MCP Tools

> The MCP tools the SOAT server exposes for the Tools module, with the arguments each tool takes.

# Tools

MCP tools for the Tools module. See the [Tools module docs](/docs/modules/tools) for permissions and data model.

### `create-tool`

Create a tool

#### Arguments

| Argument | Type | Required | Description |
| -------- | ---- | -------- | ----------- |
| `project_id` | `string` | no | Public ID of the project |
| `name` | `string` | yes | Tool name |
| `type` | `string` | no | Tool type (default http) |
| `description` | `string` | no | What the tool does |
| `parameters` | `object` | no | JSON Schema for tool input |
| `execute` | `object \| null` | no | Execution config for http tools. Supported fields: `url` (required), `method` (default `POST`), `headers`, `body_mode` and `response_mode`. The `url` may contain `{paramName}` placeholders (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool argument value (URL-encoded). Arguments consumed as path parameters are excluded from the query string and request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from base64 and attached as a file part (the hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart boundary itself). `response_mode` is `json` (default: the body parsed as JSON, else returned as text) or `base64`, which returns a 2xx body as `{ content_type, filename, data_base64 }` — the same file shape `multipart` accepts, so a pipeline step can relay a downloaded file. `filename` comes from `Content-Disposition` and is omitted when the target names none; a body over `TOOL_RESPONSE_MAX_BYTES` is `502 TOOL_RESPONSE_TOO_LARGE`. `auth` adds a computed request credential, for targets whose `Authorization` value cannot be expressed as a static header. Supported `auth.type` values: - `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`, `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at signing time. - `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service account assertion and sends it as a bearer token. Requires `credentials` (the service account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per service account and scope set until shortly before they expire. Credential fields accept `{{secret:...}}` references and should use them — a tool is readable by anyone who can `GET /tools`, and the stored reference is what is echoed back, never the resolved value. A credential slot takes a reference, never a literal: a literal `secret_access_key`, `session_token` or `credentials` under `auth`, a literal credential-named `headers` value (`Authorization`, `Cookie`, or a name containing `api-key`/`token`/`secret`/`password`), or a `url` carrying a password or a literal credential-named query parameter (those names, plus `key` and `sig`/`signature`) is refused with `400 LITERAL_CREDENTIAL`, `meta.paths` naming each one. A stored literal reads back as `{"no_echo": true}` (a `url` holding one is masked whole), an object rather than a string so a read-edit-write round trip fails the schema check instead of writing the placeholder in as the credential. A value carrying a `{{secret:...}}` reference is the wiring, not the credential, and stays readable. `headers` values additionally accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a per-user credential can be placed in the real header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an `X-Soat-Context-<key>` header. Valid **only** inside `headers` — a context value is caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an empty credential. See the Tool Context reference. |
| `mcp` | `object \| null` | no | MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and `{{context:<key>}}` references, resolved right before the outbound MCP request; `url` accepts `{{secret:...}}` only. A literal credential in `headers` or `url` is refused, exactly as in `execute`. |
| `actions` | `array<string>` | no | Allowlist of actions. For `builtin` tools: SOAT platform action names (required). For `mcp` tools: an optional allowlist of MCP tool names to scope the server surface — omit or set `null` to expose every tool the MCP server offers. Ignored for other tool types. |
| `denied_actions` | `array<string>` | no | For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after `actions` and taking precedence over it. Use it to scope a read+write MCP server read-only by denying just the write tools. Omit or set `null` to deny nothing. Ignored for other tool types. |
| `context_keys` | `array<string> \| null` | no | Optional allowlist of `tool_context` keys that may be forwarded to this tool as prefixed context headers (`X-Soat-Context-<key>` by default). When `null` or omitted, every key in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a per-user credential in `tool_context` can be confined to the tools that need it; `[]` forwards none. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token in this tool's own headers is substituted regardless of this list — the tool declared that header itself. |
| `preset_parameters` | `object` | no | Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching fields in the input schema are removed from the schema shown to the model, and a pinned value wins over one the model or a direct caller supplies for the same key. On an `mcp` tool a preset reaches each action whose input schema declares it; an action whose schema declares no properties receives every one. Values accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a pin can be the run's own value — the one account this run may act on — rather than one fixed when the tool was created. A resolved value is retyped to the parameter's declared schema type; a key missing from the call's `tool_context` fails the call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder. `{{secret:...}}` is not resolved here. See the Tool Context reference. |
| `pipeline` | `object` | no | Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema for the full structure. |
| `output_mapping` | `object` | no | Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping` field on the Tool schema for details. |
| `guardrail_ids` | `array<string> \| null` | no | Guardrails attached at the tool scope. |

### `list-tools`

List tools

#### Arguments

| Argument | Type | Required | Description |
| -------- | ---- | -------- | ----------- |
| `project_id` | `string` | no | Project public ID to filter by |
| `limit` | `number` | no | Results per page. Above the maximum it is clamped; below 1 or not an integer it is `400 VALIDATION_FAILED`. |
| `cursor` | `string` | no | The previous page's `next_cursor`; omit it for the first page. Send it with the same filters as the request that returned it: a malformed cursor, or one sent with other filters, is `400 INVALID_CURSOR`. |

### `get-tool`

Get a tool

#### Arguments

| Argument | Type | Required | Description |
| -------- | ---- | -------- | ----------- |
| `tool_id` | `string` | yes | — |

### `update-tool`

Update a tool

#### Arguments

| Argument | Type | Required | Description |
| -------- | ---- | -------- | ----------- |
| `tool_id` | `string` | yes | — |
| `name` | `string` | no | — |
| `type` | `string` | no | — |
| `description` | `string \| null` | no | — |
| `parameters` | `object \| null` | no | — |
| `execute` | `object \| null` | no | Execution config for http tools. Supported fields: `url` (required), `method` (default `POST`), `headers`, `body_mode` and `response_mode`. The `url` may contain `{paramName}` placeholders (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool argument value (URL-encoded). Arguments consumed as path parameters are excluded from the query string and request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from base64 and attached as a file part (the hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart boundary itself). `response_mode` is `json` (default: the body parsed as JSON, else returned as text) or `base64`, which returns a 2xx body as `{ content_type, filename, data_base64 }` — the same file shape `multipart` accepts, so a pipeline step can relay a downloaded file. `filename` comes from `Content-Disposition` and is omitted when the target names none; a body over `TOOL_RESPONSE_MAX_BYTES` is `502 TOOL_RESPONSE_TOO_LARGE`. `auth` adds a computed request credential, for targets whose `Authorization` value cannot be expressed as a static header. Supported `auth.type` values: - `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`, `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at signing time. - `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service account assertion and sends it as a bearer token. Requires `credentials` (the service account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per service account and scope set until shortly before they expire. Credential fields accept `{{secret:...}}` references and should use them — a tool is readable by anyone who can `GET /tools`, and the stored reference is what is echoed back, never the resolved value. A credential slot takes a reference, never a literal: a literal `secret_access_key`, `session_token` or `credentials` under `auth`, a literal credential-named `headers` value (`Authorization`, `Cookie`, or a name containing `api-key`/`token`/`secret`/`password`), or a `url` carrying a password or a literal credential-named query parameter (those names, plus `key` and `sig`/`signature`) is refused with `400 LITERAL_CREDENTIAL`, `meta.paths` naming each one. A stored literal reads back as `{"no_echo": true}` (a `url` holding one is masked whole), an object rather than a string so a read-edit-write round trip fails the schema check instead of writing the placeholder in as the credential. A value carrying a `{{secret:...}}` reference is the wiring, not the credential, and stays readable. `headers` values additionally accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a per-user credential can be placed in the real header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an `X-Soat-Context-<key>` header. Valid **only** inside `headers` — a context value is caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an empty credential. See the Tool Context reference. |
| `mcp` | `object \| null` | no | MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and `{{context:<key>}}` references, resolved right before the outbound MCP request; `url` accepts `{{secret:...}}` only. A literal credential in `headers` or `url` is refused, exactly as in `execute`. |
| `actions` | `array<string> \| null` | no | Allowlist of actions. For `builtin` tools: SOAT platform action names. For `mcp` tools: an optional allowlist of MCP tool names to scope the server surface (`null` exposes every tool). Ignored for other tool types. |
| `denied_actions` | `array<string> \| null` | no | For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after `actions` and taking precedence over it. Use it to scope a read+write MCP server read-only by denying just the write tools. `null` denies nothing. Ignored for other tool types. |
| `context_keys` | `array<string> \| null` | no | Optional allowlist of `tool_context` keys that may be forwarded to this tool as prefixed context headers (`X-Soat-Context-<key>` by default). When `null` or omitted, every key in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a per-user credential in `tool_context` can be confined to the tools that need it; `[]` forwards none. The server-pinned identity keys (`session_id`, `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token in this tool's own headers is substituted regardless of this list — the tool declared that header itself. |
| `preset_parameters` | `object \| null` | no | Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching fields in the input schema are removed from the schema shown to the model, and a pinned value wins over one the model or a direct caller supplies for the same key. On an `mcp` tool a preset reaches each action whose input schema declares it; an action whose schema declares no properties receives every one. Values accept `{{context:<key>}}` references, resolved per call from the caller's `tool_context`, so a pin can be the run's own value — the one account this run may act on — rather than one fixed when the tool was created. A resolved value is retyped to the parameter's declared schema type; a key missing from the call's `tool_context` fails the call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder. `{{secret:...}}` is not resolved here. See the Tool Context reference. |
| `pipeline` | `object \| null` | no | Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema for the full structure. |
| `output_mapping` | `object \| null` | no | Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping` field on the Tool schema for details. |
| `guardrail_ids` | `array<string> \| null` | no | Guardrails attached at the tool scope. |

### `delete-tool`

Delete a tool

#### Arguments

| Argument | Type | Required | Description |
| -------- | ---- | -------- | ----------- |
| `tool_id` | `string` | yes | — |
| `force` | `boolean` | no | Delete the tool even when another project has accepted a share of it, revoking those shares, or an ingestion rule converts with it. A decider backend still refuses. |

### `call-tool`

Call a tool

#### Arguments

| Argument | Type | Required | Description |
| -------- | ---- | -------- | ----------- |
| `tool_id` | `string` | yes | — |
| `action` | `string` | no | For `builtin` tools: the action name (must be in the tool's `actions` list). For `mcp` tools: the MCP tool name to invoke (must be in the tool's `actions` allowlist when one is set, and must not be in its `denied_actions` denylist). Ignored for `http` tools. |
| `input` | `object` | no | Input parameters for the tool call. These are merged with the tool's `preset_parameters` before execution; a preset value wins over the same key sent here. |
| `tool_context` | `object` | no | Key/value context for this call, forwarded to the tool as `X-Soat-Context-<key>` request headers and resolving any `{{context:<key>}}` token in the tool's `execute.headers`, `mcp.headers` or `preset_parameters`. Narrowed by the tool's `context_keys` allowlist when it sets one. This route has no session, so it stamps no server-derived identity: the reserved keys `session_id`, `actor_id` and `actor_external_id` are dropped from this bag (in any casing) rather than forwarded, so a downstream tool can still trust that a context header naming one is server-derived. Every other key becomes an HTTP header name and must match that grammar, or the call fails with `INVALID_TOOL_CONTEXT_KEY`. |
