mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 16:02:24 +00:00
feat(frontend): add wildcard pattern support to MCP token custom scope (#7306)
* feat(mcp): add wildcard pattern support to token UI custom scope - Add text input fields for wildcard patterns in Custom scope - Combine wildcard patterns with individual script/flow selections - Support comma-separated patterns (e.g., f/outline/*,f/docs/*) - Add help popover explaining pattern syntax with examples - Backward compatible: empty patterns preserve existing behavior Closes #7252 * fix(mcp): apply critical code review fixes for wildcard patterns Apply fixes identified by code-smells agent: **P0 - Code Duplication (CRITICAL)** - Extract pattern parsing logic into reusable helper function - Eliminates duplicate code between scripts and flows processing - Improves maintainability and consistency **P1 - Button Validation (CRITICAL)** - Fix button disable condition to allow pattern-only tokens - Users can now create tokens with ONLY wildcard patterns - Resolves Test Scenario #6 from design document **P2 - State Management (MODERATE)** - Add $effect to clear patterns when switching scopes - Prevents stale data from persisting across mode changes - Improves user experience and data consistency Changes: - Added parsePatterns() helper function - Updated button disable condition with pattern checks - Added two $effect hooks for state cleanup - Reduced code duplication by 10 lines Testing: All edge cases now properly handled including pattern-only tokens * nits * nit --------- Co-authored-by: Devdatta Talele <devtalele0@gmail.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
import TextInput from '../text_input/TextInput.svelte'
|
||||
import Select from '../select/Select.svelte'
|
||||
import { mcpEndpointTools } from '$lib/mcpEndpointTools'
|
||||
import InfoIcon from 'lucide-svelte/icons/info'
|
||||
|
||||
interface Props {
|
||||
showMcpMode?: boolean
|
||||
@@ -70,6 +71,10 @@
|
||||
let customScopes = $state<string[]>([])
|
||||
let showCustomScopes = $state(false)
|
||||
|
||||
// Wildcard pattern inputs for custom scope
|
||||
let customScriptPatterns = $state<string>('')
|
||||
let customFlowPatterns = $state<string>('')
|
||||
|
||||
function ensureCurrentWorkspaceIncluded(
|
||||
workspacesList: UserWorkspace[],
|
||||
currentWorkspace: string | undefined
|
||||
@@ -84,6 +89,21 @@
|
||||
return [{ id: currentWorkspace, name: currentWorkspace }, ...workspacesList]
|
||||
}
|
||||
|
||||
function parsePatterns(input: string): string[] {
|
||||
return input
|
||||
.split(',')
|
||||
.map((p) => p.trim())
|
||||
.filter((p) => p.length > 0)
|
||||
}
|
||||
|
||||
// Clear pattern inputs when MCP mode is disabled OR when not in custom scope
|
||||
$effect(() => {
|
||||
if (!mcpCreationMode || newMcpScope !== 'custom') {
|
||||
customScriptPatterns = ''
|
||||
customFlowPatterns = ''
|
||||
}
|
||||
})
|
||||
|
||||
async function createToken(mcpMode: boolean = false): Promise<void> {
|
||||
try {
|
||||
let date: Date | undefined
|
||||
@@ -94,14 +114,28 @@
|
||||
let tokenScopes = scopes
|
||||
if (mcpMode) {
|
||||
if (newMcpScope === 'custom') {
|
||||
// Granular scope format
|
||||
// Granular scope format - combine individual selections with wildcard patterns
|
||||
tokenScopes = []
|
||||
if (selectedScripts.length > 0) {
|
||||
tokenScopes.push(`mcp:scripts:${selectedScripts.join(',')}`)
|
||||
|
||||
// Scripts: combine individual selections with patterns
|
||||
let scriptPaths = [...selectedScripts]
|
||||
if (customScriptPatterns.trim()) {
|
||||
scriptPaths.push(...parsePatterns(customScriptPatterns))
|
||||
}
|
||||
if (selectedFlows.length > 0) {
|
||||
tokenScopes.push(`mcp:flows:${selectedFlows.join(',')}`)
|
||||
if (scriptPaths.length > 0) {
|
||||
tokenScopes.push(`mcp:scripts:${scriptPaths.join(',')}`)
|
||||
}
|
||||
|
||||
// Flows: combine individual selections with patterns
|
||||
let flowPaths = [...selectedFlows]
|
||||
if (customFlowPatterns.trim()) {
|
||||
flowPaths.push(...parsePatterns(customFlowPatterns))
|
||||
}
|
||||
if (flowPaths.length > 0) {
|
||||
tokenScopes.push(`mcp:flows:${flowPaths.join(',')}`)
|
||||
}
|
||||
|
||||
// Endpoints: no wildcard support needed
|
||||
if (selectedEndpoints.length > 0) {
|
||||
tokenScopes.push(`mcp:endpoints:${selectedEndpoints.join(',')}`)
|
||||
}
|
||||
@@ -537,6 +571,47 @@
|
||||
Selected: {selectedScripts.length} scripts, {selectedFlows.length} flows, {selectedEndpoints.length}
|
||||
endpoints
|
||||
</div>
|
||||
|
||||
<!-- Wildcard Patterns Section -->
|
||||
<div class="flex flex-col gap-2 mt-4 pt-4 border-t border-surface-hover">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="block text-xs font-semibold">Script wildcard patterns</span>
|
||||
<Popover notClickable>
|
||||
{#snippet text()}
|
||||
<div class="text-xs max-w-xs">
|
||||
<p class="font-semibold mb-2">Add folder wildcards or complex patterns</p>
|
||||
<p class="mb-1"><b>Examples:</b></p>
|
||||
<ul class="list-disc ml-4 space-y-1">
|
||||
<li><code>f/folder/*</code> - all scripts/flows in folder</li>
|
||||
<li><code>f/folder1/*,f/folder2/*</code> - multiple folders</li>
|
||||
<li>Mix: <code>f/folder/*,f/specific/path</code></li>
|
||||
</ul>
|
||||
<p class="mt-2 text-xs text-secondary">
|
||||
Patterns are combined with individual selections above.
|
||||
</p>
|
||||
</div>
|
||||
{/snippet}
|
||||
<Button color="light" size="xs2" nonCaptureEvent startIcon={{ icon: InfoIcon }}>
|
||||
Pattern Help
|
||||
</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
<TextInput
|
||||
inputProps={{ placeholder: 'e.g., f/outline/*,f/docs/*' }}
|
||||
bind:value={customScriptPatterns}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="block text-xs font-semibold">Flow wildcard patterns</span>
|
||||
</div>
|
||||
<TextInput
|
||||
inputProps={{ placeholder: 'e.g., f/workflows/*' }}
|
||||
bind:value={customFlowPatterns}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if mcpCreationMode && (newMcpScope !== 'folder' || selectedFolder.length > 0)}
|
||||
@@ -616,7 +691,9 @@
|
||||
(newMcpScope === 'custom' &&
|
||||
selectedScripts.length === 0 &&
|
||||
selectedFlows.length === 0 &&
|
||||
selectedEndpoints.length === 0))}
|
||||
selectedEndpoints.length === 0 &&
|
||||
!customScriptPatterns.trim() &&
|
||||
!customFlowPatterns.trim()))}
|
||||
variant="accent"
|
||||
>
|
||||
New token
|
||||
|
||||
Reference in New Issue
Block a user