Merge branch 'main' into crondinini/support-client-secret
This commit is contained in:
commit
982e10d71c
6 changed files with 146 additions and 38 deletions
33
AGENTS.md
Normal file
33
AGENTS.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# MCP Inspector Development Guide
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
|
- Build all: `npm run build`
|
||||||
|
- Build client: `npm run build-client`
|
||||||
|
- Build server: `npm run build-server`
|
||||||
|
- Development mode: `npm run dev` (use `npm run dev:windows` on Windows)
|
||||||
|
- Format code: `npm run prettier-fix`
|
||||||
|
- Client lint: `cd client && npm run lint`
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
- Use TypeScript with proper type annotations
|
||||||
|
- Follow React functional component patterns with hooks
|
||||||
|
- Use ES modules (import/export) not CommonJS
|
||||||
|
- Use Prettier for formatting (auto-formatted on commit)
|
||||||
|
- Follow existing naming conventions:
|
||||||
|
- camelCase for variables and functions
|
||||||
|
- PascalCase for component names and types
|
||||||
|
- kebab-case for file names
|
||||||
|
- Use async/await for asynchronous operations
|
||||||
|
- Implement proper error handling with try/catch blocks
|
||||||
|
- Use Tailwind CSS for styling in the client
|
||||||
|
- Keep components small and focused on a single responsibility
|
||||||
|
|
||||||
|
## Project Organization
|
||||||
|
|
||||||
|
The project is organized as a monorepo with workspaces:
|
||||||
|
|
||||||
|
- `client/`: React frontend with Vite, TypeScript and Tailwind
|
||||||
|
- `server/`: Express backend with TypeScript
|
||||||
|
- `cli/`: Command-line interface for testing and invoking MCP server methods directly
|
||||||
34
CLAUDE.md
34
CLAUDE.md
|
|
@ -1,33 +1 @@
|
||||||
# MCP Inspector Development Guide
|
@./AGENTS.md
|
||||||
|
|
||||||
## Build Commands
|
|
||||||
|
|
||||||
- Build all: `npm run build`
|
|
||||||
- Build client: `npm run build-client`
|
|
||||||
- Build server: `npm run build-server`
|
|
||||||
- Development mode: `npm run dev` (use `npm run dev:windows` on Windows)
|
|
||||||
- Format code: `npm run prettier-fix`
|
|
||||||
- Client lint: `cd client && npm run lint`
|
|
||||||
|
|
||||||
## Code Style Guidelines
|
|
||||||
|
|
||||||
- Use TypeScript with proper type annotations
|
|
||||||
- Follow React functional component patterns with hooks
|
|
||||||
- Use ES modules (import/export) not CommonJS
|
|
||||||
- Use Prettier for formatting (auto-formatted on commit)
|
|
||||||
- Follow existing naming conventions:
|
|
||||||
- camelCase for variables and functions
|
|
||||||
- PascalCase for component names and types
|
|
||||||
- kebab-case for file names
|
|
||||||
- Use async/await for asynchronous operations
|
|
||||||
- Implement proper error handling with try/catch blocks
|
|
||||||
- Use Tailwind CSS for styling in the client
|
|
||||||
- Keep components small and focused on a single responsibility
|
|
||||||
|
|
||||||
## Project Organization
|
|
||||||
|
|
||||||
The project is organized as a monorepo with workspaces:
|
|
||||||
|
|
||||||
- `client/`: React frontend with Vite, TypeScript and Tailwind
|
|
||||||
- `server/`: Express backend with TypeScript
|
|
||||||
- `cli/`: Command-line interface for testing and invoking MCP server methods directly
|
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,9 @@ const PromptsTab = ({
|
||||||
<Alert variant="destructive">
|
<Alert variant="destructive">
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-4 w-4" />
|
||||||
<AlertTitle>Error</AlertTitle>
|
<AlertTitle>Error</AlertTitle>
|
||||||
<AlertDescription>{error}</AlertDescription>
|
<AlertDescription className="break-all">
|
||||||
|
{error}
|
||||||
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
) : selectedPrompt ? (
|
) : selectedPrompt ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,9 @@ const ResourcesTab = ({
|
||||||
<Alert variant="destructive">
|
<Alert variant="destructive">
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-4 w-4" />
|
||||||
<AlertTitle>Error</AlertTitle>
|
<AlertTitle>Error</AlertTitle>
|
||||||
<AlertDescription>{error}</AlertDescription>
|
<AlertDescription className="break-all">
|
||||||
|
{error}
|
||||||
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
) : selectedResource ? (
|
) : selectedResource ? (
|
||||||
<JsonView
|
<JsonView
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,13 @@ import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import DynamicJsonForm, { DynamicJsonFormRef } from "./DynamicJsonForm";
|
import DynamicJsonForm, { DynamicJsonFormRef } from "./DynamicJsonForm";
|
||||||
import type { JsonValue, JsonSchemaType } from "@/utils/jsonUtils";
|
import type { JsonValue, JsonSchemaType } from "@/utils/jsonUtils";
|
||||||
import {
|
import {
|
||||||
|
|
@ -114,7 +121,7 @@ const ToolsTab = ({
|
||||||
renderItem={(tool) => (
|
renderItem={(tool) => (
|
||||||
<div className="flex flex-col items-start">
|
<div className="flex flex-col items-start">
|
||||||
<span className="flex-1">{tool.name}</span>
|
<span className="flex-1">{tool.name}</span>
|
||||||
<span className="text-sm text-gray-500 text-left">
|
<span className="text-sm text-gray-500 text-left line-clamp-3">
|
||||||
{tool.description}
|
{tool.description}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -137,10 +144,12 @@ const ToolsTab = ({
|
||||||
<Alert variant="destructive">
|
<Alert variant="destructive">
|
||||||
<AlertCircle className="h-4 w-4" />
|
<AlertCircle className="h-4 w-4" />
|
||||||
<AlertTitle>Error</AlertTitle>
|
<AlertTitle>Error</AlertTitle>
|
||||||
<AlertDescription>{error}</AlertDescription>
|
<AlertDescription className="break-all">
|
||||||
|
{error}
|
||||||
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
<p className="text-sm text-gray-600 dark:text-gray-400 whitespace-pre-wrap max-h-48 overflow-y-auto">
|
||||||
{selectedTool.description}
|
{selectedTool.description}
|
||||||
</p>
|
</p>
|
||||||
{Object.entries(selectedTool.inputSchema.properties ?? []).map(
|
{Object.entries(selectedTool.inputSchema.properties ?? []).map(
|
||||||
|
|
@ -180,6 +189,42 @@ const ToolsTab = ({
|
||||||
{prop.description || "Toggle this option"}
|
{prop.description || "Toggle this option"}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
) : prop.type === "string" && prop.enum ? (
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
params[key] === undefined
|
||||||
|
? ""
|
||||||
|
: String(params[key])
|
||||||
|
}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
if (value === "") {
|
||||||
|
setParams({
|
||||||
|
...params,
|
||||||
|
[key]: undefined,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setParams({
|
||||||
|
...params,
|
||||||
|
[key]: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger id={key} className="mt-1">
|
||||||
|
<SelectValue
|
||||||
|
placeholder={
|
||||||
|
prop.description || "Select an option"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{prop.enum.map((option) => (
|
||||||
|
<SelectItem key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
) : prop.type === "string" ? (
|
) : prop.type === "string" ? (
|
||||||
<Textarea
|
<Textarea
|
||||||
id={key}
|
id={key}
|
||||||
|
|
|
||||||
|
|
@ -638,6 +638,64 @@ describe("ToolsTab", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Enum Parameters", () => {
|
||||||
|
const toolWithEnumParam: Tool = {
|
||||||
|
name: "enumTool",
|
||||||
|
description: "Tool with enum parameter",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
format: {
|
||||||
|
type: "string" as const,
|
||||||
|
enum: ["json", "xml", "csv", "yaml"],
|
||||||
|
description: "Output format",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock scrollIntoView for Radix UI Select
|
||||||
|
Element.prototype.scrollIntoView = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render enum parameter as dropdown", () => {
|
||||||
|
renderToolsTab({
|
||||||
|
tools: [toolWithEnumParam],
|
||||||
|
selectedTool: toolWithEnumParam,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should render a select button instead of textarea
|
||||||
|
const selectTrigger = screen.getByRole("combobox", { name: /format/i });
|
||||||
|
expect(selectTrigger).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render non-enum string parameter as textarea", () => {
|
||||||
|
const toolWithStringParam: Tool = {
|
||||||
|
name: "stringTool",
|
||||||
|
description: "Tool with regular string parameter",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
text: {
|
||||||
|
type: "string" as const,
|
||||||
|
description: "Some text input",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
renderToolsTab({
|
||||||
|
tools: [toolWithStringParam],
|
||||||
|
selectedTool: toolWithStringParam,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should render textarea, not select
|
||||||
|
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("textbox")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("JSON Validation Integration", () => {
|
describe("JSON Validation Integration", () => {
|
||||||
const toolWithJsonParams: Tool = {
|
const toolWithJsonParams: Tool = {
|
||||||
name: "jsonTool",
|
name: "jsonTool",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue