From 7e260317d63da2eda08d23096c04963918fc0993 Mon Sep 17 00:00:00 2001 From: Anirudh Cheriyachanaseri Bijay Date: Thu, 25 Jun 2026 02:04:48 +0530 Subject: [PATCH 1/5] Use cross-platform temp dir --- plugins/glean/dist/index.js | 4 ++-- src/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/glean/dist/index.js b/plugins/glean/dist/index.js index a1f39e7..ce02361 100644 --- a/plugins/glean/dist/index.js +++ b/plugins/glean/dist/index.js @@ -22984,7 +22984,7 @@ var StdioServerTransport = class { // src/index.ts import path7 from "node:path"; import fs7 from "node:fs"; -import { homedir as homedir4 } from "node:os"; +import { homedir as homedir4, tmpdir } from "node:os"; // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/client.js var ExperimentalClientTasks = class { @@ -26279,7 +26279,7 @@ function resolveSkillsBaseDir() { if (process.env.SKILLS_BASE_DIR) { return process.env.SKILLS_BASE_DIR; } - return path7.join("/tmp", "glean-skills-cache"); + return path7.join(tmpdir(), "glean-skills-cache"); } var server = new Server( { name: "glean", version: "1.0.0" }, diff --git a/src/index.ts b/src/index.ts index 3034752..b4596d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import { } from "@modelcontextprotocol/sdk/types.js"; import path from "node:path"; import fs from "node:fs"; -import { homedir } from "node:os"; +import { homedir, tmpdir } from "node:os"; import { AuthRequiredError, createRemoteClient, @@ -118,7 +118,7 @@ function resolveSkillsBaseDir(): string { if (process.env.SKILLS_BASE_DIR) { return process.env.SKILLS_BASE_DIR; } - return path.join("/tmp", "glean-skills-cache"); + return path.join(tmpdir(), "glean-skills-cache"); } const server = new Server( From 75195351cd784ac3ef55df46c3da3fd90eef2f15 Mon Sep 17 00:00:00 2001 From: Anirudh Cheriyachanaseri Bijay Date: Thu, 25 Jun 2026 02:25:57 +0530 Subject: [PATCH 2/5] Replace `start.sh` with cross-platform `start.mjs` --- README.md | 10 ++--- plugins/glean/.mcp.codex.json | 4 +- plugins/glean/.mcp.json | 4 +- plugins/glean/start.mjs | 70 +++++++++++++++++++++++++++++++++++ plugins/glean/start.sh | 47 ----------------------- src/session-id.ts | 2 +- src/tools/approval-args.ts | 2 +- 7 files changed, 81 insertions(+), 58 deletions(-) create mode 100644 plugins/glean/start.mjs delete mode 100755 plugins/glean/start.sh diff --git a/README.md b/README.md index ae6a5d4..16c1bb9 100644 --- a/README.md +++ b/README.md @@ -116,8 +116,8 @@ HITL ones — or in your shell: Empty values and un-interpolated `${VAR}` placeholders are ignored, falling back to the default. -The launcher (`plugins/glean/start.sh`) also derives and **exports** three more -variables the bundle reads, so these are internal — start.sh overwrites them on +The launcher (`plugins/glean/start.mjs`) also derives and **exports** three more +variables the bundle reads, so these are internal — start.mjs overwrites them on every launch and setting them yourself has no effect: - `PLUGIN_DATA_DIR` — directory for credentials, caches, the saved server URL, @@ -195,9 +195,9 @@ plugins/glean/ by `npm run build`; checked in) skills/glean_run/ Skill that tells the agent how to use the tools. Uses the open SKILL.md standard. - start.sh Bash launcher: sanitizes env (SKILLS_BASE_DIR, - PLUGIN_DATA_DIR, GLEAN_SESSION_ID), then execs - node on the bundle + start.mjs Cross-platform Node launcher: sanitizes env + (SKILLS_BASE_DIR, PLUGIN_DATA_DIR, GLEAN_SESSION_ID), + then imports the dist/index.js bundle in-process package.json Minimal "type": "module" manifest so Node treats dist/index.js as ESM at runtime src/ TypeScript sources for the MCP server diff --git a/plugins/glean/.mcp.codex.json b/plugins/glean/.mcp.codex.json index c49dc45..c7b69eb 100644 --- a/plugins/glean/.mcp.codex.json +++ b/plugins/glean/.mcp.codex.json @@ -1,8 +1,8 @@ { "mcpServers": { "glean-local": { - "command": "bash", - "args": ["./start.sh"], + "command": "node", + "args": ["./start.mjs"], "cwd": ".", "env": { "ENABLE_HITL": "true", diff --git a/plugins/glean/.mcp.json b/plugins/glean/.mcp.json index 1cb1410..b11da8a 100644 --- a/plugins/glean/.mcp.json +++ b/plugins/glean/.mcp.json @@ -1,8 +1,8 @@ { "mcpServers": { "glean": { - "command": "bash", - "args": ["${CLAUDE_PLUGIN_ROOT}/start.sh"], + "command": "node", + "args": ["${CLAUDE_PLUGIN_ROOT}/start.mjs"], "env": { "ENABLE_HITL": "true", "HITL_TIMEOUT_MS": "300000" diff --git a/plugins/glean/start.mjs b/plugins/glean/start.mjs new file mode 100644 index 0000000..84e549b --- /dev/null +++ b/plugins/glean/start.mjs @@ -0,0 +1,70 @@ +#!/usr/bin/env node +// @ts-check +// Invoked by the plugin host (Claude Code, Codex, or Cursor) to launch the Glean +// MCP server. The plugin ships a single-file esbuild output at dist/index.js with +// every non-builtin inlined — no node_modules next to it. This script handles +// env sanitation before launching the plugin proper. +import os from "node:os"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; + +// Treat empty strings and un-interpolated "${VAR}" placeholders (which a host +// may pass through verbatim when a variable is unset) as "not set" — matching +// the bundle's own readEnv / resolveSessionId guards. +/** @param {string | undefined} v @returns {string | undefined} */ +function val(v) { + if (v === undefined) return undefined; + const t = v.trim(); + if (t === "" || t.startsWith("${")) return undefined; + return t; +} + +const launchCwd = process.cwd(); + +// Resolve where credentials, caches, and config are stored. +// CLAUDE_PLUGIN_DATA is the managed lifecycle dir provided by the plugin host. +const pluginDataDir = + val(process.env.CLAUDE_PLUGIN_DATA) ?? + path.join(os.homedir() || os.tmpdir(), ".glean"); +process.env.PLUGIN_DATA_DIR = pluginDataDir; + +// Discovered skill files are written under the data dir by default, so the +// skills cache tracks PLUGIN_DATA_DIR instead of being resolved separately. +let skillsBaseDir = path.join(pluginDataDir, "glean-skills-cache"); + +// Opt-in: when USE_CLAUDE_PROJECT_DIR=1, route the skills cache under the launch +// project's .claude/tmp/ so the glean_run skill's allowed-tools Read glob can +// match cache files via a path anchored to the project root. projectDir is the +// git repo root for the launch cwd, falling back to the launch cwd when it is +// not inside a git repo (or git is unavailable). +if (process.env.USE_CLAUDE_PROJECT_DIR === "1") { + let projectDir = launchCwd; + try { + const top = execFileSync( + "git", + ["-C", launchCwd, "rev-parse", "--show-toplevel"], + { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }, + ).trim(); + if (top) projectDir = top; + } catch { + /* not a git repo or git missing: keep the launch cwd fallback */ + } + skillsBaseDir = path.join(projectDir, ".claude", "tmp", "glean-skills-cache"); +} +process.env.SKILLS_BASE_DIR = skillsBaseDir; + +// Resolve the chat session id host-side. Host-awareness lives here, not in the +// plugin: the launcher reads whatever variable this host exposes and exports the +// normalized GLEAN_SESSION_ID that the Node bundle reads. Claude Code exposes +// CLAUDE_CODE_SESSION_ID; Codex exposes the conversation id as CODEX_THREAD_ID. +// Hosts that expose no session id (Cursor) leave it unset, and the plugin falls +// back to a generated per-process id. +const sessionId = + val(process.env.CLAUDE_CODE_SESSION_ID) ?? val(process.env.CODEX_THREAD_ID); +if (sessionId !== undefined) { + process.env.GLEAN_SESSION_ID = sessionId; +} + +// Boot the server in-process. Import via a file URL resolved against this +// module so the dynamic specifier works regardless of cwd and on Windows paths. +await import(new URL("./dist/index.js", import.meta.url).href); diff --git a/plugins/glean/start.sh b/plugins/glean/start.sh deleted file mode 100755 index 7779fcb..0000000 --- a/plugins/glean/start.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -# Invoked by the plugin host (Claude Code, Codex, or Cursor) to launch the Glean -# MCP server. The plugin ships a single-file esbuild output at dist/index.js with -# every non-builtin inlined — no node_modules next to it. This script handles -# env sanitation before launching the Node process. -set -e -LAUNCH_CWD="$PWD" -PLUGIN_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Resolve where credentials, caches, and config are stored. -# CLAUDE_PLUGIN_DATA is the managed lifecycle dir provided by the plugin host. -if [ -n "${CLAUDE_PLUGIN_DATA}" ]; then - export PLUGIN_DATA_DIR="$CLAUDE_PLUGIN_DATA" -else - export PLUGIN_DATA_DIR="${HOME:-/tmp}/.glean" -fi - -# Discovered skill files are written under the data dir by default, so the -# skills cache tracks PLUGIN_DATA_DIR instead of being resolved separately. -export SKILLS_BASE_DIR="$PLUGIN_DATA_DIR/glean-skills-cache" - -# Opt-in: when USE_CLAUDE_PROJECT_DIR=1, route the skills cache under the -# launch project's .claude/tmp/ so the glean_run skill's allowed-tools Read -# glob can match cache files via a path anchored to the project root. -# PROJECT_DIR is the git repo root for the launch cwd, falling back to the -# launch cwd when it is not inside a git repo. -if [ "${USE_CLAUDE_PROJECT_DIR:-}" = "1" ]; then - PROJECT_DIR=$(git -C "$LAUNCH_CWD" rev-parse --show-toplevel 2>/dev/null || true) - if [ -z "$PROJECT_DIR" ]; then - PROJECT_DIR="$LAUNCH_CWD" - fi - export SKILLS_BASE_DIR="$PROJECT_DIR/.claude/tmp/glean-skills-cache" -fi - -# Resolve the chat session id host-side. Host-awareness lives here, not in the -# plugin: the launcher reads whatever variable this host exposes and exports the -# normalized GLEAN_SESSION_ID that the Node bundle reads. Claude Code exposes -# CLAUDE_CODE_SESSION_ID; Codex exposes the conversation id as CODEX_THREAD_ID. -# Hosts that expose no session id (Cursor) leave it unset, and the plugin falls -# back to a generated per-process id. -if [ -n "${CLAUDE_CODE_SESSION_ID:-}" ]; then - export GLEAN_SESSION_ID="$CLAUDE_CODE_SESSION_ID" -elif [ -n "${CODEX_THREAD_ID:-}" ]; then - export GLEAN_SESSION_ID="$CODEX_THREAD_ID" -fi - -exec node "$PLUGIN_DIR/dist/index.js" diff --git a/src/session-id.ts b/src/session-id.ts index 937317f..d8c0f93 100644 --- a/src/session-id.ts +++ b/src/session-id.ts @@ -4,7 +4,7 @@ let fallbackSessionId: string | undefined; /** * Resolves the chat session id from GLEAN_SESSION_ID, which the host-aware - * launcher (start.sh) exports after reading whatever variable the current host + * launcher (start.mjs) exports after reading whatever variable the current host * uses for the session/conversation id. The plugin itself stays host-agnostic * and never reads host-specific env vars. * diff --git a/src/tools/approval-args.ts b/src/tools/approval-args.ts index 95faa04..de6be3b 100644 --- a/src/tools/approval-args.ts +++ b/src/tools/approval-args.ts @@ -116,7 +116,7 @@ export function formatArgumentsForFile( } // The full arguments are written to a per-session file under the plugin's data -// dir (CLAUDE_PLUGIN_DATA, exported by start.sh as PLUGIN_DATA_DIR). The file is +// dir (CLAUDE_PLUGIN_DATA, set by start.mjs as PLUGIN_DATA_DIR). The file is // scoped to the chat session id so parallel sessions don't overwrite each // other; within a session it is intentionally overwritten on each approval — // only the most recent prompt's arguments need to be inspectable. From b858f68d45d6472d7c9d515fdde501668cf60bdd Mon Sep 17 00:00:00 2001 From: Anirudh Cheriyachanaseri Bijay Date: Thu, 25 Jun 2026 02:27:16 +0530 Subject: [PATCH 3/5] Bump plugin version --- plugins/glean/.claude-plugin/plugin.json | 2 +- plugins/glean/.codex-plugin/plugin.json | 2 +- plugins/glean/.cursor-plugin/plugin.json | 2 +- scripts/check-version-bump.sh | 2 +- scripts/pack-plugin.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/glean/.claude-plugin/plugin.json b/plugins/glean/.claude-plugin/plugin.json index 6514bba..711d058 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.34", + "version": "0.2.35", "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 2e18a5a..feedbf4 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.34", + "version": "0.2.35", "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 d197d8e..9495404 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.34", + "version": "0.2.35", "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/scripts/check-version-bump.sh b/scripts/check-version-bump.sh index 34efd79..42c6ad8 100755 --- a/scripts/check-version-bump.sh +++ b/scripts/check-version-bump.sh @@ -20,7 +20,7 @@ MANIFESTS=( ) # Only trigger on files that affect the plugin runtime — not CI or tooling. -PLUGIN_PATHS='^(src/|plugins/glean/(dist/|skills/|start\.sh|\.mcp\.json|\.mcp\.codex\.json|package\.json|\.(claude|codex|cursor)-plugin/plugin\.json))' +PLUGIN_PATHS='^(src/|plugins/glean/(dist/|skills/|start\.mjs|\.mcp\.json|\.mcp\.codex\.json|package\.json|\.(claude|codex|cursor)-plugin/plugin\.json))' if ! git diff --name-only "$BASE_REF"...HEAD | grep -qE "$PLUGIN_PATHS"; then echo "No plugin files changed — skipping version check." diff --git a/scripts/pack-plugin.sh b/scripts/pack-plugin.sh index 0bcf290..3666f06 100755 --- a/scripts/pack-plugin.sh +++ b/scripts/pack-plugin.sh @@ -22,7 +22,7 @@ zip -r "$OUT" \ .mcp.json \ dist \ skills \ - start.sh \ + start.mjs \ package.json \ >/dev/null From 267ea038acc70a5b010d7d072159cb21d8bbeda3 Mon Sep 17 00:00:00 2001 From: Anirudh Cheriyachanaseri Bijay Date: Tue, 30 Jun 2026 15:52:25 +0530 Subject: [PATCH 4/5] Update pre-commit hook to check `start.mjs` instead of `start.sh` --- .githooks/pre-commit.d/02-auto-bump-version.sh | 2 +- plugins/glean/.claude-plugin/plugin.json | 2 +- plugins/glean/.codex-plugin/plugin.json | 2 +- plugins/glean/.cursor-plugin/plugin.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.githooks/pre-commit.d/02-auto-bump-version.sh b/.githooks/pre-commit.d/02-auto-bump-version.sh index 79b8a47..a2a9792 100755 --- a/.githooks/pre-commit.d/02-auto-bump-version.sh +++ b/.githooks/pre-commit.d/02-auto-bump-version.sh @@ -27,7 +27,7 @@ MANIFESTS=( "plugins/glean/.cursor-plugin/plugin.json" ) -PLUGIN_PATHS='^(src/|plugins/glean/(dist/|skills/|start\.sh|\.mcp\.json|\.mcp\.codex\.json|package\.json|\.(claude|codex|cursor)-plugin/plugin\.json))' +PLUGIN_PATHS='^(src/|plugins/glean/(dist/|skills/|start\.mjs|\.mcp\.json|\.mcp\.codex\.json|package\.json|\.(claude|codex|cursor)-plugin/plugin\.json))' if ! git diff --cached --name-only | grep -qE "$PLUGIN_PATHS"; then exit 0 diff --git a/plugins/glean/.claude-plugin/plugin.json b/plugins/glean/.claude-plugin/plugin.json index 711d058..301b08a 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.35", + "version": "0.2.36", "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 feedbf4..7b31deb 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.35", + "version": "0.2.36", "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 9495404..232df50 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.35", + "version": "0.2.36", "description": "Search and act across your company's apps — Jira, Slack, Salesforce, Google Workspace, and more — without leaving Cursor.", "author": { "name": "Glean" From 876f71393ae1fd726765d9c77b0ccf0cda74e7c7 Mon Sep 17 00:00:00 2001 From: Anirudh Cheriyachanaseri Bijay Date: Fri, 3 Jul 2026 11:51:04 +0530 Subject: [PATCH 5/5] Bump version to 0.2.38 --- plugins/glean/.claude-plugin/plugin.json | 2 +- plugins/glean/.codex-plugin/plugin.json | 2 +- plugins/glean/.cursor-plugin/plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/glean/.claude-plugin/plugin.json b/plugins/glean/.claude-plugin/plugin.json index 256dc47..c2700e0 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.37", + "version": "0.2.38", "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 68fd1d3..6fdd874 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.37", + "version": "0.2.38", "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 e6b0d3c..788f628 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.37", + "version": "0.2.38", "description": "Search and act across your company's apps — Jira, Slack, Salesforce, Google Workspace, and more — without leaving Cursor.", "author": { "name": "Glean"