DIAL OpenAPI to MCP configuration
The OpenAPI to MCP bridge is configured in three places: its environment variables, the application type registered in DIAL Core, and the properties of each application. This page covers registration, properties, credentials, security, and a Helm example. It is for platform engineers who deploy the bridge and assumes you know Application type schemas. Everything here is checked against bridge 0.2.1 and DIAL Core 0.49.4.
Where each setting lives
| Place | What it sets | Reference |
|---|---|---|
| Environment variables of the bridge | Port, log level, cache, header lists, the DIAL Core URL, and telemetry | Environment variables |
| Application type in DIAL Core | The bridge's MCP and schema endpoints, how properties are delivered, and the per-request key | Register the application type |
| Properties of each application | The API's document, base URL, extra headers, and external service | Application properties |
| External services of an application | The credentials that the bridge adds to API calls | Credentials from external services |
| DIAL Core static settings | The headers Core strips before they reach the bridge | Secure the bridge |
Register the application type
-
Deploy the bridge. See the Helm example. In the steps below,
<BRIDGE_URL>is its URL inside your network, such ashttp://openapi-to-mcp:8080. -
Register the type in DIAL Core. Add it to
applicationTypeSchemasin the DIAL Core configuration:{
"applicationTypeSchemas": [
{
"$schema": "https://dial.epam.com/application_type_schemas/schema#",
"$id": "https://dial.example.com/custom_application_schemas/openapi-to-mcp",
"dial:applicationTypeDisplayName": "OpenAPI to MCP",
"dial:applicationTypeSchemaEndpoint": "<BRIDGE_URL>/v1/configuration-support/application-schema",
"dial:applicationTypeMcp": {
"dial:endpoint": "<BRIDGE_URL>/mcp",
"dial:mcpConfigDelivery": "META",
"dial:forwardPerRequestKey": true
}
}
]
}To register it in DIAL Admin instead, create an Application Runner with an ID such as the
$idabove. In Source type, select only MCP Endpoint. Set Endpoint to<BRIDGE_URL>/mcpand Configuration delivery to Meta, and keep Forward per request key on. Then set the runner's Schema endpoint in its Properties. -
Check the result. The bridge answers
GET <BRIDGE_URL>/readywith{"status": "ready"}, andGET /v1/application_type_schemas/schemason DIAL Core lists the type.
| Property | What it does for the bridge |
|---|---|
$id | The type's ID. Applications refer to it as applicationTypeSchemaId. |
dial:applicationTypeDisplayName | The type's name. It overrides the name in the bridge's schema, which is also OpenAPI to MCP. |
dial:applicationTypeSchemaEndpoint | Core downloads the bridge's schema from it, with a 5-second timeout. Core then sets the registration's own fields over the downloaded schema, so the application properties come from the bridge. |
dial:endpoint | The bridge's MCP endpoint. Only Streamable HTTP is supported. |
dial:mcpConfigDelivery | Must be META, the default. The bridge reads the properties only from params._meta.ai_dial_config. It ignores the X-DIAL-APPLICATION-PROPERTIES header that HEADER uses. |
dial:forwardPerRequestKey | true by default. The bridge needs the key to read external-service credentials. In 0.2.1, the key also reaches the API; see Secure the bridge. |
Application properties
The bridge's schema declares four properties. All are server properties: DIAL Core doesn't return them to clients.
| Property | Title in the schema | Required | Description |
|---|---|---|---|
openapi | OpenAPI Spec | Yes | The API's OpenAPI 3.x document, as a JSON object |
base_url | Base URL | No | The API's base URL. A value without a scheme gets https://. |
extra_headers | Extra Headers | No | Headers added to every API call, as an array of {"name": ..., "value": ...} objects. Each name must pass the header lists. |
external_service | External Service | No | The ID of an external service defined on the same application. The caller's credential for it is added to every API call. |
The bridge also accepts these values from MCP clients that call it directly. For each value, it takes the first source that has one:
| Value | Sources, in order |
|---|---|
| Document | The X-META header; _meta.ai_dial_config.openapi; _meta.openapi |
| Base URL | The X-BASE-URL header; _meta.ai_dial_config.base_url; _meta.base_url; the document's first servers URL; a top-level x-base-url in the document |
| Extra headers | The X-EXTRA-HEADERS header, as a JSON array; _meta.ai_dial_config.extra_headers; _meta.extra_headers |
| External service | _meta.ai_dial_config.external_service |
The base URL fields also accept the spellings baseurl and baseURL. A document in a header or in _meta.openapi can be JSON or YAML text.
Credentials from external services
When an application sets external_service, the bridge gets a credential for every tool call. Credentials aren't cached.
- It reads the application at
GET <DIAL_URL>/v1/<application>. The application ID comes fromX-DIAL-APPLICATION-IDand the key fromAPI-KEY. It checks that the application'sexternal_servicescontain the service. - It asks
POST <DIAL_URL>/v1/ops/external-service/credentialsfor the credential of the scope<application>/external_services/<service>. - It adds the returned header to the API call. Each call to DIAL Core has a 10-second timeout.
A lookup that fails ends the tool call with an error result, and the API isn't called. The result's _meta["dial.epam.com/error"] holds status_code and external_service:
status_code | Cause |
|---|---|
404 | The application has no external service with this ID. |
401 | DIAL Core has no credential for the user yet. _meta["dial.epam.com/auth-challenge"] holds a one-item list: [{"method": "external-service/signin", "scope": "<application>/external_services/<service>"}]. |
500 | Any other failure: DIAL_URL unset, X-DIAL-APPLICATION-ID or API-KEY missing, an error from DIAL Core, or a credential header the block list contains |
When DIAL Core returns a malformed credential, or one in a header the block list contains, external_service is unknown.
A Quick App handles the 401 result: it asks the user to sign in to the service and retries the call once. This needs the client channel that DIAL Chat opens, as for MCP Toolsets.
Warning
In 0.2.1, an OAuth external service fails with
500and "DIAL core returned a prohibited credential header". DIAL Core returns OAuth credentials inAuthorization, which the default block list contains. To use one, setOUTBOUND_HEADER_BLOCKLISTto the default list withoutauthorization. Also setOUTBOUND_HEADER_ALLOWLISTwithoutauthorization, so thatextra_headersstill can't set it. Thedevelopmentbranch drops this check after 0.2.1.
Secure the bridge
- No client authentication. The bridge accepts every request on
/mcp. Let only DIAL Core and other trusted clients reach it, for example with a network policy. - No destination limits. The bridge calls whatever base URL a request names, and never follows redirects. Limit its egress with network policy when your environment needs it.
- Headers that override properties. DIAL Core passes most of a caller's headers on to the bridge. The bridge prefers
X-META,X-BASE-URL, andX-EXTRA-HEADERSto the application's properties. Add all three to Core'sproxy.additionalHopByHopHeadersso that Core strips them. - Set
base_urlon every application. Core writes the application's properties into_meta.ai_dial_configbut keeps other fields the caller put in_meta. When an application has nobase_url, a caller's_meta.ai_dial_config.base_urlor_meta.base_urldecides the destination. When it has noextra_headers, a caller'sextra_headersare added to API calls, within the header lists.
Warning
In 0.2.1, every API call also carries the headers of the MCP request that reached the bridge. Behind DIAL Core, these include the per-request key in
API-KEYand theX-DIAL-APPLICATION-IDheader. FastMCP 3.4.5, which 0.2.1 locks, copies them into each call. The bridge removes onlyX-META,X-BASE-URL, andX-EXTRA-HEADERS, and the header lists don't apply. Use the bridge only with APIs you trust with the per-request key. Alternatively, setdial:forwardPerRequestKeytofalseon a type whose applications don't useexternal_service. Thedevelopmentbranch removesAPI-KEYandX-DIAL-APPLICATION-IDafter 0.2.1.
FastMCP doesn't copy host, content-length, content-type, connection, transfer-encoding, upgrade, te, keep-alive, expect, accept, authorization, proxy-authenticate, proxy-authorization, proxy-connection, or mcp-session-id.
Example: Helm values
The bridge isn't part of the dial chart. Deploy it with the dial-extension chart 3.1.1:
image:
registry: ghcr.io
repository: epam/ai-dial-openapi-to-mcp
tag: 0.2.1
containerPorts:
http: 8080
env:
DIAL_URL: http://dial-core.dial.svc.cluster.local
livenessProbe:
enabled: true
readinessProbe:
enabled: true
httpGet:
path: /ready
port: http
helm install openapi-to-mcp dial/dial-extension --version 3.1.1 -f values.yaml --namespace dial
The chart's probes call /health unless set otherwise, so the example points readiness at /ready. The chart's Service listens on port 80 by default (service.ports.http). Use the Service's URL as <BRIDGE_URL> in the registration. DIAL_URL is needed only by applications that set external_service.
Troubleshooting
| Symptom | What to check |
|---|---|
| Listing tools fails with "Failed to create MCP from OpenAPI spec — check server logs for details" | The bridge's log. Common causes are a Swagger 2.0 or non-3.x document, a document FastMCP can't build tools from, and an extra_headers name that the header lists reject. Check the document with openapi_verify. |
An application lists only openapi_verify and openapi_convert | The bridge received no document. Check that dial:mcpConfigDelivery is META and that the application's openapi isn't empty. |
| Core rejects MCP calls with "Application doesn't support MCP protocol" | The registration has no dial:applicationTypeMcp, or the application doesn't use the type. |
| Core logs "Failed to download application schema" | That Core can reach <BRIDGE_URL>/v1/configuration-support/application-schema within 5 seconds |
| A tool call returns "Tool '...' not found" | The tool names the document produces. The result lists up to five of them. |
| "External service '...' not found in application ... external_services registry" | The external_service value and the IDs of the application's external services. This is the 404 result. |
| "Sign-in required for external service '...'" | The user hasn't signed in to the service yet. This is the 401 result. |
| "... DIAL_URL env var is not set" or "... no Api-Key header found" | DIAL_URL on the bridge, and dial:forwardPerRequestKey on the type |
| "DIAL core returned a prohibited credential header" | The credential's header is in OUTBOUND_HEADER_BLOCKLIST. For OAuth, see the warning in Credentials from external services. |
GET /ready returns 503 | The bridge hasn't finished starting. |