mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(mcp): enforce tool name validation in deploy modal (#4879)
* fix(mcp): enforce tool name validation in deploy modal * fix(mcp): correct cn import path to fix build * fix(mcp): align tool-name regex with server sanitization, add disabled-combobox hint
This commit is contained in:
+40
-8
@@ -13,6 +13,7 @@ import {
|
||||
Skeleton,
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { generateToolInputSchema, sanitizeToolName } from '@/lib/mcp/workflow-tool-schema'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
@@ -32,6 +33,14 @@ import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
|
||||
const logger = createLogger('McpToolDeploy')
|
||||
|
||||
/**
|
||||
* Mirrors the server's `sanitizeToolName` output: lowercase alphanumerics with single
|
||||
* underscores between segments. Disallows leading/trailing and consecutive underscores so
|
||||
* the validated name matches exactly what the server persists (no silent rewrite).
|
||||
*/
|
||||
const TOOL_NAME_PATTERN = /^[a-z0-9]+(_[a-z0-9]+)*$/
|
||||
const MAX_TOOL_NAME_LENGTH = 64
|
||||
|
||||
/** InputFormatField with guaranteed name (after normalization) */
|
||||
type NormalizedField = InputFormatField & { name: string }
|
||||
|
||||
@@ -166,6 +175,18 @@ export function McpDeploy({
|
||||
[inputFormat, parameterDescriptions]
|
||||
)
|
||||
|
||||
const toolNameError = useMemo(() => {
|
||||
const trimmed = toolName.trim()
|
||||
if (!trimmed) return null
|
||||
if (trimmed.length > MAX_TOOL_NAME_LENGTH) {
|
||||
return `Tool name must be ${MAX_TOOL_NAME_LENGTH} characters or fewer`
|
||||
}
|
||||
if (!TOOL_NAME_PATTERN.test(trimmed)) {
|
||||
return 'Use lowercase letters and numbers, separated by single underscores'
|
||||
}
|
||||
return null
|
||||
}, [toolName])
|
||||
|
||||
const [serverToolsMap, setServerToolsMap] = useState<
|
||||
Record<string, { tool: WorkflowMcpTool | null; isLoading: boolean }>
|
||||
>({})
|
||||
@@ -270,11 +291,11 @@ export function McpDeploy({
|
||||
(hasToolConfigurationChanges && selectedServerIdsForForm.length > 0)
|
||||
|
||||
useEffect(() => {
|
||||
onCanSaveChange?.(hasChanges && !!toolName.trim())
|
||||
}, [hasChanges, toolName, onCanSaveChange])
|
||||
onCanSaveChange?.(hasChanges && !!toolName.trim() && !toolNameError)
|
||||
}, [hasChanges, toolName, toolNameError, onCanSaveChange])
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!toolName.trim()) return
|
||||
if (!toolName.trim() || toolNameError) return
|
||||
|
||||
const currentIds = new Set(selectedServerIds)
|
||||
const nextIds = new Set(selectedServerIdsForForm)
|
||||
@@ -492,9 +513,16 @@ export function McpDeploy({
|
||||
value={toolName}
|
||||
onChange={(e) => setToolName(e.target.value)}
|
||||
placeholder='e.g., book_flight'
|
||||
aria-invalid={!!toolNameError}
|
||||
className={cn(toolNameError && 'border-[var(--text-error)]')}
|
||||
/>
|
||||
<p className='mt-[6.5px] text-[var(--text-secondary)] text-xs'>
|
||||
Use lowercase letters, numbers, and underscores only
|
||||
<p
|
||||
className={cn(
|
||||
'mt-[6.5px] text-xs',
|
||||
toolNameError ? 'text-[var(--text-error)]' : 'text-[var(--text-secondary)]'
|
||||
)}
|
||||
>
|
||||
{toolNameError ?? 'Use lowercase letters, numbers, and underscores only'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -564,16 +592,20 @@ export function McpDeploy({
|
||||
placeholder='Select servers...'
|
||||
searchable
|
||||
searchPlaceholder='Search servers...'
|
||||
disabled={!toolName.trim() || isPending}
|
||||
disabled={!toolName.trim() || !!toolNameError || isPending}
|
||||
overlayContent={
|
||||
<span className='truncate text-[var(--text-primary)]'>{selectedServersLabel}</span>
|
||||
}
|
||||
/>
|
||||
{!toolName.trim() && (
|
||||
{!toolName.trim() ? (
|
||||
<p className='mt-[6.5px] text-[var(--text-secondary)] text-xs'>
|
||||
Enter a tool name to select servers
|
||||
</p>
|
||||
)}
|
||||
) : toolNameError ? (
|
||||
<p className='mt-[6.5px] text-[var(--text-secondary)] text-xs'>
|
||||
Fix the tool name to select servers
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{saveErrors.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user