Utility Functions
The @leanmcp/utils package provides a collection of helper utilities and shared functions for building LeanMCP servers.
Installation
npm install @leanmcp/utils
API Reference
Validation
validateSchema()
Validate JSON data against a schema. Basic implementation for common use cases.
import { validateSchema } from '@leanmcp/utils';
const { valid, errors } = validateSchema(
{ name: 'John', age: 25 },
{
type: 'object',
required: ['name', 'age'],
properties: {
name: { type: 'string' },
age: { type: 'number' }
}
}
);
if (!valid) {
console.error('Validation errors:', errors);
}
For advanced validation, consider using ajv or similar libraries in your project.
JSON Schema to validate against
return
{ valid: boolean; errors?: any[] }
Validation result with optional error array
Format response data based on render type.
import { formatResponse } from '@leanmcp/utils';
const data = { users: [{ name: 'John' }, { name: 'Jane' }] };
const json = formatResponse(data, 'json');
const markdown = formatResponse(data, 'markdown');
const html = formatResponse(data, 'html');
const table = formatResponse(data, 'table');
Output formatOptions: json | markdown | html | table
Examples
// JSON with pretty printing
formatResponse({ name: 'John' }, 'json')
// Returns:
// {
// "name": "John"
// }
// Markdown code block
formatResponse({ name: 'John' }, 'markdown')
// Returns:
// ```json
// {
// "name": "John"
// }
// ```
// HTML pre block
formatResponse({ name: 'John' }, 'html')
// Returns: <pre>{"name":"John"}</pre>
Format array data as a markdown table.
import { formatAsTable } from '@leanmcp/utils';
const users = [
{ name: 'John', age: 25, role: 'Admin' },
{ name: 'Jane', age: 30, role: 'User' },
];
const table = formatAsTable(users);
console.log(table);
// | name | age | role |
// | --- | --- | --- |
// | John | 25 | Admin |
// | Jane | 30 | User |
Array of objects to format as table
Object Utilities
deepMerge()
Deep merge multiple objects.
import { deepMerge } from '@leanmcp/utils';
const target = { a: 1, b: { c: 2 } };
const source1 = { b: { d: 3 } };
const source2 = { e: 4 };
const result = deepMerge(target, source1, source2);
// { a: 1, b: { c: 2, d: 3 }, e: 4 }
target
T extends Record<string, any>
required
Target object (will be mutated)
Merged object (same reference as target)
isObject()
Check if value is a plain object.
import { isObject } from '@leanmcp/utils';
isObject({}) // true
isObject({ a: 1 }) // true
isObject([]) // false
isObject(null) // false
isObject(new Date()) // true (caution!)
true if value is a plain object, false otherwise
Async Utilities
retry()
Retry a function with exponential backoff.
import { retry } from '@leanmcp/utils';
const result = await retry(
async () => {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('Request failed');
return response.json();
},
{
maxRetries: 3,
delayMs: 1000,
backoff: 2
}
);
Retry optionsMaximum number of retry attempts
Initial delay in milliseconds
Backoff multiplier (delay = delayMs * backoff^attempt)
Result from successful execution
Throws
Throws the last error if all retry attempts fail.
sleep()
Sleep for a given duration.
import { sleep } from '@leanmcp/utils';
await sleep(1000); // Wait 1 second
console.log('Done waiting');
Promise that resolves after the delay
timeout()
Add timeout to a promise.
import { timeout } from '@leanmcp/utils';
try {
const result = await timeout(
fetch('https://api.example.com/slow'),
5000,
'API request timed out'
);
} catch (error) {
console.error(error); // 'API request timed out' after 5s
}
Promise to add timeout to
Timeout duration in milliseconds
errorMessage
string
default:"Operation timed out"
Error message for timeout
Promise that rejects if timeout is reached
Function Utilities
debounce()
Create a debounced function.
import { debounce } from '@leanmcp/utils';
const debouncedSave = debounce(async (data: string) => {
await saveToDatabase(data);
}, 500);
// Called multiple times, but only executes once after 500ms
debouncedSave('a');
debouncedSave('ab');
debouncedSave('abc'); // Only this executes
func
T extends (...args: any[]) => any
required
Function to debounce
Wait duration in milliseconds
return
(...args: Parameters<T>) => void
Debounced function
throttle()
Create a throttled function.
import { throttle } from '@leanmcp/utils';
const throttledLog = throttle((message: string) => {
console.log(message);
}, 1000);
// Called multiple times, but only first call executes
throttledLog('a'); // Executes
throttledLog('b'); // Ignored
throttledLog('c'); // Ignored
// Wait 1s
throttledLog('d'); // Executes
func
T extends (...args: any[]) => any
required
Function to throttle
Throttle limit in milliseconds
return
(...args: Parameters<T>) => void
Throttled function
String Utilities
truncate()
Truncate string to maximum length.
import { truncate } from '@leanmcp/utils';
truncate('Hello World', 8) // 'Hello...'
truncate('Hello World', 8, '…') // 'Hello W…'
truncate('Short', 10) // 'Short'
Maximum length including suffix
camelToKebab()
Convert camelCase to kebab-case.
import { camelToKebab } from '@leanmcp/utils';
camelToKebab('helloWorld') // 'hello-world'
camelToKebab('getUserById') // 'get-user-by-id'
camelToKebab('XMLParser') // 'xml-parser'
kebabToCamel()
Convert kebab-case to camelCase.
import { kebabToCamel } from '@leanmcp/utils';
kebabToCamel('hello-world') // 'helloWorld'
kebabToCamel('get-user-by-id') // 'getUserById'
kebabToCamel('xml-parser') // 'xmlParser'
capitalize()
Capitalize first letter of string.
import { capitalize } from '@leanmcp/utils';
capitalize('hello') // 'Hello'
capitalize('world') // 'World'
capitalize('UPPER') // 'UPPER'
String with first letter capitalized
Environment Utilities
parseEnv()
Parse environment variables with type coercion.
import { parseEnv } from '@leanmcp/utils';
// String (default)
const host = parseEnv(process.env.HOST, 'localhost', 'string');
// Number
const port = parseEnv(process.env.PORT, 3000, 'number');
// Boolean
const debug = parseEnv(process.env.DEBUG, false, 'boolean');
value
string | undefined
required
Environment variable value
Default value if undefined or parse fails
type
'string' | 'number' | 'boolean'
default:"string"
Type to coerce to
Examples
// Number parsing
parseEnv('3000', 8080, 'number') // 3000
parseEnv('invalid', 8080, 'number') // 8080 (fallback)
parseEnv(undefined, 8080, 'number') // 8080 (fallback)
// Boolean parsing
parseEnv('true', false, 'boolean') // true
parseEnv('false', true, 'boolean') // false
parseEnv('yes', false, 'boolean') // false (not 'true')
generateId()
Generate a unique ID.
import { generateId } from '@leanmcp/utils';
const id1 = generateId(); // 'l3k5j2h-a8d9f2g'
const id2 = generateId('user'); // 'user-l3k5j2h-a8d9f2g'
const id3 = generateId('session'); // 'session-l3k5j2h-b7e1k3m'
Optional prefix for the ID
Usage Examples
import { formatResponse, formatAsTable } from '@leanmcp/utils';
import { Tool } from '@leanmcp/core';
class DataService {
@Tool({ description: 'Get user list' })
async getUsers() {
const users = await db.getUsers();
// Return formatted table
return formatAsTable(users);
}
@Tool({ description: 'Get user details' })
async getUser(args: { id: string }) {
const user = await db.getUser(args.id);
// Return formatted JSON
return formatResponse(user, 'markdown');
}
}
Retry with Exponential Backoff
import { retry } from '@leanmcp/utils';
import { Tool } from '@leanmcp/core';
class ExternalApiService {
@Tool({ description: 'Fetch data from external API' })
async fetchData(args: { endpoint: string }) {
return await retry(
async () => {
const response = await fetch(`https://api.example.com/${args.endpoint}`);
if (!response.ok) throw new Error('Request failed');
return response.json();
},
{ maxRetries: 3, delayMs: 1000, backoff: 2 }
);
}
}
Debounced Search
import { debounce } from '@leanmcp/utils';
class SearchService {
private debouncedSearch = debounce(async (query: string) => {
return await this.performSearch(query);
}, 300);
@Tool({ description: 'Search' })
async search(args: { query: string }) {
return await this.debouncedSearch(args.query);
}
private async performSearch(query: string) {
// Actual search implementation
return await db.search(query);
}
}
Configuration with Environment Variables
import { parseEnv, deepMerge } from '@leanmcp/utils';
const defaultConfig = {
server: {
host: 'localhost',
port: 3000,
},
features: {
debug: false,
},
};
const config = deepMerge(defaultConfig, {
server: {
host: parseEnv(process.env.HOST, 'localhost', 'string'),
port: parseEnv(process.env.PORT, 3000, 'number'),
},
features: {
debug: parseEnv(process.env.DEBUG, false, 'boolean'),
},
});
Best Practices
1. Use Retry for External APIs
Wrap unreliable external API calls in retry logic:
await retry(() => externalApi.call(), { maxRetries: 3 });
Prevent excessive API calls from user input:
const search = debounce(performSearch, 300);
3. Add Timeouts to Long Operations
Prevent hanging requests:
await timeout(longOperation(), 30000, 'Operation took too long');
Use schema validation for tool arguments:
const { valid, errors } = validateSchema(args, schema);
if (!valid) throw new Error(`Invalid input: ${errors}`);
See Also