gemini-web-mcp 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -191,11 +191,11 @@ Then use `gemini-web-mcp` as the command instead of `npx -y gemini-web-mcp` in y
191
191
  | Variable | Required | Default | Description |
192
192
  |----------|:--------:|---------|-------------|
193
193
  | `GEMINI_API_KEY` | ✓ | — | Your [Gemini API key](https://aistudio.google.com/apikey) |
194
- | `GEMINI_WEBSEARCH_MODEL` | | `gemini-3-flash-preview` | Model for web search |
195
- | `GEMINI_DEEP_RESEARCH_AGENT` | | `deep-research-pro-preview-12-2025` | Deep Research model |
196
- | `GEMINI_DEEP_RESEARCH_TIMEOUT` | | `1200` | Research timeout in seconds |
194
+ | `GEMINI_WEBSEARCH_MODEL` | | `gemini-3.5-flash` | Model for web search |
195
+ | `GEMINI_DEEP_RESEARCH_AGENT` | | `deep-research-preview-04-2026` | Deep Research model |
196
+ | `GEMINI_DEEP_RESEARCH_TIMEOUT` | | `900` | Research timeout in seconds |
197
197
  | `GEMINI_DEEP_RESEARCH_POLL_INTERVAL` | | `10` | Polling interval in seconds |
198
- | `GEMINI_IMAGE_MODEL` | | `gemini-3-pro-image-preview` | Image generation model |
198
+ | `GEMINI_IMAGE_MODEL` | | `gemini-3.1-flash-image` | Image generation model |
199
199
  | `LOG_LEVEL` | | `info` | Logging level |
200
200
 
201
201
  ---
@@ -206,9 +206,11 @@ Then use `gemini-web-mcp` as the command instead of `npx -y gemini-web-mcp` in y
206
206
 
207
207
  Performs a web search and returns an AI-synthesized summary with citations.
208
208
 
209
- | Parameter | Type | Required | Description |
210
- |-----------|------|:--------:|-------------|
211
- | `query` | string | ✓ | Search query |
209
+ | Parameter | Type | Required | Default | Description |
210
+ |-----------|------|:--------:|---------|-------------|
211
+ | `query` | string | ✓ | — | Search query |
212
+ | `domain` | string | | — | Optional domain to recommend the search prioritize |
213
+ | `model` | string | | Configured default | Override default search model |
212
214
 
213
215
  ---
214
216
 
@@ -241,6 +243,7 @@ Conducts comprehensive web research using Gemini's Deep Research Agent. Blocks u
241
243
  |-----------|------|:--------:|---------|-------------|
242
244
  | `prompt` | string | ✓ | — | Research question or topic |
243
245
  | `include_citations` | boolean | | `true` | Include source URLs |
246
+ | `agent` | string | | Configured default | Override default Deep Research Agent name |
244
247
 
245
248
  **Returns:** `{ status, report_text }`
246
249
 
@@ -255,6 +258,7 @@ Generate images from text prompts or edit existing images.
255
258
  | `prompt` | string | ✓ | — | Text description or edit instructions |
256
259
  | `images` | string[] | | — | File paths for image editing (up to 7) |
257
260
  | `aspect_ratio` | string | | `"1:1"` | `"1:1"`, `"16:9"`, `"9:16"`, `"4:3"`, `"3:4"` |
261
+ | `model` | string | | Configured default | Override default image generation model |
258
262
 
259
263
  ---
260
264
 
package/dist/config.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { z } from "zod";
2
2
  const configSchema = z.object({
3
3
  GEMINI_API_KEY: z.string().min(1).optional(),
4
- GEMINI_WEBSEARCH_MODEL: z.string().min(1).default("gemini-3-flash-preview"),
5
- GEMINI_DEEP_RESEARCH_AGENT: z.string().min(1).default("deep-research-pro-preview-12-2025"),
6
- GEMINI_DEEP_RESEARCH_TIMEOUT: z.coerce.number().int().min(60).default(1200),
4
+ GEMINI_WEBSEARCH_MODEL: z.string().min(1).default("gemini-3.5-flash"),
5
+ GEMINI_DEEP_RESEARCH_AGENT: z.string().min(1).default("deep-research-preview-04-2026"),
6
+ GEMINI_DEEP_RESEARCH_TIMEOUT: z.coerce.number().int().min(60).default(900),
7
7
  GEMINI_DEEP_RESEARCH_POLL_INTERVAL: z.coerce.number().int().min(5).default(10),
8
- GEMINI_IMAGE_MODEL: z.string().min(1).default("gemini-3-pro-image-preview"),
8
+ GEMINI_IMAGE_MODEL: z.string().min(1).default("gemini-3.1-flash-image"),
9
9
  LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info")
10
10
  });
11
11
  export function loadConfig() {
@@ -41,10 +41,24 @@ export function formatSources(chunks) {
41
41
  const sources = [];
42
42
  chunks.forEach((c, i) => {
43
43
  const title = c.web?.title?.trim();
44
- if (!title || seen.has(title))
44
+ const uri = c.web?.uri?.trim();
45
+ // We need at least a title or a URI
46
+ if (!title && !uri)
45
47
  return;
46
- seen.add(title);
47
- sources.push(`[${i + 1}] ${title}`);
48
+ // Use URI for deduplication if available, otherwise title
49
+ const uniqueKey = uri || title;
50
+ if (!uniqueKey || seen.has(uniqueKey))
51
+ return;
52
+ seen.add(uniqueKey);
53
+ if (title && uri) {
54
+ sources.push(`[${i + 1}] ${title} (${uri})`);
55
+ }
56
+ else if (uri) {
57
+ sources.push(`[${i + 1}] ${uri}`);
58
+ }
59
+ else if (title) {
60
+ sources.push(`[${i + 1}] ${title}`);
61
+ }
48
62
  });
49
63
  return sources;
50
64
  }
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ registerToolCompat(server, "search_web", {
25
25
  const ai = createGeminiClient(apiKey);
26
26
  const text = await runWebSearch({
27
27
  ai,
28
- model: config.webSearchModel,
28
+ model: input.model || config.webSearchModel,
29
29
  input
30
30
  });
31
31
  return { content: [{ type: "text", text }] };
@@ -69,22 +69,22 @@ registerToolCompat(server, "view_content_chunk", {
69
69
  return { content: [{ type: "text", text: formatError(err) }], isError: true };
70
70
  }
71
71
  });
72
- const DEEP_RESEARCH_DESCRIPTION = `Conduct comprehensive web research using Gemini's Deep Research Agent.
73
-
74
- When to use this tool:
75
- - Researching complex topics requiring multi-source analysis
76
- - Need synthesized information from the web
77
- - Require fact-checking and cross-referencing of information
78
-
79
- Parameters:
80
- - \`prompt\`: Your research question or topic (required)
81
- - \`include_citations\`: Whether to include source URLs in the report (default: true)
82
-
83
- Returns:
84
- - \`status\`: Final state (completed, failed, cancelled)
85
- - \`report_text\`: The synthesized research report with findings
86
-
87
- Notes:
72
+ const DEEP_RESEARCH_DESCRIPTION = `Conduct comprehensive web research using Gemini's Deep Research Agent.
73
+
74
+ When to use this tool:
75
+ - Researching complex topics requiring multi-source analysis
76
+ - Need synthesized information from the web
77
+ - Require fact-checking and cross-referencing of information
78
+
79
+ Parameters:
80
+ - \`prompt\`: Your research question or topic (required)
81
+ - \`include_citations\`: Whether to include source URLs in the report (default: true)
82
+
83
+ Returns:
84
+ - \`status\`: Final state (completed, failed, cancelled)
85
+ - \`report_text\`: The synthesized research report with findings
86
+
87
+ Notes:
88
88
  - This tool blocks until research completes (typically 10-20 minutes)`;
89
89
  registerToolCompat(server, "gemini_deep_research", {
90
90
  title: "Gemini Deep Research",
@@ -96,7 +96,7 @@ registerToolCompat(server, "gemini_deep_research", {
96
96
  const ai = createGeminiClient(apiKey);
97
97
  const result = await runDeepResearch({
98
98
  ai,
99
- agent: config.deepResearchAgent,
99
+ agent: input.agent || config.deepResearchAgent,
100
100
  timeoutSeconds: config.deepResearchTimeoutSeconds,
101
101
  pollIntervalSeconds: config.deepResearchPollIntervalSeconds,
102
102
  input
@@ -109,20 +109,20 @@ registerToolCompat(server, "gemini_deep_research", {
109
109
  return { content: [{ type: "text", text: formatError(err) }], isError: true };
110
110
  }
111
111
  });
112
- const CREATE_IMAGE_DESCRIPTION = `Generate or edit images using natural language instructions.
113
-
114
- Capabilities:
115
- - **Text-to-Image**: Generate images from text prompts
116
- - **Image-to-Image**: Edit existing images based on text instructions + reference image(s)
117
-
118
- Parameters:
119
- - \`prompt\`: Text prompt describing what to generate or how to edit (required)
120
- - \`images\`: Array of file paths to images for editing mode (optional, up to 7 images)
121
- - \`aspect_ratio\`: Output dimensions - "1:1", "16:9", "9:16", "4:3", "3:4" (optional, default: "1:1")
122
-
123
- Notes:
124
- - The resulting image will be saved as an artifact for use and file paths are returned
125
- - Image model can't generate transparent images, use workarounds
112
+ const CREATE_IMAGE_DESCRIPTION = `Generate or edit images using natural language instructions.
113
+
114
+ Capabilities:
115
+ - **Text-to-Image**: Generate images from text prompts
116
+ - **Image-to-Image**: Edit existing images based on text instructions + reference image(s)
117
+
118
+ Parameters:
119
+ - \`prompt\`: Text prompt describing what to generate or how to edit (required)
120
+ - \`images\`: Array of file paths to images for editing mode (optional, up to 7 images)
121
+ - \`aspect_ratio\`: Output dimensions - "1:1", "16:9", "9:16", "4:3", "3:4" (optional, default: "1:1")
122
+
123
+ Notes:
124
+ - The resulting image will be saved as an artifact for use and file paths are returned
125
+ - Image model can't generate transparent images, use workarounds
126
126
  - For best results, be specific and descriptive in your prompts`;
127
127
  registerToolCompat(server, "create_image", {
128
128
  title: "Create Image",
@@ -134,7 +134,7 @@ registerToolCompat(server, "create_image", {
134
134
  const ai = createGeminiClient(apiKey);
135
135
  const result = await runCreateImage({
136
136
  ai,
137
- model: config.imageModel,
137
+ model: input.model || config.imageModel,
138
138
  input
139
139
  });
140
140
  if (!result.success) {
@@ -14,7 +14,11 @@ export const deepResearchInput = z.object({
14
14
  include_citations: z
15
15
  .boolean()
16
16
  .default(true)
17
- .describe("Whether to include source URLs in the report")
17
+ .describe("Whether to include source URLs in the report"),
18
+ agent: z
19
+ .string()
20
+ .optional()
21
+ .describe("Override the default Deep Research Agent name/model")
18
22
  });
19
23
  // --- URL Redirect Resolution ---
20
24
  /**
@@ -10,7 +10,8 @@ const IMAGE_OUTPUT_DIR = path.join(os.tmpdir(), "gemini-mcp-images");
10
10
  export const createImageInput = {
11
11
  prompt: z.string().describe("Text prompt describing the image to generate or how to edit the input image(s)"),
12
12
  images: z.array(z.string()).optional().describe("Optional array of file paths to images for editing. Supports up to 7 images. Example: ['/path/to/image.png']"),
13
- aspect_ratio: z.enum(["1:1", "16:9", "9:16", "4:3", "3:4"]).optional().describe("Output aspect ratio. Default: 1:1. Only used for generation, not editing.")
13
+ aspect_ratio: z.enum(["1:1", "16:9", "9:16", "4:3", "3:4"]).optional().describe("Output aspect ratio. Default: 1:1. Only used for generation, not editing."),
14
+ model: z.string().optional().describe("Override the default Gemini image model")
14
15
  };
15
16
  /**
16
17
  * Run image generation or editing using Gemini's native image model
@@ -2,7 +2,8 @@ import { z } from "zod";
2
2
  import { safeGetResponseText, extractGrounding, addInlineCitations, formatSources } from "../gemini/response.js";
3
3
  export const webSearchInput = {
4
4
  query: z.string().describe("The search query to look up"),
5
- domain: z.string().optional().describe("Optional domain to recommend the search prioritize")
5
+ domain: z.string().optional().describe("Optional domain to recommend the search prioritize"),
6
+ model: z.string().optional().describe("Override the default Gemini model used for search")
6
7
  };
7
8
  export async function runWebSearch(params) {
8
9
  const systemInstruction = buildSystemInstruction(params.input.domain);
@@ -30,19 +31,19 @@ export async function runWebSearch(params) {
30
31
  return lines.join("\n");
31
32
  }
32
33
  function buildSystemInstruction(domain) {
33
- const base = `You are a web search assistant with real-time search capabilities.
34
-
35
- **Response Guidelines:**
36
- - For factual/quick lookups (dates, definitions, simple facts): Be brief and direct
37
- - For complex topics (how-to, comparisons, analysis): Provide structured, detailed explanations
38
- - For current events/news: Summarize key points with context and timeline
39
- - For technical queries: Include relevant code, specifications, or documentation references
40
-
41
- **Core Principles:**
42
- - Always use the most recent and authoritative sources
43
- - Lead with the direct answer, then expand if needed
44
- - Use bullet points or numbered lists for multi-part information
45
- - Omit filler phrases and redundant context
34
+ const base = `You are a web search assistant with real-time search capabilities.
35
+
36
+ **Response Guidelines:**
37
+ - For factual/quick lookups (dates, definitions, simple facts): Be brief and direct
38
+ - For complex topics (how-to, comparisons, analysis): Provide structured, detailed explanations
39
+ - For current events/news: Summarize key points with context and timeline
40
+ - For technical queries: Include relevant code, specifications, or documentation references
41
+
42
+ **Core Principles:**
43
+ - Always use the most recent and authoritative sources
44
+ - Lead with the direct answer, then expand if needed
45
+ - Use bullet points or numbered lists for multi-part information
46
+ - Omit filler phrases and redundant context
46
47
  - Match the depth of your response to the complexity of the query`;
47
48
  if (domain) {
48
49
  return `${base}\nPrioritize results from: ${domain}`;
package/package.json CHANGED
@@ -1,56 +1,56 @@
1
- {
2
- "name": "gemini-web-mcp",
3
- "version": "0.1.2",
4
- "description": "MCP server with Gemini-powered web tools: search, content extraction, deep research, and image generation",
5
- "author": "bharatvansh",
6
- "license": "MIT",
7
- "type": "module",
8
- "keywords": [
9
- "mcp",
10
- "gemini",
11
- "google",
12
- "ai",
13
- "web-search",
14
- "deep-research",
15
- "image-generation",
16
- "model-context-protocol"
17
- ],
18
- "repository": {
19
- "type": "git",
20
- "url": "https://github.com/bharatvansh/gemini-web-mcp.git"
21
- },
22
- "homepage": "https://github.com/bharatvansh/gemini-web-mcp#readme",
23
- "bugs": {
24
- "url": "https://github.com/bharatvansh/gemini-web-mcp/issues"
25
- },
26
- "bin": {
27
- "gemini-web-mcp": "dist/index.js"
28
- },
29
- "files": [
30
- "dist"
31
- ],
32
- "scripts": {
33
- "dev": "tsx watch src/index.ts",
34
- "build": "tsc -p tsconfig.json",
35
- "start": "node dist/index.js",
36
- "typecheck": "tsc -p tsconfig.json --noEmit"
37
- },
38
- "dependencies": {
39
- "@google/genai": "^1.0.0",
40
- "@modelcontextprotocol/sdk": "^1.0.0",
41
- "@mozilla/readability": "^0.6.0",
42
- "dotenv": "^16.4.5",
43
- "jsdom": "^28.0.0",
44
- "node-html-markdown": "^2.0.0",
45
- "zod": "^3.24.2"
46
- },
47
- "devDependencies": {
48
- "@types/jsdom": "^27.0.0",
49
- "@types/node": "^20.11.30",
50
- "tsx": "^4.19.2",
51
- "typescript": "^5.7.3"
52
- },
53
- "engines": {
54
- "node": ">=18"
55
- }
1
+ {
2
+ "name": "gemini-web-mcp",
3
+ "version": "0.2.0",
4
+ "description": "MCP server with Gemini-powered web tools: search, content extraction, deep research, and image generation",
5
+ "author": "bharatvansh",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "keywords": [
9
+ "mcp",
10
+ "gemini",
11
+ "google",
12
+ "ai",
13
+ "web-search",
14
+ "deep-research",
15
+ "image-generation",
16
+ "model-context-protocol"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/bharatvansh/gemini-web-mcp.git"
21
+ },
22
+ "homepage": "https://github.com/bharatvansh/gemini-web-mcp#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/bharatvansh/gemini-web-mcp/issues"
25
+ },
26
+ "bin": {
27
+ "gemini-web-mcp": "dist/index.js"
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "dev": "tsx watch src/index.ts",
34
+ "build": "tsc -p tsconfig.json",
35
+ "start": "node dist/index.js",
36
+ "typecheck": "tsc -p tsconfig.json --noEmit"
37
+ },
38
+ "dependencies": {
39
+ "@google/genai": "^1.0.0",
40
+ "@modelcontextprotocol/sdk": "^1.0.0",
41
+ "@mozilla/readability": "^0.6.0",
42
+ "dotenv": "^16.4.5",
43
+ "jsdom": "^28.0.0",
44
+ "node-html-markdown": "^2.0.0",
45
+ "zod": "^3.24.2"
46
+ },
47
+ "devDependencies": {
48
+ "@types/jsdom": "^27.0.0",
49
+ "@types/node": "^20.11.30",
50
+ "tsx": "^4.19.2",
51
+ "typescript": "^5.7.3"
52
+ },
53
+ "engines": {
54
+ "node": ">=18"
55
+ }
56
56
  }
@@ -1,43 +0,0 @@
1
- import { z } from "zod";
2
- import { safeGetResponseText } from "../gemini/response.js";
3
- export const pageSummaryInput = {
4
- url: z.string().url(),
5
- focus: z.string().min(1).optional(),
6
- summary_length: z.enum(["short", "medium", "long"]).default("medium"),
7
- max_output_tokens: z.number().int().min(128).max(4096).default(1024)
8
- };
9
- export async function runPageSummary(params) {
10
- const prompt = buildPrompt(params.input.url, params.input.focus, params.input.summary_length);
11
- const response = await params.ai.models.generateContent({
12
- model: params.model,
13
- contents: [{ role: "user", parts: [{ text: prompt }] }],
14
- config: {
15
- tools: [{ urlContext: {} }],
16
- temperature: 1.0,
17
- maxOutputTokens: params.input.max_output_tokens
18
- }
19
- });
20
- const summary = safeGetResponseText(response).trim();
21
- const lines = [];
22
- lines.push(`## URL\n\n${params.input.url}`);
23
- lines.push(`\n## Summary\n\n${summary || "_No summary returned._"}`);
24
- return lines.join("\n");
25
- }
26
- function buildPrompt(url, focus, length) {
27
- const lengthGuidance = length === "short"
28
- ? "Write 5–8 bullet points and a 2-sentence summary."
29
- : length === "long"
30
- ? "Write a detailed outline with sections and key takeaways."
31
- : "Write a medium-length summary with sections and bullet points.";
32
- const focusLine = focus ? `Focus on: ${focus}` : "Cover the most important information on the page.";
33
- return [
34
- "Summarize the content of the URL using URL context retrieval.",
35
- "Do not rely on prior knowledge. If the page cannot be accessed or parsed, say so explicitly.",
36
- "",
37
- `URL: ${url}`,
38
- focusLine,
39
- "",
40
- lengthGuidance,
41
- "Output markdown."
42
- ].join("\n");
43
- }
@@ -1,71 +0,0 @@
1
- /**
2
- * Resolve Gemini grounding redirect URLs to actual source URLs.
3
- */
4
- const REDIRECT_URL_PATTERN = /https:\/\/vertexaisearch\.cloud\.google\.com\/grounding-api-redirect\/[A-Za-z0-9_-]+/g;
5
- const RESOLVE_TIMEOUT = 10000; // 10 seconds
6
- // Simple cache for resolved URLs
7
- const urlCache = new Map();
8
- /**
9
- * Follow a redirect URL and return the final destination.
10
- */
11
- export async function resolveRedirectUrl(url) {
12
- if (!url || !url.includes('grounding-api-redirect')) {
13
- return null;
14
- }
15
- // Check cache first
16
- if (urlCache.has(url)) {
17
- return urlCache.get(url);
18
- }
19
- try {
20
- const controller = new AbortController();
21
- const timeoutId = setTimeout(() => controller.abort(), RESOLVE_TIMEOUT);
22
- // Use HEAD request with redirect: 'manual' to capture Location header
23
- const response = await fetch(url, {
24
- method: 'HEAD',
25
- redirect: 'manual',
26
- signal: controller.signal
27
- });
28
- clearTimeout(timeoutId);
29
- // Check for redirect status codes
30
- if ([301, 302, 303, 307, 308].includes(response.status)) {
31
- const location = response.headers.get('location');
32
- if (location) {
33
- urlCache.set(url, location);
34
- return location;
35
- }
36
- }
37
- // Fallback: try GET with follow redirects
38
- const getResponse = await fetch(url, {
39
- method: 'GET',
40
- redirect: 'follow',
41
- signal: controller.signal
42
- });
43
- const finalUrl = getResponse.url;
44
- if (finalUrl !== url && !finalUrl.includes('grounding-api-redirect')) {
45
- urlCache.set(url, finalUrl);
46
- return finalUrl;
47
- }
48
- }
49
- catch (error) {
50
- // Silently fail - return null
51
- }
52
- return null;
53
- }
54
- /**
55
- * Resolve a single source object's URL if it's a redirect URL.
56
- */
57
- export async function resolveSource(source) {
58
- if (source.url.includes('grounding-api-redirect')) {
59
- const resolved = await resolveRedirectUrl(source.url);
60
- if (resolved) {
61
- return { ...source, url: resolved };
62
- }
63
- }
64
- return source;
65
- }
66
- /**
67
- * Resolve all sources in an array concurrently.
68
- */
69
- export async function resolveSources(sources) {
70
- return Promise.all(sources.map(resolveSource));
71
- }