InterLogue
InterLogue is an MCP server that takes a one-sentence brief about a person, interviews them (text or real phone call), and produces a citation-verified published piece where every quote links to a transcript timestamp. Capabilities include:
Brief a subject: pass name, phone, and a one-sentence description; the server infers role, company, genre, and builds an angle-driven question plan (nobody is contacted).
Record human approval: gate all outreach behind an approval record — over stdio a named approver with a verbatim statement; over HTTP a signed one-time click link; the dial gate refuses interviews and calls without it.
Run interviews: text-only via a fictional fixture or inline turns (
run_interview), or place a real outbound call (place_call) and fetch a timestamped, consent-checked transcript (fetch_transcript) through ElevenLabs/Twilio.Get the reporter's packet (
draft_piece): brief, question plan, writing contract, ranked verbatim quote candidates, and the full transcript with contact details masked.Fact-check a piece (
check_citations): validate section order, every quoted span verbatim at its (MM:SS), no unquoted numbers or repeated frames; a clean pass stores the piece, a fail returns every failing span.Write the piece server-side (
write_piece/generate_piece): deterministic fallback or Claude-model writer, submitted to the same citation check with one retry.Track status of a brief through the spine (brief → approve → interview → draft → check) and get the next tool to call.
Withdraw a subject: preview and confirm with a token, then replace all their records with tombstones (append-only except this sanctioned removal), plus audit lines and external references to delete by hand.
Ledger: cost and duration per brief from call records, pieces per brief, per-ten-minutes figures (metadata only, no names or numbers).
Quote check: show the subject the exact words a piece attributes to them with timestamps, record confirm/dispute; a disputed piece blocks asset derivation.
Derive assets: LinkedIn post, short post under 280 characters, and email teaser from a stored piece, re-verifying every quote against the transcript.
Hosted/remote mode: Streamable HTTP with bearer tokens, per-tenant data isolation, signed approval pages and webhook, health check, Docker/Render deploy.
Stubs only:
discover_contacts,question_templates, anddownload_recordingexist for shape — automated contact discovery and audio downloads are not in this build; the timestamped transcript is the record.
Places outbound phone calls through an ElevenLabs agent, using the six dynamic variables from the brief to conduct AI-led interviews.
Uses Twilio as the native telephony integration for the phone path, enabling real outbound calls through the ElevenLabs agent.
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., "@InterLogueInterview Marisol Teague and fact-check every quote before publishing"
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.
InterLogue
Brief it the way an editor briefs a reporter. It interviews the subject and returns a published piece with every quote cited to a timestamp in the transcript.
InterLogue is an MCP server. Inside a host such as Claude, the host writes the
piece and InterLogue is the fact-checker: nothing is stored as a piece until every
quoted span resolves, verbatim, to a subject turn at the cited timestamp. A product
with no host model calls write_piece instead and the server writes the piece, then
puts it through the same check. The spine is:
brief -> approve_contact -> interview (place_call + fetch_transcript, or run_interview) -> draft_piece -> [the host writes] -> check_citations
or -> write_piece (the server writes, same check)The output leads with the story. Pull quotes come second, each with its timestamp. The per-question answers come third, as a secondary view. Every piece ends with a line that says whether a recorded call stands behind it.
The one-sentence brief
The brief tool needs three things: the person's name, their phone number, and one
sentence in the user's words about who they are and what the piece is about.
Interview Marisol Teague, +1-502-555-0142, founder of Ridgeline Provisions, about switching her order entry to Tallyhook, for Tallyhook's marketing team.
Role, company, client, product, topic, angle and genre are inferred from the sentence
or defaulted, and the result names every choice. Four genres share the same seven
story beats: a subject at another company than the client gets the customer case
study plan; a subject whose company is the client gets the founder story plan; a
sentence that says podcast or episode gets the podcast segment plan (a hook question
first, a closing question that invites a story); one that says testimonial gets the
testimonial plan (five beats, ending on a recommendation). An explicit genre field
overrides all of that, and question_templates lists what each genre asks. A phone
number, email address or street address inside the sentence is stripped before
anything is inferred, so it never becomes the angle.
Related MCP server: Mission
The three rules
Output leads with the story. A bulleted question-and-answer summary is meeting minutes, not a piece. The checker fails a piece that is not prose first.
No cold outreach. A human approves the specific person and number before any dial. The agent states it is an AI and asks permission to record at the top of the call. A subject who declines to be recorded stops the interview: nothing is stored.
No agenda steering. The user sets an angle, which shapes which questions are asked and what is emphasized. It never shapes what the subject is portrayed as having said. Every claim and quote links to a timestamp in the transcript.
The tools as a host sees them
Tool | Inputs | What it does |
|
| Stores the brief and builds the question plan from the sentence, in one of four genres, and says how the genre was chosen. Nobody is contacted. |
|
| Over stdio: records that a named human approved contacting this brief's subject at this brief's number. Over HTTP: records nothing and returns a one-time approval link for a person to click; only the click counts. A name or number that differs from the brief refuses. See "What the approval record is" below. |
|
| Text-only interview from the fictional fixture or from turns supplied inline. Refuses without an approval on file and refuses a transcript whose subject declined to be recorded. |
|
| Phone path, step 1. Refused by the dial gate without the approval record. Places one outbound call through the ElevenLabs agent over Twilio. Present only when the ElevenLabs variables are set. |
|
| Phone path, step 2. Waits for the call to end, fetches and normalizes the transcript with real timestamps, runs the consent check, stores it append-only. A refusal to be recorded stores nothing and marks the call |
|
| Returns the reporter's packet: brief, question plan, writing contract, ranked verbatim quote candidates with timestamps, and the full transcript, with contact details masked. |
|
| The fact-checker and, with |
|
| The server writes the piece from the stored transcript, with the configured Claude model when |
|
| The deterministic fallback writer for the no-host run. Registered only when |
|
| Where a brief sits in the spine and what to call next. Names a withdrawn brief as withdrawn. With a transcript on file it also names the planned beats the call never reached and the questions to send the subject by email. |
|
| Replaces every record of a subject with tombstones at their request, after a preview. Returns the external references the operator must delete by hand. |
| optional | Cost and duration per brief and in total from the call records (credits and USD as ElevenLabs reported them), pieces per brief, a per-ten-minutes figure, and what one stored interview costs against the two hours of a marketer's time it replaces (dollars with |
|
| The subject sees exactly the words a stored piece attributes to them, each with its timestamp, and confirms or disputes them before anything is published. Hosted: a signed one-time page, delivered through the operator's channel when one is configured. Stdio: the message to send, and the answer relayed by a person. Recorded per piece, shown by |
|
| A LinkedIn post, a short post under 280 characters and an email teaser from a stored piece, built only from the piece's own sentences and pull quotes, every quote re-verified against the transcript and kept with its timestamp. Nothing stored. |
|
| Stub: "not in this build". Automated contact discovery is not part of InterLogue. |
| optional | The four genres and the questions each asks, with placeholders. |
|
| Stub: "not in this build". The timestamped transcript is the record. |
Every tool returns readable text: a heading line, the facts, then a Next: line
naming the tool to call next. A refusal sets isError. Phone numbers are never
echoed beyond their last four digits.
A judge can try it two ways
Requires Node 20 or newer. No env file, no API keys.
Path 1: one command, no Claude needed (the fallback writer)
git clone https://github.com/aminehamlouchi/interlogue.git interlogue && cd interlogue && npm install && npm run spinenpm run spine builds the server, starts it over stdio, and drives the tools in order
with the fictional fixture using the deterministic reporter as the writer. It prints
the generated piece and the citation check, and exits non-zero if any quote fails to
resolve. It also proves the gate: it calls run_interview before approve_contact
and confirms the refusal.
npm testRuns the test suite: the dial gate, the append-only store and its tombstones,
consent detection and the consent gate, the redaction of contact details, the
fallback writer, the host-piece checker with tamper tests, withdrawal, the
server-side writer behind the citation gate (with a fake writer and a canned piece
carrying a quote the subject never said), the hosted entry (bearer token, session
isolation, health check, the approval link, the post-call webhook with signed and
tampered payloads, tenants), the four genres, the ledger, and the partner sidecar.
Nothing in the suite reads a real transcript unless INTERLOGUE_REAL_DATA_DIR names
a directory that holds one, nothing in the suite can reach the phone path, and no
test calls a model: the Anthropic writer runs against a fake client.
Path 3: the hosted flow, one command, no keys
npm run demo:hostedStarts the hosted entry in-process on a loopback port with a token and an approval
secret minted for the run, then drives it the way a partner's product would, with
the sidecar example over plain JSON-RPC: brief, an approval link issued and nothing
recorded, the gate refusing before the click, the link opened and the button pressed
(by the script here; by the person the operator sent it to in a deployment), the
fixture interview, write_piece with the deterministic reporter, the subject's
quote_check (the one-time page opened and "These are my words" pressed by the
script; by the subject in a deployment), derive_assets with the LinkedIn post
printed, status and ledger. It prints the piece and exits non-zero if any step
refuses. No call is
placed, no model is called, no key is used. This is the flow to show a partner first.
Path 2: inside Claude, where Claude writes the piece
Claude Desktop: after npm install && npm run build in the cloned folder, quit Claude
Desktop fully (it rewrites its config from memory while running) and run:
npm run register-desktopIt backs up the config, adds or refreshes only the interlogue entry (with the env-file
flag when .env exists), and leaves every other entry alone. Or add the entry by hand:
{
"mcpServers": {
"interlogue": {
"command": "node",
"args": ["/absolute/path/to/interlogue/dist/src/index.js"]
}
}
}Claude Code: the repo ships a .mcp.json, so opening the folder registers the
interlogue server after npm run build.
Paste the one-sentence brief above into Claude and add: "I approve contacting her at
that number. Use the founder-case-study fixture instead of calling, then write the
piece and show it to me." Claude calls brief, approve_contact, run_interview,
draft_piece, writes the piece, and check_citations stores it or returns every
failing span with the closest transcript turn.
From a shell, without a host:
npm run tool -- brief '{"subject_name":"Marisol Teague","subject_phone":"+1-502-555-0142","about":"founder of Ridgeline Provisions, about switching her order entry to Tallyhook, for Tallyhook'"'"'s marketing team"}'npm run tool -- approve_contact '{"brief_id":"<brief_id>","approved_by":"Your Name","statement":"I approve contacting this person at this number."}'npm run tool -- run_interview '{"brief_id":"<brief_id>","fixture":"founder-case-study"}'npm run tool -- draft_piece '{"brief_id":"<brief_id>"}'npm run tool -- check_citations '{"brief_id":"<brief_id>"}' --markdown-file my-piece.mdThe writing contract
draft_piece hands the writer a contract and check_citations enforces the parts it
can enforce mechanically:
Order: H1 headline, story as prose, then
## Pull quotes. The per-question view is appended by the checker from the transcript.Every quoted span is verbatim from a subject turn and is followed by its
(MM:SS). A span without a timestamp, or a timestamp without a span, fails. One ellipsis is allowed inside a single turn, in order, with fragments of at least three words.No quotes in the dek or the byline: they would sit above the fold with no timestamp.
Connective prose may frame and sequence but may not assert a fact about the subject outside a cited quote. A number outside a quote fails.
The angle chooses emphasis, never words. More than one sentence starting with "Asked", or "said:" more than once, fails.
Three to seven pull quotes. Plain, reported voice.
A thin interview must say so in the headline;
draft_piecegrades the interview and the packet says when that clause applies.
Hosted deploy on your own keys
src/remote.ts serves the same tools over the MCP Streamable HTTP transport, for a
partner who wants to run InterLogue on their own infrastructure with their own
ElevenLabs account.
Endpoint:
POST,GETandDELETEon/mcp. Every request must carryAuthorization: Bearer <INTERLOGUE_TOKEN>. A missing or wrong token, or a server with no token set, is refused with401before the request body is read./healthzanswersGETwithout a token with liveness only:{ ok, version, token_configured, sessions }. With a tenant's bearer token it adds that tenant's owntools,phone_tools,webhook_configuredandapproval_links. No subject data, no list of tenants./approve/<token>: the approval page. Over HTTPapprove_contactrecords nothing and returns this link for a person to open; the page shows the subject's name, the masked number, the client and the approver with one button, "I approve this call". The click records the approval and the dial gate accepts only such a record. The token is an HMAC keyed withINTERLOGUE_APPROVAL_SECRET(never the bearer token, so a caller holding the token cannot mint a link; without the secret no link is issued); the URL carries only ids, an expiry and a nonce, nothing about the person; it expires after 24 hours (INTERLOGUE_APPROVAL_TTL_SECS, capped at a week), works once, is replaced by a newer link for the same brief, and a tampered, stale or superseded link records nothing. The signature is checked before anything is read from disk.INTERLOGUE_PUBLIC_URLsets the host in the link. WithINTERLOGUE_APPROVAL_DELIVERY_URLset, the link is posted there (signed withINTERLOGUE_APPROVAL_DELIVERY_SECRET, last four digits of the number only) instead of being returned to the caller, which then learns only that it was sent: the operator's own channel is what hands the link to a person, and the calling program never holds it./webhooks/elevenlabs: the ElevenLabs post-call webhook, verified withELEVENLABS_WEBHOOK_SECRETagainst the raw body (ElevenLabs-Signature: t=<seconds>,v0=<HMAC-SHA256 over "<timestamp>.<body>">, 30 minute tolerance). A verified transcript is stored through the same path asfetch_transcript, consent check included; polling stays as the fallback.Several operators on one server:
INTERLOGUE_TOKENS="id:token,id2:token2"with per-tenantINTERLOGUE_<ID>_*variables for the data root, the public URL, the ElevenLabs keys and webhook secret, the Anthropic key and writer model, the approval secret and the link lifetime. Each tenant's sessions, data and keys are its own; data roots must be distinct and must not nest, or the server refuses to start; the webhook is/webhooks/elevenlabs/<id>.INTEGRATION.mdlists the variables.The server-side writer: with
ANTHROPIC_API_KEYset,write_piecewrites with Claude (INTERLOGUE_WRITER_MODEL, defaultclaude-sonnet-5); without it, with the deterministic reporter. Either way the piece goes throughcheck_citations's check and is stored only on a clean pass.One data directory per MCP session under
INTERLOGUE_DATA_ROOT(defaultdata/sessions). Two clients of the same deployment never see each other's briefs. A session's directory stays on disk when the session ends, so withdrawal can still find it.The phone tools (
place_call,fetch_transcript) are registered only whenELEVENLABS_API_KEY,ELEVENLABS_AGENT_IDandELEVENLABS_PHONE_NUMBER_IDare all set. Without them the deployment is text-only and cannot dial.PORTdefaults to 3000.INTERLOGUE_ALLOW_FALLBACK=1adds the deterministic writer.
Run it locally:
npm run build && INTERLOGUE_TOKEN=$(openssl rand -hex 24) npm run start:remoteDocker (Node 20, npm ci, npm run build, runs as a non-root user, copies no .env):
docker build -t interlogue . && docker run -p 3000:3000 -e INTERLOGUE_TOKEN=<secret> -v interlogue-data:/data interloguerender.yaml is a Render blueprint for the same image. Every secret in it is declared
unset (sync: false) and is entered in the Render dashboard; autoDeploy is off.
Nothing in the repo deploys anything. The Dockerfile was written and read, not built,
during the run that added it; the first build is on whoever deploys it.
A client connects with the SDK's StreamableHTTPClientTransport and a
requestInit.headers.authorization of Bearer <token>, or with plain JSON-RPC over
HTTP as examples/sidecar/sidecar.ts does. INTEGRATION.md has the request and
response shapes, the approval and webhook flows, and the tenant variables;
OPERATIONS.md has what to set, what to watch, how to rotate a secret and how to
handle a withdrawal.
Withdrawal
The consent script promises a subject can ask afterwards to have their interview
thrown out. withdraw_subject honours that:
Called with a
brief_id, or with asubject_nameandphonethat match exactly one approval record (two matches refuse as ambiguous, none refuse), it previews what would be removed and returns aconfirm_token. Nothing is removed.Called again with
confirm: trueand that token, it replaces the brief, the approval, the call record, the transcript and every piece with a tombstone that holds only the id, the time and the reason"withdrawn". A tombstone is never overwritten by a later save, and every loader treats it as withdrawn: the dial gate refuses,statusreports it,draft_piecehas nothing to draft from.Two audit lines go to
data/audit/withdrawals.jsonl, the first before any record is touched. They carry ids and external references only: no name, number or text.The result names what the operator must delete by hand: the ElevenLabs conversation id (audio and their transcript copy) and the Twilio call SID (the call log). The tool never calls either service.
Withdrawal is the one sanctioned exception to the append-only transcript rule, and it removes rather than edits.
Redaction of contact details
The stored transcript is verbatim. Everything derived from it for display is masked:
a phone number becomes [phone number removed], an email address [email removed],
a street address [address removed]. That covers the reporter's packet (quote
candidates and the full transcript), the per-question view of a piece, the story and
pull quotes of a stored piece, the citation report, the opening lines
fetch_transcript shows, and the hints in a failing check. The matchers are written
for speech: "five oh two, five five five, oh one four two" and "marisol at ridgeline
dot com" count; year ranges, money and business figures do not.
The citation check runs against the verbatim turn first. A quote that wholly contains a contact detail resolves and is stored masked. A quote that cuts through one fails. A quote copied from the masked packet, mask token included, resolves against the masked rendering of the turn. After a clean check the piece is masked and a leak check confirms no cited contact detail survives; if one did, the check is marked failed and nothing is stored.
Where data lives
Everything at runtime goes under data/, which is gitignored, so subject names and
numbers never enter git. The hosted entry uses one such tree per session under
INTERLOGUE_DATA_ROOT; the stdio server uses INTERLOGUE_DATA_DIR or data/.
data/briefs/<brief_id>.json name, phone, role, company, client, topic, angle, question plan
data/approvals/<brief_id>.json who approved, when, their statement verbatim; earlier approvals under "replaced"
data/transcripts/<brief_id>.json append-only; turns with time_in_call_secs and MM:SS; consent evidence
data/pieces/<brief_id>_pc_<id>.json story, pull quotes, per-question view, citation check, markdown (contact details masked)
data/calls/<brief_id>.json conversation id, call SID, timing, cost, last four digits only
data/audit/withdrawals.jsonl withdrawal events: ids and external references onlyA withdrawn record is { "id", "withdrawn_at", "reason": "withdrawn" } at the same
path. The fixture under fixtures/ is fictional and committed; every name, company
and number in it is invented, and the 555-01XX range is reserved for fiction.
The quote check: the subject's word on their words
A piece attributes quotes to a person. quote_check shows that person those quotes,
each with the minute in the call it was said, and records one answer per piece:
confirmed, or disputed with the quote numbers and a note of up to 500 characters.
Hosted, the page at /quotes/<token> is signed with the approval secret, verified
before any disk is read, answers once, expires with the approval lifetime, and shows
no phone number; with a delivery channel the link goes to the operator's endpoint as
a quote_check payload and the calling program never holds it. Over stdio the tool
returns the message to send and a person records the answer with relayed_by. A
disputed piece blocks derive_assets; the fix is a new piece from the same
transcript without the disputed quotes. The page and the message both tell the
subject that the whole interview can be withdrawn on request, which is what
withdraw_subject does. Nothing publishes on its own either way: the check is a
record for the human review, not a gate the server enforces.
Security: what the approval record is and is not
The approval record is the only thing that unlocks a dial, so it is worth being exact about what it proves.
It is: a file under data/approvals/ that says a named person (approved_by)
approved contacting this brief's subject at this brief's number, with the statement
they made stored verbatim, and the time. place_call and run_interview both call
the same assertDialApproved in src/gate/dialGate.ts, which requires that file to
exist, not be a tombstone, and name the brief's exact subject and number. A name or
number that differs from the brief is refused at approve_contact, so the record can
only ever cover the person on the brief. Replacing an approval keeps the earlier ones
inside the record.
It is not, over stdio: an identity check, a login, or proof that a human was in
the loop. There is no user authentication. approved_by and statement are whatever
the host passes, and the tool descriptions tell the host that a user request which
names the person and asks to contact them already counts as the approval. In a Claude
session the human is the one typing that request; in any other host the operator has
to make sure that is still true.
Over HTTP the hosted entry supplies the human-only surface: approve_contact records
nothing and returns a signed one-time link; a person opens it, sees the name, the
masked number, the client and the approver, and clicks "I approve this call"; the
record that click makes (approved_via: "click") is the only kind the dial gate
accepts there. Be exact about what that proves: that whoever held the link pressed
the button. The calling program receives the link from approve_contact in order to
hand it to a person, so it could press the button itself; the server cannot tell a
browser from a program. The link is signed with a secret the caller does not hold,
so it cannot mint links, but it can use the one it was given. The human step is therefore the operator's delivery of the link to a person and nobody else. A
deployment that sets INTERLOGUE_APPROVAL_DELIVERY_URL closes that gap: the server
posts the link to the operator's own channel and the calling program never receives
it, so the click can only come from someone that channel reached.
Other facts a partner should rely on, each verified by a test:
Credentials are read from
process.envonly, never from a file by this code, never printed, and the ElevenLabs base URL is fixed so no environment value can redirect a request carrying the key.Nothing reaches
placeOutboundCallexceptplace_call, after the gate. The test suite deletes everyELEVENLABS_*variable before the tools load and fails on any network call.Transcripts are append-only. A piece is regenerated from its transcript, never the other way round. Withdrawal replaces, it never edits.
A subject's explicit refusal to be recorded stores nothing. A missing AI disclosure or recording ask is stored with a notice; the disclosure and the ask themselves live in the ElevenLabs agent's configuration, outside this repository.
The elision rule allows one ellipsis inside a single turn with fragments in order; the checker cannot see what was dropped between them. A reviewer should read elided quotes against the transcript.
A transcript can come from the fixture or from turns the host supplies, and the check confirms quotes against whatever was stored. The footer of every piece names its source so a reader knows whether a recorded call stands behind it.
The phone path
The phone path places a real call through an ElevenLabs agent over the native Twilio integration. It is two tools, because a live call outlasts one MCP tool call:
place_callwithbrief_id. Refused unless the dial gate finds the approval for the brief's exact name and number. It triggers the outbound call, passing six dynamic variables built from the brief (subject_name,subject_role,client_name,genre,angle,question_plan), records the conversation id underdata/calls/, and returns.fetch_transcriptwithbrief_id. Waits for the call to end, fetches the conversation, normalizes it to one timestamped turn per entry, runs the consent check on the real opening, and stores the transcript append-only. A refusal stores nothing. A voicemail greeting isNO ANSWER. Each call waits up to about three minutes and answersSTILL IN PROGRESSif the interview is still running; a ten-minute interview needs about four calls. If a host cancels a wait early, the next call for that brief stays under that limit.
Credentials for a local stdio run go in app/.env and are loaded by Node's own flag:
ELEVENLABS_API_KEY=...
ELEVENLABS_AGENT_ID=...
ELEVENLABS_PHONE_NUMBER_ID=...npm run start:phoneThat runs node --env-file=.env dist/src/index.js. For Claude Desktop, use the same
two arguments with absolute paths. The agent itself is configured in ElevenLabs: its
first message must state that it is an AI and ask permission to record, its prompt
reads the six dynamic variables, and the Twilio number is imported under Phone
Numbers. Endpoints used: POST /v1/convai/twilio/outbound-call and
GET /v1/convai/conversations/{id}, authenticated with the xi-api-key header.
Layout
src/index.ts MCP server entry, stdio
src/remote.ts the same tools over Streamable HTTP, bearer token, one data dir per session, tenants, approval page, webhook
src/hosted.ts hosted-mode context and the signed approval token
src/writer/ the server-side writers: interface, deterministic reporter, Anthropic API writer
src/phone/ingest.ts one path from a finished conversation to a stored transcript (polling and webhook)
src/phone/webhook.ts ElevenLabs post-call webhook signature and payload
examples/sidecar/sidecar.ts a partner's sidecar over plain JSON-RPC, no SDK
src/tools/*.ts one tool per file
src/gate/dialGate.ts the only unlock for a dial
src/store/fileStore.ts JSON persistence under data/, tombstones, audit lines
src/withdraw.ts withdrawal: plan, preview token, execute
src/consent.ts AI disclosure, recording ask, refusal and voicemail detection
src/generate/packet.ts the reporter's packet for the host writer
src/generate/contract.ts the writing contract
src/generate/markdownPiece.ts the host-piece checker and assembler
src/generate/citations.ts the citation rule and re-check
src/generate/redact.ts contact-detail matchers and masking
src/generate/maskPiece.ts masking a checked piece, leak check
src/generate/reporter.ts the deterministic fallback writer
src/questionBank.ts question banks and angle weighting
src/phone/elevenlabs.ts ElevenLabs REST client (outbound call, conversation fetch)
src/phone/normalize.ts brief -> dynamic variables; conversation -> timestamped turns
fixtures/ the fictional text-only fixture
scripts/run-spine.ts end-to-end fallback runner over stdio
scripts/demo-hosted.ts the hosted flow in one command over loopback HTTP, no keys
scripts/call-tool.ts generic one-tool client over stdio
scripts/register-desktop.ts writes the Claude Desktop config entry
test/ node:test suite
Dockerfile, render.yaml the hosted entry's image and blueprint
CHANGELOG.md, INTEGRATION.md history, and the contract for another product
OPERATIONS.md, SECURITY.md running the hosted entry; what is enforced and what is asked forAvailable Tools
12 toolsapprove_contactRecord human approval to contact the subjectA
A human records that InterLogue may contact exactly this person at exactly this number for this brief. This is the no-cold-outreach gate: run_interview refuses to proceed without a matching record. The name and number must match the brief. Re-approving with identical values returns the existing record; different values are refused. Next step: run_interview.
| Name | Required | Description | Default |
|---|---|---|---|
| phone | Yes | The subject's phone number, exactly as on the brief. | |
| confirm | Yes | A human must pass true. This records approval to contact exactly this person at exactly this number. | |
| brief_id | Yes | The brief_id returned by the brief tool. | |
| statement | Yes | The approver's own words, stored verbatim. | |
| approved_by | Yes | The human approving this contact, by name. | |
| subject_name | Yes | The subject's full name, exactly as on the brief. | |
| consent_basis | Yes | How the approver knows the subject consents to being interviewed and recorded, e.g. "agreed by email on Tuesday". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the burden and delivers: it discloses idempotency behavior (identical re-approval returns the existing record; different values are refused), a matching precondition (name and number must match the brief), and the downstream enforcement role. This is materially more than the schema conveys.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the action and gate semantics, ending with the next step. No redundancy, though the caveat about identical vs. different values could be slightly compressed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-param mutation tool with no annotations and no output schema, the description covers action, precondition, idempotency, refusal behavior, and the follow-up tool. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every one of the 7 parameters is already documented in the schema. The description reinforces the matching requirement for name/number but adds no syntax or format detail beyond it, so the baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('A human records that InterLogue may contact exactly this person') and immediately frames it as the 'no-cold-outreach gate', which distinguishes it from siblings like discover_contacts or place_call. An agent can identify the tool 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states the dependency: 'run_interview refuses to proceed without a matching record' and closes with 'Next step: run_interview.' It gives both when-to-use (before interviewing) and the constraint that selects it (the gate).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
briefBrief the reporterA
Start here. Brief InterLogue the way an editor briefs a reporter: who the subject is, which client and product the case study is for, the topic, and the angle. The angle decides which questions get asked and what the piece emphasizes; it never changes what the subject is portrayed as having said. Saves the brief and returns a brief_id plus the question plan. Nobody is contacted at this step. Next step: approve_contact.
| Name | Required | Description | Default |
|---|---|---|---|
| angle | Yes | The editor's angle, e.g. "a two-person team getting its Mondays back from manual order entry". Shapes which questions are asked and what is emphasized. Never shapes what the subject is portrayed as having said. | |
| genre | No | Only customer_case_study exists in this build. | customer_case_study |
| topic | Yes | The subject matter as a noun phrase, e.g. "order entry and fulfillment". | |
| subject_name | Yes | Full name of the person to interview, e.g. the founder or customer. | |
| subject_role | Yes | The subject's role, e.g. "founder" or "head of operations". | |
| subject_phone | Yes | The subject's phone number. It is stored only in the brief and approval records and never echoed in full. | |
| client_company | Yes | Who commissioned the piece (the marketing team's company). | |
| client_product | Yes | The product the case study is about. | |
| content_needed | No | What the client needs back. | |
| subject_company | Yes | The subject's company. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden and does well: it discloses the side effect (saves the brief), the return shape (brief_id plus question plan), and the safety-relevant fact that no contact occurs. It omits auth/permission requirements and rate limits, so not 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with 'Start here.' Every sentence earns its place: input categories, the angle's semantic constraint, the save/return behavior, the no-contact guarantee, and the next step. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter, no-output-schema, no-annotation tool, the description compensates well by describing the return value (brief_id, question plan) and the workflow position. It leaves the remaining lifecycle (what approve_contact then triggers) implicit, but that is the sibling's job.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description groups the inputs conceptually (subject, client, product, topic, angle) but adds little beyond what the schema already documents, including the angle semantics which the schema repeats verbatim.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (brief) and resource (InterLogue), enumerates the inputs it captures (subject, client, product, topic, angle), and marks itself as the entry point with 'Start here.' An agent can distinguish it from siblings like approve_contact or draft_piece 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells the agent when to use it ('Start here'), what does not happen ('Nobody is contacted at this step'), and routes to the next tool ('Next step: approve_contact'). This is when/when-not/alternatives in miniature.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_citationsFact-check a piece against its transcriptA
Validates that every quoted span resolves to a subject turn at the cited timestamp. With piece_id it re-checks a stored piece. With brief_id and markdown it checks a host-written piece: section order (story, then pull quotes), every quote verbatim and timestamped, no numbers outside quotes, no repeated frames. A clean pass persists the piece as a draft for human review and returns its piece_id. A fail returns every failing span with the closest matching turn so you can fix and resubmit; nothing is persisted on a fail.
| Name | Required | Description | Default |
|---|---|---|---|
| brief_id | No | With markdown: the brief whose transcript the piece was written from. | |
| markdown | No | With brief_id: the host-written piece. H1 headline, prose story, then '## Pull quotes' with one '> “quote” (MM:SS)' per line. The per-question view is appended for you. | |
| piece_id | No | Re-check a stored piece by id. Give either piece_id, or brief_id plus markdown. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses the validation rules (section order, verbatim timestamped quotes, no numbers outside quotes, no repeated frames), that a clean pass persists a draft for human review and returns a piece_id, and that a fail persists nothing and returns failing spans with the closest matching turn.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core validation statement, then the two modes, then the pass/fail outcomes. Every sentence conveys a distinct behavioral fact with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description must explain return behavior; it does so for both paths (piece_id on pass, failing spans plus nearest turn on fail). Nothing an agent needs to call or interpret this tool is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and the parameter descriptions already explain the two invocation modes and the required markdown format. The description restates the piece_id vs. brief_id+markdown split without adding syntax or format detail beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: validates that quoted spans resolve to a subject turn at the cited timestamp. This is clearly distinct from sibling generators like draft_piece and generate_piece, and the title reinforces the fact-check framing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly documents two invocation modes: 'With piece_id it re-checks a stored piece' and 'With brief_id and markdown it checks a host-written piece.' That gives clear context for selecting arguments, though it never names sibling alternatives or says when this check should be skipped.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
discover_contactsDiscover contacts (not in this build)A
Cut-list stub. Automated contact discovery is not implemented and will not find, suggest or look up anyone. Explains what to do instead.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Ignored. Kept so the tool has a stable shape. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full burden and does disclose the critical behavior: discovery is unimplemented and will return nothing useful. It is honest about the no-op nature, though it does not say what the call returns or whether the required query matters.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with 'Cut-list stub' so the agent grasps the situation immediately. No filler; every clause conveys necessary information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter stub with no output schema or annotations, the description supplies the one fact that matters: it will not work. The remaining gap is a concrete pointer to the alternative workflow, which would make it fully complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema itself states the query is 'Ignored. Kept so the tool has a stable shape.' The description adds nothing about the parameter beyond what the schema already documents, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states the tool's real purpose: it is a 'cut-list stub' that 'will not find, suggest or look up anyone.' This is a specific and honest statement of scope that distinguishes it from action-oriented siblings like place_call, approve_contact, and run_interview, though the verb+resource framing is inverted (it says what it does NOT do).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Explains what to do instead' implies the tool is used as a redirect mechanism, giving implied usage. However, no alternative tool or action is named, so the agent is left to infer the actual path forward on its own.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
download_recordingDownload recording (not in this build)A
Cut-list stub. No audio is fetched or stored by this build; the timestamped transcript is the record. Any transcript_id gets the same answer.
| Name | Required | Description | Default |
|---|---|---|---|
| transcript_id | Yes | Ignored. Kept so the tool has a stable shape. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that no audio is fetched or stored, that the timestamped transcript is the record, and that input is ignored. It does not discuss return format or edge cases, but the core stub behavior is transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with the essential stub status. Every sentence adds useful information without waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a stub tool with no annotations and no output schema, the description is nearly sufficient: it explains the no-op behavior and that the transcript is the record. It could more explicitly describe the shape of the returned answer, but the agent has enough to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and already says transcript_id is ignored. The description adds that any transcript_id gets the same answer, which is marginally useful, but otherwise the schema does the heavy lifting. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states this is a cut-list stub, not a real download, and explains that no audio is fetched or stored. That distinguishes it from a functional download, though it does not explicitly contrast it with the sibling fetch_transcript tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no when-to-use guidance or alternatives. It only notes that any transcript_id returns the same answer, which is behavioral context 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.
draft_pieceGet the reporter's packet to write the pieceA
Returns everything needed to write the published piece from the transcript: the brief and angle, the question plan, a strict writing contract, ranked verbatim quote candidates each with its timestamp, and the full timestamped transcript. You (the host) write the story first, then pull quotes, in markdown, and submit it to check_citations. Every quoted span must be verbatim from a subject turn and followed by its (MM:SS). Nothing is published until the check passes.
| Name | Required | Description | Default |
|---|---|---|---|
| brief_id | Yes | The brief_id whose transcript you are writing from. run_interview must have stored a transcript for it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the publication gate ('nothing is published until the check passes') and the hard constraint that every quoted span be verbatim from a subject turn and carry its (MM:SS). It stops short of stating access/permission requirements or whether the packet is cached/regenerated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, front-loaded with what is returned, then the authoring rules. Dense but each sentence earns its place; the return-content list is long but necessary given there is no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so describing the returned packet is essential, and the description does so thoroughly while also covering the citation format and the check_citations gate. An agent has everything needed to invoke it and act on the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and there is only one parameter, so the schema already defines brief_id's meaning and the precondition that run_interview stored a transcript. The description adds no further semantics about brief_id, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific retrieval action and enumerates the exact payload (brief/angle, question plan, writing contract, ranked verbatim quote candidates with timestamps, full timestamped transcript). It also implicitly distinguishes itself from siblings like generate_piece and fetch_transcript by clarifying that the host writes the story, not the tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear workflow: write the story first, then pull quotes in markdown, then submit to check_citations, with the rule that nothing is published until the check passes. It names a downstream sibling and its ordering but never states when NOT to use this tool (e.g., versus fetch_transcript) or any prerequisites beyond the transcript existing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetch_transcriptFetch the call transcript (phone path, step 2 of 2)A
Step 2 of the phone path. Waits for the placed call to end (up to about three minutes per call), then fetches the transcript from ElevenLabs, normalizes it to timestamped turns, runs the consent check (AI disclosure, permission to record, the subject's yes), and stores it against the brief. If the call is still running it answers STILL IN PROGRESS: call it again, as many times as needed, until it answers TRANSCRIPT STORED. A ten-minute interview needs about four calls. Then: draft_piece.
| Name | Required | Description | Default |
|---|---|---|---|
| brief_id | Yes | The brief whose call was placed with place_call. | |
| wait_secs | No | Seconds to wait for the call to end before answering "still in progress". Default 170. A ten-minute interview needs about four calls at the default. Leave it alone unless you know the host's timeout. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: it discloses the blocking wait (up to ~3 minutes per call), the retry/idempotency pattern, the mutation (transcript stored against the brief), and a consent-validation side effect (AI disclosure, permission to record, subject's yes). These are exactly the behaviors an agent needs to plan around, none of which appear in the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the step position and the core action, then the operational loop, then the handoff. Nearly every sentence earns its place, though the normalize/consent-check detail is slightly more than needed to select and invoke the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description must explain return values, and it does: the two terminal answers (STILL IN PROGRESS vs TRANSCRIPT STORED) are named verbatim. Combined with the wait semantics and next-step routing, an agent has everything required to call it correctly and repeatedly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema itself richly documents brief_id and wait_secs, so the baseline is 3. The description adds real meaning on top: 'up to about three minutes per call' and 'a ten-minute interview needs about four calls' directly explain the wait_secs default of 170 and its retry economics, which is more than the schema's terse note provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (fetch transcript) plus the full pipeline: waits for the call to end, pulls from ElevenLabs, normalizes to timestamped turns, runs a consent check, stores against the brief. It also positions itself in the workflow ('step 2 of the phone path') and names the predecessor (place_call, via brief_id) and successor (draft_piece), so it is unmistakable against siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-call and when-to-call-again: if the answer is STILL IN PROGRESS, call it again as many times as needed until TRANSCRIPT STORED, with a concrete expectation ('a ten-minute interview needs about four calls'). It also hands off the next step ('Then: draft_piece'), so the agent knows both the loop condition and the exit path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_pieceFallback writer: generate the piece deterministicallyA
FALLBACK writer, used when no host is writing (for example npm run spine). Inside Claude, prefer draft_piece then check_citations, where the host writes the prose. This tool assembles the customer case study deterministically from the saved transcript: the story first, pull quotes second, per-question answers third. Every quote is a verbatim span of a subject turn and carries its transcript timestamp; the piece is saved only if the citation check passes. Regenerate any time; the transcript is never edited to fit a piece. Next step: check_citations.
| Name | Required | Description | Default |
|---|---|---|---|
| brief_id | Yes | The brief_id whose transcript should be written up. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full load and delivers: deterministic assembly, verbatim quote spans with transcript timestamps, save-only-if-citation-check-passes, transcript never edited to fit a piece, and idempotent regeneration. These are exactly the behavioral traits an agent needs before invoking it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the crucial routing word FALLBACK in caps, then layers usage, mechanics, and next step in four tight sentences. Slightly dense but every sentence carries distinct information; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers purpose, routing, output ordering, citation gating, and follow-up tool despite having no annotations or output schema. An agent has everything needed to decide whether and how to call it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter (brief_id) and schema description coverage is 100%, so the schema already documents it. The description adds only the implication that the brief's transcript is what gets written up; it adds no syntax or selection nuance beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Explicitly names itself as the FALLBACK writer for deterministic assembly of the case study from the saved transcript, and states the output order (story, quotes, per-question answers). This makes it clearly distinguishable from the sibling draft_piece, which is the host-written path.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-to-use condition (only when no host is writing, e.g. `npm run spine`) and a when-not/preferred-alternative (inside Claude, use draft_piece then check_citations). It even names the next step, check_citations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
place_callPlace the interview call (phone path, step 1 of 2)A
Places one real outbound call to the brief's subject through the ElevenLabs agent over Twilio. Refused unless a human approval for this exact person and number is on file. The agent opens by stating it is an AI and asking permission to record. Returns at once with the conversation id; call fetch_transcript to wait for the transcript. Requires ELEVENLABS_API_KEY, ELEVENLABS_AGENT_ID and ELEVENLABS_PHONE_NUMBER_ID in the server's environment.
| Name | Required | Description | Default |
|---|---|---|---|
| brief_id | Yes | The brief to interview. approve_contact must have recorded approval for its exact name and number. | |
| confirm_dial | Yes | A human must pass true. This places a real phone call to the approved number. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and delivers: it is a real outbound call, it is refused without approval, the agent self-identifies as AI and seeks recording consent, it returns immediately rather than blocking, and it lists the required environment variables.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but efficient: each sentence adds distinct information (purpose, gate, call semantics, return, env requirements) with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a side-effecting tool with no output schema and no annotations, the description covers safety gating, return value, follow-up tool, and runtime prerequisites — everything an agent needs before invoking it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already fully documented, including confirm_dial's const:true and the approval linkage on brief_id. The description adds no syntax or format detail beyond the schema, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('places one real outbound call to the brief's subject') plus the delivery mechanism (ElevenLabs over Twilio), and its 'step 1 of 2' framing lets an agent separate it from fetch_transcript and run_interview.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear precondition for use (human approval on file for the exact person and number) and routes the agent onward to fetch_transcript to await the result. It does not, however, contrast this phone path against any purely text path or explain when to prefer the alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
question_templatesQuestion templates by genre (not in this build)A
Cut-list stub. There is one genre, customer case study, and its questions come from the brief tool's angle-driven plan. Any genre passed here gets the same answer.
| Name | Required | Description | Default |
|---|---|---|---|
| genre | Yes | Ignored. Kept so the tool has a stable shape. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does reasonably well: it discloses that only one genre exists, that the genre argument is meaningless, and that output is identical for any input. It omits nothing critical for a no-op stub, though it doesn't say whether the call succeeds or errors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the key fact ('Cut-list stub') so the agent learns the tool is non-functional immediately. No filler, though 'cut-list' is internal jargon that carries little meaning to an outside caller.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a stub with one ignored parameter, no output schema and no annotations, the description covers what an agent needs: it is a stub, input is irrelevant, and the real question source is brief. Return-value detail is unnecessary since output is constant.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the schema itself already states the 'genre' param is ignored and exists only for shape stability. The description's 'any genre passed here gets the same answer' reinforces that but adds no new semantic detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states plainly that this is a cut-list stub, identifies the single supported genre (customer case study), and says where the real questions come from (the brief tool). An agent knows this tool returns a fixed answer rather than genre-specific templates, though the name 'question_templates' implies a generality the description correctly denies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It points at the 'brief tool's angle-driven plan' as the real source of questions, which nudges the agent toward brief, but it never explicitly says 'use brief instead of this tool.' Usage is implied rather than prescribed, and there is no statement of when this tool should ever be called (if at all).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_interviewRun the interview (text-only in this build)A
Runs the interview step of the spine on text: either a named fixture transcript or inline turns. Refuses unless a human approval for this brief's exact person and number is on file (the same gate a real dial would need). Checks that the agent disclosed it is an AI and asked permission to record at the top of the call, then saves the transcript. Transcripts are append-only. No phone call is placed in this build. Next step: generate_piece.
| Name | Required | Description | Default |
|---|---|---|---|
| turns | No | Inline interview turns in call order: { speaker: agent|subject, time_in_call_secs, text }. Give either fixture or turns, not both. | |
| fixture | No | Name of a fixture in the package's fixtures/ directory without the ".transcript.json" suffix, e.g. "founder-case-study". Give either fixture or turns, not both. | |
| brief_id | Yes | The brief_id from the brief tool. Must have a matching approve_contact record. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so: it discloses the approval gate and its refusal behavior, the AI-disclosure and recording-permission checks performed at the top of the call, that the transcript is saved, that transcripts are append-only, and that no phone call is placed. These are exactly the mutation/authorization semantics an agent needs before invoking.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then the refusal gate, then side effects, then the next step. Every sentence adds a distinct fact; there is no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no annotations and no output schema, the description covers preconditions, side effects, persistence semantics, and the follow-up tool. It stops short of describing what the call returns (e.g. confirmation shape or transcript handle), which is the only remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents fixture/turns mutual exclusivity and that brief_id must have a matching approve_contact record. The description restates the fixture-or-turns choice and the approval gate without adding new syntax or format detail, so baseline 3 is correct.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ("Runs the interview step of the spine on text") and immediately narrows the input modes to a named fixture transcript or inline turns. It also distinguishes itself from place_call by declaring "No phone call is placed in this build," so an agent can tell it apart from the dialing sibling 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a hard precondition (refuses unless a human approval for this brief's exact person and number is on file) and names the next step (generate_piece), which is strong routing guidance. It does not explicitly say when to prefer this over place_call or fetch_transcript, so it falls just short of full when/when-not/alternatives coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statusWhere is this brief in the spine?A
Reports which of brief, human approval, transcript and generated pieces exist for a brief_id, and names the next tool to call. Spine: brief -> approve_contact -> run_interview -> generate_piece -> check_citations.
| Name | Required | Description | Default |
|---|---|---|---|
| brief_id | Yes | The brief_id to report on. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does imply a read-only inspection operation by saying it 'reports which ... exist'. However, it never explicitly states it is non-mutating, nor does it describe auth requirements, rate limits, or what the response payload looks like beyond 'names the next tool'.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, both earning their place: the first states the behavior, the second supplies the pipeline context that makes the artifact list interpretable. The core behavior is front-loaded with zero filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with no output schema and no annotations, the description supplies the key missing context: the pipeline spine and the promise that the next tool is named. That largely compensates for the absence of a return schema, though a hint about the shape of the status report itself is still absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is a single parameter, brief_id, and schema description coverage is 100%, so the schema already documents it fully. The description adds no format, source, or constraint information beyond what the schema provides, which makes the baseline 3 appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: it reports which artifacts (brief, human approval, transcript, generated pieces) exist for a given brief_id. It also enumerates the pipeline spine, which implicitly positions it against the sibling workflow tools. It stops short of naming a specific sibling it is not, but the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The spine ordering ('brief -> approve_contact -> run_interview -> generate_piece -> check_citations') gives clear context for when an agent should consult this tool: to determine where in the pipeline a brief currently sits. It also states the tool names the next tool to call, which is actionable guidance. No explicit when-not or exclusion conditions are given.
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.
12 tool updates
v0.1.0- First observed
approve_contact - First observed
brief - First observed
check_citations - First observed
discover_contacts - First observed
download_recording - First observed
draft_piece - First observed
fetch_transcript - First observed
generate_piece - First observed
place_call - First observed
question_templates - First observed
run_interview - First observed
status
TDQS
Scored across 12 tools
Tools mostly map to distinct steps in a linear interview spine, but there is a potential confusion between the simulated text path (run_interview) and the real phone path (place_call + fetch_transcript), and between the fallback writer (generate_piece) and the preferred writer flow (draft_piece + check_citations). Fortunately, the descriptions explicitly clarify these boundaries, which mitigates the overlap.
The names are mostly snake_case verb_noun (e.g., approve_contact, run_interview, fetch_transcript, check_citations). A few names are nouns or noun phrases (brief, question_templates, status), which is a minor deviation but still readable and consistent with the domain.
With 12 tools, the count is not unreasonable, but three stubs (question_templates, download_recording, discover_contacts) are explicitly cut-list placeholders that add no real functionality and dilute the set. The remaining tools feel appropriately scoped for the interview workflow.
The surface covers the full spine from brief to approval to interview to piece generation and citation checking, with status reporting. However, the stubs represent unimplemented features, and there is no tool to update or delete briefs or transcripts, though the workflow may not require them.
Maintenance
Related MCP Connectors
Shared task queue for humans and AI agents: leases, handoffs, approvals and signed receipts.
- BalladicOAuthcom.balladic
Fluid cowork platform. Synthesizes work data across boundaries, with project tracking built in.
Search, draft and manage your podcast workspace from your agent.
Writing studio for novels and screenplays: read your projects and run cited fact-checks.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables agents to create and manage persistent task logs, decisions, dead ends, questions, and handoffs, with file staleness detection and activity reporting.3 npm2MIT
- AlicenseNot gradedqualityAmaintenanceEnables persistent work-state coordination with identity-bound assignments, governed lifecycle transitions, bounded queries, and auditable history.Apache 2.0
- AlicenseBqualityCmaintenanceEnables revision-bound source audits with exact article fingerprinting, claim-to-source mapping, quotation verification, and immutable JSON evidence reports for prepublication review.9MIT
- AlicenseAqualityCmaintenanceEnables append-only revision projects with SHA-256-fingerprinted changes, style heuristics, human approval checkpoints, and draft-only WordPress export handoffs.9MIT