Skip to content

Commit 7737c48

Browse files
jaykaycodesclaude
andcommitted
fix: graceful degradation when GEMINI_API_KEY is missing
Tools that require Gemini (find_patterns, trace_dataflow, semantic analysis) now check for the key upfront and return a helpful error with setup instructions instead of a cryptic crash. Tells users exactly how to add the env var to their MCP config. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06e216c commit 7737c48

6 files changed

Lines changed: 148 additions & 3 deletions

File tree

dist/cli/index.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42998,6 +42998,9 @@ var init_node = __esm(() => {
4299842998
});
4299942999

4300043000
// src/core/gemini.ts
43001+
function hasGeminiKey() {
43002+
return !!process.env.GEMINI_API_KEY;
43003+
}
4300143004
function getClient() {
4300243005
if (!client) {
4300343006
const apiKey = process.env.GEMINI_API_KEY;
@@ -43104,6 +43107,21 @@ var init_gemini = __esm(() => {
4310443107

4310543108
// src/core/layers/semantic.ts
4310643109
async function semanticAnalysis(surface, structural, options = {}) {
43110+
if (!hasGeminiKey()) {
43111+
throw new Error(`Semantic analysis (deep depth) requires GEMINI_API_KEY.
43112+
43113+
` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
43114+
43115+
` + ` "codebase-analyzer": {
43116+
` + ` "command": "npx",
43117+
` + ` "args": ["-y", "codebase-analyzer-mcp"],
43118+
` + ` "env": { "GEMINI_API_KEY": "your_key" }
43119+
` + ` }
43120+
43121+
` + `Get a free key at https://aistudio.google.com/apikey
43122+
43123+
` + "Use --depth surface or --depth standard for free analysis without an API key.");
43124+
}
4310743125
const repoName = surface.repositoryMap.name;
4310843126
const languages = surface.repositoryMap.languages.slice(0, 5).map((l) => `${l.language} (${l.percentage}%)`).join(", ");
4310943127
const fileCount = surface.repositoryMap.fileCount;
@@ -58155,6 +58173,21 @@ __export(exports_patterns, {
5815558173
import { join as join4 } from "path";
5815658174
import { readFile as readFile4 } from "fs/promises";
5815758175
async function executeFindPatterns(input) {
58176+
if (!hasGeminiKey()) {
58177+
throw new Error(`find_patterns requires GEMINI_API_KEY for AI-powered pattern detection.
58178+
58179+
` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
58180+
58181+
` + ` "codebase-analyzer": {
58182+
` + ` "command": "npx",
58183+
` + ` "args": ["-y", "codebase-analyzer-mcp"],
58184+
` + ` "env": { "GEMINI_API_KEY": "your_key" }
58185+
` + ` }
58186+
58187+
` + `Get a free key at https://aistudio.google.com/apikey
58188+
58189+
` + "Alternatively, use analyze_repo (free, no API key needed) or query_repo (degrades gracefully without Gemini).");
58190+
}
5815858191
const { source, patternTypes } = input;
5815958192
const { repoPath, cleanup } = await resolveSource(source);
5816058193
try {
@@ -58267,6 +58300,21 @@ __export(exports_dataflow, {
5826758300
import { join as join5 } from "path";
5826858301
import { readFile as readFile5 } from "fs/promises";
5826958302
async function executeTraceDataflow(input) {
58303+
if (!hasGeminiKey()) {
58304+
throw new Error(`trace_dataflow requires GEMINI_API_KEY for AI-powered dataflow analysis.
58305+
58306+
` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
58307+
58308+
` + ` "codebase-analyzer": {
58309+
` + ` "command": "npx",
58310+
` + ` "args": ["-y", "codebase-analyzer-mcp"],
58311+
` + ` "env": { "GEMINI_API_KEY": "your_key" }
58312+
` + ` }
58313+
58314+
` + `Get a free key at https://aistudio.google.com/apikey
58315+
58316+
` + "Alternatively, use query_repo which degrades gracefully without Gemini.");
58317+
}
5827058318
const { source, from, to } = input;
5827158319
const { repoPath, cleanup } = await resolveSource(source);
5827258320
try {

dist/mcp/server.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69557,6 +69557,9 @@ function getApiKeyFromEnv() {
6955769557

6955869558
// src/core/gemini.ts
6955969559
var client = null;
69560+
function hasGeminiKey() {
69561+
return !!process.env.GEMINI_API_KEY;
69562+
}
6956069563
function getClient() {
6956169564
if (!client) {
6956269565
const apiKey = process.env.GEMINI_API_KEY;
@@ -69731,6 +69734,21 @@ Focus on:
6973169734

6973269735
Be specific and reference actual file paths when possible.`;
6973369736
async function semanticAnalysis(surface, structural, options = {}) {
69737+
if (!hasGeminiKey()) {
69738+
throw new Error(`Semantic analysis (deep depth) requires GEMINI_API_KEY.
69739+
69740+
` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
69741+
69742+
` + ` "codebase-analyzer": {
69743+
` + ` "command": "npx",
69744+
` + ` "args": ["-y", "codebase-analyzer-mcp"],
69745+
` + ` "env": { "GEMINI_API_KEY": "your_key" }
69746+
` + ` }
69747+
69748+
` + `Get a free key at https://aistudio.google.com/apikey
69749+
69750+
` + "Use --depth surface or --depth standard for free analysis without an API key.");
69751+
}
6973469752
const repoName = surface.repositoryMap.name;
6973569753
const languages = surface.repositoryMap.languages.slice(0, 5).map((l) => `${l.language} (${l.percentage}%)`).join(", ");
6973669754
const fileCount = surface.repositoryMap.fileCount;
@@ -70641,6 +70659,21 @@ var findPatternsSchema = {
7064170659
patternTypes: exports_external.array(exports_external.string()).optional().describe(`Optional: specific patterns to look for. Available: ${DETECTABLE_PATTERNS.join(", ")}`)
7064270660
};
7064370661
async function executeFindPatterns(input) {
70662+
if (!hasGeminiKey()) {
70663+
throw new Error(`find_patterns requires GEMINI_API_KEY for AI-powered pattern detection.
70664+
70665+
` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
70666+
70667+
` + ` "codebase-analyzer": {
70668+
` + ` "command": "npx",
70669+
` + ` "args": ["-y", "codebase-analyzer-mcp"],
70670+
` + ` "env": { "GEMINI_API_KEY": "your_key" }
70671+
` + ` }
70672+
70673+
` + `Get a free key at https://aistudio.google.com/apikey
70674+
70675+
` + "Alternatively, use analyze_repo (free, no API key needed) or query_repo (degrades gracefully without Gemini).");
70676+
}
7064470677
const { source, patternTypes } = input;
7064570678
const { repoPath, cleanup } = await resolveSource(source);
7064670679
try {
@@ -70721,6 +70754,21 @@ var traceDataflowSchema = {
7072170754
to: exports_external.string().optional().describe("Optional: destination to trace to (if known)")
7072270755
};
7072370756
async function executeTraceDataflow(input) {
70757+
if (!hasGeminiKey()) {
70758+
throw new Error(`trace_dataflow requires GEMINI_API_KEY for AI-powered dataflow analysis.
70759+
70760+
` + `To set it up, add the env var to your MCP server config in ~/.mcp.json:
70761+
70762+
` + ` "codebase-analyzer": {
70763+
` + ` "command": "npx",
70764+
` + ` "args": ["-y", "codebase-analyzer-mcp"],
70765+
` + ` "env": { "GEMINI_API_KEY": "your_key" }
70766+
` + ` }
70767+
70768+
` + `Get a free key at https://aistudio.google.com/apikey
70769+
70770+
` + "Alternatively, use query_repo which degrades gracefully without Gemini.");
70771+
}
7072470772
const { source, from, to } = input;
7072570773
const { repoPath, cleanup } = await resolveSource(source);
7072670774
try {

src/core/gemini.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import { GoogleGenAI } from "@google/genai";
22

33
let client: GoogleGenAI | null = null;
44

5+
/**
6+
* Check if a Gemini API key is available without throwing.
7+
*/
8+
export function hasGeminiKey(): boolean {
9+
return !!process.env.GEMINI_API_KEY;
10+
}
11+
512
function getClient(): GoogleGenAI {
613
if (!client) {
714
const apiKey = process.env.GEMINI_API_KEY;

src/core/layers/semantic.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import type {
1818
StructuralAnalysis,
1919
SurfaceAnalysis,
2020
} from "../../types.js";
21-
import { generateJsonWithGemini } from "../gemini.js";
21+
import { generateJsonWithGemini, hasGeminiKey } from "../gemini.js";
2222

2323
/**
2424
* Semantic analysis prompt template
@@ -101,6 +101,20 @@ export async function semanticAnalysis(
101101
focusAreas?: string[];
102102
} = {}
103103
): Promise<SemanticAnalysis> {
104+
if (!hasGeminiKey()) {
105+
throw new Error(
106+
"Semantic analysis (deep depth) requires GEMINI_API_KEY.\n\n" +
107+
"To set it up, add the env var to your MCP server config in ~/.mcp.json:\n\n" +
108+
' "codebase-analyzer": {\n' +
109+
' "command": "npx",\n' +
110+
' "args": ["-y", "codebase-analyzer-mcp"],\n' +
111+
' "env": { "GEMINI_API_KEY": "your_key" }\n' +
112+
" }\n\n" +
113+
"Get a free key at https://aistudio.google.com/apikey\n\n" +
114+
"Use --depth surface or --depth standard for free analysis without an API key."
115+
);
116+
}
117+
104118
// Build context for LLM
105119
const repoName = surface.repositoryMap.name;
106120
const languages = surface.repositoryMap.languages

src/mcp/tools/dataflow.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { z } from "zod";
99
import { resolveSource } from "../../core/repo-loader.js";
1010
import { surfaceAnalysis } from "../../core/layers/index.js";
11-
import { generateJsonWithGemini } from "../../core/gemini.js";
11+
import { generateJsonWithGemini, hasGeminiKey } from "../../core/gemini.js";
1212
import { join } from "path";
1313
import { readFile } from "fs/promises";
1414
import { glob } from "glob";
@@ -68,6 +68,20 @@ interface DataflowResult {
6868
* Execute trace_dataflow tool
6969
*/
7070
export async function executeTraceDataflow(input: TraceDataflowInput): Promise<DataflowResult> {
71+
if (!hasGeminiKey()) {
72+
throw new Error(
73+
"trace_dataflow requires GEMINI_API_KEY for AI-powered dataflow analysis.\n\n" +
74+
"To set it up, add the env var to your MCP server config in ~/.mcp.json:\n\n" +
75+
' "codebase-analyzer": {\n' +
76+
' "command": "npx",\n' +
77+
' "args": ["-y", "codebase-analyzer-mcp"],\n' +
78+
' "env": { "GEMINI_API_KEY": "your_key" }\n' +
79+
" }\n\n" +
80+
"Get a free key at https://aistudio.google.com/apikey\n\n" +
81+
"Alternatively, use query_repo which degrades gracefully without Gemini."
82+
);
83+
}
84+
7185
const { source, from, to } = input;
7286

7387
// Resolve source

src/mcp/tools/patterns.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { z } from "zod";
99
import { resolveSource } from "../../core/repo-loader.js";
1010
import { surfaceAnalysis, analyzeModulesStructurally, loadModuleFiles } from "../../core/layers/index.js";
11-
import { generateJsonWithGemini } from "../../core/gemini.js";
11+
import { generateJsonWithGemini, hasGeminiKey } from "../../core/gemini.js";
1212
import { join } from "path";
1313
import { readFile } from "fs/promises";
1414
import { glob } from "glob";
@@ -78,6 +78,20 @@ interface PatternAnalysisResult {
7878
* Execute find_patterns tool
7979
*/
8080
export async function executeFindPatterns(input: FindPatternsInput): Promise<PatternAnalysisResult> {
81+
if (!hasGeminiKey()) {
82+
throw new Error(
83+
"find_patterns requires GEMINI_API_KEY for AI-powered pattern detection.\n\n" +
84+
"To set it up, add the env var to your MCP server config in ~/.mcp.json:\n\n" +
85+
' "codebase-analyzer": {\n' +
86+
' "command": "npx",\n' +
87+
' "args": ["-y", "codebase-analyzer-mcp"],\n' +
88+
' "env": { "GEMINI_API_KEY": "your_key" }\n' +
89+
" }\n\n" +
90+
"Get a free key at https://aistudio.google.com/apikey\n\n" +
91+
"Alternatively, use analyze_repo (free, no API key needed) or query_repo (degrades gracefully without Gemini)."
92+
);
93+
}
94+
8195
const { source, patternTypes } = input;
8296

8397
// Resolve source

0 commit comments

Comments
 (0)