Skip to content

Commit 6e0be80

Browse files
improvement(mcp): simplify server and operation selection
1 parent 70e2157 commit 6e0be80

54 files changed

Lines changed: 1085 additions & 708 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/docs/openapi-v2-workflows.json

Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -8130,95 +8130,6 @@
81308130
"AgentMcpTool": {
81318131
"type": "object",
81328132
"properties": {
8133-
"operationPolicy": {
8134-
"oneOf": [
8135-
{
8136-
"type": "object",
8137-
"properties": {
8138-
"mode": {
8139-
"type": "string",
8140-
"const": "all",
8141-
"description": "Allow all operations available to the authorized credential."
8142-
}
8143-
},
8144-
"required": ["mode"],
8145-
"additionalProperties": false
8146-
},
8147-
{
8148-
"type": "object",
8149-
"properties": {
8150-
"mode": {
8151-
"type": "string",
8152-
"const": "allow",
8153-
"description": "Allow only the selected exact operations."
8154-
},
8155-
"operations": {
8156-
"maxItems": 1000,
8157-
"type": "array",
8158-
"items": {
8159-
"type": "object",
8160-
"properties": {
8161-
"serverId": {
8162-
"type": "string",
8163-
"minLength": 1,
8164-
"maxLength": 256,
8165-
"description": "Canonical MCP server identity, independent of the selected credential."
8166-
},
8167-
"name": {
8168-
"type": "string",
8169-
"minLength": 1,
8170-
"maxLength": 256,
8171-
"description": "Exact operation name returned by MCP discovery."
8172-
}
8173-
},
8174-
"required": ["serverId", "name"],
8175-
"additionalProperties": false
8176-
},
8177-
"description": "Allowed server-scoped operation identities; an empty list grants no access."
8178-
}
8179-
},
8180-
"required": ["mode", "operations"],
8181-
"additionalProperties": false
8182-
},
8183-
{
8184-
"type": "object",
8185-
"properties": {
8186-
"mode": {
8187-
"type": "string",
8188-
"const": "deny",
8189-
"description": "Exclude the selected exact operations."
8190-
},
8191-
"operations": {
8192-
"maxItems": 1000,
8193-
"type": "array",
8194-
"items": {
8195-
"type": "object",
8196-
"properties": {
8197-
"serverId": {
8198-
"type": "string",
8199-
"minLength": 1,
8200-
"maxLength": 256,
8201-
"description": "Canonical MCP server identity, independent of the selected credential."
8202-
},
8203-
"name": {
8204-
"type": "string",
8205-
"minLength": 1,
8206-
"maxLength": 256,
8207-
"description": "Exact operation name returned by MCP discovery."
8208-
}
8209-
},
8210-
"required": ["serverId", "name"],
8211-
"additionalProperties": false
8212-
},
8213-
"description": "Denied server-scoped operation identities; an empty list allows otherwise permitted tools."
8214-
}
8215-
},
8216-
"required": ["mode", "operations"],
8217-
"additionalProperties": false
8218-
}
8219-
],
8220-
"description": "Saved workflow operation restrictions that can only narrow authorized credential access."
8221-
},
82228133
"type": {
82238134
"type": "string",
82248135
"const": "mcp",
@@ -8382,12 +8293,6 @@
83828293
"params": {
83838294
"type": "object",
83848295
"properties": {
8385-
"connectionId": {
8386-
"description": "Optional managed connection ID or upstream reference bound to the canonical server.",
8387-
"type": "string",
8388-
"minLength": 1,
8389-
"maxLength": 128
8390-
},
83918296
"serverId": {
83928297
"type": "string",
83938298
"minLength": 1,
@@ -8397,7 +8302,7 @@
83978302
},
83988303
"required": ["serverId"],
83998304
"additionalProperties": false,
8400-
"description": "Server and optional connection identity for authorized operation discovery and execution."
8305+
"description": "Executable server or connection identity for authorized operation discovery and execution."
84018306
},
84028307
"usageControl": {
84038308
"type": "string",

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-dynamic-args/mcp-dynamic-args.tsx

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { useCallback, useState } from 'react'
22
import { Combobox, FieldDivider, Label, Slider, Switch } from '@sim/emcn'
33
import { createLogger } from '@sim/logger'
44
import { useParams } from 'next/navigation'
5-
import { isMcpRuntimeReference } from '@/lib/mcp/operation-policy'
65
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
76
import { LongInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input'
87
import { ShortInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input'
98
import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
9+
import { useMcpBlockConfig } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-mcp-block-config'
1010
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
1111
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
1212
import { useActiveSearchTarget } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/providers/active-search-target-provider'
@@ -111,28 +111,20 @@ export function McpDynamicArgs({
111111
const params = useParams()
112112
const workspaceId = params.workspaceId as string
113113
const { mcpTools, isLoading } = useMcpTools(workspaceId)
114-
const [toolFromStore] = useSubBlockValue(blockId, 'tool')
115-
const [serverFromStore] = useSubBlockValue(blockId, 'server')
116-
const [connectionFromStore] = useSubBlockValue(blockId, 'connection')
117-
const selectedConnection = previewContextValues
118-
? resolvePreviewContextValue(previewContextValues.connection)
119-
: connectionFromStore
120-
const selectedServer = previewContextValues
121-
? resolvePreviewContextValue(previewContextValues.server)
122-
: serverFromStore
123-
const selectedTool = previewContextValues
124-
? resolvePreviewContextValue(previewContextValues.tool)
125-
: toolFromStore
126114
const [schemaFromStore] = useSubBlockValue(blockId, '_toolSchema')
115+
const {
116+
server: selectedServer,
117+
tool: selectedTool,
118+
argumentsMode,
119+
} = useMcpBlockConfig({ blockId, previewContextValues })
127120
const cachedSchema = previewContextValues
128121
? resolvePreviewContextValue(previewContextValues._toolSchema)
129122
: schemaFromStore
130123
const [toolArgs, setToolArgs] = useSubBlockValue(blockId, subBlockId)
131124

132125
const selectedToolConfig = mcpTools.find(
133126
(tool) =>
134-
tool.serverId === (selectedConnection || selectedServer) &&
135-
(tool.id === selectedTool || tool.name === selectedTool)
127+
tool.serverId === selectedServer && (tool.id === selectedTool || tool.name === selectedTool)
136128
)
137129
const toolSchema = selectedToolConfig?.inputSchema || cachedSchema
138130

@@ -463,26 +455,24 @@ export function McpDynamicArgs({
463455
}
464456
}
465457

466-
if (
467-
isMcpRuntimeReference(selectedTool) ||
468-
isMcpRuntimeReference(selectedServer) ||
469-
isMcpRuntimeReference(selectedConnection) ||
470-
(selectedTool && !toolSchema && !isLoading)
471-
) {
458+
if (argumentsMode === 'json') {
472459
return (
473-
<LongInput
474-
blockId={blockId}
475-
subBlockId={subBlockId}
476-
config={{
477-
id: subBlockId,
478-
type: 'long-input',
479-
title: 'Arguments',
480-
placeholder: 'JSON arguments or an upstream reference',
481-
}}
482-
disabled={disabled}
483-
isPreview={isPreview}
484-
previewValue={previewValue}
485-
/>
460+
<div className='flex flex-col gap-[9px]'>
461+
<Label>JSON arguments</Label>
462+
<LongInput
463+
blockId={blockId}
464+
subBlockId={subBlockId}
465+
config={{
466+
id: subBlockId,
467+
type: 'long-input',
468+
title: 'JSON arguments',
469+
placeholder: 'JSON arguments or an upstream reference',
470+
}}
471+
disabled={disabled}
472+
isPreview={isPreview}
473+
previewValue={previewValue}
474+
/>
475+
</div>
486476
)
487477
}
488478

@@ -494,19 +484,22 @@ export function McpDynamicArgs({
494484
)
495485
}
496486

497-
if (
498-
selectedTool &&
499-
!cachedSchema &&
500-
!selectedToolConfig &&
501-
(isLoading || mcpTools.length === 0)
502-
) {
487+
if (selectedTool && !cachedSchema && !selectedToolConfig && isLoading) {
503488
return (
504489
<div className='rounded-lg border p-8 text-center'>
505490
<p className='text-muted-foreground text-sm'>Loading tool schema…</p>
506491
</div>
507492
)
508493
}
509494

495+
if (!toolSchema) {
496+
return (
497+
<p className='text-[var(--text-error)] text-small'>
498+
Operation schema unavailable. Refresh or select an available operation.
499+
</p>
500+
)
501+
}
502+
510503
if (!toolSchema?.properties || Object.keys(toolSchema.properties).length === 0) {
511504
return (
512505
<div className='rounded-lg border p-8 text-center'>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-server-modal/mcp-server-selector.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,7 @@ export function McpServerSelector({
3232
const workspaceId = params.workspaceId as string
3333

3434
const { data: servers = [], isLoading, error } = useMcpToolServers(workspaceId)
35-
const [configuredServer] = useSubBlockValue(blockId, 'server')
36-
const targetOptions = getMcpTargetOptions(
37-
servers,
38-
subBlock.id === 'connection' ? 'connection' : 'server',
39-
configuredServer
40-
)
35+
const targetOptions = getMcpTargetOptions(servers)
4136

4237
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)
4338

@@ -77,15 +72,15 @@ export function McpServerSelector({
7772
onChange={handleComboboxChange}
7873
placeholder={label}
7974
disabled={disabled}
80-
editable={true}
81-
filterOptions={true}
75+
editable={false}
76+
searchable
77+
filterOptions={false}
8278
isLoading={isLoading}
8379
error={error instanceof Error ? error.message : null}
80+
overlayLabel={inputValue || undefined}
8481
overlayContent={
8582
workflowSearchHighlight ? (
86-
<span className='block truncate'>
87-
{formatDisplayText(inputValue, { workflowSearchHighlight })}
88-
</span>
83+
<span>{formatDisplayText(inputValue, { workflowSearchHighlight })}</span>
8984
) : undefined
9085
}
9186
/>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/mcp-server-modal/mcp-tool-selector.tsx

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
import { useMemo } from 'react'
44
import { ChipCombobox } from '@sim/emcn'
55
import { useParams } from 'next/navigation'
6-
import { normalizeMcpOperationPolicy, permitsMcpOperation } from '@/lib/mcp/operation-policy'
76
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
87
import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight'
8+
import { useMcpBlockConfig } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-mcp-block-config'
99
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
10-
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
1110
import { useActiveSearchTarget } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/providers/active-search-target-provider'
1211
import type { SubBlockConfig } from '@/blocks/types'
1312
import { useMcpTools } from '@/hooks/mcp/use-mcp-tools'
@@ -38,36 +37,17 @@ export function McpToolSelector({
3837
const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id)
3938
const [, setSchemaCache] = useSubBlockValue(blockId, '_toolSchema')
4039

41-
const [serverFromStore] = useSubBlockValue(blockId, 'server')
42-
const [connectionFromStore] = useSubBlockValue(blockId, 'connection')
43-
const [policyFromStore] = useSubBlockValue(blockId, 'operationPolicy')
44-
const serverValue = previewContextValues
45-
? resolvePreviewContextValue(previewContextValues.server)
46-
: serverFromStore
40+
const { server: serverValue } = useMcpBlockConfig({ blockId, previewContextValues })
4741

4842
const label = subBlock.placeholder || 'Select tool'
4943

5044
const effectiveValue = isPreview && previewValue !== undefined ? previewValue : storeValue
5145
const selectedToolId = effectiveValue || ''
5246

53-
const availableTools = useMemo(() => {
54-
if (!serverValue) return []
55-
const connection = previewContextValues
56-
? resolvePreviewContextValue(previewContextValues.connection)
57-
: connectionFromStore
58-
const policy = normalizeMcpOperationPolicy(
59-
previewContextValues
60-
? resolvePreviewContextValue(previewContextValues.operationPolicy)
61-
: policyFromStore
62-
)
63-
return mcpTools.filter(
64-
(tool) =>
65-
(connection
66-
? tool.serverId === connection
67-
: tool.serverId === serverValue || tool.canonicalServerId === serverValue) &&
68-
permitsMcpOperation(policy, tool.canonicalServerId ?? tool.serverId, tool.name)
69-
)
70-
}, [serverValue, mcpTools, connectionFromStore, policyFromStore, previewContextValues])
47+
const availableTools = useMemo(
48+
() => mcpTools.filter((tool) => tool.serverId === serverValue),
49+
[mcpTools, serverValue]
50+
)
7151

7252
const selectedTool = availableTools.find(
7353
(tool) => tool.id === selectedToolId || tool.name === selectedToolId
@@ -114,15 +94,15 @@ export function McpToolSelector({
11494
onOpenChange={handleOpenChange}
11595
placeholder={serverValue ? label : 'Select server first'}
11696
disabled={isDisabled}
117-
editable={true}
118-
filterOptions={true}
97+
editable={false}
98+
searchable
99+
filterOptions={false}
119100
isLoading={isLoading}
120101
error={error || null}
102+
overlayLabel={inputValue || undefined}
121103
overlayContent={
122104
workflowSearchHighlight ? (
123-
<span className='block truncate'>
124-
{formatDisplayText(inputValue, { workflowSearchHighlight })}
125-
</span>
105+
<span>{formatDisplayText(inputValue, { workflowSearchHighlight })}</span>
126106
) : undefined
127107
}
128108
/>

0 commit comments

Comments
 (0)