Skip to content

About

Lint rules for code that calls LLM SDKs: Vercel AI SDK, openai, @anthropic-ai/sdk, and the MCP TypeScript SDK.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

oxlint-plugin-llm

npm version npm downloads CI license

Lint rules for code that calls LLM SDKs. Vercel AI SDK (ai), openai, @anthropic-ai/sdk, and the MCP TypeScript SDK. Every rule reads one file's AST and needs no type information, so it runs at Oxlint speed.

It catches things like:

client.messages.create({ model, messages });
// anthropic-require-max-tokens: the API rejects this with a 400

streamText({ model, prompt });
// no-unconsumed-stream: nothing reads the stream, so nothing is sent

generateText({ model, maxTokens: 200 });
// no-deprecated-api: renamed to maxOutputTokens in ai 5

const res = await openai.chat.completions.create({ model, messages, stream: true });
res.choices[0];
// no-stream-result-as-response: res is a stream of events, choices is undefined

export async function POST(req: Request) {
  return streamText({ model, messages });
  // require-abort-signal-in-handlers: pass abortSignal: req.signal or pay for the whole answer
}

The rules match the SDK API, not your local names. import { streamText as st }, import * as ai, require("ai"), new AzureOpenAI() behind a class field: all of them resolve.

Install

npm i -D oxlint oxlint-plugin-llm
pnpm add -D oxlint oxlint-plugin-llm
yarn add -D oxlint oxlint-plugin-llm
bun add -d oxlint oxlint-plugin-llm

Use

oxlint.config.ts:

import { defineConfig } from "oxlint";
import llm from "oxlint-plugin-llm";

export default defineConfig({
  extends: [llm.configs.recommended],
});

Or .oxlintrc.json:

{
  "jsPlugins": ["oxlint-plugin-llm"],
  "rules": {
    "llm/anthropic-require-max-tokens": "error",
    "llm/no-deprecated-api": "warn",
    "llm/no-unconsumed-stream": "error",
    "llm/no-stream-result-as-response": "error",
    "llm/require-abort-signal-in-handlers": "error"
  }
}

The same package loads in ESLint 9: plugins: { llm }.

Plugins cannot read package.json, so no-deprecated-api assumes the current majors. If you are pinned to an older one, say so and it only reports what applies:

{
  "settings": {
    "llm": { "ai": 5, "openai": 6 }
  }
}

Rules

🔧 has an autofix. ✅ is in recommended.

Rule What it flags 🔧 ✅
anthropic-require-max-tokens messages.create() without max_tokens. ✅
no-deprecated-api Calls, options, and result fields deprecated in your SDK major. 🔧 ✅
no-unconsumed-stream A streaming call whose result nothing reads. ✅
no-stream-result-as-response .choices or .content read on the result of a stream: true call. ✅
require-abort-signal-in-handlers A generation call in a request handler with no abort signal. ✅

Not covered on purpose

Hardcoded API keys, user input in system prompts, and tools without schemas are already handled by eslint-plugin-openai-security, eslint-plugin-anthropic-security, eslint-plugin-vercel-ai-security, and eslint-plugin-mcp-sdk-security, which load in Oxlint too. This plugin does not repeat them. It covers what they leave out: renames and deprecations, stream misuse, and abort signals across all three SDKs.

Contributing

Read CONTRIBUTING.md. The short version: pnpm new-rule <name> scaffolds a rule with its test and doc page, PR titles are conventional commits, and issues labelled good first issue are a fine place to start.

License

MIT

About

Lint rules for code that calls LLM SDKs: Vercel AI SDK, openai, @anthropic-ai/sdk, and the MCP TypeScript SDK.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages