mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-21 00:02:30 +00:00
fix: read the MCP URL policy when a URL is asked for, and drop the all-workspaces option
Two review findings on the token drawer: The policy was read once per page load and cached for the browser session, so a superadmin turning the setting on left every open tab handing out `?token=` URLs the server now refuses. Both entry points now read it when the user actually asks for an MCP URL: when MCP mode is entered, and when the connect drawer opens. The workspace picker offered "All workspaces / Multi-workspace", but the gateway's consent screen binds the token it issues to the one workspace picked there, so OAuth has no multi-workspace grant to hand out. That entry is now token-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0cd9306068
commit
7206f92f63
@@ -6,7 +6,6 @@
|
||||
import { Bot, ExternalLink, Terminal } from 'lucide-svelte'
|
||||
import { shell } from 'svelte-highlight/languages'
|
||||
import { mcpTokenUrlDisabled } from '$lib/mcpAuth'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
type ConnectTab = 'cli' | 'mcp'
|
||||
|
||||
@@ -22,15 +21,12 @@ wmill workspace add ${workspaceId} ${workspaceId} ${origin}
|
||||
wmill init
|
||||
wmill sync pull`)
|
||||
|
||||
onMount(async () => {
|
||||
tokenUrlDisabled = await mcpTokenUrlDisabled()
|
||||
})
|
||||
|
||||
function noop() {}
|
||||
|
||||
export function openDrawer(tab: ConnectTab = 'cli') {
|
||||
selectedTab = tab
|
||||
openVersion += 1
|
||||
void mcpTokenUrlDisabled().then((v) => (tokenUrlDisabled = v))
|
||||
drawer?.openDrawer()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onMount, untrack } from 'svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { userWorkspaces, workspaceStore, type UserWorkspace } from '$lib/stores'
|
||||
import { Alert, Button } from '../common'
|
||||
import { triggerableByAI } from '$lib/actions/triggerableByAI.svelte'
|
||||
@@ -75,6 +75,7 @@
|
||||
|
||||
function enterMcpMode() {
|
||||
mcpCreationMode = true
|
||||
void mcpTokenUrlDisabled().then((v) => (tokenUrlDisabled = v))
|
||||
newTokenExpiration = undefined
|
||||
newTokenWorkspace = defaultNewTokenWorkspace ?? $workspaceStore
|
||||
newToken = undefined
|
||||
@@ -157,12 +158,6 @@
|
||||
)
|
||||
const mcpBaseUrl = $derived(`${mcpUrl}?token=`)
|
||||
|
||||
onMount(async () => {
|
||||
if (showMcpMode || mcpOnly || openWithMcpMode) {
|
||||
tokenUrlDisabled = await mcpTokenUrlDisabled()
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
const requestedMcpMode = mcpOnly || openWithMcpMode
|
||||
if (requestedMcpMode === lastRequestedMcpMode) {
|
||||
@@ -225,16 +220,11 @@
|
||||
{#if !lockWorkspace}
|
||||
<div class="mb-4 max-w-md">
|
||||
<span class="block mb-1 text-emphasis text-xs font-semibold">Workspace</span>
|
||||
<!-- No all-workspaces entry: the gateway's consent screen binds the token it issues
|
||||
to the one workspace picked there, so OAuth has no multi-workspace grant to offer. -->
|
||||
<Select
|
||||
bind:value={newTokenWorkspace}
|
||||
items={[
|
||||
{
|
||||
label: 'All workspaces',
|
||||
value: ALL_WORKSPACES,
|
||||
subtitle: 'Multi-workspace'
|
||||
},
|
||||
...workspaces.map((w) => ({ label: w.name, value: w.id, subtitle: w.id }))
|
||||
]}
|
||||
items={workspaces.map((w) => ({ label: w.name, value: w.id, subtitle: w.id }))}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
+14
-12
@@ -1,20 +1,22 @@
|
||||
import { SettingService } from '$lib/gen'
|
||||
|
||||
let cached: Promise<boolean> | undefined
|
||||
|
||||
/**
|
||||
* Whether the instance refuses `?token=` on the MCP endpoints. When it does, an MCP URL is
|
||||
* handed over bare and the client reaches it by completing the OAuth flow, so nothing in the
|
||||
* UI should offer to mint a token for one.
|
||||
*
|
||||
* Deliberately uncached: callers read it at the moment an MCP URL is asked for, so a superadmin
|
||||
* flipping the setting does not leave open tabs handing out URLs the server now refuses.
|
||||
*/
|
||||
export function mcpTokenUrlDisabled(): Promise<boolean> {
|
||||
cached ??= SettingService.getGlobal({ key: 'mcp_disable_token_query_param' })
|
||||
.then((v) => (v as boolean | null) ?? false)
|
||||
.catch((err) => {
|
||||
console.error('Failed to load the MCP token setting:', err)
|
||||
// Retry on the next caller rather than pinning the fallback for the session.
|
||||
cached = undefined
|
||||
return false
|
||||
})
|
||||
return cached
|
||||
export async function mcpTokenUrlDisabled(): Promise<boolean> {
|
||||
try {
|
||||
return (
|
||||
((await SettingService.getGlobal({
|
||||
key: 'mcp_disable_token_query_param'
|
||||
})) as boolean | null) ?? false
|
||||
)
|
||||
} catch (err) {
|
||||
console.error('Failed to load the MCP token setting:', err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user