
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
Universal Tool Calling Protocol (UTCP) Core SDK for TypeScript
The @utcp/sdk package provides the fundamental components and interfaces for the Universal Tool Calling Protocol (UTCP) in TypeScript. It is designed to be lean and extensible, serving as the central hub for integrating various communication protocols via a plugin-based architecture.
UtcpClient: The main client for interacting with the UTCP ecosystem
Data Models: Type-safe data structures with Zod validation
Tool: Tool definitions with inputs/outputs schemasCallTemplate: Protocol-specific call templatesAuth: Authentication configurations (API Key, Basic, OAuth2)UtcpManual: Tool collectionsPluggable Architecture:
CommunicationProtocol: Interface for protocol implementationsConcurrentToolRepository: Interface for tool storage (default: in-memory)ToolSearchStrategy: Interface for search algorithms (default: tag-based)VariableSubstitutor: Interface for variable resolutionSecurity Features:
npm install @utcp/sdk
# Or with bun
bun add @utcp/sdk
Important: Plugins must be explicitly imported to register their protocols:
import { UtcpClient } from '@utcp/sdk';
import '@utcp/http'; // Auto-registers HTTP protocol
import { HttpCallTemplateSerializer } from '@utcp/http';
async function main() {
// Create client
const serializer = new HttpCallTemplateSerializer();
const apiTemplate = serializer.validateDict({
name: 'api_manual',
call_template_type: 'http',
http_method: 'GET',
url: 'https://api.example.com/data',
headers: {
'Authorization': 'Bearer ${API_KEY}' // Resolves to api__manual_API_KEY
}
});
const client = await UtcpClient.create(process.cwd(), {
variables: {
// Namespaced format: manual_name_VARIABLE
api__manual_API_KEY: process.env.API_KEY || ''
},
manual_call_templates: [apiTemplate]
});
// Search for tools
const tools = await client.searchTools('data');
// Call a tool
const result = await client.callTool('api_manual.get_data', {});
await client.close();
}
import { UtcpClient } from '@utcp/sdk';
import '@utcp/dotenv-loader'; // Required for .env file support
const client = await UtcpClient.create(process.cwd(), {
load_variables_from: [
{
variable_loader_type: 'dotenv',
env_file_path: './.env'
}
],
variables: {
// Direct config (highest priority)
'github__api_TOKEN': 'override-token'
}
});
static async create(
root_dir: string,
config?: Partial<UtcpClientConfig>
): Promise<UtcpClient>
Parameters:
root_dir: Base directory for resolving relative paths (typically process.cwd())config: Optional configuration objectConfiguration:
interface UtcpClientConfig {
// Variable definitions (highest priority)
variables?: Record<string, string>;
// External variable loaders
load_variables_from?: VariableLoader[];
// Initial manuals to register
manual_call_templates?: CallTemplate[];
// Tool storage (default: in-memory)
tool_repository?: ConcurrentToolRepository;
// Search algorithm (default: tag and description matching)
tool_search_strategy?: ToolSearchStrategy;
// Result post-processors
post_processing?: ToolPostProcessor[];
}
async searchTools(
query: string,
limit?: number,
anyOfTagsRequired?: string[]
): Promise<Tool[]>
Searches for tools matching the query. Considers:
async callTool(
toolName: string,
args: Record<string, any>
): Promise<any>
Execute a tool. Tool names use format:
manual_name.tool_namemanual_name.server_name.tool_nameasync getTools(): Promise<Tool[]>
async getTool(toolName: string): Promise<Tool | undefined>
async registerManual(callTemplate: CallTemplate): Promise<void>
async deregisterManual(manualName: string): Promise<boolean>
async registerManuals(callTemplates: CallTemplate[]): Promise<void>
async getRequiredVariablesForTool(toolName: string): Promise<string[]>
async close(): Promise<void>
Closes all communication protocols and releases resources.
Variables are automatically namespaced by manual name for security:
// Manual name: "github_api"
// Variable reference: ${TOKEN}
// Actual lookup: "github__api_TOKEN"
const client = await UtcpClient.create(process.cwd(), {
variables: {
'github__api_TOKEN': 'github-secret',
'slack__api_TOKEN': 'slack-secret',
},
manual_call_templates: [
{
name: 'github_api',
// ...
headers: {
// Resolves ONLY to 'github__api_TOKEN'
'Authorization': 'Bearer ${TOKEN}'
}
}
]
});
Namespace transformation:
github_api → github__api_config.variables (highest priority)All lookups use the namespaced key: {namespace}_VARIABLE_NAME
UTCP provides several optional protocol plugins:
Browser-Compatible:
@utcp/http - HTTP/HTTPS requests with full authentication support@utcp/text - Direct text/string content (inline UTCP manuals or OpenAPI specs)@utcp/direct-call - Direct function callsNode.js Only:
@utcp/file - File system access for loading manuals from local files@utcp/mcp - Model Context Protocol support@utcp/cli - CLI command execution@utcp/dotenv-loader - Load variables from .env filesEach plugin must be explicitly imported to register its protocol:
// Browser application - only import browser-compatible plugins
import { UtcpClient } from '@utcp/sdk';
import '@utcp/http';
import '@utcp/text';
import '@utcp/direct-call';
// Node.js application - can use all plugins
import { UtcpClient } from '@utcp/sdk';
import '@utcp/http';
import '@utcp/mcp';
import '@utcp/file';
import '@utcp/dotenv-loader';
This explicit import approach ensures:
For custom or third-party plugins:
import { CallTemplateSerializer } from '@utcp/sdk';
import { CommunicationProtocol } from '@utcp/sdk';
// Register custom call template
CallTemplateSerializer.registerCallTemplate(
'custom_type',
new CustomCallTemplateSerializer()
);
// Register custom protocol
CommunicationProtocol.communicationProtocols['custom_type'] =
new CustomCommunicationProtocol();
import { ConcurrentToolRepository, Tool } from '@utcp/sdk';
class CustomRepository implements ConcurrentToolRepository {
tool_repository_type = 'custom' as const;
async getTools(): Promise<Tool[]> { /* ... */ }
async getTool(name: string): Promise<Tool | undefined> { /* ... */ }
async saveManual(callTemplate: CallTemplate, manual: UtcpManual): Promise<void> { /* ... */ }
async removeManual(manualName: string): Promise<boolean> { /* ... */ }
// ... implement other required methods
}
const client = await UtcpClient.create(process.cwd(), {
tool_repository: new CustomRepository()
});
import { ToolSearchStrategy } from '@utcp/sdk';
class CustomSearchStrategy implements ToolSearchStrategy {
tool_search_strategy_type = 'custom' as const;
async searchTools(
repository: ConcurrentToolRepository,
query: string,
limit?: number,
anyOfTagsRequired?: string[]
): Promise<Tool[]> {
// Implement custom search logic
}
}
Transform tool results:
const client = await UtcpClient.create(process.cwd(), {
post_processing: [
{
tool_post_processor_type: 'filter_dict',
allowed_keys: ['id', 'name', 'email']
},
{
tool_post_processor_type: 'limit_strings',
max_length: 1000
}
]
});
import { UtcpVariableNotFoundError } from '@utcp/sdk';
try {
await client.callTool('manual.tool', {});
} catch (error) {
if (error instanceof UtcpVariableNotFoundError) {
console.error(`Missing variable: ${error.variableName}`);
}
throw error;
}
The package is fully typed with TypeScript. All schemas are validated at runtime using Zod:
import { ToolSchema, CallTemplateSchema } from '@utcp/sdk';
// Runtime validation
const tool = ToolSchema.parse(toolData);
const callTemplate = CallTemplateSchema.parse(templateData);
@utcp/sdk/
├── client/ # UtcpClient and configuration
├── data/ # Core data models (Tool, CallTemplate, Auth, etc.)
├── interfaces/ # Abstract interfaces for plugins
├── implementations/ # Default implementations
│ ├── in_mem_concurrent_tool_repository.ts
│ ├── tag_search_strategy.ts
│ └── default_variable_substitutor.ts
└── plugins/ # Plugin loader and registry
@utcp/sdk - Core SDK (this package)@utcp/http - HTTP protocol support with OpenAPI conversion@utcp/mcp - Model Context Protocol integration@utcp/text - File-based tool loading@utcp/cli - Command-line tool executionSee the root repository for contribution guidelines.
Mozilla Public License Version 2.0
FAQs
Universal Tool Calling Protocol SDK
We found that @utcp/sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.