Skip to content

aiperceivable/mcp-embedded-ui-typescript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

8 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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

About

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

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors