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

webmcp-next

v0.1.0

Published

Turn your Next.js app into an AI-agent-ready website — auto-discover routes and server actions as WebMCP tools and resources

Downloads

103

Readme

webmcp-next

Turn your Next.js app into an AI-agent-ready website in under a minute. Your existing API routes and server actions automatically become WebMCP tools and resources — zero refactoring, no extra backend.

npm install webmcp-next

How to use it

Three steps:

1. Wrap your Next.js config:

// next.config.ts
import { withWebMCP } from "webmcp-next";
export default withWebMCP()(nextConfig);

2. Add the manifest route:

// app/api/mcp/manifest/route.ts
export { GET, POST } from "webmcp-next";

3. Drop in the script component:

// app/layout.tsx
import { WebMCPScript } from "webmcp-next";

export default function Layout({ children }) {
  return (
    <html>
      <body>
        {children}
        <WebMCPScript />
      </body>
    </html>
  );
}

That's it. Your API routes and server actions are automatically exposed to AI agents via navigator.modelContext — POST handlers and "use server" exports become tools, GET handlers become resources.

Controlling what's exposed

Since everything is auto-discovered, use path filtering to control visibility:

// Only expose specific paths
export default withWebMCP({
  paths: ["/api/cart/**", "/api/products/**"],
})(nextConfig);
// Exclude specific paths
export default withWebMCP({
  exclude: ["/api/internal/**", "/api/admin/**"],
})(nextConfig);

By default, all routes and server actions are exposed. The /api/mcp/** path is always excluded automatically.

Adding descriptions and schemas

Routes and actions work out of the box, but you can add metadata to help agents understand what they do.

API route tools (POST handlers)

POST handlers are auto-discovered as tools. Add .tool for a description and input schema:

// app/api/products/search/route.ts
import { z } from "zod";

export async function POST(req: Request) {
  const { query } = await req.json();
  const results = await db.products.search(query);
  return Response.json({ results });
}

POST.tool = {
  description: "Search products by keyword",
  inputSchema: z.object({
    query: z.string().describe("Search keyword"),
  }),
};

API route resources (GET handlers)

GET handlers are auto-discovered as resources. Add .resource for a description:

// app/api/products/route.ts
export async function GET() {
  return Response.json(await db.products.findMany());
}

GET.resource = { description: "Full product catalog" };

Dynamic route segments become MCP resource template URIs automatically:

// app/api/products/[category]/route.ts → MCP URI: products/{category}
export async function GET(req, { params }) {
  const { category } = await params;
  return Response.json(await db.products.findMany({ category }));
}

GET.resource = { description: "Products filtered by category" };

Server actions

All exported functions in "use server" files are auto-discovered as tools. Add .tool for a description and input schema:

// app/actions/cart.ts
"use server";
import { z } from "zod";

// Works without .tool — auto-discovered by name
export async function clearCart() {
  db.cart.clear();
}

// .tool adds a description and input schema to help agents
export async function removeFromCart(formData: FormData) {
  const productId = formData.get("productId") as string;
  db.cart.remove(productId);
}

removeFromCart.tool = {
  description: "Remove a product from the shopping cart",
  inputSchema: z.object({
    productId: z.string().describe("The product ID to remove"),
  }),
};

Server actions (next-safe-action)

If you use next-safe-action, you can reuse your existing input schemas. Wrap your client with withMCP and use the mcp() middleware to add descriptions. The input schema is captured automatically from the chain:

// app/lib/safe-action.ts
import { createSafeActionClient } from "next-safe-action";
import { withMCP } from "webmcp-next";

export const actionClient = withMCP(createSafeActionClient());
// app/actions/cart.ts
"use server";
import { z } from "zod";
import { mcp } from "webmcp-next";
import { actionClient } from "../lib/safe-action";

export const addToCart = actionClient
  .use(mcp({ description: "Add a product to the shopping cart" }))
  .inputSchema(z.object({
    productId: z.string().describe("The product ID to add"),
    quantity: z.number().describe("Number of items to add"),
  }))
  .action(async ({ parsedInput: { productId, quantity } }) => {
    db.cart.add(productId, quantity);
  });

Server component resources

Server components can expose their data as MCP resources via .resource with a data function:

// app/components/CartServer.tsx
export const cartData = () => {
  const items = db.cart.items();
  return { items, total: items.reduce((s, i) => s + i.subtotal, 0) };
};

export const CartServer = () => {
  const { items, total } = cartData();
  return <div>...</div>;
};

CartServer.resource = {
  description: "Current shopping cart contents",
  data: cartData,
};

The same data function powers both the UI and the agent.

How it works

Your Next.js App
+------------------------------------------+
|                                          |
|  API routes (auto-discovered)            |
|    POST handlers ──────────┐  + optional |
|    GET handlers             │  .tool /   |
|                             │  .resource |
|  Server actions             │  metadata  |
|    "use server" files ──────┤            |
|                             │  Scanner   |
|  Server components          │  reads     |
|    .resource = { data } ───┘  source     |
|                                files     |
|              ┌───────────────────┐       |
|              │  MCP Manifest     │       |
|              │  /api/mcp/manifest│       |
|              └────────┬──────────┘       |
|                       │                  |
|  <WebMCPScript /> ────┘                  |
|    fetches manifest, registers with      |
|    navigator.modelContext                 |
+------------------------------------------+
         │                    │
         v                    v
   Browser UI           AI Agent (WebMCP)
   (same code)          (same code)

The withWebMCP() plugin scans your source files at build time. Everything is auto-discovered: POST handlers and all "use server" exports become tools, GET handlers become resources. Optional .tool and .resource metadata adds descriptions and input schemas. Component resources need .resource with a data function. The <WebMCPScript /> component fetches the manifest and registers everything with navigator.modelContext.

Input schemas

Tools accept either Zod schemas or plain JSON Schema:

// Zod (recommended — .describe() becomes the field description)
POST.tool = {
  description: "Add to cart",
  inputSchema: z.object({
    productId: z.string().describe("The product ID"),
    quantity: z.number().describe("How many to add"),
  }),
};

// JSON Schema (works too)
POST.tool = {
  description: "Add to cart",
  inputSchema: {
    type: "object",
    properties: {
      productId: { type: "string", description: "The product ID" },
      quantity: { type: "number", description: "How many to add" },
    },
    required: ["productId", "quantity"],
  },
};

/.well-known/mcp

The plugin automatically rewrites /.well-known/mcp to /api/mcp/manifest, so server-side MCP clients can discover your tools and resources at the standard location.

API

withWebMCP(config?)

Wraps your Next.js config. Scans for tools/resources, generates action imports, adds the /.well-known/mcp rewrite.

| Option | Type | Description | |--------|------|-------------| | paths | string[] | Glob patterns to include (default: all) | | exclude | string[] | Glob patterns to exclude (default: none) |

GET / POST

Ready-made route handlers for the manifest endpoint. GET returns the manifest, POST executes server actions by name.

<WebMCPScript manifestPath? />

Client component that fetches the manifest and registers all tools/resources with navigator.modelContext. Defaults to /api/mcp/manifest.

withMCP(client)

Wraps a next-safe-action client to automatically capture input schemas from builder chains.

mcp(meta)

Passthrough middleware for next-safe-action's .use() that carries MCP metadata (description) through the chain.

Browser support

WebMCP (navigator.modelContext) is available in Chrome Canary behind the "Prompt API" flag. The <WebMCPScript /> component polls for the API, so it works with polyfills too.

License

MIT