{
  "markdown": "# sqlike: MCP server and CLI\n\n[![@sqlike/mcp](https://img.shields.io/npm/v/%40sqlike%2Fmcp?label=%40sqlike%2Fmcp&color=17a673)](https://www.npmjs.com/package/@sqlike/mcp)\n[![@sqlike/cli](https://img.shields.io/npm/v/%40sqlike%2Fcli?label=%40sqlike%2Fcli&color=17a673)](https://www.npmjs.com/package/@sqlike/cli)\n[![license](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](#license)\n\n**Check your SQL before it runs, and check whether a rewrite still returns the same results.**\n\n[sqlike](https://sqlike.com) reads a query and tells you what is wrong with it: validity errors,\nanti-patterns, rewrites it can apply for you, and index advice. It also compares two queries and\nreports whether the second one still returns the same results. There is no model anywhere in the\nanalysis, so the same query always gets the same answer.\n\nThis repository holds the clients: an [MCP](https://modelcontextprotocol.io) server, a CLI, and the\nlibrary they share. They tokenize your SQL locally, so identifiers and literals are masked before\nanything leaves your machine, and forward only the tokenized query. The analysis engine is not in\nthis repo. It runs server-side and is closed.\n\n**Dialects:** Postgres, MySQL, MariaDB, SQLite, SQL Server, and DuckDB. Each rule carries a verdict\nmeasured on that engine, so the severity you get is that database's behaviour and not Postgres by\ninheritance. DuckDB is the columnar one: it has no general-purpose secondary index, so the ten index\nadvisors are replaced there by four columnar ones.\n\n## Why\n\nA lot of SQL is written by an AI now, and the SQL it writes reads well more often than it runs\ncorrectly. A `LEFT JOIN` turns into an `INNER` and rows quietly disappear. A `WHERE` goes missing and\nthe `UPDATE` hits every row. Two tables get joined on the wrong key. None of it looks wrong on the\npage, and none of it shows up until it has already done something.\n\nsqlike is the check in between. It flags unsafe patterns from a catalog of 170 rules, each one\nverified against a real database before it ships, and it decides whether a rewrite preserves results.\nThat second check is sound rather than complete: it certifies the rewrites it can prove, and when it\ncannot prove one it answers `Undecided` instead of guessing. `Undecided` never means equivalent.\n\nBecause nothing is generated, there is no retry loop, no per-token cost, and no variance between two\nruns on the same input. The equivalence check normalizes rather than solves, so it answers in about\na millisecond where the state-of-the-art academic prover takes hundreds. That is\n[measured head to head](https://sqlike.com/benchmark), including the queries where the prover wins.\n\n## Install the MCP server\n\nAdd it to any MCP client (Claude Code, Claude Desktop, Cursor, and so on):\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlike\": { \"command\": \"npx\", \"args\": [\"-y\", \"@sqlike/mcp\"] }\n  }\n}\n```\n\nOr install it through [Smithery](https://smithery.ai/server/orifisher2/sqlike). Set `SQLIKE_API_KEY`\nif you have a key and want the higher rate limits. Without one you get the anonymous tier, which\nneeds no signup.\n\n## Tools\n\n### `analyze`\n\nStatic analysis of one query: validity, anti-patterns, suggested rewrites, and schema and index\nadvice. Returns the JSON analysis envelope.\n\n| Argument    | Type    | Description                                                              |\n| ----------- | ------- | ------------------------------------------------------------------------ |\n| `sql`       | string  | The query to analyze. **Required.**                                      |\n| `schema`    | string  | Optional DDL (`CREATE TABLE` / `CREATE INDEX`) for column and type aware checks. |\n| `dialect`   | string  | `postgres` (default), `mysql`, `mariadb`, `sqlite`, `mssql`, or `duckdb`. |\n| `allow_raw` | boolean | Only used when a query fails to parse, and so cannot be tokenized: send the raw SQL to get a parse diagnostic. Default `false`. |\n\n### `diff`\n\nChecks whether two queries are equivalent, which is the judgement an LLM cannot reliably make about\nits own rewrite. Returns a verdict (`Equivalent`, `EquivalentWithNotes`, `Differs`, or `Undecided`),\na confidence level, and a report per property (columns, rows, cardinality, order), so you see what\nchanged rather than a single yes or no.\n\n| Argument  | Type   | Description                                                        |\n| --------- | ------ | ----------------------------------------------------------------- |\n| `sql_a`   | string | The original query. **Required.**                                  |\n| `sql_b`   | string | The rewritten query to check against `sql_a`. **Required.**        |\n| `schema`  | string | Optional DDL both queries resolve against (one shared schema).     |\n| `dialect` | string | `postgres` (default), `mysql`, `mariadb`, `sqlite`, `mssql`, `duckdb`. |\n\n## CLI\n\nThe same checks from a terminal or a CI job. Run it with no install, or put it on the path:\n\n```sh\nnpx -y @sqlike/cli --help\nnpm i -g @sqlike/cli\nbrew install orifisher2/sqlike/sqlike\n```\n\n```sh\n# analyze a query — the hosted API is the default, so no flags are needed\nsqlike check query.sql\n\n# several files at once, with a schema\nsqlike check migrations/*.sql --schema schema.sql\n\n# reading from stdin, machine-readable output (one JSON record per file)\ncat query.sql | sqlike check - --schema schema.sql --json\n\n# check that a rewrite is equivalent\nsqlike diff before.sql after.sql\n```\n\nPoint it elsewhere with `--remote` or `SQLIKE_URL`, and authenticate with `--key` or\n`SQLIKE_API_KEY`.\n\nIf you have the query's `EXPLAIN` output, pass it with `--explain plan.json` and the real access\npaths will confirm or dismiss the index findings. The plan is tokenized before it leaves the machine,\nsame as the query and the schema.\n\nExit codes are a contract, so each one means exactly one thing:\n\n| | `check` | `diff` |\n|---|---|---|\n| **0** | clean, or nothing at or above `--fail-on` | equivalent |\n| **1** | a warning, when `--fail-on warn` asks for it | differs |\n| **2** | a blocking defect: invalid SQL, or a high-severity correctness problem | undecided |\n| **3** | operational — unreachable, rate-limited, or unparseable | same |\n| **4** | usage error (a bad flag) | same |\n\n`check` defaults to `--fail-on block`, so advisory findings report without failing a build. Ask for\n`--fail-on warn` to be strict, or `never` to only report. **3 is never a verdict about your SQL** —\na pipeline can tell \"this query is bad\" from \"the call did not go through\".\n\n## In CI, and on commit\n\nGate SQL where it is written. Both run the same checks as the CLI, and both tokenize locally first.\n\n**GitHub Actions.** Findings land inline on the pull-request diff. The workflow authenticates as\nyour repository owner using GitHub's own OIDC token — no signup, no API key to store or leak.\n\n```yaml\npermissions:\n  id-token: write        # authenticate as this repo's owner\n  pull-requests: read    # check only the files this PR changes\n  contents: read         # mode: diff, to read the earlier version of a file\nsteps:\n  - uses: actions/checkout@v5\n  - uses: orifisher2/sqlike@cli-v0.4.0\n    with:\n      dialect: postgres\n      schema: db/schema.sql\n```\n\nSet `mode: diff` and it answers a different question on every pull request: *did this rewrite\nchange what the query returns?* Only modified files are compared, and a verdict of \"cannot prove\neither way\" never fails a build.\n\n**Pull requests from forks run anonymously.** GitHub does not issue OIDC tokens to workflows a fork\ntriggers, so a contributor's pull request cannot authenticate as your repository — it falls back to\nan anonymous, per-IP rate limit. Nothing is broken and there is nothing to configure; your own\nbranches still authenticate normally. The action says so rather than suggesting a permission that\nwould not help.\n\nOutside a pull request there is no set of changed files, so `changed-only` cannot apply and every\nmatching file is checked. The action warns when that happens, because on a large repository it is\nenough requests to reach a rate limit.\n\n**pre-commit.** Same checks before the commit lands:\n\n```yaml\nrepos:\n  - repo: https://github.com/orifisher2/sqlike\n    rev: cli-v0.4.0\n    hooks:\n      - id: sqlike\n        args: [--dialect, postgres]\n```\n\nThe hook blocks a commit on a real defect, not on a bad connection: if sqlike cannot be reached it\nsays so and lets the commit through.\n\n## Private by design\n\nTokenization happens here, on your machine, before any request goes out. sqlike never sees your real\ntable names, columns, or values, so there is nothing to leak and nothing to train on. An AI\nassistant needs the real thing to help you; sqlike does not.\n\nIf a query cannot be parsed it cannot be tokenized, and the client refuses to send it rather than\ntransmit raw SQL. Sending it anyway is an explicit opt-in (`allow_raw` on the tools, `--allow-raw` on\nthe CLI).\n\nSee [THREAT-MODEL.md](THREAT-MODEL.md) for what that does and does not cover.\n\n## What is in this repo\n\n- **`crates/mcp`**: `sqlike-mcp`, the MCP server. Ships to npm as [`@sqlike/mcp`](packages/mcp).\n- **`crates/cli`**: `sqlike`, the command-line client. Ships to npm as [`@sqlike/cli`](packages/cli).\n- **`crates/client`**: the shared forwarder, with no engine in it: tokenize, call the API, detokenize.\n- **`crates/core-parse`**: the SQL parser, stage model, tokenizer, and result types.\n- **`packages/`**: the npm packaging, with a prebuilt binary per platform.\n- **`skills/`**: the agent skill, so a coding agent knows when to reach for sqlike on its own.\n\n## Learn more\n\nTry it at **[sqlike.com](https://sqlike.com)**. The equivalence checker is measured in public against\nthe standard academic benchmark, including a head-to-head with the state-of-the-art prover, at\n**[sqlike.com/benchmark](https://sqlike.com/benchmark)**.\n\n## Note\n\nThis repository is generated from the upstream monorepo, which is the source of truth. Please file\nissues here. Code changes are made upstream and mirrored back.\n\n## License\n\nMIT OR Apache-2.0, at your option.\n",
  "bytes": 10065,
  "sha": "bd05c6c12827d632cd68e618abd1089de1828b4a602fc8b9845921abce750375",
  "repo_slug": "orifisher2/sqlike",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_orifisher2_sqlike_mcp_d9cda54e/readme"
}