diff --git a/plugins/glean/.claude-plugin/plugin.json b/plugins/glean/.claude-plugin/plugin.json index 194d72f..e0bda4d 100644 --- a/plugins/glean/.claude-plugin/plugin.json +++ b/plugins/glean/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "glean-vnext", - "version": "0.2.32", + "version": "0.2.33", "description": "Glean plugin for discovering skills and running tools.", "author": { "name": "Glean" diff --git a/plugins/glean/.codex-plugin/plugin.json b/plugins/glean/.codex-plugin/plugin.json index dba787b..f358af1 100644 --- a/plugins/glean/.codex-plugin/plugin.json +++ b/plugins/glean/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "glean-vnext", - "version": "0.2.32", + "version": "0.2.33", "description": "Glean Codex plugin for discovering skills and running tools.", "author": { "name": "Glean" diff --git a/plugins/glean/.cursor-plugin/plugin.json b/plugins/glean/.cursor-plugin/plugin.json index 6d60e1c..a3428e7 100644 --- a/plugins/glean/.cursor-plugin/plugin.json +++ b/plugins/glean/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "glean-vnext", "displayName": "Glean vNext", - "version": "0.2.32", + "version": "0.2.33", "description": "Search and act across your company's apps — Jira, Slack, Salesforce, Google Workspace, and more — without leaving Cursor.", "author": { "name": "Glean" diff --git a/plugins/glean/dist/index.js b/plugins/glean/dist/index.js index 6a8ada8..a1f39e7 100644 --- a/plugins/glean/dist/index.js +++ b/plugins/glean/dist/index.js @@ -26374,14 +26374,23 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { !!server.getClientCapabilities()?.elicitation ) }; - const tools = [FIND_SKILLS_TOOL, runTool, SETUP_TOOL]; + const staticTools = [FIND_SKILLS_TOOL, runTool, SETUP_TOOL]; + const serve = (state, dynamic) => { + logLine("tools-list.served", { + static: staticTools.length, + dynamic: dynamic.length, + names: dynamic.map((t) => t.name), + state + }); + return { tools: [...staticTools, ...dynamic] }; + }; const serverUrl = resolveServerUrl(); if (!serverUrl) { - return { tools: [...tools, ...cachedRemoteTools] }; + return serve("unconfigured", cachedRemoteTools); } const provider = getOAuthProvider(); if (!provider.tokens()) { - return { tools: [...tools, ...cachedRemoteTools] }; + return serve("unauthenticated", cachedRemoteTools); } let remoteClient; try { @@ -26393,21 +26402,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { } catch (err) { const msg = err instanceof Error ? err.message : String(err); logLine("connect.backend-error", { label: "tools/list", msg }); - return { tools: [...tools, ...cachedRemoteTools] }; + return serve("connect-error", cachedRemoteTools); } try { const remoteTools = await fetchAllowedRemoteTools(remoteClient); cachedRemoteTools = remoteTools; saveRemoteTools(serverUrl, remoteTools); - tools.push(...remoteTools); + return serve("fetched", remoteTools); } catch (err) { const msg = err instanceof Error ? err.message : String(err); logLine("tools-list.fetch-failed", { label: "tools/list", msg }); - tools.push(...cachedRemoteTools); + return serve("fetch-failed", cachedRemoteTools); } finally { await remoteClient.close(); } - return { tools }; }); var SIGN_IN_WAIT_MS = 3e5; function withTimeout(p, ms) { diff --git a/src/index.ts b/src/index.ts index ee3a3e4..3034752 100644 --- a/src/index.ts +++ b/src/index.ts @@ -280,7 +280,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { !!server.getClientCapabilities()?.elicitation, ), }; - const tools: Tool[] = [FIND_SKILLS_TOOL, runTool, SETUP_TOOL]; + const staticTools: Tool[] = [FIND_SKILLS_TOOL, runTool, SETUP_TOOL]; + + // One structured line on every return path, so "why don't my tools appear?" + // is answerable from the log alone: `static` is constant, `names` lists the + // dynamic tools we actually surfaced (freshly fetched or served from cache), + // and `state` names the path we took. The allow-list only ever drops tools + // outside our fixed set, so a missing allow-listed name (e.g. `chat`) means + // the backend never returned it. Only tool *names*, counts and the state + // tag are logged — never argument values, which can carry PII/secrets. + const serve = (state: string, dynamic: Tool[]): { tools: Tool[] } => { + logLine("tools-list.served", { + static: staticTools.length, + dynamic: dynamic.length, + names: dynamic.map((t) => t.name), + state, + }); + return { tools: [...staticTools, ...dynamic] }; + }; // Pre-auth gate: tokens() is sync. When unauthenticated (or unconfigured) // skip the remote round-trip — but keep surfacing whatever we successfully @@ -290,11 +307,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { // emits [AUTHENTICATION_REQUIRED] during the sign-in step. const serverUrl = resolveServerUrl(); if (!serverUrl) { - return { tools: [...tools, ...cachedRemoteTools] }; + return serve("unconfigured", cachedRemoteTools); } const provider = getOAuthProvider(); if (!provider.tokens()) { - return { tools: [...tools, ...cachedRemoteTools] }; + return serve("unauthenticated", cachedRemoteTools); } let remoteClient; @@ -309,23 +326,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => { // static + last-known dynamic tools. Agent isn't blocked. const msg = err instanceof Error ? err.message : String(err); logLine("connect.backend-error", { label: "tools/list", msg }); - return { tools: [...tools, ...cachedRemoteTools] }; + return serve("connect-error", cachedRemoteTools); } try { const remoteTools = await fetchAllowedRemoteTools(remoteClient); cachedRemoteTools = remoteTools; saveRemoteTools(serverUrl, remoteTools); - tools.push(...remoteTools); + return serve("fetched", remoteTools); } catch (err) { const msg = err instanceof Error ? err.message : String(err); logLine("tools-list.fetch-failed", { label: "tools/list", msg }); - tools.push(...cachedRemoteTools); + return serve("fetch-failed", cachedRemoteTools); } finally { await remoteClient.close(); } - - return { tools }; }); // How long to wait for the user to complete the browser sign-in (open the