├── README.md ├── .gitignore ├── packages ├── proxy │ ├── src │ │ ├── types │ │ │ ├── index.ts │ │ │ └── env.ts │ │ ├── validations │ │ │ ├── index.ts │ │ │ └── transport.ts │ │ ├── client │ │ │ ├── validations │ │ │ │ ├── index.ts │ │ │ │ └── config.ts │ │ │ ├── pages │ │ │ │ ├── Tracing │ │ │ │ │ ├── providers │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Table │ │ │ │ │ │ ├── FilterMethod.tsx │ │ │ │ │ │ ├── FilterSession.tsx │ │ │ │ │ │ ├── FilterServer.tsx │ │ │ │ │ │ └── TableDrawer │ │ │ │ │ │ │ └── UpdateRequestDialog │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── Form.tsx │ │ │ │ │ ├── DownloadButton.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Home │ │ │ │ │ └── index.tsx │ │ │ │ ├── Servers │ │ │ │ │ ├── Form │ │ │ │ │ │ └── OptionalFields │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── ServerTable │ │ │ │ │ │ ├── Table │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ └── Content │ │ │ │ │ │ │ │ └── DeleteButton.tsx │ │ │ │ │ │ └── TableDrawer │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── AddServerDialog.tsx │ │ │ │ └── ServerConfigurations │ │ │ │ │ └── index.tsx │ │ │ ├── providers │ │ │ │ ├── index.ts │ │ │ │ ├── config.tsx │ │ │ │ └── shiki.tsx │ │ │ ├── main.tsx │ │ │ ├── components │ │ │ │ ├── AppWrapper.tsx │ │ │ │ ├── ui │ │ │ │ │ ├── skeleton.tsx │ │ │ │ │ ├── spinner.tsx │ │ │ │ │ ├── separator.tsx │ │ │ │ │ ├── label.tsx │ │ │ │ │ ├── input.tsx │ │ │ │ │ └── checkbox.tsx │ │ │ │ ├── FieldErrorMessage.tsx │ │ │ │ ├── NotFound.tsx │ │ │ │ ├── LogoSmall.tsx │ │ │ │ ├── sidebar │ │ │ │ │ └── SidebarItem.tsx │ │ │ │ └── CopyButton.tsx │ │ │ ├── queries │ │ │ │ ├── useStats.ts │ │ │ │ ├── useServersData.ts │ │ │ │ └── useServerData.ts │ │ │ ├── hooks │ │ │ │ └── use-mobile.ts │ │ │ └── lib │ │ │ │ └── eventHandler.ts │ │ ├── routes │ │ │ ├── client.ts │ │ │ ├── stats.ts │ │ │ ├── index.ts │ │ │ └── servers │ │ │ │ ├── [slug].ts │ │ │ │ └── index.ts │ │ ├── cli.ts │ │ └── index.ts │ ├── wrangler.json │ ├── build.mjs │ ├── index.html │ ├── .gitignore │ ├── tsconfig.json │ ├── public │ │ └── muppet.svg │ └── components.json ├── inspector │ ├── src │ │ ├── types │ │ │ ├── index.ts │ │ │ └── env.ts │ │ ├── validations │ │ │ └── index.ts │ │ ├── client │ │ │ ├── types │ │ │ │ ├── index.ts │ │ │ │ └── notifications.ts │ │ │ ├── validations │ │ │ │ ├── index.ts │ │ │ │ └── transport.ts │ │ │ ├── pages │ │ │ │ ├── Explorer │ │ │ │ │ ├── types │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── items.ts │ │ │ │ │ ├── providers │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Render │ │ │ │ │ │ ├── Executor │ │ │ │ │ │ │ ├── constant.ts │ │ │ │ │ │ │ ├── JSONPanel.tsx │ │ │ │ │ │ │ ├── FormWrapper.tsx │ │ │ │ │ │ │ ├── FormResetButton.tsx │ │ │ │ │ │ │ ├── SchemaPanel.tsx │ │ │ │ │ │ │ ├── AnalyseButtonGroup │ │ │ │ │ │ │ │ ├── AnalyseDialog │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── useAnalyse.ts │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── GenerateButtonGroup │ │ │ │ │ │ │ │ └── GenerateDialog │ │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── FormPanel │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ ├── SendButton.tsx │ │ │ │ │ │ │ └── provider.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── CardDescriptionRender.tsx │ │ │ │ │ │ └── SamplingRender │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── History │ │ │ │ │ ├── providers │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── DownloadButton.tsx │ │ │ │ │ └── Table │ │ │ │ │ │ └── TableDrawer │ │ │ │ │ │ └── UpdateRequestDialog │ │ │ │ │ │ └── index.tsx │ │ │ │ ├── MCPScan │ │ │ │ │ ├── providers │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── ScanButton.tsx │ │ │ │ │ └── PageHeader.tsx │ │ │ │ ├── Tracing │ │ │ │ │ ├── providers │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── Table │ │ │ │ │ │ ├── FilterMethod.tsx │ │ │ │ │ │ ├── FilterSession.tsx │ │ │ │ │ │ └── TableDrawer │ │ │ │ │ │ │ └── UpdateRequestDialog │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── DownloadButton.tsx │ │ │ │ ├── Playground │ │ │ │ │ ├── providers │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── type.ts │ │ │ │ │ ├── Chat │ │ │ │ │ │ ├── Thread │ │ │ │ │ │ │ ├── MarkdownText.tsx │ │ │ │ │ │ │ └── TooltipIconButton.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Home │ │ │ │ │ └── index.tsx │ │ │ │ └── Settings │ │ │ │ │ └── Footer.tsx │ │ │ ├── providers │ │ │ │ ├── duck-form │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── field.tsx │ │ │ │ │ ├── blueprint.tsx │ │ │ │ │ └── duckform.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── roots.tsx │ │ │ │ ├── sampling.tsx │ │ │ │ ├── shiki.tsx │ │ │ │ ├── ping-server.tsx │ │ │ │ └── connection │ │ │ │ │ └── index.tsx │ │ │ ├── components │ │ │ │ ├── icons │ │ │ │ │ ├── type.ts │ │ │ │ │ ├── Anthropic.tsx │ │ │ │ │ ├── Groq.tsx │ │ │ │ │ ├── Perplexity.tsx │ │ │ │ │ ├── Inception.tsx │ │ │ │ │ ├── xAI.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── Fireworks.tsx │ │ │ │ │ ├── Cohere.tsx │ │ │ │ │ └── Google.tsx │ │ │ │ ├── fields │ │ │ │ │ ├── constants.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── Textarea.tsx │ │ │ │ │ ├── Number.tsx │ │ │ │ │ ├── Checkbox.tsx │ │ │ │ │ └── Object.tsx │ │ │ │ ├── configurationsDialog │ │ │ │ │ ├── Tabs │ │ │ │ │ │ ├── Connect │ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ │ ├── Fields.tsx │ │ │ │ │ │ │ └── Footer.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── ui │ │ │ │ │ ├── skeleton.tsx │ │ │ │ │ ├── spinner.tsx │ │ │ │ │ ├── textarea.tsx │ │ │ │ │ ├── separator.tsx │ │ │ │ │ ├── label.tsx │ │ │ │ │ ├── input.tsx │ │ │ │ │ ├── checkbox.tsx │ │ │ │ │ ├── badge.tsx │ │ │ │ │ └── popover.tsx │ │ │ │ ├── FormattedDataRender │ │ │ │ │ ├── Audio.tsx │ │ │ │ │ ├── Image.tsx │ │ │ │ │ └── Json.tsx │ │ │ │ ├── FieldErrorMessage.tsx │ │ │ │ ├── DuckFieldNotFound.tsx │ │ │ │ ├── PreferencesDialog │ │ │ │ │ ├── ThemeDialog │ │ │ │ │ │ ├── validation.ts │ │ │ │ │ │ └── GenerateButtonGroup │ │ │ │ │ │ │ ├── GenerateDialog │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── provider.tsx │ │ │ │ │ ├── ItemCard.tsx │ │ │ │ │ ├── ColorModeSetting.tsx │ │ │ │ │ ├── ThemeSettings │ │ │ │ │ │ └── DeleteButton.tsx │ │ │ │ │ └── ToastSetting.tsx │ │ │ │ ├── ConfigFormFields │ │ │ │ │ ├── OptionalFields │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── DuckField.tsx │ │ │ │ ├── NotFound.tsx │ │ │ │ ├── AppProviders.tsx │ │ │ │ ├── highlightMatches.tsx │ │ │ │ ├── Sidebar │ │ │ │ │ ├── PingButton │ │ │ │ │ │ ├── OptionsMenu │ │ │ │ │ │ │ └── Dialog │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ └── SidebarItem.tsx │ │ │ │ ├── LogoSmall.tsx │ │ │ │ ├── ErrorBoundaryRender.tsx │ │ │ │ ├── ConfigForm │ │ │ │ │ └── useConfigForm.ts │ │ │ │ └── CopyButton.tsx │ │ │ ├── hooks │ │ │ │ └── use-mobile.ts │ │ │ ├── lib │ │ │ │ └── eventHandler.ts │ │ │ ├── main.tsx │ │ │ └── App.tsx │ │ ├── routes │ │ │ ├── client.ts │ │ │ ├── proxy │ │ │ │ ├── types.ts │ │ │ │ ├── broadcast.ts │ │ │ │ ├── index.ts │ │ │ │ └── mcpProxy.ts │ │ │ └── tunnel.ts │ │ ├── index.ts │ │ └── cli.ts │ ├── .env.example │ ├── build.mjs │ ├── .gitignore │ ├── index.html │ ├── tsconfig.json │ ├── muppet.config.ts │ ├── public │ │ └── muppet.svg │ ├── wrangler.json │ └── README.md └── shared │ ├── src │ ├── config │ │ ├── index.ts │ │ └── inspector.ts │ ├── types │ │ └── index.ts │ ├── actions │ │ └── index.ts │ ├── validations │ │ ├── index.ts │ │ └── transport.ts │ ├── index.ts │ └── utils │ │ └── index.ts │ ├── tsconfig.json │ └── package.json ├── pnpm-workspace.yaml ├── public └── inspector.png ├── kit ├── src │ ├── tunnel │ │ ├── index.ts │ │ ├── cloudflare.ts │ │ └── ngrok.ts │ ├── index.ts │ ├── cli.ts │ └── commands │ │ └── inspector.ts ├── scripts │ └── build.js ├── tsconfig.json ├── README.md └── package.json ├── .nano-staged.js ├── package.json ├── biome.json └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | ./kit/README.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /packages/proxy/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./env"; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'kit' 3 | - 'packages/*' -------------------------------------------------------------------------------- /packages/inspector/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./env"; 2 | -------------------------------------------------------------------------------- /packages/shared/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./inspector"; 2 | -------------------------------------------------------------------------------- /packages/shared/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./inspector"; 2 | -------------------------------------------------------------------------------- /packages/inspector/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY="" 2 | NGROK_API_KEY="" -------------------------------------------------------------------------------- /packages/inspector/src/validations/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./theme"; 2 | -------------------------------------------------------------------------------- /packages/proxy/src/validations/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./transport"; 2 | -------------------------------------------------------------------------------- /packages/shared/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./inspector"; 2 | -------------------------------------------------------------------------------- /packages/shared/src/validations/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./transport"; 2 | -------------------------------------------------------------------------------- /packages/proxy/src/client/validations/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./config"; 2 | -------------------------------------------------------------------------------- /packages/inspector/src/client/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./notifications"; 2 | -------------------------------------------------------------------------------- /packages/inspector/src/client/validations/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./transport"; 2 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./items"; 2 | -------------------------------------------------------------------------------- /packages/proxy/src/client/pages/Tracing/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./logs"; 2 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/History/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./history"; 2 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/MCPScan/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./scan"; 2 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Tracing/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./logs"; 2 | -------------------------------------------------------------------------------- /public/inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muppet-dev/kit/HEAD/public/inspector.png -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Playground/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./chats"; 2 | -------------------------------------------------------------------------------- /kit/src/tunnel/index.ts: -------------------------------------------------------------------------------- 1 | export { cloudflare } from "./cloudflare"; 2 | export { ngrok } from "./ngrok"; 3 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./item"; 2 | export * from "./tools"; 3 | -------------------------------------------------------------------------------- /packages/inspector/src/client/providers/duck-form/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./blueprint"; 2 | export * from "./duckform"; 3 | export * from "./field"; 4 | -------------------------------------------------------------------------------- /packages/shared/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./config"; 2 | export * from "./types"; 3 | export * from "./utils"; 4 | export * from "./validations"; 5 | -------------------------------------------------------------------------------- /packages/shared/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export const Transport = { 2 | STDIO: "stdio", 3 | SSE: "sse", 4 | HTTP: "streamable-http", 5 | } as const; 6 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/icons/type.ts: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export type IconProps = SVGProps; 4 | -------------------------------------------------------------------------------- /.nano-staged.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "./**/*.{js,jsx,ts,tsx,json}": (api) => 3 | `pnpm dlx @biomejs/biome check --write ${api.filenames.join(" ")}`, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/proxy/src/client/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./preferences"; 2 | export * from "./config"; 3 | export * from "./traces"; 4 | export * from "./shiki"; 5 | -------------------------------------------------------------------------------- /kit/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type InspectorConfig, 3 | defineInspectorConfig, 4 | } from "@muppet-kit/shared"; 5 | 6 | export { defineInspectorConfig, type InspectorConfig }; 7 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/Render/Executor/constant.ts: -------------------------------------------------------------------------------- 1 | export enum RequestTab { 2 | SCHEMA = "schema", 3 | FORM = "form", 4 | JSON = "json", 5 | SCORE = "score", 6 | } 7 | -------------------------------------------------------------------------------- /packages/proxy/src/client/main.tsx: -------------------------------------------------------------------------------- 1 | import { createRoot } from "react-dom/client"; 2 | import App from "./App"; 3 | import "./index.css"; 4 | 5 | createRoot(document.getElementById("root")!).render(); 6 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/fields/constants.ts: -------------------------------------------------------------------------------- 1 | export enum FieldType { 2 | ARRAY = "array", 3 | BOOLEAN = "boolean", 4 | NUMBER = "number", 5 | OBJECT = "object", 6 | STRING = "string", 7 | DEFAULT = "default", 8 | } 9 | -------------------------------------------------------------------------------- /packages/inspector/src/routes/client.ts: -------------------------------------------------------------------------------- 1 | import { serveStatic } from "@hono/node-server/serve-static"; 2 | import { Hono } from "hono"; 3 | 4 | const router = new Hono().use( 5 | serveStatic({ 6 | path: "./index.html", 7 | }), 8 | ); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /packages/proxy/src/routes/client.ts: -------------------------------------------------------------------------------- 1 | import { serveStatic } from "@hono/node-server/serve-static"; 2 | import { Hono } from "hono"; 3 | 4 | const router = new Hono().use( 5 | serveStatic({ 6 | path: "./index.html", 7 | }), 8 | ); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /packages/proxy/src/client/components/AppWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router"; 2 | import { AppSidebar } from "./sidebar"; 3 | 4 | export function AppWrapper() { 5 | return ( 6 | <> 7 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/proxy/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | import pkg from "../package.json" with { type: "json" }; 3 | 4 | program 5 | .name("muppet proxy") 6 | .description("start the MCP Proxy server") 7 | .version(pkg.version) 8 | .action((options) => {}) 9 | .parse(); 10 | -------------------------------------------------------------------------------- /packages/proxy/src/routes/stats.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | 3 | const router = new Hono(); 4 | 5 | router.get("/", (c) => { 6 | return c.json({ 7 | servers: 12, 8 | tools: 100, 9 | prompts: 10, 10 | resources: 0, 11 | }); 12 | }); 13 | 14 | export default router; 15 | -------------------------------------------------------------------------------- /packages/proxy/src/types/env.ts: -------------------------------------------------------------------------------- 1 | import type { configTransportSchema } from "@/validations"; 2 | import type { z } from "zod"; 3 | 4 | export type BaseEnv = { 5 | Variables: { 6 | servers: (z.infer & { 7 | slug: string; 8 | })[]; 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /kit/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | import pkg from "../package.json" with { type: "json" }; 3 | import inspectorCommand from "./commands/inspector.js"; 4 | 5 | program 6 | .name("muppet kit") 7 | .description("devtools for MCPs") 8 | .version(pkg.version) 9 | .addCommand(inspectorCommand) 10 | .parse(); 11 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Playground/type.ts: -------------------------------------------------------------------------------- 1 | import type { ThreadComposerRuntime } from "@assistant-ui/react"; 2 | 3 | export type ChatProps = { 4 | id: string; 5 | model: string; 6 | // Internal 7 | sync?: boolean; 8 | activePrompt?: string; 9 | composer?: ThreadComposerRuntime; 10 | } & Record; 11 | -------------------------------------------------------------------------------- /packages/inspector/src/client/providers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./config"; 2 | export * from "./connection"; 3 | export * from "./duck-form"; 4 | export * from "./notifications"; 5 | export * from "./ping-server"; 6 | export * from "./shiki"; 7 | export * from "./preferences"; 8 | export * from "./traces"; 9 | export * from "./sampling"; 10 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/configurationsDialog/Tabs/Connect/index.tsx: -------------------------------------------------------------------------------- 1 | import { ConfigForm } from "../../../ConfigForm"; 2 | import { FormFields } from "./Fields"; 3 | import { FormFooter } from "./Footer"; 4 | 5 | export function Connect() { 6 | return ( 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "../../lib/utils"; 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 |
10 | ); 11 | } 12 | 13 | export { Skeleton }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@muppet-kit/workspace", 3 | "type": "module", 4 | "scripts": { 5 | "prepare": "is-ci || husky", 6 | "format": "biome check --write ." 7 | }, 8 | "devDependencies": { 9 | "@biomejs/biome": "^1.9.4", 10 | "husky": "^9.1.7", 11 | "is-ci": "^4.1.0", 12 | "nano-staged": "^0.8.0" 13 | }, 14 | "packageManager": "pnpm@10.0.0" 15 | } 16 | -------------------------------------------------------------------------------- /packages/proxy/src/client/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/client/lib/utils"; 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 |
10 | ); 11 | } 12 | 13 | export { Skeleton }; 14 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/FormattedDataRender/Audio.tsx: -------------------------------------------------------------------------------- 1 | import type { AudioContent } from "@modelcontextprotocol/sdk/types.js"; 2 | 3 | export function AudioRender(props: AudioContent) { 4 | const src = `data:${props.mimeType};base64,${props.data}`; 5 | 6 | return ( 7 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/FormattedDataRender/Image.tsx: -------------------------------------------------------------------------------- 1 | import type { ImageContent } from "@modelcontextprotocol/sdk/types.js"; 2 | 3 | export function ImageRender(props: ImageContent) { 4 | const src = `data:${props.mimeType};base64,${props.data}`; 5 | 6 | return ( 7 | Rendered content 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import { Logo } from "../../components/Logo"; 2 | 3 | export function HomePage() { 4 | return ( 5 |
6 | 7 |

8 | MCP Inspector 9 |

10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/proxy/src/client/pages/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import { Logo } from "../../components/Logo"; 2 | 3 | export default function HomePage() { 4 | return ( 5 |
6 | 7 |

8 | MCP Proxy 9 |

10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/proxy/src/validations/transport.ts: -------------------------------------------------------------------------------- 1 | import { 2 | remoteTransportSchema, 3 | stdioTransportSchema, 4 | } from "@muppet-kit/shared"; 5 | import z from "zod"; 6 | 7 | const extraPropValidation = z.object({ 8 | name: z.string().trim(), 9 | }); 10 | 11 | export const configTransportSchema = z.union([ 12 | stdioTransportSchema.merge(extraPropValidation), 13 | remoteTransportSchema.merge(extraPropValidation), 14 | ]); 15 | -------------------------------------------------------------------------------- /packages/proxy/wrangler.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/wrangler/config-schema.json", 3 | "name": "muppet-inspector", 4 | "compatibility_date": "2025-05-19", 5 | "compatibility_flags": ["nodejs_compat"], 6 | "main": "./src/index.ts", 7 | "observability": { 8 | "enabled": true 9 | }, 10 | "assets": { 11 | "directory": "./dist", 12 | "not_found_handling": "single-page-application" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/proxy/src/client/validations/config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | remoteTransportSchema, 3 | stdioTransportSchema, 4 | } from "@muppet-kit/shared"; 5 | import z from "zod"; 6 | 7 | const extraPropValidation = z.object({ 8 | name: z.string().optional(), 9 | }); 10 | 11 | export const configValidation = z.union([ 12 | stdioTransportSchema.merge(extraPropValidation), 13 | remoteTransportSchema.merge(extraPropValidation), 14 | ]); 15 | -------------------------------------------------------------------------------- /packages/proxy/src/client/components/FieldErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorMessage } from "@hookform/error-message"; 2 | 3 | export function FieldErrorMessage(props: { name: string }) { 4 | return ( 5 | ( 8 |

9 | {message} 10 |

11 | )} 12 | /> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/FieldErrorMessage.tsx: -------------------------------------------------------------------------------- 1 | import { ErrorMessage } from "@hookform/error-message"; 2 | 3 | export function FieldErrorMessage(props: { name: string }) { 4 | return ( 5 | ( 8 |

9 | {message} 10 |

11 | )} 12 | /> 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/inspector/src/routes/proxy/types.ts: -------------------------------------------------------------------------------- 1 | import type { EnvWithConfig } from "@/types"; 2 | import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; 3 | import type { Broadcast } from "./broadcast"; 4 | 5 | export type ProxyEnv = { 6 | Variables: EnvWithConfig["Variables"] & { 7 | webAppTransports: Map; 8 | serverTransports: Map; 9 | broadcast: Broadcast; 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/MCPScan/index.tsx: -------------------------------------------------------------------------------- 1 | import { PageHeader } from "./PageHeader"; 2 | import { PageRender } from "./Render"; 3 | import { MCPScanProvider } from "./providers"; 4 | 5 | export default function MCPScanPage() { 6 | return ( 7 | 8 |
9 | 10 | 11 |
12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/inspector/build.mjs: -------------------------------------------------------------------------------- 1 | import { performance } from "node:perf_hooks"; 2 | import esbuild from "esbuild"; 3 | 4 | const start = performance.now(); 5 | 6 | await esbuild.build({ 7 | entryPoints: ["./src/cli.ts"], 8 | banner: { js: "#!/usr/bin/env node" }, 9 | platform: "node", 10 | minify: true, 11 | outfile: "dist/cli.js", 12 | }); 13 | 14 | const end = performance.now(); 15 | 16 | console.log(`Build time: ${((end - start) / 1000).toFixed(2)}s`); 17 | -------------------------------------------------------------------------------- /packages/proxy/build.mjs: -------------------------------------------------------------------------------- 1 | import { performance } from "node:perf_hooks"; 2 | import esbuild from "esbuild"; 3 | 4 | const start = performance.now(); 5 | 6 | await esbuild.build({ 7 | entryPoints: ["./src/cli.ts"], 8 | banner: { js: "#!/usr/bin/env node" }, 9 | platform: "node", 10 | minify: true, 11 | outfile: "dist/cli.js", 12 | }); 13 | 14 | const end = performance.now(); 15 | 16 | console.log(`Build time: ${((end - start) / 1000).toFixed(2)}s`); 17 | -------------------------------------------------------------------------------- /kit/src/tunnel/cloudflare.ts: -------------------------------------------------------------------------------- 1 | import type { TunnelHandler } from "@muppet-kit/shared"; 2 | import { startTunnel } from "untun"; 3 | 4 | export function cloudflare(): TunnelHandler { 5 | return { 6 | generate: async ({ port }) => { 7 | // Creating a new tunnel 8 | const listener = await startTunnel({ 9 | port, 10 | }); 11 | 12 | return { 13 | url: (await listener?.getURL()) ?? null, 14 | }; 15 | }, 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/proxy/src/index.ts: -------------------------------------------------------------------------------- 1 | import { config } from "dotenv"; 2 | import { Hono } from "hono"; 3 | import { cors } from "hono/cors"; 4 | import routes from "./routes"; 5 | 6 | if (import.meta.env.DEV) { 7 | config(); 8 | } 9 | 10 | const app = new Hono().use(cors()); 11 | 12 | app.route("/", routes); 13 | 14 | app.onError((err, c) => { 15 | console.error(`Error on ${c.req.path} router`, err); 16 | return c.json(err, 500); 17 | }); 18 | 19 | export default app; 20 | -------------------------------------------------------------------------------- /packages/inspector/src/index.ts: -------------------------------------------------------------------------------- 1 | import { config } from "dotenv"; 2 | import { Hono } from "hono"; 3 | import { cors } from "hono/cors"; 4 | import routes from "./routes"; 5 | 6 | if (import.meta.env.DEV) { 7 | config(); 8 | } 9 | 10 | const app = new Hono().use(cors()); 11 | 12 | app.route("/", routes); 13 | 14 | app.onError((err, c) => { 15 | console.error(`Error on ${c.req.path} router`, err); 16 | return c.json(err, 500); 17 | }); 18 | 19 | export default app; 20 | -------------------------------------------------------------------------------- /packages/proxy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Muppet Proxy 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/proxy/src/client/pages/Tracing/Table/FilterMethod.tsx: -------------------------------------------------------------------------------- 1 | import { useLogs } from "../providers"; 2 | import { FilterComponent } from "./FilterComponent"; 3 | 4 | export function FilterMethod() { 5 | const { methodFilters, filterData } = useLogs(); 6 | 7 | if (!filterData.methods) return <>; 8 | 9 | return ( 10 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Tracing/Table/FilterMethod.tsx: -------------------------------------------------------------------------------- 1 | import { useLogs } from "../providers"; 2 | import { FilterComponent } from "./FilterComponent"; 3 | 4 | export function FilterMethod() { 5 | const { methodFilters, filterData } = useLogs(); 6 | 7 | if (!filterData.methods) return <>; 8 | 9 | return ( 10 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/proxy/src/client/pages/Tracing/Table/FilterSession.tsx: -------------------------------------------------------------------------------- 1 | import { useLogs } from "../providers"; 2 | import { FilterComponent } from "./FilterComponent"; 3 | 4 | export function FilterSession() { 5 | const { sessionFilters, filterData } = useLogs(); 6 | 7 | if (!filterData.sessions) return <>; 8 | 9 | return ( 10 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Tracing/Table/FilterSession.tsx: -------------------------------------------------------------------------------- 1 | import { useLogs } from "../providers"; 2 | import { FilterComponent } from "./FilterComponent"; 3 | 4 | export function FilterSession() { 5 | const { sessionFilters, filterData } = useLogs(); 6 | 7 | if (!filterData.methods) return <>; 8 | 9 | return ( 10 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/proxy/.gitignore: -------------------------------------------------------------------------------- 1 | # dev 2 | .yarn/ 3 | !.yarn/releases 4 | .vscode/* 5 | !.vscode/launch.json 6 | !.vscode/*.code-snippets 7 | .idea/workspace.xml 8 | .idea/usage.statistics.xml 9 | .idea/shelf 10 | 11 | # deps 12 | node_modules/ 13 | 14 | # env 15 | .env 16 | .env.production 17 | .dev.vars 18 | 19 | # logs 20 | logs/ 21 | *.log 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | pnpm-debug.log* 26 | lerna-debug.log* 27 | 28 | # misc 29 | .DS_Store 30 | 31 | dist-server 32 | 33 | .wrangler -------------------------------------------------------------------------------- /packages/inspector/.gitignore: -------------------------------------------------------------------------------- 1 | # dev 2 | .yarn/ 3 | !.yarn/releases 4 | .vscode/* 5 | !.vscode/launch.json 6 | !.vscode/*.code-snippets 7 | .idea/workspace.xml 8 | .idea/usage.statistics.xml 9 | .idea/shelf 10 | 11 | # deps 12 | node_modules/ 13 | 14 | # env 15 | .env 16 | .env.production 17 | .dev.vars 18 | 19 | # logs 20 | logs/ 21 | *.log 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | pnpm-debug.log* 26 | lerna-debug.log* 27 | 28 | # misc 29 | .DS_Store 30 | 31 | dist-server 32 | 33 | .wrangler -------------------------------------------------------------------------------- /packages/inspector/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Muppet Inspector 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/inspector/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "strict": true, 7 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 8 | "types": ["vite/client", "@cloudflare/workers-types"], 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "react", 11 | "outDir": "./dist", 12 | "baseUrl": ".", 13 | "paths": { 14 | "@/*": ["./src/*"], 15 | "@/client/*": ["./src/client/*"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/proxy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "strict": true, 7 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 8 | "types": ["vite/client", "@cloudflare/workers-types"], 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "react", 11 | "outDir": "./dist", 12 | "baseUrl": ".", 13 | "paths": { 14 | "@/*": ["./src/*"], 15 | "@/client/*": ["./src/client/*"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/DuckFieldNotFound.tsx: -------------------------------------------------------------------------------- 1 | import { useField } from "../providers"; 2 | 3 | export function DuckFieldComponentNotFound() { 4 | const options = useField(); 5 | 6 | if (options.type === "default") 7 | return ( 8 |

9 | Unable to render component with props{" "} 10 | {JSON.stringify(options)} 11 |

12 | ); 13 | 14 | return ( 15 |

16 | Component of type {options.type} doesn't exist! 17 |

18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/Render/index.tsx: -------------------------------------------------------------------------------- 1 | import { Tool, useTool } from "../providers"; 2 | import { GridComponent } from "./GridComponent"; 3 | import { RootsRender } from "./RootsRender"; 4 | import { SamplingRender } from "./SamplingRender"; 5 | 6 | export function ExplorerRender() { 7 | const { activeTool } = useTool(); 8 | 9 | if (activeTool.name === Tool.ROOTS) return ; 10 | if (activeTool.name === Tool.SAMPLING) return ; 11 | 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /packages/inspector/src/types/env.ts: -------------------------------------------------------------------------------- 1 | import type { SanitizedInspectorConfig } from "@muppet-kit/shared"; 2 | import type { LanguageModel } from "ai"; 3 | import type { Logger } from "pino"; 4 | 5 | export type EnvWithConfig = { 6 | Variables: { 7 | logger: Logger; 8 | config: SanitizedInspectorConfig; 9 | }; 10 | }; 11 | 12 | export type EnvWithDefaultModel = { 13 | Variables: EnvWithConfig["Variables"] & { 14 | models: NonNullable; 15 | modelToBeUsed: LanguageModel; 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/proxy/src/client/queries/useStats.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | 3 | export function useStats() { 4 | return useQuery({ 5 | queryKey: ["stats"], 6 | queryFn: () => 7 | fetch("/api/stats").then((res) => { 8 | if (!res.ok) { 9 | throw new Error( 10 | "Failed to fetch stats data. Please check your network connection or try again later.", 11 | ); 12 | } 13 | 14 | return res.json() as Promise>; 15 | }), 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /packages/inspector/muppet.config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used in development and when deploying to Cloudflare. 3 | * You can use it to configure the inspector. 4 | */ 5 | import { createOpenAI } from "@ai-sdk/openai"; 6 | import { defineInspectorConfig } from "@muppet-kit/shared"; 7 | 8 | export default (env: Record) => { 9 | const openai = createOpenAI({ 10 | apiKey: env.OPENAI_API_KEY, 11 | }); 12 | 13 | return defineInspectorConfig({ 14 | models: [openai("gpt-4.1-nano"), openai("gpt-4.1-mini")], 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/index.tsx: -------------------------------------------------------------------------------- 1 | import { SidebarInset } from "../../components/ui/sidebar"; 2 | import { ExplorerRender } from "./Render"; 3 | import { MCPItemProvider, ToolProvider } from "./providers"; 4 | 5 | export default function ExplorerPage() { 6 | return ( 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /packages/proxy/public/muppet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/inspector/public/muppet.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/proxy/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "src/client/index.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/client/components", 15 | "utils": "@/client/lib/utils", 16 | "ui": "@/client/components/ui", 17 | "lib": "@/client/lib", 18 | "hooks": "@/client/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/PreferencesDialog/ThemeDialog/validation.ts: -------------------------------------------------------------------------------- 1 | import { customThemeSchema } from "@/validations"; 2 | import z from "zod"; 3 | 4 | export const colorThemeValidation = z.object({ 5 | name: z.string().optional(), 6 | variables: z.string().refine( 7 | (str) => { 8 | try { 9 | const parsed = JSON.parse(str); 10 | customThemeSchema.parse(parsed); 11 | return true; 12 | } catch { 13 | return false; 14 | } 15 | }, 16 | { 17 | message: "Invalid theme JSON.", 18 | }, 19 | ), 20 | }); 21 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.4.1/schema.json", 3 | "organizeImports": { 4 | "enabled": true 5 | }, 6 | "linter": { 7 | "enabled": true, 8 | "rules": { 9 | "recommended": true, 10 | "style": { 11 | "noNonNullAssertion": "off" 12 | }, 13 | "suspicious": { 14 | "noExplicitAny": "warn" 15 | } 16 | } 17 | }, 18 | "formatter": { 19 | "indentStyle": "space", 20 | "lineWidth": 80, 21 | "lineEnding": "lf" 22 | }, 23 | "vcs": { 24 | "enabled": true, 25 | "clientKind": "git", 26 | "useIgnoreFile": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/proxy/src/client/pages/Servers/Form/OptionalFields/index.tsx: -------------------------------------------------------------------------------- 1 | import type { configValidation } from "@/client/validations"; 2 | import { Transport } from "@muppet-kit/shared"; 3 | import { useFormContext, useWatch } from "react-hook-form"; 4 | import type z from "zod"; 5 | import { SSEFields } from "./SSE"; 6 | import { STDIOFields } from "./STDIO"; 7 | 8 | export function OptionalFields() { 9 | const { control } = useFormContext>(); 10 | const type = useWatch({ control, name: "type" }); 11 | 12 | if (type === Transport.STDIO) return ; 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /packages/proxy/src/client/queries/useServersData.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | 3 | export const serversDataQueryKey = ["api", "servers"]; 4 | 5 | export function useServersData() { 6 | return useQuery({ 7 | queryKey: serversDataQueryKey, 8 | queryFn: () => 9 | fetch("/api/servers").then((res) => { 10 | if (!res.ok) { 11 | throw new Error( 12 | "Failed to fetch servers data. Please check your network connection or try again later.", 13 | ); 14 | } 15 | 16 | return res.json() as Promise[]>; 17 | }), 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/ConfigFormFields/OptionalFields/index.tsx: -------------------------------------------------------------------------------- 1 | import { Transport } from "@muppet-kit/shared"; 2 | import { useFormContext, useWatch } from "react-hook-form"; 3 | import type z from "zod"; 4 | import type { configTransportSchema } from "../../../validations"; 5 | import { SSEFields } from "./SSE"; 6 | import { STDIOFields } from "./STDIO"; 7 | 8 | export function OptionalFields() { 9 | const { control } = useFormContext>(); 10 | const type = useWatch({ control, name: "type" }); 11 | 12 | if (type === Transport.STDIO) return ; 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Playground/Chat/Thread/MarkdownText.tsx: -------------------------------------------------------------------------------- 1 | import { defaultMakdownComponents } from "@/client/components/MakdownComponents"; 2 | import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown"; 3 | import "@assistant-ui/react-markdown/styles/dot.css"; 4 | import { memo } from "react"; 5 | import remarkGfm from "remark-gfm"; 6 | 7 | const MarkdownTextImpl = () => { 8 | return ( 9 | 14 | ); 15 | }; 16 | 17 | export const MarkdownText = memo(MarkdownTextImpl); 18 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/Render/Executor/JSONPanel.tsx: -------------------------------------------------------------------------------- 1 | import { useFormContext, useWatch } from "react-hook-form"; 2 | import { CodeEditor } from "../../../../components/CodeEditor"; 3 | 4 | export function JSONPanel() { 5 | const { control, reset } = useFormContext(); 6 | 7 | const formData = useWatch({ control }); 8 | 9 | const value = formData ? JSON.stringify(formData, null, 2) : undefined; 10 | 11 | return ( 12 | { 15 | const data = value ? JSON.parse(value) : undefined; 16 | reset(data); 17 | }} 18 | /> 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /kit/src/tunnel/ngrok.ts: -------------------------------------------------------------------------------- 1 | import type { TunnelHandler } from "@muppet-kit/shared"; 2 | import { disconnect, forward } from "@ngrok/ngrok"; 3 | 4 | /** 5 | * Ngrok tunnel handler 6 | */ 7 | export function ngrok(options?: { apiKey?: string }): TunnelHandler { 8 | return { 9 | generate: async ({ port }) => { 10 | // Disconnecting all existing tunnels 11 | await disconnect(); 12 | 13 | // Creating a new tunnel 14 | const listener = await forward({ 15 | addr: port, 16 | authtoken: options?.apiKey ?? process.env.NGROK_API_KEY, 17 | }); 18 | 19 | return { 20 | url: listener.url(), 21 | }; 22 | }, 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /packages/proxy/src/client/hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | const MOBILE_BREAKPOINT = 768; 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState( 7 | undefined, 8 | ); 9 | 10 | React.useEffect(() => { 11 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); 12 | const onChange = () => { 13 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 14 | }; 15 | mql.addEventListener("change", onChange); 16 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 17 | return () => mql.removeEventListener("change", onChange); 18 | }, []); 19 | 20 | return !!isMobile; 21 | } 22 | -------------------------------------------------------------------------------- /packages/inspector/src/client/hooks/use-mobile.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | const MOBILE_BREAKPOINT = 768; 4 | 5 | export function useIsMobile() { 6 | const [isMobile, setIsMobile] = React.useState( 7 | undefined, 8 | ); 9 | 10 | React.useEffect(() => { 11 | const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); 12 | const onChange = () => { 13 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 14 | }; 15 | mql.addEventListener("change", onChange); 16 | setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); 17 | return () => mql.removeEventListener("change", onChange); 18 | }, []); 19 | 20 | return !!isMobile; 21 | } 22 | -------------------------------------------------------------------------------- /packages/inspector/wrangler.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/wrangler/config-schema.json", 3 | "name": "muppet-inspector", 4 | "compatibility_date": "2025-05-19", 5 | "compatibility_flags": ["nodejs_compat"], 6 | "main": "./src/index.ts", 7 | "durable_objects": { 8 | "bindings": [ 9 | { 10 | "class_name": "MuppetInspector", 11 | "name": "Inspector" 12 | } 13 | ] 14 | }, 15 | "migrations": [ 16 | { 17 | "tag": "v1", 18 | "new_classes": ["MuppetInspector"] 19 | } 20 | ], 21 | "observability": { 22 | "enabled": true 23 | }, 24 | "assets": { 25 | "directory": "./dist", 26 | "not_found_handling": "single-page-application" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/fields/index.ts: -------------------------------------------------------------------------------- 1 | import { ArrayField } from "./Array"; 2 | import { CheckboxField } from "./Checkbox"; 3 | import { NumberField } from "./Number"; 4 | import { ObjectField } from "./Object"; 5 | import { TextareaField } from "./Textarea"; 6 | import { FieldType } from "./constants"; 7 | 8 | export { FieldWrapper, type FieldWrapperProps } from "./FieldWrapper"; 9 | 10 | export const quackFields: Record React.JSX.Element> = { 11 | [FieldType.ARRAY]: ArrayField, 12 | [FieldType.BOOLEAN]: CheckboxField, 13 | [FieldType.NUMBER]: NumberField, 14 | [FieldType.OBJECT]: ObjectField, 15 | [FieldType.STRING]: TextareaField, 16 | [FieldType.DEFAULT]: TextareaField, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/inspector/src/client/pages/Explorer/Render/Executor/FormWrapper.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren } from "react"; 2 | import { useFormContext } from "react-hook-form"; 3 | import { cn } from "../../../../lib/utils"; 4 | import { useCustomForm } from "./provider"; 5 | 6 | export function FormWrapper(props: PropsWithChildren<{ className?: string }>) { 7 | const { handleSubmit } = useFormContext(); 8 | 9 | const mutation = useCustomForm(); 10 | 11 | return ( 12 |
mutation.mutateAsync(values), 16 | console.error, 17 | )} 18 | className={cn("h-full w-full", props.className)} 19 | > 20 | {props.children} 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/inspector/src/client/types/notifications.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NotificationSchema as BaseNotificationSchema, 3 | ClientNotificationSchema, 4 | ServerNotificationSchema, 5 | } from "@modelcontextprotocol/sdk/types.js"; 6 | import z from "zod"; 7 | 8 | export const StdErrNotificationSchema = BaseNotificationSchema.extend({ 9 | method: z.literal("notifications/stderr"), 10 | params: z.object({ 11 | content: z.string(), 12 | }), 13 | }); 14 | 15 | export const NotificationSchema = ClientNotificationSchema.or( 16 | StdErrNotificationSchema, 17 | ) 18 | .or(ServerNotificationSchema) 19 | .or(BaseNotificationSchema); 20 | 21 | export type StdErrNotification = z.infer; 22 | export type Notification = z.infer; 23 | -------------------------------------------------------------------------------- /packages/proxy/src/client/pages/Tracing/Table/FilterServer.tsx: -------------------------------------------------------------------------------- 1 | import { useSearchParams } from "react-router"; 2 | import { useLogs } from "../providers"; 3 | import { FilterComponent } from "./FilterComponent"; 4 | 5 | export function FilterServer() { 6 | const { serverFilters, filterData } = useLogs(); 7 | const [searchParams] = useSearchParams(); 8 | 9 | const serverID = searchParams.get("server"); 10 | 11 | if (!filterData.servers) return <>; 12 | 13 | const selectedServerFilters = serverID 14 | ? [serverID, ...(serverFilters ?? [])] 15 | : serverFilters; 16 | 17 | return ( 18 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/icons/Anthropic.tsx: -------------------------------------------------------------------------------- 1 | import type { IconProps } from "./type"; 2 | 3 | export function AnthropicIcon({ style, ...props }: IconProps) { 4 | return ( 5 | 14 | Anthropic 15 | 16 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /packages/inspector/src/client/lib/eventHandler.ts: -------------------------------------------------------------------------------- 1 | import type { BaseSyntheticEvent } from "react"; 2 | 3 | export function eventHandler( 4 | func: (event?: BaseSyntheticEvent) => void, 5 | options?: { preventDefault?: boolean }, 6 | ) { 7 | return (event?: BaseSyntheticEvent) => { 8 | if (event != null) { 9 | if ("key" in event && event.key !== "Enter") 10 | // On keyboard, work only when enter is pressed 11 | return; 12 | 13 | if (options?.preventDefault) { 14 | // Prevent the default browser behavior for the event 15 | event.preventDefault(); 16 | // Stop the event from propagating to parent elements 17 | event.stopPropagation(); 18 | } 19 | } 20 | 21 | // Call the function 22 | func(event); 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /packages/shared/src/validations/transport.ts: -------------------------------------------------------------------------------- 1 | import z from "zod"; 2 | import { Transport } from "../utils"; 3 | 4 | export const stdioTransportSchema = z.object({ 5 | type: z.literal(Transport.STDIO), 6 | command: z.string(), 7 | args: z.string().optional(), 8 | env: z 9 | .array( 10 | z.object({ 11 | key: z.string(), 12 | value: z.string(), 13 | }), 14 | ) 15 | .optional(), 16 | }); 17 | 18 | export const remoteTransportSchema = z.object({ 19 | type: z.union([z.literal(Transport.SSE), z.literal(Transport.HTTP)]), 20 | url: z.string().url(), 21 | headerName: z.string().optional(), 22 | bearerToken: z.string().optional(), 23 | }); 24 | 25 | export const transportSchema = z.union([ 26 | stdioTransportSchema, 27 | remoteTransportSchema, 28 | ]); 29 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/ui/spinner.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "../../lib/utils"; 2 | 3 | function Spinner({ 4 | title = "loading", 5 | className, 6 | ...props 7 | }: React.ComponentProps<"svg"> & { title?: string }) { 8 | return ( 9 | 19 | {title} 20 | 25 | 26 | ); 27 | } 28 | 29 | export { Spinner }; 30 | -------------------------------------------------------------------------------- /packages/proxy/src/client/components/ui/spinner.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/client/lib/utils"; 2 | 3 | function Spinner({ 4 | title = "loading", 5 | className, 6 | ...props 7 | }: React.ComponentProps<"svg"> & { title?: string }) { 8 | return ( 9 | 19 | {title} 20 | 25 | 26 | ); 27 | } 28 | 29 | export { Spinner }; 30 | -------------------------------------------------------------------------------- /kit/src/commands/inspector.ts: -------------------------------------------------------------------------------- 1 | import app from "@muppet-kit/inspector"; 2 | import pkg from "@muppet-kit/inspector/package.json" with { type: "json" }; 3 | import { inspectorAction } from "@muppet-kit/shared/actions"; 4 | import { Command, Option } from "commander"; 5 | 6 | const command = new Command("inspector") 7 | .description("start the MCP Inspector") 8 | .version(pkg.version) 9 | .addOption(new Option("-p, --port ", "Port to run the inspector on")) 10 | .addOption(new Option("-h, --host ", "Host to run the inspector on")) 11 | .addOption(new Option("-c, --config ", "Path to the config file")) 12 | .action((options) => 13 | inspectorAction({ 14 | options: { version: pkg.version, ...(options ?? {}) }, 15 | app, 16 | }), 17 | ); 18 | 19 | export default command; 20 | -------------------------------------------------------------------------------- /packages/proxy/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import type { BaseEnv } from "@/types"; 2 | import { Hono } from "hono"; 3 | import pkg from "../../package.json"; 4 | import clientRouter from "./client"; 5 | import serversRouter from "./servers"; 6 | import statsRouter from "./stats"; 7 | 8 | const servers: BaseEnv["Variables"]["servers"] = []; 9 | 10 | const apiRouter = new Hono().use(async (c, next) => { 11 | c.set("servers", servers); 12 | 13 | await next(); 14 | }); 15 | 16 | apiRouter.route("/stats", statsRouter); 17 | apiRouter.route("/servers", serversRouter); 18 | 19 | const router = new Hono(); 20 | 21 | router.route("/api", apiRouter); 22 | router.get("/version", (c) => c.text(pkg.version)); 23 | 24 | if (import.meta.env.DEV) { 25 | router.route("/", clientRouter); 26 | } 27 | 28 | export default router; 29 | -------------------------------------------------------------------------------- /packages/inspector/src/cli.ts: -------------------------------------------------------------------------------- 1 | import { inspectorAction } from "@muppet-kit/shared/actions"; 2 | import { Option, program } from "commander"; 3 | import pkg from "../package.json" with { type: "json" }; 4 | import app from "./index.js"; 5 | 6 | program 7 | .name("muppet inspector") 8 | .description("start the MCP Inspector") 9 | .version(pkg.version) 10 | .addOption(new Option("-p, --port ", "Port to run the inspector on")) 11 | .addOption(new Option("-h, --host ", "Host to run the inspector on")) 12 | .addOption(new Option("-c, --config ", "Path to the config file")) 13 | .action((options) => 14 | inspectorAction({ 15 | options: { version: pkg.version, ...(options ?? {}) }, 16 | // @ts-expect-error The build output is different 17 | app, 18 | }), 19 | ) 20 | .parse(); 21 | -------------------------------------------------------------------------------- /packages/inspector/src/client/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "../../lib/utils"; 2 | 3 | function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { 4 | return ( 5 |