Private betaZynth Auth is currently in private beta testing.New organizations are created by invitation only, and no plan can be purchased yet.Request early access

Author content with your own agent

Zynth Auth's CMS is built for one loop: an AI agent drafts under its own identity, a person approves the exact bytes, and the published page proves both. This guide connects any MCP-capable model — Claude, GPT, Gemini, or your own harness — to that loop.

Availability: the content tools ride the MCP tool gateway, which is enabled per installation, and the deployment must have its content store configured. While either is off, the surfaces below return 404.

The design in one paragraph

The agent can draft, revise, ask for review, and read its feedback. It cannot publish — there is no publish tool on the agent surface: not disabled, absent. Publication spends a human approval that is fingerprinted to the content hash, so editing after approval invalidates it, and the public page renders its provenance (which agent, which model, which hash, who approved) in prose and in its Article JSON-LD. If a person never says yes, nothing ever goes public.

1. Register the agent and delegate the scope (admin)

The author is an ordinary Zynth agent principal with one delegated permission:

curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{"name": "content-author", "description": "Drafts blog posts over MCP"}' \
  https://auth.example.com/api/v1/agents

Delegate content:draft:write to it (the console's Agents page, or the delegation API). That scope covers every content tool; nothing else is needed — and deliberately nothing else works.

2. Point your model at the gateway

The content tools live on the internal zynth-content server, called through the gateway with the agent's own token:

POST https://auth.example.com/api/v1/mcp/call/zynth-content
Authorization: Bearer <agent token>
Content-Type: application/json

{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
 "params": {"name": "create_draft", "arguments": { ... }}}

tools/list on the same endpoint returns the five tools:

ToolWhat it does
create_draftStart a post: title, description, kind, tags, markdown.
update_draftRevise it — each revision is a new immutable, content-addressed version.
request_reviewPark the ask with your reviewers, bound to the current bytes.
get_review_feedbackRead every human verdict on this post — a denial's reason is the next revision's brief.
list_my_draftsWhere the agent's posts sit.

Rate limits are deliberate (20/min, 200/day): room to iterate, a wall for a runaway loop — plus a detection rule watching draft volume per agent.

What a draft may contain

Markdown (CommonMark). Every rule below is enforced at ingest and comes back as a structured reason (code, detail, line) your agent can act on — nothing is silently rewritten, so what you sent is what a reviewer reads and what the page serves:

FieldRuleRefusal code
markdownNo raw HTML.raw_html
markdownImages from the deployment's own media store only — a rooted /media/… path. No external, no scheme, no //host.external_image
markdownLink targets are http(s), mailto:, or a site path starting with /. javascript:, data: and vbscript: are refused, not stripped.unsafe_link
markdownNo control or format characters (bidi overrides, zero-width). \n, \t and the emoji joiner are fine.control_characters
title, description, tags[i]No < or >, no control/format characters. These fields render outside the markdown sanitiser — in the page <title>, the OpenGraph meta and the Article JSON-LD — so markup characters are refused at the door.unsafe_characters, control_characters
tagsAt most 10 tags, at most 64 characters each.too_many_tags, tag_too_long, empty_tag
slug (optional)Must match ^[a-z0-9]+(-[a-z0-9]+)*$, at most 120 characters. Omit it and one is derived from the title.invalid_slug

An invalid_slug refusal carries a suggested field — the compliant slug derived from what you sent — so the retry is one edit away. Links to external sites are fine; that is what content does. The Content console renders these refusals as sent — every reason, its line where there is one, and the suggested slug — rather than a generic failure message.

3. A person reviews the exact bytes

Reviewers see the post rendered by the same function the public page uses — approving it means approving the page, not a markdown diff. The approval is single-use and fingerprinted to the content hash: any later edit walks the workflow again. Denials carry the reviewer's reason, which the agent reads back through get_review_feedback.

4. Publishing, scheduling, and what the page says

Publishing is a human action in the Content console — now, or scheduled. A scheduled publish re-verifies the content hash at fire time: if the bytes moved after approval, it refuses and alerts rather than publishing something nobody read. A refusal takes that one queued post out of the queue; everything else scheduled behind it still fires.

Publishing, unpublishing, rolling back, calling off a queued publish and renaming a live URL all require content:review — the same authority a reviewer spends on the approval itself. content:read sees the whole pipeline and changes none of it, so an auditor role can be given the Content console without being able to alter what the public sees. Renaming a URL takes the new slug exactly as typed (same alphabet as above) and mints an audited redirect from the old one.

The live page states its provenance: the author kind and model, the content hash, and that a person approved this exact version — in the byline and in the Article JSON-LD (author as SoftwareApplication, reviewedBy as your organization).

Subscribe a webhook to content.published to trigger anything downstream — socials, a newsletter, a search reindex — whenever a page goes live or its live version changes.

5. Earned autonomy (optional, off by default)

An agent whose drafts your reviewers approve consistently — a statistical confidence bar over its whole record, a minimum span of days, zero withdrawn posts in 30 days — can have new drafts approved by its track record, with a notification to your operators every time. The console shows each agent's progress toward the bar and what one denial would cost. Unpublishing any of its posts suspends the privilege for 30 days. Pages published this way say so: the byline states the earned-autonomy basis instead of claiming human review.

Nothing about this is on until an operator enables content.autonomy_enabled, and publication itself still requires a person.

The provable claims

Every step above leaves evidence a compliance reviewer can walk: the draft and every revision (immutable, content-addressed, author-stamped from the verified token — an agent cannot name itself in a request body), the review request, the human decision, the single-use spend, and the receipt on the published version. It is the AI-content disclosure story produced as a by-product of access control, not as an annotation exercise.