npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ottrix

v2.1.0

Published

TypeScript LLM agent framework: ReAct loop, tool calling, MCP, multi-agent workflows, memory, guardrails. Anthropic Claude, OpenAI, Ollama via fetch — vendor-neutral, zero SDK deps.

Readme

ottrix

TypeScript framework for production LLM agents — the ottrix npm package is the core of the Ottrix monorepo. ReAct loop, structured output, tool calling, memory, guardrails, observability, evals, multi-agent workflows, and MCP client support. Vendor-neutral: Anthropic Claude, OpenAI-compatible APIs, and Ollama via native fetch (no vendor SDK required).

All packages: docs/README.md

npm version Node

Version: 2.1.0 · Node: ≥20 · License: MIT

Full project docs: github.com/ashwinpaulallen/ottrix · Core module guides: docs/README.md · Monorepo index: ../../docs/README.md


Install

npm install ottrix

Optional peers (install when you need them):

npm install zod          # structured output, Zod tools, schema eval scorers
npm install js-yaml      # full YAML workflow files
npm install ioredis pg   # Redis/Postgres DAG state stores

Standalone packages (install when you need them):

npm install @ottrix/exporter-langfuse   # Langfuse trace export
npm install @ottrix/exporter-braintrust # Braintrust trace export
npm install @ottrix/exporter-otel       # OpenTelemetry OTLP export
npm install @ottrix/mcp-server          # MCP server hosting + ottrix-serve CLI
export ANTHROPIC_API_KEY=your-api-key-here

Quick start

import { createAgent } from 'ottrix';

const agent = createAgent({
  provider: 'anthropic',
  apiKey: process.env.ANTHROPIC_API_KEY!,
  systemPrompt: 'You are a helpful assistant.',
});

const { response } = await agent.run('What is 2 + 2?');
console.log(response);

Guardrails (prompt injection, PII, budgets) are on by default. One-liner helper:

import { quickAgent } from 'ottrix';

const answer = await quickAgent('Summarize TypeScript in one sentence.', {
  provider: 'anthropic',
  apiKey: process.env.ANTHROPIC_API_KEY!,
});

Subpath exports

| Import | Purpose | Key APIs | |--------|---------|----------| | ottrix | Main barrel | createAgent, Agent, runWith, AuditEmitter, orchestration, guardrails | | ottrix/agent | Agent internals | Agent, planner, reflector, structured output | | ottrix/providers | LLM backends | createAnthropicProvider, ProviderRegistry, CircuitBreaker | | ottrix/tools | Tool calling | FunctionTool, createTool, ToolRegistry, MCPClient | | ottrix/memory | Agent memory | WorkingMemory, SemanticMemory, ObservationalMemory | | ottrix/orchestration | Multi-agent | SequentialWorkflow, SupervisorWorkflow, DAGBuilder | | ottrix/guardrails | Safety | createGuardrails, configureBudgets, PromptInjectionGuardrail | | ottrix/observability | Telemetry | getTelemetry, getLogger, replay utilities | | ottrix/evals | Evaluation | evaluate, EvalRunner, scorers, EvalReporter | | ottrix/exporters/webhook | Webhooks | WebhookExporter | | ottrix/types | Types only | Shared TypeScript contracts |

| Standalone package | Purpose | Key APIs | |--------------------|---------|----------| | @ottrix/mcp-server | MCP hosting | serveMCP, MCPServer, ottrix-serve CLI | | @ottrix/exporter-otel | OpenTelemetry | OtelExporter, createOtelExporter | | @ottrix/exporter-langfuse | Langfuse | LangfuseExporter | | @ottrix/exporter-braintrust | Braintrust | BraintrustExporter |

CLI: npx ottrix-serve --transport stdio — requires @ottrix/mcp-server.


Usage by module

Agent and structured output

import { createAgent } from 'ottrix';
import { z } from 'zod';

const schema = z.object({ name: z.string(), age: z.number() });
const agent = createAgent({ provider: 'anthropic' });

const { parsedOutput } = await agent.run('Introduce Ada Lovelace', { outputSchema: schema });

Streaming:

for await (const event of agent.stream('Explain quantum entanglement briefly.')) {
  if (event.type === 'text') process.stdout.write(String((event.data as { text: string }).text));
}

Tools

import { createAgent, createTool } from 'ottrix';
import { z } from 'zod';

const weather = createTool({
  name: 'get_weather',
  description: 'Get weather for a city',
  input: z.object({ city: z.string() }),
  execute: async ({ city }) => ({ city, tempF: 72 }),
});

const agent = createAgent({ provider: 'anthropic', tools: [weather] });

Providers and fallback

import { ProviderRegistry, createAnthropicProvider, createOpenAIProvider } from 'ottrix/providers';

const registry = new ProviderRegistry()
  .register('anthropic', createAnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }))
  .register('openai', createOpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }))
  .setFallbackChain(['anthropic', 'openai']);

| Provider | createAgent | API key env | Default model | |----------|---------------|-------------|---------------| | Anthropic | provider: 'anthropic' | ANTHROPIC_API_KEY | claude-sonnet-4-20250514 | | OpenAI-compatible | provider: 'openai' | OPENAI_API_KEY | gpt-4o | | Ollama | provider: 'ollama' | none | llama3.1 |

Run context

Propagate runId, orgId, and custom fields through agent runs without threading parameters:

import { createAgent, runWith } from 'ottrix';

await runWith({ runId: 'req-42', orgId: 'acme-corp' }, () =>
  agent.run('Summarize Q1 earnings'),
);

MCP server

Install @ottrix/mcp-server — the MCP client (MCPClient) remains in core.

import { serveMCP } from '@ottrix/mcp-server';
import { ToolRegistry } from 'ottrix';

const registry = new ToolRegistry();
registry.register(myTool);
await serveMCP({ name: 'my-tools', version: '2.0.0', toolRegistry: registry, transport: 'stdio' });

Workflows

import { createSupervisor, DAGBuilder } from 'ottrix';

const supervisor = createSupervisor({
  provider,
  workers: {
    researcher: { systemPrompt: 'You research.', description: 'Finds facts' },
    writer: { systemPrompt: 'You write.', description: 'Drafts prose' },
  },
});

const dag = new DAGBuilder()
  .addStep('draft', { name: 'Draft', execute: async (input) => `Draft: ${input}` })
  .addStep('review', { name: 'Review', suspend: true, execute: async (i) => i, dependencies: ['draft'] })
  .build();

Observability

import { getTelemetry } from 'ottrix';
import { LangfuseExporter } from '@ottrix/exporter-langfuse';
import { createOtelExporter } from '@ottrix/exporter-otel';

getTelemetry().setExporter(new LangfuseExporter({
  publicKey: process.env.LANGFUSE_PUBLIC_KEY!,
  secretKey: process.env.LANGFUSE_SECRET_KEY!,
}));

getTelemetry().addExporter(createOtelExporter('jaeger', { serviceName: 'my-agent' }));

Evals

import { evaluate, ExactMatchScorer } from 'ottrix/evals';

const report = await evaluate({
  agent,
  dataset: [{ input: 'Capital of France?', expectedOutput: 'Paris' }],
  scorers: [new ExactMatchScorer()],
});

Audit trail

import { AuditEmitter, FileSink, HmacSigner, useAudit } from 'ottrix';

useAudit(new AuditEmitter({
  sink: new FileSink({ path: './audit.jsonl' }),
  signer: new HmacSigner({ secret: process.env.AUDIT_SECRET! }),
  redact: ['args.token', 'args.password'],
}));

Configuration

Environment variables (legacy AGENT_KIT_* / AGENTIC_* aliases still supported):

| Variable | Description | |----------|-------------| | OTTRIX_PROVIDER | Default provider (anthropic, openai, ollama) | | OTTRIX_MODEL | Default model id | | OTTRIX_MAX_STEPS | Max ReAct iterations (default 10) | | OTTRIX_CONFIG_PATH | Path to .ottrixrc.json | | OTTRIX_TELEMETRY_EXPORTER | console, webhook, memory, none (Langfuse/Braintrust/OTel → standalone packages) |

import { loadConfig, createAgent } from 'ottrix';

const { config } = loadConfig();
const agent = createAgent({ provider: config.defaultProvider, model: config.defaultModel });

Framework adapters

Use ottrix directly in any Node.js HTTP framework, or install a first-party adapter:

| Adapter | Package | Status | |---------|---------|--------| | NestJS | @ottrix/nestjs | Published — docs | | Express | @ottrix/express | Implemented — README · docs | | Fastify | @ottrix/fastify | Implemented — README · docs | | Hono | @ottrix/hono | Implemented — README · docs | | Next.js | @ottrix/nextjs | Planned — docs |


Version constant

import { OTTRIX_VERSION } from 'ottrix';
// '2.1.0'

Links

MIT © ashwinpaulallen