| name | api-utils |
| description | API reference for all utilities exported from `@cyanheads/mcp-ts-core/utils`. Use when looking up utility method signatures, options, peer dependencies, or usage patterns.
|
| metadata | {"author":"cyanheads","version":"2.5","audience":"external","type":"reference"} |
Overview
Utility exports from @cyanheads/mcp-ts-core/utils. Utilities with complex APIs have dedicated reference files; simpler utilities are documented inline below.
Tier 3 = optional peer dependency. Install as needed (e.g., bun add js-yaml). All Tier 3 methods are async (lazy-load deps on first call).
References
| Reference | Path | Covers |
|---|
| Formatting | references/formatting.md | markdown(), MarkdownBuilder, diffFormatter, tableFormatter, treeFormatter — builder patterns, option types, style variants, usage examples |
| Parsing | references/parsing.md | yamlParser, xmlParser, csvParser, jsonParser, pdfParser, dateParser, frontmatterParser — method signatures, option types, peer deps, Allow flags, PDF workflows |
| Security | references/security.md | sanitization, RateLimiter, IdGenerator — config types, method details, sensitive fields, usage examples |
@cyanheads/mcp-ts-core/utils — network
| Export | API | Notes |
|---|
fetchWithTimeout | (url, timeoutMs, context: RequestContext, options?: FetchWithTimeoutOptions) -> Promise<Response> | Wraps fetch with AbortController timeout. FetchWithTimeoutOptions extends RequestInit (minus signal) and adds rejectPrivateIPs?: boolean, expectedStatuses?: number[] (listed non-2xx statuses logged at debug not error, still thrown), and signal?: AbortSignal (external cancellation). On a non-2xx, error.data carries status/body plus the legacy statusCode/responseBody aliases (identical values; consolidating in a future major). SSRF guard (best-effort, not hard isolation): blocks RFC 1918, loopback, link-local, CGNAT, cloud metadata. DNS validation on Node, Bun, and Cloudflare Workers under nodejs_compat; hostname-only fallback otherwise. Manual redirect following (max 5) with per-hop SSRF check. DNS rebinding / TOCTOU gap — the validation lookup and fetch's own resolution are independent; pair with egress controls or a DNS-pinning fetch proxy for strong isolation. Error/log redaction: URLs written into thrown errors and log lines are reduced to origin + pathname — the query string (where API keys commonly ride: ?api-key=…, ?api_key=…) never reaches the client or the logs. The actual request still uses the full URL. |
withRetry | <T>(fn: () => Promise<T>, options?: RetryOptions) -> Promise<T> | Executes fn with exponential backoff. Retries on transient errors (ServiceUnavailable, Timeout, RateLimited); non-transient errors fail immediately. Honors an upstream Retry-After on data.retryAfter (delta-seconds or HTTP-date) over exponential backoff, capped at maxDelayMs; a requested wait beyond the cap fails fast rather than sleeping. On exhaustion, enriches the final error with attempt count in message and data.retryAttempts. Place the retry boundary around the full pipeline (fetch + parse), not just the network call. RetryOptions: maxRetries (default 3), baseDelayMs (default 1000), maxDelayMs (default 30000), jitter (default 0.25), operation (log label), context (RequestContext), signal (AbortSignal), isTransient (custom predicate). |
httpErrorFromResponse | (response: Response, options?: HttpErrorFromResponseOptions) -> Promise<McpError> | Maps an HTTP Response to a properly classified McpError — full status table including 401/403/408/422/429/5xx, body capture (truncated), retry-after header, optional cause. error.data carries status/body plus the legacy statusCode/responseBody aliases (identical values), so a consumer can classify either helper's error without knowing which raised it. Use this instead of hand-rolling if (status === 429) ... ladders. Reads the response body — clone() first if you need it elsewhere. HttpErrorFromResponseOptions: service? (logical name in message, e.g. 'NCBI'), captureBody? (default true), bodyLimit? (default 500), data? (extra fields merged into error.data), cause?, codeOverride? (per-status mapping override). Pairs naturally with withRetry — both classify codes the same way. |
httpStatusToErrorCode | (status: number) -> JsonRpcErrorCode | undefined | Sync status → code lookup. Returns undefined for 1xx/2xx/3xx. Use when you need just the code without a Response object handy. |
@cyanheads/mcp-ts-core/utils — pagination
| Export | API | Notes |
|---|
extractCursor | (params?) -> string | undefined | Extracts opaque cursor string from MCP request params. Checks params.cursor then params._meta.cursor. Returns undefined when no cursor is present. Does not decode. |
paginateArray | <T>(items, cursorStr, defaultPageSize, maxPageSize, context: RequestContext) -> PaginatedResult<T> | Decodes cursor, slices array, returns { items, nextCursor?, totalCount }. nextCursor omitted on last page. Throws McpError(InvalidParams) on invalid cursor. |
encodeCursor | (state: PaginationState) -> string | Encodes { offset, limit, ...extra } to opaque base64url string. |
decodeCursor | (cursor, context: RequestContext) -> PaginationState | Decodes opaque base64url cursor. Throws McpError(InvalidParams) if malformed. |
@cyanheads/mcp-ts-core/utils — runtime
| Export | API | Notes |
|---|
runtimeCaps | RuntimeCapabilities object | Snapshot at import time. Fields: isNode, isBun, isWorkerLike, isBrowserLike, hasProcess, hasBuffer, hasTextEncoder, hasPerformanceNow. All booleans. Never throws. |
@cyanheads/mcp-ts-core/utils — scheduling
| Export | API | Notes |
|---|
schedulerService | .schedule(id, schedule, taskFunction, description) -> Promise<Job> .start(id) -> void .stop(id) -> void .remove(id) -> void .listJobs() -> Job[] | Async schedule() — Tier 3 peer: node-cron. Node-only (throws ConfigurationError in Workers). Jobs start in stopped state; call start(id) to activate. Skips overlapping executions. Each tick gets fresh RequestContext. Job: { id, schedule, description, isRunning, task }. taskFunction: (context: RequestContext) => void | Promise<void>. |
@cyanheads/mcp-ts-core/utils — types
The utils export includes two type guards. The full set of guards lives in the internal module and is not part of the public API.
| Export | Signature | Notes |
|---|
isErrorWithCode | (error: unknown) -> error is Error & { code: unknown } | Type guard — true when value is an Error instance with a code property |
isRecord | (value: unknown) -> value is Record<string, unknown> | Type guard for plain objects (non-null, non-array) |
@cyanheads/mcp-ts-core/utils — logger
| Export | API | Notes |
|---|
Logger | Class | The Logger class itself. Use Logger.getInstance() if needed; most consumers use the logger singleton. |
logger | Logger instance (wraps Pino). .debug(msg, ctx?) .info(msg, ctx?) .notice(msg, ctx?) .warning(msg, ctx?) .error(msg, errorOrCtx, ctx?) .crit(msg, errorOrCtx, ctx?) .alert(msg, errorOrCtx, ctx?) .emerg(msg, errorOrCtx, ctx?) .fatal(msg, errorOrCtx, ctx?) | Global structured logger. Use ctx.log in handlers instead. logger is for lifecycle/background contexts (startup, shutdown, setup()). Auto-redacts sensitive fields. Note: .error() and higher accept (msg, Error, ctx?) or (msg, ctx?) — the second arg is overloaded. .fatal() is an alias for .emerg(). Full RFC 5424 severity set. |
McpLogLevel | Type | Log level union type for typing level variables. |
@cyanheads/mcp-ts-core/utils — requestContext
| Export | API | Notes |
|---|
requestContextService | .createRequestContext(params?) -> RequestContext .withAuthInfo(authInfo, parentContext?) -> RequestContext | Creates tracing context with requestId, timestamp, traceId, spanId, tenantId, auth. Internal — most consumers use ctx from handlers. |
RequestContext | Type: { requestId, timestamp, operation?, traceId?, spanId?, tenantId?, auth?, [key: string]: unknown } | Request tracing metadata. |
CreateRequestContextParams | Type: { parentContext?, additionalContext?, operation?, [key: string]: unknown } | Params accepted by createRequestContext. Named fields get special merge handling; other properties spread directly onto the context. |
AuthContext | Type: { clientId, scopes, sub, token, tenantId?, [key: string]: unknown } | Structured auth data attached to RequestContext.auth after token verification. |
createRequestContext merge order (later wins, except requestId/timestamp): parentContext → spread rest params → additionalContext (strips requestId/timestamp) → pinned requestId/timestamp → resolved tenantId → operation → OTel traceId/spanId.
withAuthInfo(authInfo, parentContext?) builds a context and populates auth from a validated token. Does not write to AsyncLocalStorage — ALS propagation is the auth middleware's responsibility.
@cyanheads/mcp-ts-core/utils — errorHandler
| Export | API | Notes |
|---|
ErrorHandler | .tryCatch<T>(fn, opts) -> Promise<T> .handleError(error, opts) -> Error .classifyOnly(error) -> { code, message, data? } .determineErrorCode(error) -> JsonRpcErrorCode .mapError(error, mappings, defaultFactory?) -> T | Error .formatError(error) -> Record<string, unknown> | Service-level error handling. tryCatch wraps async or sync fn, logs via handleError, and always rethrows. No .tryCatchSync(). Use in services, NOT in tool handlers (those throw raw McpError). tryCatch accepts Omit<ErrorHandlerOptions, 'rethrow'> — required: operation. Optional: context, errorCode, input, includeStack, critical, errorMapper. handleError accepts the full ErrorHandlerOptions including rethrow. |
@cyanheads/mcp-ts-core/utils — encoding
Cross-platform encoding utilities. No peer deps.
| Export | Signature | Notes |
|---|
arrayBufferToBase64 | (buffer: ArrayBuffer) -> string | Encodes an ArrayBuffer to base64. Uses Buffer on Node/Bun; chunked btoa on Workers/browsers to avoid stack overflow on large buffers. |
stringToBase64 | (str: string) -> string | UTF-8 string → base64. Uses Buffer.from(str, 'utf-8') on Node/Bun; TextEncoder + arrayBufferToBase64 on Workers. |
base64ToString | (base64: string) -> string | base64 → UTF-8 string. Uses Buffer on Node/Bun; atob + TextDecoder on Workers. Throws if input is not valid base64. |
@cyanheads/mcp-ts-core/utils — token counting
Dependency-free heuristic token estimation. No native/WASM deps.
| Export | Signature | Notes |
|---|
countTokens | async (text: string, context?: RequestContext, model?: string) -> Promise<number> | Estimates tokens in a plain string. Normalizes whitespace, divides by charsPerToken. Returns 0 for empty/whitespace input. Falls back to gpt-4o heuristics when model is omitted or unrecognized. |