Compare commits

...

8 Commits

3 changed files with 100 additions and 77 deletions

View File

@@ -1,3 +1,9 @@
# Original Repo: [https://github.com/modelcontextprotocol/inspector](https://github.com/modelcontextprotocol/inspector)
## Modification Log
- Containerize
- Support custom headers for sse transport
# MCP Inspector # MCP Inspector
The MCP inspector is a developer tool for testing and debugging MCP servers. The MCP inspector is a developer tool for testing and debugging MCP servers.

View File

@@ -362,7 +362,64 @@ const Sidebar = ({
</div> </div>
</> </>
)} )}
{transportType === "sse" && (
<div className="space-y-2">
<Button
variant="outline"
onClick={() => setShowCustomHeaders(!showCustomHeaders)}
className="flex items-center w-full"
data-testid="custom-headers-button"
aria-expanded={showCustomHeaders}
>
{showCustomHeaders ? (
<ChevronDown className="w-4 h-4 mr-2" />
) : (
<ChevronRight className="w-4 h-4 mr-2" />
)}
Custom Headers
</Button>
{showCustomHeaders && (
<div className="space-y-2">
{customHeaders.map((header, index) => (
<div key={index} className="space-y-2">
<label className="text-sm font-medium">Header Name</label>
<Input
placeholder="Header Name"
value={header[0]}
onChange={(e) => updateCustomHeader(index, 'key', e.target.value)}
className="font-mono"
/>
<label className="text-sm font-medium">Header Value</label>
<Input
placeholder="Header Value"
value={header[1]}
onChange={(e) => updateCustomHeader(index, 'value', e.target.value)}
className="font-mono"
/>
<Button
variant="destructive"
size="sm"
onClick={() => removeCustomHeader(index)}
className="w-full"
>
Remove Header
</Button>
</div>
))}
<Button
variant="outline"
className="w-full mt-2"
onClick={() => {
setCustomHeaders([ ...customHeaders, ["", ""]]);
}}
>
Add Custom Header
</Button>
</div>
)}
</div>
)}
{transportType === "stdio" && ( {transportType === "stdio" && (
<div className="space-y-2"> <div className="space-y-2">
<Button <Button
@@ -743,61 +800,7 @@ const Sidebar = ({
)} )}
</div> </div>
<div className="space-y-2">
<Button
variant="outline"
onClick={() => setShowCustomHeaders(!showCustomHeaders)}
className="flex items-center w-full"
data-testid="custom-headers-button"
aria-expanded={showCustomHeaders}
>
{showCustomHeaders ? (
<ChevronDown className="w-4 h-4 mr-2" />
) : (
<ChevronRight className="w-4 h-4 mr-2" />
)}
Custom Headers
</Button>
{showCustomHeaders && (
<div className="space-y-2">
{customHeaders.map((header, index) => (
<div key={index} className="space-y-2">
<label className="text-sm font-medium">Header Name</label>
<Input
placeholder="Header Name"
value={header[0]}
onChange={(e) => updateCustomHeader(index, 'key', e.target.value)}
className="font-mono"
/>
<label className="text-sm font-medium">Header Value</label>
<Input
placeholder="Header Value"
value={header[1]}
onChange={(e) => updateCustomHeader(index, 'value', e.target.value)}
className="font-mono"
/>
<Button
variant="destructive"
size="sm"
onClick={() => removeCustomHeader(index)}
className="w-full"
>
Remove Header
</Button>
</div>
))}
<Button
variant="outline"
className="w-full mt-2"
onClick={() => {
setCustomHeaders([ ...customHeaders, ["", ""]]);
}}
>
Add Custom Header
</Button>
</div>
)}
</div>
</div> </div>
</div> </div>
<div className="p-4 border-t"> <div className="p-4 border-t">

View File

@@ -41,6 +41,7 @@ const ToolsTab = ({
}) => { }) => {
const [params, setParams] = useState<Record<string, unknown>>({}); const [params, setParams] = useState<Record<string, unknown>>({});
const [isToolRunning, setIsToolRunning] = useState(false); const [isToolRunning, setIsToolRunning] = useState(false);
const [filterText, setFilterText] = useState("");
useEffect(() => { useEffect(() => {
const params = Object.entries( const params = Object.entries(
@@ -52,6 +53,12 @@ const ToolsTab = ({
setParams(Object.fromEntries(params)); setParams(Object.fromEntries(params));
}, [selectedTool]); }, [selectedTool]);
const filteredTools = filterText
? tools.filter((tool) =>
tool.name.toLowerCase().includes(filterText.toLowerCase())
)
: tools;
const renderToolResult = () => { const renderToolResult = () => {
if (!toolResult) return null; if (!toolResult) return null;
@@ -124,27 +131,34 @@ const ToolsTab = ({
return ( return (
<TabsContent value="tools"> <TabsContent value="tools">
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<ListPane <div className="space-y-2">
items={tools} <Input
listItems={listTools} placeholder="Filter tools by name..."
clearItems={() => { value={filterText}
clearTools(); onChange={(e) => setFilterText(e.target.value)}
setSelectedTool(null); className="w-full"
}} />
setSelectedItem={setSelectedTool} <ListPane
renderItem={(tool) => ( items={filteredTools}
<div className="flex flex-col items-start"> listItems={listTools}
<span className="flex-1">{tool.name}</span> clearItems={() => {
<span className="text-sm text-gray-500 text-left"> clearTools();
{tool.description} setSelectedTool(null);
</span> }}
</div> setSelectedItem={setSelectedTool}
)} renderItem={(tool) => (
title="Tools" <div className="flex flex-col items-start">
buttonText={nextCursor ? "List More Tools" : "List Tools"} <span className="flex-1">{tool.name}</span>
isButtonDisabled={!nextCursor && tools.length > 0} <span className="text-sm text-gray-500 text-left">
/> {tool.description}
</span>
</div>
)}
title="Tools"
buttonText={nextCursor ? "List More Tools" : "List Tools"}
isButtonDisabled={!nextCursor && tools.length > 0}
/>
</div>
<div className="bg-card rounded-lg shadow"> <div className="bg-card rounded-lg shadow">
<div className="p-4 border-b border-gray-200 dark:border-gray-800"> <div className="p-4 border-b border-gray-200 dark:border-gray-800">
<h3 className="font-semibold"> <h3 className="font-semibold">
@@ -301,4 +315,4 @@ const ToolsTab = ({
); );
}; };
export default ToolsTab; export default ToolsTab;