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 +11 -7
- package/dist/config.js +4 -4
- package/dist/gemini/response.js +17 -3
- package/dist/index.js +33 -33
- package/dist/tools/deepResearch.js +5 -1
- package/dist/tools/imageGeneration.js +2 -1
- package/dist/tools/webSearch.js +15 -14
- package/package.json +55 -55
- package/dist/tools/pageSummary.js +0 -43
- package/dist/utils/resolveUrl.js +0 -71
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
|
|
195
|
-
| `GEMINI_DEEP_RESEARCH_AGENT` | | `deep-research-
|
|
196
|
-
| `GEMINI_DEEP_RESEARCH_TIMEOUT` | | `
|
|
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-
|
|
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
|
|
5
|
-
GEMINI_DEEP_RESEARCH_AGENT: z.string().min(1).default("deep-research-
|
|
6
|
-
GEMINI_DEEP_RESEARCH_TIMEOUT: z.coerce.number().int().min(60).default(
|
|
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-
|
|
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() {
|
package/dist/gemini/response.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
47
|
-
|
|
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
|
package/dist/tools/webSearch.js
CHANGED
|
@@ -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.
|
|
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
|
-
}
|
package/dist/utils/resolveUrl.js
DELETED
|
@@ -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
|
-
}
|