ai-core/middleware
The middleware skill in TanStack AI provides lifecycle hooks for intercepting and controlling chat streams at multiple points: initialization, iteration, chunk streaming, tool execution, and completion. Use it to implement analytics tracking, error handling, request logging, tool call validation, and structured output configuration across AI chat operations.
git clone --depth 1 https://github.com/TanStack/ai /tmp/ai-core-middleware && cp -r /tmp/ai-core-middleware/packages/ai/skills/ai-core/middleware ~/.claude/skills/ai-core-middlewareSKILL.md
# Middleware
> **Dependency note:** This skill builds on ai-core. Read it first for critical rules.
## Setup — Analytics Tracking Middleware
```typescript
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const stream = chat({
adapter: openaiText('gpt-5.2'),
messages,
middleware: [
{
onStart: (ctx) => {
console.log('Chat started:', ctx.model)
},
onFinish: (ctx, info) => {
trackAnalytics({ model: ctx.model, tokens: info.usage?.totalTokens })
},
onError: (ctx, info) => {
reportError(info.error)
},
},
],
})
return toServerSentEventsResponse(stream)
```
## Hooks Reference
Every hook receives a `ChatMiddlewareContext` as its first argument, which provides
`requestId`, `streamId`, `phase`, `iteration`, `chunkIndex`, `model`, `provider`,
`signal`, `abort()`, `defer()`, and more.
| Hook | When | Second Argument |
| -------------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `onConfig` | Once at startup (`init`) + once per iteration (`beforeModel`) + once at structured-output boundary | `ChatMiddlewareConfig` (return partial to merge) |
| `onStructuredOutputConfig` | Once at the structured-output boundary (only when `chat({ outputSchema })`) | `StructuredOutputMiddlewareConfig` (return partial) |
| `onStart` | Once after initial `onConfig` | none |
| `onIteration` | Start of each agent loop iteration | `IterationInfo` |
| `onChunk` | Every streamed chunk | `StreamChunk` (return void/chunk/chunk[]/null) |
| `onBeforeToolCall` | Before each tool executes | `ToolCallHookContext` (return decision or void) |
| `onAfterToolCall` | After each tool executes | `AfterToolCallInfo` |
| `onToolPhaseComplete` | After all tool calls in an iteration | `ToolPhaseCompleteInfo` |
| `onUsage` | When `RUN_FINISHED` includes usage data | `UsageInfo` |
| `onFinish` | Run completed normally | `FinishInfo` |
| `onAbort` | Run was aborted | `AbortInfo` |
| `onError` | Unhandled error occurred | `ErrorInfo` |
Terminal hooks (`onFinish`, `onAbort`, `onError`) are **mutually exclusive** -- exactly
one fires per `chat()` invocation.
> **Sampling in `onConfig`:** `temperature`, `topP`, and `maxTokens` are **not**
> first-class fields on `ChatMiddlewareConfig`. To adjust sampling from
> middleware, return a partial that mutates `config.modelOptions` using the
> provider's native key (e.g. OpenAI `temperature` / `max_output_tokens`,
> Anthropic `max_tokens`, Ollama nested `options.num_predict`). Returning a
> top-level `temperature`/`maxTokens` has no effect.
### Phase values
`ctx.phase` is one of:
| Phase | When |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `'init'` | Initial setup (before the first `onConfig` snapshot is built). |
| `'beforeModel'` | Right before each agent-loop adapter call (`onConfig` re-fires here). |
| `'modelStream'` | During model streaming chunks within the agent loop. |
| `'beforeTools'` | Before tool execution phase. |
| `'afterTools'` | After tool execution phase. |
| `>
Triage all open GitHub issues, PRs, and discussions in the current repository by fanning out up to 100 parallel subagents (one per item), then produce a single prioritized report ranking which PRs to review first, which issues to address first, and which discussions need maintainer attention. Use when the user asks to "triage open issues/PRs", "triage discussions", "prioritize the backlog", "what should I review first", "sweep the repo", or any request to bulk-evaluate open GitHub work and recommend an order.
>
>
>
>
>
>