tdqs / cli
Command line
The tdqs package is the reference implementation of the specification: the same code scores the playground, the hosted API and your terminal. Lint needs no model; score needs any OpenAI-compatible endpoint.
Install
npm install --global tdqs
# or run it without installing
npx tdqs --helpLint
The deterministic checks, no model. Everything in the specification that is computed rather than judged: context signals, the hard gates, the invocation-cost prefilter for shadowing, and the checklist items a definition can fail on its own. It runs offline and is safe on every pull request.
# a tools/list result saved to disk
tdqs lint --file tools.json
# a local stdio server
tdqs lint --command "npx -y @modelcontextprotocol/server-filesystem ."
# a remote Streamable HTTP server
tdqs lint --url https://mcp.example.com/mcp
# fail the build on warnings too
tdqs lint --file tools.json --fail-on warningFindings link to the flag or dimension they come from. A lint result is not a score: it tells you what will cost you points before a model reads the description.
Score
The full rubric. Runs the four stages of the specification for every tool, then the server coherence evaluation and the rollup. Bring a key for any OpenAI-compatible endpoint. Glama scores its registry with deepseek-v4-flash-0731; scores are calibrated to a rubric+model pair, so use the same model when you want numbers that compare with the registry's.
export TDQS_BASE_URL=https://openrouter.ai/api/v1
export TDQS_API_KEY=sk-...
export TDQS_MODEL=deepseek/deepseek-v4-flash-0731
export TDQS_REQUEST_OVERRIDES='{"reasoning":{"enabled":false}}'
tdqs score --file tools.json --format markdown
# gate a release on the passing tier
tdqs score --url https://mcp.example.com/mcp --fail-under BTurn extended reasoning off. The reference model reasons before it answers unless told not to, which makes a call take a minute instead of seconds, and the calibration examples reproduce with reasoning off. How to say so is provider-specific, so it is an opaque JSON object merged into every request: --request-overrides '{"reasoning":{"enabled":false}}' on OpenRouter, or TDQS_REQUEST_OVERRIDES in the environment.
Or let this site run the model for you. Hosted mode posts the definitions to the API with a key from your account, waits for the report, and prints the same output — with a shareable report URL. It spends one of the account's daily API calls.
TDQS_API_KEY=tdqs_... tdqs score --file tools.json --hosted https://tdqs.devOptions
- --file, --command, --url
- Where the definitions come from. Exactly one. --file - reads stdin, and --header "Name: value" (repeatable) sends credentials to a remote server.
- --server-name
- The name the coherence prompt sees; defaults to the server's own name when connecting to one.
- --format
- json, markdown or text. JSON matches the published report schema; markdown is for pull request comments.
- --output
- Write the report to a file instead of stdout.
- --fail-on, --fail-under
- --fail-on error | warning | never (lint) and --fail-under A | B | C | D (score): what turns a report into a non-zero exit.
- --model, --base-url, --api-key
- (score) The model configuration, with --concurrency for how many tools are scored at once. Also read from TDQS_MODEL, TDQS_BASE_URL and TDQS_API_KEY.
Exit codes
- 0
- The report was produced and passed the threshold, or no threshold was set.
- 1
- The threshold failed. The report is still printed.
- 2
- Usage error, unreadable input, or a model call that could not complete. The reason is on stderr.
Continuous integration
name: TDQS
on: [pull_request]
jobs:
tdqs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npx tdqs lint --command "node ./dist/server.js" --fail-on warning
- run: npx tdqs score --command "node ./dist/server.js" --fail-under B --format markdown >> "$GITHUB_STEP_SUMMARY"
env:
TDQS_BASE_URL: https://openrouter.ai/api/v1
TDQS_API_KEY: ${{ secrets.TDQS_API_KEY }}
TDQS_MODEL: deepseek/deepseek-v4-flash-0731
TDQS_REQUEST_OVERRIDES: '{"reasoning":{"enabled":false}}'Library
Every stage is exported on its own, so a registry or a gateway can run the deterministic parts, hash definitions the way this site does, or build the prompts and call its own model.
import { createLlmClient, lintServer, parseToolDefinitions, scoreServer } from 'tdqs';
const { serverName, tools } = parseToolDefinitions(await response.json());
const lint = lintServer({ serverName, tools });
const report = await scoreServer({
llm: createLlmClient({ apiKey, baseUrl, model: 'deepseek/deepseek-v4-flash-0731' }),
serverName,
tools,
});
console.log(report.serverScore.overallTier, report.serverScore.overallScore);