diff --git a/.gitignore b/.gitignore index 0f4928e..3f9579f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ node_modules server/build client/dist +client/coverage client/tsconfig.app.tsbuildinfo client/tsconfig.node.tsbuildinfo diff --git a/client/coverage/base.css b/client/coverage/base.css deleted file mode 100644 index f418035..0000000 --- a/client/coverage/base.css +++ /dev/null @@ -1,224 +0,0 @@ -body, html { - margin:0; padding: 0; - height: 100%; -} -body { - font-family: Helvetica Neue, Helvetica, Arial; - font-size: 14px; - color:#333; -} -.small { font-size: 12px; } -*, *:after, *:before { - -webkit-box-sizing:border-box; - -moz-box-sizing:border-box; - box-sizing:border-box; - } -h1 { font-size: 20px; margin: 0;} -h2 { font-size: 14px; } -pre { - font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; - margin: 0; - padding: 0; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; -} -a { color:#0074D9; text-decoration:none; } -a:hover { text-decoration:underline; } -.strong { font-weight: bold; } -.space-top1 { padding: 10px 0 0 0; } -.pad2y { padding: 20px 0; } -.pad1y { padding: 10px 0; } -.pad2x { padding: 0 20px; } -.pad2 { padding: 20px; } -.pad1 { padding: 10px; } -.space-left2 { padding-left:55px; } -.space-right2 { padding-right:20px; } -.center { text-align:center; } -.clearfix { display:block; } -.clearfix:after { - content:''; - display:block; - height:0; - clear:both; - visibility:hidden; - } -.fl { float: left; } -@media only screen and (max-width:640px) { - .col3 { width:100%; max-width:100%; } - .hide-mobile { display:none!important; } -} - -.quiet { - color: #7f7f7f; - color: rgba(0,0,0,0.5); -} -.quiet a { opacity: 0.7; } - -.fraction { - font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; - font-size: 10px; - color: #555; - background: #E8E8E8; - padding: 4px 5px; - border-radius: 3px; - vertical-align: middle; -} - -div.path a:link, div.path a:visited { color: #333; } -table.coverage { - border-collapse: collapse; - margin: 10px 0 0 0; - padding: 0; -} - -table.coverage td { - margin: 0; - padding: 0; - vertical-align: top; -} -table.coverage td.line-count { - text-align: right; - padding: 0 5px 0 20px; -} -table.coverage td.line-coverage { - text-align: right; - padding-right: 10px; - min-width:20px; -} - -table.coverage td span.cline-any { - display: inline-block; - padding: 0 5px; - width: 100%; -} -.missing-if-branch { - display: inline-block; - margin-right: 5px; - border-radius: 3px; - position: relative; - padding: 0 4px; - background: #333; - color: yellow; -} - -.skip-if-branch { - display: none; - margin-right: 10px; - position: relative; - padding: 0 4px; - background: #ccc; - color: white; -} -.missing-if-branch .typ, .skip-if-branch .typ { - color: inherit !important; -} -.coverage-summary { - border-collapse: collapse; - width: 100%; -} -.coverage-summary tr { border-bottom: 1px solid #bbb; } -.keyline-all { border: 1px solid #ddd; } -.coverage-summary td, .coverage-summary th { padding: 10px; } -.coverage-summary tbody { border: 1px solid #bbb; } -.coverage-summary td { border-right: 1px solid #bbb; } -.coverage-summary td:last-child { border-right: none; } -.coverage-summary th { - text-align: left; - font-weight: normal; - white-space: nowrap; -} -.coverage-summary th.file { border-right: none !important; } -.coverage-summary th.pct { } -.coverage-summary th.pic, -.coverage-summary th.abs, -.coverage-summary td.pct, -.coverage-summary td.abs { text-align: right; } -.coverage-summary td.file { white-space: nowrap; } -.coverage-summary td.pic { min-width: 120px !important; } -.coverage-summary tfoot td { } - -.coverage-summary .sorter { - height: 10px; - width: 7px; - display: inline-block; - margin-left: 0.5em; - background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; -} -.coverage-summary .sorted .sorter { - background-position: 0 -20px; -} -.coverage-summary .sorted-desc .sorter { - background-position: 0 -10px; -} -.status-line { height: 10px; } -/* yellow */ -.cbranch-no { background: yellow !important; color: #111; } -/* dark red */ -.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } -.low .chart { border:1px solid #C21F39 } -.highlighted, -.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ - background: #C21F39 !important; -} -/* medium red */ -.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } -/* light red */ -.low, .cline-no { background:#FCE1E5 } -/* light green */ -.high, .cline-yes { background:rgb(230,245,208) } -/* medium green */ -.cstat-yes { background:rgb(161,215,106) } -/* dark green */ -.status-line.high, .high .cover-fill { background:rgb(77,146,33) } -.high .chart { border:1px solid rgb(77,146,33) } -/* dark yellow (gold) */ -.status-line.medium, .medium .cover-fill { background: #f9cd0b; } -.medium .chart { border:1px solid #f9cd0b; } -/* light yellow */ -.medium { background: #fff4c2; } - -.cstat-skip { background: #ddd; color: #111; } -.fstat-skip { background: #ddd; color: #111 !important; } -.cbranch-skip { background: #ddd !important; color: #111; } - -span.cline-neutral { background: #eaeaea; } - -.coverage-summary td.empty { - opacity: .5; - padding-top: 4px; - padding-bottom: 4px; - line-height: 1; - color: #888; -} - -.cover-fill, .cover-empty { - display:inline-block; - height: 12px; -} -.chart { - line-height: 0; -} -.cover-empty { - background: white; -} -.cover-full { - border-right: none !important; -} -pre.prettyprint { - border: none !important; - padding: 0 !important; - margin: 0 !important; -} -.com { color: #999 !important; } -.ignore-none { color: #999; font-weight: normal; } - -.wrapper { - min-height: 100%; - height: auto !important; - height: 100%; - margin: 0 auto -48px; -} -.footer, .push { - height: 48px; -} diff --git a/client/coverage/block-navigation.js b/client/coverage/block-navigation.js deleted file mode 100644 index cc12130..0000000 --- a/client/coverage/block-navigation.js +++ /dev/null @@ -1,87 +0,0 @@ -/* eslint-disable */ -var jumpToCode = (function init() { - // Classes of code we would like to highlight in the file view - var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; - - // Elements to highlight in the file listing view - var fileListingElements = ['td.pct.low']; - - // We don't want to select elements that are direct descendants of another match - var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` - - // Selecter that finds elements on the page to which we can jump - var selector = - fileListingElements.join(', ') + - ', ' + - notSelector + - missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` - - // The NodeList of matching elements - var missingCoverageElements = document.querySelectorAll(selector); - - var currentIndex; - - function toggleClass(index) { - missingCoverageElements - .item(currentIndex) - .classList.remove('highlighted'); - missingCoverageElements.item(index).classList.add('highlighted'); - } - - function makeCurrent(index) { - toggleClass(index); - currentIndex = index; - missingCoverageElements.item(index).scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center' - }); - } - - function goToPrevious() { - var nextIndex = 0; - if (typeof currentIndex !== 'number' || currentIndex === 0) { - nextIndex = missingCoverageElements.length - 1; - } else if (missingCoverageElements.length > 1) { - nextIndex = currentIndex - 1; - } - - makeCurrent(nextIndex); - } - - function goToNext() { - var nextIndex = 0; - - if ( - typeof currentIndex === 'number' && - currentIndex < missingCoverageElements.length - 1 - ) { - nextIndex = currentIndex + 1; - } - - makeCurrent(nextIndex); - } - - return function jump(event) { - if ( - document.getElementById('fileSearch') === document.activeElement && - document.activeElement != null - ) { - // if we're currently focused on the search input, we don't want to navigate - return; - } - - switch (event.which) { - case 78: // n - case 74: // j - goToNext(); - break; - case 66: // b - case 75: // k - case 80: // p - goToPrevious(); - break; - } - }; -})(); -window.addEventListener('keydown', jumpToCode); diff --git a/client/coverage/client/bin/cli.js.html b/client/coverage/client/bin/cli.js.html deleted file mode 100644 index 29aaeb3..0000000 --- a/client/coverage/client/bin/cli.js.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - -
-- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 | - - - - - - - - - - - - - - - - | #!/usr/bin/env node - -import { join, dirname } from "path"; -import { fileURLToPath } from "url"; -import handler from "serve-handler"; -import http from "http"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const distPath = join(__dirname, "../dist"); - -const server = http.createServer((request, response) => { - return handler(request, response, { public: distPath }); -}); - -const port = process.env.PORT || 5173; -server.listen(port, () => {}); - |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| cli.js | -
-
- |
- 0% | -0/16 | -0% | -0/1 | -0% | -0/1 | -0% | -0/16 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| postcss.config.js | -
-
- |
- 0% | -0/6 | -0% | -0/1 | -0% | -0/1 | -0% | -0/6 | -
| tailwind.config.js | -
-
- |
- 0% | -0/58 | -0% | -0/1 | -0% | -0/1 | -0% | -0/58 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 | - - - - - - | export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; - |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 -270 -271 -272 -273 -274 -275 -276 -277 -278 -279 -280 -281 -282 -283 -284 -285 -286 -287 -288 -289 -290 -291 -292 -293 -294 -295 -296 -297 -298 -299 -300 -301 -302 -303 -304 -305 -306 -307 -308 -309 -310 -311 -312 -313 -314 -315 -316 -317 -318 -319 -320 -321 -322 -323 -324 -325 -326 -327 -328 -329 -330 -331 -332 -333 -334 -335 -336 -337 -338 -339 -340 -341 -342 -343 -344 -345 -346 -347 -348 -349 -350 -351 -352 -353 -354 -355 -356 -357 -358 -359 -360 -361 -362 -363 -364 -365 -366 -367 -368 -369 -370 -371 -372 -373 -374 -375 -376 -377 -378 -379 -380 -381 -382 -383 -384 -385 -386 -387 -388 -389 -390 -391 -392 -393 -394 -395 -396 -397 -398 -399 -400 -401 -402 -403 -404 -405 -406 -407 -408 -409 -410 -411 -412 -413 -414 -415 -416 -417 -418 -419 -420 -421 -422 -423 -424 -425 -426 -427 -428 -429 -430 -431 -432 -433 -434 -435 -436 -437 -438 -439 -440 -441 -442 -443 -444 -445 -446 -447 -448 -449 -450 -451 -452 -453 -454 -455 -456 -457 -458 -459 -460 -461 -462 -463 -464 -465 -466 -467 -468 -469 -470 -471 -472 -473 -474 -475 -476 -477 -478 -479 -480 -481 -482 -483 -484 -485 -486 -487 -488 -489 -490 -491 -492 -493 -494 -495 -496 -497 -498 -499 -500 -501 -502 -503 -504 -505 -506 -507 -508 -509 -510 -511 -512 -513 -514 -515 -516 -517 -518 -519 -520 -521 -522 -523 -524 -525 -526 -527 -528 -529 -530 -531 -532 -533 -534 -535 -536 -537 | 1x -1x - - - - - - - - - - - - - - - - - -1x -1x - - - -1x - - - - - - - -1x - - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x - -1x -16x -16x - -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -7x -16x -16x -7x -16x - -16x -16x -16x -16x - -16x -16x -16x - -16x - - - - - - -16x -16x -16x - -16x - - - - - - - -16x - - - - - - - -16x -16x -16x -16x -16x -16x - -16x -16x - -16x -16x - -16x -16x -16x - -16x -16x -16x -16x - -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x - - -16x - - -16x -1x -1x -1x -1x -1x -16x -16x - -16x - - - - - - - - - - - - - - - - - - - - - - -16x -10x -16x - -16x -8x -16x - -16x -7x -7x -7x -7x -7x -7x -7x -7x -7x -7x -7x -7x - -7x -16x - -16x -7x -16x - -16x -7x -7x -7x -16x - -16x - - - -16x - - - - - - - - - - - - -16x - - - - - - - - - - - - - - - - -16x - - - - - - - - - - - -16x - - - - - - - - - - - - -16x - - - - - - - - - - - -16x - - - - - - - - - - - - -16x - - - - - - - - - - - - - - - - - -16x - - - -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -9x -9x -9x - -9x -7x -5x -5x - -9x -9x - -9x -9x -9x - -9x -9x -9x - -9x -9x -9x - -9x -9x -9x - -9x -9x -9x - -9x -1x -1x -1x - -9x -9x -9x - -9x -9x - -9x -9x -5x -5x - -5x -5x - -4x -4x -4x -4x -4x - - - -4x - - - -4x - - - -4x - - - -4x - - - -4x -4x - - - -4x -4x -4x -4x -4x -4x -4x -4x - - - -4x - - - -4x - - - -4x -4x - - - -4x -4x -4x -4x -4x -4x -4x - - - -4x - - - -4x - - - -4x -4x - - - - -4x -4x -4x -4x -4x -4x -4x - - - - - - - -4x -4x -4x -4x -4x -4x -4x -4x -4x -4x -4x -4x - -9x -9x - -7x -7x - -7x -7x - -16x -16x -16x -16x -16x -16x - -16x -16x -16x - -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x - -16x - -1x - | import { useDraggablePane } from "./lib/hooks/useDraggablePane";
-import { useConnection } from "./lib/hooks/useConnection";
-import {
- ClientRequest,
- CompatibilityCallToolResult,
- CompatibilityCallToolResultSchema,
- CreateMessageResult,
- EmptyResultSchema,
- GetPromptResultSchema,
- ListPromptsResultSchema,
- ListResourcesResultSchema,
- ListResourceTemplatesResultSchema,
- ReadResourceResultSchema,
- ListToolsResultSchema,
- Resource,
- ResourceTemplate,
- Root,
- ServerNotification,
- Tool
-} from "@modelcontextprotocol/sdk/types.js";
-import { useEffect, useRef, useState } from "react";
-
-import { StdErrNotification } from "./lib/notificationTypes";
-
-import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
-import {
- Bell,
- Files,
- FolderTree,
- Hammer,
- Hash,
- MessageSquare,
-} from "lucide-react";
-
-import { z } from "zod";
-import "./App.css";
-import ConsoleTab from "./components/ConsoleTab";
-import HistoryAndNotifications from "./components/History";
-import PingTab from "./components/PingTab";
-import PromptsTab, { Prompt } from "./components/PromptsTab";
-import ResourcesTab from "./components/ResourcesTab";
-import RootsTab from "./components/RootsTab";
-import SamplingTab, { PendingRequest } from "./components/SamplingTab";
-import Sidebar from "./components/Sidebar";
-import ToolsTab from "./components/ToolsTab";
-
-const params = new URLSearchParams(window.location.search);
-const PROXY_PORT = params.get("proxyPort") ?? "3000";
-const PROXY_SERVER_URL = `http://localhost:${PROXY_PORT}`;
-
-const App = () => {
- const [resources, setResources] = useState<Resource[]>([]);
- const [resourceTemplates, setResourceTemplates] = useState<
- ResourceTemplate[]
- >([]);
- const [resourceContent, setResourceContent] = useState<string>("");
- const [prompts, setPrompts] = useState<Prompt[]>([]);
- const [promptContent, setPromptContent] = useState<string>("");
- const [tools, setTools] = useState<Tool[]>([]);
- const [toolResult, setToolResult] =
- useState<CompatibilityCallToolResult | null>(null);
- const [errors, setErrors] = useState<Record<string, string | null>>({
- resources: null,
- prompts: null,
- tools: null,
- });
- const [command, setCommand] = useState<string>(() => {
- return localStorage.getItem("lastCommand") || "mcp-server-everything";
- });
- const [args, setArgs] = useState<string>(() => {
- return localStorage.getItem("lastArgs") || "";
- });
-
- const [sseUrl, setSseUrl] = useState<string>("http://localhost:3001/sse");
- const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio");
- const [notifications, setNotifications] = useState<ServerNotification[]>([]);
- const [stdErrNotifications, setStdErrNotifications] = useState<
- StdErrNotification[]
- >([]);
- const [roots, setRoots] = useState<Root[]>([]);
- const [env, setEnv] = useState<Record<string, string>>({});
-
- const [pendingSampleRequests, setPendingSampleRequests] = useState<
- Array<
- PendingRequest & {
- resolve: (result: CreateMessageResult) => void;
- reject: (error: Error) => void;
- }
- >
- >([]);
- const nextRequestId = useRef(0);
- const rootsRef = useRef<Root[]>([]);
-
- const handleApproveSampling = (id: number, result: CreateMessageResult) => {
- setPendingSampleRequests((prev) => {
- const request = prev.find((r) => r.id === id);
- request?.resolve(result);
- return prev.filter((r) => r.id !== id);
- });
- };
-
- const handleRejectSampling = (id: number) => {
- setPendingSampleRequests((prev) => {
- const request = prev.find((r) => r.id === id);
- request?.reject(new Error("Sampling request rejected"));
- return prev.filter((r) => r.id !== id);
- });
- };
-
- const [selectedResource, setSelectedResource] = useState<Resource | null>(
- null,
- );
- const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
- const [selectedTool, setSelectedTool] = useState<Tool | null>(null);
- const [nextResourceCursor, setNextResourceCursor] = useState<
- string | undefined
- >();
- const [nextResourceTemplateCursor, setNextResourceTemplateCursor] = useState<
- string | undefined
- >();
- const [nextPromptCursor, setNextPromptCursor] = useState<
- string | undefined
- >();
- const [nextToolCursor, setNextToolCursor] = useState<string | undefined>();
- const progressTokenRef = useRef(0);
-
- const {
- height: historyPaneHeight,
- handleDragStart
- } = useDraggablePane(300);
-
- const {
- connectionStatus,
- serverCapabilities,
- mcpClient,
- requestHistory,
- makeRequest: makeConnectionRequest,
- sendNotification,
- connect: connectMcpServer
- } = useConnection({
- transportType,
- command,
- args,
- sseUrl,
- env,
- proxyServerUrl: PROXY_SERVER_URL,
- onNotification: (notification) => {
- setNotifications(prev => [...prev, notification as ServerNotification]);
- },
- onStdErrNotification: (notification) => {
- setStdErrNotifications(prev => [...prev, notification as StdErrNotification]);
- },
- onPendingRequest: (request, resolve, reject) => {
- setPendingSampleRequests(prev => [
- ...prev,
- { id: nextRequestId.current++, request, resolve, reject }
- ]);
- },
- getRoots: () => rootsRef.current
- });
-
- const makeRequest = async <T extends z.ZodType>(
- request: ClientRequest,
- schema: T,
- tabKey?: keyof typeof errors,
- ) => {
- try {
- const response = await makeConnectionRequest(request, schema);
- if (tabKey !== undefined) {
- clearError(tabKey);
- }
- return response;
- } catch (e) {
- const errorString = (e as Error).message ?? String(e);
- if (tabKey !== undefined) {
- setErrors((prev) => ({
- ...prev,
- [tabKey]: errorString,
- }));
- }
- throw e;
- }
- };
-
- useEffect(() => {
- localStorage.setItem("lastCommand", command);
- }, [command]);
-
- useEffect(() => {
- localStorage.setItem("lastArgs", args);
- }, [args]);
-
- useEffect(() => {
- fetch(`${PROXY_SERVER_URL}/config`)
- .then((response) => response.json())
- .then((data) => {
- setEnv(data.defaultEnvironment);
- if (data.defaultCommand) {
- setCommand(data.defaultCommand);
- }
- if (data.defaultArgs) {
- setArgs(data.defaultArgs);
- }
- })
- .catch((error) =>
- console.error("Error fetching default environment:", error),
- );
- }, []);
-
- useEffect(() => {
- rootsRef.current = roots;
- }, [roots]);
-
- useEffect(() => {
- if (!window.location.hash) {
- window.location.hash = "resources";
- }
- }, []);
-
- const clearError = (tabKey: keyof typeof errors) => {
- setErrors((prev) => ({ ...prev, [tabKey]: null }));
- };
-
- const listResources = async () => {
- const response = await makeRequest(
- {
- method: "resources/list" as const,
- params: nextResourceCursor ? { cursor: nextResourceCursor } : {},
- },
- ListResourcesResultSchema,
- "resources",
- );
- setResources(resources.concat(response.resources ?? []));
- setNextResourceCursor(response.nextCursor);
- };
-
- const listResourceTemplates = async () => {
- const response = await makeRequest(
- {
- method: "resources/templates/list" as const,
- params: nextResourceTemplateCursor
- ? { cursor: nextResourceTemplateCursor }
- : {},
- },
- ListResourceTemplatesResultSchema,
- "resources",
- );
- setResourceTemplates(
- resourceTemplates.concat(response.resourceTemplates ?? []),
- );
- setNextResourceTemplateCursor(response.nextCursor);
- };
-
- const readResource = async (uri: string) => {
- const response = await makeRequest(
- {
- method: "resources/read" as const,
- params: { uri },
- },
- ReadResourceResultSchema,
- "resources",
- );
- setResourceContent(JSON.stringify(response, null, 2));
- };
-
- const listPrompts = async () => {
- const response = await makeRequest(
- {
- method: "prompts/list" as const,
- params: nextPromptCursor ? { cursor: nextPromptCursor } : {},
- },
- ListPromptsResultSchema,
- "prompts",
- );
- setPrompts(response.prompts);
- setNextPromptCursor(response.nextCursor);
- };
-
- const getPrompt = async (name: string, args: Record<string, string> = {}) => {
- const response = await makeRequest(
- {
- method: "prompts/get" as const,
- params: { name, arguments: args },
- },
- GetPromptResultSchema,
- "prompts",
- );
- setPromptContent(JSON.stringify(response, null, 2));
- };
-
- const listTools = async () => {
- const response = await makeRequest(
- {
- method: "tools/list" as const,
- params: nextToolCursor ? { cursor: nextToolCursor } : {},
- },
- ListToolsResultSchema,
- "tools",
- );
- setTools(response.tools);
- setNextToolCursor(response.nextCursor);
- };
-
- const callTool = async (name: string, params: Record<string, unknown>) => {
- const response = await makeRequest(
- {
- method: "tools/call" as const,
- params: {
- name,
- arguments: params,
- _meta: {
- progressToken: progressTokenRef.current++,
- },
- },
- },
- CompatibilityCallToolResultSchema,
- "tools",
- );
- setToolResult(response);
- };
-
- const handleRootsChange = async () => {
- await sendNotification({ method: "notifications/roots/list_changed" });
- };
-
- return (
- <div className="flex h-screen bg-background">
- <Sidebar
- connectionStatus={connectionStatus}
- transportType={transportType}
- setTransportType={setTransportType}
- command={command}
- setCommand={setCommand}
- args={args}
- setArgs={setArgs}
- sseUrl={sseUrl}
- setSseUrl={setSseUrl}
- env={env}
- setEnv={setEnv}
- onConnect={connectMcpServer}
- stdErrNotifications={stdErrNotifications}
- />
- <div className="flex-1 flex flex-col overflow-hidden">
- <div className="flex-1 overflow-auto">
- {mcpClient ? (
- <Tabs
- defaultValue={
- Object.keys(serverCapabilities ?? {}).includes(window.location.hash.slice(1)) ?
- window.location.hash.slice(1) :
- serverCapabilities?.resources ? "resources" :
- serverCapabilities?.prompts ? "prompts" :
- serverCapabilities?.tools ? "tools" :
- "ping"
- }
- className="w-full p-4"
- onValueChange={(value) => (window.location.hash = value)}
- >
- <TabsList className="mb-4 p-0">
- <TabsTrigger value="resources" disabled={!serverCapabilities?.resources}>
- <Files className="w-4 h-4 mr-2" />
- Resources
- </TabsTrigger>
- <TabsTrigger value="prompts" disabled={!serverCapabilities?.prompts}>
- <MessageSquare className="w-4 h-4 mr-2" />
- Prompts
- </TabsTrigger>
- <TabsTrigger value="tools" disabled={!serverCapabilities?.tools}>
- <Hammer className="w-4 h-4 mr-2" />
- Tools
- </TabsTrigger>
- <TabsTrigger value="ping">
- <Bell className="w-4 h-4 mr-2" />
- Ping
- </TabsTrigger>
- <TabsTrigger value="sampling" className="relative">
- <Hash className="w-4 h-4 mr-2" />
- Sampling
- {pendingSampleRequests.length > 0 && (
- <span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full h-4 w-4 flex items-center justify-center">
- {pendingSampleRequests.length}
- </span>
- )}
- </TabsTrigger>
- <TabsTrigger value="roots">
- <FolderTree className="w-4 h-4 mr-2" />
- Roots
- </TabsTrigger>
- </TabsList>
-
- <div className="w-full">
- {!serverCapabilities?.resources && !serverCapabilities?.prompts && !serverCapabilities?.tools ? (
- <div className="flex items-center justify-center p-4">
- <p className="text-lg text-gray-500">
- The connected server does not support any MCP capabilities
- </p>
- </div>
- ) : (
- <>
- <ResourcesTab
- resources={resources}
- resourceTemplates={resourceTemplates}
- listResources={() => {
- clearError("resources");
- listResources();
- }}
- clearResources={() => {
- setResources([]);
- setNextResourceCursor(undefined);
- }}
- listResourceTemplates={() => {
- clearError("resources");
- listResourceTemplates();
- }}
- clearResourceTemplates={() => {
- setResourceTemplates([]);
- setNextResourceTemplateCursor(undefined);
- }}
- readResource={(uri) => {
- clearError("resources");
- readResource(uri);
- }}
- selectedResource={selectedResource}
- setSelectedResource={(resource) => {
- clearError("resources");
- setSelectedResource(resource);
- }}
- resourceContent={resourceContent}
- nextCursor={nextResourceCursor}
- nextTemplateCursor={nextResourceTemplateCursor}
- error={errors.resources}
- />
- <PromptsTab
- prompts={prompts}
- listPrompts={() => {
- clearError("prompts");
- listPrompts();
- }}
- clearPrompts={() => {
- setPrompts([]);
- setNextPromptCursor(undefined);
- }}
- getPrompt={(name, args) => {
- clearError("prompts");
- getPrompt(name, args);
- }}
- selectedPrompt={selectedPrompt}
- setSelectedPrompt={(prompt) => {
- clearError("prompts");
- setSelectedPrompt(prompt);
- }}
- promptContent={promptContent}
- nextCursor={nextPromptCursor}
- error={errors.prompts}
- />
- <ToolsTab
- tools={tools}
- listTools={() => {
- clearError("tools");
- listTools();
- }}
- clearTools={() => {
- setTools([]);
- setNextToolCursor(undefined);
- }}
- callTool={(name, params) => {
- clearError("tools");
- callTool(name, params);
- }}
- selectedTool={selectedTool}
- setSelectedTool={(tool) => {
- clearError("tools");
- setSelectedTool(tool);
- setToolResult(null);
- }}
- toolResult={toolResult}
- nextCursor={nextToolCursor}
- error={errors.tools}
- />
- <ConsoleTab />
- <PingTab
- onPingClick={() => {
- void makeRequest(
- {
- method: "ping" as const,
- },
- EmptyResultSchema,
- );
- }}
- />
- <SamplingTab
- pendingRequests={pendingSampleRequests}
- onApprove={handleApproveSampling}
- onReject={handleRejectSampling}
- />
- <RootsTab
- roots={roots}
- setRoots={setRoots}
- onRootsChange={handleRootsChange}
- />
- </>
- )}
- </div>
- </Tabs>
- ) : (
- <div className="flex items-center justify-center h-full">
- <p className="text-lg text-gray-500">
- Connect to an MCP server to start inspecting
- </p>
- </div>
- )}
- </div>
- <div
- className="relative border-t border-border"
- style={{
- height: `${historyPaneHeight}px`,
- }}
- >
- <div
- className="absolute w-full h-4 -top-2 cursor-row-resize flex items-center justify-center hover:bg-accent/50"
- onMouseDown={handleDragStart}
- >
- <div className="w-8 h-1 rounded-full bg-border" />
- </div>
- <div className="h-full overflow-auto">
- <HistoryAndNotifications
- requestHistory={requestHistory}
- serverNotifications={notifications}
- />
- </div>
- </div>
- </div>
- </div>
- );
-};
-
-export default App;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 | 1x - -1x -4x -4x -4x - -4x -4x - - -1x - | import { TabsContent } from "@/components/ui/tabs";
-
-const ConsoleTab = () => (
- <TabsContent value="console" className="h-96">
- <div className="bg-gray-900 text-gray-100 p-4 rounded-lg h-full font-mono text-sm overflow-auto">
- <div className="opacity-50">Welcome to MCP Client Console</div>
- {/* Console output would go here */}
- </div>
- </TabsContent>
-);
-
-export default ConsoleTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 | 1x -1x -1x - -1x -21x -21x -21x - - -21x -21x - -21x -21x - -21x - -21x -1x -1x - -21x -1x -1x - -21x - - - -21x -21x -21x -21x -21x -18x - -3x -3x -3x -3x -3x -6x -6x -6x - -6x -6x -6x -1x - - -6x -6x -6x -6x -6x -6x -1x -5x -6x -6x -6x -1x -1x -1x -1x - -1x -1x -1x -1x - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x -1x - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x - -6x -3x -3x - -21x -21x -21x -21x -19x - -2x -2x -2x -2x -2x -4x -4x -4x - -4x -4x -4x - -4x -4x -4x -4x -4x -4x -4x -1x -1x -1x - -1x -1x -1x - - -1x - -1x -1x -1x -1x -1x -1x -1x - -4x -2x -2x - -21x -21x - -21x - -1x - | import { ServerNotification } from "@modelcontextprotocol/sdk/types.js";
-import { Copy } from "lucide-react";
-import { useState } from "react";
-
-const HistoryAndNotifications = ({
- requestHistory,
- serverNotifications,
-}: {
- requestHistory: Array<{ request: string; response?: string }>;
- serverNotifications: ServerNotification[];
-}) => {
- const [expandedRequests, setExpandedRequests] = useState<{
- [key: number]: boolean;
- }>({});
- const [expandedNotifications, setExpandedNotifications] = useState<{
- [key: number]: boolean;
- }>({});
-
- const toggleRequestExpansion = (index: number) => {
- setExpandedRequests((prev) => ({ ...prev, [index]: !prev[index] }));
- };
-
- const toggleNotificationExpansion = (index: number) => {
- setExpandedNotifications((prev) => ({ ...prev, [index]: !prev[index] }));
- };
-
- const copyToClipboard = (text: string) => {
- navigator.clipboard.writeText(text);
- };
-
- return (
- <div className="bg-card overflow-hidden flex h-full">
- <div className="flex-1 overflow-y-auto p-4 border-r">
- <h2 className="text-lg font-semibold mb-4">History</h2>
- {requestHistory.length === 0 ? (
- <p className="text-sm text-gray-500 italic">No history yet</p>
- ) : (
- <ul className="space-y-3">
- {requestHistory
- .slice()
- .reverse()
- .map((request, index) => (
- <li
- key={index}
- className="text-sm text-foreground bg-secondary p-2 rounded"
- >
- <div
- className="flex justify-between items-center cursor-pointer"
- onClick={() =>
- toggleRequestExpansion(requestHistory.length - 1 - index)
- }
- >
- <span className="font-mono">
- {requestHistory.length - index}.{" "}
- {JSON.parse(request.request).method}
- </span>
- <span>
- {expandedRequests[requestHistory.length - 1 - index]
- ? "â–¼"
- : "â–¶"}
- </span>
- </div>
- {expandedRequests[requestHistory.length - 1 - index] && (
- <>
- <div className="mt-2">
- <div className="flex justify-between items-center mb-1">
- <span className="font-semibold text-blue-600">
- Request:
- </span>
- <button
- onClick={() => copyToClipboard(request.request)}
- className="text-blue-500 hover:text-blue-700"
- >
- <Copy size={16} />
- </button>
- </div>
- <pre className="whitespace-pre-wrap break-words bg-background p-2 rounded">
- {JSON.stringify(JSON.parse(request.request), null, 2)}
- </pre>
- </div>
- {request.response && (
- <div className="mt-2">
- <div className="flex justify-between items-center mb-1">
- <span className="font-semibold text-green-600">
- Response:
- </span>
- <button
- onClick={() => copyToClipboard(request.response!)}
- className="text-blue-500 hover:text-blue-700"
- >
- <Copy size={16} />
- </button>
- </div>
- <pre className="whitespace-pre-wrap break-words bg-background p-2 rounded">
- {JSON.stringify(
- JSON.parse(request.response),
- null,
- 2,
- )}
- </pre>
- </div>
- )}
- </>
- )}
- </li>
- ))}
- </ul>
- )}
- </div>
- <div className="flex-1 overflow-y-auto p-4">
- <h2 className="text-lg font-semibold mb-4">Server Notifications</h2>
- {serverNotifications.length === 0 ? (
- <p className="text-sm text-gray-500 italic">No notifications yet</p>
- ) : (
- <ul className="space-y-3">
- {serverNotifications
- .slice()
- .reverse()
- .map((notification, index) => (
- <li
- key={index}
- className="text-sm text-foreground bg-secondary p-2 rounded"
- >
- <div
- className="flex justify-between items-center cursor-pointer"
- onClick={() => toggleNotificationExpansion(index)}
- >
- <span className="font-mono">
- {serverNotifications.length - index}.{" "}
- {notification.method}
- </span>
- <span>{expandedNotifications[index] ? "â–¼" : "â–¶"}</span>
- </div>
- {expandedNotifications[index] && (
- <div className="mt-2">
- <div className="flex justify-between items-center mb-1">
- <span className="font-semibold text-purple-600">
- Details:
- </span>
- <button
- onClick={() =>
- copyToClipboard(JSON.stringify(notification))
- }
- className="text-blue-500 hover:text-blue-700"
- >
- <Copy size={16} />
- </button>
- </div>
- <pre className="whitespace-pre-wrap break-words bg-background p-2 rounded">
- {JSON.stringify(notification, null, 2)}
- </pre>
- </div>
- )}
- </li>
- ))}
- </ul>
- )}
- </div>
- </div>
- );
-};
-
-export default HistoryAndNotifications;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 | 1x - - - - - - - - - - - - -1x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x -44x - -44x -44x -44x -44x -44x -44x -44x -44x - -44x -44x -44x -74x -74x -74x -74x - -74x -74x -44x -44x -44x -44x - - -1x - | import { Button } from "./ui/button";
-
-type ListPaneProps<T> = {
- items: T[];
- listItems: () => void;
- clearItems: () => void;
- setSelectedItem: (item: T) => void;
- renderItem: (item: T) => React.ReactNode;
- title: string;
- buttonText: string;
- isButtonDisabled?: boolean;
-};
-
-const ListPane = <T extends object>({
- items,
- listItems,
- clearItems,
- setSelectedItem,
- renderItem,
- title,
- buttonText,
- isButtonDisabled,
-}: ListPaneProps<T>) => (
- <div className="bg-card rounded-lg shadow">
- <div className="p-4 border-b border-gray-200 dark:border-gray-700">
- <h3 className="font-semibold dark:text-white">{title}</h3>
- </div>
- <div className="p-4">
- <Button
- variant="outline"
- className="w-full mb-4"
- onClick={listItems}
- disabled={isButtonDisabled}
- >
- {buttonText}
- </Button>
- <Button
- variant="outline"
- className="w-full mb-4"
- onClick={clearItems}
- disabled={items.length === 0}
- >
- Clear
- </Button>
- <div className="space-y-2 overflow-y-auto max-h-96">
- {items.map((item, index) => (
- <div
- key={index}
- className="flex items-center p-2 rounded hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer"
- onClick={() => setSelectedItem(item)}
- >
- {renderItem(item)}
- </div>
- ))}
- </div>
- </div>
- </div>
-);
-
-export default ListPane;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 | 1x -1x - -1x -9x -9x -9x -9x -9x -9x - -9x - -9x -9x -9x -9x - -9x - -1x - | import { TabsContent } from "@/components/ui/tabs";
-import { Button } from "@/components/ui/button";
-
-const PingTab = ({ onPingClick }: { onPingClick: () => void }) => {
- return (
- <TabsContent value="ping" className="grid grid-cols-2 gap-4">
- <div className="col-span-2 flex justify-center items-center">
- <Button
- onClick={onPingClick}
- className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white font-bold py-6 px-12 rounded-full shadow-lg transform transition duration-300 hover:scale-110 focus:outline-none focus:ring-4 focus:ring-purple-300 animate-pulse"
- >
- <span className="text-3xl mr-2">🚀</span>
- MEGA PING
- <span className="text-3xl ml-2">💥</span>
- </Button>
- </div>
- </TabsContent>
- );
-};
-
-export default PingTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 | 1x -1x -1x -1x -1x -1x - -1x -1x -1x - - - - - - - - - - - -1x -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x - - - - - - - - - -10x -10x - -10x -1x -1x - -10x -1x -1x -1x -1x - -10x -10x -10x -10x -10x -10x -10x - - - -10x -12x -12x -12x -12x - -10x -10x -10x -10x - -10x -10x -10x -10x -10x -10x -10x -10x -1x -1x -1x -1x -1x -9x -4x -4x -4x -4x -4x - -4x -8x -8x -8x -8x -8x -8x -8x -1x - -8x -8x -8x -8x -8x -4x - -8x - -8x -4x -4x - -4x -4x -1x -1x -1x -1x -1x - -4x - -5x -5x - -5x -5x - -10x -10x -10x - -10x - -1x - | import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/label";
-import { TabsContent } from "@/components/ui/tabs";
-import { Textarea } from "@/components/ui/textarea";
-import { ListPromptsResult } from "@modelcontextprotocol/sdk/types.js";
-import { AlertCircle } from "lucide-react";
-import { useState } from "react";
-import ListPane from "./ListPane";
-
-export type Prompt = {
- name: string;
- description?: string;
- arguments?: {
- name: string;
- description?: string;
- required?: boolean;
- }[];
-};
-
-const PromptsTab = ({
- prompts,
- listPrompts,
- clearPrompts,
- getPrompt,
- selectedPrompt,
- setSelectedPrompt,
- promptContent,
- nextCursor,
- error,
-}: {
- prompts: Prompt[];
- listPrompts: () => void;
- clearPrompts: () => void;
- getPrompt: (name: string, args: Record<string, string>) => void;
- selectedPrompt: Prompt | null;
- setSelectedPrompt: (prompt: Prompt) => void;
- promptContent: string;
- nextCursor: ListPromptsResult["nextCursor"];
- error: string | null;
-}) => {
- const [promptArgs, setPromptArgs] = useState<Record<string, string>>({});
-
- const handleInputChange = (argName: string, value: string) => {
- setPromptArgs((prev) => ({ ...prev, [argName]: value }));
- };
-
- const handleGetPrompt = () => {
- if (selectedPrompt) {
- getPrompt(selectedPrompt.name, promptArgs);
- }
- };
-
- return (
- <TabsContent value="prompts" className="grid grid-cols-2 gap-4">
- <ListPane
- items={prompts}
- listItems={listPrompts}
- clearItems={clearPrompts}
- setSelectedItem={(prompt) => {
- setSelectedPrompt(prompt);
- setPromptArgs({});
- }}
- renderItem={(prompt) => (
- <>
- <span className="flex-1">{prompt.name}</span>
- <span className="text-sm text-gray-500">{prompt.description}</span>
- </>
- )}
- title="Prompts"
- buttonText={nextCursor ? "List More Prompts" : "List Prompts"}
- isButtonDisabled={!nextCursor && prompts.length > 0}
- />
-
- <div className="bg-card rounded-lg shadow">
- <div className="p-4 border-b border-gray-200">
- <h3 className="font-semibold">
- {selectedPrompt ? selectedPrompt.name : "Select a prompt"}
- </h3>
- </div>
- <div className="p-4">
- {error ? (
- <Alert variant="destructive">
- <AlertCircle className="h-4 w-4" />
- <AlertTitle>Error</AlertTitle>
- <AlertDescription>{error}</AlertDescription>
- </Alert>
- ) : selectedPrompt ? (
- <div className="space-y-4">
- {selectedPrompt.description && (
- <p className="text-sm text-gray-600">
- {selectedPrompt.description}
- </p>
- )}
- {selectedPrompt.arguments?.map((arg) => (
- <div key={arg.name}>
- <Label htmlFor={arg.name}>{arg.name}</Label>
- <Input
- id={arg.name}
- placeholder={`Enter ${arg.name}`}
- value={promptArgs[arg.name] || ""}
- onChange={(e) =>
- handleInputChange(arg.name, e.target.value)
- }
- />
- {arg.description && (
- <p className="text-xs text-gray-500 mt-1">
- {arg.description}
- {arg.required && (
- <span className="text-xs mt-1 ml-1">(Required)</span>
- )}
- </p>
- )}
- </div>
- ))}
- <Button onClick={handleGetPrompt} className="w-full">
- Get Prompt
- </Button>
- {promptContent && (
- <Textarea
- value={promptContent}
- readOnly
- className="h-64 font-mono"
- />
- )}
- </div>
- ) : (
- <Alert>
- <AlertDescription>
- Select a prompt from the list to view and use it
- </AlertDescription>
- </Alert>
- )}
- </div>
- </div>
- </TabsContent>
- );
-};
-
-export default PromptsTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 | 1x -1x -1x -1x - - - - - - -1x -1x -1x - -1x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x - - - - - - - - - - - - - -16x -16x -16x -16x -16x -16x - -16x -1x -1x -1x -1x -1x -1x -1x -1x - -16x -1x -1x -1x -1x - -1x -1x -1x - -16x -16x -16x -16x -16x -16x -16x - - - - -16x -24x -24x -24x -24x -24x -24x -24x - -16x -16x -16x -16x - -16x -16x -16x -16x -16x -2x -2x -2x -2x -16x -24x -24x -24x -24x -24x -24x -24x - -16x -16x -16x - -16x -16x - -16x -16x -16x -16x -16x - -16x -2x -14x -4x -10x -16x -16x -2x -2x -2x -2x - -2x - -2x - -16x -16x -16x -1x -1x -1x -1x -1x -15x -2x -2x -2x -13x -4x -4x -4x -4x -4x -4x -4x -8x -8x -8x -8x -8x -8x - -8x -8x -8x -8x -8x -8x -2x -2x -2x -2x - -8x -8x -8x - -4x -4x -4x -4x -4x - -4x -4x - -9x -9x - -9x -9x - -16x -16x -16x - -16x - -1x - | import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { TabsContent } from "@/components/ui/tabs";
-import {
- ListResourcesResult,
- Resource,
- ResourceTemplate,
- ListResourceTemplatesResult,
-} from "@modelcontextprotocol/sdk/types.js";
-import { AlertCircle, ChevronRight, FileText, RefreshCw } from "lucide-react";
-import ListPane from "./ListPane";
-import { useState } from "react";
-
-const ResourcesTab = ({
- resources,
- resourceTemplates,
- listResources,
- clearResources,
- listResourceTemplates,
- clearResourceTemplates,
- readResource,
- selectedResource,
- setSelectedResource,
- resourceContent,
- nextCursor,
- nextTemplateCursor,
- error,
-}: {
- resources: Resource[];
- resourceTemplates: ResourceTemplate[];
- listResources: () => void;
- clearResources: () => void;
- listResourceTemplates: () => void;
- clearResourceTemplates: () => void;
- readResource: (uri: string) => void;
- selectedResource: Resource | null;
- setSelectedResource: (resource: Resource | null) => void;
- resourceContent: string;
- nextCursor: ListResourcesResult["nextCursor"];
- nextTemplateCursor: ListResourceTemplatesResult["nextCursor"];
- error: string | null;
-}) => {
- const [selectedTemplate, setSelectedTemplate] =
- useState<ResourceTemplate | null>(null);
- const [templateValues, setTemplateValues] = useState<Record<string, string>>(
- {},
- );
-
- const fillTemplate = (
- template: string,
- values: Record<string, string>,
- ): string => {
- return template.replace(
- /{([^}]+)}/g,
- (_, key) => values[key] || `{${key}}`,
- );
- };
-
- const handleReadTemplateResource = () => {
- if (selectedTemplate) {
- const uri = fillTemplate(selectedTemplate.uriTemplate, templateValues);
- readResource(uri);
- setSelectedTemplate(null);
- // We don't have the full Resource object here, so we create a partial one
- setSelectedResource({ uri, name: uri } as Resource);
- }
- };
-
- return (
- <TabsContent value="resources" className="grid grid-cols-3 gap-4">
- <ListPane
- items={resources}
- listItems={listResources}
- clearItems={clearResources}
- setSelectedItem={(resource) => {
- setSelectedResource(resource);
- readResource(resource.uri);
- setSelectedTemplate(null);
- }}
- renderItem={(resource) => (
- <div className="flex items-center w-full">
- <FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
- <span className="flex-1 truncate" title={resource.uri.toString()}>
- {resource.name}
- </span>
- <ChevronRight className="w-4 h-4 flex-shrink-0 text-gray-400" />
- </div>
- )}
- title="Resources"
- buttonText={nextCursor ? "List More Resources" : "List Resources"}
- isButtonDisabled={!nextCursor && resources.length > 0}
- />
-
- <ListPane
- items={resourceTemplates}
- listItems={listResourceTemplates}
- clearItems={clearResourceTemplates}
- setSelectedItem={(template) => {
- setSelectedTemplate(template);
- setSelectedResource(null);
- setTemplateValues({});
- }}
- renderItem={(template) => (
- <div className="flex items-center w-full">
- <FileText className="w-4 h-4 mr-2 flex-shrink-0 text-gray-500" />
- <span className="flex-1 truncate" title={template.uriTemplate}>
- {template.name}
- </span>
- <ChevronRight className="w-4 h-4 flex-shrink-0 text-gray-400" />
- </div>
- )}
- title="Resource Templates"
- buttonText={
- nextTemplateCursor ? "List More Templates" : "List Templates"
- }
- isButtonDisabled={!nextTemplateCursor && resourceTemplates.length > 0}
- />
-
- <div className="bg-card rounded-lg shadow">
- <div className="p-4 border-b border-gray-200 flex justify-between items-center">
- <h3
- className="font-semibold truncate"
- title={selectedResource?.name || selectedTemplate?.name}
- >
- {selectedResource
- ? selectedResource.name
- : selectedTemplate
- ? selectedTemplate.name
- : "Select a resource or template"}
- </h3>
- {selectedResource && (
- <Button
- variant="outline"
- size="sm"
- onClick={() => readResource(selectedResource.uri)}
- >
- <RefreshCw className="w-4 h-4 mr-2" />
- Refresh
- </Button>
- )}
- </div>
- <div className="p-4">
- {error ? (
- <Alert variant="destructive">
- <AlertCircle className="h-4 w-4" />
- <AlertTitle>Error</AlertTitle>
- <AlertDescription>{error}</AlertDescription>
- </Alert>
- ) : selectedResource ? (
- <pre className="bg-gray-50 dark:bg-gray-800 p-4 rounded text-sm overflow-auto max-h-96 whitespace-pre-wrap break-words text-gray-900 dark:text-gray-100">
- {resourceContent}
- </pre>
- ) : selectedTemplate ? (
- <div className="space-y-4">
- <p className="text-sm text-gray-600">
- {selectedTemplate.description}
- </p>
- {selectedTemplate.uriTemplate
- .match(/{([^}]+)}/g)
- ?.map((param) => {
- const key = param.slice(1, -1);
- return (
- <div key={key}>
- <label
- htmlFor={key}
- className="block text-sm font-medium text-gray-700"
- >
- {key}
- </label>
- <Input
- id={key}
- value={templateValues[key] || ""}
- onChange={(e) =>
- setTemplateValues({
- ...templateValues,
- [key]: e.target.value,
- })
- }
- className="mt-1"
- />
- </div>
- );
- })}
- <Button
- onClick={handleReadTemplateResource}
- disabled={Object.keys(templateValues).length === 0}
- >
- Read Resource
- </Button>
- </div>
- ) : (
- <Alert>
- <AlertDescription>
- Select a resource or template from the list to view its contents
- </AlertDescription>
- </Alert>
- )}
- </div>
- </div>
- </TabsContent>
- );
-};
-
-export default ResourcesTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 | 1x -1x -1x -1x - -1x - -1x -9x -9x -9x -9x - - - -9x -9x -1x -1x - -9x -1x -1x - -9x -1x -1x -2x -1x -1x -1x - -9x -1x -1x - -9x -9x -9x -9x - -9x -9x - -9x -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x - -10x -10x -10x -9x - -9x -9x -9x - -9x -9x -9x - -9x -9x -9x - -9x - -1x - | import { Alert, AlertDescription } from "@/components/ui/alert";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { TabsContent } from "@/components/ui/tabs";
-import { Root } from "@modelcontextprotocol/sdk/types.js";
-import { Plus, Minus, Save } from "lucide-react";
-
-const RootsTab = ({
- roots,
- setRoots,
- onRootsChange,
-}: {
- roots: Root[];
- setRoots: React.Dispatch<React.SetStateAction<Root[]>>;
- onRootsChange: () => void;
-}) => {
- const addRoot = () => {
- setRoots((currentRoots) => [...currentRoots, { uri: "file://", name: "" }]);
- };
-
- const removeRoot = (index: number) => {
- setRoots((currentRoots) => currentRoots.filter((_, i) => i !== index));
- };
-
- const updateRoot = (index: number, field: keyof Root, value: string) => {
- setRoots((currentRoots) =>
- currentRoots.map((root, i) =>
- i === index ? { ...root, [field]: value } : root,
- ),
- );
- };
-
- const handleSave = () => {
- onRootsChange();
- };
-
- return (
- <TabsContent value="roots" className="space-y-4">
- <Alert>
- <AlertDescription>
- Configure the root directories that the server can access
- </AlertDescription>
- </Alert>
-
- {roots.map((root, index) => (
- <div key={index} className="flex gap-2 items-center">
- <Input
- placeholder="file:// URI"
- value={root.uri}
- onChange={(e) => updateRoot(index, "uri", e.target.value)}
- className="flex-1"
- />
- <Button
- variant="destructive"
- size="sm"
- onClick={() => removeRoot(index)}
- aria-label="Remove root"
- >
- <Minus className="h-4 w-4" />
- </Button>
- </div>
- ))}
-
- <div className="flex gap-2">
- <Button variant="outline" onClick={addRoot}>
- <Plus className="h-4 w-4 mr-2" />
- Add Root
- </Button>
- <Button onClick={handleSave}>
- <Save className="h-4 w-4 mr-2" />
- Save Changes
- </Button>
- </div>
- </TabsContent>
- );
-};
-
-export default RootsTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 | 1x -1x -1x - - - - - - - - - - - - - - - - -1x -10x - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -10x -10x -10x -10x - - -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x -10x - -10x -10x -10x -10x -10x -5x - -10x -10x - -10x - -1x - | import { Alert, AlertDescription } from "@/components/ui/alert";
-import { Button } from "@/components/ui/button";
-import { TabsContent } from "@/components/ui/tabs";
-import {
- CreateMessageRequest,
- CreateMessageResult,
-} from "@modelcontextprotocol/sdk/types.js";
-
-export type PendingRequest = {
- id: number;
- request: CreateMessageRequest;
-};
-
-export type Props = {
- pendingRequests: PendingRequest[];
- onApprove: (id: number, result: CreateMessageResult) => void;
- onReject: (id: number) => void;
-};
-
-const SamplingTab = ({ pendingRequests, onApprove, onReject }: Props) => {
- const handleApprove = (id: number) => {
- // For now, just return a stub response
- onApprove(id, {
- model: "stub-model",
- stopReason: "endTurn",
- role: "assistant",
- content: {
- type: "text",
- text: "This is a stub response.",
- },
- });
- };
-
- return (
- <TabsContent value="sampling" className="h-96">
- <Alert>
- <AlertDescription>
- When the server requests LLM sampling, requests will appear here for
- approval.
- </AlertDescription>
- </Alert>
- <div className="mt-4 space-y-4">
- <h3 className="text-lg font-semibold">Recent Requests</h3>
- {pendingRequests.map((request) => (
- <div key={request.id} className="p-4 border rounded-lg space-y-4">
- <pre className="bg-gray-50 p-2 rounded">
- {JSON.stringify(request.request, null, 2)}
- </pre>
- <div className="flex space-x-2">
- <Button onClick={() => handleApprove(request.id)}>Approve</Button>
- <Button variant="outline" onClick={() => onReject(request.id)}>
- Reject
- </Button>
- </div>
- </div>
- ))}
- {pendingRequests.length === 0 && (
- <p className="text-gray-500">No pending requests</p>
- )}
- </div>
- </TabsContent>
- );
-};
-
-export default SamplingTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 -248 -249 -250 -251 -252 -253 -254 -255 -256 -257 -258 -259 -260 -261 -262 -263 -264 -265 -266 -267 -268 -269 | 1x -1x -1x -1x - - - - - - -1x - - -1x -1x - - - - - - - - - - - - - - - - - -1x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x - -16x -16x -16x -16x -16x -16x -16x -16x -16x - -16x -16x -16x -16x -16x -16x -16x - - - -16x -16x -16x -16x -16x -16x -16x -16x -16x - -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x - - - - - - - - - - - -16x -16x -16x -16x -16x -16x - -16x - - -16x -16x - -16x -16x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -16x - - -16x -16x -16x - -16x - -16x -16x -16x -16x -9x -7x - -7x -16x -16x -16x -16x -9x -7x - -7x -16x -16x -16x - - - - - - - - - - - - - - - - - - -16x -16x -16x -16x -16x -16x -16x -16x - - - -16x -16x -16x -16x -16x -16x -16x -16x -16x - -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x -16x - -16x - -1x - | import { useState } from "react";
-import { Play, ChevronDown, ChevronRight, CircleHelp, Bug, Github } from "lucide-react";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
-} from "@/components/ui/select";
-import { StdErrNotification } from "@/lib/notificationTypes";
-
-import useTheme from "../lib/useTheme";
-import { version } from "../../../package.json";
-
-interface SidebarProps {
- connectionStatus: "disconnected" | "connected" | "error";
- transportType: "stdio" | "sse";
- setTransportType: (type: "stdio" | "sse") => void;
- command: string;
- setCommand: (command: string) => void;
- args: string;
- setArgs: (args: string) => void;
- sseUrl: string;
- setSseUrl: (url: string) => void;
- env: Record<string, string>;
- setEnv: (env: Record<string, string>) => void;
- onConnect: () => void;
- stdErrNotifications: StdErrNotification[];
-}
-
-const Sidebar = ({
- connectionStatus,
- transportType,
- setTransportType,
- command,
- setCommand,
- args,
- setArgs,
- sseUrl,
- setSseUrl,
- env,
- setEnv,
- onConnect,
- stdErrNotifications,
-}: SidebarProps) => {
- const [theme, setTheme] = useTheme();
- const [showEnvVars, setShowEnvVars] = useState(false);
-
- return (
- <div className="w-80 bg-card border-r border-border flex flex-col h-full">
- <div className="flex items-center justify-between p-4 border-b border-gray-200">
- <div className="flex items-center">
- <h1 className="ml-2 text-lg font-semibold">
- MCP Inspector v{version}
- </h1>
- </div>
- </div>
-
- <div className="p-4 flex-1 overflow-auto">
- <div className="space-y-4">
- <div className="space-y-2">
- <label className="text-sm font-medium">Transport Type</label>
- <Select
- value={transportType}
- onValueChange={(value: "stdio" | "sse") =>
- setTransportType(value)
- }
- >
- <SelectTrigger>
- <SelectValue placeholder="Select transport type" />
- </SelectTrigger>
- <SelectContent>
- <SelectItem value="stdio">STDIO</SelectItem>
- <SelectItem value="sse">SSE</SelectItem>
- </SelectContent>
- </Select>
- </div>
-
- {transportType === "stdio" ? (
- <>
- <div className="space-y-2">
- <label className="text-sm font-medium">Command</label>
- <Input
- placeholder="Command"
- value={command}
- onChange={(e) => setCommand(e.target.value)}
- className="font-mono"
- />
- </div>
- <div className="space-y-2">
- <label className="text-sm font-medium">Arguments</label>
- <Input
- placeholder="Arguments (space-separated)"
- value={args}
- onChange={(e) => setArgs(e.target.value)}
- className="font-mono"
- />
- </div>
- </>
- ) : (
- <div className="space-y-2">
- <label className="text-sm font-medium">URL</label>
- <Input
- placeholder="URL"
- value={sseUrl}
- onChange={(e) => setSseUrl(e.target.value)}
- className="font-mono"
- />
- </div>
- )}
- {transportType === "stdio" && (
- <div className="space-y-2">
- <Button
- variant="outline"
- onClick={() => setShowEnvVars(!showEnvVars)}
- className="flex items-center w-full"
- >
- {showEnvVars ? (
- <ChevronDown className="w-4 h-4 mr-2" />
- ) : (
- <ChevronRight className="w-4 h-4 mr-2" />
- )}
- Environment Variables
- </Button>
- {showEnvVars && (
- <div className="space-y-2">
- {Object.entries(env).map(([key, value], idx) => (
- <div key={idx} className="grid grid-cols-[1fr,auto] gap-2">
- <div className="space-y-1">
- <Input
- placeholder="Key"
- value={key}
- onChange={(e) => {
- const newEnv = { ...env };
- delete newEnv[key];
- newEnv[e.target.value] = value;
- setEnv(newEnv);
- }}
- className="font-mono"
- />
- <Input
- placeholder="Value"
- value={value}
- onChange={(e) => {
- const newEnv = { ...env };
- newEnv[key] = e.target.value;
- setEnv(newEnv);
- }}
- className="font-mono"
- />
- </div>
- <Button
- variant="destructive"
- onClick={() => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { [key]: removed, ...rest } = env;
- setEnv(rest);
- }}
- >
- Remove
- </Button>
- </div>
- ))}
- <Button
- variant="outline"
- onClick={() => {
- const newEnv = { ...env };
- newEnv[""] = "";
- setEnv(newEnv);
- }}
- >
- Add Environment Variable
- </Button>
- </div>
- )}
- </div>
- )}
-
- <div className="space-y-2">
- <Button className="w-full" onClick={onConnect}>
- <Play className="w-4 h-4 mr-2" />
- Connect
- </Button>
-
- <div className="flex items-center justify-center space-x-2 mb-4">
- <div
- className={`w-2 h-2 rounded-full ${
- connectionStatus === "connected"
- ? "bg-green-500"
- : connectionStatus === "error"
- ? "bg-red-500"
- : "bg-gray-500"
- }`}
- />
- <span className="text-sm text-gray-600">
- {connectionStatus === "connected"
- ? "Connected"
- : connectionStatus === "error"
- ? "Connection Error"
- : "Disconnected"}
- </span>
- </div>
- {stdErrNotifications.length > 0 && (
- <>
- <div className="mt-4 border-t border-gray-200 pt-4">
- <h3 className="text-sm font-medium">
- Error output from MCP server
- </h3>
- <div className="mt-2 max-h-80 overflow-y-auto">
- {stdErrNotifications.map((notification, index) => (
- <div
- key={index}
- className="text-sm text-red-500 font-mono py-2 border-b border-gray-200 last:border-b-0"
- >
- {notification.params.content}
- </div>
- ))}
- </div>
- </div>
- </>
- )}
- </div>
- </div>
- </div>
- <div className="p-4 border-t">
- <div className="flex items-center justify-between">
- <Select
- value={theme}
- onValueChange={(value: string) =>
- setTheme(value as "system" | "light" | "dark")
- }
- >
- <SelectTrigger className="w-[100px]" id="theme-select">
- <SelectValue />
- </SelectTrigger>
- <SelectContent>
- <SelectItem value="system">System</SelectItem>
- <SelectItem value="light">Light</SelectItem>
- <SelectItem value="dark">Dark</SelectItem>
- </SelectContent>
- </Select>
-
- <div className="flex items-center space-x-2">
- <a href="https://modelcontextprotocol.io/docs/tools/inspector" target="_blank" rel="noopener noreferrer">
- <Button variant="ghost" title="Inspector Documentation">
- <CircleHelp className="w-4 h-4 text-gray-800" />
- </Button>
- </a>
- <a href="https://modelcontextprotocol.io/docs/tools/debugging" target="_blank" rel="noopener noreferrer">
- <Button variant="ghost" title="Debugging Guide">
- <Bug className="w-4 h-4 text-gray-800" />
- </Button>
- </a>
- <a href="https://github.com/modelcontextprotocol/inspector" target="_blank" rel="noopener noreferrer">
- <Button variant="ghost" title="Report bugs or contribute on GitHub">
- <Github className="w-4 h-4 text-gray-800" />
- </Button>
- </a>
- </div>
- </div>
- </div>
- </div>
- );
-};
-
-export default Sidebar;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 -189 -190 -191 -192 -193 -194 -195 -196 -197 -198 -199 -200 -201 -202 -203 -204 -205 -206 -207 -208 -209 -210 -211 -212 -213 -214 -215 -216 -217 -218 -219 -220 -221 -222 -223 -224 -225 -226 -227 -228 -229 -230 -231 -232 -233 -234 -235 -236 -237 -238 -239 -240 -241 -242 -243 -244 -245 -246 -247 | 1x -1x -1x -1x -1x -1x - - - - -1x -1x -1x -1x - - - -1x -6x -6x -6x -6x -6x -6x -6x -6x -6x -6x - - - - - - - - - -6x -6x -6x -2x -6x - -6x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -6x -6x -6x -6x -6x -6x - - - -6x -6x - - - - - - - -6x -6x -6x -6x - -6x -6x -6x -6x -6x -6x -6x -6x - - - - - -6x - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -6x -6x - -6x -6x - -6x -6x -6x - -6x - -1x - | import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/label";
-import { TabsContent } from "@/components/ui/tabs";
-import { Textarea } from "@/components/ui/textarea";
-import {
- ListToolsResult,
- Tool,
- CallToolResultSchema,
-} from "@modelcontextprotocol/sdk/types.js";
-import { AlertCircle, Send } from "lucide-react";
-import { useEffect, useState } from "react";
-import ListPane from "./ListPane";
-
-import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js";
-
-const ToolsTab = ({
- tools,
- listTools,
- clearTools,
- callTool,
- selectedTool,
- setSelectedTool,
- toolResult,
- nextCursor,
- error,
-}: {
- tools: Tool[];
- listTools: () => void;
- clearTools: () => void;
- callTool: (name: string, params: Record<string, unknown>) => void;
- selectedTool: Tool | null;
- setSelectedTool: (tool: Tool | null) => void;
- toolResult: CompatibilityCallToolResult | null;
- nextCursor: ListToolsResult["nextCursor"];
- error: string | null;
-}) => {
- const [params, setParams] = useState<Record<string, unknown>>({});
- useEffect(() => {
- setParams({});
- }, [selectedTool]);
-
- const renderToolResult = () => {
- if (!toolResult) return null;
-
- if ("content" in toolResult) {
- const parsedResult = CallToolResultSchema.safeParse(toolResult);
- if (!parsedResult.success) {
- return (
- <>
- <h4 className="font-semibold mb-2">Invalid Tool Result:</h4>
- <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64">
- {JSON.stringify(toolResult, null, 2)}
- </pre>
- <h4 className="font-semibold mb-2">Errors:</h4>
- {parsedResult.error.errors.map((error, idx) => (
- <pre
- key={idx}
- className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64"
- >
- {JSON.stringify(error, null, 2)}
- </pre>
- ))}
- </>
- );
- }
- const structuredResult = parsedResult.data;
- const isError = structuredResult.isError ?? false;
-
- return (
- <>
- <h4 className="font-semibold mb-2">
- Tool Result: {isError ? "Error" : "Success"}
- </h4>
- {structuredResult.content.map((item, index) => (
- <div key={index} className="mb-2">
- {item.type === "text" && (
- <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64">
- {item.text}
- </pre>
- )}
- {item.type === "image" && (
- <img
- src={`data:${item.mimeType};base64,${item.data}`}
- alt="Tool result image"
- className="max-w-full h-auto"
- />
- )}
- {item.type === "resource" && (
- <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words p-4 rounded text-sm overflow-auto max-h-64">
- {JSON.stringify(item.resource, null, 2)}
- </pre>
- )}
- </div>
- ))}
- </>
- );
- } else if ("toolResult" in toolResult) {
- return (
- <>
- <h4 className="font-semibold mb-2">Tool Result (Legacy):</h4>
- <pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64">
- {JSON.stringify(toolResult.toolResult, null, 2)}
- </pre>
- </>
- );
- }
- };
-
- return (
- <TabsContent value="tools" className="grid grid-cols-2 gap-4">
- <ListPane
- items={tools}
- listItems={listTools}
- clearItems={() => {
- clearTools();
- setSelectedTool(null);
- }}
- setSelectedItem={setSelectedTool}
- renderItem={(tool) => (
- <>
- <span className="flex-1">{tool.name}</span>
- <span className="text-sm text-gray-500 text-right">
- {tool.description}
- </span>
- </>
- )}
- title="Tools"
- buttonText={nextCursor ? "List More Tools" : "List Tools"}
- isButtonDisabled={!nextCursor && tools.length > 0}
- />
-
- <div className="bg-card rounded-lg shadow">
- <div className="p-4 border-b border-gray-200">
- <h3 className="font-semibold">
- {selectedTool ? selectedTool.name : "Select a tool"}
- </h3>
- </div>
- <div className="p-4">
- {error ? (
- <Alert variant="destructive">
- <AlertCircle className="h-4 w-4" />
- <AlertTitle>Error</AlertTitle>
- <AlertDescription>{error}</AlertDescription>
- </Alert>
- ) : selectedTool ? (
- <div className="space-y-4">
- <p className="text-sm text-gray-600">
- {selectedTool.description}
- </p>
- {Object.entries(selectedTool.inputSchema.properties ?? []).map(
- ([key, value]) => (
- <div key={key}>
- <Label
- htmlFor={key}
- className="block text-sm font-medium text-gray-700"
- >
- {key}
- </Label>
- {
- /* @ts-expect-error value type is currently unknown */
- value.type === "string" ? (
- <Textarea
- id={key}
- name={key}
- // @ts-expect-error value type is currently unknown
- placeholder={value.description}
- onChange={(e) =>
- setParams({
- ...params,
- [key]: e.target.value,
- })
- }
- className="mt-1"
- />
- ) :
- /* @ts-expect-error value type is currently unknown */
- value.type === "object" ? (
- <Textarea
- id={key}
- name={key}
- // @ts-expect-error value type is currently unknown
- placeholder={value.description}
- onChange={(e) => {
- try {
- const parsed = JSON.parse(e.target.value);
- setParams({
- ...params,
- [key]: parsed,
- });
- } catch (err) {
- // If invalid JSON, store as string - will be validated on submit
- setParams({
- ...params,
- [key]: e.target.value,
- });
- }
- }}
- className="mt-1"
- />
- ) : (
- <Input
- // @ts-expect-error value type is currently unknown
- type={value.type === "number" ? "number" : "text"}
- id={key}
- name={key}
- // @ts-expect-error value type is currently unknown
- placeholder={value.description}
- onChange={(e) =>
- setParams({
- ...params,
- [key]:
- // @ts-expect-error value type is currently unknown
- value.type === "number"
- ? Number(e.target.value)
- : e.target.value,
- })
- }
- className="mt-1"
- />
- )
- }
- </div>
- ),
- )}
- <Button onClick={() => callTool(selectedTool.name, params)}>
- <Send className="w-4 h-4 mr-2" />
- Run Tool
- </Button>
- {toolResult && renderToolResult()}
- </div>
- ) : (
- <Alert>
- <AlertDescription>
- Select a tool from the list to view its details and run it
- </AlertDescription>
- </Alert>
- )}
- </div>
- </div>
- </TabsContent>
- );
-};
-
-export default ToolsTab;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| ConsoleTab.tsx | -
-
- |
- 100% | -8/8 | -100% | -1/1 | -100% | -1/1 | -100% | -8/8 | -
| History.tsx | -
-
- |
- 97.74% | -130/133 | -100% | -19/19 | -55.55% | -5/9 | -97.74% | -130/133 | -
| ListPane.tsx | -
-
- |
- 100% | -43/43 | -100% | -3/3 | -100% | -2/2 | -100% | -43/43 | -
| PingTab.tsx | -
-
- |
- 100% | -16/16 | -100% | -1/1 | -100% | -1/1 | -100% | -16/16 | -
| PromptsTab.tsx | -
-
- |
- 97.11% | -101/104 | -94.11% | -16/17 | -83.33% | -5/6 | -97.11% | -101/104 | -
| ResourcesTab.tsx | -
-
- |
- 97.54% | -159/163 | -89.28% | -25/28 | -88.88% | -8/9 | -97.54% | -159/163 | -
| RootsTab.tsx | -
-
- |
- 100% | -61/61 | -100% | -15/15 | -100% | -7/7 | -100% | -61/61 | -
| SamplingTab.tsx | -
-
- |
- 100% | -41/41 | -100% | -6/6 | -100% | -4/4 | -100% | -41/41 | -
| Sidebar.tsx | -
-
- |
- 65.43% | -142/217 | -50% | -6/12 | -25% | -2/8 | -65.43% | -142/217 | -
| ToolsTab.tsx | -
-
- |
- 29.38% | -57/194 | -33.33% | -2/6 | -20% | -1/5 | -29.38% | -57/194 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 | 1x - - - - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x - - -1x -23x -23x -23x -23x -23x -23x -23x -1x - -1x - - -1x -2x -2x -2x -2x -2x -2x -1x - -1x - - -1x -23x -23x -23x -23x -23x -23x -1x - - - | import * as React from "react";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const alertVariants = cva(
- "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
- {
- variants: {
- variant: {
- default: "bg-background text-foreground",
- destructive:
- "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-const Alert = React.forwardRef<
- HTMLDivElement,
- React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
->(({ className, variant, ...props }, ref) => (
- <div
- ref={ref}
- role="alert"
- className={cn(alertVariants({ variant }), className)}
- {...props}
- />
-));
-Alert.displayName = "Alert";
-
-const AlertTitle = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLHeadingElement>
->(({ className, ...props }, ref) => (
- <h5
- ref={ref}
- className={cn("mb-1 font-medium leading-none tracking-tight", className)}
- {...props}
- />
-));
-AlertTitle.displayName = "AlertTitle";
-
-const AlertDescription = React.forwardRef<
- HTMLParagraphElement,
- React.HTMLAttributes<HTMLParagraphElement>
->(({ className, ...props }, ref) => (
- <div
- ref={ref}
- className={cn("text-sm [&_p]:leading-relaxed", className)}
- {...props}
- />
-));
-AlertDescription.displayName = "AlertDescription";
-
-export { Alert, AlertTitle, AlertDescription };
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 | 1x - - - - - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - - - - - - - -1x -1x -223x -223x -223x -223x -223x -223x -223x - -223x -1x -1x - - - | import * as React from "react";
-import { Slot } from "@radix-ui/react-slot";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const buttonVariants = cva(
- "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
- {
- variants: {
- variant: {
- default:
- "bg-primary text-primary-foreground shadow hover:bg-primary/90 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700",
- destructive:
- "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
- outline:
- "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
- secondary:
- "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
- ghost: "hover:bg-accent hover:text-accent-foreground",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-9 px-4 py-2",
- sm: "h-8 rounded-md px-3 text-xs",
- lg: "h-10 rounded-md px-8",
- icon: "h-9 w-9",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
-);
-
-export interface ButtonProps
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
- VariantProps<typeof buttonVariants> {
- asChild?: boolean;
-}
-
-const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
- ({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button";
- return (
- <Comp
- className={cn(buttonVariants({ variant, size, className }))}
- ref={ref}
- {...props}
- />
- );
- },
-);
-Button.displayName = "Button";
-
-export { Button, buttonVariants };
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| alert.tsx | -
-
- |
- 100% | -44/44 | -100% | -3/3 | -100% | -0/0 | -100% | -44/44 | -
| button.tsx | -
-
- |
- 100% | -42/42 | -50% | -1/2 | -100% | -0/0 | -100% | -42/42 | -
| input.tsx | -
-
- |
- 100% | -16/16 | -100% | -1/1 | -100% | -0/0 | -100% | -16/16 | -
| label.tsx | -
-
- |
- 100% | -13/13 | -100% | -1/1 | -100% | -0/0 | -100% | -13/13 | -
| select.tsx | -
-
- |
- 89.09% | -98/110 | -100% | -5/5 | -100% | -0/0 | -89.09% | -98/110 | -
| tabs.tsx | -
-
- |
- 100% | -38/38 | -100% | -3/3 | -100% | -0/0 | -100% | -38/38 | -
| textarea.tsx | -
-
- |
- 100% | -15/15 | -100% | -1/1 | -100% | -0/0 | -100% | -15/15 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 | 1x - - - - - - -1x -1x -58x -58x -58x -58x -58x -58x -58x -58x -58x -58x - -58x -1x -1x - - - | import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-export interface InputProps
- extends React.InputHTMLAttributes<HTMLInputElement> {}
-
-const Input = React.forwardRef<HTMLInputElement, InputProps>(
- ({ className, type, ...props }, ref) => {
- return (
- <input
- type={type}
- className={cn(
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
- className,
- )}
- ref={ref}
- {...props}
- />
- );
- },
-);
-Input.displayName = "Input";
-
-export { Input };
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 | 1x - - - - - -1x -1x -1x - -1x - - - -1x -8x -8x -8x -8x -8x -8x -1x - - - | import * as React from "react";
-import * as LabelPrimitive from "@radix-ui/react-label";
-import { cva, type VariantProps } from "class-variance-authority";
-
-import { cn } from "@/lib/utils";
-
-const labelVariants = cva(
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
-);
-
-const Label = React.forwardRef<
- React.ElementRef<typeof LabelPrimitive.Root>,
- React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
- VariantProps<typeof labelVariants>
->(({ className, ...props }, ref) => (
- <LabelPrimitive.Root
- ref={ref}
- className={cn(labelVariants(), className)}
- {...props}
- />
-));
-Label.displayName = LabelPrimitive.Root.displayName;
-
-export { Label };
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 | 1x - - - - - - - - - - -1x - -1x - -1x - -1x - - -1x -32x -32x -32x -32x -32x -32x -32x - -32x -32x -32x -32x -32x -32x -1x - -1x - - -1x -32x -32x -32x -32x -32x -32x -32x - -32x -32x -32x -1x - -1x - - -1x -32x -32x -32x -32x -32x -32x -32x - -32x -32x -32x -1x -1x - -1x - - -1x -32x -32x -32x -32x -32x -32x -32x -32x -32x -32x -32x - -32x -32x -32x -32x -32x -32x -32x - -32x -32x -32x -32x -32x -1x -1x - -1x - - -1x - - - - - - -1x - -1x - - -1x -80x -80x -80x -80x -80x -80x -80x - -80x -80x -80x -80x -80x -80x -80x -80x -1x - -1x - - -1x - - - - - - -1x - - - - - - - - - - - - - - | import * as React from "react";
-import {
- CaretSortIcon,
- CheckIcon,
- ChevronDownIcon,
- ChevronUpIcon,
-} from "@radix-ui/react-icons";
-import * as SelectPrimitive from "@radix-ui/react-select";
-
-import { cn } from "@/lib/utils";
-
-const Select = SelectPrimitive.Root;
-
-const SelectGroup = SelectPrimitive.Group;
-
-const SelectValue = SelectPrimitive.Value;
-
-const SelectTrigger = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Trigger>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
->(({ className, children, ...props }, ref) => (
- <SelectPrimitive.Trigger
- ref={ref}
- className={cn(
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
- className,
- )}
- {...props}
- >
- {children}
- <SelectPrimitive.Icon asChild>
- <CaretSortIcon className="h-4 w-4 opacity-50" />
- </SelectPrimitive.Icon>
- </SelectPrimitive.Trigger>
-));
-SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
-
-const SelectScrollUpButton = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
->(({ className, ...props }, ref) => (
- <SelectPrimitive.ScrollUpButton
- ref={ref}
- className={cn(
- "flex cursor-default items-center justify-center py-1",
- className,
- )}
- {...props}
- >
- <ChevronUpIcon />
- </SelectPrimitive.ScrollUpButton>
-));
-SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
-
-const SelectScrollDownButton = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
->(({ className, ...props }, ref) => (
- <SelectPrimitive.ScrollDownButton
- ref={ref}
- className={cn(
- "flex cursor-default items-center justify-center py-1",
- className,
- )}
- {...props}
- >
- <ChevronDownIcon />
- </SelectPrimitive.ScrollDownButton>
-));
-SelectScrollDownButton.displayName =
- SelectPrimitive.ScrollDownButton.displayName;
-
-const SelectContent = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
->(({ className, children, position = "popper", ...props }, ref) => (
- <SelectPrimitive.Portal>
- <SelectPrimitive.Content
- ref={ref}
- className={cn(
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
- position === "popper" &&
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
- className,
- )}
- position={position}
- {...props}
- >
- <SelectScrollUpButton />
- <SelectPrimitive.Viewport
- className={cn(
- "p-1",
- position === "popper" &&
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
- )}
- >
- {children}
- </SelectPrimitive.Viewport>
- <SelectScrollDownButton />
- </SelectPrimitive.Content>
- </SelectPrimitive.Portal>
-));
-SelectContent.displayName = SelectPrimitive.Content.displayName;
-
-const SelectLabel = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Label>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
->(({ className, ...props }, ref) => (
- <SelectPrimitive.Label
- ref={ref}
- className={cn("px-2 py-1.5 text-sm font-semibold", className)}
- {...props}
- />
-));
-SelectLabel.displayName = SelectPrimitive.Label.displayName;
-
-const SelectItem = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Item>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
->(({ className, children, ...props }, ref) => (
- <SelectPrimitive.Item
- ref={ref}
- className={cn(
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
- className,
- )}
- {...props}
- >
- <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
- <SelectPrimitive.ItemIndicator>
- <CheckIcon className="h-4 w-4" />
- </SelectPrimitive.ItemIndicator>
- </span>
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
- </SelectPrimitive.Item>
-));
-SelectItem.displayName = SelectPrimitive.Item.displayName;
-
-const SelectSeparator = React.forwardRef<
- React.ElementRef<typeof SelectPrimitive.Separator>,
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
->(({ className, ...props }, ref) => (
- <SelectPrimitive.Separator
- ref={ref}
- className={cn("-mx-1 my-1 h-px bg-muted", className)}
- {...props}
- />
-));
-SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
-
-export {
- Select,
- SelectGroup,
- SelectValue,
- SelectTrigger,
- SelectContent,
- SelectLabel,
- SelectItem,
- SelectSeparator,
- SelectScrollUpButton,
- SelectScrollDownButton,
-};
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 | 1x - - - - -1x - -1x - - -1x -9x -9x -9x -9x -9x -9x -9x -9x -9x -1x - -1x - - -1x -54x -54x -54x -54x -54x -54x -54x -54x -54x -1x - -1x - - -1x -64x -64x -64x -64x -64x -64x -64x -64x -64x -1x - - - | import * as React from "react";
-import * as TabsPrimitive from "@radix-ui/react-tabs";
-
-import { cn } from "@/lib/utils";
-
-const Tabs = TabsPrimitive.Root;
-
-const TabsList = React.forwardRef<
- React.ElementRef<typeof TabsPrimitive.List>,
- React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
->(({ className, ...props }, ref) => (
- <TabsPrimitive.List
- ref={ref}
- className={cn(
- "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
- className,
- )}
- {...props}
- />
-));
-TabsList.displayName = TabsPrimitive.List.displayName;
-
-const TabsTrigger = React.forwardRef<
- React.ElementRef<typeof TabsPrimitive.Trigger>,
- React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
->(({ className, ...props }, ref) => (
- <TabsPrimitive.Trigger
- ref={ref}
- className={cn(
- "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-muted data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
- className,
- )}
- {...props}
- />
-));
-TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
-
-const TabsContent = React.forwardRef<
- React.ElementRef<typeof TabsPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
->(({ className, ...props }, ref) => (
- <TabsPrimitive.Content
- ref={ref}
- className={cn(
- "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
- className,
- )}
- {...props}
- />
-));
-TabsContent.displayName = TabsPrimitive.Content.displayName;
-
-export { Tabs, TabsList, TabsTrigger, TabsContent };
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 | 1x - - - - - - -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x - -1x -1x -1x - - - | import * as React from "react";
-
-import { cn } from "@/lib/utils";
-
-export interface TextareaProps
- extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
-
-const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
- ({ className, ...props }, ref) => {
- return (
- <textarea
- className={cn(
- "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
- className,
- )}
- ref={ref}
- {...props}
- />
- );
- },
-);
-Textarea.displayName = "Textarea";
-
-export { Textarea };
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| useConnection.ts | -
-
- |
- 0% | -0/136 | -0% | -0/1 | -0% | -0/1 | -0% | -0/136 | -
| useDraggablePane.ts | -
-
- |
- 0% | -0/38 | -0% | -0/1 | -0% | -0/1 | -0% | -0/38 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 -100 -101 -102 -103 -104 -105 -106 -107 -108 -109 -110 -111 -112 -113 -114 -115 -116 -117 -118 -119 -120 -121 -122 -123 -124 -125 -126 -127 -128 -129 -130 -131 -132 -133 -134 -135 -136 -137 -138 -139 -140 -141 -142 -143 -144 -145 -146 -147 -148 -149 -150 -151 -152 -153 -154 -155 -156 -157 -158 -159 -160 -161 -162 -163 -164 -165 -166 -167 -168 -169 -170 -171 -172 -173 -174 -175 -176 -177 -178 -179 -180 -181 -182 -183 -184 -185 -186 -187 -188 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; -import { - ClientNotification, - ClientRequest, - CreateMessageRequestSchema, - ListRootsRequestSchema, - ProgressNotificationSchema, - Request, - Result, - ServerCapabilities, -} from "@modelcontextprotocol/sdk/types.js"; -import { useState } from "react"; -import { toast } from "react-toastify"; -import { Notification, StdErrNotificationSchema } from "../notificationTypes"; -import { z } from "zod"; - -const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000; - -interface UseConnectionOptions { - transportType: "stdio" | "sse"; - command: string; - args: string; - sseUrl: string; - env: Record<string, string>; - proxyServerUrl: string; - requestTimeout?: number; - onNotification?: (notification: Notification) => void; - onStdErrNotification?: (notification: Notification) => void; - onPendingRequest?: (request: any, resolve: any, reject: any) => void; - getRoots?: () => any[]; -} - -export function useConnection({ - transportType, - command, - args, - sseUrl, - env, - proxyServerUrl, - requestTimeout = DEFAULT_REQUEST_TIMEOUT_MSEC, - onNotification, - onStdErrNotification, - onPendingRequest, - getRoots, -}: UseConnectionOptions) { - const [connectionStatus, setConnectionStatus] = useState<"disconnected" | "connected" | "error">("disconnected"); - const [serverCapabilities, setServerCapabilities] = useState<ServerCapabilities | null>(null); - const [mcpClient, setMcpClient] = useState<Client | null>(null); - const [requestHistory, setRequestHistory] = useState<{ request: string; response?: string }[]>([]); - - const pushHistory = (request: object, response?: object) => { - setRequestHistory((prev) => [ - ...prev, - { - request: JSON.stringify(request), - response: response !== undefined ? JSON.stringify(response) : undefined, - }, - ]); - }; - - const makeRequest = async <T extends z.ZodType>( - request: ClientRequest, - schema: T - ) => { - if (!mcpClient) { - throw new Error("MCP client not connected"); - } - - try { - const abortController = new AbortController(); - const timeoutId = setTimeout(() => { - abortController.abort("Request timed out"); - }, requestTimeout); - - let response; - try { - response = await mcpClient.request(request, schema, { - signal: abortController.signal, - }); - pushHistory(request, response); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - pushHistory(request, { error: errorMessage }); - throw error; - } finally { - clearTimeout(timeoutId); - } - - - return response; - } catch (e: unknown) { - const errorString = (e as Error).message ?? String(e); - toast.error(errorString); - - throw e; - } - }; - - const sendNotification = async (notification: ClientNotification) => { - if (!mcpClient) { - throw new Error("MCP client not connected"); - } - - try { - await mcpClient.notification(notification); - pushHistory(notification); - } catch (e: unknown) { - toast.error((e as Error).message ?? String(e)); - throw e; - } - }; - - const connect = async () => { - try { - const client = new Client<Request, Notification, Result>( - { - name: "mcp-inspector", - version: "0.0.1", - }, - { - capabilities: { - sampling: {}, - roots: { - listChanged: true, - }, - }, - }, - ); - - const backendUrl = new URL(`${proxyServerUrl}/sse`); - - backendUrl.searchParams.append("transportType", transportType); - if (transportType === "stdio") { - backendUrl.searchParams.append("command", command); - backendUrl.searchParams.append("args", args); - backendUrl.searchParams.append("env", JSON.stringify(env)); - } else { - backendUrl.searchParams.append("url", sseUrl); - } - - const clientTransport = new SSEClientTransport(backendUrl); - - if (onNotification) { - client.setNotificationHandler(ProgressNotificationSchema, onNotification); - } - - if (onStdErrNotification) { - client.setNotificationHandler(StdErrNotificationSchema, onStdErrNotification); - } - - await client.connect(clientTransport); - - const capabilities = client.getServerCapabilities(); - setServerCapabilities(capabilities ?? null); - - if (onPendingRequest) { - client.setRequestHandler(CreateMessageRequestSchema, (request) => { - return new Promise((resolve, reject) => { - onPendingRequest(request, resolve, reject); - }); - }); - } - - if (getRoots) { - client.setRequestHandler(ListRootsRequestSchema, async () => { - return { roots: getRoots() }; - }); - } - - setMcpClient(client); - setConnectionStatus("connected"); - } catch (e) { - console.error(e); - setConnectionStatus("error"); - } - }; - - return { - connectionStatus, - serverCapabilities, - mcpClient, - requestHistory, - makeRequest, - sendNotification, - connect - }; -} |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | import { useCallback, useEffect, useRef, useState } from "react"; - -export function useDraggablePane(initialHeight: number) { - const [height, setHeight] = useState(initialHeight); - const [isDragging, setIsDragging] = useState(false); - const dragStartY = useRef<number>(0); - const dragStartHeight = useRef<number>(0); - - const handleDragStart = useCallback((e: React.MouseEvent) => { - setIsDragging(true); - dragStartY.current = e.clientY; - dragStartHeight.current = height; - document.body.style.userSelect = "none"; - }, [height]); - - const handleDragMove = useCallback((e: MouseEvent) => { - if (!isDragging) return; - const deltaY = dragStartY.current - e.clientY; - const newHeight = Math.max(100, Math.min(800, dragStartHeight.current + deltaY)); - setHeight(newHeight); - }, [isDragging]); - - const handleDragEnd = useCallback(() => { - setIsDragging(false); - document.body.style.userSelect = ""; - }, []); - - useEffect(() => { - if (isDragging) { - window.addEventListener("mousemove", handleDragMove); - window.addEventListener("mouseup", handleDragEnd); - return () => { - window.removeEventListener("mousemove", handleDragMove); - window.removeEventListener("mouseup", handleDragEnd); - }; - } - }, [isDragging, handleDragMove, handleDragEnd]); - - return { - height, - isDragging, - handleDragStart - }; -} |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| notificationTypes.ts | -
-
- |
- 0% | -0/10 | -0% | -0/1 | -0% | -0/1 | -0% | -0/10 | -
| useTheme.ts | -
-
- |
- 72.5% | -29/40 | -71.42% | -5/7 | -66.66% | -2/3 | -72.5% | -29/40 | -
| utils.ts | -
-
- |
- 100% | -4/4 | -100% | -1/1 | -100% | -1/1 | -100% | -4/4 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 | - - - - - - - - - - - - - - - - - - - | import { - NotificationSchema as BaseNotificationSchema, - ClientNotificationSchema, -} from "@modelcontextprotocol/sdk/types.js"; -import { z } from "zod"; - -export const StdErrNotificationSchema = BaseNotificationSchema.extend({ - method: z.literal("notifications/stderr"), - params: z.object({ - content: z.string(), - }), -}); - -export const NotificationSchema = ClientNotificationSchema.or( - StdErrNotificationSchema, -); - -export type StdErrNotification = z.infer<typeof StdErrNotificationSchema>; -export type Notification = z.infer<typeof NotificationSchema>; - |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 | 1x - - - -1x -16x -7x -7x -16x - -16x -7x -7x -7x -7x - - - - - -7x -7x -7x - - -7x -7x -7x - - - -7x - -7x -7x -7x -16x - -16x -16x -16x - - - - - -16x -16x -16x - -1x - | import { useCallback, useEffect, useState } from "react";
-
-type Theme = "light" | "dark" | "system";
-
-const useTheme = (): [Theme, (mode: Theme) => void] => {
- const [theme, setTheme] = useState<Theme>(() => {
- const savedTheme = localStorage.getItem("theme") as Theme;
- return savedTheme || "system";
- });
-
- useEffect(() => {
- const darkModeMediaQuery = window.matchMedia(
- "(prefers-color-scheme: dark)",
- );
- const handleDarkModeChange = (e: MediaQueryListEvent) => {
- if (theme === "system") {
- updateDocumentTheme(e.matches ? "dark" : "light");
- }
- };
-
- const updateDocumentTheme = (newTheme: "light" | "dark") => {
- document.documentElement.classList.toggle("dark", newTheme === "dark");
- };
-
- // Set initial theme based on current mode
- if (theme === "system") {
- updateDocumentTheme(darkModeMediaQuery.matches ? "dark" : "light");
- } else {
- updateDocumentTheme(theme);
- }
-
- darkModeMediaQuery.addEventListener("change", handleDarkModeChange);
-
- return () => {
- darkModeMediaQuery.removeEventListener("change", handleDarkModeChange);
- };
- }, [theme]);
-
- return [
- theme,
- useCallback((newTheme: Theme) => {
- setTheme(newTheme);
- localStorage.setItem("theme", newTheme);
- if (newTheme !== "system") {
- document.documentElement.classList.toggle("dark", newTheme === "dark");
- }
- }, []),
- ];
-};
-
-export default useTheme;
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 | 1x - - -1x -705x -705x - | import { clsx, type ClassValue } from "clsx";
-import { twMerge } from "tailwind-merge";
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
- |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 | - - - - - - - - - - - - - | import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import { ToastContainer } from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.css'; -import App from "./App.tsx"; -import "./index.css"; - -createRoot(document.getElementById("root")!).render( - <StrictMode> - <App /> - <ToastContainer /> - </StrictMode>, -); - |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | /** @type {import('tailwindcss').Config} */ -import animate from "tailwindcss-animate"; -export default { - darkMode: ["class"], - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - theme: { - extend: { - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - colors: { - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - chart: { - 1: "hsl(var(--chart-1))", - 2: "hsl(var(--chart-2))", - 3: "hsl(var(--chart-3))", - 4: "hsl(var(--chart-4))", - 5: "hsl(var(--chart-5))", - }, - }, - }, - }, - plugins: [animate], -}; - |
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| client | -
-
- |
- 0% | -0/64 | -0% | -0/2 | -0% | -0/2 | -0% | -0/64 | -
| client/bin | -
-
- |
- 0% | -0/16 | -0% | -0/1 | -0% | -0/1 | -0% | -0/16 | -
| client/src | -
-
- |
- 59.17% | -274/463 | -86.2% | -25/29 | -5.88% | -2/34 | -59.17% | -274/463 | -
| client/src/components | -
-
- |
- 77.34% | -758/980 | -87.03% | -94/108 | -69.23% | -36/52 | -77.34% | -758/980 | -
| client/src/components/ui | -
-
- |
- 95.68% | -266/278 | -93.75% | -15/16 | -100% | -0/0 | -95.68% | -266/278 | -
| client/src/lib | -
-
- |
- 61.11% | -33/54 | -66.66% | -6/9 | -60% | -3/5 | -61.11% | -33/54 | -
| client/src/lib/hooks | -
-
- |
- 0% | -0/174 | -0% | -0/2 | -0% | -0/2 | -0% | -0/174 | -