Dynamic tool registration, MCP integration, and extensible server capabilities in the HAIP Server
{
name: 'echo',
description: 'Echo back the input',
inputSchema: {
type: 'object',
properties: {
message: { type: 'string' }
},
required: ['message']
},
outputSchema: {
type: 'object',
properties: {
echoed: { type: 'string' }
}
}
}
// Call echo tool
const echoCall = {
id: "call-1",
session: "test-session",
seq: "1",
ts: Date.now().toString(),
type: "TOOL_CALL",
channel: "USER",
payload: {
call_id: "echo-1",
tool: "echo",
params: {
message: "Hello, HAIP Server!",
},
},
};
ws.send(JSON.stringify(echoCall));
{
"type": "TOOL_DONE",
"channel": "AGENT",
"payload": {
"call_id": "echo-1",
"status": "OK",
"result": {
"echoed": "Hello, HAIP Server!"
}
}
}
{
name: 'add',
description: 'Add two numbers',
inputSchema: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' }
},
required: ['a', 'b']
},
outputSchema: {
type: 'object',
properties: {
result: { type: 'number' }
}
}
}
// Call add tool
const addCall = {
id: "call-2",
session: "test-session",
seq: "2",
ts: Date.now().toString(),
type: "TOOL_CALL",
channel: "USER",
payload: {
call_id: "add-1",
tool: "add",
params: {
a: 5,
b: 3,
},
},
};
ws.send(JSON.stringify(addCall));
{
"type": "TOOL_DONE",
"channel": "AGENT",
"payload": {
"call_id": "add-1",
"status": "OK",
"result": {
"result": 8
}
}
}
{
name: 'weather',
description: 'Get weather information',
inputSchema: {
type: 'object',
properties: {
location: { type: 'string' }
}
},
outputSchema: {
type: 'object',
properties: {
temperature: { type: 'string' },
condition: { type: 'string' },
location: { type: 'string' }
}
}
}
// Call weather tool
const weatherCall = {
id: "call-3",
session: "test-session",
seq: "3",
ts: Date.now().toString(),
type: "TOOL_CALL",
channel: "USER",
payload: {
call_id: "weather-1",
tool: "weather",
params: {
location: "London",
},
},
};
ws.send(JSON.stringify(weatherCall));
{
"type": "TOOL_DONE",
"channel": "AGENT",
"payload": {
"call_id": "weather-1",
"status": "OK",
"result": {
"temperature": "22°C",
"condition": "Sunny",
"location": "London"
}
}
}
interface HAIPToolDefinition {
name: string;
description: string;
inputSchema: object; // JSON Schema
outputSchema: object; // JSON Schema
execute?: (params: any) => Promise<any>; // Optional custom executor
}
// Simple calculator tool
const calculatorTool = {
name: "calculator",
description: "Perform basic mathematical operations",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["add", "subtract", "multiply", "divide"],
},
a: { type: "number" },
b: { type: "number" },
},
required: ["operation", "a", "b"],
},
outputSchema: {
type: "object",
properties: {
result: { type: "number" },
operation: { type: "string" },
},
},
};
// Register the tool
server.registerTool(calculatorTool);
// Async database query tool
const databaseTool = {
name: "query_database",
description: "Query the database",
inputSchema: {
type: "object",
properties: {
query: { type: "string" },
limit: { type: "number", default: 100 },
},
required: ["query"],
},
outputSchema: {
type: "object",
properties: {
results: { type: "array" },
count: { type: "number" },
},
},
execute: async (params) => {
// Simulate database query
await new Promise((resolve) => setTimeout(resolve, 1000));
return {
results: [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Jane Smith" },
],
count: 2,
};
},
};
server.registerTool(databaseTool);
// File processing tool with progress
const fileProcessorTool = {
name: "process_file",
description: "Process a file with progress updates",
inputSchema: {
type: "object",
properties: {
filename: { type: "string" },
operation: { type: "string" },
},
required: ["filename", "operation"],
},
outputSchema: {
type: "object",
properties: {
processed: { type: "boolean" },
lines: { type: "number" },
},
},
execute: async (params, sessionId, callId, server) => {
// Send progress updates
server.sendToolUpdate(sessionId, callId, "RUNNING", 0);
// Simulate processing steps
for (let i = 0; i <= 100; i += 20) {
await new Promise((resolve) => setTimeout(resolve, 200));
server.sendToolUpdate(sessionId, callId, "RUNNING", i);
}
return {
processed: true,
lines: 150,
};
},
};
server.registerTool(fileProcessorTool);
// Register a new tool
server.registerTool({
name: "custom_tool",
description: "A custom tool",
inputSchema: {
type: "object",
properties: {
input: { type: "string" },
},
required: ["input"],
},
outputSchema: {
type: "object",
properties: {
output: { type: "string" },
},
},
});
// Unregister a tool
server.unregisterTool("custom_tool");
// Get all registered tools
const tools = server.getTools();
console.log(
"Registered tools:",
tools.map((t) => t.name)
);
// Request tool list
const toolListRequest = {
id: "discovery-1",
session: "test-session",
seq: "1",
ts: Date.now().toString(),
type: "TOOL_LIST",
channel: "USER",
payload: {},
};
ws.send(JSON.stringify(toolListRequest));
{
"type": "TOOL_LIST",
"channel": "AGENT",
"payload": {
"tools": [
{
"name": "echo",
"description": "Echo back the input"
},
{
"name": "add",
"description": "Add two numbers"
},
{
"name": "weather",
"description": "Get weather information"
}
]
}
}
// Request tool schema
const schemaRequest = {
id: "schema-1",
session: "test-session",
seq: "2",
ts: Date.now().toString(),
type: "TOOL_SCHEMA",
channel: "USER",
payload: {
tool: "echo",
},
};
ws.send(JSON.stringify(schemaRequest));
{
"type": "TOOL_SCHEMA",
"channel": "AGENT",
"payload": {
"tool": "echo",
"inputSchema": {
"type": "object",
"properties": {
"message": { "type": "string" }
},
"required": ["message"]
},
"outputSchema": {
"type": "object",
"properties": {
"echoed": { "type": "string" }
}
}
}
}
const toolCall = {
id: "call-1",
session: "test-session",
seq: "1",
ts: Date.now().toString(),
type: "TOOL_CALL",
channel: "USER",
payload: {
call_id: "unique-call-id",
tool: "echo",
params: {
message: "Hello, world!",
},
},
};
{
"type": "TOOL_UPDATE",
"channel": "AGENT",
"payload": {
"call_id": "unique-call-id",
"status": "RUNNING",
"progress": 50
}
}
{
"type": "TOOL_DONE",
"channel": "AGENT",
"payload": {
"call_id": "unique-call-id",
"status": "OK",
"result": {
"echoed": "Hello, world!"
}
}
}
const toolCancel = {
id: "cancel-1",
session: "test-session",
seq: "2",
ts: Date.now().toString(),
type: "TOOL_CANCEL",
channel: "USER",
payload: {
call_id: "unique-call-id",
},
};
const emailTool = {
name: "send_email",
description: "Send an email",
inputSchema: {
type: "object",
properties: {
to: { type: "string", format: "email" },
subject: { type: "string" },
body: { type: "string" },
},
required: ["to", "subject", "body"],
},
outputSchema: {
type: "object",
properties: {
sent: { type: "boolean" },
message_id: { type: "string" },
},
},
execute: async (params) => {
// Send email (implementation depends on your email service)
const messageId = await sendEmail(params.to, params.subject, params.body);
return {
sent: true,
message_id: messageId,
};
},
};
const userTool = {
name: "create_user",
description: "Create a new user",
inputSchema: {
type: "object",
properties: {
username: {
type: "string",
minLength: 3,
maxLength: 20,
pattern: "^[a-zA-Z0-9_]+$",
},
email: {
type: "string",
format: "email",
},
age: {
type: "number",
minimum: 13,
maximum: 120,
},
},
required: ["username", "email", "age"],
},
outputSchema: {
type: "object",
properties: {
user_id: { type: "string" },
created: { type: "boolean" },
},
},
execute: async (params) => {
// Validate input (schema validation is automatic)
if (params.age < 13) {
throw new Error("User must be at least 13 years old");
}
// Create user
const userId = await createUser(params);
return {
user_id: userId,
created: true,
};
},
};
const apiTool = {
name: "api_call",
description: "Make an API call",
inputSchema: {
type: "object",
properties: {
url: { type: "string", format: "uri" },
method: { type: "string", enum: ["GET", "POST", "PUT", "DELETE"] },
},
required: ["url", "method"],
},
outputSchema: {
type: "object",
properties: {
status: { type: "number" },
data: { type: "object" },
},
},
execute: async (params) => {
try {
const response = await fetch(params.url, {
method: params.method,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return {
status: response.status,
data: data,
};
} catch (error) {
// Return error in expected format
return {
status: 500,
data: { error: error.message },
};
}
},
};
// Get all registered tools
const tools = server.getTools();
console.log(
"Available tools:",
tools.map((t) => t.name)
);
// Check if a tool exists
const toolExists = server.getTools().some((t) => t.name === "echo");
console.log("Echo tool exists:", toolExists);
// Get tool execution statistics
const stats = server.getStats();
console.log("Tool executions:", stats.toolExecutions);
// Good
{
name: "send_email_notification";
}
// Bad
{
name: "email";
}
// Good
{
description: "Send an email notification to the specified recipient";
}
// Bad
{
description: "Sends email";
}
// Use JSON Schema for validation
inputSchema: {
type: 'object',
properties: {
email: { type: 'string', format: 'email' },
age: { type: 'number', minimum: 0 }
},
required: ['email', 'age']
}
execute: async (params) => {
try {
// Tool logic
return { success: true, data: result };
} catch (error) {
return { success: false, error: error.message };
}
};
execute: async (params, sessionId, callId, server) => {
server.sendToolUpdate(sessionId, callId, "RUNNING", 0);
// ... work ...
server.sendToolUpdate(sessionId, callId, "RUNNING", 50);
// ... more work ...
server.sendToolUpdate(sessionId, callId, "RUNNING", 100);
return result;
};
Was this page helpful?