The Universal Tool Calling Protocol (UTCP) is a modern, flexible, and scalable standard for defining and interacting with tools across a wide variety of communication protocols. This library is the official TypeScript implementation, designed to be easy to use, interoperable, and extensible.
In contrast to other protocols, UTCP places a strong emphasis on:
- Scalability: UTCP is designed to handle a large number of tools and providers without compromising performance.
- Interoperability: With support for a wide range of provider types (including HTTP, WebSockets, SSE, and even CLI tools), UTCP can integrate with almost any existing service or infrastructure.
- Type Safety: The protocol is built on simple, well-defined TypeScript interfaces with runtime validation powered by Zod, making it robust and easy for developers to use.
These examples illustrate the core concepts of the UTCP client and server. They are not designed to be a single, runnable example.
Note: For complete, end-to-end runnable examples, please refer to the
examples/
directory in this repository.
Setting up a client is simple. You point it to a providers.json
file, and it handles the rest.
providers.json
This file tells the client where to find one or more UTCP Manuals (providers which return a list of tools).
[
{
"name": "example_server",
"provider_type": "http",
"url": "http://localhost:8080/utcp",
"http_method": "GET"
}
]
client.ts
This script initializes the client and calls a tool from the provider defined above.
import { UtcpClient } from './src/client/utcp-client';
async function main() {
console.log('Initializing UTCP client...');
const client = await UtcpClient.create({
providers_file_path: './providers.json',
});
const tools = await client.toolRepository.getTools();
if (tools.length === 0) {
console.log('No tools found. Make sure the example server is running.');
return;
}
console.log('Registered tools:');
for (const tool of tools) {
console.log(` - ${tool.name}`);
}
// Call the first available tool
const toolToCall = tools[0]!;
const args = {
body: { value: 'hello from the client!' },
};
console.log(`\nCalling tool: '${toolToCall.name}'...`);
try {
const result = await client.call_tool(toolToCall.name, args);
console.log('Tool call result:');
console.log(result);
} catch (error) {
console.error('Error calling tool:', error);
}
}
main().catch(error => {
console.error('An unexpected error occurred:', error);
});
Any type of server or service can be exposed as a UTCP tool. The only requirement is that a UtcpManual
is provided to the client. This manual can be served by the tool itself or, more powerfully, by a third-party registry. This allows for wrapping existing APIs and services that are not natively UTCP-aware.
Here is a minimal example using Express to serve a UTCPManual
for a tool:
server.ts
import express from 'express';
import { HttpProvider } from './src/shared/provider';
import { Tool } from './src/shared/tool';
import { UtcpManual } from './src/shared/utcp-manual';
const app = express();
app.use(express.json());
const PORT = 8080;
const __version__ = '0.1.0';
const BASE_PATH = `http://localhost:${PORT}`;
// Manually define the tool
const testTool: Tool = {
name: 'test_endpoint',
description: 'A simple test endpoint that echoes a value.',
tags: ['test', 'example'],
tool_provider: {
name: 'test_provider',
provider_type: 'http',
url: `${BASE_PATH}/test`,
http_method: 'POST',
} as HttpProvider,
inputs: {
type: 'object',
properties: {
value: { type: 'string', description: 'A string value to be echoed back.' },
},
required: ['value'],
},
outputs: {
type: 'object',
properties: {
received: { type: 'string', description: 'The value that was received by the tool.' },
},
required: ['received'],
},
};
// Manually construct the UTCP manual
const manual: UtcpManual = {
version: __version__,
tools: [testTool],
};
// Endpoint to serve the UTCP manual
app.get('/utcp', (req, res) => {
res.json(manual);
});
// The actual tool endpoint
app.post('/test', (req, res) => {
const { value } = req.body;
if (typeof value !== 'string') {
return res.status(400).json({ error: 'Invalid input: value must be a string.' });
}
return res.json({ received: value });
});
app.listen(PORT, () => {
console.log(`UTCP example server running at :${PORT}`);
});
For a complete, end-to-end demonstration of how to integrate UTCP with a Large Language Model (LLM) like OpenAI, see the example in examples/src/full_llm_example/openai_utcp_example.ts
.
This advanced example showcases:
- Dynamic Tool Discovery: No hardcoded tool names. The client loads all available tools from the
providers.json
config. - Relevant Tool Search: For each user prompt, it uses
utcpClient.search_tools()
to find the most relevant tools for the task. - LLM-Driven Tool Calls: It instructs the OpenAI model to respond with a custom JSON format to call a tool.
- Robust Execution: It parses the LLM's response, executes the tool call via
utcpClient.call_tool()
, and sends the result back to the model for a final, human-readable answer. - Conversation History: It maintains a full conversation history for contextual, multi-turn interactions.
To run the example:
- Navigate to the
examples/
directory. - Install dependencies:
npm install
- Navigate to
src/full_llm_example/
- Rename
example.env
to.env
and add your OpenAI API key. - Run the example from the
examples
directory:npx ts-node src/full_llm_example/openai_utcp_example.ts
UTCP is defined by a set of core data models that describe tools, how to connect to them (providers), and how to secure them (authentication). These models are defined as TypeScript interfaces and Zod schemas for runtime validation.
For a client to use a tool, it must be provided with a UtcpManual
object. This manual contains a list of all the tools available from a provider. Depending on the provider type, this manual might be retrieved from a discovery endpoint (like an HTTP URL) or loaded from a local source (like a file for a CLI tool).
interface UtcpManual {
version: string;
tools: Tool[];
}
version
: The version of the UTCP protocol being used.tools
: A list ofTool
objects.
Each tool is defined by the Tool
model.
interface Tool {
name: string;
description: string;
inputs: Record<string, any>; // Simplified, uses JSON schema
outputs: Record<string, any>; // Simplified, uses JSON schema
tags: string[];
tool_provider: ToolProvider;
}
name
: The name of the tool.description
: A human-readable description of what the tool does.inputs
: A JSON schema defining the input parameters for the tool.outputs
: A JSON schema defining the output of the tool.tags
: A list of tags for categorizing the tool making searching for relevant tools easier.tool_provider
: TheToolProvider
object that describes how to connect to and use the tool.
UTCP supports several authentication methods to secure tool access. The auth
object within a provider's configuration specifies the authentication method to use.
Authentication using a static API key, typically sent in a request header.
{
"auth_type": "api_key",
"api_key": "YOUR_SECRET_API_KEY",
"var_name": "X-API-Key"
}
Authentication using a username and password.
{
"auth_type": "basic",
"username": "your_username",
"password": "your_password"
}
Authentication using the OAuth2 client credentials flow. The UTCP client will automatically fetch a bearer token from the token_url
and use it for subsequent requests.
{
"auth_type": "oauth2",
"token_url": "https://auth.example.com/token",
"client_id": "your_client_id",
"client_secret": "your_client_secret",
"scope": "read write"
}
Providers are at the heart of UTCP's flexibility. They define the communication protocol for a given tool. UTCP supports a wide range of provider types:
http
: RESTful HTTP/HTTPS APIsse
: Server-Sent Eventshttp_stream
: HTTP Chunked Transfer Encodingcli
: Command Line Interfacewebsocket
: WebSocket bidirectional connection (work in progress)grpc
: gRPC (Google Remote Procedure Call) (work in progress)graphql
: GraphQL query language (work in progress)tcp
: Raw TCP socket (work in progress)udp
: User Datagram Protocol (work in progress)webrtc
: Web Real-Time Communication (work in progress)mcp
: Model Context Protocol (for interoperability)text
: Local text file
Each provider type has its own specific configuration options. For example, an HttpProvider
will have a url
and an http_method
.
Below are examples of how to configure each of the supported provider types in a JSON configuration file. Where possible, the tool discovery endpoint should be /utcp
. Each tool provider should offer users their json provider configuration for the tool discovery endpoint.
For connecting to standard RESTful APIs.
{
"name": "my_rest_api",
"provider_type": "http",
"url": "https://api.example.com/utcp",
"http_method": "POST",
"content_type": "application/json",
"auth": {
"auth_type": "oauth2",
"token_url": "https://api.example.com/oauth/token",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}
}
UTCP simplifies integration with existing web services by automatically converting OpenAPI v3 specifications into UTCP tools. Instead of pointing to a UtcpManual
, the url
for an http
provider can point directly to an OpenAPI JSON specification. The OpenApiConverter
handles this conversion automatically, making it seamless to integrate thousands of existing APIs.
{
"name": "open_library_api",
"provider_type": "http",
"url": "https://openlibrary.org/dev/docs/api/openapi.json"
}
When the client registers this provider, it will fetch the OpenAPI spec from the URL, convert all defined endpoints into UTCP Tool
objects, and make them available for searching and calling.
For tools that stream data using SSE. The url
should point to the discovery endpoint.
{
"name": "live_updates_service",
"provider_type": "sse",
"url": "https://api.example.com/utcp",
"event_type": "message"
}
For wrapping local command-line tools.
{
"name": "my_cli_tool",
"provider_type": "cli",
"command_name": "my-command -utcp"
}