Custom Tool OpenAPI Integration#
Overview#
Dify's custom tool system (ApiTool) allows users to register arbitrary HTTP APIs by supplying an OpenAPI (or Swagger) schema. At parse time, the schema is converted into one or more ApiToolBundle objects — one per operation — each carrying a frozen server_url, HTTP method, operation parameters, and the raw OpenAPI operation dict. These bundles drive every subsequent invocation.
Schema Parsing and Server URL Extraction#
Entry point: ApiBasedToolSchemaParser.parse_openapi_to_tool_bundle()
The parser accepts an OpenAPI dict (JSON or YAML), Swagger 2.x, or an OpenAI plugin JSON manifest. For Swagger, parse_swagger_to_openapi() upgrades it to OpenAPI 3.0 before the main parse path runs . Auto-detection order is OpenAPI → Swagger → OpenAI plugin .
Server URL selection :
- The parser takes
servers[0]["url"]as the default. - If an HTTP request context exists and the
X-Request-Envheader is set, it scans theserversarray for the first entry whose"env"field matches and uses that URL instead. - No other server selection logic exists.
Bundle construction: For each path+method combination, the parser concatenates the resolved server_url with the path string and stores the result as ApiToolBundle.server_url . This string is baked in at parse time — it is not re-evaluated on each call.
Invocation Flow#
At invocation, ApiTool._invoke() calls:
assembling_request()— builds auth headers and validates required parameters.do_http_request()— resolvesin: pathtokens via string replace (e.g.{id}→ value), mergesin: query/in: header/in: cookieparameters, serialises the request body, then dispatches viassrf_proxy.
The URL passed to do_http_request is always self.api_bundle.server_url — the value frozen during parsing .
Limitations on Dynamic Parameterization#
| Limitation | Detail |
|---|---|
| No runtime server URL override | The server_url on ApiToolBundle is a plain str field set once at parse time . There is no mechanism to substitute it per-invocation. |
OpenAPI variables not supported | The OpenAPI 3.x servers[].variables templating (e.g. https://{tenant}.example.com) is silently ignored; the raw URL string is used verbatim . |
| Only one server effectively used | The X-Request-Env fallback notwithstanding, all possible server URLs must be pre-defined in the schema — nothing can be injected at call time. |
| Path parameters only | Only path-level {param} tokens (OpenAPI in: path) are interpolated at runtime . The host portion of the URL is never touched. |
Workarounds#
- Multiple
serversentries withenvtags: Pre-define all base URLs in the schema; use theX-Request-Envheader to select among them . - Reverse proxy with stable address: Place a proxy in front of your backend so the custom tool always hits a fixed URL.
- Code node in a workflow: For fully dynamic base URLs, use a Code node to issue the HTTP request directly instead of a custom tool.
Key Files#
| File | Role |
|---|---|
api/core/tools/utils/parser.py | Schema parsing; server URL extraction; bundle construction |
api/core/tools/entities/tool_bundle.py | ApiToolBundle data model |
api/core/tools/custom_tool/tool.py | ApiTool invocation, auth assembly, HTTP dispatch |