docs.businys.dev

Core features

Architecture

@businys/ops is a composable middleware pipeline built around a storage-agnostic adapter interface. Swap adapters without changing middleware. Add or disable layers without touching others.

Middleware pipeline

createMCPProxy() assembles the default pipeline in order. Each layer wraps the next — next() executes the downstream layers and ultimately the actual tool handler.

1
LineagecreateLineageMiddlewareoptional
Causal DAG — outermost so nodes are created even for blocked calls. Optional.
2
TelemetrycreateTelemetryMiddlewareoptional
OpenTelemetry span wraps the full call. Optional — pass a configured Tracer.
3
MeteringcreateMeteringMiddleware
Records ALL calls to storage, including blocked ones. getStats() reflects total traffic.
4
ReputationcreateReputationMiddleware
Blocks or throttles agents with bad reputation scores. Loop detection, burst protection.
5
Rate LimitcreateRateLimitMiddleware
Per-agent + per-group sliding window. Feeds reputation on breach.
6
ConfirmationcreateConfirmationMiddleware
Destructive tool calls require confirm: true in context. Human override hook.
7
RevenuecreateRevenueMiddlewareoptional
Budget check before call; deduct credits on success. After confirmation — unconfirmed calls not charged.
8
AuditcreateAuditMiddleware
Structured JSON audit log to stderr (or custom logger).
9
Custommiddleware: [...]optional
Your custom middleware, appended after built-ins.
↓ Your tool handler

createMCPProxy()

Batteries-included pipeline. All middleware enabled by default except lineage, telemetry, and revenue (which require explicit configuration).

proxy.tsts
import { createMCPProxy, FileAdapter } from "@businys/ops"

const proxy = createMCPProxy({
  // Storage backend (default: MemoryAdapter)
  storage: new FileAdapter({ path: ".ops-data.json" }),

  // Rate limit options
  rateLimit: {
    globalMax: 100,   // calls per window across all agents
    groupMax: 20,     // calls per window per agent group
    windowMs: 60_000, // window size in ms
  },

  // Disable specific layers
  disable: {
    reputation: false,
    rateLimit: false,
    metering: false,
    audit: false,
    confirmation: false,
  },

  // Audit log sink (default: process.stderr)
  auditLog: (entry) => myLogger.info(entry),

  // OpenTelemetry (optional)
  telemetry: { tracer: opentelemetry.trace.getTracer("my-server") },

  // Agent Lineage (optional)
  lineage: { store: new MemoryLineageStore() },

  // MCP Revenue (optional)
  revenue: {
    pricing: {
      default: { perCall: 1 },
      overrides: { expensive_tool: { perCall: 10 } },
    },
  },

  // Custom middleware (appended last)
  middleware: [myCustomMiddleware],
})

Storage adapters

Every adapter implements the StorageAdapter interface. The interface covers call records, stats, reputation, loop detection, SSE subscriptions, and credit wallets. Swap adapters without changing any middleware.

MemoryAdapterMemoryAdapter

In-memory ring buffer. Zero config. Resets on restart. Default when no storage is passed.

When to use: Development, demos, testing
FileAdapterFileAdapter

JSON file on disk. Persists across restarts. Good for local dev and CI.

When to use: Local dev, single-process production
PostgresAdapterPostgresAdapter

PostgreSQL via pg. Requires peer dependency: npm install pg. Full production support.

When to use: Production, multi-process, horizontal scaling
HostedAdapterHostedAdapter

In-memory ring buffer + async batch flush to businysdotdev hosted dashboard. Team-accessible, persistent, with compliance export.

When to use: Hosted dashboard, team visibility, compliance

Custom middleware

Implement the Middleware interface to add your own layers:

my-middleware.tsts
import type { Middleware } from "@businys/ops"

const myMiddleware: Middleware = {
  name: "my-middleware",
  async execute(ctx, next) {
    // ctx.toolName, ctx.agentId, ctx.input, ctx.bearerToken, ...
    console.log("before:", ctx.toolName)

    const result = await next() // call downstream

    console.log("after:", result.isError ? "error" : "ok")
    return result
  },
}

const proxy = createMCPProxy({ middleware: [myMiddleware] })

MiddlewareContext

FieldTypeDescription
toolNamestringThe MCP tool being called
toolGroupstringGroup prefix (e.g. client from client_create)
toolTier"core" | "craft" | "kit"Tool tier classification
methodstringMCP method (e.g. tools/call)
pathstringRequest path
inputRecord<string, unknown>Tool call arguments
agentIdstringAgent identifier from the API key or header
serverNamestringMCP server name
startedAtnumberUnix timestamp ms when the call started
destructivebooleanTrue if the tool is marked as destructive
bearerToken?string | undefinedRaw bearer token from Authorization header