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

mcp-embedded-ui

v0.3.0

Published

A lightweight, minimal-dependency embedded Web UI for any MCP Server.

Readme

mcp-embedded-ui (TypeScript)

The TypeScript implementation of mcp-embedded-ui — a browser-based tool explorer for any MCP (Model Context Protocol) server.

What is this?

If you build an MCP server in TypeScript/JavaScript, your users interact with tools through raw JSON — no visual feedback, no schema browser, no quick way to test. This library adds a full browser UI to your server with zero dependencies and one function call.

┌───────────────────────────────────┐
│  Browser                          │
│  Tool list → Schema → Try it      │
└──────────────┬────────────────────┘
               │ HTTP / JSON
┌──────────────▼────────────────────┐
│  Your TypeScript MCP Server       │
│  + mcp-embedded-ui                │
│    (Node / Bun / Deno / Hono)     │
└───────────────────────────────────┘

What does the UI provide?

  • Tool list — browse all registered tools with descriptions and annotation badges
  • Schema inspector — expand any tool to view its full JSON Schema (inputSchema)
  • Try-it console — type JSON arguments, execute the tool, see results instantly
  • cURL export — copy a ready-made cURL command for any execution
  • Auth support — enter a Bearer token in the UI, sent with all requests

No build step. No CDN. No external dependencies. The entire UI is a single self-contained HTML page embedded in the package.

Install

npm install mcp-embedded-ui

Requires Node.js 18+ (or Bun/Deno with Web API support). Zero runtime dependencies.

Quick Start

Web Fetch API (Bun, Deno, Hono, Cloudflare Workers)

import { createHandler } from "mcp-embedded-ui";

const handler = createHandler(tools, handleCall, { title: "My Explorer", allowExecute: true });

// Use with any framework that supports Request/Response:
// Bun.serve({ fetch: (req) => handler(req, "/explorer") });
// Deno.serve((req) => handler(req, "/explorer"));

Node.js http

import http from "node:http";
import { createNodeHandler } from "mcp-embedded-ui";

const handle = createNodeHandler(tools, handleCall, {
  prefix: "/explorer",
  title: "My Explorer",
  allowExecute: true,
});

http.createServer(handle).listen(8000);
// Visit http://localhost:8000/explorer

Full working example

import http from "node:http";
import { createNodeHandler } from "mcp-embedded-ui";
import type { Tool, ToolCallHandler } from "mcp-embedded-ui";

// 1. Define your tools
const tools: Tool[] = [
  {
    name: "greet",
    description: "Say hello",
    inputSchema: {
      type: "object",
      properties: { name: { type: "string" } },
    },
  },
];

// 2. Define a handler: (name, args) -> [content, isError, traceId?]
const handleCall: ToolCallHandler = async (name, args) => {
  if (name === "greet") {
    return [
      [{ type: "text", text: `Hello, ${args.name ?? "world"}!` }],
      false,
      undefined,
    ];
  }
  return [[{ type: "text", text: `Unknown tool: ${name}` }], true, undefined];
};

// 3. Create and start the server
const handle = createNodeHandler(tools, handleCall, { prefix: "/explorer", allowExecute: true });
http.createServer(handle).listen(8000);

With auth hook

import type { AuthHook } from "mcp-embedded-ui";

const authHook: AuthHook = async (req, next) => {
  const token = req.headers["authorization"] ?? "";
  if (typeof token !== "string" || !token.startsWith("Bearer ")) {
    throw new Error("Unauthorized");
  }
  // Verify the token with your own logic (JWT, API key, session, etc.)
  return next();
};

// Pass authHook to enable, omit to disable
const handle = createNodeHandler(tools, handleCall, {
  prefix: "/explorer",
  allowExecute: true,
  authHook,
});

Auth only guards POST /tools/{name}/call. Discovery endpoints are always public. The UI has a built-in token input field — enter your Bearer token there and it's sent with every execution request.

The included demo (examples/node-demo.ts) uses a hardcoded Bearer demo-secret-token — the token is printed at startup so you know what to paste into the UI.

Dynamic tools

// Sync function — re-evaluated on every request
function getTools(): Tool[] {
  return registry.listTools();
}

// Async function
async function getTools(): Promise<Tool[]> {
  return await registry.asyncListTools();
}

const handler = createHandler(getTools, handleCall, { allowExecute: true });

API

Three-tier API

| Function | Returns | Use case | |----------|---------|----------| | createHandler(tools, handleCall, config?) | (req: Request, prefix?) => Promise<Response> | Bun, Deno, Hono, Cloudflare Workers | | createNodeHandler(tools, handleCall, config?) | (req, res) => void | Node.js http.createServer | | buildUIRoutes(tools, handleCall, config?) | Route[] | Power users — fine-grained route control |

Parameters

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | tools | Tool[] \| () => Tool[] \| () => Promise<Tool[]> | required | MCP Tool objects | | handleCall | ToolCallHandler | required | async (name, args) => [content, isError, traceId?] | | allowExecute | boolean | false | Enable/disable tool execution (enforced server-side) | | authHook | AuthHook | — | Middleware: (req, next) => Promise<Response> | | title | string | "MCP Tool Explorer" | Page title (HTML-escaped automatically) | | projectName | string | — | Project name shown in footer | | projectUrl | string | — | Project URL linked in footer (requires projectName) |

Auth Hook

The authHook is a middleware function that receives the request and a next function. Throw to reject with 401. The error response is always {"error": "Unauthorized"} — internal details are never leaked.

const authHook: AuthHook = async (req, next) => {
  const token = req.headers["authorization"];
  if (!token || !isValid(token)) {
    throw new Error("Bad token");
  }
  return next();
};

Auth only guards POST /tools/{name}/call. Discovery endpoints (GET /tools, GET /tools/{name}) are always public.

Endpoints

| Method | Path | Description | |--------|------|-------------| | GET | / | Self-contained HTML explorer page | | GET | /tools | Summary list of all tools | | GET | /tools/{name} | Full tool detail with inputSchema | | POST | /tools/{name}/call | Execute a tool, returns MCP CallToolResult |

Development

# Install dependencies
npm install

# Type check
npx tsc --noEmit

# Run tests
npx vitest run

# Run the demo (auth enabled with a demo token)
npx tsx examples/node-demo.ts
# Visit http://localhost:8000/explorer
# Paste "Bearer demo-secret-token" in the UI's token field to execute tools

Cross-Language Specification

This package implements the mcp-embedded-ui specification. The spec repo contains:

  • PROTOCOL.md — endpoint spec, data shapes, security checklist
  • explorer.html — shared HTML template (identical across all language implementations)
  • Feature specs — detailed requirements and test criteria

License

Apache-2.0