Paper Webpage Builder — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Paper Webpage Builder (MCP Server) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Use this skill to turn a paper project folder into a web-ready project page. It is optimized for repos with paper.tex, *.pdf, images/, and assets/, but also works from a PDF plus image assets.
Do not mechanically clone an existing webpage. Use prior pages only as references for interaction patterns and content completeness. The final design must follow the target paper's topic, figures, color palette, density, and audience.
Two failure modes to actively avoid:
overflow:hidden, or overflow-x:auto/scroll. Wide tables should use grouped columns, multiple full sub-tables, readable density reduction, or a larger responsive container while preserving values.template.html, and target index.html.scripts/scan_paper.py <paper.tex> when a TeX source exists. Multi-file projects are followed via \input/\include automatically. This gives the skeleton (title, authors, captions, table/figure metadata, links) — not the argument.scripts/extract_sections.py <paper.tex> to read the body prose of every section. The skeleton is not enough: designing from scan_paper.py alone is the main cause of shallow pages with abstract-paraphrased copy and invented headings. Read this output in full (use --full for truncated sections).scripts/scan_pdf.py <paper.pdf> when only a PDF is available; it produces the same shape of inventory (title, authors, abstract, sections, figure/table captions, links).scripts/extract_tables.py <paper.tex> to dump every table (caption, label, header rows, data rows) as JSON. Use this to seed the table ledger instead of eyeballing the .tex.references/paper_reading.md.references/module_patterns.md when choosing sections or table placement.scripts/convert_figures.py <source_images_dir> <target_figures_dir> (the legacy convert_figures.sh is now a shim around it). The script handles multi-page PDFs, .eps, .svg passthrough, raster passthrough, and writes a figures.manifest.json that records source→output mapping plus an empty alt field per asset for the LLM to fill.web-logo.* / favicon is a project icon, not the teaser figure and not an institution logo.scripts/extract_citation.py <paper.tex> [--pdf paper.pdf] and embed the produced BibTeX as the page's citation default.% NOTE: comments verbatim; they document fields that were inferred or guessed (year, venue, authors)..warn div or multi-line warning block for draft citations — it reads as broken, not cautious.@article with a best-guess year (e.g., 2025) rather than @unpublished when the paper has a clear title and authors but no venue yet. The @unpublished type confuses citation tools and looks unprofessional on the page.% NOTE: comment lines from the BibTeX shown on the page; they are for the builder's internal tracking, not for end users to see.figures.manifest.json. Wide pipeline/heatmap/table figures, tall case figures, and compact charts should not be forced into one equal-height grid.object-fit: contain for paper figures. Use natural image height (width: 100%; height: auto) by default; reserve fixed boxes for icons, logos, thumbnails, or zoom modals.references/design_principles.md when deciding visual style or revising design feedback. The "Measurable Criteria" section there names the thresholds the design-drift check enforces.assets/single-page-template.html. It includes semantic landmarks (header/nav/main/footer), a skip-link, a prefers-reduced-motion fallback, a CJK-friendly font stack, and the {{LANG}} {{TITLE}} {{DESCRIPTION}} {{CANONICAL_URL}} {{OG_IMAGE}} {{JSONLD}} placeholders. Fill them with scripts/render_template.py (or scripts/inject_metadata.py --inplace if you only need to refresh the head block of an existing page).<html lang="..."> to the paper's language (BCP-47: en, zh-CN, ja, ko, etc.); the scan scripts do not auto-detect this. For CJK papers keep the bundled font stack and add lang attributes around any embedded English titles so browsers pick the right glyphs.scripts/inject_metadata.py --title ... --description ... --canonical ... --og-image ... --author ... and pipe its render-values JSON into render_template.py. Use --arxiv / --doi when known so the ScholarlyArticle JSON-LD includes a stable identifier.index.html unless the repo already has a framework.compact-table, inspect column count, header length, and cell density:colgroup widths when needed.Active stages, Family, Tier, Rank, Metric, and Scenario. Give those columns enough width and use white-space: nowrap when values are short.Family, Active stages, Tier, Rank, Best config., and compact stage/objective codes. Keep long mechanism/explanation columns left-aligned.<table> (or its wrapping <section>) with data-tex-label="<label>" so step 8 can reconcile it.overflow-x:auto/scroll, overflow:hidden, fixed heights, clipped cards, text fades, or partial table previews.--primary, --accent, --surface) not hue-literal names (--green, --blue). See references/design_principles.md "CSS Variable Naming" section.var(--...) reference.height: 48px; object-fit: contain for consistent visual weight across logos of different native sizes.@media (max-width: 960px) breakpoint that reduces to max 3 columns. Verify no grid item is narrower than 180px at any viewport 360-1440px.align-items: start or baseline-aligned captions when aspect ratios are compatible; if aspect ratios differ substantially, stack the figures or redesign the row. Reserve object-fit: contain for logos, icons, thumbnails, and footer marks, not for primary paper figures.scripts/check_webpage_links.py <index.html> --full for the broader lint: missing src/href/data-figure, CSS url(...), srcset, <source>/<video poster>/preload, og:image/twitter:image, broken #fragment targets, duplicate ids, missing alt/title on <img>/<iframe>, and path-traversal hrefs that resolve outside the page directory. Use --json when you need a machine-readable report.scripts/reconcile_tables.py --ledger <ledger.json> --html <index.html>. The ledger is the JSON produced by extract_tables.py in step 1; tag any HTML <table> (or its wrapping <section>) with data-tex-label="<label>" to enable strict matching. The script flags missing, abbreviated, and column-stripped tables; treat any "MISSING(central)" or "abbrev" line as a blocker.scripts/check_table_fit.py <index.html> --json to verify rendered tables are not clipped, do not spill outside their visual containers, and do not require horizontal scrolling. Treat any error as a blocker; this means the first generated page design is wrong and must be repaired before delivery.scripts/check_html_sanity.py <index.html> for HTML5-aware validation. It prefers html5validator, then tidy, then html5lib; without those installed it falls back to a stdlib structural check and says so explicitly. Do NOT use xmllint --html — it false-flags HTML5 elements like <main>, <dialog>, and <picture>.scripts/check_design_drift.py --html <index.html> --manifest <figures.manifest.json> [--reports-dir reports]. It computes palette overlap between the figure colors and the page's chosen colors, flags unsupported grid/dot/notebook backgrounds, warns about fixed figure containers that can create blank space, warns about mixed-ratio figures placed in the same row, warns about overstretched low-column tables, flags CSS variable names that contradict their hue, flags hardcoded hex colors not backed by a CSS variable, flags grids missing responsive breakpoints, and warns when class-token overlap with a sibling reports/<other>/index.html exceeds 70% (likely template clone). Treat any warning as a design review item, not silent acceptance.scripts/check_webpage_links.py <index.html> --full --check-external to HEAD-request all external URLs (with 5s timeout, max 20 requests). Any URL not provided by the user or extracted from the paper should be tagged with data-unverified="true" in the HTML so this check surfaces them clearly. Treat 4xx/5xx responses as blockers; treat timeouts and data-unverified links as warnings requiring user confirmation.scripts/capture_screenshots.py <index.html> --out-dir reports/<slug>/screenshots. The script tries Playwright, then Pyppeteer, then headless Chromium; if none are installed it skips cleanly with meta.json.status == "skipped" so the rest of validation can continue.object-fit: contain on footer logos), record why it is acceptable rather than silently ignoring it.meta.json skip reason when screenshots were not captured.When asked to upload a generated webpage to GitHub Pages or a project repo:
gh-pages for the webpage branch unless the user names another branch.main unless explicitly requested.main, create a minimal README-only main branch and keep webpage content isolated on the page branch.git ls-remote --heads <repo> and report whether the target branches already exist.git ls-remote --heads <repo> and report the branch names and commit hashes.python3 scripts/webpage_workbench.py --port 8765.codex exec when available; override with --agent-command for Claude or another runner), tracks progress, logs output, and mounts the generated index.html in the preview pane.annotation.json, context.html, and repair_prompt.md under <paper-project>/.paper-webpage-builder/annotations/<timestamp>/, then includes that context in the next chat turn when requested.references/visual_workbench.md for the exact closed-loop contract.In the final response, include:
index.html path.lang, canonical URL, OG image; DOI/arXiv if known) — these populate the metadata block of the output schema.not_run with a reason rather than omitting an entry).The output is validated against schemas/output.schema.json; keywords and quality_checks are required.
references/paper_reading.md: deep-reading protocol and the Paper Brief that all page copy must derive from. Read this before designing.references/module_patterns.md: section patterns, paper-content extraction targets, and table handling.references/design_principles.md: visual design rules for paper webpages.references/visual_workbench.md: local UI loop for previewing generated pages, selecting visual defects, and handing region context to an agent.scripts/scan_paper.py: summarize TeX title/authors/abstract/sections/figures/tables/links; follows \input/\include. Emits an explicit warning when the file is not UTF-8 (with the fallback encoding it used). Produces the skeleton only — not section body prose.scripts/extract_sections.py: print the cleaned body prose of every section (drops float/equation noise, keeps citations as [cite] and contribution lists). Read this to understand the paper's argument before designing; pair it with references/paper_reading.md.scripts/scan_pdf.py: PDF-only inventory in the same shape as scan_paper.py (used for kind: pdf_with_assets).scripts/extract_tables.py: dump every LaTeX table (caption/label/header/data rows) as JSON for the table ledger.scripts/extract_citation.py: produce a best-effort BibTeX draft with explicit notes for unverified fields.scripts/convert_figures.py (and convert_figures.sh shim): convert paper figures to web assets and emit figures.manifest.json. Handles multi-page PDFs, .eps, .svg, raster passthrough, and CJK filenames.scripts/render_template.py: substitute {{NAME}} placeholders in assets/single-page-template.html; --inplace refreshes only the head metadata of an existing page.scripts/inject_metadata.py: build SEO + Schema.org ScholarlyArticle JSON-LD; outputs render_template.py values, a copy-paste meta block, or runs the in-place head refresh in one shot.scripts/check_webpage_links.py: link/lint check for local assets; --full adds CSS url(...), srcset, <source>/poster, preload, og:image, #fragment targets, duplicate ids, missing alt/title, and path-traversal warnings. --check-external HEAD-requests external URLs (max 20, 5s timeout) and flags 4xx/5xx as errors, timeouts as warnings.scripts/check_design_drift.py: palette overlap between figures and page, plus figure-container, grid/dark/clone warnings, CSS variable name vs hue mismatch, hardcoded color orphans, grid responsive breakpoint checks, and paired-figure alignment checks against sibling reports/.scripts/reconcile_tables.py: cross-check the LaTeX table ledger against <table>/data-tex-label in the rendered HTML.scripts/check_table_fit.py: browser-level table fit check; flags clipped rows/cells, tables spilling outside white/card visual containers, and tables that require horizontal scrolling.scripts/check_html_sanity.py: HTML5-aware sanity check (html5validator → tidy → html5lib → stdlib fallback).scripts/capture_screenshots.py: desktop+mobile screenshots and meta.json (Playwright → Pyppeteer → headless Chromium; skips cleanly if none installed).scripts/webpage_workbench.py: dependency-light local chat UI/API for agent-backed paper webpage generation, progress/log streaming, preview mounting, iframe region marking, annotation capture, and local visual repair turns.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.