mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
feat(mcp): plugins:use OAuth scope
One coarse scope rather than per-plugin granularity. Which plugins are
installed is runtime data, so a per-plugin scope cannot be statically
enumerated: it would break the ${group}:${mode} derivation and the
AssertExact guards in nest-mcp-policy.ts, and its consent copy could not
be written ahead of time for a third-party plugin name. Tokens issued
before a plugin existed could never carry its scope either. Per-plugin
granularity already exists one layer down as the admin's mcp:tools grant.
'use' is a fourth mode, which widens ScopeMode. Nothing else moves:
McpAccessGroup and McpAccessMode derive from Scope, so both AssertExact
guards self-heal, and the policy's fallback branch already covers a third
mode the way it covers files:content.
The presets needed an explicit exclusion. Five of the six are written as
"everything except deletes", so plugins:use would have joined Claude.ai,
Cursor, Windsurf and Zed silently and handed out third-party tool
execution without anyone choosing it. That filter was duplicated across
the desktop and mobile files with nothing pinning it, so both now use one
shared constant, and FE-OAUTH-SCOPES-012 holds the line for the next
scope like this one.
The consent copy says what the scope does not do: the RPC host authorises
from grants frozen at construction, so this scope plus weather:read still
reaches whatever the plugin itself was granted.
Two i18n gates, not one. The dot-only group key is tracked by
i18n-parity.mjs, whose regex has no colon in its character class; the
colon-bearing label/description pair is invisible to it but is compared by
client/tests/unit/i18n/parity.test.ts, which diffs merged bundles. All 23
locales get all three keys, including the three in neither gate.
The consent glyph gets a ':use' branch. Falling through to the eye would
tell the user a tool-running scope only looks at things.
This commit is contained in:
@@ -9,7 +9,7 @@ import PhotoProvidersSection from './PhotoProvidersSection'
|
||||
import AirTrailConnectionSection from './AirTrailConnectionSection'
|
||||
import LlmConnectionSection from './LlmConnectionSection'
|
||||
import ApiKeysSection from './ApiKeysSection'
|
||||
import { ALL_SCOPES } from '../../api/oauthScopes'
|
||||
import { PRESET_SCOPES_DEFAULT, PRESET_SCOPES_READONLY } from '../../api/oauthScopes'
|
||||
import ScopeGroupPicker from '../OAuth/ScopeGroupPicker'
|
||||
import { useAuthStore } from '../../store/authStore'
|
||||
|
||||
@@ -27,42 +27,42 @@ const OAUTH_PRESETS: OAuthPreset[] = [
|
||||
label: 'Claude.ai',
|
||||
name: 'Claude.ai',
|
||||
uris: 'https://claude.ai/api/mcp/auth_callback',
|
||||
scopes: ALL_SCOPES.filter(s => !s.includes(':delete')),
|
||||
scopes: PRESET_SCOPES_DEFAULT,
|
||||
},
|
||||
{
|
||||
id: 'claude-desktop',
|
||||
label: 'Claude Desktop',
|
||||
name: 'Claude Desktop',
|
||||
uris: 'http://localhost',
|
||||
scopes: ALL_SCOPES.filter(s => !s.includes(':delete')),
|
||||
scopes: PRESET_SCOPES_DEFAULT,
|
||||
},
|
||||
{
|
||||
id: 'cursor',
|
||||
label: 'Cursor',
|
||||
name: 'Cursor',
|
||||
uris: 'http://localhost',
|
||||
scopes: ALL_SCOPES.filter(s => !s.includes(':delete')),
|
||||
scopes: PRESET_SCOPES_DEFAULT,
|
||||
},
|
||||
{
|
||||
id: 'vscode',
|
||||
label: 'VS Code',
|
||||
name: 'VS Code / Copilot',
|
||||
uris: 'http://localhost',
|
||||
scopes: ALL_SCOPES.filter(s => s.endsWith(':read')),
|
||||
scopes: PRESET_SCOPES_READONLY,
|
||||
},
|
||||
{
|
||||
id: 'windsurf',
|
||||
label: 'Windsurf',
|
||||
name: 'Windsurf',
|
||||
uris: 'http://localhost',
|
||||
scopes: ALL_SCOPES.filter(s => !s.includes(':delete')),
|
||||
scopes: PRESET_SCOPES_DEFAULT,
|
||||
},
|
||||
{
|
||||
id: 'zed',
|
||||
label: 'Zed',
|
||||
name: 'Zed',
|
||||
uris: 'http://localhost',
|
||||
scopes: ALL_SCOPES.filter(s => !s.includes(':delete')),
|
||||
scopes: PRESET_SCOPES_DEFAULT,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user