contrakt
v0.1.7
Published
Auto-infer API contracts from vibe-coded apps, detect schema drift, generate MCP server configs
Readme
Contrakt
Auto-infer API contracts from vibe-coded apps, detect schema drift, and generate MCP server configs so AI agents can call your endpoints.
The problem
You ship a Next.js app with Cursor or Lovable. Two sessions later, an AI assistant silently changes a response shape. Your consumers break. You had no contract to catch it.
Contrakt scans your actual handler code, infers the contract, and tells you exactly what changed — without a single line of hand-written spec.
Install
npm install -g contraktQuickstart
cd my-nextjs-app
# 1. Infer contract + generate MCP config (run once)
contrakt init
# 2. Check for drift anytime — static analysis + live sampling if server is running
contrakt check
# 3. See what setup step is missing
contrakt doctor
# 4. Intentionally changed the API? Accept it as the new baseline
contrakt check --update
# 5. Publish your contract to the public registry
contrakt publish --name my-app
# 6. Optional: charge AI agents per API call with x402
contrakt monetize --receiver 0xYourWallet --price 0.001 --free-calls 5
contrakt monetize --publish
# 7. Watch a contract you depend on
contrakt watch https://contrakt-registry.vercel.app/u/username/my-app --dependThat's the whole workflow. Commit contrakt.lock to your repo. Run contrakt check in CI.
Commands
contrakt init
Scans your Next.js API routes, infers request/response schemas, writes contrakt.lock, and generates an MCP server stub.
Options:
--cwd <path> Project directory (default: current directory)
--base-url <url> Base URL of your running app (default: http://localhost:3000)
--force Overwrite existing contrakt.lock without prompting
--no-mcp Skip MCP config generation
--verbose Debug loggingOutputs:
contrakt.lock— versioned contract file (commit this)contrakt.config.json— local config (baseUrl, registry info).contrakt/mcp.json— Claude Desktop / MCP client config snippet.contrakt/contrakt-mcp-server.ts— runnable MCP server stub
contrakt check
The main command. Re-scans your code for static drift, and if your dev server is running, also samples live endpoints to catch runtime schema changes.
Options:
--cwd <path> Project directory
--update Accept current state as new baseline and update contrakt.lock
--base-url <url> Override base URL for live sampling
--watch Watch for file changes and re-check continuously
--verbose Debug logging
Exit codes:
0 No breaking changes
1 Breaking changes detected (CI-friendly)Change categories:
- 🔴 Breaking — removed endpoint, removed field, changed field type, added required request field
- 🟡 Non-breaking — added endpoint, added optional field, new status code
- 🟢 Additive — description updates, JSDoc changes
Example output (server running):
Contrakt — check
Scanning /my-app
GET /api/config
✓ No code changes.
Sampling live endpoints against http://localhost:3000
↓ GET /api/config 200 -1 field(s): showArchive
Breaking changes (1):
✗ Field "response.showArchive" was removed [GET /api/config]
Run 'contrakt check --update' to accept these changes as your new baseline.If ANTHROPIC_API_KEY is set in your environment, Contrakt will also run an AI impact analysis after detecting breaking changes — it scans your codebase for consumers of the changed fields and explains what will break and how to fix it.
contrakt doctor
Checks your local setup and tells you the next best command to run.
contrakt doctorIt checks:
- whether Next.js API routes are present
- whether
contrakt.lockexists - whether MCP artifacts were generated
- whether the project has been published to the registry
- whether
CONTRAKT_TOKENis available - whether monetization is enabled
contrakt publish
Publish your contrakt.lock to the public registry so AI agents and other developers can discover your API.
Options:
--cwd <path> Project directory
--name <name> Project name on the registry (default: directory name)
--token <token> API token (or set CONTRAKT_TOKEN env var)
--registry <url> Registry URL override
--verbose Debug loggingHow to get a token:
- Go to contrakt-registry.vercel.app
- Sign in with GitHub
- Go to Dashboard → Create Token
- Set
export CONTRAKT_TOKEN=<token>in your shell (or pass--token)
export CONTRAKT_TOKEN=your-token
contrakt publish --name my-app
# ✓ Published → https://contrakt-registry.vercel.app/c/username/my-appRun contrakt publish again after contrakt check --update to keep the registry in sync.
contrakt monetize
Configure x402 payment gating so AI agents can pay per API call in USDC on Base.
# Save local monetization settings and regenerate the MCP server
contrakt monetize \
--receiver 0xYourBaseWalletAddress \
--price 0.001 \
--free-calls 5
# Publish payment metadata to the registry
contrakt publish --name my-app
contrakt monetize --publishOptions:
--cwd <path> Project directory
--receiver <addr> EVM wallet address on Base that receives USDC
--price <usd> Price per API call in USD, e.g. 0.001
--free-calls <n> Free calls per agent per day before payment
--publish Push monetization config to the registry
--token <token> API token for --publish (or set CONTRAKT_TOKEN)
--registry <url> Registry URL override
--disable Disable payment gating
--base-url <url> Override base URL for MCP regeneration
--verbose Debug loggingWhat it does:
- Writes
monetizationsettings intocontrakt.config.json - Regenerates
.contrakt/contrakt-mcp-server.ts - Wraps generated MCP tools with
@contrakt/x402-middleware - Uses the registry to verify USDC payments and track free-tier usage after
contrakt monetize --publish
The generated MCP server still proxies to your app. Your app must be running, and agents call the MCP server. Once the free tier is used, the MCP server requires a valid x402 payment before forwarding the request.
To turn it off:
contrakt monetize --disable
contrakt mcpcontrakt watch <registry-url>
Subscribe to a published registry contract. First run stores the current registry version in contrakt.config.json; future runs notify you when the publisher updates it.
contrakt watch https://contrakt-registry.vercel.app/u/username/my-app
contrakt watch https://contrakt-registry.vercel.app/u/username/my-app --onceOptions:
--cwd <path> Project directory
--interval <seconds> Poll interval for continuous watch (default: 30)
--once Check once and exit 1 if the contract changed
--depend Declare this project as a consumer of the contract
--consumer-name <name> Consumer name for --depend
--consumer-url <url> Consumer URL for --depend
--token <token> API token for --depend (or set CONTRAKT_TOKEN)
--verbose Debug logging--depend tells the publisher “this project relies on your contract,” so they can see who might break before publishing a change.
contrakt webhook <registry-url> [webhook-url]
Create webhook notifications for a contract you own. The registry sends contract.created and contract.updated events whenever you publish.
contrakt webhook https://contrakt-registry.vercel.app/u/me/my-app https://example.com/contrakt-webhook
contrakt webhook https://contrakt-registry.vercel.app/u/me/my-app --list
contrakt webhook https://contrakt-registry.vercel.app/u/me/my-app --delete <webhook-id>Options:
--list List configured webhooks
--delete <id> Delete a webhook by id
--secret <secret> HMAC secret for X-Contrakt-Signature
--token <token> API token (or set CONTRAKT_TOKEN)
--verbose Debug loggingREADME badges
Published contracts have a badge endpoint:
[](https://contrakt-registry.vercel.app/u/username/my-app)It shows the current endpoint count and links readers to the live contract page.
contrakt diff <ref>
Diff the current codebase against contrakt.lock at any git ref.
contrakt diff main # vs main branch
contrakt diff HEAD~1 # vs last commit
contrakt diff v1.2.0 # vs a tagOptions:
--cwd <path> Project directory
--verbose Debug logging
Exit codes:
0 No breaking changes vs ref
1 Breaking changes detectedcontrakt mcp
Generates (or regenerates) the MCP server artifacts from contrakt.lock.
Options:
--cwd <path> Project directory
--base-url <url> Override base URL for this generation only
--output <path> Directory to write artifacts (default: .contrakt/)
--verbose Debug loggingCI usage
# .github/workflows/api-check.yml
- name: Check API drift
run: contrakt checkcontrakt check exits 1 on breaking changes. It only does static analysis in CI (no running server), which is fine — contrakt.lock is the agreed baseline, committed by a developer after running contrakt check --update locally.
AI impact analysis
Set ANTHROPIC_API_KEY to get automatic impact analysis after breaking changes are detected:
export ANTHROPIC_API_KEY=sk-ant-...
contrakt checkContrakt will grep your codebase for consumers of the changed fields and call Claude to explain what will break and suggest how to fix it — inline, right after the diff output.
Base URL resolution
Priority order (highest first):
CONTRAKT_BASE_URLenv var--base-urlflagcontrakt.config.json→baseUrl- Default:
http://localhost:3000
Files
| File | Commit? | Purpose |
|---|---|---|
| contrakt.lock | ✅ Yes | Contract definition — diffed on every check |
| contrakt.config.json | Optional | Local config (baseUrl, registry URL) — gitignore if env-specific |
| .contrakt/mcp.json | Optional | Claude Desktop config snippet |
| .contrakt/contrakt-mcp-server.ts | Optional | Runnable MCP server — regenerate with contrakt mcp |
Using the MCP server
# Start your Next.js app
pnpm dev
# In another terminal, start the MCP server
tsx .contrakt/contrakt-mcp-server.ts
# Override base URL at runtime
CONTRAKT_BASE_URL=https://staging.myapp.com tsx .contrakt/contrakt-mcp-server.tsAdd to Claude Desktop's claude_desktop_config.json:
// paste the contents of .contrakt/mcp.json hereWhat gets inferred
For each route file:
- Path — derived from file location (
app/api/users/[id]/route.ts→/api/users/[id]) - Methods — exported
GET,POST,PUT,PATCH,DELETEfunctions - Path params —
[id],[...slug]segments - Query params —
searchParams.get("key")calls - Request body —
(await req.json()) as MyType→ follows the type definition - Response —
NextResponse.json({ ... })→ infers from object literal or type - Status codes — collected from every
NextResponse.jsoncall - Description — first sentence of the handler's JSDoc comment
Anything that can't be confidently inferred is marked { "type": "unknown" } rather than guessed. Run contrakt check with your dev server running to fill in those gaps from live responses.
Supported stacks
- ✅ Next.js App Router (
app/api/**/route.ts) - ✅ Next.js Pages Router (
pages/api/**/*.ts) - 🔜 Express
- 🔜 FastAPI
Registry
Contracts published with contrakt publish are browsable at contrakt-registry.vercel.app.
The registry source is open at github.com/shouryasrivastava/contrakt-registry.
Local development
git clone https://github.com/shouryasrivastava/contrakt
cd contrakt
pnpm install
pnpm build # compile to dist/
pnpm link --global # make contrakt available globally
pnpm test # run integration tests (26 tests)See CONTRIBUTING.md for how to add framework support and submit PRs.
License
MIT
