Skip to main content
Glama

fieldtwin

CI License: Apache 2.0 Node 22+

Jobber, Housecall Pro, and QuickBooks Online, running on your laptop: real API shapes, a believable fake business, and every failure on demand. Build and test integrations and AI agents without touching a customer's account.

Watch the two-minute demo: the dashboard drives a twin, an agent's changes are listed and rewound, and a sync brings drifted books back into line.

Point your code at http://localhost:4010 instead of api.getjobber.com or quickbooks.api.intuit.com (or at http://localhost:4010/housecallpro instead of api.housecallpro.com). It gets the same paths, headers, and JSON back, from a fake trades business with customers, technicians, and months of jobs, quotes, invoices, and payments, kept in every system at once. Then make it hard: throttle QuickBooks, lose the answer to a write, deliver a webhook twice, jump the clock past every token's expiry. Afterwards, see exactly what your code changed, and undo it.

curl -X POST localhost:4010/_twin/checkpoints/before-agent           # name this moment
# ...let your integration or agent loose on http://localhost:4010...
curl -s "localhost:4010/_twin/checkpoints/before-agent/changes?source=api" | jq -r '.[].summary'
2026-07-13T11:00:00.000Z  quickbooks  CustomerCreated Id=26 "Dana Whitfield" SyncToken=0  (api)
2026-07-13T11:00:00.000Z  quickbooks  InvoiceCreated Id=29 DocNumber=1001 "Dana Whitfield" TotalAmt=185.00 Balance=185.00  (api)
2026-07-13T11:00:00.000Z  quickbooks  CustomerUpdated Id=3 "Harper Alder Jr" SyncToken=1  (api)
curl -X POST localhost:4010/_twin/checkpoints/before-agent/rewind    # and it never happened

Every run starts from a seed, so the same requests give the same results, byte for byte. A bug you hit once, you can hit again.

Why fieldtwin

If you build software for field-service and trades businesses, you know the options. Test against a client's live books and hope. Wait for vendor sandbox access, then fill an empty account by hand. Or write mocks that return exactly what you expected, and so never catch what you didn't. None of these suits an AI agent, which needs somewhere it can make mistakes many times over, in parallel.

A client's live account

Vendor sandbox

Hand-written mocks

fieldtwin

Safe to break

No

Yes

Yes

Yes

Ready to use

When the client trusts you

After a developer account, an app, and setup

After you write each endpoint

npx fieldtwin

Realistic data

Yes

Generic samples, or none

What you typed

A seeded business with months of history

Jobber, Housecall Pro, and QuickBooks describe the same business

Yes

No

No

Yes, or drifted on purpose

Back to a clean start

Never

By hand

Restart

POST /_twin/reset

429s, 5xx, lost answers, duplicate webhooks on demand

No

No

Only the ones you thought of

Yes, in the vendor's own error format

Skip ahead in time (token expiry, retries, scheduled events)

No

No

Rarely

Yes, instantly

See what your code changed, and undo it

No

No

No

Checkpoints, change lists, rewind

Offline, in CI, many copies in parallel

No

No

Yes

Yes

Exactly like production

Yes

Nearly

No

No. Modeled from the public docs, and FIDELITY.md says how closely

fieldtwin doesn't replace the vendors' sandboxes; it covers what they can't, and a final check in one before you go live is still worth doing. When you have a sandbox, fieldtwin's conformance suite records what the real API does and scores the twin against it.

Related MCP server: fetchsandbox-mcp

Who it's for

Developers building integrations for trades and field-service software. The bugs that cost you are the ones you can't make happen on purpose: a throttled API, a write whose answer never came back, a webhook that arrives twice, a token that expires halfway through a sync. fieldtwin makes them happen whenever you ask, in a business that looks real in every system, and twin time skips the waiting. The reference integration gets through more than a hundred retries and two hours of token expiry in a couple of seconds. Every run starts from a seed, so a failure replays exactly, in CI or on a teammate's laptop. You ship sooner, and you find the bad night in testing instead of in a customer's books.

Teams building AI agents that touch a business's money. An agent needs somewhere to make mistakes, many times over, and you need proof of what it actually did. Mark a checkpoint, let the agent work, and judge it on the exact list of changes it made, not on what it says it did. Then rewind and run it again. Load mismatched-books to see whether it notices the systems disagree, or flaky-apis to see whether it gives up. The dashboard shows all of it as it happens.

Agencies and integrators who sell to trades businesses. "We test against a full simulation of your Jobber or Housecall Pro and your QuickBooks before anything touches your account" is a promise you can keep, and a reason a cautious owner says yes.

The businesses themselves, though they never run it. They get syncs that don't create duplicate customers, leave invoices out of the books, or record a payment twice, and AI assistants that made their mistakes in a copy of the business instead of in theirs.

What you get

You get

Instead of

A working fake business, in Jobber, Housecall Pro, and QuickBooks at once, from one command

Developer accounts, app setup, and test data typed in by hand

Throttling, outages, lost answers, and duplicate webhooks whenever you ask

Meeting them for the first time in production

Hours of retry waits and token expiry, in seconds

Tests that sleep, or that never cover it

The same results from the same seed, every run

"Works on my machine"

An exact list of what your code or agent changed, and an undo

Guessing from logs, on an account you can never reset

Tests that run on every commit, offline, with no vendor account

Checking by hand in a sandbox before each release

fieldtwin is built by Nick George. Its page, with both demo videos, is at nickgeorgeai.com/fieldtwin; more of his work, and how to reach him, at nickgeorgeai.com.

What's modeled

QuickBooks Online

Jobber

Housecall Pro

Records

Customers, items, invoices, payments

Clients, jobs, visits, quotes, invoices, users

Customers and their addresses, employees, jobs with appointments and line items, estimates, invoices

Reading

By Id; the query language (WHERE with =, <, >, LIKE, IN, joined by AND; ORDERBY; paging; COUNT(*)); change data capture

GraphQL, with Relay pagination both ways, encoded ids, and dated API versions

REST lists with page and page_size, sorting, customer search, and the documented job and invoice filters

Writing

Customers and invoices created and updated with SyncTokens (5010 when stale), sparse or full; payments created and applied; requestid for safe retries

clientCreate

Customers created and updated, addresses added; jobs created, scheduled, and dispatched

Rate limits

500 requests a minute per company: HTTP 429

The query-cost budget: 10,000 points refilling 500 a second, THROTTLED

Not published: fault rules make 429s, with RateLimit-Reset

Webhooks

CloudEvents, signed, retried on Intuit's schedule

HMAC-signed, delivered at least once, retried

The whole record, signed with Api-Timestamp and Api-Signature, tried once

OAuth 2.0

Authorize, tokens, refresh (rolling 100 days), revoke

Authorize with PKCE, tokens, refresh tokens that rotate

Authorize, tokens (30 days), refresh tokens that rotate; API keys as Authorization: Token

Anything not listed fails clearly in the vendor's own error format rather than returning made-up data. FIDELITY.md has every behavior, how closely it matches, and the docs page it comes from.

Quick start

Requires Node 22+.

npx fieldtwin
fieldtwin: Rowntree Electric is open for business at http://localhost:4010
fieldtwin: auth is any-token: ANY Bearer token is accepted. Set FIELDTWIN_AUTH=oauth for real OAuth checks.

Open http://localhost:4010 in a browser for where everything is. Read a customer from the QuickBooks twin (by default any bearer token works; see Authentication):

curl -H "Authorization: Bearer dev" \
  http://localhost:4010/v3/company/9341000000000001/customer/1

List clients from the Jobber twin:

curl -X POST http://localhost:4010/api/graphql \
  -H "Authorization: Bearer dev" \
  -H "X-JOBBER-GRAPHQL-VERSION: 2025-04-16" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ clients(first: 5) { nodes { id name } totalCount } }"}'

List scheduled jobs from the Housecall Pro twin, with an API key (any key works):

curl -H "Authorization: Token dev" \
  "http://localhost:4010/housecallpro/jobs?work_status[]=scheduled&page_size=5"

Start it in a ready-made situation instead: npx fieldtwin flaky-apis (see Scenarios and faults). npx fieldtwin --help lists the scenarios and settings.

To work on fieldtwin itself, or run the dashboard and the worked example, clone it instead: git clone https://github.com/nickgeorgeseo/fieldtwin && cd fieldtwin && npm ci && npm start.

Using it with your code

Change only the base URL. Paths, headers, and body shapes match the real APIs.

Vendor

Real base URL

Twin base URL

QuickBooks Online

https://quickbooks.api.intuit.com

http://localhost:4010

Jobber

https://api.getjobber.com

http://localhost:4010

Housecall Pro

https://api.housecallpro.com

http://localhost:4010/housecallpro

The QuickBooks twin serves one company, realm 9341000000000001. The Housecall Pro twin serves the real paths under /housecallpro, because some of them (/api/invoices/{uuid}) start the way Jobber's do.

The QuickBooks and Jobber twins enforce the vendors' documented rate limits in twin time: Jobber answers THROTTLED when a query costs more than its budget has left, and QuickBooks answers HTTP 429 after 500 requests in a minute. Advancing the clock lets them recover; a reset starts them full. Housecall Pro doesn't publish its limits, so its twin throttles only when a fault rule says so. Faults can add more throttling on top of any of them.

Configuration

Variable

Default

What it does

FIELDTWIN_PORT

4010

The port to listen on

FIELDTWIN_HOST

127.0.0.1

The address to listen on. 0.0.0.0 reaches it from a container or another machine: only on a network you trust, since anyone who can reach the twin can control it (SECURITY.md)

FIELDTWIN_AUTH

any-token

oauth makes the vendor APIs check tokens the way the real ones do

FIELDTWIN_SEED

42

Which fake business to generate. The same seed always builds the same one

Testing on every commit

Your tests start a twin of their own and reach it the way your app does. Each test loads a fresh world, runs your code against it, and checks the change list: what your code really did, not what it logged. Twin time skips the waiting, so retries and token expiry take seconds, and every world comes from a seed, so a test that fails in CI fails the same way on your laptop.

1. Write a test. This one checks for the bug a lost answer causes. QuickBooks can accept two invoices with the same number, so code that retries a create after a timeout, without a requestid, books the same work twice:

// test/sync.test.ts in your project, with fieldtwin installed: npm install --save-dev fieldtwin
import assert from "node:assert/strict";
import { after, beforeEach, test } from "node:test";
import { startTwin } from "fieldtwin";
import { syncPaidInvoices } from "../src/sync.ts"; // your code

const running = await startTwin(); // a twin of this file's own, on a free port
after(() => running.close());

/** Something the twin recorded: what changed, in which vendor, with the whole record as that vendor's API returns it. */
interface Change {
  vendor: string;
  type: string;
  data: any;
}

/** A request to the twin's control plane, failing the test with the twin's reason if it refuses. */
async function twin(method: string, path: string) {
  const response = await fetch(`${running.url}/_twin${path}`, { method });
  if (!response.ok) throw new Error(`${method} /_twin${path}: ${await response.text()}`);
  return response.json();
}

beforeEach(async () => {
  await twin("POST", "/scenarios/flaky-apis"); // a fresh world for each test: 429s, 503s, and writes whose answer is lost
  await twin("POST", "/checkpoints/before-sync");
});

test("books each invoice once, even when QuickBooks loses the answer to a write", async () => {
  await syncPaidInvoices({ jobberUrl: running.url, quickbooksUrl: running.url });

  const changes: Change[] = await twin("GET", "/checkpoints/before-sync/changes?source=api"); // what your code did
  const booked = changes
    .filter((change) => change.vendor === "quickbooks" && change.type === "InvoiceCreated")
    .map((change) => change.data.invoice.DocNumber);

  assert.ok(booked.length > 0, "the sync booked no invoices");
  assert.equal(new Set(booked).size, booked.length, `an invoice was booked twice: ${booked.join(", ")}`);
});

It uses Node's built-in test runner, and Vitest works the same way. Jest needs its ESM mode, since fieldtwin is an ES module. Against a sync that retries without a requestid, it fails with the duplicate it found:

✖ books each invoice once, even when QuickBooks loses the answer to a write
  AssertionError [ERR_ASSERTION]: an invoice was booked twice: 41, 41

2. Run it. node --test (or npm test). There's nothing else to start: the test file starts its twin and closes it. Node runs a .ts test file as it is from 22.18 (and 23.6); on an older Node, run node --test --experimental-strip-types, or node --test finds no tests.

3. Run it on every commit. The same command runs in CI, since fieldtwin is a dev dependency like any other. If your tests aren't in Node, or you'd rather keep one twin running beside them, start it as a step of its own and wait until it answers. In GitHub Actions:

# .github/workflows/test.yml in your project
name: Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - name: Start the twin
        run: |
          nohup npx -y fieldtwin@0.5 > twin.log 2>&1 & # pin the version for repeatable runs
          timeout 60 bash -c 'until curl -sf localhost:4010/_twin > /dev/null; do sleep 1; done'
      - run: npm ci && npm test # your tests, with the twin at http://localhost:4010
      - name: Keep the twin's event log
        if: failure()
        run: curl -s localhost:4010/_twin/log.jsonl > fieldtwin-log.jsonl
      - if: failure()
        uses: actions/upload-artifact@v7
        with:
          name: fieldtwin-log
          path: fieldtwin-log.jsonl

When a run fails, its event log is kept with it: everything your code did to the twin, to read or to replay.

A few things to know:

  • Tests that share a twin share its world. Each file that calls startTwin() gets its own, so files can run in parallel; tests within a file take turns, as Node's runner and Vitest run them by default. One twin started beside the tests is shared by all of them: run the files one at a time (node --test --test-concurrency=1, Vitest's --no-file-parallelism, Jest's --runInBand).

  • startTwin() takes the settings the command does: seed, auth ("any-token" or "oauth"), scenario (a ready-made one's name or a YAML file's path), and port (a free one unless you say). It answers url, port, and close().

  • flaky-apis checks OAuth tokens for real, so your code connects through the twin's OAuth endpoints, as it will in production. To test retries alone, follow the scenario with PUT /_twin/auth and {"mode": "any-token"}.

  • For a stronger check, the reference integration's flaky test syncs through flaky-apis and checks the books end exactly as a clean run leaves them.

A worked example: Jobber to QuickBooks

examples/sync-jobber-invoice-to-quickbooks is a reference integration written like production code, and you're welcome to copy it. It books every paid Jobber invoice into QuickBooks (customer, invoice, payment), on a schedule and from Jobber's webhooks as customers pay. It handles everything the twins throw at it: 429s and 5xx with backoff, answers lost after a write, Jobber's query-cost throttle, expiring and rotating OAuth tokens, stale SyncTokens, customers spelled differently in each system, webhooks that come twice or late, and two syncs at once.

npm start                  # terminal 1
npm run example:sync       # terminal 2

Its tests show what testing against a twin buys you: the same sync under flaky-apis settles to exactly the books a clean run produces, through more than a hundred retries, and the two hours it waits for its tokens to expire take no real time.

Testing an AI agent

The twin gives an agent a whole business to work in and you a precise record of what it did. Give each test a checkpoint, let the agent work, then judge it on the change list rather than on what it says it did:

curl -X POST localhost:4010/_twin/checkpoints/before-agent
# ...run your agent against the twin...
curl "localhost:4010/_twin/checkpoints/before-agent/changes?source=api"
curl -X POST localhost:4010/_twin/checkpoints/before-agent/rewind

Load flaky-apis to see how it copes when the APIs fight back, or mismatched-books to see whether it notices that the field-service systems and QuickBooks disagree. To let a coding agent drive the twin itself, use the MCP server.

Authentication

By default the twin accepts any bearer token. That keeps the quick start to one command, and it is the wrong setting for testing your OAuth code. To make the twin check tokens the way the real APIs do, start it with FIELDTWIN_AUTH=oauth npx fieldtwin, put auth: oauth in a scenario, or switch a running twin: curl -X PUT localhost:4010/_twin/auth -H "Content-Type: application/json" -d '{"mode":"oauth"}'.

Each vendor has a twin OAuth 2.0 server at the vendor's own paths (under /housecallpro for Housecall Pro), so your OAuth client only needs new URLs:

Endpoint

Real URL

Twin URL

QuickBooks authorize

https://appcenter.intuit.com/connect/oauth2

http://localhost:4010/connect/oauth2

QuickBooks tokens

https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer

http://localhost:4010/oauth2/v1/tokens/bearer

QuickBooks revoke

https://developer.api.intuit.com/v2/oauth2/tokens/revoke

http://localhost:4010/v2/oauth2/tokens/revoke

Jobber authorize

https://api.getjobber.com/api/oauth/authorize

http://localhost:4010/api/oauth/authorize

Jobber tokens

https://api.getjobber.com/api/oauth/token

http://localhost:4010/api/oauth/token

Housecall Pro authorize

https://pro.housecallpro.com/oauth/authorize

http://localhost:4010/housecallpro/oauth/authorize

Housecall Pro tokens

https://api.housecallpro.com/oauth/token

http://localhost:4010/housecallpro/oauth/token

The twin approves every authorization request at once (there is no consent page) and accepts any client_id and client_secret. Everything after that follows the vendors' docs: codes work once, access tokens last 60 minutes (30 days for Housecall Pro), Jobber and Housecall Pro rotate refresh tokens and reject the old one, QuickBooks keeps the refresh token on a rolling 100 days. Tokens expire on twin time, so POST /_twin/clock/advance {"minutes": 60} tests your refresh path in a second. (Jobber's access tokens are JWTs whose exp is in twin time too, so compare it with the twin's clock, not your own.)

A Pro's own Housecall Pro integration uses an API key instead, sent as Authorization: Token <key>. The twin can't know which keys a Pro made, so it takes any key, in either auth mode.

Getting a Jobber token from the command line:

CODE=$(curl -s -o /dev/null -w '%{redirect_url}' \
  "http://localhost:4010/api/oauth/authorize?response_type=code&client_id=my-app&redirect_uri=http://localhost:3000/callback" \
  | sed -E 's/.*code=([^&]+).*/\1/')
curl -s -X POST http://localhost:4010/api/oauth/token \
  -d client_id=my-app -d client_secret=dev -d grant_type=authorization_code \
  -d code=$CODE -d redirect_uri=http://localhost:3000/callback

Webhooks

Every twin sends webhooks the way its vendor does. Each vendor sets them up per app (in a developer portal, or in a Pro's own settings for Housecall Pro); the twin's equivalent is the control plane (or the twin_set_webhooks MCP tool):

curl -X PUT localhost:4010/_twin/webhooks -H "Content-Type: application/json" -d '[
  {"vendor":"jobber","url":"http://localhost:3000/webhooks/jobber","topics":["INVOICE_UPDATE"],"clientId":"my-app","clientSecret":"dev"},
  {"vendor":"quickbooks","url":"http://localhost:3000/webhooks/qbo","topics":["qbo.payment.created.v1"],"clientId":"my-app","clientSecret":"my-verifier-token"},
  {"vendor":"housecallpro","url":"http://localhost:3000/webhooks/hcp","topics":["invoice.paid"],"clientId":"my-app","clientSecret":"my-signing-secret"}
]'
curl -X POST localhost:4010/_twin/actions/jobber/payInvoices -H "Content-Type: application/json" -d '2'
curl -X POST localhost:4010/_twin/actions/housecallpro/payInvoices -H "Content-Type: application/json" -d '2'

Jobber deliveries carry Jobber's documented payload ({ "data": { "webHookEvent": { "topic", "appId", "accountId", "itemId", "occurredAt" } } }) and an X-Jobber-Hmac-SHA256 signature made with the subscription's client secret, so your verification code runs unchanged. Like Jobber, the twin delivers at least once: paying an invoice fires INVOICE_UPDATE twice, and a delivery your app doesn't accept within a second is sent again later in twin time. CLIENT_CREATE and INVOICE_UPDATE are modeled so far.

QuickBooks deliveries are CloudEvents, the format Intuit sends today: a JSON array of events like { "specversion": "1.0", "type": "qbo.invoice.updated.v1", "intuitentityid": "130", "intuitaccountid": "9341000000000001", ... }, signed in intuit-signature with the subscription's clientSecret standing in for your app's verifier token. Customers, invoices, and payments created or updated through the API are announced (qbo.customer.created.v1, qbo.customer.updated.v1, qbo.invoice.created.v1, qbo.invoice.updated.v1, qbo.payment.created.v1), and a payment also announces each invoice it pays. Your app has 3 seconds to answer HTTP 200; otherwise the twin tries again on Intuit's schedule, from 10 seconds up to every 6 hours of twin time.

Housecall Pro deliveries carry the documented shared fields (event, company_id, event_occurred_at, event_created_at) and the whole record, as the event left it: { "event": "job.created", ..., "job": { ... } }. Api-Timestamp and Api-Signature follow the docs' verification steps, an HMAC-SHA256 of the timestamp, a dot, and the payload, keyed with the subscription's clientSecret as your signing secret. customer.created, customer.updated, job.created, job.scheduled, job.completed, job.paid, invoice.created, invoice.sent, invoice.payment.succeeded, and invoice.paid are modeled so far. Jobs get finished, invoiced, and paid by the completeJobs, invoiceJobs, and payInvoices actions, in a scenario's timeline or through the control plane: invoicing a job sends invoice.created and then invoice.sent, and paying an invoice sends invoice.payment.succeeded, invoice.paid, and job.paid, in that order. The docs promise no retries, so the twin tries each delivery once.

Each URL gets its deliveries one at a time, in order; an app that is slow to answer doesn't hold up deliveries to another. GET /_twin/webhooks shows every recent delivery and how your app answered it.

Fault rules with a webhook topic (INVOICE_UPDATE, qbo.invoice.updated.v1, job.created, or "*") make deliveries misbehave instead of requests: sent twice, or late enough that later ones overtake them. Faulted deliveries carry X-Twin-Fault.

faults:
  - name: jobber-webhooks-twice
    vendor: jobber
    webhook: INVOICE_UPDATE
    rate: 0.3
    effect: { duplicate: true }
  - name: jobber-webhooks-late
    vendor: jobber
    webhook: "*"
    rate: 0.2
    effect: { delayMs: 300000 }   # five minutes of twin time late

Scenarios and faults

A scenario is a YAML file: seed, start time, world size, faults, and what happens as time passes. Start the twin with a ready-made one by name, or with your own file:

npx fieldtwin heat-wave-monday
npx fieldtwin my-scenario.yaml

Scenario

What it tests

heat-wave-monday

A busy HVAC Monday: 120 customers, throttled QuickBooks writes, a slow Jobber, and new clients calling in all morning

mismatched-books

The field-service systems (Jobber and Housecall Pro) and QuickBooks disagree: customers spelled differently (same email), customers only in the field with unsynced work, customers only in QuickBooks, and a customer QuickBooks has as inactive who has paid for new work since

flaky-apis

Retry testing: real OAuth checks plus layered faults (QuickBooks 429s, 503s, and writes whose answer is lost; Jobber 429s, 502s, and webhooks sent twice or late; Housecall Pro 429s with RateLimit-Reset, 502s, writes whose answer is lost, and webhooks sent late) while customers keep paying

A fault rule matches requests by vendor, method, and path (pathIncludes, and pathExcludes to leave some alone, like a vendor's OAuth endpoints), and fires at a rate:

faults:
  - name: qbo-throttled-writes
    vendor: quickbooks
    method: POST
    rate: 0.2            # one in five matching requests
    effect: { status: 429, message: "ThrottleExceeded" }

Injected failures come back in the vendor's own error format, with the headers it sends (a Housecall Pro 429 has RateLimit-Reset, in twin time), and carry an X-Twin-Fault header, so you can tell them apart from real bugs. An effect can also slow a request down (latencyMs), or lose its answer: with lostResponse: true the twin does the work first and then answers with the error anyway, the way a gateway timeout hides a write that succeeded. That is the failure a retried write has to survive without happening twice (for QuickBooks, send a requestid):

  - name: qbo-lost-answers
    vendor: quickbooks
    method: POST
    rate: 0.1
    effect: { status: 504, lostResponse: true }

A scenario can also schedule what happens as twin time passes. Advancing the clock runs each entry at its own time, recorded with source scenario:

timeline:
  - at: "08:00"                  # business time; also "+90m", "+2h", or "2026-07-13T15:00:00Z"
    jobber: { newClients: 12 }   # new clients call in
  - at: "13:00"
    jobber: { payInvoices: 4 }   # the longest-open invoices get paid
  - at: "16:00"
    housecallpro: { completeJobs: 2, invoiceJobs: 2, payInvoices: 1 }   # techs finish work, the Pro invoices it, a customer pays

Housecall Pro has newCustomers too, like Jobber's newClients. A job is finished only once all of its visits have begun, so completeJobs never marks work done before its time. Finishing a job doesn't invoice it: invoiceJobs invoices finished work, the work finished longest ago first, and from then on payInvoices can pay it.

A scenario with a typo, an unknown key, or a time without a UTC offset is refused when it loads, with the reason, and the twin stays as it was.

The control plane

Everything under /_twin controls the fake world rather than imitating a vendor.

Request

What it does

GET /_twin

Company, seed, loaded scenario, twin time, event count, auth mode, active faults, webhook subscriptions, checkpoints, what's scheduled next

POST /_twin/reset

Back to the start: the seeded world (the loaded scenario's, if any), with its timeline and faults

POST /_twin/clock/advance {"minutes": 60}

Move time forward (rate limits refill, tokens expire, scheduled events and their webhooks happen)

POST /_twin/actions/:vendor/:action (body: params)

Make something happen now, as if done in the vendor's own app: jobber/payInvoices with 2, jobber/newClients with 3, housecallpro/invoiceJobs with 1

POST /_twin/checkpoints/:name

Name this moment

GET /_twin/checkpoints/:name/changes?source=api

What your code changed since then (or seed, scenario, control; everything without source): each event, with a one-line summary. Like the log, it takes after= to read on from an earlier read

POST /_twin/checkpoints/:name/rewind

Undo everything after the checkpoint. The same requests then meet the same faults

PUT /_twin/faults

Replace the active fault rules

PUT /_twin/auth {"mode": "oauth"}

Check bearer tokens for real (oauth) or accept any (any-token)

GET /_twin/scenarios

The ready-made scenarios and what each one tests

POST /_twin/scenarios/:name

Load a ready-made scenario

POST /_twin/scenario (YAML body)

Load your own scenario

GET /_twin/log.jsonl?since=:checkpoint

The event log, one event per line (all of it without since). With the seed, it is everything a bug report needs. Its X-Twin-Log-Position header says where the log ends

GET /_twin/log.jsonl?after=:position

Only what was recorded after a position an earlier read gave (or after an event's seq): how to follow the log as it grows. A 409 means the log has changed in between (a reset, a rewind, a scenario, or a restarted twin): read it again from the start

PUT /_twin/webhooks

Replace the webhook subscriptions (see Webhooks)

GET /_twin/webhooks

The subscriptions, and each recent delivery with how your app answered

The dashboard

dashboard/ is a web dashboard for a running twin: everything in the control plane from a browser, the event log as it grows, and QuickBooks side by side with Jobber or Housecall Pro, every customer and invoice marked as matching or drifted.

npm start                                   # terminal 1: the twin
cd dashboard && npm ci && npm run dev       # terminal 2: http://localhost:4020

It is a Next.js package of its own and a client of the twin like any other: it uses only /_twin, so watching your code spends no rate limit and draws no fault.

Using it from a coding agent (MCP)

npx fieldtwin mcp starts the twin and an MCP server over stdio (npm run --silent mcp in a clone, so npm's own lines stay off stdout). Its tools mirror the control plane:

Tool

What it does

twin_status

Company, seed, scenario, twin time, auth mode, active faults, checkpoints, what's scheduled next

twin_reset

Back to the start of the loaded scenario (or the default world)

twin_advance_clock

Move time forward; scheduled scenario events happen on the way

twin_checkpoint, twin_changes, twin_rewind

Mark a moment, list what changed since (optionally only your API calls), undo it

twin_list_scenarios, twin_load_scenario

Pick a ready-made scenario by name, or load your own YAML

twin_set_faults

Make the APIs misbehave on purpose

twin_set_auth

Accept any token, or check OAuth tokens for real

twin_export_log

The event log as JSON Lines, all of it or since a checkpoint

twin_run_action

Make something happen now: customers pay, clients call in, techs finish jobs, finished work is invoiced

twin_set_webhooks, twin_webhooks

Subscribe your app to webhooks; see what was delivered and how it answered

Each tool carries MCP's tool annotations, so a client that asks before running a tool that changes things knows which ones do. The five that only look are marked read-only. The ones that replace or throw away what's there are marked destructive: resetting, rewinding, loading a scenario, setting faults or webhooks, and reusing a checkpoint's name. Only twin_advance_clock and twin_run_action reach outside the twin, by delivering webhooks to the URLs you subscribed.

Claude Code:

claude mcp add fieldtwin -- npx -y fieldtwin mcp

Other MCP clients (Cursor, Claude Desktop, VS Code) take the same command in their JSON config:

{ "mcpServers": { "fieldtwin": { "command": "npx", "args": ["-y", "fieldtwin", "mcp"] } } }

It is in the official MCP Registry as io.github.nickgeorgeseo/fieldtwin, so clients and directories that read the registry install it with the same command and its settings.

Add a scenario's name after mcp to start in it. The MCP server starts a twin of its own, on port 4010 unless FIELDTWIN_PORT says otherwise, so stop any other twin there first (claude mcp add -e FIELDTWIN_PORT=4011 ... picks another port). It exits when the agent closes the session.

The MCP tools only control the test world. Your agent still calls the vendor APIs over HTTP, the same way production code will.

When something looks wrong

You see

What it means

fieldtwin: port 4010 is already in use

Another twin (or something else) has the port. FIELDTWIN_PORT=4011 npx fieldtwin

HTTP 401 from any vendor

The twin is checking tokens (auth is oauth, from FIELDTWIN_AUTH, a scenario, or PUT /_twin/auth). Get a token from the twin's OAuth endpoints, or switch back to any-token. Housecall Pro also answers 401 for an Authorization header not written exactly Token <key> or Bearer <token>

Jobber THROTTLED on every try

The query asks for more than the whole 10,000-point budget, so it can never run. Ask for fewer nodes with first or last

QuickBooks 5010 Stale Object

Your update sent an old SyncToken, as the real API would refuse too. Read the record again and retry

An X-Twin-Fault header

A fault rule fired, on purpose. GET /_twin lists the active ones

A 500 that mentions a bug in fieldtwin

Please open an issue with the twin's log and GET /_twin/log.jsonl

Project layout

src/
  core/        the twin runtime: event log, clock, seeded randomness, OAuth server, vendor contract
  world/       the vendor-neutral fake business: customers, price list, months of work history, drift
  vendors/
    quickbooks/  one folder per vendor, all with the same shape
    jobber/
    housecallpro/
  faults/      makes vendor APIs misbehave on purpose
  control/     /_twin HTTP routes, MCP tools, scenarios (one shared operations.ts)
  app.ts       wires it together
  start.ts     startTwin(): a twin on its own port, from code
  index.ts     what `import { startTwin } from "fieldtwin"` gets
  cli.ts       the fieldtwin command
  server.ts    entry point for `npm start`
  mcp.ts       entry point for `npm run mcp` and `fieldtwin mcp`
scenarios/     ready-made situations (heat-wave-monday, mismatched-books, flaky-apis)
examples/      code that uses the twins the way yours will: over HTTP, never importing src/
  sync-jobber-invoice-to-quickbooks/   the reference integration, its demo, and its tests
conformance/   the same scripted sessions against a real sandbox and the twin, compared and scored
dashboard/     a web dashboard for a running twin (Next.js, a package of its own), over HTTP like examples/
test/          tests by vendor and area, plus core and the architecture guards

ARCHITECTURE.md explains how it fits together and the three rules that keep it easy to change. Read it before changing anything in core/.

Contributing

Contributions are welcome, especially ones that make the twins more faithful. CONTRIBUTING.md walks through adding an endpoint, from the vendor's docs page to the test to the FIDELITY.md row; every vendor folder has the same shape, so once you've read one you can read them all. If you have a QuickBooks sandbox company, a Jobber testing account, or a Housecall Pro account on the MAX or XL plan that you keep for testing, the most valuable thing you can do is record the conformance suite against it: npm run conformance:record turns the twin's guesses into evidence. Check first that the vendor's API terms allow it (links to each). Build only from public documentation; never paste vendor code, private docs, or real customer data.

Where it's going: more Jobber writes as their inputs reach the public docs, recordings from real sandboxes, more of Housecall Pro, then more vendors (Samsara or Motive, EDI trading partners) and a benchmark for dispatch and operations agents. ARCHITECTURE.md has the roadmap.

Disclaimer and license

fieldtwin is an independent project. It is not affiliated with, endorsed by, or supported by Jobber, Intuit, or Housecall Pro. Jobber, QuickBooks, and Housecall Pro are trademarks of their respective owners.

Copyright 2026 Nick George. Licensed under the Apache License 2.0.

Available Tools

14 tools
twin_advance_clockMove twin time forwardA

Twin time stands still until you move it. Scheduled scenario events happen on the way, each at its own time, and the webhooks they cause are delivered to the subscribed URLs before this answers; 0 minutes only sends deliveries already due. Answers with the new twin time and how many scenario events happened.

NameRequiredDescriptionDefault
minutesYesMinutes of twin time to move forward, up to ten years

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare non-read-only, open-world, non-idempotent and non-destructive, but the description adds genuinely new behavior: events occur at their own scheduled times, resulting webhooks are delivered synchronously before the call returns, and the response reports the new time and event count. It does not mention permission requirements or how long a large advance might take, but the side-effect ordering disclosure is the key value.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler, with the side-effect ordering front-loaded before the return value. The second sentence is a long semicolon chain that packs three distinct points together, which slightly hurts scannability but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by stating what is returned (new twin time and scenario event count). Combined with the side-effect timing and the 0-minute rule, an agent has everything it needs to call this correctly for a single-parameter tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and already documents the 'up to ten years' bound, so the baseline is 3. The description earns a point by explaining the semantics of the 0 value (flush already-due deliveries), which the schema does not say.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states that twin time is advanced and that scheduled scenario events fire along the way, which is a specific verb+resource. It is slightly narrative ('Twin time stands still until you move it') rather than a direct 'Advances the twin clock by N minutes', but an agent can still tell exactly what it does and how it differs from siblings like twin_status or twin_rewind.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear context for use (advance time so scheduled events and their webhooks fire) and explicitly defines the edge case '0 minutes only sends deliveries already due', which is the main ambiguity an agent would face. It does not name an alternative or an exclusion (e.g. relative to twin_rewind), so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_changesList changes since a checkpointA
Read-only

What changed since a checkpoint, one line per event, oldest first: when, which vendor, what, and its source. Set onlyFromApi to see only what your code's API calls did. Changes nothing. For whole events, use twin_export_log.

NameRequiredDescriptionDefault
checkpointYesA checkpoint's name, as given to twin_checkpoint
onlyFromApiNoTrue to leave out the seed, the scenario timeline, and control tools

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, and the description's 'Changes nothing' is consistent with that. It adds behavior beyond the annotations: output is ordered oldest first, one line per event, with specific fields, which helps the agent interpret results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the core purpose and output shape, then the parameter hint, then the safety note and the alternative. No wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, but the description compensates by explaining the per-event line contents and ordering. With the parameters fully documented and read-only behavior stated, nothing needed for a correct call is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds framing for onlyFromApi ('see only what your code's API calls did') that complements the schema's 'leave out seed, scenario timeline, control tools' wording, making the practical effect clearer.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('what changed since a checkpoint') and even describes the output shape: one line per event, oldest first, with when/vendor/what/source. It also names the sibling to use for whole events (twin_export_log), so an agent can distinguish it without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent to twin_export_log 'for whole events' and explains the onlyFromApi narrowing case. It gives a clear usage context but stops short of stating when this tool should not be used at all (e.g., no checkpoint yet).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_checkpointTake a checkpointA
DestructiveIdempotent

Name the current moment (log position, twin time, and fault dice), to list changes since it with twin_changes or go back to it with twin_rewind. A name already taken moves to now. Answers with the checkpoint.

NameRequiredDescriptionDefault
nameYesAny name, like before-sync

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=true; the description explains the mechanism behind both with 'a name already taken moves to now', telling the agent that reuse overwrites the prior checkpoint rather than erroring. It also discloses the response ('answers with the checkpoint'). It stops short of stating whether checkpoint creation has any side effects on twin state beyond the name collision.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero filler. The core definition and its contents come first, the sibling routing follows, and the overwrite caveat closes it out – all in priority order.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with full schema coverage and annotations covering the safety profile, this is nearly complete: it defines the capture, the collision behavior, and the return. Minor gap: given destructiveHint=true, it could say explicitly that the previous checkpoint under that name is lost.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and already examples the name format ('before-sync'), so the baseline is 3. The description goes beyond that by defining name-reuse semantics and by clarifying that the name refers to a captured moment, not a persistent resource.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific action (name the current moment) and enumerates exactly what gets captured: log position, twin time, and fault dice. It also distinguishes itself from the two siblings that consume checkpoints, twin_changes and twin_rewind, so an agent can tell what this tool is for without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the downstream partners and their conditions: use twin_changes to list changes since, twin_rewind to go back. There is no when-not-to-use or prerequisite guidance, but the routing to alternatives is clear enough to choose correctly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_export_logExport the event logA
Read-only

The event log as JSON Lines, one whole event per line, oldest first: everything, or only what happened since a checkpoint. Changes nothing. For a one-line summary of each, use twin_changes.

NameRequiredDescriptionDefault
checkpointNoOnly events after this checkpoint; leave it out for the whole log

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false; the description reinforces this with 'Changes nothing' and adds non-annotation behavior: output format, one event per line, and chronological ordering. It is silent on volume/size expectations for a full-log export, which is the main remaining behavioral unknown.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences plus a routing clause; the output format and ordering come first, which is the most decision-relevant information. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of describing the return value and does so precisely (JSON Lines, one whole event per line, oldest first). For a one-optional-parameter read tool with annotations, this is nearly complete; only expected output volume is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the single checkpoint parameter is already fully documented. The description's 'everything, or only what happened since a checkpoint' restates the schema rather than adding format or semantics the schema lacks, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (the event log), the exact serialization (JSON Lines, one whole event per line), the ordering (oldest first), and the scope (whole log or since a checkpoint). It also names the sibling twin_changes so an agent can tell the two apart without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent to twin_changes when a one-line summary per event is wanted, which is the only realistic alternative among the siblings. It does not state when to prefer the checkpoint form over the full log, though the schema implies that trade-off.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_list_scenariosList ready-made scenariosA
Read-only

The ready-made scenarios twin_load_scenario can load by name, and what each one tests. Changes nothing.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

'Changes nothing' restates readOnlyHint=true rather than adding to it. The genuinely additive part is describing the return contents (names and what each scenario tests), which matters because there is no output schema, but it says nothing about list size, ordering, or pagination.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with the resource first, the consumer second, and a safety note last. No sentence is redundant in substance, though 'Changes nothing' is close to filler given the annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only list tool with no output schema, the description covers what it returns and why an agent would call it. Minor gaps: no hint about how many scenarios come back or whether ordering is stable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is no parameter semantics to document and the baseline applies. Nothing in the description misleads about inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (ready-made scenarios) and a specific payload (the name plus what each scenario tests). It also names the sibling that consumes the output, twin_load_scenario, so an agent can tell it apart from twin_status or twin_changes without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

By saying these are the scenarios 'twin_load_scenario can load by name', it makes the discovery-then-load workflow implicit and clear. It stops short of an explicit 'call this before loading' instruction or any exclusion, so it is strong context rather than a full when/when-not statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_load_scenarioLoad a scenarioA
DestructiveIdempotent

Throw away the current world and build one from a scenario: a ready-made one by name (see twin_list_scenarios), or your own YAML (seed, startTime, world, faults, auth, timeline). Give one of the two. A scenario that doesn't load leaves the twin as it was. twin_reset comes back to its start. Answers with the new status.

NameRequiredDescriptionDefault
nameNoA ready-made scenario's name, like flaky-apis
yamlNoA whole scenario as YAML, instead of a name

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, idempotentHint=true and readOnlyHint=false, so safety is covered; the description still adds the valuable failure-mode guarantee ('a scenario that doesn't load leaves the twin as it was') and states the response is the new status. It does not discuss authorization or scenario size limits, keeping it short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense sentences with the destructive scope front-loaded, no filler, and every clause carrying a distinct fact (source modes, exclusivity, atomicity, undo path, return value).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-param, no-output-schema, fully-annotated tool, the description covers source selection, failure behavior, undo, and what is returned. Nothing an agent needs before invoking it is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description still adds meaning beyond it: the YAML shape is summarized (seed, startTime, world, faults, auth, timeline) and the exclusivity constraint between name and yaml is stated, which the schema's required-count of zero does not convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific destructive verb ('throw away the current world and build one') plus the two source modes, and names siblings (twin_list_scenarios for names, twin_reset for reverting). An agent can distinguish it from every other twin_* tool immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Give one of the two' explicitly states the mutual-exclusion rule, and it routes to twin_list_scenarios to discover names and to twin_reset as the undo path. Both when-to-use and the alternative course of action are spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_resetReset the twinA
DestructiveIdempotent

Throw away everything since the start and rebuild the seeded world (the loaded scenario's, if one is loaded), with its timeline and faults. Checkpoints go too; webhook subscriptions and the auth mode stay. To undo only part of a run, use twin_rewind. Answers with the new status.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark this as destructive and idempotent, but the description adds important detail beyond those flags: checkpoints are removed, webhook subscriptions and auth mode are retained, and the response contains the new status. This clarifies exactly what state is affected, which is critical for a destructive reset tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description front-loads the destructive reset behavior, then states what persists, then names the alternative for partial undo. Every sentence carries useful information without unnecessary repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter destructive reset tool with annotations and no output schema, the description covers the key concerns: scope of destruction, retained state, alternative for partial undo, and the fact that it returns the new status. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so there is no parameter semantics to explain; the baseline for zero parameters is 4. The description appropriately focuses on behavior rather than describing nonexistent inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action and resource: throwing away everything since the start and rebuilding the seeded world with its timeline and faults. It explicitly distinguishes itself from the sibling twin_rewind, which handles only partial undo. An agent can identify what this tool does and when it differs from alternatives without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly routes partial undo to twin_rewind, giving a clear alternative for a narrower operation. It also states what is destroyed and what persists, which helps an agent decide whether this destructive reset is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_rewindRewind to a checkpointA
DestructiveIdempotent

Undo everything recorded after a checkpoint: records, twin time, and fault dice go back, so the same requests meet the same faults again. Checkpoints taken after it are dropped. Answers with the new status.

NameRequiredDescriptionDefault
checkpointYesA checkpoint's name, as given to twin_checkpoint

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint and idempotentHint, so the description is not obligated to cover safety. It still adds real value beyond them: it says later checkpoints are dropped (a non-obvious side effect beyond the data rollback), that fault dice are restored so replay is deterministic, and that the response carries the new status.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each carrying a distinct fact (what rolls back, what is dropped, what is returned), with the destructive scope front-loaded. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, no-output-schema tool with destructive annotations, this covers the effects and the response shape adequately. A brief word on failure when the checkpoint name is unknown would close the remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter is documented there as 'A checkpoint's name, as given to twin_checkpoint'. The description adds no syntax, format, or lookup guidance beyond that, so the schema is doing all the work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb and resource ('Undo everything recorded after a checkpoint') and enumerates the state that is rolled back (records, twin time, fault dice). It is distinguishable from twin_reset and twin_checkpoint by its checkpoint-scoped nature, though it never names those siblings explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: an agent can infer you call this when you want to replay from a known point, but the description never contrasts it with twin_reset (full reset) or twin_load_scenario (scenario switch), which are the obvious alternatives an agent must choose between.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_run_actionMake something happen in a vendor's appA

Make something happen now, as if someone did it in the vendor's own app: jobber newClients or payInvoices; housecallpro newCustomers, completeJobs, invoiceJobs, or payInvoices (quickbooks has none). Each takes params: how many. Recorded with source "control"; its webhooks are delivered before this answers. Answers with the events it recorded.

NameRequiredDescriptionDefault
actionYesThe action's name, like payInvoices
paramsYesHow many, like 2
vendorYesThe vendor: quickbooks, jobber, or housecallpro

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare it is a mutating, open-world, non-idempotent operation, so the bar is lower; the description still adds real context beyond them: records are tagged with source 'control', webhooks fire before the call returns (a strong ordering guarantee agents would otherwise have to discover), and the response is the recorded events. It does not warn about duplicate side effects from repeat calls or auth prerequisites, which keeps it at 4.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Extremely compressed and front-loaded: the core behavior comes first, then the valid actions, then record semantics and return behavior. Punctuation is dense and the vendor/action run-on takes a second pass to parse, but no sentence is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description correctly covers the return value ('answers with the events it recorded') and explains side-effect timing plus source tagging. The only meaningful gap for a mutating, non-idempotent tool is whether repeated identical calls create additional fake records, which is left unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds value the schema lacks: it enumerates the valid vendor/action combinations per vendor (including 'quickbooks has none') and clarifies that the free-form params object is simply 'how many'. Since no param has an enum in the schema, this enumeration is genuinely load-bearing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The title and first clause state the intent ('make something happen now, as if someone did it in the vendor's own app') and the description then enumerates the exact actions per vendor (jobber newClients/payInvoices; housecallpro newCustomers/completeJobs/invoiceJobs/payInvoices; quickbooks none). That enumeration pins down the resource far better than the abstract verb alone. It doesn't explicitly contrast itself with siblings like twin_webhooks or twin_changes, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'now' plus the concrete action list, and there is a useful exclusion ('quickbooks has none'), which prevents a whole class of wasted calls. However, nothing says when to prefer this over siblings such as twin_webhooks, twin_changes, or twin_load_scenario, so the agent must infer the boundary.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_set_authSet the auth modeB
Idempotent

How the vendor APIs treat bearer tokens: "any-token" accepts anything; "oauth" accepts only unexpired tokens from the twin's OAuth endpoints. Changes no records. Answers with the mode.

NameRequiredDescriptionDefault
modeYesThe new auth mode

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, idempotentHint=true and destructiveHint=false; the description adds that it 'Changes no records' and that it 'Answers with the mode', which clarifies the scope of the mutation and the return value. It stops short of saying whether the change persists across resets or requires prior scenario load, and the 'Changes no records' phrasing sits in mild tension with readOnlyHint=false (configuration state does change), though not a true contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with no filler, and the mode semantics come first. The opening quotation is slightly awkwardly formatted, but nothing needs removing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, non-destructive configuration tool with no output schema, the description covers the value semantics and notes that the mode is echoed back. Nothing critical to invoking it correctly is missing, though default/current mode and persistence behavior would round it out.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the enum is already declared, so the baseline would be 3; the description earns more by defining what each enum value actually does at the API layer ('any-token' accepts anything, 'oauth' requires unexpired tokens from the twin's OAuth endpoints). That is genuine meaning beyond 'The new auth mode'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The pair of mode definitions makes clear this tool controls how the twin's vendor APIs validate bearer tokens, which is a specific resource and effect. It never states the action verb outright (the title carries 'Set'), but an agent can identify the tool against siblings like twin_set_faults or twin_set_webhooks by the auth/token subject matter.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains what the modes mean but never says when to switch modes, what the default is, or when to prefer this over other twin configuration tools. Usage must be inferred from the mode semantics alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_set_faultsSet fault rulesA
DestructiveIdempotent

Replace the active fault rules, so the vendor APIs fail the way real ones do. Each rule: { name, vendor: quickbooks|jobber|housecallpro, method?, pathIncludes?, pathExcludes?, rate: 0-1, effect: { status?: HTTP error status, latencyMs?, message?, lostResponse?: do the work, then answer with status } }. A rule with webhook: TOPIC or "*" (and no method, pathIncludes, or pathExcludes) affects webhook deliveries instead: effect { duplicate?: true, delayMs? }. An empty list turns faults off. Bad rules change nothing. Answers with the active rules' names.

NameRequiredDescriptionDefault
rulesYesEvery rule to keep active, shaped as above; [] turns faults off

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well past the annotations: replacement semantics (not merge), the fact that an empty list disables faults, that invalid rules are rejected atomically without changing current state, and that the response lists the active rule names. These are exactly the mutation risks an agent needs to know before calling a destructiveHint tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, and every sentence earns its place: replacement behavior, rule grammar, webhook variant, disabling, validation, and return value. It is a single dense run-on block, though, which makes the inline type notation slower to parse than necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

One required parameter, no output schema, no annotations beyond hints, and a nested rule shape that lives only in prose. The description supplies all of it, including the return value, so an agent has everything needed to construct a valid call.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The JSON schema declares a single array of untyped items, so the description carries the entire rule grammar: field names, the `vendor` union, `rate` range, the `effect` object with status/latencyMs/message/lostResponse, and the alternate webhook rule shape. This is substantial meaning well beyond anything in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Replace the active fault rules") plus its purpose (make vendor APIs fail like the real ones), which immediately distinguishes it from sibling configuration tools like twin_set_auth and twin_set_webhooks. The webhook-overload case is explicitly carved out, so the agent can see this tool's full scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operational triggers: an empty list turns faults off, and webhook faults are expressed here via a `webhook` topic rather than through the hooks-related siblings. It never explicitly says when to prefer twin_set_webhooks or twin_set_auth instead, so the boundary with those siblings is inferred rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_set_webhooksSet webhook subscriptionsA
DestructiveIdempotent

Replace the webhook subscriptions, as an app would set them up in the vendor's developer settings. Each: { vendor: quickbooks | jobber | housecallpro, url, topics, clientId, clientSecret (signs deliveries: for Jobber the app's OAuth client secret, for QuickBooks its verifier token, for Housecall Pro its signing secret) }. quickbooks topics: qbo.customer.created.v1, qbo.customer.updated.v1, qbo.invoice.created.v1, qbo.invoice.updated.v1, qbo.payment.created.v1. jobber topics: CLIENT_CREATE, INVOICE_UPDATE. housecallpro topics: customer.created, customer.updated, job.created, job.scheduled, job.completed, job.paid, invoice.created, invoice.sent, invoice.payment.succeeded, invoice.paid. Answers as twin_webhooks does.

NameRequiredDescriptionDefault
subscriptionsYesEvery subscription to keep, shaped as above; [] removes them all

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructive=true and idempotent=true; the description reinforces the replace-all behavior and adds genuinely new context by explaining that clientSecret signs deliveries and what that secret means per vendor (Jobber OAuth secret, QuickBooks verifier token, Housecall Pro signing secret). It also delegates return shape to twin_webhooks, which is useful, but says nothing about validation failures or whether secrets are stored/echoed back.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and the replacement contract; the long topic lists are dense but earn their place because the schema declares no item structure. The vendor/topic enumeration could be tightened into a table-like form, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive single-parameter tool with no output schema, the description covers the payload completely and points at twin_webhooks for the response shape. Gaps are minor: no mention of validation behavior, partial acceptance, or how the secrets are handled after set.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema's items are an empty object, so the description is the only source of the payload shape and it fully specifies vendor, url, topics, clientId and clientSecret, including per-vendor secret semantics. It also enumerates the valid topic strings for each vendor, which the schema does not do at all.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Replace the webhook subscriptions') and scopes it as the app-side equivalent of the vendor's developer settings. The closing reference to twin_webhooks signals it is the write counterpart of the read sibling, though it never explicitly contrasts the two.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The full-replacement semantics and the '[] removes them all' idiom are stated, which implies when this tool is the right call. There is no explicit 'use twin_webhooks to read' or guidance on ordering, prerequisites, or what happens if a subscription is malformed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_statusTwin statusA
Read-only

Where the test world stands: company, seed, loaded scenario, twin time, event count, auth mode, active faults, checkpoints, and the timeline entries due next. Changes nothing. Call it first to get your bearings.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, and "Changes nothing" reinforces the non-mutating nature. More valuable is the disclosure of what the response contains across nine state areas — behavior an agent cannot learn from any output schema here, since none exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler: the state inventory comes first and the call-to-action second. Every clause carries information the agent can act on.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no parameters, the description carries the burden of explaining what comes back, and it does so thoroughly. Only minor gaps remain — whether the listing is truncated or paginated, and how 'timeline entries due next' is bounded.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; the baseline of 4 applies. The description wisely spends its words on returned state instead of non-existent inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states specifically what the tool reports — an enumerated inventory (company, seed, loaded scenario, twin time, event count, auth mode, faults, checkpoints, upcoming timeline entries) — so the agent knows exactly what state snapshot it returns. It is clearly distinct from siblings like twin_changes or twin_webhooks by that enumeration, though it never names an alternative explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Call it first to get your bearings" gives concrete sequencing guidance that tells the agent when to reach for this tool. It stops short of naming when *not* to use it or pointing at alternatives, so it is strong but not complete.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

twin_webhooksShow webhooksA
Read-only

The webhook subscriptions, without their secrets, and the latest deliveries with how each went. Changes nothing.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover readOnlyHint=true and openWorldHint=false, so 'Changes nothing' is largely redundant. The description does add non-obvious value: secrets are redacted from the returned subscriptions, and the delivery data is limited to the 'latest' ones with their outcomes — details not present in annotations or any output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence with no filler, and the sensitive fact (secrets excluded) is stated early. It reads as a sentence fragment without a leading verb, which slightly hurts front-loading but costs no extra length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, read-only tool with no output schema, the description covers the essentials: what is returned, that secrets are stripped, and that nothing is mutated. It does not say how many 'latest' deliveries are included, a small remaining gap for a tool with no output schema to fall back on.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. No parameter information is missing.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The fragment 'The webhook subscriptions ... and the latest deliveries with how each went' makes clear it returns webhook config plus recent delivery outcomes, but it never states a verb ('show' appears only in the title) and never names twin_set_webhooks as the mutation counterpart. Still, an agent can tell this is the read-only inspection tool for webhooks rather than a scenario/faults tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance and no mention of the sibling twin_set_webhooks, which is the obvious alternative for changing webhook configuration. The only steer is the phrase 'Changes nothing', which is a behavioral statement rather than routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 10 tool updatesv0.5.3
    • Changedtwin_advance_clock1 field changed
      • addedInput schema / properties / minutes / description
        Added value: +"Minutes of twin time to move forward, up to ten years"
    • Changedtwin_changes2 fields changed
      • addedInput schema / properties / checkpoint / description
        Added value: +"A checkpoint's name, as given to twin_checkpoint"
      • addedInput schema / properties / onlyFromApi / description
        Added value: +"True to leave out the seed, the scenario timeline, and control tools"
    • Changedtwin_checkpoint1 field changed
      • addedInput schema / properties / name / description
        Added value: +"Any name, like before-sync"
    • Changedtwin_export_log1 field changed
      • addedInput schema / properties / checkpoint / description
        Added value: +"Only events after this checkpoint; leave it out for the whole log"
    • Changedtwin_load_scenario2 fields changed
      • addedInput schema / properties / name / description
        Added value: +"A ready-made scenario's name, like flaky-apis"
      • addedInput schema / properties / yaml / description
        Added value: +"A whole scenario as YAML, instead of a name"
    • Changedtwin_rewind1 field changed
      • addedInput schema / properties / checkpoint / description
        Added value: +"A checkpoint's name, as given to twin_checkpoint"
    • Changedtwin_run_action3 fields changed
      • addedInput schema / properties / action / description
        Added value: +"The action's name, like payInvoices"
      • addedInput schema / properties / params / description
        Added value: +"How many, like 2"
      • addedInput schema / properties / vendor / description
        Added value: +"The vendor: quickbooks, jobber, or housecallpro"
    • Changedtwin_set_auth1 field changed
      • addedInput schema / properties / mode / description
        Added value: +"The new auth mode"
    • Changedtwin_set_faults1 field changed
      • addedInput schema / properties / rules / description
        Added value: +"Every rule to keep active, shaped as above; [] turns faults off"
    • Changedtwin_set_webhooks1 field changed
      • addedInput schema / properties / subscriptions / description
        Added value: +"Every subscription to keep, shaped as above; [] removes them all"
  2. 14 tool updatesv0.5.2
    • First observedtwin_advance_clock
    • First observedtwin_changes
    • First observedtwin_checkpoint
    • First observedtwin_export_log
    • First observedtwin_list_scenarios
    • First observedtwin_load_scenario
    • First observedtwin_reset
    • First observedtwin_rewind
    • First observedtwin_run_action
    • First observedtwin_set_auth
    • First observedtwin_set_faults
    • First observedtwin_set_webhooks
    • First observedtwin_status
    • First observedtwin_webhooks

TDQS

A4/5.0

Scored across 14 tools

Disambiguation4/5

Most tools target clearly distinct operations (load scenario, set faults, advance clock, rewind), and descriptions actively disambiguate close pairs, e.g. twin_export_log points to twin_changes for a summary and twin_reset points to twin_rewind for partial undo. The main residual confusion is the read/write pair twin_webhooks vs twin_set_webhooks, and the three 'restore world' tools (reset, rewind, load_scenario), which descriptions clarify but which still sit close together.

Naming Consistency4/5

Every tool uses the same 'twin_' prefix and snake_case, giving a strong shared identity. The verb position is mostly predictable (twin_set_*, twin_list_*, twin_export_*, twin_advance_*), but read-only getters break the pattern by using bare nouns (twin_status, twin_webhooks, twin_changes, twin_checkpoint), creating an asymmetry with twin_set_webhooks.

Tool Count5/5

Fourteen tools is well within the sweet spot and each maps to a distinct responsibility in the simulation lifecycle: world setup, environment knobs, time control, observation, and rollback. Nothing feels padded or bolted on.

Completeness4/5

Lifecycle coverage is strong: build/load a world, configure faults, auth and webhooks, move time, trigger actions, observe via status/log/changes, and roll back via checkpoint/rewind/reset. The notable gap is there is no way to inspect the twin's actual records (customers, invoices, jobs) directly—only event streams and status—and no way to delete or list a single checkpoint.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Hosted MCP endpoint that returns realistic fake data for prototyping agents. Paste one URL into Claude Code, Cursor, or Claude Desktop — 12 pre-built tools covering users, products, orders, events, email, and knowledge base search. No signup, no config, no auth. Built for developers who want to prototype agent workflows before wiring up a real backend.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    A deterministic eval engine for coding agents. Your agent claims it fixed the bug—this checks it. The same scenario runs twice against a sandbox of the services your code calls, with failures injected on purpose. It must fail on the old code and pass on the new. The verdict is an exit code, not a model's opinion. Every run leaves a receipt.
    19
    188 npm
    1
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    A local runtime for persistent, isolated replicas of services such as GitHub, GitLab, Bitbucket, Jira, Linear, and YouTrack, providing MCP surfaces for agents to interact with software-company resources.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI agents to read realistic, coherent data—repos, pull requests, issues, commits, users—through API-compatible, read-only replicas of 21 developer platforms including GitHub, Jira, Linear, Slack, Sentry and Salesforce, all preloaded with one simulated company so integrations can be built and tested with no accounts, credentials, rate-limit worry, or cleanup.
    10
    717 npm
    MIT