From 0e445f14aea738cb97ff6cef52b9648dd4e8915b Mon Sep 17 00:00:00 2001 From: hugocasa Date: Fri, 5 Dec 2025 19:14:03 +0100 Subject: [PATCH] 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 --- .../components/settings/CreateToken.svelte | 89 +++++++++++++++++-- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/components/settings/CreateToken.svelte b/frontend/src/lib/components/settings/CreateToken.svelte index 417c911d43..64b73ad3f4 100644 --- a/frontend/src/lib/components/settings/CreateToken.svelte +++ b/frontend/src/lib/components/settings/CreateToken.svelte @@ -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([]) let showCustomScopes = $state(false) + // Wildcard pattern inputs for custom scope + let customScriptPatterns = $state('') + let customFlowPatterns = $state('') + 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 { 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 + + +
+
+
+ Script wildcard patterns + + {#snippet text()} +
+

Add folder wildcards or complex patterns

+

Examples:

+
    +
  • f/folder/* - all scripts/flows in folder
  • +
  • f/folder1/*,f/folder2/* - multiple folders
  • +
  • Mix: f/folder/*,f/specific/path
  • +
+

+ Patterns are combined with individual selections above. +

+
+ {/snippet} + +
+
+ +
+
+
+ Flow wildcard patterns +
+ +
+
{/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