mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
feat(mcp): add tool_name_to_display_name and tool_name_to_description overrides for MCP servers
This commit is contained in:
parent
09e1a06f47
commit
0b4c88d1a4
11 changed files with 279 additions and 30 deletions
|
|
@ -60,6 +60,12 @@ def _prepare_mcp_server_data(
|
|||
if data.env is not None:
|
||||
data_dict["env"] = safe_dumps(data.env)
|
||||
|
||||
# Handle tool name override serialization
|
||||
if data.tool_name_to_display_name is not None:
|
||||
data_dict["tool_name_to_display_name"] = safe_dumps(data.tool_name_to_display_name)
|
||||
if data.tool_name_to_description is not None:
|
||||
data_dict["tool_name_to_description"] = safe_dumps(data.tool_name_to_description)
|
||||
|
||||
# mcp_access_groups is already List[str], no serialization needed
|
||||
|
||||
return data_dict
|
||||
|
|
|
|||
|
|
@ -644,6 +644,12 @@ class MCPServerManager:
|
|||
),
|
||||
created_at=getattr(mcp_server, "created_at", None),
|
||||
updated_at=getattr(mcp_server, "updated_at", None),
|
||||
tool_name_to_display_name=_deserialize_json_dict(
|
||||
getattr(mcp_server, "tool_name_to_display_name", None)
|
||||
),
|
||||
tool_name_to_description=_deserialize_json_dict(
|
||||
getattr(mcp_server, "tool_name_to_description", None)
|
||||
),
|
||||
)
|
||||
return new_server
|
||||
|
||||
|
|
|
|||
|
|
@ -737,6 +737,29 @@ if MCP_AVAILABLE:
|
|||
|
||||
return tools_to_return
|
||||
|
||||
def apply_tool_overrides(
|
||||
tools: List[MCPTool],
|
||||
mcp_server: MCPServer,
|
||||
) -> List[MCPTool]:
|
||||
"""Apply admin-configured display name/description overrides to tools.
|
||||
|
||||
Overrides are keyed by the unprefixed tool name, same convention as
|
||||
allowed_tools configuration.
|
||||
"""
|
||||
display_name_map = mcp_server.tool_name_to_display_name or {}
|
||||
description_map = mcp_server.tool_name_to_description or {}
|
||||
if not display_name_map and not description_map:
|
||||
return tools
|
||||
|
||||
for tool in tools:
|
||||
unprefixed, _ = split_server_prefix_from_name(tool.name)
|
||||
lookup_key = unprefixed or tool.name
|
||||
if lookup_key in display_name_map:
|
||||
tool.name = display_name_map[lookup_key]
|
||||
if lookup_key in description_map:
|
||||
tool.description = description_map[lookup_key]
|
||||
return tools
|
||||
|
||||
def _get_client_ip_from_context() -> Optional[str]:
|
||||
"""
|
||||
Extract client_ip from auth context.
|
||||
|
|
@ -984,6 +1007,7 @@ if MCP_AVAILABLE:
|
|||
raw_headers=raw_headers,
|
||||
)
|
||||
filtered_tools = filter_tools_by_allowed_tools(tools, server)
|
||||
filtered_tools = apply_tool_overrides(filtered_tools, server)
|
||||
|
||||
filtered_tools = await filter_tools_by_key_team_permissions(
|
||||
tools=filtered_tools,
|
||||
|
|
|
|||
|
|
@ -1090,6 +1090,8 @@ class NewMCPServerRequest(LiteLLMPydanticObjectBase):
|
|||
mcp_info: Optional[MCPInfo] = None
|
||||
mcp_access_groups: List[str] = Field(default_factory=list)
|
||||
allowed_tools: Optional[List[str]] = None
|
||||
tool_name_to_display_name: Optional[Dict[str, str]] = None
|
||||
tool_name_to_description: Optional[Dict[str, str]] = None
|
||||
extra_headers: Optional[List[str]] = None
|
||||
static_headers: Optional[Dict[str, str]] = None
|
||||
# Stdio-specific fields
|
||||
|
|
@ -1144,6 +1146,8 @@ class UpdateMCPServerRequest(LiteLLMPydanticObjectBase):
|
|||
mcp_info: Optional[MCPInfo] = None
|
||||
mcp_access_groups: List[str] = Field(default_factory=list)
|
||||
allowed_tools: Optional[List[str]] = None
|
||||
tool_name_to_display_name: Optional[Dict[str, str]] = None
|
||||
tool_name_to_description: Optional[Dict[str, str]] = None
|
||||
extra_headers: Optional[List[str]] = None
|
||||
static_headers: Optional[Dict[str, str]] = None
|
||||
# Stdio-specific fields
|
||||
|
|
@ -1193,6 +1197,8 @@ class LiteLLM_MCPServerTable(LiteLLMPydanticObjectBase):
|
|||
teams: List[Dict[str, Optional[str]]] = Field(default_factory=list)
|
||||
mcp_access_groups: List[str] = Field(default_factory=list)
|
||||
allowed_tools: List[str] = Field(default_factory=list)
|
||||
tool_name_to_display_name: Optional[Dict[str, str]] = None
|
||||
tool_name_to_description: Optional[Dict[str, str]] = None
|
||||
extra_headers: List[str] = Field(default_factory=list)
|
||||
mcp_info: Optional[MCPInfo] = None
|
||||
static_headers: Optional[Dict[str, str]] = None
|
||||
|
|
|
|||
|
|
@ -287,6 +287,8 @@ model LiteLLM_MCPServerTable {
|
|||
mcp_info Json? @default("{}")
|
||||
mcp_access_groups String[]
|
||||
allowed_tools String[] @default([])
|
||||
tool_name_to_display_name Json? @default("{}")
|
||||
tool_name_to_description Json? @default("{}")
|
||||
extra_headers String[] @default([])
|
||||
static_headers Json? @default("{}")
|
||||
// Health check status
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ class MCPServer(BaseModel):
|
|||
] = None # allow admin to specify which headers to forward from client to the MCP server
|
||||
allowed_tools: Optional[List[str]] = None
|
||||
disallowed_tools: Optional[List[str]] = None
|
||||
tool_name_to_display_name: Optional[Dict[str, str]] = None
|
||||
tool_name_to_description: Optional[Dict[str, str]] = None
|
||||
allowed_params: Optional[
|
||||
Dict[str, List[str]]
|
||||
] = None # map of tool names to allowed parameter lists
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ def cleanup_mcp_global_state():
|
|||
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
|
||||
global_mcp_server_manager,
|
||||
)
|
||||
|
||||
# Clear before test
|
||||
global_mcp_server_manager.registry.clear()
|
||||
global_mcp_server_manager.tool_name_to_mcp_server_name_mapping.clear()
|
||||
|
|
@ -1788,6 +1789,79 @@ def test_filter_tools_by_allowed_tools():
|
|||
assert filtered_tools[1].name == "my_api_mcp-findpetsbystatus"
|
||||
|
||||
|
||||
def test_apply_tool_overrides():
|
||||
"""Test that apply_tool_overrides applies custom display names and descriptions."""
|
||||
from mcp.types import Tool
|
||||
|
||||
from litellm.proxy._experimental.mcp_server.server import apply_tool_overrides
|
||||
from litellm.types.mcp import MCPTransport
|
||||
from litellm.types.mcp_server.mcp_server_manager import MCPServer
|
||||
|
||||
mcp_server = MCPServer(
|
||||
server_id="my_api_mcp",
|
||||
name="my_api_mcp",
|
||||
transport=MCPTransport.http,
|
||||
tool_name_to_display_name={"getpetbyid": "Get Pet"},
|
||||
tool_name_to_description={"getpetbyid": "Custom description for get pet"},
|
||||
)
|
||||
tools = [
|
||||
Tool(
|
||||
name="my_api_mcp-getpetbyid",
|
||||
title=None,
|
||||
description="Original description",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
outputSchema=None,
|
||||
annotations=None,
|
||||
),
|
||||
Tool(
|
||||
name="my_api_mcp-findpetsbystatus",
|
||||
title=None,
|
||||
description="Finds Pets by status",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
outputSchema=None,
|
||||
annotations=None,
|
||||
),
|
||||
]
|
||||
|
||||
result = apply_tool_overrides(tools, mcp_server)
|
||||
|
||||
# First tool should have overridden name and description
|
||||
assert result[0].name == "Get Pet"
|
||||
assert result[0].description == "Custom description for get pet"
|
||||
# Second tool should be unchanged
|
||||
assert result[1].name == "my_api_mcp-findpetsbystatus"
|
||||
assert result[1].description == "Finds Pets by status"
|
||||
|
||||
|
||||
def test_apply_tool_overrides_no_overrides():
|
||||
"""Test that apply_tool_overrides returns tools unchanged when no overrides are set."""
|
||||
from mcp.types import Tool
|
||||
|
||||
from litellm.proxy._experimental.mcp_server.server import apply_tool_overrides
|
||||
from litellm.types.mcp import MCPTransport
|
||||
from litellm.types.mcp_server.mcp_server_manager import MCPServer
|
||||
|
||||
mcp_server = MCPServer(
|
||||
server_id="my_api_mcp",
|
||||
name="my_api_mcp",
|
||||
transport=MCPTransport.http,
|
||||
)
|
||||
tools = [
|
||||
Tool(
|
||||
name="my_api_mcp-getpetbyid",
|
||||
title=None,
|
||||
description="Original description",
|
||||
inputSchema={"type": "object", "properties": {}},
|
||||
outputSchema=None,
|
||||
annotations=None,
|
||||
),
|
||||
]
|
||||
|
||||
result = apply_tool_overrides(tools, mcp_server)
|
||||
assert result[0].name == "my_api_mcp-getpetbyid"
|
||||
assert result[0].description == "Original description"
|
||||
|
||||
|
||||
def _make_db_mcp_server(server_id: str, updated_at: datetime) -> LiteLLM_MCPServerTable:
|
||||
return LiteLLM_MCPServerTable(
|
||||
server_id=server_id,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
const [aliasManuallyEdited, setAliasManuallyEdited] = useState(false);
|
||||
const [tools, setTools] = useState<any[]>([]);
|
||||
const [allowedTools, setAllowedTools] = useState<string[]>([]);
|
||||
const [toolNameToDisplayName, setToolNameToDisplayName] = useState<Record<string, string>>({});
|
||||
const [toolNameToDescription, setToolNameToDescription] = useState<Record<string, string>>({});
|
||||
const [transportType, setTransportType] = useState<string>("");
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [oauthAccessToken, setOauthAccessToken] = useState<string | null>(null);
|
||||
|
|
@ -353,6 +355,8 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
mcp_access_groups: accessGroups,
|
||||
alias: restValues.alias,
|
||||
allowed_tools: allowedTools.length > 0 ? allowedTools : null,
|
||||
tool_name_to_display_name: Object.keys(toolNameToDisplayName).length > 0 ? toolNameToDisplayName : null,
|
||||
tool_name_to_description: Object.keys(toolNameToDescription).length > 0 ? toolNameToDescription : null,
|
||||
allow_all_keys: Boolean(allowAllKeysRaw),
|
||||
available_on_public_internet: Boolean(availableOnPublicInternetRaw),
|
||||
static_headers: staticHeaders,
|
||||
|
|
@ -712,6 +716,10 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
|
|||
allowedTools={allowedTools}
|
||||
existingAllowedTools={null}
|
||||
onAllowedToolsChange={setAllowedTools}
|
||||
toolNameToDisplayName={toolNameToDisplayName}
|
||||
toolNameToDescription={toolNameToDescription}
|
||||
onToolNameToDisplayNameChange={setToolNameToDisplayName}
|
||||
onToolNameToDescriptionChange={setToolNameToDescription}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [aliasManuallyEdited, setAliasManuallyEdited] = useState(false);
|
||||
const [allowedTools, setAllowedTools] = useState<string[]>([]);
|
||||
const [toolNameToDisplayName, setToolNameToDisplayName] = useState<Record<string, string>>({});
|
||||
const [toolNameToDescription, setToolNameToDescription] = useState<Record<string, string>>({});
|
||||
const [pendingRestoredValues, setPendingRestoredValues] = useState<Record<string, any> | null>(null);
|
||||
const authType = Form.useWatch("auth_type", form) as string | undefined;
|
||||
const transportType = Form.useWatch("transport", form) as string | undefined;
|
||||
|
|
@ -195,11 +197,13 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
}
|
||||
}, [mcpServer]);
|
||||
|
||||
// Initialize allowed tools from existing server data
|
||||
// Initialize allowed tools and tool overrides from existing server data
|
||||
useEffect(() => {
|
||||
if (mcpServer.allowed_tools) {
|
||||
setAllowedTools(mcpServer.allowed_tools);
|
||||
}
|
||||
setToolNameToDisplayName(mcpServer.tool_name_to_display_name ?? {});
|
||||
setToolNameToDescription(mcpServer.tool_name_to_description ?? {});
|
||||
}, [mcpServer]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -541,6 +545,8 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
// Include permission management fields
|
||||
extra_headers: restValues.extra_headers || [],
|
||||
allowed_tools: allowedTools.length > 0 ? allowedTools : null,
|
||||
tool_name_to_display_name: Object.keys(toolNameToDisplayName).length > 0 ? toolNameToDisplayName : null,
|
||||
tool_name_to_description: Object.keys(toolNameToDescription).length > 0 ? toolNameToDescription : null,
|
||||
disallowed_tools: restValues.disallowed_tools || [],
|
||||
static_headers: staticHeaders,
|
||||
allow_all_keys: Boolean(allowAllKeysRaw ?? mcpServer.allow_all_keys),
|
||||
|
|
@ -906,6 +912,10 @@ const MCPServerEdit: React.FC<MCPServerEditProps> = ({
|
|||
allowedTools={allowedTools}
|
||||
existingAllowedTools={mcpServer.allowed_tools || null}
|
||||
onAllowedToolsChange={setAllowedTools}
|
||||
toolNameToDisplayName={toolNameToDisplayName}
|
||||
toolNameToDescription={toolNameToDescription}
|
||||
onToolNameToDisplayNameChange={setToolNameToDisplayName}
|
||||
onToolNameToDescriptionChange={setToolNameToDescription}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Card, Title, Text } from "@tremor/react";
|
||||
import { ToolOutlined, CheckCircleOutlined, SearchOutlined } from "@ant-design/icons";
|
||||
import { ToolOutlined, CheckCircleOutlined, SearchOutlined, EditOutlined } from "@ant-design/icons";
|
||||
import { Badge, Spin, Checkbox, Input } from "antd";
|
||||
import { useTestMCPConnection } from "../../hooks/useTestMCPConnection";
|
||||
|
||||
|
|
@ -11,6 +11,10 @@ interface MCPToolConfigurationProps {
|
|||
allowedTools: string[];
|
||||
existingAllowedTools: string[] | null;
|
||||
onAllowedToolsChange: (tools: string[]) => void;
|
||||
toolNameToDisplayName: Record<string, string>;
|
||||
toolNameToDescription: Record<string, string>;
|
||||
onToolNameToDisplayNameChange: (map: Record<string, string>) => void;
|
||||
onToolNameToDescriptionChange: (map: Record<string, string>) => void;
|
||||
}
|
||||
|
||||
const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
||||
|
|
@ -20,10 +24,15 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
allowedTools,
|
||||
existingAllowedTools,
|
||||
onAllowedToolsChange,
|
||||
toolNameToDisplayName,
|
||||
toolNameToDescription,
|
||||
onToolNameToDisplayNameChange,
|
||||
onToolNameToDescriptionChange,
|
||||
}) => {
|
||||
const previousToolsRef = useRef<any[]>([]);
|
||||
const [toolSearchTerm, setToolSearchTerm] = useState("");
|
||||
const hasInitializedRef = useRef(false);
|
||||
const [expandedTools, setExpandedTools] = useState<Set<string>>(new Set());
|
||||
|
||||
const { tools, isLoadingTools, toolsError, canFetchTools } = useTestMCPConnection({
|
||||
accessToken,
|
||||
|
|
@ -96,6 +105,39 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
onAllowedToolsChange([]);
|
||||
};
|
||||
|
||||
const handleToggleEditExpanded = (toolName: string, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setExpandedTools((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(toolName)) {
|
||||
next.delete(toolName);
|
||||
} else {
|
||||
next.add(toolName);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const handleDisplayNameChange = (toolName: string, value: string) => {
|
||||
const next = { ...toolNameToDisplayName };
|
||||
if (value) {
|
||||
next[toolName] = value;
|
||||
} else {
|
||||
delete next[toolName];
|
||||
}
|
||||
onToolNameToDisplayNameChange(next);
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (toolName: string, value: string) => {
|
||||
const next = { ...toolNameToDescription };
|
||||
if (value) {
|
||||
next[toolName] = value;
|
||||
} else {
|
||||
delete next[toolName];
|
||||
}
|
||||
onToolNameToDescriptionChange(next);
|
||||
};
|
||||
|
||||
// Don't show anything if required fields aren't filled
|
||||
if (!canFetchTools && !formValues.url && !formValues.spec_path) {
|
||||
return null;
|
||||
|
|
@ -213,39 +255,106 @@ const MCPToolConfiguration: React.FC<MCPToolConfigurationProps> = ({
|
|||
<Text>No tools found matching "{toolSearchTerm}"</Text>
|
||||
</div>
|
||||
) : (
|
||||
filteredTools.map((tool, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`p-4 rounded-lg border transition-colors cursor-pointer ${
|
||||
allowedTools.includes(tool.name)
|
||||
? "bg-blue-50 border-blue-300 hover:border-blue-400"
|
||||
: "bg-gray-50 border-gray-200 hover:border-gray-300"
|
||||
}`}
|
||||
onClick={() => handleToolToggle(tool.name)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox checked={allowedTools.includes(tool.name)} onChange={() => handleToolToggle(tool.name)} />
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Text className="font-medium text-gray-900">{tool.name}</Text>
|
||||
<span
|
||||
className={`px-2 py-0.5 text-xs rounded-full font-medium ${
|
||||
allowedTools.includes(tool.name) ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"
|
||||
filteredTools.map((tool, index) => {
|
||||
const isEnabled = allowedTools.includes(tool.name);
|
||||
const isEditExpanded = expandedTools.has(tool.name);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`rounded-lg border transition-colors ${
|
||||
isEnabled
|
||||
? "bg-blue-50 border-blue-300 hover:border-blue-400"
|
||||
: "bg-gray-50 border-gray-200 hover:border-gray-300"
|
||||
}`}
|
||||
>
|
||||
{/* Main tool row */}
|
||||
<div
|
||||
className="p-4 cursor-pointer"
|
||||
onClick={() => handleToolToggle(tool.name)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<Checkbox checked={isEnabled} onChange={() => handleToolToggle(tool.name)} />
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Text className="font-medium text-gray-900">
|
||||
{toolNameToDisplayName[tool.name] || tool.name}
|
||||
</Text>
|
||||
<span
|
||||
className={`px-2 py-0.5 text-xs rounded-full font-medium ${
|
||||
isEnabled ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{isEnabled ? "Enabled" : "Disabled"}
|
||||
</span>
|
||||
{toolNameToDisplayName[tool.name] && (
|
||||
<span className="px-2 py-0.5 text-xs rounded-full font-medium bg-purple-100 text-purple-800">
|
||||
Custom name
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{(toolNameToDescription[tool.name] || tool.description) && (
|
||||
<Text className="text-gray-500 text-sm block mt-1">
|
||||
{toolNameToDescription[tool.name] || tool.description}
|
||||
</Text>
|
||||
)}
|
||||
<Text className="text-gray-400 text-xs block mt-1">
|
||||
{isEnabled ? "✓ Users can call this tool" : "✗ Users cannot call this tool"}
|
||||
</Text>
|
||||
</div>
|
||||
{/* Edit toggle button */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleToggleEditExpanded(tool.name, e)}
|
||||
className={`p-1.5 rounded-md transition-colors ${
|
||||
isEditExpanded
|
||||
? "bg-blue-100 text-blue-600"
|
||||
: "text-gray-400 hover:text-gray-600 hover:bg-gray-100"
|
||||
}`}
|
||||
title="Edit display name and description"
|
||||
>
|
||||
{allowedTools.includes(tool.name) ? "Enabled" : "Disabled"}
|
||||
</span>
|
||||
<EditOutlined />
|
||||
</button>
|
||||
</div>
|
||||
{tool.description && <Text className="text-gray-500 text-sm block mt-1">{tool.description}</Text>}
|
||||
<Text className="text-gray-400 text-xs block mt-1">
|
||||
{allowedTools.includes(tool.name)
|
||||
? "✓ Users can call this tool"
|
||||
: "✗ Users cannot call this tool"}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{/* Inline edit section */}
|
||||
{isEditExpanded && (
|
||||
<div
|
||||
className="px-4 pb-4 pt-3 border-t border-gray-200 space-y-3 bg-gray-50 rounded-b-lg"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div>
|
||||
<Text className="text-xs font-medium text-gray-600 mb-1 block">
|
||||
Display Name
|
||||
</Text>
|
||||
<Input
|
||||
placeholder={tool.name}
|
||||
value={toolNameToDisplayName[tool.name] || ""}
|
||||
onChange={(e) => handleDisplayNameChange(tool.name, e.target.value)}
|
||||
/>
|
||||
<Text className="text-xs text-gray-400 mt-1 block">
|
||||
Override how this tool's name appears to users. Leave blank to use original.
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text className="text-xs font-medium text-gray-600 mb-1 block">
|
||||
Description
|
||||
</Text>
|
||||
<Input.TextArea
|
||||
placeholder={tool.description || "No description"}
|
||||
value={toolNameToDescription[tool.name] || ""}
|
||||
onChange={(e) => handleDescriptionChange(tool.name, e.target.value)}
|
||||
rows={2}
|
||||
/>
|
||||
<Text className="text-xs text-gray-400 mt-1 block">
|
||||
Override the tool description shown to users. Leave blank to use original.
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -169,6 +169,8 @@ export interface MCPServer {
|
|||
teams?: Team[];
|
||||
mcp_access_groups?: string[];
|
||||
allowed_tools?: string[];
|
||||
tool_name_to_display_name?: Record<string, string>;
|
||||
tool_name_to_description?: Record<string, string>;
|
||||
allow_all_keys?: boolean;
|
||||
available_on_public_internet?: boolean;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue