authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-04 21:43:24-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-04 21:43:43-07:00
log5e5766a9141ea0439e3e4b414e88749d908626c5
tree21a5fce0d7ccefac3014c0215475d97d09affe79
parent135b25088bc13f6cd6476e2fe8eeed863512e669
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

Put Shale linking directly in MCP approval requests

Unlinked requests now offer Link account as their primary action and preserve the request through sign-in. Linked requests retain repository selection and explicit approval. Assisted-by: gpt-6.1-sol

1 files changed, 10 insertions(+), 7 deletions(-)

dashboard/web/pages/MCP.tsx+10-7
......@@ -33,6 +33,13 @@ export function MCP() {
3333 };
3434 });
3535 onCleanup(() => events?.close());
36 const linkShale = async () => {
37 setBusy(true);
38 try {
39 const result = await parseResponse(api.mcp.shale.$post({ json: { request: consent()?.scopes.includes("shale:read") ? request() || undefined : undefined } }));
40 window.location.assign(result.redirect);
41 } catch (error) { toast(reason(error)); setBusy(false); }
42 };
3643 const answer = async (deny: boolean) => {
3744 setBusy(true);
3845 try {
......@@ -52,10 +59,10 @@ export function MCP() {
5259 <div class="mcp-resources"><For each={details().resources}>{(resource) =>
5360 <Checkbox checked={picked().includes(resource.id)} onChange={(checked) => setPicked(checked ? [...picked(), resource.id] : picked().filter((item) => item !== resource.id))}>{resource.name}</Checkbox>
5461 }</For></div>
55 <Show when={!details().linked}><p>Link your Shale account below to choose repositories.</p></Show>
62 <Show when={!details().linked}><p>Link your Shale account to choose repositories.</p></Show>
5663 <Show when={details().linked && !details().resources.length}><p>No resources are available to your account.</p></Show>
5764 <Show when={details().scopes.includes("offline_access")}><p class="muted">This connection can refresh access without another sign-in.</p></Show>
58 <div class="mcp-actions"><button class="button" disabled={!picked().length || busy()} onClick={() => answer(false)}>Allow access</button>
65 <div class="mcp-actions"><Show when={details().linked} fallback={<button class="button" disabled={busy()} onClick={linkShale}>Link account</button>}><button class="button" disabled={!picked().length || busy()} onClick={() => answer(false)}>Allow access</button></Show>
5966 <button class="button secondary" disabled={busy()} onClick={() => answer(true)}>Decline</button></div>
6067 </section>}
6168 </Loaded>
......@@ -71,11 +78,7 @@ export function MCP() {
7178 <Show when={data().shale} fallback={<p>Link your Shale account to grant clients access to its repositories.</p>}>
7279 {(shale) => <p>Account linked <Ago t={shale().linkedAt} /></p>}
7380 </Show>
74 <button class="button" disabled={busy()} onClick={async () => {
75 setBusy(true);
76 try { const result = await parseResponse(api.mcp.shale.$post({ json: { request: consent()?.scopes.includes("shale:read") ? request() || undefined : undefined } })); window.location.assign(result.redirect); }
77 catch (error) { toast(reason(error)); setBusy(false); }
78 }}>{data().shale ? "Relink account" : "Link account"}</button>
81 <button class="button" disabled={busy()} onClick={linkShale}>{data().shale ? "Relink account" : "Link account"}</button>
7982 <Show when={data().shale}><button class="button secondary" disabled={busy()} onClick={async () => {
8083 setBusy(true);
8184 try { await parseResponse(api.mcp.shale.$delete()); await refetch(); }