utcp

package module
v1.10.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2026 License: MPL-2.0 Imports: 40 Imported by: 10

README

go-utcp

MCP vs. UTCP

Go Report Card PkgGoDev

Introduction

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. It is designed to be easy to use, interoperable, and extensible, making it a powerful choice for building and consuming tool-based services.

In contrast to other protocols like MCP, 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, gRPC, and even CLI tools), UTCP can integrate with almost any existing service or infrastructure.
  • Ease of Use: The protocol is built on simple.

MCP vs. UTCP

Features
  • Built-in transports for HTTP, CLI, Server-Sent Events, streaming HTTP, GraphQL, MCP and UDP.
  • Variable substitution via environment variables or .env files using UtcpDotEnv.
  • In-memory repository for storing providers and tools discovered at runtime.
  • Utilities such as OpenApiConverter to convert OpenAPI definitions into UTCP manuals.
  • Example programs demonstrating the client in the examples directory.
Examples

Each subdirectory under examples/ is a standalone Go module demonstrating a client or transport. For an overview of available examples and usage instructions, see examples/README.md. When building or running an example from this repository, disable the workspace to ensure Go uses the module's own go.mod:

GOWORK=off go run ./examples/cli_transport

Getting Started

Add the library to your project with:

go get github.com/universal-tool-calling-protocol/go-utcp@latest

You can then construct a client and call tools using any of the built-in transports. The library ships transports for HTTP, Server-Sent Events, streaming HTTP, CLI, WebSocket, gRPC, GraphQL, TCP, UDP, WebRTC and MCP providers.

package main

import (
	"context"
	"fmt"
	"os"
	"time"

	utcp "github.com/universal-tool-calling-protocol/go-utcp"
)

func main() {
	ctx := context.Background()

	cfg := &utcp.UtcpClientConfig{
		ProvidersFilePath: "providers.json",
	}

	fmt.Println("Creating UTCP client...")
	client, err := utcp.NewUTCPClient(ctx, cfg, nil, nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create UTCP client: %v\n", err)
		os.Exit(1)
	}

	// Give the client time to fully initialize
	fmt.Println("Waiting for initialization...")
	time.Sleep(500 * time.Millisecond)

	fmt.Println("\n=== Tool Discovery ===")
	tools, err := client.SearchTools("", 10)
	if err != nil {
		fmt.Fprintf(os.Stderr, "search error: %v\n", err)
		os.Exit(1)
	}

	if len(tools) == 0 {
		fmt.Println("No tools found!")
		os.Exit(1)
	}

	tool := tools[0]
	fmt.Printf("Found tool: %s\n", tool.Name)
	fmt.Printf("Tool description: %s\n", tool.Description)

	// Test the tool call
	fmt.Println("\n=== Tool Call Test ===")
	input := map[string]interface{}{
		"name": "Kamil",
	}

	fmt.Printf("Calling tool '%s' with input: %v\n", tool.Name, input)
	result, err := client.CallTool(ctx, tool.Name, input)
	if err != nil {
		fmt.Printf("ERROR: %v\n", err)

		// Try to understand the error better
		fmt.Printf("Error type: %T\n", err)
		fmt.Printf("Error string: %s\n", err.Error())

		// Let's try a direct search for the provider
		fmt.Println("\n=== Searching for provider directly ===")
		providerTools, err2 := client.SearchTools("hello", 10)
		if err2 != nil {
			fmt.Printf("Provider search failed: %v\n", err2)
		} else {
			fmt.Printf("Provider search returned %d tools\n", len(providerTools))
			for i, t := range providerTools {
				fmt.Printf("  %d: %s\n", i, t.Name)
			}
		}

	} else {
		fmt.Printf("SUCCESS: %v\n", result)
	}
}

Plugins

CodeMode (codemode.run_code)

CodeMode is an executable tool plugin that lets LLMs write and run small Go-like code snippets instead of emitting large JSON tool calls. It executes snippets inside a Yaegi sandbox, providing direct access to UTCP tools via inline helper functions:

r, err := codemode.CallTool("http.echo", map[string]any{"message": "hi"})

Available helpers inside CodeMode:

  • CallTool(name string, args map[string]any) (any, error)
  • CallToolStream(name string, args map[string]any) (*StreamResult, error)
  • SearchTools(query string, limit int) ([]tools.Tool, error)

CodeMode wraps user snippets into a structured run() function, normalizes Go syntax, converts JSON expressions automatically, and exposes the result through __out.

Key benefits:

  • LLMs can loop, branch, compose multiple tools, and process intermediate values.
  • Eliminates the overhead of complex JSON planning.
  • Enables dynamic and multi-step tool workflows.

Enable it by registering the plugin:

cm := codemode.NewCodeModeUTCP(client, llmodel)

// LLm model must satisfy interface from NewCodeModeUTCP

Further Reading

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type UtcpClient

type UtcpClient struct {
	// contains filtered or unexported fields
}

UtcpClient holds all state and implements UtcpClientInterface.

func (*UtcpClient) CallTool

func (c *UtcpClient) CallTool(
	ctx context.Context,
	toolName string,
	args map[string]any,
) (any, error)

--- HOT PATH: CallTool with lock-free fast lookup ---

func (*UtcpClient) CallToolStream added in v1.5.8

func (c *UtcpClient) CallToolStream(
	ctx context.Context,
	toolName string,
	args map[string]any,
) (transports.StreamResult, error)

func (*UtcpClient) DeregisterToolProvider

func (c *UtcpClient) DeregisterToolProvider(ctx context.Context, providerName string) error

func (*UtcpClient) GetTransports added in v1.5.1

func (u *UtcpClient) GetTransports() map[string]ClientTransport

func (*UtcpClient) RegisterToolProvider

func (c *UtcpClient) RegisterToolProvider(
	ctx context.Context,
	prov Provider,
) ([]Tool, error)

RegisterToolProvider applies variable substitution, picks the right transport, and registers tools. RegisterToolProvider picks the transport and registers tools. NOTE: do NOT call substituteProviderVariables here; processProvider already did it.

func (*UtcpClient) SearchTools

func (c *UtcpClient) SearchTools(providerPrefix string, limit int) ([]Tool, error)

type UtcpClientConfig

type UtcpClientConfig struct {
	// Variables explicitly passed in (takes precedence)
	Variables map[string]string

	// Optional path to a providers‐definition file
	ProvidersFilePath string

	// A list of providers to load from (e.g. .env, AWS SSM, Vault, etc.)
	LoadVariablesFrom []UtcpVariablesConfig
}

UtcpClientConfig holds your resolved variables and provider settings.

func NewClientConfig

func NewClientConfig() *UtcpClientConfig

NewClientConfig constructs a config with sensible defaults.

type UtcpClientInterface

type UtcpClientInterface interface {
	RegisterToolProvider(ctx context.Context, prov Provider) ([]Tool, error)
	DeregisterToolProvider(ctx context.Context, providerName string) error
	CallTool(ctx context.Context, toolName string, args map[string]any) (any, error)
	SearchTools(query string, limit int) ([]Tool, error)
	GetTransports() map[string]ClientTransport
	CallToolStream(ctx context.Context, toolName string, args map[string]any) (transports.StreamResult, error)
}

UtcpClientInterface defines the public API.

func NewUTCPClient

func NewUTCPClient(
	ctx context.Context,
	cfg *UtcpClientConfig,
	repo ToolRepository,
	strat ToolSearchStrategy,
) (UtcpClientInterface, error)

NewUTCPClient constructs a new client, loading providers if configured.

type UtcpDotEnv

type UtcpDotEnv struct {
	EnvFilePath string
}

UtcpDotEnv implements UtcpVariablesConfig by loading a .env file.

func NewDotEnv

func NewDotEnv(path string) *UtcpDotEnv

func (*UtcpDotEnv) Get

func (u *UtcpDotEnv) Get(key string) (string, error)

Get loads the file and looks up a single key.

func (*UtcpDotEnv) Load

func (u *UtcpDotEnv) Load() (map[string]string, error)

Load reads the .env file and returns a map of key→value.

type UtcpVariableNotFound

type UtcpVariableNotFound struct {
	VariableName string
}

UtcpVariableNotFound is returned when a requested variable isn't present.

func (*UtcpVariableNotFound) Error

func (e *UtcpVariableNotFound) Error() string

type UtcpVariablesConfig

type UtcpVariablesConfig interface {
	// Load returns all variables available from this provider.
	Load() (map[string]string, error)
	// Get returns a single variable value or an error if not present.
	Get(key string) (string, error)
}

UtcpVariablesConfig is the interface for any variable‐loading strategy.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL