Requires a TechFabric engineer's Microsoft sign-in (scope blog.write). The final verification publish_draft needs. With id: the full checks on your saved draft (judge, slop classifier, fact-check), about a minute; call it after lint_draft passes, never to iterate. Each call waits up to 25 seconds and then reports progress: call it again, never two at once, for the answer (nothing runs twice; a client with the tasks extension gets a task). Right after submit_draft it waits while the draft is filed. Without id it checks markdown and files nothing, which publish_draft doesn't count. On next_action confirm, send first_hand_to_confirm back as first_hand: the engineer confirms, never you.
View input schema
{
"type": "object",
"properties": {
"markdown": {
"type": "string",
"description": "The whole post: frontmatter (title, description at least) and body. With id: optional, and must be the saved text"
},
"id": {
"type": "string",
"description": "A draft of yours (the idea id from submit_draft): checks its saved text, the final verification publish_draft needs"
},
"images": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The file name the post refers to the image by, e.g. pipeline.png"
},
"data": {
"type": "string",
"description": "The image's bytes, base64 (PNG, JPEG, WebP or GIF, at most 10 MB). Required by submit_draft; check_draft needs only the name."
},
"alt": {
"type": "string",
"description": "What the image shows, for its alt text and caption"
}
},
"required": [
"name"
],
"additionalProperties": false
},
"maxItems": 10
},
"kind": {
"type": "string",
"enum": [
"post",
"case-study"
],
"default": "post",
"description": "What the draft is: \"post\" (the default, a blog post) or \"case-study\" (content/case-studies, at /case-studies/<slug>). A case study's frontmatter is title, description, sector, stack, track (ai, migration or other) and client when the client register approves naming them or the engineer says the client agreed (client_confirmed); its body opens with ## Challenge, ## Strategy, ## Solution and ## Results, each with a bold one-line statement first. See the editorial_rules resource."
},
"sources": {
"type": "string",
"description": "The engineer's own words the draft's claims come from, as text. For a post: what the engineer told you about their own work (what they built, measured, saw and decided, and any result, client or number they gave you), quoted as they said it, and their notes; the fact-check treats these as the author's own account of the work, so a first-hand sentence they don't back is a finding. Never write them yourself: ask the engineer. For a case study: the engineer's own notes and the project's records (README, changelog, metrics they gave you) its numbers come from. Every number in the case study is checked against these; one that isn't here is a finding. A source is not an approval: a client's metric also needs the client register's approval. Ask the engineer for outcome numbers; never invent them. At most 100,000 characters."
},
"client_confirmed": {
"type": "boolean",
"description": "Only when the engineer tells you they have the client's confirmation to name the client this draft names (its client:, or a client in its text the register doesn't approve). Recorded under the engineer's name with the draft and its text; never send it on your own judgement. It covers naming the client, not results: numbers still need sources."
},
"first_hand": {
"type": "array",
"maxItems": 10,
"description": "First-hand claims only the engineer can source, which the fact-check flagged first_hand (first_hand_to_confirm): each the sentence exactly as quoted, with what it rests on. Sending it asks the engineer; it never confirms anything on your word. check_draft asks them in their client (an elicitation) where it can, otherwise answers with a question and a one-time confirmation: ask them that question, and only if they say yes, send it back as first_hand_confirmation with the same first_hand. submit_draft and update_draft answer with the question and confirmation too, which come back on check_draft with the id. Never a client's result: a number about a client needs the client's confirmation, as a source.",
"items": {
"type": "object",
"properties": {
"claim": {
"type": "string",
"description": "The sentence, exactly as the fact-check quoted it"
},
"basis": {
"type": "string",
"enum": [
"measured",
"our_product",
"client_told_us",
"customers_ask",
"we_did_it",
"own_work"
],
"description": "measured: we measured or ran it; our_product: how our own product works; client_told_us: what a client said (not their numbers); customers_ask: what our customers ask; we_did_it: what we built, saw or decided; own_work: first-hand, from the engineer's own work"
}
},
"required": [
"claim",
"basis"
],
"additionalProperties": false
}
},
"first_hand_all": {
"type": "boolean",
"description": "Confirm all listed: true asks the engineer to confirm, in one go, every first-hand claim the last check of this text listed (first_hand_to_confirm), instead of listing them in first_hand. The list is the server's, from that check: a client's result, a client's fact and a known error are never in it and come back apart (first_hand.excluded). It asks as first_hand does (one form showing every claim, or a question and a one-time confirmation to send back with first_hand_all: true); it never confirms anything on your word. Each claim is still recorded on its own under the engineer's name."
},
"first_hand_confirmation": {
"type": "string",
"description": "Only after asking the engineer and hearing yes: the confirmation an earlier answer gave for exactly this first_hand (first_hand.confirmation). It works once, for ten minutes, for those claims in that text"
},
"confirm_via": {
"type": "string",
"enum": [
"elicitation",
"token"
],
"description": "With first_hand or first_hand_all: how the engineer is asked. Default: a form in their client where it can show one. \"token\": the question and a one-time confirmation instead, for a UI that asks them itself (an MCP App's button)"
},
"wait_seconds": {
"type": "integer",
"minimum": 0,
"maximum": 50,
"description": "Without the tasks extension: how long to wait for the result before answering with progress (default and most 25; a larger value, accepted for older agents, waits 25)"
}
},
"required": [],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false
}