{
  "markdown": "# promptlint-mcp\n\n[![npm](https://img.shields.io/npm/v/promptlint-mcp.svg?label=npm)](https://www.npmjs.com/package/promptlint-mcp)\n[![npm downloads](https://img.shields.io/npm/dm/promptlint-mcp.svg?label=downloads)](https://www.npmjs.com/package/promptlint-mcp)\n[![GitHub stars](https://img.shields.io/github/stars/sean-sunagaku/promptlint-mcp.svg?style=flat&label=stars)](https://github.com/sean-sunagaku/promptlint-mcp/stargazers)\n![MIT License](https://img.shields.io/badge/license-MIT-green.svg)\n![Node 18+](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)\n![MCP compatible](https://img.shields.io/badge/MCP-compatible-blue.svg)\n\nLint AI prompts like code. Save tokens. Catch contradictions.\n\nA static analyzer for AI prompts — system prompts, agent instructions, tool\ndescriptions — that runs as a **CLI** and as an **MCP server** so Claude Code\n(or any MCP-aware agent) can lint prompts before sending them.\n\n---\n\n## Quick demo\n\nRun promptlint on a typical (silently broken) system prompt:\n\n<!-- TODO: record an asciinema cast of the three CLI modes (default / --json\n     / --trim) and link here as `[![asciicast](...)](...)` once uploaded.\n     The text block below is the verbatim `NO_COLOR=1` output — it ships the\n     idea without needing the cast to land first. -->\n\n```text\n$ node src/cli.mjs examples/bad-prompt.md\n\npromptlint examples/bad-prompt.md\n  score  14/100\n  tokens 367 → 307 (-60, 16% saved)\n  issues 14\n\n  info   [ambiguous-pronoun] Pronoun \"that\" has no clear referent (line 1)\n         → Replace with a concrete noun, e.g. \"this\" → \"the config file\".\n  ...\n  error  [contradiction] Contradicting directives (length): \"concise\" vs \"detailed\"\n         → Pick one, or scope each with a clear condition (e.g. by task type).\n  error  [contradiction] Contradicting directives (commenting): \"do not add comments\" vs \"explain each step\"\n         → Pick one, or scope each with a clear condition (e.g. by task type).\n  info   [trailing-fluff] \"please\" fluff (match 1 of 1)\n         → Delete. AIs do not need politeness tokens.\n         · Please\n  ...\n```\n\nTwo `error`-level contradictions caught, score clamped to 14/100, 60 tokens (16%) reclaimable via `--trim`.\n\nAnd here's `--trim` in action on a small input:\n\n```text\n$ echo \"You are a helpful assistant. Please always be concise. Thank you for your help! When the user asks a question, handle it carefully.\" | node src/cli.mjs - --trim\n\nYou are a helpful assistant. Always be concise. When the user asks a question, handle it carefully.\n```\n\nSee [`docs/promo/`](./docs/promo/) for posting drafts and the (TODO) asciinema cast.\n\n---\n\n## Why\n\n- **Contradictions**: `\"be concise\"` + `\"be thorough\"` ships silently and\n  wastes tokens on every request. promptlint flags paired opposites\n  (length / tone / frequency / commenting / asking) as `error`.\n- **Redundancy**: near-duplicate sentences (Jaccard > 0.6) get one `warn`\n  apiece — delete one.\n- **Ambiguous pronouns**: `\"use it\"` / `\"handle that\"` without a mid-sentence\n  referent is flagged with a line number. Start-of-sentence capitals do NOT\n  count as referents, so normal English doesn't false-positive.\n- **Long code examples**: fenced code blocks over ~1200 chars or 30 lines\n  trigger a `warn` — examples are almost always trimmable to shape-only.\n- **Politeness fluff**: `Please` / `Thanks` / `I hope this helps` / `Let me\n  know if…` / `Feel free to…` are detected, counted, and (with `--trim`)\n  stripped while keeping your actual imperatives intact.\n\nScope: **English prompts, prose — not source code.** A Japanese prompt or a\nfile of JavaScript will look clean even if it isn't.\n\n---\n\n## Install\n\nInstall both the CLI and the MCP server in one go:\n\n```sh\nnpm install -g promptlint-mcp\n```\n\nTwo binaries are placed on your `$PATH`:\n\n| Bin | What it is |\n| --- | ---------- |\n| `promptlint`     | the CLI (lint a file or stdin) |\n| `promptlint-mcp` | the MCP server (stdio transport) |\n\n### Run without installing\n\n```sh\n# CLI: lint a file\nnpx -p promptlint-mcp promptlint <file>\n\n# CLI: lint stdin\necho \"be concise and thorough\" | npx -p promptlint-mcp promptlint -\n\n# MCP server (auto-launched by Claude Code via mcp config; see below)\nnpx -y promptlint-mcp\n```\n\n### From source (hacking on it)\n\n```sh\ngit clone https://github.com/sean-sunagaku/promptlint-mcp.git\ncd promptlint-mcp\nnpm install\nnode src/cli.mjs examples/bad-prompt.md\n```\n\nRequires Node **>= 18**. Zero runtime deps other than the MCP SDK.\n\n---\n\n## CLI usage\n\n### Default human report\n\n```sh\nnode src/cli.mjs examples/bad-prompt.md\n```\n\n```\npromptlint examples/bad-prompt.md\n  score  14/100\n  tokens 367 → 307 (-60, 16% saved)\n  issues 14\n\n  info   [ambiguous-pronoun] Pronoun \"that\" has no clear referent (line 1)\n         → Replace with a concrete noun, e.g. \"this\" → \"the config file\".\n  ...\n  error  [contradiction] Contradicting directives (length): \"concise\" vs \"detailed\"\n         → Pick one, or scope each with a clear condition (e.g. by task type).\n  error  [contradiction] Contradicting directives (commenting): \"do not add comments\" vs \"explain each step\"\n         → Pick one, or scope each with a clear condition (e.g. by task type).\n  info   [trailing-fluff] \"thanks\" fluff (match 1 of 2)\n         → Delete. AIs do not need politeness tokens.\n         · Thank you\n  info   [trailing-fluff] \"please\" fluff (match 1 of 1)\n         → Delete. AIs do not need politeness tokens.\n         · Please\n  info   [trailing-fluff] \"hope-helps\" fluff (match 1 of 1)\n         → Delete. AIs do not need politeness tokens.\n         · I hope this helps\n  ...\n```\n\nA clean prompt (see `examples/good-prompt.md`) returns `score 100/100` with\nzero issues — silence is a feature.\n\n### `--json` for machine consumption\n\n```sh\nnode src/cli.mjs examples/bad-prompt.md --json\n```\n\n```json\n{\n  \"score\": 14,\n  \"issues\": [\n    {\n      \"rule\": \"ambiguous-pronoun\",\n      \"severity\": \"info\",\n      \"message\": \"Pronoun \\\"that\\\" has no clear referent (line 1)\",\n      \"line\": 1,\n      \"suggestion\": \"Replace with a concrete noun, e.g. \\\"this\\\" → \\\"the config file\\\".\"\n    },\n    {\n      \"rule\": \"contradiction\",\n      \"severity\": \"error\",\n      \"message\": \"Contradicting directives (length): \\\"concise\\\" vs \\\"detailed\\\"\",\n      \"suggestion\": \"Pick one, or scope each with a clear condition (e.g. by task type).\"\n    }\n  ],\n  \"trimmed\": \"…\",\n  \"tokensBefore\": 367,\n  \"tokensAfter\": 307,\n  \"saved\": 60,\n  \"savedPercent\": 16\n}\n```\n\nEmpty input adds `\"note\": \"empty input\"`. Pure-fluff input (e.g. `\"Thanks!\"`)\nthat would otherwise trim to empty instead returns the original with\n`\"trimmerFallback\": true` — a safe drop-in never hands a blank prompt to your\nLLM.\n\n### `--trim` to print trimmed output (for piping)\n\n```sh\necho \"You are a helpful coding assistant. Please always be concise. Thank you for your help! When the user asks a question, handle it carefully. If they provide code, review it. Let me know if anything is unclear. I hope this helps!\" \\\n  | node src/cli.mjs - --trim\n```\n\n```\nYou are a helpful coding assistant. Always be concise. When the user asks a question, handle it carefully. If they provide code, review it.\n```\n\nFenced code blocks are preserved verbatim. Quoted strings (`\"…\"`) and inline\nbackticks (`` `…` ``) are masked before fluff removal so example text survives\nuntouched.\n\n### Stdin\n\n```sh\ncat prompt.md | node src/cli.mjs -\n```\n\nThe report header shows `promptlint <stdin>`.\n\n### Other flags\n\n```sh\nnode src/cli.mjs --rules      # list the 5 rule IDs, one per line\nnode src/cli.mjs --help       # usage\n```\n\n### Exit codes\n\n| Code | Meaning |\n| ---- | ------- |\n| 0 | no errors (warn/info ok) |\n| 1 | at least one `error`-severity issue |\n| 2 | bad usage (missing file, read failure, etc.) |\n\n---\n\n## MCP server usage\n\nRegister with Claude Code in one command (no clone, no global install needed):\n\n```sh\nclaude mcp add promptlint -- npx -y promptlint-mcp\n```\n\nOr add this to `~/.claude.json` (user scope) or project `.mcp.json`\n(project scope):\n\n```json\n{\n  \"mcpServers\": {\n    \"promptlint\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"promptlint-mcp\"]\n    }\n  }\n}\n```\n\nIf you've globally installed the package, the absolute-path form also works\nand skips `npx`'s first-run download:\n\n```json\n{\n  \"mcpServers\": {\n    \"promptlint\": {\n      \"command\": \"promptlint-mcp\"\n    }\n  }\n}\n```\n\n### Tools exposed\n\n| Tool | Input | Returns |\n| ---- | ----- | ------- |\n| `lint_prompt` | `{ text: string }` | Two `text` content items: `[0]` human one-line summary (`\"score: X/100 · issues: N · tokens: A → B …\"`), `[1]` full JSON of the `lint()` result (parse with `JSON.parse(result.content[1].text)`). |\n| `trim_prompt` | `{ text: string }` | Two `text` content items: `[0]` the trimmed prompt, `[1]` a savings footer (`\"Saved N tokens (P%). Original: A, trimmed: B.\"`). Does NOT resolve contradictions, redundancy, or ambiguity — run `lint_prompt` for those. |\n\nBoth tools are pure / idempotent. No network, no filesystem, no state.\n\n---\n\n## Rules\n\n| ID | Severity | What it catches | Example trigger |\n| -- | -------- | --------------- | --------------- |\n| `contradiction` | `error` | Paired opposing directives on length / tone / frequency / prohibition / commenting / asking | `\"be concise\"` + `\"be thorough\"` |\n| `redundancy` | `warn` | Two sentences with Jaccard word-set similarity > 0.6 | Two paragraphs that both say \"respond in English\" with different wording |\n| `long-example` | `warn` | Fenced code block > 1200 chars or > 30 lines | A 40-line helper function pasted inline |\n| `ambiguous-pronoun` | `info` | `it` / `this` / `that` / `these` / `those` with no mid-sentence concrete referent | `\"Use it to figure out what to do.\"` |\n| `trailing-fluff` | `info` | Politeness / filler: `please`, `thanks`, `I hope this helps`, `let me know if…`, `feel free to…`, `sorry`, `certainly/absolutely/of course` | `\"Please refactor the code. Thanks!\"` |\n\nScore formula: `100 - (errors*25 + warns*10 + infos*3)`, then clamped to `<=59`\nif any error exists and `<=79` if 2+ warnings exist. Severity ceilings are a\ntraffic-light: any error = red zone, regardless of count.\n\n---\n\n## Pricing & Roadmap\n\n**Free (OSS, MIT)**\n- CLI (`node src/cli.mjs`) — all three modes (report / JSON / trim), all 5 rules.\n- MCP server (`node src/mcp.mjs`) — both tools, unlimited use.\n- No account, no telemetry, no network.\n\n**Pro (planned, $9/mo)**\n- Team-shared custom rule dictionary (your project's bannned phrases, required\n  headers, forbidden contradictions).\n- Per-project lint history with diff view.\n- Cross-project token-savings dashboard (\"your team saved 1.2M tokens this\n  month\").\n\nPro is **not shipped yet**. No signup form today —\n[👍 the early-access issue (#1)](https://github.com/sean-sunagaku/promptlint-mcp/issues/1)\nto register interest. We DM invites in 👍 order.\n\n---\n\n## Development\n\n### Smoke test\n\n```sh\nnpm run lint:self         # lints examples/bad-prompt.md\n```\n\nExpected: exit 1 (2 `error` contradictions), score in the low teens, all\nrules firing. If this drifts, a rule regressed.\n\n### Baseline\n\n`examples/good-prompt.md` must always score **100/100** with **0 issues**. If\na change breaks that, the change is wrong.\n\n### Adding a rule\n\nRules live in `src/linter.mjs`. Each rule is a plain function:\n\n```js\nfunction ruleFooBar(text) {\n  // return an array of { rule, severity, message, samples?, suggestion, line? }\n  return [];\n}\n```\n\nAdd it to the `issues` array inside `lint()` and append its ID to the `RULES`\nexport. If it targets prose (not code), run it against the `maskCodeBlocks()`\ncopy so fenced examples don't trigger it. If it needs sentence splitting,\nreuse `splitSentences` (quote-aware) or `splitSentencesPreservingDelims`\n(quote-aware, keeps delimiters for the trimmer).\n\n### Contradictions\n\nExtend `CONTRADICTION_PAIRS` at the top of `src/linter.mjs`. Each entry is:\n\n```js\n{ a: [\"keyword-a\", ...], b: [\"keyword-b\", ...], label: \"short-label\" }\n```\n\nExisting labels: `length`, `tone`, `frequency`, `prohibition`, `commenting`,\n`asking`.\n\n---\n\n## Community\n\n- 💬 [Discussions](https://github.com/sean-sunagaku/promptlint-mcp/discussions) — questions, ideas, prompt war stories\n- 🐛 [Issues](https://github.com/sean-sunagaku/promptlint-mcp/issues) — bugs, feature requests\n  - [#1 Pro early access](https://github.com/sean-sunagaku/promptlint-mcp/issues/1) (pinned)\n  - [#2 Roadmap](https://github.com/sean-sunagaku/promptlint-mcp/issues/2)\n  - [#3 Known issues / day-2 backlog](https://github.com/sean-sunagaku/promptlint-mcp/issues/3)\n- ⭐ Star the repo if it saves you tokens — discovery helps other AI users find it.\n\n## How this was built\n\nThis package was produced end-to-end by two AI agent teams:\n\n- **[ai-auto-improve-app](https://github.com/sean-sunagaku/ai-auto-improve-app)** — built and polished the product. Customer AI uses the tool and writes feedback → Developer AI patches the code → repeat. Each round's commit (`round-001` … `round-003`) is the developer-AI's response to that round's customer-AI feedback.\n- **[ai-launch-team](https://github.com/sean-sunagaku/ai-launch-team)** — published and distributed it. 5 sub-agents handled npm publish, GitHub Release, awesome-mcp PRs, Discussions threads, social copy drafts, and SVG/PNG asset generation.\n\nCombined, this took the product from \"empty repo\" to \"v0.1.2 on npm + 2 awesome-mcp PRs + 2 Discussions seed threads + 4 Issues + full social drafts\" with the only manual step being the platform-mandated SMS / OTP authentications (X account creation, npm 2FA setup) — the rest was AI.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n",
  "bytes": 13557,
  "sha": "83f8e25f36c61a78a2be89628fc014bb58b8860a4f91ef50931d93faab9ab9a9",
  "repo_slug": "sean-sunagaku/promptlint-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_sean_sunagaku_promptlint_mcp_fe1c44dc/readme"
}