π§ SDK Custom Tools Guide
Build powerful AI applications by extending NeuroLink with your own custom tools.
π Overviewβ
NeuroLink's SDK allows you to register custom tools programmatically, giving your AI assistants access to any functionality you need. All registered tools work seamlessly with the built-in tool system across all supported providers.
Key Featuresβ
- β Type-Safe: Full TypeScript support with Zod schema validation
- β Provider Agnostic: Works with all providers that support tools
- β Easy Integration: Simple API for tool registration
- β Async Support: All tools run asynchronously
- β Error Handling: Graceful error handling built-in
π Quick Startβ
Basic Tool Registrationβ
import { NeuroLink } from "@juspay/neurolink";
import { z } from "zod";
const neurolink = new NeuroLink();
// Register a simple tool
neurolink.registerTool("greetUser", {
description: "Generate a personalized greeting",
parameters: z.object({
name: z.string().describe("User name"),
language: z.enum(["en", "es", "fr", "de"]).default("en"),
}),
execute: async ({ name, language }) => {
const greetings = {
en: `Hello, ${name}!`,
es: `Β‘Hola, ${name}!`,
fr: `Bonjour, ${name}!`,
de: `Hallo, ${name}!`,
};
return { greeting: greetings[language] };
},
});
// AI will now use your tool
const result = await neurolink.generate({
input: { text: "Greet John in Spanish" },
});
// AI calls: greetUser({ name: "John", language: "es" })
// Returns: "Β‘Hola, John!"
β οΈ Common Mistakesβ
β Using schema instead of parametersβ
// WRONG - will throw validation error
neurolink.registerTool("badTool", {
description: "This will fail",
schema: {
// β Should be 'parameters'
type: "object",
properties: { value: { type: "string" } },
},
execute: async (args) => args,
});
β Using plain JSON schema as parametersβ
// WRONG - will throw validation error
neurolink.registerTool("badTool", {
description: "This will also fail",
parameters: {
// β Should be Zod schema
type: "object",
properties: { value: { type: "string" } },
},
execute: async (args) => args,
});
β Correct Zod Schema Formatβ
// CORRECT - works perfectly
import { z } from "zod";
neurolink.registerTool("goodTool", {
description: "This works correctly",
parameters: z.object({
// β
Zod schema
value: z.string(),
}),
execute: async (args) => args,
});
π SimpleTool Interfaceβ
All custom tools implement the SimpleTool interface:
type SimpleTool<T = any, R = any> = {
description: string; // What the tool does
parameters?: ZodSchema<T>; // Input validation schema
execute: (args: T) => Promise<R>; // Tool implementation
};
Interface Componentsβ
- description: Clear, actionable description that helps the AI understand when to use the tool
- parameters: Optional Zod schema for validating inputs (highly recommended)
- execute: Async function that implements the tool's logic
π οΈ Registration Methodsβ
Register Single Toolβ
neurolink.registerTool(name: string, tool: SimpleTool): void
Register Multiple Toolsβ
neurolink.registerTools(tools: Record<string, SimpleTool>): void
Get Custom Toolsβ
// Get custom tools registered via registerTool()
const customTools = neurolink.getCustomTools(); // Returns Map<string, MCPExecutableTool>
// Get all available tools (async - includes built-in, custom, and MCP tools)
const allTools = await neurolink.getAllAvailableTools(); // Returns ToolInfo[]
π‘ Common Use Casesβ
1. API Integrationβ
neurolink.registerTool("weatherLookup", {
description: "Get current weather for any city",
parameters: z.object({
city: z.string().describe("City name"),
country: z.string().optional().describe("Country code (ISO 2-letter)"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
}),
execute: async ({ city, country, units }) => {
const response = await fetch(
`https://api.weather.com/v1/current?city=${city}&country=${country || ""}&units=${units}`,
{ headers: { "API-Key": process.env.WEATHER_API_KEY } },
);
const data = await response.json();
return {
city,
temperature: data.temp,
condition: data.condition,
humidity: data.humidity,
units,
};
},
});
2. Database Operationsβ
neurolink.registerTool("userLookup", {
description: "Find user information by email or ID",
parameters: z.object({
identifier: z.string().describe("Email address or user ID"),
fields: z
.array(z.string())
.optional()
.describe("Specific fields to return"),
}),
execute: async ({ identifier, fields }) => {
const db = getDatabase();
const query = identifier.includes("@")
? { email: identifier }
: { id: identifier };
const user = await db.users.findOne(query);
if (!user) {
return { error: "User not found" };
}
// Return only requested fields if specified
if (fields && fields.length > 0) {
return fields.reduce((acc, field) => {
acc[field] = user[field];
return acc;
}, {});
}
return user;
},
});
3. Data Processingβ
neurolink.registerTool("analyzeSentiment", {
description: "Analyze sentiment of text using ML model",
parameters: z.object({
text: z.string().describe("Text to analyze"),
language: z.string().default("en").describe("Language code"),
detailed: z.boolean().default(false).describe("Include detailed analysis"),
}),
execute: async ({ text, language, detailed }) => {
const sentimentModel = await loadSentimentModel(language);
const result = await sentimentModel.analyze(text);
if (detailed) {
return {
sentiment: result.sentiment,
score: result.score,
emotions: result.emotions,
keywords: result.keywords,
confidence: result.confidence,
};
}
return {
sentiment: result.sentiment,
score: result.score,
};
},
});
4. File Operationsβ
neurolink.registerTool("processSpreadsheet", {
description: "Process Excel/CSV files with various operations",
parameters: z.object({
filePath: z.string().describe("Path to spreadsheet file"),
operation: z.enum(["summarize", "filter", "pivot", "chart"]),
options: z.record(z.any()).optional(),
}),
execute: async ({ filePath, operation, options = {} }) => {
const workbook = await loadSpreadsheet(filePath);
switch (operation) {
case "summarize":
return {
sheets: workbook.sheetNames,
totalRows: workbook.getTotalRows(),
columns: workbook.getColumns(),
summary: workbook.generateSummary(),
};
case "filter":
const filtered = workbook.filter(options.criteria);
return {
matchingRows: filtered.length,
data: filtered,
};
case "pivot":
return workbook.createPivotTable(
options.rows,
options.columns,
options.values,
);
case "chart":
const chartData = workbook.prepareChartData(
options.type,
options.series,
);
return { chartData, recommendation: suggestChartType(chartData) };
}
},
});
5. External Service Integrationβ
neurolink.registerTools({
sendEmail: {
description: "Send email via SMTP",
parameters: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
cc: z.array(z.string().email()).optional(),
attachments: z.array(z.string()).optional(),
}),
execute: async ({ to, subject, body, cc, attachments }) => {
const mailer = getMailer();
const result = await mailer.send({
to,
subject,
body,
cc,
attachments: attachments
? await Promise.all(attachments.map(loadAttachment))
: undefined,
});
return {
messageId: result.messageId,
status: "sent",
timestamp: new Date().toISOString(),
};
},
},
scheduleCalendarEvent: {
description: "Create calendar event",
parameters: z.object({
title: z.string(),
startTime: z.string().datetime(),
duration: z.number().describe("Duration in minutes"),
attendees: z.array(z.string().email()).optional(),
location: z.string().optional(),
description: z.string().optional(),
}),
execute: async (params) => {
const calendar = getCalendarService();
const event = await calendar.createEvent({
...params,
endTime: addMinutes(params.startTime, params.duration),
});
return {
eventId: event.id,
eventLink: event.htmlLink,
status: "created",
};
},
},
});
π― Best Practicesβ
1. Clear Descriptionsβ
Make tool descriptions specific and actionable:
// β Bad
description: "Database tool";
// β
Good
description: "Search customer database by name, email, or order ID";
2. Parameter Validationβ
Always use Zod schemas for type safety:
// β Bad - No validation
parameters: undefined,
execute: async (args: any) => {
// Risky - args could be anything
}
// β
Good - Full validation
parameters: z.object({
userId: z.string().uuid(),
action: z.enum(['view', 'edit', 'delete']),
reason: z.string().min(10).optional()
}),
execute: async ({ userId, action, reason }) => {
// Type-safe with validated inputs
}
3. Error Handlingβ
Handle errors gracefully:
execute: async (args) => {
try {
const result = await riskyOperation(args);
return { success: true, data: result };
} catch (error) {
// Return error info instead of throwing
return {
success: false,
error: error.message,
code: error.code || "UNKNOWN_ERROR",
};
}
};
A returned error is classified as a failure everywhere the SDK reports one,
exactly as a thrown error is. A result is error-shaped when it carries
isError: true (the MCP convention), a non-null error field ({ error: "User not found" }), or a status of "error" / "failed" / "failure" /
"fail". Such a call emits tool:end with success: false and the error
text, is stored in session memory as success: false, replays on later turns
as [called <tool> β error], and is reported with isError: true in
result.toolExecutions. The model still receives the full object you
returned β classification only affects telemetry and memory, never what the
model sees.
4. Async Operationsβ
All execute functions must return promises:
// β Bad - Synchronous
execute: (args) => {
return { result: "data" };
};
// β
Good - Asynchronous
execute: async (args) => {
const result = await fetchData(args);
return { result };
};
5. Tool Namingβ
Use clear, consistent naming:
// β Bad naming
neurolink.registerTool('tool1', { ... });
neurolink.registerTool('doStuff', { ... });
neurolink.registerTool('x', { ... });
// β
Good naming
neurolink.registerTool('searchProducts', { ... });
neurolink.registerTool('calculateShipping', { ... });
neurolink.registerTool('updateInventory', { ... });
π§ͺ Testing Your Toolsβ
Unit Testingβ
import { describe, it, expect } from "vitest";
describe("weatherLookup tool", () => {
it("should return weather data for valid city", async () => {
const tool = {
description: "Get weather data",
parameters: z.object({
city: z.string(),
}),
execute: async ({ city }) => {
// Mock implementation for testing
return {
city,
temperature: 22,
condition: "sunny",
};
},
};
const result = await tool.execute({ city: "London" });
expect(result).toHaveProperty("temperature");
expect(result.city).toBe("London");
});
});
Integration Testingβ
import { NeuroLink } from "@juspay/neurolink";
describe("Custom tools integration", () => {
let neurolink: NeuroLink;
beforeEach(() => {
neurolink = new NeuroLink();
neurolink.registerTool("testTool", {
description: "Test tool for integration testing",
parameters: z.object({ input: z.string() }),
execute: async ({ input }) => ({ output: input.toUpperCase() }),
});
});
it("should use custom tool in generation", async () => {
const result = await neurolink.generate({
input: { text: "Use the test tool with input 'hello'" },
provider: "google-ai",
});
expect(result.content).toContain("HELLO");
});
});
π Debugging Toolsβ
Enable Debug Modeβ
export NEUROLINK_DEBUG=true
Log Tool Executionβ
neurolink.registerTool("debuggedTool", {
description: "Tool with debug logging",
parameters: z.object({ data: z.any() }),
execute: async (args) => {
console.log("[Tool] Executing with args:", args);
try {
const result = await processData(args);
console.log("[Tool] Success:", result);
return result;
} catch (error) {
console.error("[Tool] Error:", error);
throw error;
}
},
});
π Advanced Patternsβ
Tool Compositionβ
// Base tools
const baseTools = {
fetchData: {
description: "Fetch data from API",
execute: async ({ endpoint }) => {
const response = await fetch(endpoint);
return response.json();
},
},
transformData: {
description: "Transform data format",
execute: async ({ data, format }) => {
return transform(data, format);
},
},
};
// Composed tool
neurolink.registerTool("fetchAndTransform", {
description: "Fetch data and transform it",
parameters: z.object({
endpoint: z.string().url(),
format: z.enum(["json", "csv", "xml"]),
}),
execute: async ({ endpoint, format }) => {
const data = await baseTools.fetchData.execute({ endpoint });
return baseTools.transformData.execute({ data, format });
},
});
Tool Middlewareβ
// Wrap tools with middleware
function withRateLimit(tool: SimpleTool, limit: number): SimpleTool {
const rateLimiter = new RateLimiter(limit);
return {
...tool,
execute: async (args) => {
await rateLimiter.acquire();
return tool.execute(args);
},
};
}
// Register with rate limiting
neurolink.registerTool(
"limitedApi",
withRateLimit(
{
description: "Rate-limited API call",
execute: async (args) => callExpensiveAPI(args),
},
10,
), // 10 calls per minute
);
Dynamic Tool Registrationβ
// Register tools based on configuration
async function registerDynamicTools(config: ToolConfig[]) {
const tools: Record<string, SimpleTool> = {};
for (const toolConfig of config) {
tools[toolConfig.name] = {
description: toolConfig.description,
parameters: createZodSchema(toolConfig.parameters),
execute: createExecutor(toolConfig),
};
}
neurolink.registerTools(tools);
}
// Load from configuration
const toolConfigs = await loadToolConfigs();
await registerDynamicTools(toolConfigs);
Forcing a Tool Callβ
toolChoice tells the model whether it must call a tool on a step:
| Value | Effect |
|---|---|
"auto" (default) | The model decides whether and which tool to call |
"none" | No tool calls on any step |
"required" | The model must call at least one tool |
{ type: "tool", toolName: "x" } | The model must call the named tool |
A forced choice ("required" or a named tool) is applied only to the first
toolChoiceSteps steps of the tool loop β default 1 β after which the
model is free to answer. Holding a forced choice on every step would compel
a tool call on every step, and the turn could only end when maxSteps ran
out. "auto" and "none" are applied unchanged on every step.
// Force `lookup_order` once, then let the model answer from the result.
const result = await neurolink.generate({
input: { text: "Where is order 4471?" },
toolChoice: { type: "tool", toolName: "lookup_order" },
});
// Demand a tool on the first two steps, e.g. a search followed by a fetch.
await neurolink.generate({
input: { text: "Summarise today's release notes" },
toolChoice: "required",
toolChoiceSteps: 2,
});
For anything finer, prepareStep runs before every step and may return a
toolChoice for that step, which takes precedence over toolChoiceSteps:
await neurolink.generate({
input: { text: "Plan, then execute" },
prepareStep: async ({ stepNumber, steps }) => {
if (stepNumber === 0) {
return { toolChoice: { type: "tool", toolName: "make_plan" } };
}
if (steps.at(-1)?.toolCalls.some((c) => c.toolName === "make_plan")) {
return { toolChoice: "required" };
}
return { toolChoice: "auto" };
},
});
prepareStep receives { stepNumber, steps, maxSteps, model }, where steps
holds one record per completed step (its text, tool calls β with input
already parsed into an object β tool results, finish reason and usage,
including prompt-cache tokens where the provider reports them). Only the
returned toolChoice is honoured; the legacy model and
experimental_activeTools / activeTools fields are accepted for source
compatibility but ignored, and their presence is logged once as a warning β
use toolFilter / excludeTools to scope tools. A hook that throws is
logged and that step falls back to the toolChoice / toolChoiceSteps rule;
a hook still pending when the turn is aborted or times out is released with
the abort. Both toolChoice and prepareStep apply to stream() as well.
Vertex, Google AI Studio and Bedrock do not honour toolChoice at all today.
SageMaker honours tools, toolChoice and prepareStep on generate()
only: its stream() sends the prompt without tools.
π Performance Considerationsβ
1. Timeout Handlingβ
execute: async (args) => {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Tool timeout")), 30000),
);
const operation = performOperation(args);
return Promise.race([operation, timeout]);
};
2. Cachingβ
const cache = new Map();
execute: async (args) => {
const cacheKey = JSON.stringify(args);
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const result = await expensiveOperation(args);
cache.set(cacheKey, result);
return result;
};
3. Batch Operationsβ
neurolink.registerTool("batchProcess", {
description: "Process multiple items efficiently",
parameters: z.object({
items: z.array(z.any()),
operation: z.string(),
}),
execute: async ({ items, operation }) => {
// Process in parallel with concurrency limit
const results = await pLimit(5)(
items.map((item) => () => processItem(item, operation)),
);
return {
processed: results.length,
results,
};
},
});
π Security Considerationsβ
Input Sanitizationβ
parameters: z.object({
sqlQuery: z
.string()
.max(1000)
.refine(
(query) => !query.match(/DROP|DELETE|TRUNCATE/i),
"Destructive operations not allowed",
),
});
Permission Checkingβ
execute: async (args, context) => {
// Check permissions before execution
if (!hasPermission(context.user, "database.write")) {
return { error: "Insufficient permissions" };
}
return performDatabaseOperation(args);
};
Rate Limitingβ
const userLimits = new Map();
execute: async (args, context) => {
const userId = context.user?.id || "anonymous";
const userCalls = userLimits.get(userId) || 0;
if (userCalls >= 100) {
return { error: "Rate limit exceeded" };
}
userLimits.set(userId, userCalls + 1);
// Reset counters periodically
setTimeout(() => userLimits.delete(userId), 3600000);
return performOperation(args);
};
π Complete Exampleβ
Here's a complete example combining multiple concepts:
import { NeuroLink } from "@juspay/neurolink";
import { z } from "zod";
const neurolink = new NeuroLink();
// Define a comprehensive customer service tool set
neurolink.registerTools({
searchCustomer: {
description: "Search for customer by various criteria",
parameters: z.object({
query: z.string(),
searchBy: z.enum(["email", "name", "phone", "orderId"]),
limit: z.number().min(1).max(50).default(10),
}),
execute: async ({ query, searchBy, limit }) => {
const db = getDatabase();
const results = await db.customers.search({
[searchBy]: query,
limit,
});
return {
found: results.length,
customers: results.map((c) => ({
id: c.id,
name: c.name,
email: c.email,
totalOrders: c.orderCount,
memberSince: c.createdAt,
})),
};
},
},
getOrderHistory: {
description: "Get order history for a customer",
parameters: z.object({
customerId: z.string().uuid(),
status: z
.enum(["all", "pending", "completed", "cancelled"])
.default("all"),
limit: z.number().default(10),
}),
execute: async ({ customerId, status, limit }) => {
const orders = await fetchOrders(customerId, { status, limit });
return {
customerId,
orderCount: orders.length,
orders: orders.map((o) => ({
orderId: o.id,
date: o.createdAt,
status: o.status,
total: o.total,
items: o.items.length,
})),
};
},
},
processRefund: {
description: "Process refund for an order",
parameters: z.object({
orderId: z.string().uuid(),
amount: z.number().positive(),
reason: z.string().min(10),
notify: z.boolean().default(true),
}),
execute: async ({ orderId, amount, reason, notify }) => {
// Validate order exists and is refundable
const order = await getOrder(orderId);
if (!order) {
return { success: false, error: "Order not found" };
}
if (order.status !== "completed") {
return {
success: false,
error: "Only completed orders can be refunded",
};
}
if (amount > order.total) {
return { success: false, error: "Refund amount exceeds order total" };
}
// Process refund
const refund = await processPaymentRefund({
orderId,
amount,
reason,
});
// Send notification
if (notify) {
await sendRefundNotification(order.customerId, refund);
}
return {
success: true,
refundId: refund.id,
amount: refund.amount,
status: "processed",
};
},
},
});
// Now you can use natural language to access these tools
const result = await neurolink.generate({
input: {
text: "Find all orders for customer john@example.com and process a $50 refund for their most recent completed order due to damaged item",
},
provider: "openai",
});
// The AI will:
// 1. Call searchCustomer({ query: "john@example.com", searchBy: "email" })
// 2. Call getOrderHistory({ customerId: <found_id>, status: "completed" })
// 3. Call processRefund({ orderId: <most_recent>, amount: 50, reason: "damaged item" })
π MCP Server Integrationβ
Beyond simple tool registration, NeuroLink SDK supports adding complete MCP (Model Context Protocol) servers for more complex tool ecosystems.
Adding In-Memory MCP Serversβ
// Add a complete MCP server with multiple related tools
await neurolink.addInMemoryMCPServer("hr-management", {
server: {
title: "HR Management Server",
description: "Comprehensive HR tools for employee management",
tools: {
createEmployee: {
description: "Create a new employee record with full details",
execute: async (params: {
name: string;
department: string;
role: string;
salary: number;
startDate: string;
}) => {
return {
success: true,
data: {
employeeId: `EMP-${Date.now()}`,
name: params.name,
department: params.department,
role: params.role,
salary: params.salary,
startDate: params.startDate,
status: "active",
createdAt: new Date().toISOString(),
},
};
},
},
calculateSalary: {
description: "Calculate total salary including bonuses and deductions",
execute: async (params: {
baseSalary: number;
bonuses: number;
deductions: number;
taxRate: number;
}) => {
const grossSalary =
params.baseSalary + params.bonuses - params.deductions;
const netSalary = grossSalary * (1 - params.taxRate);
return {
success: true,
data: {
baseSalary: params.baseSalary,
bonuses: params.bonuses,
deductions: params.deductions,
grossSalary,
taxAmount: grossSalary * params.taxRate,
netSalary,
calculatedAt: new Date().toISOString(),
},
};
},
},
getEmployeeStats: {
description: "Get comprehensive employee statistics and analytics",
execute: async (params: { department?: string; role?: string }) => {
// Simulated analytics data
return {
success: true,
data: {
totalEmployees: 150,
byDepartment: {
engineering: 60,
sales: 35,
marketing: 25,
hr: 15,
finance: 15,
},
averageSalary: 75000,
averageTenure: "2.5 years",
openPositions: 8,
lastUpdated: new Date().toISOString(),
},
};
},
},
},
},
category: "hr-management",
metadata: {
version: "1.0.0",
author: "Your Company",
description: "Complete HR management solution",
},
});
Advanced MCP Server Examplesβ
1. Data Analytics Serverβ
await neurolink.addInMemoryMCPServer("analytics-server", {
server: {
title: "Data Analytics Server",
description: "Advanced data processing and analytics tools",
tools: {
analyzeDataset: {
description: "Perform statistical analysis on datasets",
execute: async (params: {
data: number[];
analysisType: "descriptive" | "correlation" | "regression";
}) => {
const { data, analysisType } = params;
switch (analysisType) {
case "descriptive":
const sum = data.reduce((a, b) => a + b, 0);
const mean = sum / data.length;
const sortedData = [...data].sort((a, b) => a - b);
const median = sortedData[Math.floor(data.length / 2)];
const variance =
data.reduce((acc, val) => acc + Math.pow(val - mean, 2), 0) /
data.length;
const stdDev = Math.sqrt(variance);
return {
success: true,
data: {
count: data.length,
sum,
mean,
median,
min: Math.min(...data),
max: Math.max(...data),
variance,
standardDeviation: stdDev,
range: Math.max(...data) - Math.min(...data),
},
};
case "correlation":
// Simplified correlation analysis
return {
success: true,
data: {
correlationMatrix: "Generated correlation matrix",
strongCorrelations: [],
analysisNote: "Correlation analysis completed",
},
};
default:
return { success: false, error: "Unknown analysis type" };
}
},
},
generateReport: {
description: "Generate comprehensive data reports with visualizations",
execute: async (params: {
title: string;
data: any[];
reportType: "summary" | "detailed" | "executive";
}) => {
return {
success: true,
data: {
reportId: `RPT-${Date.now()}`,
title: params.title,
type: params.reportType,
dataPoints: params.data.length,
sections: [
"Executive Summary",
"Key Metrics",
"Detailed Analysis",
"Recommendations",
],
generatedAt: new Date().toISOString(),
status: "completed",
},
};
},
},
},
},
});
2. Workflow Automation Serverβ
await neurolink.addInMemoryMCPServer("workflow-server", {
server: {
title: "Workflow Automation Server",
description: "Tools for creating and managing automated workflows",
tools: {
createWorkflow: {
description: "Create a new automated workflow with multiple steps",
execute: async (params: {
name: string;
steps: Array<{
name: string;
type: string;
config: any;
}>;
triggers: string[];
}) => {
return {
success: true,
data: {
workflowId: `WF-${Date.now()}`,
name: params.name,
steps: params.steps,
triggers: params.triggers,
status: "created",
nextExecution: null,
createdAt: new Date().toISOString(),
},
};
},
},
executeWorkflow: {
description: "Execute a workflow with specific input data",
execute: async (params: {
workflowId: string;
inputData: any;
executionMode: "test" | "production";
}) => {
return {
success: true,
data: {
executionId: `EXE-${Date.now()}`,
workflowId: params.workflowId,
mode: params.executionMode,
status: "running",
progress: 0,
startedAt: new Date().toISOString(),
estimatedCompletion: new Date(Date.now() + 300000).toISOString(), // 5 minutes
},
};
},
},
getWorkflowStatus: {
description: "Get current status and progress of workflow execution",
execute: async (params: { workflowId: string }) => {
return {
success: true,
data: {
workflowId: params.workflowId,
status: "in-progress",
currentStep: "Data Processing",
stepsCompleted: 3,
totalSteps: 8,
progress: 37.5,
timeElapsed: "2m 15s",
estimatedTimeRemaining: "3m 45s",
lastUpdated: new Date().toISOString(),
},
};
},
},
},
},
});
3. Content Generation Serverβ
await neurolink.addInMemoryMCPServer("content-server", {
server: {
title: "Content Generation Server",
description: "Advanced content creation and management tools",
tools: {
generateSampleText: {
description: "Generate sample text content for testing and development",
execute: async (params: {
topic: string;
length: "short" | "medium" | "long";
style: "formal" | "casual" | "technical";
}) => {
const samples = {
short: `A brief overview of ${params.topic}. This content covers essential information in a ${params.style} style.`,
medium: `This is a comprehensive introduction to ${params.topic}. Written in a ${params.style} style, it covers fundamental concepts, practical applications, and key considerations for understanding ${params.topic} in various contexts.`,
long: `This extensive exploration of ${params.topic} provides detailed analysis written in a ${params.style} style. The content examines multiple perspectives, methodologies, and real-world applications related to ${params.topic}. By thoroughly investigating various aspects and implications, readers gain comprehensive understanding of ${params.topic} and its significance across different fields and industries.`,
};
return {
success: true,
data: {
text: samples[params.length],
topic: params.topic,
length: params.length,
style: params.style,
wordCount: samples[params.length].split(" ").length,
characterCount: samples[params.length].length,
generatedAt: new Date().toISOString(),
},
};
},
},
analyzeContent: {
description: "Analyze text content for various metrics and insights",
execute: async (params: {
text: string;
analysisTypes: Array<
"sentiment" | "readability" | "keywords" | "topics"
>;
}) => {
const results: any = {};
params.analysisTypes.forEach((type) => {
switch (type) {
case "sentiment":
const positiveWords = ["good", "great", "excellent", "amazing"];
const negativeWords = ["bad", "terrible", "awful", "poor"];
const words = params.text.toLowerCase().split(" ");
const positive = words.filter((w) =>
positiveWords.includes(w),
).length;
const negative = words.filter((w) =>
negativeWords.includes(w),
).length;
results.sentiment = {
score: positive - negative,
sentiment:
positive > negative
? "positive"
: negative > positive
? "negative"
: "neutral",
confidence: Math.min(
(Math.abs(positive - negative) / words.length) * 10,
1,
),
};
break;
case "readability":
const sentences = params.text.split(/[.!?]+/).length;
const wordCount = params.text.split(" ").length;
const avgWordsPerSentence = wordCount / sentences;
results.readability = {
wordCount,
sentenceCount: sentences,
avgWordsPerSentence,
readabilityLevel:
avgWordsPerSentence < 15
? "easy"
: avgWordsPerSentence < 25
? "moderate"
: "difficult",
};
break;
case "keywords":
const wordFreq: Record<string, number> = {};
const meaningfulWords = params.text
.toLowerCase()
.replace(/[^\w\s]/g, "")
.split(" ")
.filter((w) => w.length > 3);
meaningfulWords.forEach((word) => {
wordFreq[word] = (wordFreq[word] || 0) + 1;
});
results.keywords = Object.entries(wordFreq)
.sort(([, a], [, b]) => b - a)
.slice(0, 10)
.map(([word, freq]) => ({ word, frequency: freq }));
break;
}
});
return {
success: true,
data: {
textLength: params.text.length,
analysisTypes: params.analysisTypes,
results,
analyzedAt: new Date().toISOString(),
},
};
},
},
},
},
});
Mixed Tool Ecosystem Exampleβ
import { NeuroLink } from "@juspay/neurolink";
import { createTool } from "@juspay/neurolink";
import { z } from "zod";
const neurolink = new NeuroLink();
// 1. Register simple custom tools (extending existing functionality)
neurolink.registerTool(
"enhancedCalculator",
createTool({
description: "Enhanced calculator with scientific and financial functions",
execute: (params: {
expression: string;
mode: "basic" | "scientific" | "financial";
}) => {
if (params.mode === "scientific" && params.expression.includes("sqrt")) {
const num = parseFloat(
params.expression.replace("sqrt(", "").replace(")", ""),
);
return { result: Math.sqrt(num), enhanced: true, mode: params.mode };
}
if (
params.mode === "financial" &&
params.expression.includes("compound")
) {
// Parse: compound(principal, rate, time)
const match = params.expression.match(
/compound\((\d+),\s*([\d.]+),\s*(\d+)\)/,
);
if (match) {
const [, principal, rate, time] = match.map(Number);
const result = principal * Math.pow(1 + rate / 100, time);
return {
result,
enhanced: true,
mode: params.mode,
calculation: "compound_interest",
};
}
}
// Use a safe, restricted math expression evaluator for security
// Requires: pnpm add mathjs
const {
create,
addDependencies,
subtractDependencies,
multiplyDependencies,
divideDependencies,
powDependencies,
sqrtDependencies,
absDependencies,
} from "mathjs";
// Create restricted math environment with only specific functions for security
const dependencies = {
addDependencies,
subtractDependencies,
multiplyDependencies,
divideDependencies,
powDependencies,
sqrtDependencies,
absDependencies,
};
const math = create(dependencies, {
matrix: "Array",
number: "number",
precision: 64,
});
// Additional sanitization for basic mathematical expressions
const sanitizedExpression = params.expression.replace(
/[^0-9+\-*/().\s]/g,
"",
);
if (sanitizedExpression !== params.expression) {
return {
error: "Expression contains invalid characters",
enhanced: false,
mode: params.mode,
};
}
try {
const result = math.evaluate(sanitizedExpression);
return { result, enhanced: false, mode: params.mode };
} catch (error) {
return {
error: `Mathematical expression failed: ${error.message || "Invalid expression"}`,
enhanced: false,
mode: params.mode,
};
}
},
}),
);
// 2. Add complete MCP servers (new functionality domains)
await neurolink.addInMemoryMCPServer("business-intelligence", {
server: {
title: "Business Intelligence Server",
tools: {
generateKPIReport: {
description: "Generate comprehensive KPI reports for business metrics",
execute: async (params: {
metrics: string[];
timeRange: string;
department?: string;
}) => {
return {
success: true,
data: {
reportId: `KPI-${Date.now()}`,
metrics: params.metrics,
timeRange: params.timeRange,
department: params.department || "All",
kpis: {
revenue: "$1.2M",
growth: "+15%",
customerSatisfaction: "94%",
efficiency: "87%",
},
trends: ["Revenue increasing", "Customer satisfaction stable"],
recommendations: [
"Focus on efficiency improvements",
"Expand successful programs",
],
generatedAt: new Date().toISOString(),
},
};
},
},
predictTrends: {
description: "Predict business trends using historical data",
execute: async (params: {
dataPoints: number[];
predictionPeriod: number;
algorithm: "linear" | "exponential" | "seasonal";
}) => {
// Simplified prediction logic
const trend =
params.dataPoints[params.dataPoints.length - 1] >
params.dataPoints[0]
? "upward"
: "downward";
const avgGrowth =
(params.dataPoints[params.dataPoints.length - 1] -
params.dataPoints[0]) /
params.dataPoints.length;
return {
success: true,
data: {
algorithm: params.algorithm,
trend,
predictedGrowth: avgGrowth,
confidence: 0.85,
predictions: Array.from(
{ length: params.predictionPeriod },
(_, i) =>
params.dataPoints[params.dataPoints.length - 1] +
avgGrowth * (i + 1),
),
generatedAt: new Date().toISOString(),
},
};
},
},
},
},
});
// 3. Use the mixed ecosystem
const comprehensiveResult = await neurolink.generate({
input: {
text: `Calculate compound interest for $10000 at 5% for 3 years, then generate a KPI report
for revenue metrics over the last quarter, and predict trends for the next 6 months
using the data points [100, 120, 115, 130, 125, 140]`,
},
provider: "google-ai",
maxTokens: 2000,
});
// The AI will automatically:
// 1. Use enhancedCalculator for compound interest: compound(10000, 5, 3)
// 2. Use generateKPIReport for business metrics
// 3. Use predictTrends for forecasting
// 4. Synthesize all results into a comprehensive response
console.log("AI Response:", comprehensiveResult.content);
console.log("Tools Used:", comprehensiveResult.toolsUsed);
Tool Discovery and Managementβ
// Get comprehensive view of all available tools
const allTools = await neurolink.getAllAvailableTools();
// Group tools by source
const toolsBySource = allTools.reduce(
(acc, tool) => {
const source = tool.serverId || "unknown";
acc[source] = (acc[source] || 0) + 1;
return acc;
},
{} as Record<string, number>,
);
console.log("Tool ecosystem summary:");
console.log("β’ Total tools available:", allTools.length);
console.log("β’ Tools by source:", toolsBySource);
// Get custom tools registered via registerTool()
const customTools = neurolink.getCustomTools();
console.log("β’ Custom tools registered:", customTools.size);
// Get in-memory MCP servers added via addInMemoryMCPServer()
const mcpServers = neurolink.getInMemoryServers();
console.log("β’ In-memory MCP servers:", mcpServers.size);
// Execute tools from any source using unified API
const timeResult = await neurolink.executeTool("getCurrentTime");
const calculationResult = await neurolink.executeTool("enhancedCalculator", {
expression: "compound(5000, 4.5, 2)",
mode: "financial",
});
const reportResult = await neurolink.executeTool("generateKPIReport", {
metrics: ["revenue", "growth"],
timeRange: "Q1-2024",
});
console.log("Tool execution results:");
console.log("β’ Built-in tool:", timeResult.data.time);
console.log("β’ Custom tool:", calculationResult.result);
console.log("β’ MCP server tool:", reportResult.data.reportId);
Adding Remote HTTP MCP Serversβ
Connect to remote MCP servers via HTTP transport with authentication, retry, and rate limiting:
import { NeuroLink } from "@juspay/neurolink";
const neurolink = new NeuroLink();
// Add HTTP MCP server with full configuration
await neurolink.addExternalMCPServer("remote-api", {
transport: "http",
url: "https://api.example.com/mcp",
headers: {
Authorization: "Bearer YOUR_API_TOKEN",
"X-Custom-Header": "value",
},
httpOptions: {
connectionTimeout: 30000,
requestTimeout: 60000,
idleTimeout: 120000,
keepAliveTimeout: 30000,
},
retryConfig: {
maxAttempts: 3,
initialDelay: 1000,
maxDelay: 30000,
backoffMultiplier: 2,
},
rateLimiting: {
requestsPerMinute: 60,
maxBurst: 10,
useTokenBucket: true,
},
});
// Add HTTP server with OAuth 2.1
await neurolink.addExternalMCPServer("oauth-api", {
transport: "http",
url: "https://api.enterprise.com/mcp",
auth: {
type: "oauth2",
oauth: {
clientId: "your-client-id",
clientSecret: "your-client-secret",
authorizationUrl: "https://auth.provider.com/authorize",
tokenUrl: "https://auth.provider.com/token",
redirectUrl: "http://localhost:8080/callback",
scope: "mcp:read mcp:write",
usePKCE: true,
},
},
});
// Use the remote server's tools in AI generation
const result = await neurolink.generate({
input: { text: "Use the remote API to perform analysis" },
provider: "google-ai",
});
HTTP Configuration Options:
| Option | Type | Description |
|---|---|---|
transport | string | Must be "http" for HTTP transport |
url | string | Remote MCP endpoint URL |
headers | object | Custom HTTP headers |
httpOptions | object | Connection timeout settings |
retryConfig | object | Retry with exponential backoff |
rateLimiting | object | Rate limiting configuration |
auth | object | Authentication (OAuth 2.1, Bearer, API Key) |
See MCP HTTP Transport Guide for complete documentation.
Best Practices for MCP Integrationβ
1. Organize Tools by Domainβ
// Group related tools into themed MCP servers
await neurolink.addInMemoryMCPServer("user-management", {
server: {
title: "User Management Server",
tools: {
createUser: {
/* ... */
},
updateUser: {
/* ... */
},
deleteUser: {
/* ... */
},
getUserProfile: {
/* ... */
},
},
},
});
await neurolink.addInMemoryMCPServer("order-processing", {
server: {
title: "Order Processing Server",
tools: {
createOrder: {
/* ... */
},
updateOrderStatus: {
/* ... */
},
calculateShipping: {
/* ... */
},
processPayment: {
/* ... */
},
},
},
});
2. Consistent Error Handlingβ
execute: async (params) => {
try {
const result = await performOperation(params);
return {
success: true,
data: result,
};
} catch (error) {
return {
success: false,
error: error.message,
code: error.code || "OPERATION_FAILED",
timestamp: new Date().toISOString(),
};
}
};
3. Comprehensive Metadataβ
await neurolink.addInMemoryMCPServer("server-id", {
server: {
title: "Human-Readable Server Name",
description: "Detailed description of server purpose",
tools: {
/* ... */
},
},
category: "business-logic", // Group similar servers
metadata: {
version: "2.1.0",
author: "Your Team",
lastUpdated: "2024-01-15",
documentation: "https://docs.yourcompany.com/mcp-servers",
supportContact: "support@yourcompany.com",
},
});
π Additional Resourcesβ
- API Reference - NeuroLink Class
- MCP Integration Guide
- Provider Tool Support
- Test Examples
- MCP SDK Integration Proof Tests
- Real AI-MCP Integration Demo
Start building powerful AI applications with custom tools and MCP servers today! π