npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zitegen/mcp

v0.0.31

Published

MCP server for Zitegen — manage AI-generated websites from Claude Code, Cursor, or any MCP-compatible tool.

Readme

@zitegen/mcp

An MCP (Model Context Protocol) server for Zitegen — manage your AI-generated websites from Claude Code, Cursor, or any MCP-compatible tool.

Setup

MCP access requires a Pro plan or above (Free-tier accounts can't generate an API key).

  1. Generate an API key in the Zitegen dashboard: Settings → API keys → Generate key.
  2. Add the server to your MCP client:
claude mcp add zitegen -e ZITEGEN_API_KEY=zit_... -- npx @zitegen/mcp

(For Cursor or another MCP client, point it at npx @zitegen/mcp with the same ZITEGEN_API_KEY environment variable.)

By default this talks to http://localhost:3000 (the dashboard dev server). Set ZITEGEN_API_URL to point at a deployed Zitegen instance instead.

Tools

  • list_sites — list all your sites.

    "What sites do I have in Zitegen?"

  • generate_site — generate a new site from a prompt (consumes 10 credits). Pass locales (e.g. ["fr", "pt-br"]) to also translate the new home page into those locales right away — one translate-page credit per locale, each attempted independently so one failure doesn't affect the site itself or the other locales.

    "Generate a Zitegen site for a portfolio for a photographer named Alex." "Generate a Zitegen site for a bakery, and translate the homepage into French and Spanish."

  • list_pages — list a site's pages (pageId, path, title, locale, translationGroupId, status). The cheap way to get a pageId — unlike get_files, it doesn't compile the project's Astro/CSS output first. Every page has a locale and a translationGroupId, even on a single-locale site; pass locale to only list pages in one locale. status is "draft" or "published" — a draft page never appears in any real deploy until published. Never includes a content type's detail-page template — see manage_templates for those.

    "What pages does my portfolio site have?" "What French pages does my portfolio site have?"

  • get_page_tree — get one page's raw, editable component tree — the same shape push_files expects back — plus its locale/translationGroupId/status. Use this (not get_files) before hand-editing a page. Not for content-type templates — those aren't ordinary pages; use manage_templates instead.

    "Show me the component tree for my portfolio site's homepage."

  • get_files — get a site's compiled Astro project files (every page, fully compiled Astro/CSS/config). Costs more per call than list_pages/get_page_tree — prefer those when you just need a pageId or one page's editable tree.

    "Show me the files for my portfolio site."

  • push_files — replace one page's component tree with edited JSON. This is a full-tree replacement, not a patch to compiled .astro source — fetch the tree via get_page_tree first, edit it, then push it back. Style nodes with Tailwind utility classes in props.className (this is how AI-generated content is styled); leave styles empty unless you need something Tailwind can't express. For a small or mechanical edit, prefer patch_page — it costs a lot less for a page with many nodes.

    "Update the hero heading on my portfolio site's homepage to 'Alex Kim, Photographer'."

  • patch_page — apply a small batch of targeted edits (insert/update/replace/delete by node id) without fetching or resending the whole tree — the cheap alternative to get_page_tree + push_files for adding a field, tweaking a color, or removing a section. Node ids come from a prior get_page_tree/push_files/generate_site response; batch several ops into one call rather than one call per change.

    "Add a subject field and a message field to the contact form on my portfolio site."

  • convert_to_shared_component — turn an existing node into a site-scoped Reusable Component (e.g. a Header or Footer shared across pages) — the same action as the canvas's "Convert to reusable component" menu item. Neither push_files nor ai_prompt does this on their own; call this afterward on the node they created.

    "Turn the header I just added into a reusable component."

  • list_shared_components — list a site's Reusable Components (id, name, rootType, pageCount). Use this to find an existing one before creating a duplicate with convert_to_shared_component, or to get the id attach_shared_component needs. Doesn't include each definition's full tree — read it via get_page_tree on any page that already uses it (its instance node carries the same id this tool returns).

    "What reusable components does my portfolio site have?"

  • attach_shared_component — add an existing Reusable Component onto a different page as a new instance (appended as the last child of that page's root). This is the reuse-elsewhere half convert_to_shared_component doesn't do on its own.

    "Add the Footer component to my portfolio site's About page too."

  • preview_site — render a site's current state via a real astro build and deploy it to the preview branch, without touching production. Use this to verify a real build succeeds before calling publish_site — same idea as git push to a branch before merging. Shares a combined 150/day rate limit with publish_site (see below).

    "Preview my portfolio site before we publish it."

  • publish_site — render a site's current state via a real astro build and publish it to production on Cloudflare Pages. Limited to 150 real builds/day combined with preview_site — generous for normal use, but a guardrail against a runaway script or agent loop.

    "Publish my portfolio site."

  • ai_prompt — send a natural-language editing instruction to a page and save the result (consumes 3 credits). Unlike push_files, you don't need to fetch or hand-edit the tree yourself — the AI does that server-side, the same way the dashboard's chat panel does. The response's touchedNodeId is the node it just created/replaced (or null), for chaining into convert_to_shared_component. It has no concept of Reusable Components itself — "create a footer as a component" just adds a normal footer. pageId also accepts a content-type detail-page template's id (get one via manage_templates, not list_pages) — the AI is told the edit applies across every entry of that content type, so keep the instruction specific.

    "On my portfolio site's homepage, change the hero heading to 'Alex Kim, Photographer'."

  • manage_domain — connect, check, or remove a site's custom domain. connect attaches it to the site's Cloudflare Pages project (add a CNAME at your DNS provider pointing at the site's *.pages.dev host — Cloudflare verifies and issues SSL automatically); status re-checks live verification/SSL; remove detaches it.

    "Connect www.alexkimphoto.com to my portfolio site."

  • manage_site_settings — get or update a site's basic settings: display name, SEO title/description, the <title> tag template (e.g. "%site% | %page%" to put the brand first; must include %page%), language/locales, favicon, heading/body fonts, social links, Open Graph title/description/image, contact email, Design Guide text, color palette, custom brand colors, and the "Built with Zitegen" badge toggle. action: "get" returns current values; action: "update" applies a partial patch (only pass the fields you want to change). Does NOT cover analytics/tracking IDs, custom head/body code, or third-party integrations (Stripe, Mailchimp, CAPTCHA/spam-protection keys, etc.) — manage those from the dashboard's Site Settings / Integrations panels.

    "Set my portfolio site's SEO title to 'Alex Kim Photography — Portfolio'." "What's the current Design Guide for my portfolio site?"

  • manage_page_seo — get or update ONE page's SEO fields: meta title/description, canonical URL, noindex, OG image, structured-data (JSON-LD) type/fields, and focus keyword. Per-page metadata, distinct from manage_site_settings' site-wide defaults — a page's own value here overrides those when set, and falls back to them when left blank. pageId comes from a prior list_pages call. action: "get" returns current values; action: "update" applies a partial patch. schemaType/schemaData are validated server-side against the real per-type shape (12 types: none/article/faq/product/organization/recipe/howto/video/review/ event/course/book), so a bad payload fails with a clear error rather than being silently dropped.

    "Set the focus keyword on my portfolio site's homepage to 'wedding photographer chicago'." "Add FAQ structured data to my pricing page with these three questions..."

  • manage_articles — manage a site's blog articles AND every user-defined content type's entries (Phase 4 CMS, separate from the page tree get_files/push_files edit). contentType (defaults "blog") selects which type an action applies to — set it to a manage_content_types slug to work with that type's entries instead. Every article/entry has a locale and a translationGroupId, same as pages — pass locale to list to filter by it, or to create (defaults "en") to author a standalone entry tagged with that locale (not accepted on update — locale can't change after creation, same as contentType). Actions: list, get, create, update (any subset of fields — setting status to "published" is how you publish/unpublish, there's no separate action for it), delete, generate (blog-only: a topic string in, a {title, excerpt, body} draft out — not saved, follow up with create/update yourself). body is a structured doc (the same tiptap/ProseMirror JSON shape the dashboard's rich-text editor produces), not markdown — see the tool's own description for a worked example. create/ update also take metadata fields — author, category, tags, terms (the general form of category/tags and the only way to assign a custom taxonomy — see manage_taxonomies; { [taxonomySlug]: string[] }, each name auto-resolved or created), featured, showToc, socialImageUrl, series/seriesPart, section/sortOrder, readingTime, canonicalUrl, publishedAt, contentUpdatedAt — mirroring the dashboard editor's Settings tab. schemaType (none/article/faq/recipe/howto/ video/review/event/course/book) plus a matching schemaData object (e.g. FAQ's questions, Recipe's ingredients/instructions) drive the entry's structured data (JSON-LD) — see the tool's own description for the exact per-type shape. For a non-"blog" contentType, also set customFields ({ [fieldKey]: value } matching that type's field definitions). Entries compile into code download, this tool's own reads, and the live published site.

    "Create a draft blog post on my portfolio site titled 'Behind the Shoot: Golden Hour Portraits'." "Generate a draft blog post about our new golden hour portrait package, then publish it."

  • manage_content_types — define user-defined content types (e.g. "Recipes", "Team Members") alongside the built-in "blog" type, each with its own custom field schema. A type's slug becomes both its URL prefix (/recipes/*) and the contentType value manage_articles uses for its entries. Actions: list, get, create (requires name, slug, and fields — at least one, each with fieldKey/label/fieldType — one of text/textarea/richtext/ number/boolean/date/url/image/select/tagsrequired, and (for select) options), update (any subset — fields REPLACES the whole list, it doesn't merge), delete (irreversible, also deletes every entry of that type). status ("draft" or "published", defaults "published" on create) is settable on both create and update — a draft type's entire /<slug>/* route, and every one of its entries regardless of that entry's own status, is hidden from every real deploy until the type itself is published.

    "Create a Recipes content type with fields for servings (number), vegan (boolean), and source URL, then add a Banana Bread recipe."

  • manage_taxonomies — manage the categorization system behind manage_articles' category/ tags fields, extensible with custom taxonomies. Every site automatically has a "category" (hierarchical — terms can have a parent) and "tags" (flat) taxonomy; you only create TERMS for those, not the taxonomies themselves. A custom taxonomy you create (e.g. "Region") applies to every content type on the site, blog included — there's no per-type scoping. Actions: list, get (returns the full term list), create (name, slug, hierarchical defaults false), update, delete (irreversible, owner-only, cascades every term + assignment — "category"/ "tags" can never be deleted, and their slug can never change). Term actions — usually unnecessary, since manage_articles' terms field creates a term automatically just by naming it — createTerm (useful to pre-seed a fixed vocabulary before anything uses it), updateTerm, deleteTerm.

    "Create a 'Region' taxonomy for my site and tag the Golden Hour article with 'North America'." "What categories does my blog currently use?"

  • manage_templates — view and customize a content type's detail-page template — the shared layout every entry of that type renders through, bound with {{fieldKey}} placeholders and two marker nodes (data-zg-article-content for the entry body, data-zg-article-toc for its table of contents). A template is a real, editable component tree but not an ordinary page — it never appears via list_pages/get_page_tree/ push_files/patch_page. A content type can have several saved variants with exactly one ACTIVE per (content type, locale); the universal variant used when no locale-specific one exists is always templateLocale: "default". Actions: list (requires contentTypeSlug), get (requires pageId — full tree), create (requires contentTypeSlug; omit locale to get-or-create the default variant, or pass it to create a new active variant for that locale), rename (requires pageId/title), setActive (requires pageId), duplicate (requires pageId), delete (requires pageId, irreversible — deleting the active variant just falls back to the default template, or plain rendering if the default has none active either), updateTree (requires pageId/tree — full replace, like push_files), patchTree (requires pageId/ops — targeted edits, like patch_page). Keep the marker nodes and {{fieldKey}} placeholders intact when editing the tree, or every entry of that type silently loses its body/table of contents rendering, not just one. For a natural-language edit instead of hand-editing the tree, pass a template's pageId to ai_prompt.

    "Show me the blog post template for my portfolio site." "Create a French variant of the blog post template with a different hero layout."

  • list_leads — list submissions to a site's forms. Any <form> element in the component tree (see get_page_tree/push_files) is auto-detected and captured with no extra wiring — there's no separate form-builder step, and no action/backend to set up. Newest first, 50 per page (page, 0-indexed). Pass formId (a leads[].formId from a prior call) to filter to one form, which also returns fieldColumns — that form's real field names (from props.name on its inputs) — so leads[].data can be read by field name instead of an opaque blob. Read-only — deleting an individual submission or a whole form is dashboard-only (the Leads panel), not available via this tool.

    "List the most recent leads on my portfolio site's contact form."

  • manage_form — get or set a form's visitor-facing submit outcome, upload limits, and an outbound webhook: the success/error message text, an optional redirect URL to send the visitor to instead of an inline success message, maxFiles/maxFileSizeMb overrides that TIGHTEN (never loosen) the platform's default upload limits (5 files/submission, 10MB/image or document, 50MB/video) for this specific form, and webhookUrl — a public http(s) URL that gets a fire-and-forget POST of {event, siteId, formId, leadId, path, submittedAt, data} on every submission (a delivery failure never blocks or fails the submission itself; a private/internal address is silently rejected). Read live at submit time, so a change takes effect on the very next submission with no republish needed. "list" returns every form the site has captured a submission for; "get" fetches one by formId (a leads[].formId from list_leads); "update" applies a partial patch — pass an empty string to clear a text field (including webhookUrl) back to the default, or null to clear maxFiles/maxFileSizeMb back to the platform default. This customizes what the visitor experiences submitting the form, not the form itself (still authored via push_files/patch_page/ai_prompt) or its confirmation-email toggle (dashboard-only) — and the accepted file types themselves stay fixed platform-wide.

    "Set a custom thank-you message on my contact form and redirect visitors to /thank-you after they submit." "Limit my résumé-upload form to a single 5MB PDF." "Send every submission on my waitlist form to my Zapier webhook at https://hooks.zapier.com/hooks/catch/123/abc."

    The same tool also manages up to 5 custom, conditional admin-notification emails per form — an additional, independently-routed layer on top of the always-on default owner notification, never a replacement for it (e.g. "also email sales@ when the budget field contains 10000", on top of whatever always goes to the site's own contact address). "addNotification" (name, sendToEmail, subject, body required; enabled defaults true, conditions defaults to none/always-send), "updateNotification" (notificationId required, everything else a partial patch), "removeNotification" (notificationId only) — notificationId comes from a notifications[].id in this tool's own list/get/update result. subject/body support merge tags: {all_data} renders every submitted field as key: value lines, {field.fieldName} renders one field's value. Each condition compares one submitted field against equals/ contains/notEquals (all require value) or isEmpty/isNotEmpty (no value); all of a notification's conditions must match for it to fire (AND only, up to 5 — no OR/nested groups). A disabled notification is kept but never sent. This is dashboard-editable too, in the Leads panel — plain fields, not a visual rule-builder — either surface works interchangeably.

    "Add a notification on my quote-request form that emails [email protected] when the budget field contains '10000' or more." "Turn off the shipping-delay notification on my order form for now, but don't delete it."

Resources

  • zitegen://sites/{siteId}/project — the full compiled Astro project for a site (package.json, astro.config.mjs, every src/*.astro page and component, public assets, SEO files — the same output get_files returns), exposed as an MCP resource so a client can attach the whole project as ambient context instead of calling a tool. Discoverable via resources/list (lists one entry per site you own).

Adding a modal via push_files

A popup (signup form, announcement, pricing detail, etc.) is a "dialog" node — it compiles to a real native <dialog> element, and Zitegen's own client script wires up opening it, closing it on a backdrop click, and closing it on Escape. You only need to author two linked nodes anywhere in the tree you push:

  • A trigger — any node (typically "button") with props["data-zg-modal-open"] set to a short name you choose, e.g. "signup".
  • The dialog itself — a "dialog" node with props["data-zg-modal"] set to that same name. This name is unrelated to the node's "id" — never link them by id, since ids get reassigned on save.
  • Give the dialog one inner wrapper child holding its real content, and optionally a button with props["data-zg-modal-close"]: true for an explicit close control.
  • Never set props.open on the dialog, and never put a display-related class ("hidden"/"flex"/"block") on it — its closed-by-default state is handled natively by the browser, not by Zitegen's styling system.
  • For a dialog that should open itself instead of (or in addition to) a trigger — e.g. "show a promo popup 3 seconds after the page loads" — set props["data-zg-modal-delay"] to the delay in milliseconds as a string, e.g. "3000". It auto-shows on every eligible page load; there's no "only show once per visitor" memory.
  • To show a modal on some pages but not others (e.g. every page except Contact), just don't push that dialog/trigger pair into the excluded page's tree — there's no separate targeting field.

This works identically whether the site is live-deployed or downloaded as a standalone Astro project via get_files — the modal script ships in both.

Building a multi-step form via push_files

A "form" node can be split into steps that show one at a time — no separate tool, this is authored directly in the tree you push. Same marker-attribute convention as the modal above (nothing in the component-model schema changes):

  • A step is any descendant of the "form" node — typically a "div" — carrying props["data-zg-form-step"]: true. Steps are shown in the order they appear in the tree, not by an index you set.
  • A "Next" control is a "button" with props.type = "button" (never "submit" — that would trigger a real submission) carrying props["data-zg-form-step-next"]: true. A "Back" control is the same shape with props["data-zg-form-step-prev"]: true.
  • The real submit button (props.type = "submit", no step-next/prev props) goes only inside the last step — it's simply not interactable until a visitor reaches it.
  • Zitegen's own client script handles step show/hide and validates only the current step's fields before letting a visitor advance (native HTML validation — props.required, type="email", etc. — still works exactly as you'd author it on any input).

Optionally, add a progress indicator — a container outside every step (a sibling of them, e.g. a direct child of the "form") with props["data-zg-form-progress"]: true. The platform sets a "--zg-progress" CSS variable (0 to 1) on it every time the visitor moves between steps, so a child element can fill a bar with a Tailwind arbitrary width like "w-[calc(var(--zg-progress)*100%)]". For a text counter, give any element props["data-zg-form-progress-current"], "-total", or "-percent": true — its text is replaced with the live current step number (1-based), total step count, or percent complete; author sensible fallback content (e.g. "1") so it still reads correctly before scripts run.

For discrete step dots instead of (or alongside) a bar/counter, use a separate container — same outside-every-step placement — with props["data-zg-form-progress-dots"]: true. Give it exactly ONE child element with props["data-zg-form-progress-dot"]: true: the platform clones that single dot once per step automatically at runtime, so never author more than one dot by hand even for a 5-step form. Style the dot with a data-zg-dot-state attribute selector — e.g. Tailwind's "data-[zg-dot-state=current]:bg-blue-600 data-[zg-dot-state=done]:bg-blue-300" — the platform sets that attribute to "upcoming", "current", or "done" per dot on every step change.

This works identically whether the site is live-deployed or downloaded as a standalone Astro project via get_files — the multi-step script ships in both.