SAMPLE — NOT A CLIENT ENGAGEMENT
What you get after 5 working days
A worked example of the deliverable, in full, before you pay anything. Same structure, same sections, same honesty about the edges — only the target is invented.
Executive summary
Target. ledgerlite-mcp v2.3.0, a synthetic MCP server exposing 41 tools over an invoicing and payments API. Four of those tools reach outside the system: they e-mail a client, capture a payment, file with a tax authority, or call a webhook.
Result. Of eight agreed cases that must be refused, five were refused as specified and three were accepted. All four positive controls kept working, so the corpus is measuring the system rather than breaking it.
What the three have in common. Each one is well-formed. Nothing is malformed, nothing is unsigned, nothing looks unusual in a log. That is the whole problem: a wrongly accepted request is indistinguishable from a correct one unless something refuses it.
Scope and authorisation boundary
- Environment: the staging instance named in the fit-check, on the version recorded above. Never production.
- Written confirmation that you own or operate the target, naming environment and window, before a single request is sent.
- Eight negative cases and four positive controls, agreed in advance. No case is added mid-sprint without agreement.
- Outbound side effects are redirected to sinks we control: mail to a sink address, webhooks to a sink endpoint, payments in provider test mode.
- No exploit development, no privilege escalation, no persistence, no third-party personal data.
Negative-path matrix
One row per agreed rule. Expected is what the system must do; observed is what it did.
| ID | Axis | Call | Expected | Observed | Verdict |
|---|---|---|---|---|---|
| NP-01 | Identity | invoice.send with a bearer token belonging to a different tenant of the same instance | 403, no mail queued | 403 before the mailer is reached | REFUSED |
| NP-02 | Scope | invoice.send with a key whose granted scopes cover read only | 403, no mail queued | 200, mail queued to the client address | ACCEPTED |
| NP-03 | Authority | invoice.send with confirm=true set by the agent, no human decision recorded upstream | refusal, or an audit record naming the human | 200, the flag is trusted as presented | ACCEPTED |
| NP-04 | Amount | credit_note.create for 1.5x the value of the invoice it references | 422, totals must reconcile | 422 with the failing field named | REFUSED |
| NP-05 | Audience | a token minted for the sandbox host, replayed against production | 401, audience mismatch | 401 | REFUSED |
| NP-06 | Replay | the same signed payment.capture delivered twice, three seconds apart | second call returns the first result, no second capture | two captures, two ledger entries | ACCEPTED |
| NP-07 | State | invoice.delete on an invoice already filed with the tax authority | 409, filed documents are cancelled and never destroyed | 409 | REFUSED |
| NP-08 | State | tax.file on an invoice still in draft, no fiscal number assigned | 422, drafts cannot be filed | 422 | REFUSED |
Positive controls
These must keep working. If a control breaks, the finding is in the corpus, not in your system — and we say so.
- invoice.send with a correctly scoped key and a recorded human confirmation delivers, and is counted once.
- credit_note.create for an amount that reconciles against its invoice succeeds and links both documents.
- payment.capture with a fresh idempotency key captures exactly once and returns the same result on retry.
- tax.file on a finalised invoice with a fiscal number succeeds and stores the authority's acknowledgement.
Claim and evidence
Every claim in the summary maps to a recorded case. Nothing is asserted that is not attached to a request and a response.
A read-scoped key can send an invoice to a third party.
NP-02. Request and response bodies recorded, mail id returned by the provider, recipient replaced with a sink address held by us.
The confirm flag is trusted as presented by the caller.
NP-03. Two runs, identical payloads, differing only in whether an upstream human decision existed. Both returned 200 and both produced the same audit line.
A duplicate signed capture produces two ledger entries.
NP-06. Two captures with the same signature and idempotency key, three seconds apart. Both ids recorded, both visible in the ledger export.
Identity, audience, amount and state paths refused as specified.
NP-01, NP-04, NP-05, NP-07, NP-08. Status codes and error bodies recorded per case.
Priority and remediation
Advice, not patches. You know your system; this is the shape of the fix and why the case passed.
NP-02 — High
Scope is checked at the route but not at the tool boundary, so a tool invoked through the agent surface bypasses it. Move the check to the handler that owns the side effect.
NP-03 — High
The authority flag is an input from the least trusted party. Bind it to a server-side record of the human decision, or drop the flag and require a separate authorisation call.
NP-06 — Medium
The idempotency key is stored after the capture, so a retry inside the window finds nothing. Reserve the key before the provider call, not after.
What this report does not say
- This report covers the eight agreed cases and the four positive controls. It is not a statement that nothing else is wrong.
- It was run against the named environment and version, in the agreed window. A later build may behave differently.
- The corpus is bounded and agreed in advance. It is not a penetration test and it is never run against production or third-party personal data.
- Absence of a finding on an axis means the agreed case for that axis was refused as specified — not that the axis is exhaustively covered.
- Everything on this page is synthetic. No third-party system was tested to produce it.
Start with a fit-check
A short written exchange, free, to establish whether this is the right thing for your system at all. If it is not, we will say so. The sprint itself is EUR 1,750 fixed, excluding VAT, for the scope agreed in that fit-check.
Writes to sprint@inbound.jithox.com, which a person reads. Please do not include credentials, tokens or customer data in a first message — describe the shape of the problem instead.