Skip to main content

Server Adapter Configuration Reference

This document provides a comprehensive reference for all configuration options available in NeuroLink Server Adapters.

Configuration via CLI

In addition to programmatic configuration, NeuroLink provides CLI commands to view and manage server settings.

Viewing Configuration

# Show all configuration
neurolink server config

# Output as JSON
neurolink server config --format json

# Get specific value
neurolink server config --get defaultPort
neurolink server config --get cors.enabled
neurolink server config --get rateLimit.maxRequests

Modifying Configuration

# Set configuration values
neurolink server config --set defaultPort=8080
neurolink server config --set defaultFramework=express
neurolink server config --set cors.enabled=true
neurolink server config --set rateLimit.maxRequests=200

# Reset to defaults
neurolink server config --reset

Configuration File Location

CLI configuration is stored at:

  • Config file: ~/.neurolink/server-config.json
  • Server state: ~/.neurolink/server-state.json

CLI vs Programmatic Configuration

AspectCLI ConfigProgrammatic Config
PersistenceFile-based, survives restartsIn-memory, per-instance
ScopeGlobal defaultsPer-server instance
Use CaseDevelopment, quick changesProduction, fine-grained control

The CLI configuration provides default values that can be overridden programmatically:

// CLI defaults are used when not specified
const server = await createServer(neurolink, {
framework: "hono", // Overrides CLI default
// port uses CLI default if not specified
});

ServerAdapterConfig

The main configuration object for server adapters.

type ServerAdapterConfig = {
port?: number;
host?: string;
basePath?: string;
cors?: CORSConfig;
rateLimit?: RateLimitConfig;
bodyParser?: BodyParserConfig;
logging?: LoggingConfig;
shutdown?: ShutdownConfig;
redaction?: RedactionConfig;
timeout?: number;
enableMetrics?: boolean;
enableSwagger?: boolean;
disableBuiltInHealth?: boolean;
};

Core Options

OptionTypeDefaultDescription
portnumber3000Server port to listen on
hoststring"0.0.0.0"Server host/interface to bind
basePathstring"/api"Base path prefix for all routes
timeoutnumber30000Request timeout in milliseconds
enableMetricsbooleantrueEnable metrics endpoint
enableSwaggerbooleanfalseEnable OpenAPI/Swagger documentation (see below)
disableBuiltInHealthbooleanfalseDisable built-in health routes

OpenAPI/Swagger Documentation (enableSwagger)

When enableSwagger is set to true, the server exposes interactive API documentation endpoints:

EndpointDescription
GET {basePath}/openapi.jsonOpenAPI 3.1 specification in JSON format
GET {basePath}/openapi.yamlOpenAPI 3.1 specification in YAML format
GET {basePath}/docsInteractive Swagger UI documentation

Example URLs (with default basePath /api):

  • http://localhost:3000/api/openapi.json
  • http://localhost:3000/api/openapi.yaml
  • http://localhost:3000/api/docs

The Swagger UI provides an interactive interface where you can:

  • Browse all available API endpoints
  • View request/response schemas
  • Test API calls directly from the browser
  • Download the OpenAPI specification

Security Consideration: In production environments, consider disabling enableSwagger to prevent exposing internal API structure. Alternatively, protect the documentation endpoints with authentication middleware.

Example: Basic Configuration

import { createServer } from "@juspay/neurolink";

const server = await createServer(neurolink, {
config: {
port: 8080,
host: "127.0.0.1",
basePath: "/v1/api",
timeout: 60000,
enableSwagger: true,
},
});

CORS Configuration

type CORSConfig = {
enabled?: boolean;
origins?: string[];
methods?: string[];
headers?: string[];
credentials?: boolean;
maxAge?: number;
};
OptionTypeDefaultDescription
enabledbooleantrueEnable CORS support
originsstring[]["*"]Allowed origins
methodsstring[]["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]Allowed HTTP methods
headersstring[]["Content-Type", "Authorization"]Allowed headers
credentialsbooleanfalseAllow credentials
maxAgenumber86400Preflight cache max age in seconds

Security Warning: The default wildcard origin ["*"] allows requests from any domain. In production environments, always specify explicit allowed origins to prevent unauthorized cross-origin requests.

Example: Restrictive CORS

const server = await createServer(neurolink, {
config: {
cors: {
enabled: true,
origins: ["https://myapp.com", "https://staging.myapp.com"],
methods: ["GET", "POST"],
headers: ["Content-Type", "Authorization", "X-Request-ID"],
credentials: true,
maxAge: 3600,
},
},
});

Rate Limit Configuration

type RateLimitConfig = {
enabled?: boolean;
windowMs?: number;
maxRequests?: number;
message?: string;
skipPaths?: string[];
keyGenerator?: (ctx: ServerContext) => string;
};
OptionTypeDefaultDescription
enabledbooleantrueEnable rate limiting
windowMsnumber900000 (15 min)Time window in milliseconds
maxRequestsnumber100Maximum requests per window
messagestring"Too many requests..."Error message when limit exceeded
skipPathsstring[][]Paths to exclude from rate limiting
keyGeneratorfunctionIP-basedCustom function to generate rate limit key

Example: Custom Rate Limiting

const server = await createServer(neurolink, {
config: {
rateLimit: {
enabled: true,
windowMs: 60000, // 1 minute
maxRequests: 30,
skipPaths: ["/api/health", "/api/ready", "/api/version"],
keyGenerator: (ctx) => {
// Rate limit by API key instead of IP
return (
ctx.headers["x-api-key"] ||
ctx.headers["x-forwarded-for"] ||
"unknown"
);
},
},
},
});

Body Parser Configuration

type BodyParserConfig = {
enabled?: boolean;
maxSize?: string;
jsonLimit?: string;
urlEncoded?: boolean;
};
OptionTypeDefaultDescription
enabledbooleantrueEnable body parsing
maxSizestring"10mb"Maximum body size
jsonLimitstring"10mb"JSON body size limit
urlEncodedbooleantrueEnable URL-encoded body parsing

Example: Large Payload Support

const server = await createServer(neurolink, {
config: {
bodyParser: {
enabled: true,
maxSize: "50mb",
jsonLimit: "50mb",
urlEncoded: true,
},
},
});

Logging Configuration

type LoggingConfig = {
enabled?: boolean;
level?: "debug" | "info" | "warn" | "error";
includeBody?: boolean;
includeResponse?: boolean;
};
OptionTypeDefaultDescription
enabledbooleantrueEnable request logging
levelstring"info"Log level
includeBodybooleanfalseInclude request body in logs
includeResponsebooleanfalseInclude response body in logs

Example: Debug Logging

const server = await createServer(neurolink, {
config: {
logging: {
enabled: true,
level: "debug",
includeBody: true,
includeResponse: true,
},
},
});

Shutdown Configuration

type ShutdownConfig = {
gracefulShutdownTimeoutMs?: number;
drainTimeoutMs?: number;
forceClose?: boolean;
};
OptionTypeDefaultDescription
gracefulShutdownTimeoutMsnumber30000Maximum time to wait for graceful shutdown (30 sec)
drainTimeoutMsnumber15000Time to drain existing connections (15 sec)
forceClosebooleantrueForce close connections after timeout

Example: Custom Shutdown Timeouts

const server = await createServer(neurolink, {
config: {
shutdown: {
gracefulShutdownTimeoutMs: 60000, // 60 seconds for long-running requests
drainTimeoutMs: 30000, // 30 seconds to drain connections
forceClose: true, // Force close after timeout
},
},
});

Redaction Configuration

The redaction system provides automatic sanitization of sensitive data in logs and responses. This feature is opt-in and must be explicitly enabled.

type RedactionConfig = {
enabled?: boolean;
additionalFields?: string[];
preserveFields?: string[];
redactToolArgs?: boolean;
redactToolResults?: boolean;
placeholder?: string;
};
OptionTypeDefaultDescription
enabledbooleanfalseEnable redaction (opt-in)
additionalFieldsstring[][]Extra field names to redact
preserveFieldsstring[][]Fields to exclude from redaction
redactToolArgsbooleantrueRedact tool arguments (when enabled)
redactToolResultsbooleantrueRedact tool results (when enabled)
placeholderstring"[REDACTED]"Replacement text for redacted values

Default Redacted Fields

When redaction is enabled, the following fields are redacted by default:

  • apiKey
  • token
  • authorization
  • credentials
  • password
  • secret
  • request
  • args
  • result

Example: Custom Redaction

const server = await createServer(neurolink, {
config: {
redaction: {
enabled: true,
additionalFields: ["ssn", "creditCard", "bankAccount"],
preserveFields: ["request"], // Allow 'request' field to pass through
redactToolArgs: true,
redactToolResults: false, // Keep tool results visible
placeholder: "***",
},
},
});

Example: Minimal Redaction

const server = await createServer(neurolink, {
config: {
redaction: {
enabled: true,
// Uses all defaults - redacts apiKey, token, password, etc.
},
},
});

Middleware Configuration

Authentication Middleware

import { createAuthMiddleware } from "@juspay/neurolink";

const authMiddleware = createAuthMiddleware({
type: "bearer", // 'bearer' | 'api-key' | 'basic' | 'custom'
validate: async (token, ctx) => {
// Return user info or null
const user = await verifyJWT(token);
return user ? { id: user.id, email: user.email, roles: user.roles } : null;
},
headerName: "Authorization", // Optional: custom header name
skipPaths: ["/api/health", "/api/ready"],
errorMessage: "Invalid authentication token",
});

server.registerMiddleware(authMiddleware);

Auth Types

TypeHeader FormatDescription
bearerAuthorization: Bearer <token>JWT/OAuth token
api-keyX-API-Key: <key>API key authentication
basicAuthorization: Basic <base64>HTTP Basic auth
customCustomUse extractToken function

Rate Limit Middleware

import {
createRateLimitMiddleware,
createSlidingWindowRateLimitMiddleware,
} from "@juspay/neurolink";

// Fixed window rate limiter
const rateLimiter = createRateLimitMiddleware({
maxRequests: 100,
windowMs: 15 * 60 * 1000,
skipPaths: ["/api/health"],
});

// Sliding window rate limiter (more accurate)
const slidingRateLimiter = createSlidingWindowRateLimitMiddleware({
maxRequests: 100,
windowMs: 15 * 60 * 1000,
subWindows: 10, // Number of sub-windows for smoothing
});

server.registerMiddleware(rateLimiter);

Cache Middleware

import { createCacheMiddleware, InMemoryCacheStore } from "@juspay/neurolink";

const cacheMiddleware = createCacheMiddleware({
ttlMs: 60 * 1000, // 1 minute cache
maxSize: 1000, // Max cached entries
methods: ["GET"], // Only cache GET requests
excludePaths: ["/api/agent/execute", "/api/agent/stream"],
includeQuery: true, // Include query params in cache key
ttlByPath: {
"/api/tools": 5 * 60 * 1000, // 5 minutes for tools
"/api/version": 60 * 60 * 1000, // 1 hour for version
},
});

server.registerMiddleware(cacheMiddleware);

Cache Response Headers

The cache middleware adds these headers to responses:

HeaderDescriptionExample
X-CacheCache statusHIT or MISS
X-Cache-AgeSeconds since cached (on HIT)45
Cache-ControlCaching directive (on MISS)max-age=300

Validation Middleware

import {
createRequestValidationMiddleware,
createFieldValidator,
} from "@juspay/neurolink";

// JSON Schema validation
const validationMiddleware = createRequestValidationMiddleware({
body: {
type: "object",
properties: {
input: { type: "string", minLength: 1 },
provider: { type: "string" },
},
required: ["input"],
},
});

// Field-level validation
const fieldValidator = createFieldValidator({
required: ["name", "email"],
types: { name: "string", email: "string", age: "number" },
validators: {
email: (value) => typeof value === "string" && value.includes("@"),
age: (value) => typeof value === "number" && value >= 0,
},
});

server.registerMiddleware(validationMiddleware);

Role-Based Access Control

import { createRoleMiddleware } from "@juspay/neurolink";

// Require any of the specified roles
const adminMiddleware = createRoleMiddleware({
requiredRoles: ["admin", "superuser"],
requireAll: false, // Any role matches
errorMessage: "Admin access required",
});

// Require all specified roles
const superAdminMiddleware = createRoleMiddleware({
requiredRoles: ["admin", "superuser"],
requireAll: true, // All roles required
});

Framework-Specific Options

Hono

import { ServerAdapterFactory } from "@juspay/neurolink";

const server = await ServerAdapterFactory.createHono(neurolink, {
port: 3000,
// Hono uses @hono/node-server under the hood
});

For more details, see the Hono Guide.

Express

const server = await ServerAdapterFactory.createExpress(neurolink, {
port: 3000,
// Express-specific middleware can be added via getFrameworkInstance()
});

const app = server.getFrameworkInstance();
app.use(customExpressMiddleware);

For more details, see the Express Guide.

Fastify

const server = await ServerAdapterFactory.createFastify(neurolink, {
port: 3000,
// Fastify plugins can be registered on the instance
});

const fastify = server.getFrameworkInstance();
await fastify.register(customFastifyPlugin);

For more details, see the Fastify Guide.

Koa

const server = await ServerAdapterFactory.createKoa(neurolink, {
port: 3000,
// Koa middleware can be added via getFrameworkInstance()
});

const app = server.getFrameworkInstance();
app.use(customKoaMiddleware);

For more details, see the Koa Guide.

Complete Configuration Example

import {
createServer,
createAuthMiddleware,
createRateLimitMiddleware,
createCacheMiddleware,
} from "@juspay/neurolink";
import { NeuroLink } from "@juspay/neurolink";

const neurolink = new NeuroLink({
defaultProvider: "openai",
});

const server = await createServer(neurolink, {
framework: "hono",
config: {
port: 8080,
host: "0.0.0.0",
basePath: "/v1",
timeout: 120000,
enableSwagger: true,
cors: {
enabled: true,
origins: ["https://app.example.com"],
credentials: true,
},
rateLimit: {
enabled: true,
maxRequests: 1000,
windowMs: 3600000,
},
bodyParser: {
maxSize: "25mb",
},
logging: {
level: "info",
},
},
});

// Add custom middleware
server.registerMiddleware(
createAuthMiddleware({
type: "bearer",
validate: async (token) => verifyToken(token),
skipPaths: ["/v1/health", "/v1/ready"],
}),
);

server.registerMiddleware(
createCacheMiddleware({
ttlMs: 300000,
methods: ["GET"],
}),
);

// Start server
await server.start();
console.log(`Server running on http://localhost:8080`);

Environment Variables

The server adapters respect these environment variables:

VariableDescriptionDefault
PORTServer port3000
HOSTServer host0.0.0.0
NODE_ENVEnvironment modedevelopment
npm_package_versionPackage version (for health endpoint)unknown

Configuration Validation

Invalid configuration will throw errors at initialization:

// This will throw: "Invalid port number"
const server = await createServer(neurolink, {
config: { port: -1 },
});

// This will throw: "Invalid rate limit configuration"
const server = await createServer(neurolink, {
config: { rateLimit: { maxRequests: -100 } },
});

Always validate your configuration in development before deploying to production.

API Endpoints

The server adapters expose the following endpoints (all prefixed with basePath, default /api):

Health Endpoints

MethodEndpointDescription
GET/healthBasic health check
GET/readyReadiness probe
GET/liveLiveness probe
GET/versionVersion information

Agent Endpoints

MethodEndpointDescription
POST/agent/executeExecute agent with input
POST/agent/streamStream agent response

Tool Endpoints

MethodEndpointDescription
GET/toolsList available tools
POST/tools/:nameExecute a specific tool
GET/tools/:nameGet tool metadata

MCP Endpoints

MethodEndpointDescription
GET/mcp/serversList MCP servers
POST/mcp/executeExecute MCP tool
GET/mcp/healthMCP subsystem health check

Memory Endpoints

MethodEndpointDescription
GET/memory/sessionsList memory sessions
GET/memory/sessions/:idGet session details
DELETE/memory/sessions/:idDelete a session
DELETE/memory/sessionsClear all sessions
GET/memory/healthMemory subsystem health check

OpenAPI Endpoints (when enableSwagger: true)

MethodEndpointDescription
GET/openapi.jsonOpenAPI 3.1 spec (JSON)
GET/openapi.yamlOpenAPI 3.1 spec (YAML)
GET/docsSwagger UI

Lifecycle Management

Server adapters implement a comprehensive lifecycle management system that enables graceful startup, connection tracking, and orderly shutdown. Understanding the lifecycle is essential for production deployments.

Lifecycle States

The server adapter progresses through 9 distinct lifecycle states:

StateDescription
uninitializedInitial state before initialize() is called
initializingFramework and routes are being set up
initializedSetup complete, ready to start
startingServer is binding to port and preparing to listen
runningServer is actively accepting and processing requests
drainingNo new connections accepted, existing ones finishing
stoppingServer is closing after connections drained
stoppedServer has completely shut down
errorAn error occurred during any state transition

State Transition Diagram

                    ┌─────────────────┐
│ uninitialized │◄──────────────────────────────────┐
└────────┬────────┘ │
│ initialize() │
▼ │
┌─────────────────┐ │
│ initializing │ │
└────────┬────────┘ │
│ success │
▼ │
┌─────────────────┐ │
│ initialized │◄──────────────────────────────────┤
└────────┬────────┘ │
│ start() │
▼ │
┌─────────────────┐ │
│ starting │ │
└────────┬────────┘ │
│ bound to port │
▼ │
┌─────────────────┐ │
│ running │ │
└────────┬────────┘ │
│ stop() │
▼ │
┌─────────────────┐ │
│ draining │──── drain timeout ────┐ │
└────────┬────────┘ │ │
│ connections drained │ │
▼ ▼ │
┌─────────────────┐ forceClose() │
│ stopping │◄──────────────────────┘ │
└────────┬────────┘ │
│ server closed │
▼ │
┌─────────────────┐ │
│ stopped │───────────────────────────────────┘
└─────────────────┘ (can restart)

Any state ─────────► ┌─────────────────┐
(on error) │ error │
└─────────────────┘

Valid State Transitions

Current StateValid Next StatesTrigger
uninitializedinitializinginitialize() called
initializinginitialized, errorSetup completes or fails
initializedstartingstart() called
startingrunning, errorPort bound or bind fails
runningdrainingstop() called
drainingstoppingConnections drained/timeout
stoppingstopped, errorServer closes
stoppedinitializinginitialize() for restart
error(terminal, requires new instance)N/A

InvalidLifecycleStateError

Attempting an operation in an invalid state throws InvalidLifecycleStateError:

import { InvalidLifecycleStateError } from "@juspay/neurolink";

try {
await server.start(); // Called when already running
} catch (error) {
if (error instanceof InvalidLifecycleStateError) {
console.log(`Operation: ${error.operation}`);
console.log(`Current state: ${error.currentState}`);
console.log(`Expected states: ${error.expectedStates.join(", ")}`);
}
}
// Output:
// Operation: start
// Current state: running
// Expected states: initialized, stopped

Querying Lifecycle State

// Get current lifecycle state
const state = server.getLifecycleState();
console.log(`Server state: ${state}`);

// Get full server status including lifecycle
const status = server.getStatus();
console.log({
running: status.running,
lifecycleState: status.lifecycleState,
activeConnections: status.activeConnections,
uptime: status.uptime,
});

Connection Tracking

Server adapters track active connections to enable graceful shutdown. This is essential for ensuring in-flight requests complete before the server stops.

TrackedConnection Type

type TrackedConnection = {
/** Unique connection identifier */
id: string;

/** Timestamp when connection was created */
createdAt: number;

/** Underlying socket or connection object */
socket?: unknown;

/** Request ID if associated with a request */
requestId?: string;

/** Whether the connection is currently processing a request */
isActive?: boolean;
};

Connection Tracking Methods

Framework adapters use these methods internally to track connections:

// Track a new connection (called by adapter implementations)
protected trackConnection(
id: string,
socket?: unknown,
requestId?: string
): void;

// Untrack a connection when completed
protected untrackConnection(id: string): void;

// Get count of active connections (public API)
public getActiveConnectionCount(): number;

Monitoring Active Connections

// Check active connections before shutdown
const activeCount = server.getActiveConnectionCount();
console.log(`Active connections: ${activeCount}`);

// Include in health check responses
app.get("/health", (req, res) => {
const status = server.getStatus();
res.json({
status: "ok",
connections: status.activeConnections,
lifecycleState: status.lifecycleState,
});
});

Graceful Shutdown

Graceful shutdown ensures all in-flight requests complete before the server stops, preventing data loss and providing a better user experience.

Shutdown Process

When stop() is called, the server follows this sequence:

  1. Stop Accepting Connections

    • Server stops accepting new connections
    • New requests receive connection refused
    • State transitions to draining
  2. Drain Existing Connections

    • Wait for in-flight requests to complete
    • Monitor activeConnections count
    • Timeout after drainTimeoutMs
  3. Handle Drain Timeout

    • If connections remain after drainTimeoutMs:
      • If forceClose: true, forcibly close all connections
      • If forceClose: false, throw DrainTimeoutError
  4. Close Server

    • Close the underlying server
    • State transitions to stopping, then stopped
    • Overall timeout enforced by gracefulShutdownTimeoutMs

Shutdown Configuration Options

OptionTypeDefaultDescription
gracefulShutdownTimeoutMsnumber30000Maximum total shutdown duration
drainTimeoutMsnumber15000Maximum time to wait for connections to complete
forceClosebooleantrueIf true, forcibly closes connections after drainTimeoutMs expires

Shutdown Example

import { createServer } from "@juspay/neurolink";
import { ShutdownTimeoutError, DrainTimeoutError } from "@juspay/neurolink";

const server = await createServer(neurolink, {
framework: "hono",
config: {
port: 3000,
shutdown: {
gracefulShutdownTimeoutMs: 30000,
drainTimeoutMs: 15000,
forceClose: true,
},
},
});

await server.initialize();
await server.start();

// Handle shutdown signals
async function shutdown(signal: string): Promise<void> {
console.log(`Received ${signal}, starting graceful shutdown...`);
console.log(`Active connections: ${server.getActiveConnectionCount()}`);

try {
await server.stop();
console.log("Server stopped gracefully");
process.exit(0);
} catch (error) {
if (error instanceof ShutdownTimeoutError) {
console.error(
`Shutdown timed out with ${error.remainingConnections} connections`,
);
} else if (error instanceof DrainTimeoutError) {
console.error(
`Drain timed out with ${error.remainingConnections} connections`,
);
} else {
console.error("Shutdown error:", error);
}
process.exit(1);
}
}

process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));

Kubernetes Graceful Shutdown

For Kubernetes deployments, configure appropriate timeouts:

const server = await createServer(neurolink, {
framework: "hono",
config: {
port: 3000,
shutdown: {
// Should be less than Kubernetes terminationGracePeriodSeconds
gracefulShutdownTimeoutMs: 25000,
drainTimeoutMs: 20000,
forceClose: true,
},
},
});

In your Kubernetes deployment:

spec:
terminationGracePeriodSeconds: 30 # Must be > gracefulShutdownTimeoutMs
containers:
- name: api
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # Allow load balancer to drain

Shutdown Errors

ErrorDescriptionHandling
ShutdownTimeoutErrorOverall shutdown exceeded gracefulShutdownTimeoutMsForce close was attempted if forceClose: true
DrainTimeoutErrorDrain exceeded drainTimeoutMs with forceClose: falseConnections remain open
InvalidLifecycleStateErrorCalled stop() when not in running stateServer was not running

Server Events

Server adapters emit events at key lifecycle points. Subscribe to these events for monitoring, logging, and custom behaviors.

Available Events

type ServerAdapterEvents = {
/** Emitted when server initialization completes */
initialized: {
config: ServerAdapterConfig;
routeCount: number;
middlewareCount: number;
};

/** Emitted when server starts listening */
started: {
port: number;
host: string;
timestamp: Date;
};

/** Emitted when server stops */
stopped: {
uptime: number;
timestamp: Date;
};

/** Emitted for each incoming request */
request: {
requestId: string;
method: string;
path: string;
timestamp: Date;
};

/** Emitted for each outgoing response */
response: {
requestId: string;
statusCode: number;
duration: number;
timestamp: Date;
};

/** Emitted when an error occurs */
error: {
requestId?: string;
error: Error;
timestamp: Date;
};
};

Subscribing to Events

const server = await createServer(neurolink, {
framework: "hono",
config: { port: 3000 },
});

// Lifecycle events
server.on("initialized", (event) => {
console.log(`Server initialized with ${event.routeCount} routes`);
});

server.on("started", (event) => {
console.log(`Server started on ${event.host}:${event.port}`);
});

server.on("stopped", (event) => {
console.log(`Server stopped after ${event.uptime}ms uptime`);
});

// Request/response events for monitoring
server.on("request", (event) => {
console.log(`[${event.requestId}] ${event.method} ${event.path}`);
});

server.on("response", (event) => {
console.log(
`[${event.requestId}] ${event.statusCode} in ${event.duration}ms`,
);
});

// Error tracking
server.on("error", (event) => {
console.error(`[${event.requestId ?? "unknown"}] Error:`, event.error);
});

await server.initialize();
await server.start();

Event-Based Metrics Collection

import { createServer } from "@juspay/neurolink";

const metrics = {
requests: 0,
responses: 0,
errors: 0,
totalDuration: 0,
};

const server = await createServer(neurolink, {
framework: "hono",
config: { port: 3000 },
});

server.on("request", () => {
metrics.requests++;
});

server.on("response", (event) => {
metrics.responses++;
metrics.totalDuration += event.duration;
});

server.on("error", () => {
metrics.errors++;
});

// Expose metrics endpoint
server.registerRoute({
method: "GET",
path: "/metrics/custom",
handler: async () => ({
requests: metrics.requests,
responses: metrics.responses,
errors: metrics.errors,
avgDuration:
metrics.responses > 0 ? metrics.totalDuration / metrics.responses : 0,
activeConnections: server.getActiveConnectionCount(),
lifecycleState: server.getLifecycleState(),
}),
description: "Custom application metrics",
tags: ["monitoring"],
});

OpenAPI Customization

NeuroLink includes a powerful OpenAPI 3.1 specification generator that creates comprehensive API documentation from your server routes. This section covers how to customize the generated OpenAPI specification.

OpenAPIGenerator Class

The OpenAPIGenerator class is the core component for generating OpenAPI specifications.

import { OpenAPIGenerator } from "@juspay/neurolink";

const generator = new OpenAPIGenerator({
// Customize API info
info: {
title: "My Custom API",
version: "2.0.0",
description: "Custom API description",
},
// Server configuration
servers: [
{ url: "https://api.example.com", description: "Production" },
{ url: "https://staging-api.example.com", description: "Staging" },
],
// Base path for all routes
basePath: "/v2",
// Include security schemes in the spec
includeSecurity: true,
// Add custom tags
additionalTags: [
{ name: "custom", description: "Custom endpoints" },
{ name: "analytics", description: "Analytics and reporting" },
],
// Add custom schemas
customSchemas: {
CustomRequest: {
type: "object",
properties: {
customField: { type: "string" },
},
},
},
// Pass routes to document
routes: myRouteDefinitions,
});

// Generate the specification
const spec = generator.generate();

// Export as JSON or YAML
const jsonSpec = generator.toJSON(true); // pretty-printed
const yamlSpec = generator.toYAML();

Constructor Options

OptionTypeDefaultDescription
infoobject-Override API info (title, version, description)
serversarray-Custom server URLs
basePathstring/apiBase path for all routes
includeSecuritybooleantrueInclude security schemes
additionalTagsarray[]Extra API tags
customSchemasobject{}Custom JSON schemas to add
routesarray[]Route definitions to document

Generator Methods

// Add routes after initialization
generator.addRoutes(routeArray);
generator.addRoute(singleRoute);

// Generate the OpenAPI spec
const spec = generator.generate();

// Export formats
const json = generator.toJSON(true); // pretty-printed JSON
const yaml = generator.toYAML(); // YAML format

Built-in Schemas

NeuroLink provides pre-defined JSON schemas for common API types.

Error and Response Schemas

import { ErrorResponseSchema, TokenUsageSchema } from "@juspay/neurolink";

// ErrorResponseSchema
// - error.code (string): Error code identifier
// - error.message (string): Human-readable error message
// - error.details (object): Additional error details
// - metadata.timestamp (date-time): Error timestamp
// - metadata.requestId (string): Request identifier

// TokenUsageSchema
// - input (integer): Input/prompt tokens
// - output (integer): Output/completion tokens
// - total (integer): Total tokens used
// - cacheCreationTokens (integer): Tokens for cache creation
// - cacheReadTokens (integer): Tokens read from cache
// - reasoning (integer): Tokens used for reasoning
// - cacheSavingsPercent (number): Cache savings percentage

Agent Schemas

import {
AgentExecuteRequestSchema,
AgentExecuteResponseSchema,
AgentInputSchema,
ProviderInfoSchema,
} from "@juspay/neurolink";

// AgentExecuteRequestSchema
// - input (string | object): Agent input
// - provider (string): AI provider to use
// - model (string): Specific model
// - systemPrompt (string): System prompt
// - temperature (number): Sampling temperature (0-2)
// - maxTokens (integer): Maximum tokens to generate
// - tools (string[]): Tool names to enable
// - stream (boolean): Enable streaming
// - sessionId (string): Session ID for memory
// - userId (string): User ID for context

// AgentExecuteResponseSchema
// - content (string): Generated text content
// - provider (string): Provider used
// - model (string): Model used
// - usage (TokenUsage): Token usage
// - toolCalls (array): Tool calls made
// - finishReason (string): Completion reason

Tool Schemas

import {
ToolDefinitionSchema,
ToolExecuteRequestSchema,
ToolExecuteResponseSchema,
ToolListResponseSchema,
ToolParameterSchema,
} from "@juspay/neurolink";

// ToolDefinitionSchema
// - name (string): Tool name
// - description (string): Tool description
// - source (string): Tool source (builtin, external, custom)
// - parameters (object): Tool parameters schema

// ToolExecuteRequestSchema
// - name (string): Tool name to execute
// - arguments (object): Tool arguments
// - sessionId (string): Session context
// - userId (string): User context

// ToolExecuteResponseSchema
// - success (boolean): Execution success
// - data: Result data
// - error (string): Error message if failed
// - duration (number): Execution duration in ms

MCP Server Schemas

import {
MCPServerStatusSchema,
MCPServersListResponseSchema,
MCPServerToolSchema,
} from "@juspay/neurolink";

// MCPServerStatusSchema
// - serverId (string): Server ID
// - name (string): Server name
// - status (string): connected | disconnected | error | connecting
// - toolCount (integer): Number of available tools
// - lastHealthCheck (date-time): Last health check timestamp
// - error (string): Error message if in error state

Health Schemas

import {
HealthResponseSchema,
ReadyResponseSchema,
MetricsResponseSchema,
} from "@juspay/neurolink";

// HealthResponseSchema
// - status (string): ok | degraded | unhealthy
// - timestamp (date-time): Check timestamp
// - uptime (integer): Server uptime in ms
// - version (string): Server version

// ReadyResponseSchema
// - ready (boolean): Overall readiness
// - timestamp (date-time): Check timestamp
// - services.neurolink (boolean): SDK status
// - services.tools (boolean): Tool registry status
// - services.externalServers (boolean): MCP servers status

Template Functions

The OpenAPI module provides template functions for creating operations and parameters.

Operation Templates

import {
createGetOperation,
createPostOperation,
createStreamingPostOperation,
createDeleteOperation,
} from "@juspay/neurolink";

// GET operation
const getOp = createGetOperation(
"List users", // summary
"Get all users in the system", // description
["users"], // tags
"UserListResponse", // response schema reference
[limitParam, offsetParam], // optional parameters
);

// POST operation
const postOp = createPostOperation(
"Create user", // summary
"Create a new user", // description
["users"], // tags
"CreateUserRequest", // request schema reference
"UserResponse", // response schema reference
[authHeader], // optional parameters
);

// Streaming POST operation
const streamOp = createStreamingPostOperation(
"Stream data", // summary
"Stream data via SSE", // description
["streaming"], // tags
"StreamRequest", // request schema reference
);

// DELETE operation
const deleteOp = createDeleteOperation(
"Delete user", // summary
"Delete a user by ID", // description
["users"], // tags
[userIdParam], // parameters
);

Parameter Templates

import {
createPathParameter,
createQueryParameter,
createHeaderParameter,
CommonParameters,
} from "@juspay/neurolink";

// Path parameter
const userIdParam = createPathParameter(
"userId", // name
"User ID", // description
{ type: "string", format: "uuid" }, // schema (optional)
);

// Query parameter
const searchParam = createQueryParameter(
"q", // name
"Search query", // description
{ type: "string" }, // schema (optional)
false, // required (optional, default: false)
);

// Header parameter
const apiKeyHeader = createHeaderParameter(
"X-API-Key", // name
"API key for authentication", // description
true, // required (optional, default: false)
);

// Pre-defined common parameters
const { sessionId, serverName, toolName } = CommonParameters;
const { limitQuery, offsetQuery, searchQuery } = CommonParameters;
const { requestIdHeader, authorizationHeader } = CommonParameters;

Security Schemes

NeuroLink provides pre-defined security schemes for common authentication methods.

import {
BearerSecurityScheme,
ApiKeySecurityScheme,
BasicSecurityScheme,
} from "@juspay/neurolink";

// Bearer token (JWT)
// {
// type: "http",
// scheme: "bearer",
// bearerFormat: "JWT",
// description: "JWT Bearer token authentication"
// }

// API Key (header)
// {
// type: "apiKey",
// in: "header",
// name: "X-API-Key",
// description: "API key authentication via header"
// }

// Basic auth
// {
// type: "http",
// scheme: "basic",
// description: "HTTP Basic authentication"
// }

Using Security Schemes

const generator = new OpenAPIGenerator({
includeSecurity: true, // Enables security schemes
});

const spec = generator.generate();
// spec.components.securitySchemes = {
// bearerAuth: BearerSecurityScheme,
// apiKeyAuth: ApiKeySecurityScheme
// }
// spec.security = [{ bearerAuth: [] }, { apiKeyAuth: [] }]

Custom Schema Registration

Add custom schemas to extend the built-in types.

const generator = new OpenAPIGenerator({
customSchemas: {
// Simple custom schema
MyCustomType: {
type: "object",
required: ["id", "name"],
properties: {
id: { type: "string", format: "uuid" },
name: { type: "string", minLength: 1 },
metadata: { type: "object", additionalProperties: true },
},
},

// Extended schema referencing built-in types
ExtendedAgentResponse: {
allOf: [
{ $ref: "#/components/schemas/AgentExecuteResponse" },
{
type: "object",
properties: {
customField: { type: "string" },
analytics: { $ref: "#/components/schemas/AnalyticsData" },
},
},
],
},

// Enum schema
Priority: {
type: "string",
enum: ["low", "medium", "high", "critical"],
description: "Priority level",
},
},
});

Complete Customization Example

import {
OpenAPIGenerator,
createGetOperation,
createPostOperation,
createPathParameter,
createQueryParameter,
BearerSecurityScheme,
} from "@juspay/neurolink";

// Create generator with full customization
const generator = new OpenAPIGenerator({
info: {
title: "Enterprise AI API",
version: "3.0.0",
description: `
Enterprise AI API provides secure access to AI capabilities.

## Features
- Multi-model AI generation
- Real-time streaming
- Tool execution
- Conversation memory

## Rate Limits
- Standard: 1000 req/hour
- Enterprise: Unlimited
`.trim(),
},

servers: [
{ url: "https://api.enterprise.com/v3", description: "Production" },
{ url: "https://api.staging.enterprise.com/v3", description: "Staging" },
{ url: "http://localhost:3000/v3", description: "Local Development" },
],

basePath: "/v3",
includeSecurity: true,

additionalTags: [
{ name: "analytics", description: "Usage analytics and reporting" },
{ name: "admin", description: "Administrative operations" },
{ name: "webhooks", description: "Webhook management" },
],

customSchemas: {
// Custom request types
WebhookConfig: {
type: "object",
required: ["url", "events"],
properties: {
url: { type: "string", format: "uri" },
events: {
type: "array",
items: { type: "string", enum: ["execute", "error", "complete"] },
},
secret: { type: "string", description: "HMAC secret for validation" },
},
},

// Custom response types
AnalyticsReport: {
type: "object",
properties: {
period: { type: "string" },
totalRequests: { type: "integer" },
averageLatency: { type: "number" },
tokenUsage: { $ref: "#/components/schemas/TokenUsage" },
topModels: {
type: "array",
items: {
type: "object",
properties: {
model: { type: "string" },
count: { type: "integer" },
},
},
},
},
},
},
});

// Add custom routes
generator.addRoute({
method: "GET",
path: "/v3/analytics",
description: "Get usage analytics for the specified period",
tags: ["analytics"],
responseSchema: { $ref: "#/components/schemas/AnalyticsReport" },
auth: true,
});

generator.addRoute({
method: "POST",
path: "/v3/webhooks",
description: "Register a new webhook endpoint",
tags: ["webhooks"],
requestSchema: { $ref: "#/components/schemas/WebhookConfig" },
responseSchema: {
type: "object",
properties: {
id: { type: "string" },
status: { type: "string" },
},
},
auth: true,
});

// Generate the specification
const spec = generator.generate();

// Export to file
import { writeFileSync } from "fs";
writeFileSync("openapi.json", generator.toJSON(true));
writeFileSync("openapi.yaml", generator.toYAML());

Factory Functions

For quick OpenAPI generation without instantiating the class:

import {
createOpenAPIGenerator,
generateOpenAPISpec,
generateOpenAPIFromConfig,
} from "@juspay/neurolink";

// Create generator with config
const generator = createOpenAPIGenerator({
basePath: "/api",
includeSecurity: true,
});

// Generate spec directly from routes
const spec = generateOpenAPISpec(routes, {
info: { title: "My API", version: "1.0.0" },
});

// Generate from server adapter configuration
const spec = generateOpenAPIFromConfig(serverConfig, routes);
// Automatically uses host/port from serverConfig

All Available Schemas

The OpenAPISchemas registry provides access to all built-in schemas:

import { OpenAPISchemas } from "@juspay/neurolink";

// Common
OpenAPISchemas.ErrorResponse;
OpenAPISchemas.TokenUsage;

// Agent
OpenAPISchemas.AgentInput;
OpenAPISchemas.AgentExecuteRequest;
OpenAPISchemas.AgentExecuteResponse;
OpenAPISchemas.ToolCall;
OpenAPISchemas.ProviderInfo;

// Tools
OpenAPISchemas.ToolParameter;
OpenAPISchemas.ToolDefinition;
OpenAPISchemas.ToolListResponse;
OpenAPISchemas.ToolExecuteRequest;
OpenAPISchemas.ToolExecuteResponse;

// MCP
OpenAPISchemas.MCPServerTool;
OpenAPISchemas.MCPServerStatus;
OpenAPISchemas.MCPServersListResponse;

// Memory
OpenAPISchemas.ConversationMessage;
OpenAPISchemas.Session;
OpenAPISchemas.SessionsListResponse;

// Health
OpenAPISchemas.HealthResponse;
OpenAPISchemas.ReadyResponse;
OpenAPISchemas.MetricsResponse;