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

@cloudhal/mcp-server

v0.6.2

Published

Cloudhal MCP server: semantic site tools (pages, articles, snapshots, preview, publish) governed by the policy engine

Readme

@cloudhal/mcp-server

The Model Context Protocol surface of Cloudhal (spec 2026-07-28, @modelcontextprotocol/server 2.3). ADR-0004 and ADR-0011.

Tools: every tool, with its scopes and whether a person approves it, is in the tool reference below, generated from the definitions in src/tools.ts (node scripts/mcp-tools-doc.ts; CI fails when it is stale). The server lists them in a fixed order. Every tool has JSON Schema input/output (built from the contract schemas), honest annotations and structuredContent. Updates need the current rev (optimistic locking). Every mutation snapshots automatically (exactly one snapshot per call; governance snapshots carry cloudhal.opId).

Taxonomy (RFC-0002): create_entry / update_entry take terms: { [taxonomy]: { set?, add?, remove?, primary? } } with values by id, slug, old slug, name, synonym or singular/plural, resolved by the store; an unknown or ambiguous value is an error with suggestions (unknown_term, ambiguous_term), never an implicit create. tags is a deprecated alias for terms.tag.add. list_entries takes the RFC §5 filter. Agents add terms only through propose_term (allowNewTerms: none → denied, propose → proposed, free → active; an existing match is returned). Structural tools (site:write) always wait for a person; their prompts list the articles touched and every redirect. Agents never redirect off-site, never reclaim slugs and never approve their own proposals (checked before anyone is asked). Store invariant errors map to stable codes (required_term, too_many_terms, …) with the next step in the text. Assignment deltas and governance results (opId, counts, redirects) go into the audit row's input.effect.

Approval: publish, rollback, delete_* and the structural taxonomy/redirect tools return MCP's multi-round-trip input_required with an elicitation (the message carries the diff summary) and an HMAC-signed requestState bound to the tool, the arguments and the principal. The person answers in their client; the call completes only on an explicit accept. Clients without elicitation get a confirmToken to pass on a second identical call (confirmTokenFallback: false turns that off).

Skills: skill://<name>/SKILL.md and skill://<name>/references/<file> resources. A client that follows a skill sends _meta["cloudhal.skill"] = "<name>"; the policy engine then limits the call to that skill's declared scopes.

import { createCloudhalMcpServer } from "@cloudhal/mcp-server";          // McpServer for one principal
import { serveCloudhalStdio } from "@cloudhal/mcp-server/stdio";          // what `cloudhal mcp` runs
import { createCloudhalMcpHandler } from "@cloudhal/mcp-server/http";     // fetch(request, principal) — mounted at /mcp by @cloudhal/api

tools.ts is SDK-independent (defineTools(), runTool() = policy → audit → handler) and is what the tests exercise first; test/e2e.test.ts runs a real client ↔ server pair over the in-memory transport and proves the Sprint 5 exit criterion.

Tool reference

110 tools, in the order the server lists them.

| Tool | Scopes | Kind | Approval | What it does | |---|---|---|---|---| | list_sites | site:read | read | — | Sites this principal can operate. | | get_site | site:read | read | — | A site with its page order, locales, theme, published snapshot and per-collection settings (collections.<key>.permalink, e.g. /blog/{slug}; a collection without one lives at /<c… | | list_pages | content:read | read | — | Pages of a site in navigation order (all statuses). | | get_page | content:read | read | — | One page with its sections and current revision (send rev back when updating). | | create_page | content:write | write | by the key's mode | Create a draft page and append it to the site's navigation. | | update_page | content:write | write | by the key's mode | Replace a page's title, path, sections or seo, or set its own motion level and chrome (RFC-0025: header default|transparent|hidden, footer default|minimal|hidden; null goes back t… | | get_catalog | content:read | read | — | The components a page surface may use on this site (RFC-0020): Cloudhal's layout, content and interactive primitives plus the theme's block types, with their props, child rules, b… | | patch_draft | content:write | write | by the key's mode | Build or change a page step by step with RFC 6902 JSON Patch operations on /title, /sections and /seo, e.g. {"op":"add","path":"/sections/-","value":{…a surface…}}. | | import_surface | content:write | write | by the key's mode | Add (or replace) a page surface from another generative-UI format: a2ui takes A2UI v0.9 messages (createSurface, updateComponents, updateDataModel; basic catalog), json-render… | | export_surface | content:read | read | — | A page's surface section in another generative-UI format: a2ui (A2UI v0.9 messages for the basic catalog, images as absolute URLs when the site has a primary domain) or json-re… | | delete_page | content:write | write (destructive) | by the key's mode | Delete a page and remove it from navigation. | | list_entries | content:read | read | — | Content entries, newest first. | | get_entry | content:read | read | — | One content entry with its body and current revision. | | create_entry | content:write | write | by the key's mode | Create a draft content entry (markdown body) in an existing collection (list_collections; a new one needs create_collection first), optionally filed under existing terms (terms;… | | update_entry | content:write | write | by the key's mode | Update title, summary, body, slug, status (draft → review → published), terms (terms: add, remove or set categories and tags by id, slug or name) or typed custom fields (fields… | | add_correction | content:write | write | by the key's mode | Append a correction, clarification or update notice to an article (RFC-0019 §2) when a published story changed in substance. | | delete_entry | content:write | write (destructive) | by the key's mode | Delete a content entry. | | list_translations | content:read | read | — | Every language version of a page or entry (its translation group): locale, status, method (original, human, machine, machine-reviewed), whether it is stale (its source changed sin… | | list_stale_translations | content:read | read | — | Translations whose source changed since they were made (or whose source is gone), optionally in one locale: the work queue for keeping languages in step. | | get_translation_source | content:read | read | — | What you need to translate a page or entry into locale: the source (translate its title, text, summary and SEO; keep section ids, kinds, links, numbers, images and bindings), fo… | | save_translation | content:write | write | by the key's mode | Save your translation of a page or entry into locale as a draft linked to the source's current revision, with your model as its provenance. | | search_site | content:read | read | — | Find nodes by words instead of listing everything. | | get_theme_settings | content:read | read | — | The theme's design settings (RFC-0017): their schema (colors as #rrggbb, enums, numbers; per-locale titles), the contrast rules colors must meet, and the site's current values. | | set_theme_settings | site:write | write | always a person | Change the site's design settings (colors, type, spacing: get_theme_settings lists them). | | list_block_types | content:read | read | — | The typed blocks this site can use (RFC-0015): callouts, buttons, video embeds, FAQs and the theme's own. | | get_body | content:read | read | — | Read a markdown body (an article's, or a page's rich-text section) as blocks: each with its id, hash, type, markdown and who last changed it. | | edit_body | content:write | write | depends on the call | Change a markdown body by block instead of rewriting it: replace a block, replace text inside one, insert, move or delete blocks, insert a typed block (insertBlock: a callout, a b… | | diff_node | content:read | read | — | Compare one page or article between two versions: the fields that changed, and its body (or each rich-text section) block by block, with the words that changed inside edited block… | | get_design | site:read | read | — | Read this first when you build or redesign pages (RFC-0021). | | get_design_tokens | site:read | read | — | The site's design tokens as a W3C Design Tokens (DTCG 2025.10) file, for Figma (Tokens Studio), Style Dictionary or a designer: the color roles for light and dark, corner radii, s… | | set_design | site:write | write | always a person | Replace the site's design rules (DESIGN.md, Markdown, at most 24,000 characters; null removes them). | | get_chrome | site:read | read | — | The site's header, footer and brand (RFC-0025): what is set, what pages show (the defaults filled in), the form schemas with every allowed value, and the menu keys the header and… | | set_chrome | site:write | write | always a person | Change the site's header and footer (get_chrome lists the fields and allowed values). | | set_brand | site:write | write | always a person | Change the site's brand (get_chrome lists the fields): logo and logoDark as an image asset of the site (asset:<id>, from list_assets or upload_asset; the site's name shows without… | | render_page | preview:create | read | — | Look at a page's draft as people will see it: renders it in a real browser at desktop (1440), tablet (768) and phone (390) widths and returns screenshots plus automatic checks (si… | | list_taxonomies | site:read | read | — | The site's taxonomies (e.g. category, tag) and their rules: required (review/published articles need a primary term), maxPerEntry, allowNewTerms (none: only people add terms… | | list_terms | content:read | read | — | Terms of one taxonomy with names per locale, synonyms (altNames), old slugs (slugHistory), parent, status, visibility and usage (articles in total and published, last used). | | resolve_terms | content:read | read | — | Which existing terms of a taxonomy the given ids, slugs, names or synonyms mean (the same rules as assigning terms: id → slug → old slug → name → synonym → singular/plural). | | propose_term | content:draft:own | write | by the key's mode | Suggest a new term when no existing one fits (check list_terms / resolve_terms first). | | update_term | content:write | write | by the key's mode | Change a term's text only: names, synonyms, archive intro (description) and SEO per locale, and its image. | | rename_term | site:write | write | always a person | Change a term's slug (its archive URL). | | merge_terms | site:write | write | always a person | Merge duplicate terms into one target term of the same taxonomy: every article is re-filed under the target (primary terms follow), the sources retire and their names and slugs be… | | retire_term | site:write | write (destructive) | always a person | Retire a term: it is removed from every article, its sub-terms move up, and its archive redirects (301) to redirectToId (or, for categories, to its parent) — without a redirect… | | set_term_parent | site:write | write | always a person | Move a term under another term of the same hierarchical taxonomy (null: to the top level). | | set_term_visibility | site:write | write | always a person | Make a term public (archive, feed and sitemap entry; shown on articles) or internal (no public URL; themes and filters may still use it). | | review_term | site:write | write | always a person | Decide on a proposed term: approve (it becomes active and, if public, gets its archive), reject (retired and removed from articles) or merge (into an existing term, mergeIntoId). | | taxonomy_report | content:read | read | — | Findings about the vocabulary: single-use and unused terms, near-duplicates (edit distance ≤ 2, singular/plural) and synonym collisions, empty or oversized categories, archives wi… | | create_taxonomy | site:write | write | always a person | Create a taxonomy (e.g. series, region) with its rules and archive path. | | update_taxonomy | site:write | write | always a person | Change a taxonomy's rules, labels or archive (the key never changes). | | list_redirects | site:read | read | — | The site's redirects (from → to, 301/302, exact/prefix, reason, source node). | | create_redirect | site:write | write | always a person | Redirect a path the site no longer serves to another path of this site (agents cannot redirect to other hosts). | | delete_redirect | site:write | write (destructive) | always a person | Delete a redirect; its old path then answers 404 unless something serves it. | | list_collections | content:read | read | — | The site's collections (RFC-0040): declared ones, ones that already have entries, and posts. | | create_collection | site:write | write | always a person | Declare a new collection (RFC-0040), e.g. events, team or jobs, with its names in the site's default locale (labels), an optional icon, permalink (/<prefix>/{slug}, default /<key>… | | update_collection | site:write | write | always a person | Change a collection's names (merged per locale), icon, feed, sitemap, default sort (RFC-0040) or its typed custom fields (fields, the whole list; RFC-0041), list fields, date-dr… | | preview_structured_data | content:read | read | — | The JSON-LD document an entry's page emits under its collection's structured-data preset (RFC-0042: Event, JobPosting, Product, Recipe), built exactly as the site builds it, with… | | rename_field | site:write, content:write | write | always a person | Rename a typed custom field's key (RFC-0041 §6): every entry's value moves from from to to; nothing is converted. | | change_field_type | site:write, content:write | write (destructive) | always a person | Change a typed custom field's type, or a select's multiple (RFC-0041 §6), converting every value by a closed table (text↔richText, text↔number, text↔select, text↔url, date↔datetim… | | set_field_localized | site:write, content:write | write | always a person | Make a typed custom field localized (each language version has its own value; translations fall back to the original's) or shared (only the original holds it; translations' own co… | | remove_field | site:write, content:write | write (destructive) | always a person | Remove a typed custom field (RFC-0041 §6): its value is deleted from every entry, then its definition. | | set_collection_permalink | site:write | write | always a person | Move where a collection's entries live (RFC-0038): permalink "/blog/{slug}" puts collection "posts" at /blog/<slug>; null goes back to the default /<collection>/{slug}. | | list_menus | site:read | read | — | The site's navigation menus (primary = header, footer, sidebar, custom keys) with id, rev, maxDepth, item count and how many items are hidden on the site. | | get_menu | site:read | read | — | One menu: the stored tree (send its rev back when writing) and, per item for a locale, whether the site shows it (state), why not (reason: missing, unpublished, other-locale,… | | update_menu | site:write | write | always a person | Edit a menu with operations, applied in order and all or nothing: add {item, parent?, index?}, remove {itemId} (with its subtree), move {itemId, parent?, index?}, update {itemId,… | | set_menu | site:write | write | always a person | Replace a menu's whole tree (or create the menu: omit rev). | | delete_menu | site:write | write (destructive) | always a person | Delete a menu; the site falls back to the theme's derived default for that key (e.g. top-level pages in the header). | | list_assets | content:read | read | — | Files of a site's media library, newest first: images, video, audio, documents and data. | | get_asset | content:read | read | — | One file of the media library with its URL, checksum file and every place that uses it (node, field and title). | | upload_asset | asset:upload | write | by the key's mode | Add a file to the site's media library from base64 data (at most 4 MB decoded). | | fetch_asset_url | asset:upload | write | by the key's mode | Have the server download a public https file (at most 25 MB) into the site's media library: the server fetches it under strict rules (https only, no internal hosts or IP addresses… | | update_asset | asset:upload | write | by the key's mode | Change a file's alt text, title or caption (in locale), credit, license, decorative flag, URL slug or an image's focal point (focal: where the subject is, x and y from 0 to 1… | | retire_asset | asset:manage | write | by the key's mode | Hide a file from the library and pickers so nothing new uses it. | | restore_asset | asset:manage | write | by the key's mode | Make a retired file available again. | | delete_asset | asset:manage | write (destructive) | by the key's mode | Delete a file from the media library. | | create_snapshot | content:draft:own | write | by the key's mode | Freeze the current state of the site as an immutable, labelled snapshot. | | list_snapshots | site:read | read | — | Snapshots of a site, newest first, and which one is live. | | diff_snapshots | site:read | read | — | Added, changed and removed nodes between two snapshots. | | preview_page | preview:create | read | — | Render a page (or an article by entryId) to HTML as the site would show it, plus a preview link (signed, to the deployed site, or the cloudhal dev URL). | | publish | site:publish | write (destructive) | always a person | Snapshot the current state and make it live. | | rollback | site:rollback | write (destructive) | always a person | Point production at an earlier snapshot (must be an ancestor of the live one). | | get_analytics | analytics:read | read | — | How the site is doing over a range: page views, visits (entrances from other sites or typed in), unique visitors when the site counts them, the estimated cache-hit ratio (on Cloud… | | top_content | analytics:read | read | — | The most viewed pages, articles and archives in a range, with their current title and path (renamed articles show their new ones; deleted ones read as gone) and the change of page… | | content_stats | analytics:read | read | — | Readers of one page, article or archive (by id, so the history survives slug changes): page views and visits with the previous period, a series, visits by channel, referrer hosts,… | | broken_links | analytics:read | read | — | Requests that ended in 404 or 410 in a range, most hits first, with the same-site pages that link to them, when they were last seen and a suggested target (an existing redirect, o… | | list_comments | comments:read | read | — | Reader comments of a site, newest first (status pending, approved, hidden, spam, deleted, or reported for open reports). | | get_comment_thread | comments:read | read | — | One article's comments in display order (top level oldest first, each followed by its replies), with the thread's state and open reports' details. | | comments_summary | comments:read | read | — | Counts for a site's comments: by status, pending ages, open reports, the busiest threads, labels, unanswered questions and the daily cap. | | label_comments | comments:moderate | write | depends on the call | Add or remove advisory labels on up to 50 comments (spam-suspect, toxic, off-topic, question, needs-reply, praise, personal-data, …) for the people who review them. | | moderate_comments | comments:moderate | write | depends on the call | Change the status of up to 50 comments. | | draft_comment_reply | comments:reply | write | depends on the call | Draft a reply on the site's behalf to an approved comment (plain text, 2–5,000 characters). | | set_comment_thread | comments:moderate | write | depends on the call | Set an article's comment thread: closed (no new comments, existing ones stay) and hidden (nothing shown) reduce visibility; open, default and a closesAt date wait for a person in… | | list_schedule | schedules:read | read | — | What happens on the site between from and to (at most 62 days): releases (scheduled publishes), time gates (embargoes and expiries on articles and pages) and automation runs, with… | | get_schedule | schedules:read | read | — | One item from list_schedule by its id (rel_…, gate:<nodeId>:from|until, occ:<automationId>:<at>), with detail. | | list_releases | schedules:read | read | — | The site's releases (scheduled publishes) in time order, by state: draft, awaiting_approval, scheduled, running, published, conflict, missed, expired, failed, canceled. | | get_release | schedules:read | read | — | A release with its pinned items (titles, paths, pinned and live versions), what the dependency closure added, overlaps with other releases, locked nodes, the approval and the wait… | | propose_release | schedules:write | write | depends on the call | Leave a draft release for a person: the named articles and pages are pinned at their current versions (plus what they need that is not live yet), optionally with a time. | | schedule_release | schedules:write | write | depends on the call | Publish a release (or the named articles and pages, pinned now) at a time, or "on-approval". | | reschedule | schedules:write | write | depends on the call | Move a release to another time. | | cancel_schedule | schedules:write | write | depends on the call | Withdraw a release: nothing in it is published. | | set_embargo | schedules:write, content:write | write | depends on the call | Keep an article or page off the live site until from (an instant): it is absent from every page, list, feed and sitemap until then, enforced by the site itself. | | lift_embargo | schedules:write | write | depends on the call | Ask a person to publish an article or page before its embargo (remove from, or move it earlier). | | schedule_unpublish | schedules:write, content:write | write | depends on the call | Take an article or page off the live site at a time (it disappears from every page, list, feed and sitemap from then on, enforced by the site itself). | | list_automations | schedules:read | read | — | The site's automations: built-in jobs and scheduled agent tasks, with their recurrence, state and next run. | | list_runs | schedules:read | read | — | What happened at each occurrence of an automation or release, newest first: state (ran or not) and outcome (did something or not), usage, taint and the summary. | | get_run | schedules:read | read | — | One run with the audit rows, snapshots and approval requests it produced. | | begin_run | | read | — | Start this automation's run while its window is open (its key works only inside a run). | | end_run | | read | — | Close your run with a short plain-text summary (what you did, what a person should look at) and its outcome: did-work, nothing-to-do, proposed (you left proposals) or waiting-appr… | | pause_automation | | read | — | Stop an automation from running again (your own automation's key, or an automation of the person behind your key). | | get_mode | | read | — | Your current mode, your key's own mode, any time-boxed elevation in force (and when it ends), and the status of your latest elevation request. | | request_elevation | site:read | read | — | Ask a person for a more autonomous mode for a limited time (5–1440 minutes), e.g. AUTO for an hour to publish a series of small article updates. |