utcp 0.2.0
utcp: ^0.2.0 copied to clipboard
Universal Tool Calling Protocol (UTCP) client for Dart. Discover tools from UTCP manuals and call them directly over their native HTTP interfaces.
utcp #
A Dart client for the Universal Tool Calling Protocol (UTCP).
UTCP lets an agent call tools directly through their native interfaces. A provider publishes a manual: a JSON or YAML document that lists its tools, their input/output JSON Schemas, and how to call each one. The client fetches the manual, then calls the tool's own endpoint. There is no wrapper server in between.
Usage #
import 'package:utcp/utcp.dart';
Future<void> main() async {
final client = await UtcpClient.create(
config: UtcpClientConfig(
variables: {'weather_API_KEY': 'my-key'},
manualCallTemplates: [
HttpCallTemplate(name: 'weather', url: 'https://api.example.com/utcp'),
],
),
);
final tools = await client.searchTools('current weather');
final result = await client.callTool(tools.first.name, {'city': 'Seoul'});
print(result);
await client.close();
}
Tool names are prefixed with their manual name (weather.get_weather). Tool.inputs holds the
JSON Schema you hand to an LLM as the tool definition.
A runnable end-to-end example against a local server is in example/utcp_example.dart.
Variables #
Call templates can reference ${VAR} or $VAR. Values are resolved from
UtcpClientConfig.variables, then any VariableLoaders, then the process environment.
Inside a manual, variables are namespaced by the manual name, with underscores in the name doubled:
${API_KEY} in manual my_api resolves my__api_API_KEY. This matches the Python and TypeScript SDKs.
HTTP call templates #
Tool arguments are mapped onto the request in this order:
header_fieldsbecome request headers.body_field(defaultbody) becomes the request body.{name}placeholders inurlbecome path segments.- Any remaining arguments become query parameters.
Supported auth types are api_key (header, query, or cookie), basic, oauth2 (client
credentials, with token caching), and oauth2_user (a pre-provisioned bearer token).
Streaming transports #
sse and streamable_http map arguments and auth the same way. Use callToolStreaming to get
results as they arrive; callTool collects them. Cancelling the stream subscription closes the
HTTP response.
sseyields thedataof each Server-Sent Event, JSON-decoded when it parses.event_typekeeps only events of that type (an event withoutevent:ismessage). A dropped stream is reopened withLast-Event-ID, waitingretry_timeoutor the server'sretry:(capped at 60 s), up to 5 times per call; calls that send a body are never re-issued. HTTP errors and responses that are nottext/event-streamfail at once.streamable_httpdecodes the response by Content-Type:application/x-ndjsonyields one value per line,application/jsonone value for the whole body, anything elseUint8Listchunks of at mostchunk_sizebytes.timeoutbounds the whole call, including reading the stream.
Post-processing #
UtcpClientConfig.postProcessing (post_processing in JSON) runs in order on every tool result
and every streamed chunk:
"post_processing": [
{"tool_post_processor_type": "filter_dict", "exclude_keys": ["internal_id"]},
{"tool_post_processor_type": "limit_strings", "limit": 2000, "only_include_manuals": ["docs"]}
]
filter_dict removes keys (exclude_keys) or keeps only some (only_include_keys) at any depth.
limit_strings truncates every string to limit code points (default 10000). Both take
exclude_tools, only_include_tools, exclude_manuals and only_include_manuals. Custom
processors register with ToolPostProcessor.registry.register.
OpenAPI #
An http manual URL may serve an OpenAPI 2.0 (Swagger) or 3.x document, in JSON or YAML, instead of
a UTCP manual. Each operation with an operationId becomes a tool of that name: header parameters
become header_fields, the request body becomes the body argument, and path parameters stay
{name} placeholders. Local $refs are inlined. Relative server and OAuth2 token URLs resolve against
the URL the document was fetched from.
Security schemes become auth with variable placeholders such as ${API_KEY_0}, resolved as
petstore_API_KEY_0 in manual petstore (apiKey, HTTP basic, HTTP bearer, and OAuth2 flows
with a token URL). Set auth_tools on the manual call
template to supply the credentials instead; it replaces generated auth of the same type and, for API
keys, the same name and location. The manual's static headers are sent only by generated tools on the spec's own origin, since they often carry credentials.
To convert a document you loaded yourself, use OpenApiConverter(spec, specUrl: ...).convert().
Text call templates #
A text call template carries a UTCP manual or OpenAPI document inline in content, as JSON or YAML,
so it works on every platform. base_url replaces an OpenAPI document's servers. The tools converted
from OpenAPI use http, so list it in allowed_communication_protocols. Inline OpenAPI documents are
treated as remote: their tools may target loopback only through base_url. Calling a tool whose call
template is text returns its content.
Security #
- Remote URLs must use HTTPS. Plain HTTP is allowed only to
localhost,127.0.0.1, or::1. - A manual or OpenAPI document fetched from a remote origin may not point tools or token URLs at loopback addresses. Tool arguments cannot move a request onto loopback.
- Every redirect hop is re-validated, and auth headers are dropped when a redirect changes origin.
- A manual's tools may only use the manual's own transport unless
allowed_communication_protocolssays otherwise.
Extending #
A transport is a CommunicationProtocol that lists the call template types it serves. Pass it to the
client, which registers its parsers:
final client = await UtcpClient.create(protocols: [MyProtocol()]);
Custom auth types register with Auth.registry.register.
Status #
This package implements the core client, result post-processors, OpenAPI conversion, and the
http, sse, streamable_http and text transports. The mcp, cli, file, tcp, udp and
websocket transports ship as separate packages in this repository.
License #
MPL-2.0, the same license as the reference UTCP SDKs this package is ported from.