Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill helps with using the @ax-llm/ax TypeScript library for building LLM applications. Use when the user asks about ax(), ai(), f(), s(), agent(), flow(), AxGen, AxAgent, AxFlow, signatures, streaming, or mentions @ax-llm/ax.
.claude/skills/ax-llm-ax-llm/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-19 | ✗→✓ | ▲ Improved | 86% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 133% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 60% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 145% | 0% |
Ax is a TypeScript library for building LLM-powered applications with type-safe signatures, streaming support, and multi-provider compatibility.
> Detailed skills available: ax-ai (providers, routing, adaptive balancing), ax-typesafe (Typesafe/Jev decisions, criteria, native scoring), ax-signature (signatures/types), ax-gen (generators), ax-agent (core agents/tools), ax-agent-rlm (agent runtime/RLM/delegation), ax-agent-observability (callbacks/logs/usage), ax-agent-memory-skills (recall and dynamic skill loading), ax-agent-optimize (agent tuning/eval), ax-flow (workflows), ax-gepa (top-level optimize(...), BootstrapFewShot -> GEPA, Pareto optimization).
typescript// Prefer factory functions: ax(), ai(), agent(), flow(); avoid class constructors. import { ax, ai, f, s, fn, agent, flow, AxMemory, AxMCPClient } from '@ax-llm/ax'; import { z } from 'zod'; // optional — any Standard Schema v1 library works // AI provider const llm = ai({ name: 'openai', apiKey: process.env.OPENAI_APIKEY }); // Generator (from string signature) const gen = ax('question:string -> answer:string'); // Generator (from fluent signature) const gen = ax( f() .input('question', f.string('User question')) .output('answer', f.string('AI response')) .build() ); // Generator (from zod — Standard Schema v1, also works with valibot/arktype) const zodGen = ax( f() .input(z.object({ question: z.string().describe('User question') })) .output(z.object({ answer: z.string().describe('AI response') })) .build() ); // Reusable signature const sig = s('question:string, context:string[] -> answer:string'); // Agent const myAgent = agent('userInput:string -> response:string', { name: 'helper', description: 'A helpful assistant', }); // Flow const wf = flow<{ input: string }, { output: string }>() .node('step1', 'input:string -> output:string') .execute('step1', (state) => ({ input: state.input })) .returns((state) => ({ output: state.step1Result.output })); // Function tool — native fluent const tool = fn('search') .description('Search the web') .arg('query', f.string('Search query')) .returns(f.string('Search results')) .handler(({ query }) => searchWeb(query)) .build(); // Function tool — zod schema (Standard Schema v1: also works with valibot, arktype) const zodTool = fn('calculateTax') .description('Calculate tax for an amount') .arg(z.object({ amount: z.number().positive().describe('Pre-tax amount in USD'), region: z.enum(['US', 'EU', 'UK']).describe('Tax region'), })) .returns(z.object({ tax: z.number(), total: z.number() })) .handler(async ({ amount }) => ({ tax: amount * 0.1, total: amount * 1.1 })) .build();
typescript// Forward (blocking) const result = await gen.forward(llm, { question: 'What is 2+2?' }); // Streaming for await (const chunk of gen.streamingForward(llm, { question: 'Tell a story' })) { if (chunk.delta.answer) process.stdout.write(chunk.delta.answer); }
Provider capabilities determine which outputs and options apply. Typesafe/Jev supports required boolean/class signatures; use its separate native client for Score and full probabilities, and a generative provider for prose or tools. See the ax-typesafe skill before applying the general generation options below to Jev.
| Goal | Option | Example | |------|--------|---------| | Model override | model | { model: 'gpt-5.4-mini' } | | Temperature | modelConfig.temperature | { modelConfig: { temperature: 0.8 } } | | Max tokens | modelConfig.maxTokens | { modelConfig: { maxTokens: 500 } } | | Retry on failure | maxRetries | { maxRetries: 3 } | | Max agent steps | maxSteps | { maxSteps: 10 } | | Fail fast | fastFail | { fastFail: true } | | Thinking budget | thinkingTokenBudget | { thinkingTokenBudget: 'medium' } | | Show thoughts | showThoughts | { showThoughts: true } | | Context caching | contextCache | { contextCache: { cacheBreakpoint: 'after-examples' } } | | Multi-sampling | sampleCount | { sampleCount: 5 } | | Debug logging | debug | { debug: true } | | Abort signal | abortSignal | { abortSignal: controller.signal } | | Memory | mem | { mem: new AxMemory() } | | Stop function | stopFunction | { stopFunction: 'finalAnswer' } | | Function mode | functionCallMode | { functionCallMode: 'auto' } |
Global runtime defaults can be set with axGlobals and are read live by future AI, AxGen, and AxFlow calls:
typescriptimport { axGlobals, axCreateDefaultColorLogger } from '@ax-llm/ax'; import { metrics, trace } from '@opentelemetry/api'; axGlobals.rateLimiter = async (next, info) => next(); axGlobals.tracer = trace.getTracer('my-app'); axGlobals.meter = metrics.getMeter('my-app'); axGlobals.debug = true; axGlobals.logger = axCreateDefaultColorLogger();
Runtime hooks resolve as: forward/direct-call hooks, enclosing program defaults, child-program defaults, AI-service hooks, then globals snapshotted at operation start. They are native run-scoped values and never enter AxIR JSON state, cache keys, exported state, traces, or optimizer artifacts. Agent and flow forwards carry them through every internal generator and model call without mutating children or leaking across concurrent runs. Limiter failures propagate; tracer, meter, and usage-observer failures are fail-open. customLabels merge by precedence, and abortSignal values are combined so either global or local cancellation works.
typescriptimport { AxMemory } from '@ax-llm/ax'; const memory = new AxMemory(); // Multi-turn conversation await gen.forward(llm, { userMessage: 'My name is Alice' }, { mem: memory }); const r = await gen.forward(llm, { userMessage: 'What is my name?' }, { mem: memory });
typescriptconst classifier = ax('reviewText:string -> sentiment:class "positive, negative, neutral"'); classifier.setExamples([ { reviewText: 'I love this!', sentiment: 'positive' }, { reviewText: 'Terrible.', sentiment: 'negative' }, { reviewText: 'It works.', sentiment: 'neutral' }, ]);
typescriptconst classifier = ax( f() .input('text', f.string()) .output('category', f.class(['spam', 'ham', 'uncertain'])) .output('confidence', f.number().min(0).max(1)) .build() );
typescriptconst extractor = ax( f() .input('text', f.string()) .output('entities', f.object({ people: f.string().array(), organizations: f.string().array(), locations: f.string().array() })) .build() );
typescriptconst analyzer = ax( f() .input('image', f.image('Image to analyze')) .input('question', f.string('Question').optional()) .output('description', f.string()) .output('objects', f.string().array()) .build() ); const result = await analyzer.forward(llm, { image: { mimeType: 'image/jpeg', data: base64Data }, question: 'What objects are in this image?' });
typescriptconst researcher = ax('topic:string -> research:string, keyFacts:string[]'); const writer = ax('research:string, keyFacts:string[] -> article:string'); const research = await researcher.forward(llm, { topic: 'AGI' }); const draft = await writer.forward(llm, { research: research.research, keyFacts: research.keyFacts });
typescriptimport { AxGenerateError, AxAIServiceError, AxAIServiceAbortedError } from '@ax-llm/ax'; try { const result = await gen.forward(llm, { input: 'test' }); } catch (error) { if (error instanceof AxGenerateError) { console.error('Generation failed:', error.details.model, error.details.signature); } else if (error instanceof AxAIServiceAbortedError) { console.log('Request was aborted'); } else if (error instanceof AxAIServiceError) { console.error('AI service error:', error.message); } }
typescriptimport { axCreateDefaultColorLogger, axGlobals } from '@ax-llm/ax'; const result = await gen.forward(llm, { input: 'test' }, { debug: true, logger: axCreateDefaultColorLogger(), // OpenTelemetry tracer: openTelemetryTracer, meter: openTelemetryMeter, }); // Or set live app-wide defaults for future calls: axGlobals.tracer = openTelemetryTracer; axGlobals.meter = openTelemetryMeter;
Use the ax-mcp skill for the complete native client, transport, authentication, catalog, task, subscription, event, and replay workflow.
typescriptimport { AxMCPClient, agent } from '@ax-llm/ax'; import { AxMCPStdioTransport } from '@ax-llm/ax-tools'; // Stdio transport (local MCP server) const transport = new AxMCPStdioTransport({ command: 'npx', args: ['-y', '@modelcontextprotocol/server-memory'], }); const mcpClient = new AxMCPClient(transport, { namespace: 'memory' }); // Native MCP context is initialized once and inherited by all agent stages. const myAgent = agent('userMessage:string -> response:string', { mcp: mcpClient, functionDiscovery: true, contextFields: [], }); const result = await myAgent.forward(llm, { userMessage: 'Remember this.' }); await mcpClient.close(); // caller-owned clients remain caller-owned
typescriptimport { AxMCPStreamableHTTPTransport } from '@ax-llm/ax'; const transport = new AxMCPStreamableHTTPTransport('https://remote.example/mcp', { headers: { 'x-pd-project-id': projectId }, authorization: `Bearer ${accessToken}`, });
mcp and ucp to AxGen, streaming AxGen, chat, AxAgent, AxFlow, optimization, or evaluation options.mcpContext to inject attributed prompts/resources before the first model call.mcpInheritance: 'all' | 'none' | string[] to restrict child programs.mcp.<namespace> and ucp.<namespace>.inspectCatalog() discovers tool/prompt names, concrete resources, and URItemplates from only an endpoint. Resource event sources default to no subscriptions and require an explicit all/URI/selector policy.
toFunction() remains a compatibility adapter only; native Ax execution never uses it.typescriptconst catalog = await mcpClient.inspectCatalog(); const tools = catalog.tools; const prompts = await mcpClient.listPrompts(); const resource = await mcpClient.readResource('docs://guide'); const tasks = await mcpClient.listTasks();
typescriptconst mcpClient = new AxMCPClient(transport, { functionOverrides: [ { name: 'search_documents', updates: { name: 'findDocs', description: 'Search docs' } } ] });
typescriptclass AxGen<IN, OUT> { forward(ai: AxAIService, values: IN, options?: AxProgramForwardOptions): Promise<OUT>; streamingForward(ai: AxAIService, values: IN, options?: AxProgramStreamingForwardOptions): AsyncGenerator<{ delta: Partial<OUT> }>; setExamples(examples: Array<Partial<IN & OUT>>): void; addAssert(fn: (output: OUT) => boolean | string | undefined | Promise<boolean | string | undefined>, message?: string): void; addStreamingAssert(field: keyof OUT, fn: (chunk: string, done?: boolean) => boolean | string | undefined | Promise<boolean | string | undefined>, message?: string): void; addFieldProcessor(field: keyof OUT, fn: (value: any) => any): void; addStreamingFieldProcessor(field: keyof OUT, fn: (chunk: string, ctx: any) => void): void; stop(): void; } class AxAgent<IN, OUT> { forward(ai: AxAIService, values: IN, options?: AxAgentOptions): Promise<OUT>; streamingForward(ai: AxAIService, values: IN, options?: AxAgentOptions): AsyncGenerator<{ delta: Partial<OUT> }>; getFunction(): AxFunction; } class AxFlow<IN, OUT> { node(name: string, signature: string | AxSignature): AxFlow; execute(name: string, mapper: (state) => any): AxFlow; returns(mapper: (state) => OUT): AxFlow; forward(ai: AxAIService, values: IN): Promise<OUT>; }
Use eventRuntime() when notifications, webhooks, timers, or remote tasks should wake or resume an Ax program. Sources publish into an inbox; explicit routes choose observe, invalidate, wake, or resume. Event payloads are never inserted as user messages automatically. See ax-event-runtime.md.
Fetch these for full working code:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-19 | fail→pass | 25,341 | 17,840 | -30% | 1 | 1 | 0% | 2,861 | 5,314 | +86% | 0 | 0 | — |
case-01 | fail→pass | 18,929 | 14,967 | -21% | 1 | 1 | 0% | 2,579 | 5,997 | +133% | 0 | 0 | — |
case-02 | fail→pass | 24,378 | 15,275 | -37% | 1 | 1 | 0% | 3,736 | 5,658 | +51% | 0 | 0 | — |
case-03 | fail→pass | 29,708 | 16,876 | -43% | 1 | 1 | 0% | 3,797 | 6,061 | +60% | 0 | 0 | — |
case-04 | fail→pass | 14,887 | 11,842 | -20% | 1 | 1 | 0% | 1,899 | 4,661 | +145% | 0 | 0 | — |
case-05 | fail→pass | 20,793 | 19,961 | -4% | 1 | 1 | 0% | 2,963 | 5,013 | +69% | 0 | 0 | — |
case-06 | fail→pass | 20,480 | 13,312 | -35% | 1 | 1 | 0% | 2,913 | 5,315 | +82% | 0 | 0 | — |
case-07 | fail→pass | 22,202 | 7,894 | -64% | 1 | 1 | 0% | 2,496 | 4,131 | +66% | 0 | 0 | — |
case-08 | fail→pass | 19,334 | 20,445 | +6% | 1 | 1 | 0% | 2,588 | 5,545 | +114% | 0 | 0 | — |
case-09 | fail→pass | 20,514 | 12,561 | -39% | 1 | 1 | 0% | 2,850 | 4,557 | +60% | 0 | 0 | — |
case-10 | fail→pass | 19,550 | 10,826 | -45% | 1 | 1 | 0% | 1,922 | 4,700 | +145% | 0 | 0 | — |
case-11 | fail→pass | 35,943 | 13,973 | -61% | 1 | 1 | 0% | 2,993 | 5,478 | +83% | 0 | 0 | — |
case-12 | fail→pass | 20,982 | 11,428 | -46% | 1 | 1 | 0% | 2,613 | 5,002 | +91% | 0 | 0 | — |
case-13 | fail→pass | 12,668 | 14,478 | +14% | 1 | 1 | 0% | 2,299 | 5,356 | +133% | 0 | 0 | — |
case-14 | fail→pass | 13,175 | 12,144 | -8% | 1 | 1 | 0% | 2,487 | 4,976 | +100% | 0 | 0 | — |
case-15 | fail→pass | 21,468 | 5,381 | -75% | 1 | 1 | 0% | 2,439 | 4,524 | +85% | 0 | 0 | — |
case-16 | fail→pass | 25,612 | 5,158 | -80% | 1 | 1 | 0% | 3,221 | 4,524 | +40% | 0 | 0 | — |
case-17 | fail→pass | 19,715 | 5,744 | -71% | 1 | 1 | 0% | 2,233 | 4,727 | +112% | 0 | 0 | — |
case-18 | fail→pass | 14,119 | 10,420 | -26% | 1 | 1 | 0% | 2,576 | 4,577 | +78% | 0 | 0 | — |
case-20 | pass→pass | 14,633 | 12,266 | -16% | 1 | 1 | 0% | 1,978 | 4,838 | +145% | 0 | 0 | — |
case-21 | pass→fail | 13,758 | 18,271 | +33% | 1 | 1 | 0% | 2,793 | 6,223 | +123% | 0 | 0 | — |
case-22 | pass→pass | 9,115 | 11,524 | +26% | 1 | 1 | 0% | 1,981 | 5,876 | +197% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +82 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The publisher has shipped newer versions since this run, so these numbers describe v2, not the version currently listed.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/10/2026 | +73% |
Other measured skills in the registry, with their headline benchmark lift.