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

@artstorefronts/mcp

v0.2.0

Published

MCP server wrapping the Art Storefronts GraphQL surface for end-customer agents (Claude Desktop, Claude in Chrome, Cursor).

Readme

@asf/mcp

An MCP server that exposes the Art Storefronts GraphQL surface to MCP-capable agents — Claude Desktop, Claude in Chrome, Cursor, and anything else that speaks stdio MCP.

End-user shape: "On filip-cloudinary-regression-testing, add a product called 'Sunset' for $50, enable Lyve Canvas + Vibrance Gloss, remove the old print called 'Old Cat'" — and the agent walks through the right sequence of mutations under the user's authorization (website_ownage_policy enforcement happens server-side; this MCP server never bypasses it).

Status

v0.1 — initial release. Nine grouped tools wrapping the icono_pregame PR #4912 GraphQL surface. stdio transport. Env-var auth. Sibling to @asf/cli, which mirrors the same GraphQL client file-for-file (a future @asf/api package will collapse the duplication).

Why grouped tools and not one tool per mutation?

Empirically, admin agents start to degrade when an MCP server exposes more than ~20 tools — they reach for the wrong tool or stall. This server ships 9 tools with a verb discriminator + a verb-specific payload, well under that threshold. The tool descriptions name the underlying GraphQL field for every verb so the agent can cross-reference the runbook when in doubt.

Tool surface

| Tool | Verbs | Underlying GraphQL fields | |---|---|---| | discover | whoami, websites, website_detail | /api/v1/users/me, websites { stores pages ... } | | manage_website | update, update_subscribe_bar, update_announcement_bar | updateWebsite, updateSubscribeBar, updateAnnouncementBar | | manage_store | update_markups, update_default_sizes, update_medium_availabilities | updateMediumMarkups, updateDefaultActivatedSizes, updateMediumAvailabilities | | manage_product | create, bulk_create, update, add_photo, remove_photo, delete, bulk_delete | createProduct(s), updateProduct, addProductPhoto, removeProductPhoto, deleteProduct(s) | | manage_gallery | create, add_products, remove_products, set_cover, reorder | createProductGalleryPage, addProductsToGallery, removeProductsFromGallery, setGalleryCover, reorderGalleryProducts | | manage_page | update, delete | updatePage, deletePage (cascade-safe — store/blog need cascadeConfirmation: true) | | manage_contacts | create, bulk_create, delete, bulk_delete | createContact(s), deleteContact(s) | | manage_shipping | update_datum, create_method, update_method, delete_method | updateShippingDatum, createShippingMethod, updateShippingMethod, deleteShippingMethod | | upload_asset | (single verb) | POST /api/v1/uploads/s3/fetch_signed_url + S3 PUT |

Install — three steps

git clone [email protected]:Art-Storefronts/artstorefronts-mcp.git
cd artstorefronts-mcp
./scripts/install.sh --print-config

That runs nvm use (Node 20.x via .nvmrc), npm ci, npm run build, and then prints a ready-to-paste mcp.json snippet with the absolute path to dist/index.js already filled in.

For local development with hot reload: npm run dev (runs src/index.ts through tsx; no build step).

Auth

v0.2 reads credentials from the same OS keychain entry that asf auth login populates — no env vars, no ASF_TOKEN. Sign in once via the CLI on the workstation, point the MCP at the same host, and it picks up the access + refresh tokens automatically.

# One-time, on the workstation:
npm install -g @asf/cli           # or `npm link` from a local checkout
asf auth login --host artstorefronts.com         # browser opens, you click Allow
# Now the MCP can read the keychain entry; restart the host (Claude Desktop, ...).

Token refresh is automatic: on a 401 the MCP server transparently exchanges the cached refresh_token for a new pair and retries the original request. If the refresh fails, the next tool call returns Not signed in to <host>. Run \asf auth login --host ` first.` — guiding the user through recovery.

Wire it up to your agent

The MCP server takes its target host via the --host flag in the args array. No env block needed.

{
  "mcpServers": {
    "asf": {
      "command": "node",
      "args": [
        "/absolute/path/to/artstorefronts-mcp/dist/index.js",
        "--host", "artstorefronts.com"
      ]
    }
  }
}

For staging or local dev, change just the --host arg:

"args": ["/abs/path/dist/index.js", "--host", "staging.artstorefronts.com"]
"args": ["/abs/path/dist/index.js", "--host", "app.lvh.me:3000"]

--host is optional — omit it and the server defaults to artstorefronts.com. Local-dev shorthand applies HTTP automatically to any host whose authority includes lvh.me, localhost, or 127.0.0.1; everything else gets HTTPS. Pass a full URL (https://...) to override the scheme inference.

| Agent | Config file location | |---|---| | Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json | | Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json | | Cursor | ~/.cursor/mcp.json | | Claude in Chrome | Paste into the extension's MCP picker; it persists locally. | | Claude Code (CLI) | .mcp.json at the repo root, or ~/.claude/mcp.json for a global server. |

Restart the agent after editing the config. The 9 tools should appear in the MCP picker. Try:

"List the websites I own, then create an ART_PRINT product named 'MCP Smoke' on the first one with image https://res.cloudinary.com/decosites/image/upload/v1426724962/image_quality_auditor_test_sml_el2jaa.jpg, then make a gallery called 'MCP Smoke Gallery' and add the product to it."

Smoke test against the local dev stack

# 1. Sign in once via the CLI (one-time per host):
asf auth login --host app.lvh.me:3000

# 2. Verify the MCP can read the keychain + boot:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | node dist/index.js --host app.lvh.me:3000

# 3. Configure Claude Desktop / Cursor / Claude Code to launch the MCP
#    with `--host app.lvh.me:3000`, restart, and try the demo prompt.

For a full end-to-end check, drive it through Claude Desktop with the prompt above and verify in the Rails console that the product + gallery were created and the product is linked to the gallery.

Smoke evidence per release lives under artifacts/asf-mcp/<release>/smoke/ in the artstore-agent workspace.

Architecture

artstorefronts-mcp/
  src/
    index.ts                 # stdio entry; wires Server + tool registry + GraphQLClient
    graphql/
      client.ts              # fetch wrapper with JWT + Mozilla UA (Rack::Attack workaround)
      operations.ts          # one helper per GraphQL field, grouped by domain
    tools/
      types.ts               # ToolDefinition / ToolResult shapes
      registry.ts            # exports the 9 grouped tools
      discover.ts
      manage_website.ts
      manage_store.ts
      manage_product.ts
      manage_gallery.ts
      manage_page.ts
      manage_contacts.ts
      manage_shipping.ts
      upload_asset.ts
  .github/workflows/ci.yml

src/graphql/ deliberately mirrors @asf/cli's src/utils/api.ts + per-domain command shapes so a future @asf/api extraction is a plain git mv. If you find yourself diverging the two clients, that is a signal to extract.

Authorization model

The MCP server adds no new authorization layer. Every request carries the JWT verbatim; the icono_pregame backend enforces the existing website_ownage_policy (site owners see only their owned websites; global admins see all). When the user says "my site filip-cloudinary-regression-testing", the agent picks the right website from discover.websites, and the server-side policy refuses anything outside the JWT's scope.

Roadmap

  • 0.1 (this release): nine grouped tools, stdio transport, env-var auth, smoke against local dev stack.
  • 0.2: optional authenticate tool (if env-var-only auth proves too friction-heavy for end users).
  • 0.3: SSE / Streamable-HTTP transport for hosted multi-tenant deployments.
  • Later: anything that requires a new GraphQL operation belongs in a follow-up icono_pregame PR first — file it there, then add the MCP verb.

References