Service Descriptors¶
How external HTTP services are declared and resolved at runtime.
What is a service descriptor?¶
A service descriptor is a YAML file that tells the runtime how to reach an
external HTTP API. It lives under services/official/ and is referenced by
bindings that use the openapi protocol.
File format¶
id: text_openai_chat
type: openapi
base_url: https://api.openai.com/v1
auth:
type: bearer
token: ${OPENAI_API_KEY}
headers:
Content-Type: application/json
Required fields¶
| Field | Type | Description |
|---|---|---|
id |
string | Unique service identifier, referenced by bindings |
type |
string | Protocol type: openapi, mcp, openrpc |
base_url |
string | Base URL for the API (no trailing slash) |
Optional fields¶
| Field | Type | Description |
|---|---|---|
auth |
object | Authentication configuration |
auth.type |
string | bearer, api_key, basic |
auth.token |
string | Token value or env var placeholder |
auth.header |
string | Custom header name (for api_key type) |
headers |
object | Additional HTTP headers |
timeout |
integer | Request timeout in seconds |
Environment variable placeholders¶
Use ${ENV_VAR} syntax for secrets:
auth:
type: bearer
token: ${OPENAI_API_KEY}
The runtime resolves ${OPENAI_API_KEY} from the process environment at
invocation time. If the variable is not set, the request will fail with a
clear error referencing the missing variable.
How bindings reference services¶
A binding with protocol: openapi points to a service descriptor by its id:
# bindings/official/text.content.summarize/openapi_text_summarize_openai_chat.yaml
id: openapi_text_summarize_openai_chat
capability: text.content.summarize
service: text_openai_chat # ← matches service descriptor id
protocol: openapi
operation: chat/completions # ← HTTP path appended to base_url
At runtime, the invoker constructs:
POST https://api.openai.com/v1/chat/completions
SSRF protection¶
The openapi_invoker enforces URL safety:
- Only
httpandhttpsschemes are allowed. - Requests to private/link-local IPs and cloud metadata endpoints
(
169.254.169.254) are blocked. - The final resolved URL is validated, not just the configured one.
See docs/SECURITY.md for details.
Python baseline services (no descriptor needed)¶
Bindings with protocol: pythoncall don't need a service descriptor. The
service field is the Python module name under official_services/:
service: text_baseline # → official_services/text_baseline.py
protocol: pythoncall
operation: summarize_text # → text_baseline.summarize_text()
Existing service descriptors¶
| File | ID | API |
|---|---|---|
services/official/text_openai_chat.yaml |
text_openai_chat |
OpenAI Chat Completions |
services/official/text_mcp_inprocess.yaml |
text_mcp_inprocess |
In-process MCP (text) |
services/official/data_mcp_inprocess.yaml |
data_mcp_inprocess |
In-process MCP (data) |
services/official/web_mcp_inprocess.yaml |
web_mcp_inprocess |
In-process MCP (web) |
Adding a new service descriptor¶
- Create
services/official/<id>.yamlwith the fields above. - Set
${ENV_VAR}placeholders for any secrets. - Create a binding YAML that references the service
id. - Document the required environment variable in
.env.example. - Test:
python test_capabilities_batch.py(with the env var set).