shiftguard
Optionally sends run telemetry and events to PostHog for observability when SHIFTGUARD_TELEMETRY is enabled and a project key is configured.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@shiftguardcheck the proposed rota against the café rules"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
taste-to-evals
An agent interviews a business owner, turns her plain-language rules into typed checks (and an AI-judge rubric for the one rule code can't decide), and gates every proposal another agent makes on them before a human approves.
The demo case is ShiftGuard, running at Ollie's, an invented café in Dublin. Its owner, Gráinne, never wrote her rules down. A rota assistant, which has never seen her rules and can't save anything, has to get every draft past her gate. Only her Approve changes the week.

Presented at AI Tinkerers Dublin, 28 October 2026. Everything runs locally; every name and number is synthetic.
The problem
Agents are starting to act on businesses: drafting rotas, answering customers, booking tables. The people who know what "good" looks like are the owners, and their rules live in their heads, as taste. They aren't written down, they have exceptions, and the agent acting for them (often a vendor's, with a prompt they don't control) doesn't know them.
Evals usually get written by engineers, after the fact. This project tries the other direction: start from the owner's taste and end with tests that run before anything happens.
Related MCP server: Carbo MCP Gateway
How it works
flowchart LR
O["Owner<br/>(her notes, her stories)"] -->|"interview:<br/>what went wrong?"| I["Interviewer<br/>(Claude)"]
I -->|precise rule + kind| D{"Can code<br/>decide it?"}
D -->|yes| C["Typed check<br/>written by Claude,<br/>type-checked, sandboxed"]
D -->|no| J["Rubric for the<br/>AI judge, labelled as AI"]
C --> P["Proven on her<br/>good and bad weeks<br/>(all right, or it doesn't ship)"]
J --> P
A["Acting agent<br/>read-only tools,<br/>never sees the rules"] -->|proposal| G["Gate:<br/>code checks first,<br/>judge last"]
P --> G
G -->|"what broke, in plain words"| A
G -->|all pass| H["Owner's Approve<br/>re-runs the checks,<br/>the only write"]The interview asks about bad weeks, not rules. "Tell me about a time a rota went wrong on hours." Gráinne: Tom did 46 hours covering for Niamh, and dropped a full tray at the Saturday rush. Out comes Max 40 hours a week, breaks counted, exactly 40 allowed, no exceptions for sick cover. (
src/interview.ts)Each rule becomes a check, or a rubric. If code can decide it from the facts, Claude writes a TypeScript
check(rota)function. If it needs judgement ("weekends shared fairly"), it becomes a rubric for an AI judge that's labelled as AI everywhere it shows up. (src/generate.ts,src/judge.ts, output inchecks/)No rule ships without examples. Every check runs against weeks the owner calls fine and weeks she doesn't, plus edge cases Claude writes. It has to get all of them right before it guards anything. (
src/setup.ts,src/examples.ts)The acting agent is boxed in. The rota assistant gets two tools,
read_weekandread_staff. It never sees her rules; when a draft breaks one, it hears only what broke ("Tom would work 48 hours. Your limit is 40."). Tests fail if it's ever given a tool that isn't read-only or can reach the store. (src/agent.ts,src/agent.test.ts)Code first, judge last. The gate runs every typed check. The AI judge is asked only when they all pass, so the model never gets a vote on something arithmetic can settle. (
src/gate.ts)Only the human writes. Approve is the single code path that saves a rota, and it re-runs every check instead of trusting the stored result. (
src/store.ts)Every run leaves evidence. A one-page PDF per run: what was asked, every try, what each rule said, the judge's reason, who approved, what it cost. Sample. (
src/evidence.ts)
The full walk-through, with the data model and the safety boundaries, is in docs/how-it-works.md.
Her words become a check | The gate sends the draft back |
|
|
Second act: the same gate on a website chatbot
At the demo, people asked whether this only works for rotas. It doesn't: the second act points the same gate at Ollie's website chat, which talks to customers with nobody checking it first.
Gráinne's chatbot rules come from the same interview: never call anything allergen-safe unless her allergen sheet says so, no discounts ever, bookings up to 8 between 7am and 7pm, and "sound like us" (judged).
Code can't read free text reliably, so Claude reads, code decides: Claude lists only what a draft reply promises (a booking for 10 at 7:30pm), and her typed checks decide on those promises.
Customers can't wait for a human, so here the gate is the approval. A passing reply goes out in about a second. A stopped draft goes back to the bot with only what broke. After three stopped drafts the customer gets a holding line and the question waits for Gráinne.

Where else the gate fits
The pattern needs three things: an agent that proposes instead of acting, rules the owner can tell stories about, and a single human or code path that writes. Places it maps onto directly:
Agent | Owner's rules that become checks | Judged |
Refunds and returns | Never above €50 without a person; never on final-sale items | "Was the customer treated fairly?" |
Purchasing | Only approved suppliers; nothing over budget per category | "Is this a sensible order for the week?" |
Appointment booking | Never double-book a room; 15 minutes between clients | Tone of the confirmation |
Outbound email | Never mention pricing that isn't on the rate card; no attachments | "Does this sound like us?" |
Pricing changes | Never below cost; never more than 10% in a day | "Does it fit the brand?" |
The domain types (src/domain.ts, src/chat/domain.ts) and the owner's notes (data/ollies/) are café-specific; the interview, generate, prove, gate, judge and approve loop is not.
Use the gate from any agent (MCP)
The gate is also an MCP server, so Claude Desktop, Claude Code or any other MCP client has to pass the same rules. Every tool is read-only; there's no tool that saves a rota or sends a reply.
Tool | What it does |
| Gráinne's rules, in her words, and whether code or the AI judge checks each one |
| This week's rota, every shift with its id |
| Staff, roles, who's trained on the coffee machine, who can't work when |
| Runs a proposed change (absences, shifts removed, shifts added) through the gate; returns what broke |
| Reads what a customer reply promises and checks it; returns whether it may be sent |
Claude Code:
claude mcp add shiftguard -- "$(pwd)/node_modules/.bin/tsx" "$(pwd)/src/mcp.ts"Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"shiftguard": {
"command": "/absolute/path/to/taste-to-evals/node_modules/.bin/tsx",
"args": ["/absolute/path/to/taste-to-evals/src/mcp.ts"]
}
}
}The server reads the project's .env. Then ask: "Sarah's off sick all next week. Redo the rota so it passes Gráinne's rules." Code checks run without a key. The AI judge and check_chat_reply call Claude, so they need ANTHROPIC_API_KEY in .env.
Quick start
Requires Node.js 22.13 or later (it uses the built-in node:sqlite) and pnpm.
git clone https://github.com/AIwithDiego/taste-to-evals && cd taste-to-evals
pnpm install
pnpm demo # http://localhost:4321Pick an act on the title card and press Start. It replays a recorded run from replay/: no API key, no internet. Press → or Space to move on; Approve is a real click. Technical detail (bottom left) shows the check code, the judge's reasoning, and every Claude call with its cost.
To run it live, copy .env.example to .env, add your ANTHROPIC_API_KEY and press Start live. Live runs are saved to runs/, and Start then replays your latest one.
Command | What it does |
| The demo screen (replay or live) |
| Interview, write the check, prove it, in the terminal. Rules: |
| One rota request through the assistant and the gate |
| Approve a passing proposal (re-checks first) |
| Same as rota-setup, for the chatbot rules |
| A customer conversation, every draft through the gate |
| The MCP server over stdio |
| 31 tests; no key or network needed |
Live setup rewrites the files in checks/. Those files are what Claude wrote for the recorded runs, formatted for reading.
What it cost
Everything runs on Claude Haiku 5.5 (src/claude.ts).
Step | Claude calls | Cost |
Setting up all ten rules (interview, write, prove) | 144 | about $0.10 |
One rota request, two tries, judged and approved | 5 | about $0.01 |
A customer conversation, two replies, one stopped draft | 8 | about $0.001 |
Observability (optional)
Set SHIFTGUARD_TELEMETRY=on and a PostHog project key in .env, and every live run becomes one trace in PostHog's LLM analytics. Each Claude call is a generation with its tokens and cost, each of the owner's checks is a span in the same trace, and shiftguard_check / shiftguard_gate_run events give the failure rate by rule, code versus judge. Traces include the full prompts and replies. It's off by default. (src/telemetry.ts)
Safety boundaries, stated plainly
The checks Claude writes are screened (no imports, no
process, noeval, no timers or network names) and run in anode:vmcontext with nothing in it, code generation from strings disabled, and a one-second limit. That's enough for a local demo with synthetic data.node:vmis not a security boundary, so in production run generated code in a separate process or an isolate with no file or network access, and review it before it guards anything real.The acting agents have read-only tools and no access to the store; tests enforce both.
Writes happen in exactly two places,
approve()for a rota anddeliver()for a chat reply, and both re-run the code checks before writing.
What I learned
The longer version, including what didn't work, is in LEARNINGS.md. The short version:
Owners don't know their rules until something breaks. Ask about the bad week, not the rule.
Every rule ships with a passing and a failing example, or it isn't a rule yet.
Code checks first. The AI judge goes last, only for what code can't decide, and it's labelled.
The agent that acts has no write tool. Only the approve step writes, and only after a passing run.
For an agent that talks to customers, the gate is the approval. Whatever can't pass goes to a person.
Project layout
src/ interview, generate, prove, gate, judge, store, evidence, MCP server, demo server
src/chat/ the website chatbot act: bot, claims reader, chat gate, delivery
checks/ the checks and rubrics Claude wrote from the interview (*.ts, *.rubric.json, *.rule.json)
data/ollies/ the owner's notes, staff, menu, allergen sheet, weekend history (all invented)
web/ the demo screen (vanilla TypeScript, bundled with esbuild on request)
replay/ one recorded run of each kind, so the demo works offline
docs/ how it works, decisions, sample evidence page, mediaBuilt with
TypeScript, Claude (Haiku 5.5) through the Anthropic SDK with structured outputs, the official MCP TypeScript SDK, Node's built-in SQLite, esbuild, and PostHog for traces. No framework on the front end.
License
MIT. Built by Diego Bauer in Dublin.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Find, vet, and run MCP tools through a secure audited gateway with prompt-injection risk scoring
Read-only search of 2,189 AI agent tools (MCP servers, x402 APIs) with daily automated trust scores.
Read-only MCP tools for AI agent discovery, structured resources, and NIULAI information.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides safe local-model MCP support tools (summarization, classification, critique, draft) for a cloud owner agent, with strict boundary guards to prevent risky actions.2MIT
- FlicenseNot gradedqualityBmaintenanceA read-only, OAuth 2.1-authenticated MCP server that lets Claude inspect the live state of a self-hosted server — system resources, containers, service and website health, n8n automations, video jobs, backups, repositories, error summaries, a component design system, uptime monitoring, and its own audit trail. All twenty-one tools are strictly observation-only, with no shell, file browser, SQL console, or Docker socket, so nothing can be started, stopped, deployed, or deleted.-
- AlicenseNot gradedqualityCmaintenanceLets MCP clients such as Claude and ChatGPT read a rota and time-and-attendance account, exposing venues, groups, shifts, absences and absence types, time entries, venue events and staff names through read-only tools that never return pay data.MIT
- AlicenseNot gradedqualityBmaintenanceA read-only MCP server that lets an agent check whether a public MCP server's tool definitions have changed, and whether any locally pinned server has a change waiting for review. It also returns the exact setup steps for protecting servers in Claude Code, Cursor, Codex, VS Code and others, without ever exposing third-party tool text.52 npmMIT

