docs(web): explain tool approval settings flow

Adds concise reviewer comments around the nested Configure route, backend-backed query and mutation hooks, the virtualized catalog, per-tool rows, and the Manage-menu ownership boundary.
This commit is contained in:
Sreeram Sreedhar 2026-08-03 13:28:40 +05:30
parent 749deee12c
commit a26d988ada
4 changed files with 26 additions and 6 deletions

View file

@ -96,6 +96,10 @@ function ScopeChip({
const menuItemClass =
"gap-2.5 rounded-lg px-2.5 py-2 text-sm font-medium text-white/85 hover:bg-white/[0.06] focus:bg-white/[0.06] focus:text-white cursor-pointer"
/**
* Keeps personal tool permissions in the same Manage menu as connection
* actions, while workspace connection actions remain admin-only.
*/
function AppCard({
name,
subtitle,

View file

@ -24,6 +24,7 @@ function titleCase(value: string) {
return value.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
}
/** Compact Ask/Always allow control shared by the connection default and rows. */
function Segmented({
value,
options,
@ -61,6 +62,10 @@ function Segmented({
)
}
/**
* Renders one backend-classified tool. Read tools are informational; only tools
* that can change state expose an individual approval override.
*/
function ToolRow({
tool,
disabled,
@ -121,6 +126,11 @@ function ToolRow({
)
}
/**
* Presents one connection's catalog and persists only the user's chosen default
* or named override. Decisions come from the backend so this page cannot drift
* from lease, scheduled-run, or runtime safety rules.
*/
export default function CompanyBrainToolApprovals({
serverSlug,
}: {
@ -151,6 +161,7 @@ export default function CompanyBrainToolApprovals({
})
}, [data?.tools, search])
// MCP servers may expose hundreds of tools, so render only visible rows.
const virtualizer = useVirtualizer({
count: tools.length,
getScrollElement: () => scrollRef.current,
@ -309,11 +320,7 @@ export default function CompanyBrainToolApprovals({
No tools match "{search}".
</p>
) : (
<div
ref={scrollRef}
className="max-h-[520px] overflow-y-auto"
// A connected server can expose hundreds of tools.
>
<div ref={scrollRef} className="max-h-[520px] overflow-y-auto">
<div
style={{
height: virtualizer.getTotalSize(),

View file

@ -35,6 +35,10 @@ function url(serverSlug: string) {
return `${BACKEND}/brain/mcp-connections/${serverSlug}/tool-approvals`
}
/**
* Loads the backend-computed catalog view and polls only while a cold MCP tool
* list is warming inside the organization Durable Object.
*/
export function useToolApprovals(serverSlug: string, enabled: boolean) {
const { org } = useAuth()
return useQuery({
@ -52,6 +56,10 @@ export function useToolApprovals(serverSlug: string, enabled: boolean) {
})
}
/**
* Applies a partial default/rule update and replaces the cached view with the
* server response so visible decisions always match runtime precedence.
*/
export function useUpdateToolApprovals(serverSlug: string) {
const { org } = useAuth()
const queryClient = useQueryClient()

View file

@ -34,11 +34,12 @@ export function pathToConfigureSection(
return null
}
// /configure/tools/<serverSlug> — the per-server tool approval page.
/** Builds the nested Configure route for one server's tool permissions. */
export function configureToolPath(serverSlug: string): string {
return `/configure/tools/${serverSlug}`
}
/** Extracts exactly one server slug so deeper or malformed paths stay invalid. */
export function pathToConfigureToolSlug(pathname: string): string | null {
const trimmed = pathname.replace(/\/$/, "")
return trimmed.match(/^\/configure\/tools\/([^/]+)$/)?.[1] ?? null