gemini_query
Send prompts to Google Gemini AI models through a secure CLI interface. Include local files using @path syntax and configure execution modes for safe interactions.
Instructions
Send a prompt to Google Gemini via locally authenticated CLI. Supports all Gemini models. Use @path to reference local files. Options: sandbox mode, yolo (auto-approve), approval modes, extra directories.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| prompt | Yes | Prompt for Gemini. Use @file.ts to include file context. | |
| model | No | Model name (e.g. gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite). Default: gemini-2.5-flash | |
| sandbox | No | Run in sandbox mode for safe code execution. | |
| yolo | No | Auto-approve all tool actions (no confirmation prompts). | |
| approval_mode | No | Approval mode: default (prompt), auto_edit (auto-approve edits), yolo (auto-approve all), plan (read-only). | |
| include_stats | No | Include token usage stats in the response. | |
| include_directories | No | Additional directories to include in Gemini's workspace. | |
| cwd | No | Working directory for file references (@ syntax). |
Implementation Reference
- src/index.ts:483-551 (handler)Registration and handler implementation for the gemini_query tool. It accepts parameters like prompt, model, sandbox, yolo, and approval_mode, and invokes runGeminiQuery.
server.tool( "gemini_query", "Send a prompt to Google Gemini via locally authenticated CLI. " + "Supports all Gemini models. Use @path to reference local files. " + "Options: sandbox mode, yolo (auto-approve), approval modes, extra directories.", { prompt: z.string().trim().min(1, "Prompt cannot be empty").describe( "Prompt for Gemini. Use @file.ts to include file context." ), model: z.string().optional().describe( "Model name (e.g. gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite). Default: " + DEFAULT_MODEL ), sandbox: z.boolean().optional().describe( "Run in sandbox mode for safe code execution." ), yolo: z.boolean().optional().describe( "Auto-approve all tool actions (no confirmation prompts)." ), approval_mode: z.enum(["default", "auto_edit", "yolo", "plan"]).optional().describe( "Approval mode: default (prompt), auto_edit (auto-approve edits), yolo (auto-approve all), plan (read-only)." ), include_stats: z.boolean().optional().describe( "Include token usage stats in the response." ), include_directories: z.array(z.string()).optional().describe( "Additional directories to include in Gemini's workspace." ), cwd: z.string().optional().describe( "Working directory for file references (@ syntax)." ), }, async ({ prompt, model, sandbox, yolo, approval_mode, include_stats, include_directories, cwd }) => { try { const result = await runGeminiQuery({ prompt, model, sandbox, yolo, approvalMode: approval_mode, includeDirectories: include_directories, cwd, }); if (result.raw.code !== 0) { const errorMsg = extractErrorMessage(result.raw.stderr, result.raw.stdout); return { content: [{ type: "text" as const, text: `Gemini CLI error (exit ${result.raw.code}): ${errorMsg}` }], isError: true, }; } const parts: string[] = [result.response]; if (include_stats && result.stats) { parts.push("\n---\nToken stats: " + JSON.stringify(result.stats)); } const output = truncate(parts.join(""), MAX_RESPONSE_CHARS); return { content: [{ type: "text" as const, text: output || "(empty response)" }], }; } catch (err: unknown) { const message = err instanceof Error ? err.message : String(err); return { content: [{ type: "text" as const, text: `Error: ${message}` }], isError: true, }; } } ); - src/index.ts:419-445 (helper)The runGeminiQuery function which prepares the CLI arguments and invokes the underlying process handler, runGeminiRaw, with stream-json output for activity-based timeout management.
async function runGeminiQuery(opts: GeminiQueryOptions): Promise<GeminiQueryResult> { const args: string[] = []; args.push("-p", opts.prompt); args.push("-m", opts.model ?? DEFAULT_MODEL); args.push("-o", "stream-json"); // Stream for idle-timeout awareness if (opts.sandbox) args.push("-s"); if (opts.yolo) args.push("-y"); if (opts.approvalMode) args.push("--approval-mode", opts.approvalMode); if (opts.includeDirectories?.length) { for (const dir of opts.includeDirectories) { args.push("--include-directories", dir); } } const raw = await runGeminiRaw({ args, cwd: opts.cwd }); if (raw.code !== 0) { return { response: "", raw }; } // Parse stream-json: each line is a JSON object. // Collect assistant message deltas and extract stats from result line. return parseStreamJson(raw); } - src/index.ts:298-386 (helper)The low-level runGeminiRaw function that spawns the gemini CLI process and manages idle timeouts based on stdout activity.
function runGeminiRaw(opts: GeminiRunOptions): Promise<GeminiResult> { return new Promise((resolve, reject) => { const escapedArgs = opts.args.map(escapeArg); const idleTimeoutMs = opts.timeoutMs ?? TIMEOUT_MS; let timedOut = false; let closed = false; const child: ChildProcess = spawn(GEMINI_BIN, escapedArgs, { shell: IS_WIN, stdio: ["ignore", "pipe", "pipe"], env: CHILD_ENV, cwd: opts.cwd, windowsHide: true, }); // stdin closed via "ignore" in stdio config — no input needed. const stdoutChunks: Buffer[] = []; const stderrChunks: Buffer[] = []; // --- Idle timeout: resets on any stdout activity --- function forceKill(): void { if (closed) return; timedOut = true; child.kill("SIGTERM"); // SIGKILL escalation: gemini spawns child MCP servers that inherit // pipe fds, preventing `close` from firing. Force-resolve after 3s. setTimeout(() => { if (closed) return; child.kill("SIGKILL"); child.stdout?.destroy(); child.stderr?.destroy(); closed = true; resolve({ stdout: Buffer.concat(stdoutChunks).toString("utf-8"), stderr: `Idle timeout (no output for ${idleTimeoutMs}ms). ${Buffer.concat(stderrChunks).toString("utf-8")}`.trim(), code: 124, }); }, 3_000); } let idleTimer = setTimeout(forceKill, idleTimeoutMs); function resetIdleTimer(): void { clearTimeout(idleTimer); idleTimer = setTimeout(forceKill, idleTimeoutMs); } child.stdout!.on("data", (chunk: Buffer) => { stdoutChunks.push(chunk); resetIdleTimer(); // AI is actively outputting — reset idle clock }); child.stderr!.on("data", (chunk: Buffer) => { stderrChunks.push(chunk); // Don't reset on stderr — error output during 429 retries shouldn't // prevent timeout. Only stdout activity (actual AI output) resets. }); child.on("error", (err) => { clearTimeout(idleTimer); reject( new Error( `Failed to start gemini CLI (${GEMINI_BIN}): ${err.message}. ` + "Install: npm install -g @google/gemini-cli" ) ); }); child.on("close", (code) => { if (closed) return; closed = true; clearTimeout(idleTimer); const stdout = Buffer.concat(stdoutChunks).toString("utf-8"); const stderr = Buffer.concat(stderrChunks).toString("utf-8"); if (timedOut) { resolve({ stdout, stderr: `Idle timeout (no output for ${idleTimeoutMs}ms). ${stderr}`.trim(), code: 124, }); return; } resolve({ stdout, stderr, code: code ?? 1 }); }); }); }