# IterFact MCP

## Purpose
This folder contains the first public-facing MCP server for IterFact.

The v1 goal is narrow on purpose:

- one remote MCP server
- thirteen public tools:
  - `generate_document`
  - `list_engines`
  - `list_templates`
  - `match_template`
  - `preview_normalized_input`
  - `generate_from_template`
  - `create_artifact_draft`
  - `append_section`
  - `get_artifact_status`
  - `get_artifact_feedback`
  - `apply_artifact_feedback`
  - `finalize_artifact`
  - `regenerate_section`
- one artifact pipeline:
  - natural-language prompt or client-supplied `artifact_plan`
  - optional Anthropic planner call
  - IterFact assembler
  - saved HTML output
  - returned artifact pointers

This is the fastest path from vault assets to a real callable IterFact surface.

## Quick Start
Installable package:

```bash
pip install iterfact
iterfact-mcp --host 0.0.0.0 --port 8000
```

Or with the multi-command CLI:

```bash
iterfact mcp --host 0.0.0.0 --port 8000
```

Local editable install from this repo:

```bash
pip install -e .
```

The packaged install bundles the IterFact vault, static assets, and template metadata so the MCP can boot outside this repo without a manual `ITERFACT_VAULT_PATH`.

PyPI publishing:

- this repo now includes a GitHub Actions publish workflow at `.github/workflows/publish-pypi.yml`
- the intended public package name is `iterfact`
- the recommended release path is GitHub trusted publishing on a `v*` tag, not manual password-based `twine upload`

For longform or section-by-section documents, use this exact flow:

1. `create_artifact_draft`
2. `append_section` until the draft is complete
3. `finalize_artifact`
4. `get_artifact_status` until `artifact_status` is `published` or `render_job.status` is `failed`

If an MCP client is running with a stale tool manifest and does not expose
`get_artifact_status` yet, call `finalize_artifact` again with the same
`artifact_id`. The server treats that as a safe status check while a background
render job is already queued or running, so it will not start a duplicate
render. A plain JSON fallback is also available at
`/v1/artifacts/<artifact_id>/status`.

If a background finalize fails after completing an expensive render stage, the
server now saves a resumable `render_checkpoint` in artifact state. The next
`finalize_artifact(retry_failed_render=true)` retry resumes from the last completed stage instead of
regenerating the whole artifact from scratch.

Use this draft flow instead of `generate_document` when the source is long,
already sectioned, or likely to trigger headless validation and critic passes.
Standard freeform `generate_document` requests now queue in the background by
default and return immediately with an `artifact_id` plus status metadata so
MCP clients do not race the 45-second tool-call timeout. Callers should be
ready to poll `get_artifact_status` whenever `generate_document` returns
`artifact_status` or `render_job`.

When Claude already knows the story shape, both `generate_document` and
`create_artifact_draft` now accept `artifact_plan`. This lets Claude do the
thesis, audience, wrapper, section list, and narrative-spine planning on the
user side first, while IterFact only handles render, validation, and publish.
That reduces server-side planner usage and makes the MCP friendlier to any
Claude surface that supports tools.

If `skip_critic=true` is supplied to `generate_document` or
`finalize_artifact`, IterFact skips the editorial critic and publishes as soon
as delivery validation passes. This is the speed-first path for iteration and
live demos.

If the critic is enabled but times out, IterFact now falls back gracefully:

- the artifact still publishes once delivery validation passes
- `quality_score` becomes `null`
- status responses expose `critic_timed_out: true`
- notes explain that the artifact shipped without the editorial critic pass

The critic stack now also has an agentic lane:

- quick regex/style checks run with no LLM call
- optional Playwright viewport checks can surface overflow and clipping before publish
- the production default is now the Claude Agent SDK critic backend when the Claude CLI is present in the runtime image
- the compact Anthropic review path remains as the fallback if the SDK backend is unavailable or errors

If a background freeform `generate_document` run fails after planning or
rendering completes, the same `artifact_id` can be passed to
`finalize_artifact` with `retry_failed_render=true` to resume from the saved `render_checkpoint` instead of
starting the request over from scratch.

For live Iterate-link revision after publication, use this exact flow:

1. share the published artifact in `iterate` mode
2. leave notes directly on the artifact link
3. call `get_artifact_feedback`
4. call `apply_artifact_feedback`
5. poll `get_artifact_status` until the updated revision is published under the same `artifact_id`

This is the in-place revision path. It lets operators update the existing
artifact instead of starting over from a new prompt.

## What it does
`list_engines` returns:

- wrapper and component engines
- best-fit guidance
- supported interaction/chart/component vocabulary
- complexity and category metadata

`regenerate_section` returns:

- updated artifact pointers for the same artifact id
- the regenerated section index and title
- `quality_score`
- `warnings`

`generate_from_template` returns:

- a vault-backed template artifact with the same publish path as freeform artifacts
- `template_id`
- `quality_score`
- `warnings`
- optional composed panel output when `compose` is supplied

`create_artifact_draft` returns:

- an unpublished `artifact_id`
- planned section titles for the chunked build
- wrapper and theme context for the draft
- optional `planning_source` so the caller can tell whether a Claude-side `artifact_plan` was used

`append_section` returns:

- the appended section index and title
- the current section title list for the draft

`get_artifact_status` returns:

- the current `artifact_status` for the same `artifact_id`
- background render job state for long-running finalize calls
- preview pointers and quality data once publication finishes
- feedback summary counts so the caller can see whether open notes still exist

`get_artifact_feedback` returns:

- the saved feedback notes for the same `artifact_id`
- note status metadata:
  - `open`
  - `applied`
  - `dismissed`
- a feedback summary snapshot for operator tooling

`apply_artifact_feedback` returns:

- by default, an immediate queued/running status for the same `artifact_id`
- the feedback notes are applied to the saved plan instead of starting from zero
- when rendering finishes, the updated artifact pointers are available through `get_artifact_status`
- if `wait_for_completion=true`, the tool blocks and returns the revised artifact payload directly

`finalize_artifact` returns:

- by default, an immediate queued/running status for the same `artifact_id`
- when rendering finishes, the published artifact pointers are available through `get_artifact_status`
- if `wait_for_completion=true`, the tool blocks and returns the published artifact pointers directly
- optional `critic_skipped` / `critic_timed_out` flags so the caller can tell whether the editorial critic ran

`generate_document`:

1. reads a prompt
2. if `artifact_plan` is supplied, skips the server-side planner and uses Claude's supplied story shape directly
3. otherwise routes planner and repair calls through smart Anthropic model selection so compact JSON-heavy jobs can use Haiku while heavier composition work stays on Sonnet
4. parses or normalizes the assembly plan
5. assembles the HTML with the saved vault
6. applies delivery chrome and validates the final shipped HTML in a headless browser
7. if the first rendered draft is weak or broken, runs a bounded
   generate -> evaluate -> critique -> fix loop before publish
8. optimizes the final HTML before write/upload so inline CSS, JS, and document whitespace are smaller on the wire
9. saves the output locally or uploads it to R2
10. returns:
   - `artifact_id`
   - `title`
   - `summary`
   - optional `planning_source`
   - `quality_score`
   - optional `critic_skipped`
   - optional `critic_timed_out`
   - `warnings`
   - optional `iterative_refinement`
   - optional `preview_url`
   - optional `download_url`

For standard freeform generation, `generate_document` now returns early with:

- `artifact_id`
- `artifact_status`
- `render_job`

In that case the caller should poll `get_artifact_status` until the artifact is
published or failed. If the status tool is unavailable in the client session,
the same `artifact_id` can be passed back to `finalize_artifact` as a fallback
status check.

If `skip_critic=true`, the background render skips the editorial critic and
publishes as soon as delivery validation succeeds. If the critic is enabled but
hits the configured timeout, the artifact still publishes with
`critic_timed_out: true` and `quality_score: null` instead of hanging
indefinitely in the critic stage.

For large non-HTML uploaded reports, `generate_document` can now switch into a
chunked builder path instead of forcing the whole source through one planner
call. That path:

1. creates a draft scaffold
2. splits the source into section-sized chunks
3. appends sections one at a time
4. starts finalize under the same `artifact_id`
5. the caller polls `get_artifact_status` until the artifact is published

This is the first true multi-call longform builder in the MCP.

The chunked draft flow is now background-safe for long renders. This matters
because the finalize step can include:

- HTML assembly
- headless validation
- prompt-stack audit
- critic-loop regeneration passes
- upload/publish

Those steps can exceed some MCP client tool-call deadlines. The server now
avoids that by returning quickly from `finalize_artifact` and persisting render
job state on the artifact until publication completes.

One current boundary: uploaded HTML ingest is still a specialized path. IterFact
now treats HTML upload as either admin passthrough or product-policy rejection;
it no longer rebuilds or reconstructs imported HTML into managed artifacts, and
that branch is not yet forced through the same always-async queue as standard
freeform generation.

Publication now has a real quality floor. By default, artifacts scoring below
`75` do not publish, even if they are technically renderable. This is separate
from hard failures like `NaN`, `undefined`, broken calculator behavior, or
headless validation errors. The intent is simple: the HTML that ships to
`a.iterfact.com` should be the same HTML that was validated and scored.

The MCP now also does two production-safe Python optimizations from the
infrastructure roadmap:

- smart Anthropic model routing for planner/repair-style calls so compact jobs can downshift to Haiku automatically
- HTML optimization before local write or R2 upload so artifacts ship with smaller inline CSS/JS and less markup overhead

It can also now accept:

- `data`
  - structured JSON input for exact chart numbers and other non-approximate values
- `engine_override`
  - force a specific wrapper engine such as `02` or `18`
- `section_count`
  - request a tighter or broader section count when the caller knows the desired pacing

If `uploaded_content` contains a full HTML document, the MCP no longer tries to
rebuild or reconstruct it into a new IterFact. Admin callers can still use
`upload_passthrough: true` to host the original runtime. Everyone else gets a
clear product-policy rejection telling them to use templates or create natively
inside IterFact instead.

The tool can also accept an optional `interaction_mode`:

- `iterate`
- `fact`

This injects a lightweight IterFact overlay into the artifact.

- `iterate` opens with targeted-note UI visible
- `fact` opens with that iteration layer hidden by default

This is the first live slice of the branded Iterate -> Fact flow.

That same Iterate layer can now persist targeted notes back to the MCP. A
published artifact in `iterate` mode can collect feedback from the live link,
store it on the artifact, and then feed those notes back through
`apply_artifact_feedback` so the same artifact id is revised in place.

Every generated IterFact now also carries a platform-owned `Save to IterFact` CTA.
That CTA routes recipients into the app-plane library claim flow so they can create a free account and save the artifact.

For data-heavy or comparison-heavy requests, the planner can also apply a stored
data-visual cognition guidance profile from the vault spec layer. That improves
information hierarchy, chart choice, readability, and narrative pacing without
forcing every artifact into the same visual treatment.

The planner now also applies a compact MCP-side bridge derived from the newer
IterFact prompt stack. That bridge pushes harder on:

- stronger section jobs instead of filler sections
- a required `meta.narrative_spine` with:
  - `core_thesis`
  - `audience`
  - `desired_action`
  - `throughlines`
  - `must_keep_facts`
  - `must_not_claim`
  - `key_entities`
  - `claim_map`
  - `closing_obligation`
  - `section_jobs`
- framework-aware compositions for strategy, finance, roadmap, and disclosure work
- composition-profile selection before planning:
  - `minimal`
  - `editorial`
  - `data-forward`
  - `immersive`
- chaptered report wrappers over default slide decks for analysis-heavy artifacts
- clearer benchmark, delta, target, and takeaway context around quantitative sections
- large non-HTML uploaded content is compacted into outline-plus-excerpt planner context before retries so long research docs are less likely to break planner JSON output

The MCP now also applies a Visualizer-derived design-system layer for new
artifacts. In practice this means:

- runtime prompt bundles now carry explicit visual negative constraints:
  - no gradients
  - no shadows
  - no blur or glow
  - no font weight above `500`
  - no `position: fixed`
  - no `100vh` or `100vw`
  - sentence case instead of all-caps chrome
- chart-capable engines now receive the approved color-ramp hex values so
  Chart.js configs use the IterFact palette instead of arbitrary AI colors
- the render validator now flags visual drift such as:
  - gradients
  - shadows
  - heavy weights
  - disallowed hardcoded colors
  - viewport-sized wrappers

The template path now also has a real vault-backed directory system under
`Template Vault/IterFact-template-vault/templates/`.

- each template directory can carry `manifest.json`, `template.html`, and `example-data.json`
- `list_templates` now reads from that vault template layer
- `generate_from_template` can fill slots directly from `normalized_input` with zero LLM calls
- deterministic template renders now land in the cost audit as `template_instance_render` with
  explicit `formula_cost_usd: 0.0` when no provider calls were needed
- primary templates can compose compatible panel templates through `compose`
- templates can now declare manifest-level `input_contract` rules so strict artifacts can require
  `csv`, `xlsx`, or structured `json` instead of pretending every prompt can be parsed from chat
- shared design tokens now live in the vault and are injected into assembled
  artifacts before wrapper and component CSS

That bridge is no longer a single fixed hint block. The MCP now resolves active
runtime prompt modules per request from the prompt stack in `../Prompts`, then
uses those modules to steer planning. In practice this means:

- uploaded research can activate authored-content preservation rules
- template landing-page requests can activate SEO and page-specificity rules
- self-serve template requests can activate personalization and input-model rules
- public-facing or claim-heavy requests can activate trust/safety guardrails
- high-quality runs can activate an editorial critic pass

When the planner returns valid JSON but the plan still looks weak or misrouted,
the MCP can now run one additional prompt-stack critic retry before rendering.
This catches failures such as:

- dashboard requests that came back as mostly prose narrative
- calculator requests with no real calculator surface
- generic section-title sludge
- unsupported delta or benchmark language
- thin preservation of uploaded source outlines

That stored profile now also pushes harder on:

- action-oriented chart titles and takeaways
- direct labels over legends when the chart is simple enough
- benchmark or delta context in KPI sections
- shared scales across related comparison charts
- stronger chart anti-pattern avoidance like oversized pies, dual-axis charts,
  and rainbow scales

The MCP now also applies a render-quality pass before returning success:

- strips visible `undefined`, `NaN`, and `null` placeholders from rendered text
- records structured warnings instead of silently pretending a broken artifact is clean
- returns a `quality_score` so operator and caller surfaces can distinguish strong output from “technically generated but needs attention”
- flags prompt-stack drift such as inline event handlers, large z-index ladders,
  missing container-query support on chart-heavy artifacts, and missing lazy
  loading on media-heavy outputs
- folds in a plan-quality audit so quality score reflects misrouting, generic
  filler structure, source-outline drift, unsupported deltas, and risky claim
  language instead of only raw HTML cleanliness

The MCP now also supports an automated refinement loop for plan-backed artifacts:

- render the first draft
- validate visible output and score it
- run an editorial critic pass against the rendered artifact and the original request
- regenerate only the flagged weak sections
- re-run validation and scoring before publish

This is the first runtime path that intentionally simulates the human IterFact
workflow instead of assuming the first complete draft should ship.

The plan layer now also carries a document-level narrative spine so section
regeneration and critic passes can preserve one coherent story instead of
treating the artifact like unrelated good-enough sections.

That narrative spine is now stronger than a thesis sentence alone. It also
tracks:

- `key_entities`
  - the people, products, metrics, or anchor phrases that should stay stable across sections
- `claim_map`
  - the claims the artifact is making and which section indices are supposed to support them

This makes the narrative layer more checkable during planning, criticism, and
feedback-driven revision.

The MCP now also records richer warning types for:

- per-token placeholder cleanup
- empty sections
- section-count mismatches
- missing chart surfaces
- invalid image-src states

The MCP also now persists artifact state locally alongside each artifact:

- saved assembly plan
- saved prompt and control parameters
- current artifact file pointer
- lightweight revision history when `regenerate_section` writes a new version

This is what makes section-level regeneration possible without rerendering the whole artifact from scratch every time.

The same state layer now also supports chunk-built drafts:

- `create_artifact_draft` creates an unpublished draft scaffold
- `append_section` appends one source-backed section at a time
- `finalize_artifact` kicks off render/publish for the completed draft
- `get_artifact_status` polls the background render job until the draft is published or failed

The same persisted state now also supports feedback-driven revisions:

- feedback notes can be saved against a published artifact from the live Iterate link
- `get_artifact_feedback` reads those notes back through the MCP
- `apply_artifact_feedback` revises the saved plan and republishes under the same `artifact_id`

The MCP folder now also includes a fixed regression corpus and runner:

- [run_regression_harness.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/run_regression_harness.py)
  Runs a canonical set of prompts with explicit assertions for wrapper/engine selection, visible facts, warning types, and narrative-contract fields.
- [regression_cases.json](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/regression_cases.json)
  The default case corpus for that regression harness.

Use it when a runtime or prompt-stack change needs a measurable answer to:

- did the right engine/wrapper get selected?
- did exact facts survive?
- did the narrative contract survive?
- did the artifact ship cleanly?

The MCP now also has a deterministic template-instance path for reusable artifacts.

`list_templates` returns:

- public fixed-template families
- supported input rails
- preferred input mode and contract metadata for strict templates
- renderer identity and summary metadata
- tags, categories, slot metadata, composition hints, and quality baselines

`match_template` returns:

- the best matching fixed templates for a prompt
- confidence scores
- the current match method:
  - `exact_name`
  - `tag_match`
- supported input modes, preferred input mode, and human-readable input guidance so clients can tell the user how to continue before rendering

`preview_normalized_input` returns:

- normalized template input
- parse confidence
- warnings
- preview rows

`generate_from_template`:

1. normalizes raw input or accepts already-normalized input
2. skips the planner
3. renders a fixed template shell with new data
4. writes the artifact through the same artifact pipeline
5. returns the same preview-link style payload as other IterFact runs

Template-aware clients should now pause before rendering and surface the matched
template's supported input rails, preferred mode, and any strict contract
requirements such as required columns or sheets.

This is the first live path for:

- frozen template structure
- data-driven refresh
- private reusable template copies on the site

The current live catalog is still intentionally small, but the matching surface
is now in place so the MCP can recommend fixed templates before a caller pays
for a freeform generation path.

Safest starter path for testing:

- use `revenue-dashboard-dark`
- send the same structured JSON shape shown in
  `Template Vault/IterFact-template-vault/templates/revenue-dashboard-dark/example-data.json`
  as `normalized_input`
- expect a deterministic template render with zero LLM calls and near-zero infra cost
- use `network-intelligence` when you specifically want CSV or pasted-source normalization today

## Admin passthrough uploads
IterFact now has an explicit admin-only passthrough path for trusted HTML uploads.

- normal users do not get HTML reconstruction
- admin callers can pass `upload_passthrough: true` to `generate_document`
- raw passthrough is only honored when the MCP request is authenticated with
  `ITERFACT_MCP_ADMIN_BEARER_TOKEN`
- this path hosts the original HTML runtime without IterFact reconstruction

Use this sparingly. It is intentionally separate from the default ingest path so
trusted/operator uploads do not weaken the platform safety posture for everyone else.

Important rule:

- IterFact-originated artifacts can be saved to the library
- arbitrary third-party HTML should not become a first-class saved library object

## Current structure
- [server.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/server.py)
  Public MCP server and `generate_document` tool.
- [prompt_stack_runtime.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/prompt_stack_runtime.py)
  Runtime prompt module resolver, planner/compiler guidance, and critic rails.
- [requirements.txt](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/requirements.txt)
  Python dependencies.
- [.env.example](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/.env.example)
  Required environment variables.
- [Dockerfile](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/Dockerfile)
  Container deployment path.
- [iterfact-design-tokens.css](/C:/Users/Paul/OneDrive/Documents/IterFact/Template%20Vault/IterFact-template-vault/iterfact-design-tokens.css)
  Shared Visualizer-derived design token layer injected into assembled artifacts.

## Local setup
From [MCP](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP):

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
Copy-Item .env.example .env
```

Then set at minimum in `.env`:

```env
ANTHROPIC_API_KEY=your_key_here
```

Recommended for live telemetry:

```env
NEXT_PUBLIC_SUPABASE_URL=
SUPABASE_SECRET_KEY=
ANTHROPIC_API_KEY_LABEL=iterfact-mcp
```

Optional but important for true billed-cost reconciliation:

```env
ANTHROPIC_ADMIN_API_KEY=
```

Run:

```powershell
python server.py
```

Health check:

```text
http://localhost:8000/healthz
```

MCP endpoint:

```text
http://localhost:8000/mcp
```

## R2 output setup
R2 is Cloudflare object storage.

If you want generated artifacts uploaded instead of only saved locally, set:

```env
R2_ACCOUNT_ID=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_BUCKET=
R2_PREFIX=generated
ITERFACT_PUBLIC_ARTIFACT_BASE_URL=https://a.iterfact.com
```

Important rule:

- the tool can upload to R2 without a preview URL
- but for Claude users to click and open the result, you should configure a public artifact base URL
- the long-term intended public artifact base is still `a.iterfact.com`

## Analytics and cost tracking
The MCP now writes generation telemetry in two places:

### 1. Local bridge logs
- [generation_events.jsonl](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/analytics/generation_events.jsonl)
- [generation_failures.jsonl](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/analytics/generation_failures.jsonl)

These remain useful as an operator-side bridge and export source.

### Metadata-only telemetry stream
- `analytics/telemetry/YYYY-MM-DD.jsonl`

This stream is the new lightweight optimization feed. It intentionally stores
metadata only:

- trigger source
- prompt category and prompt token estimate
- engine/template selection
- LLM call count, token totals, and estimated cost
- quality score, warning types, and critic-fix cycles
- final artifact status and render duration
- regeneration metadata

It does not store the user's prompt text, uploaded source text, or structured
artifact content.

HTTP summary endpoint:

```text
GET /v1/telemetry/summary
GET /v1/telemetry/summary?days=30
```

This returns aggregate stats such as:

- total artifacts
- published vs failed counts
- average estimated cost
- average quality score
- top engines
- top prompt categories
- top warning types
- trigger-source mix
- template usage

### 2. Supabase durable telemetry
When `NEXT_PUBLIC_SUPABASE_URL` and a Supabase service key are configured, the
MCP also writes directly into the control plane:

- `generation_runs`
- `generation_run_engines`
- `provider_usage_events`
- `view_events`
- `engagement_events`
- optional daily rollups:
  - `cloudflare_zone_daily`
  - `r2_inventory_daily`

This is now the real source of truth for live cost and usage dashboards.

Each successful generation row can now capture:

- timestamp
- source surface and actor key
- user id when available
- artifact id and preview URL
- title and artifact family key
- wrapper/document format
- section count and section titles
- full engine usage list
- Anthropic usage tokens
- immediate formula cost
- generation timings
- structured data presence and top-level keys
- engine override requests and requested section count
- render warnings and quality score
- anonymous or connector-provided client id if the client provides one

Provider-step rows can now capture:

- provider
- model
- provider request id
- usage bucket date
- input/output/cache token usage
- immediate formula cost
- later reconciled actual cost

Failure runs now also sync provider-step usage rows into Supabase, so cost
analysis does not disappear just because a render failed before publication.

View telemetry can now arrive from both sides of the product:

- `iterfact.com` page views beacon into `view_events`
- hosted artifact views from the Iterate shell on `a.iterfact.com` beacon into `view_events`
- internal library thumbnail previews should not count as artifact views
- artifact opens from the owner's own library can be tagged separately from public or embedded artifact opens
- shared-recipient artifact views can now be tied back to `artifact_share_invites` through `share_invite_id`

Public view endpoint:

```text
POST /v1/telemetry/view
```

Public engagement endpoint:

```text
POST /v1/telemetry/engagement
```

Supabase-backed cost audit endpoint:

```text
GET /v1/telemetry/cost-audit?days=30&limit=25
GET /v1/telemetry/cost-audit?artifact_id=<artifact-id>
```

This returns cost and token totals plus recent provider usage rows grouped by:

- day
- model
- step
- generation mode
- source mode
- recent generation run

Expected payload shape:

```json
{
  "event": "view",
  "surface": "site",
  "path": "/create",
  "url": "https://iterfact.com/create",
  "referrer": "https://claude.ai/",
  "viewport": { "w": 1440, "h": 900 }
}
```

The MCP stores only metadata such as sanitized path/url, sanitized referrer
host, viewport size, user agent, and a hashed client IP. It does not store page
HTML, user prompt content, scroll depth, dwell time, or section-level reading
behavior in `view_events`.

The engagement stream is intentionally coarse:

- CTA clicks such as `contact_agent`, `book_showing`, `request_comps`, and `copied_link`
- aggregate module interactions such as `payment_calculator_used`, `pricing_scenario_toggled`, and `map_section_expanded`
- viewer buckets such as `owner`, `recipient`, and `audience`
- library search usage and zero-result search events without storing raw query text

This is meant for creator-side product analytics and share follow-up, not reader surveillance.

The previous local analytics files are still written into:

- [generation_events.jsonl](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/analytics/generation_events.jsonl)
- [generation_failures.jsonl](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/analytics/generation_failures.jsonl)

The export script:

- [export_usage_analytics.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/export_usage_analytics.py)

builds the current Excel workbook at:

- [IterFact MCP Analytics.xlsx](/C:/Users/Paul/OneDrive/Documents/IterFact/IterFact%20MCP%20Analytics.xlsx)

Important current limitation:

- without OAuth or a real IterFact account system, analytics can show volume, costs, engines, and artifact families
- it cannot yet reliably identify real users

Important cost-tracking distinction:

- `formula_cost_usd` is available immediately from the request-time usage response
- `reconciled_cost_usd` is reserved for later billed-cost allocation from Anthropic admin reporting

Cloudflare and R2 daily sync helper:

- [sync_cloudflare_r2_analytics.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/sync_cloudflare_r2_analytics.py)

This can backfill:

- daily Cloudflare zone traffic into `cloudflare_zone_daily`
- daily R2 object inventory snapshots into `r2_inventory_daily`

Before running it, create the supporting Supabase tables from:

- [2026-03-22_iterfact_analytics_views_cloudflare.sql](/C:/Users/Paul/OneDrive/Documents/IterFact/scripts/2026-03-22_iterfact_analytics_views_cloudflare.sql)

## Quality harness and comparison tooling
IterFact now also has an operator-side quality loop for engine coverage and visual review.

Coverage harness:

- [run_quality_harness.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/run_quality_harness.py)
- [quality_cases.json](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/quality_cases.json)

Visual report:

- [mcp-quality-report.js](/C:/Users/Paul/OneDrive/Documents/IterFact/apps/web/scripts/mcp-quality-report.js)

Direct side-by-side baseline comparison:

- [mcp-compare.js](/C:/Users/Paul/OneDrive/Documents/IterFact/apps/web/scripts/mcp-compare.js)

One-command wrapper:

- [run_iterfact_mcp_quality_harness.ps1](/C:/Users/Paul/OneDrive/Documents/IterFact/scripts/run_iterfact_mcp_quality_harness.ps1)
- [run_iterfact_mcp_quality_harness.cmd](/C:/Users/Paul/OneDrive/Documents/IterFact/scripts/run_iterfact_mcp_quality_harness.cmd)

Run a small harness sweep:

```powershell
.\scripts\run_iterfact_mcp_quality_harness.cmd -MaxCases 1 -MaxEngines 1
```

Run a broader wrapper sweep:

```powershell
.\scripts\run_iterfact_mcp_quality_harness.cmd -MaxCases 3 -MaxEngines 8
```

After the run, open the generated local visual report:

- `MCP/quality_reports/<timestamp>/visual-report/index.html`

Create a direct side-by-side comparison:

```powershell
cd apps\web
node .\scripts\mcp-compare.js --baseline https://example.com/source --artifact https://a.iterfact.com/generated/.../artifact.html --output ..\..\MCP\quality_reports\comparison-demo
```

## Anthropic billed-cost reconciliation
True provider billing is bucketed and delayed on Anthropic's side, so the MCP
ships a separate reconciler:

- [reconcile_anthropic_usage_costs.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/reconcile_anthropic_usage_costs.py)

Run it after setting `ANTHROPIC_ADMIN_API_KEY`:

```powershell
python reconcile_anthropic_usage_costs.py 7
```

Or from the repo root:

```powershell
.\scripts\reconcile_iterfact_costs.ps1 -DaysBack 7
```

That script:

1. fetches Anthropic Usage API buckets
2. fetches Anthropic Cost API buckets
3. upserts those buckets into Supabase
4. allocates billed cost back onto `provider_usage_events`
5. rolls reconciled cost up onto `generation_runs`

This gives IterFact both:

- immediate real-time formula cost for product ops
- later finance-grade billed-cost reconciliation for margin analysis

## Existing artifact backfill
The helper script:

- [backfill_library_shell.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/backfill_library_shell.py)
- [backfill_r2_html_backups.py](/C:/Users/Paul/OneDrive/Documents/IterFact/MCP/backfill_r2_html_backups.py)

can patch the live save-to-library CTA into existing local and R2-hosted artifacts so old IterFacts participate in the new library loop too.

The backup helper can create immutable `_source/<published-file>.html` copies for every hosted artifact so future repair or shell backfill work never operates against the only surviving copy.

## Public deploy target
Current public server target:

- domain: `mcp.iterfact.com`
- MCP endpoint: `https://mcp.iterfact.com/mcp`
- Railway service: `iterfact-mcp`
- Railway environment: `production`

Remote host options remain:

- Railway
- Fly.io
- Render

Cloudflare should handle DNS.
R2 should hold generated artifact output.

## Current production deploy reality
As of `2026-03-22`, the live MCP is deployed manually to Railway.

Important truth:

- `git push origin main` does **not** update `mcp.iterfact.com` by itself
- the Railway service is not currently GitHub-connected for auto-deploy
- production must be updated with an explicit Railway CLI deployment from the repo

Deploy from the repo root:

```powershell
cmd /c npx --yes @railway/cli deployment up -s iterfact-mcp -e production -d -m "Deploy message"
```

Verify what production is actually running:

```powershell
cmd /c npx --yes @railway/cli deployment list -s iterfact-mcp -e production --limit 3 --json
cmd /c npx --yes @railway/cli service status -s iterfact-mcp -e production --json
cmd /c npx --yes @railway/cli service logs -s iterfact-mcp -e production -n 80
```

Testing rule:

- do not assume endpoint health alone proves the newest code is live
- check the active Railway deployment id and timestamp before running a quality battery
- if the active deployment predates the target commit, deploy manually first

## Rachel test path
The first real external test should be:

1. deploy this server remotely
2. point `mcp.iterfact.com` to it
3. configure R2 output
4. connect the remote MCP in Claude
5. ask Claude to use `IterFact` and call `generate_document`

## Current boundary
This is not the full IterFact product.

It is:

- a public connector surface
- a real testable distribution channel
- a thin wrapper around the existing planner and assembler

It is not yet:

- account-aware billing
- draft save into the future real library
- moderation or publish workflow
- multi-tool MCP suite

Important current nuance:

- uploaded HTML is no longer reconstructed into managed IterFacts by default
- admin passthrough can still host the original runtime as-is
- non-admin HTML ingest is rejected with a clear “use templates or create from scratch” product policy
- IterFact still uses planner plus assembler for normal prompt-driven document generation
- IterFact can now open generated or hosted artifacts in `Iterate` or `Fact` mode when requested

That is intentional.
The point of this slice is to prove IterFact can be called as a real tool.
