From 93f1dd4af89281cbda161a6277db5463e9527847 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Mon, 19 Jan 2026 09:36:41 -0800 Subject: [PATCH 1/2] feat(mcp): updated mcp subblocks for mcp tools to match subblocks --- .../mcp-dynamic-args/mcp-dynamic-args.tsx | 381 ++++-------------- 1 file changed, 74 insertions(+), 307 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx index 6912bb2150..41527a5165 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx @@ -1,281 +1,17 @@ -import type { RefObject } from 'react' -import { useCallback, useMemo, useRef, useState } from 'react' +import { useCallback, useMemo } from 'react' import { createLogger } from '@sim/logger' import { useParams } from 'next/navigation' -import { Combobox, Input, Label, Slider, Switch, Textarea } from '@/components/emcn/components' +import { Combobox, Label, Slider, Switch } from '@/components/emcn/components' import { cn } from '@/lib/core/utils/cn' -import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' -import { - checkTagTrigger, - TagDropdown, -} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown' +import { LongInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input' +import { ShortInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' -import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes' +import type { SubBlockConfig } from '@/blocks/types' import { useMcpTools } from '@/hooks/mcp/use-mcp-tools' import { formatParameterLabel } from '@/tools/params' const logger = createLogger('McpDynamicArgs') -interface McpInputWithTagsProps { - value: string - onChange: (value: string) => void - placeholder?: string - disabled?: boolean - isPassword?: boolean - blockId: string - accessiblePrefixes?: Set -} - -function McpInputWithTags({ - value, - onChange, - placeholder, - disabled, - isPassword, - blockId, - accessiblePrefixes, -}: McpInputWithTagsProps) { - const [showTags, setShowTags] = useState(false) - const [cursorPosition, setCursorPosition] = useState(0) - const [activeSourceBlockId, setActiveSourceBlockId] = useState(null) - const inputRef = useRef(null) - const inputNameRef = useRef(`mcp_input_${Math.random()}`) - - const handleChange = (e: React.ChangeEvent) => { - const newValue = e.target.value - const newCursorPosition = e.target.selectionStart ?? 0 - - onChange(newValue) - setCursorPosition(newCursorPosition) - - const tagTrigger = checkTagTrigger(newValue, newCursorPosition) - setShowTags(tagTrigger.show) - } - - const handleDrop = (e: React.DragEvent) => { - e.preventDefault() - - try { - const data = JSON.parse(e.dataTransfer.getData('application/json')) - if (data.type !== 'connectionBlock') return - - const dropPosition = inputRef.current?.selectionStart ?? value.length ?? 0 - const currentValue = value ?? '' - const newValue = `${currentValue.slice(0, dropPosition)}<${currentValue.slice(dropPosition)}` - - onChange(newValue) - setCursorPosition(dropPosition + 1) - setShowTags(true) - - if (data.connectionData?.sourceBlockId) { - setActiveSourceBlockId(data.connectionData.sourceBlockId) - } - - setTimeout(() => { - if (inputRef.current) { - inputRef.current.selectionStart = dropPosition + 1 - inputRef.current.selectionEnd = dropPosition + 1 - } - }, 0) - } catch (error) { - logger.error('Failed to parse drop data:', { error }) - } - } - - const handleDragOver = (e: React.DragEvent) => { - e.preventDefault() - } - - const handleTagSelect = (newValue: string) => { - onChange(newValue) - setShowTags(false) - setActiveSourceBlockId(null) - } - - return ( -
-
- { - e.currentTarget.removeAttribute('readOnly') - // Show tag dropdown on focus when input is empty - if (!disabled && (value?.trim() === '' || !value)) { - setShowTags(true) - setCursorPosition(0) - } - }} - className={cn(!isPassword && 'text-transparent caret-foreground')} - /> - {!isPassword && ( -
-
- {formatDisplayText(value?.toString() || '', { - accessiblePrefixes, - highlightAll: !accessiblePrefixes, - })} -
-
- )} -
- { - setShowTags(false) - setActiveSourceBlockId(null) - }} - inputRef={inputRef as RefObject} - /> -
- ) -} - -interface McpTextareaWithTagsProps { - value: string - onChange: (value: string) => void - placeholder?: string - disabled?: boolean - blockId: string - accessiblePrefixes?: Set - rows?: number -} - -function McpTextareaWithTags({ - value, - onChange, - placeholder, - disabled, - blockId, - accessiblePrefixes, - rows = 4, -}: McpTextareaWithTagsProps) { - const [showTags, setShowTags] = useState(false) - const [cursorPosition, setCursorPosition] = useState(0) - const [activeSourceBlockId, setActiveSourceBlockId] = useState(null) - const textareaRef = useRef(null) - const textareaNameRef = useRef(`mcp_textarea_${Math.random()}`) - - const handleChange = (e: React.ChangeEvent) => { - const newValue = e.target.value - const newCursorPosition = e.target.selectionStart ?? 0 - - onChange(newValue) - setCursorPosition(newCursorPosition) - - const tagTrigger = checkTagTrigger(newValue, newCursorPosition) - setShowTags(tagTrigger.show) - } - - const handleDrop = (e: React.DragEvent) => { - e.preventDefault() - - try { - const data = JSON.parse(e.dataTransfer.getData('application/json')) - if (data.type !== 'connectionBlock') return - - const dropPosition = textareaRef.current?.selectionStart ?? value.length ?? 0 - const currentValue = value ?? '' - const newValue = `${currentValue.slice(0, dropPosition)}<${currentValue.slice(dropPosition)}` - - onChange(newValue) - setCursorPosition(dropPosition + 1) - setShowTags(true) - - if (data.connectionData?.sourceBlockId) { - setActiveSourceBlockId(data.connectionData.sourceBlockId) - } - - setTimeout(() => { - if (textareaRef.current) { - textareaRef.current.selectionStart = dropPosition + 1 - textareaRef.current.selectionEnd = dropPosition + 1 - } - }, 0) - } catch (error) { - logger.error('Failed to parse drop data:', { error }) - } - } - - const handleDragOver = (e: React.DragEvent) => { - e.preventDefault() - } - - const handleTagSelect = (newValue: string) => { - onChange(newValue) - setShowTags(false) - setActiveSourceBlockId(null) - } - - return ( -
-