@rlabs-inc/gemini-mcp 0.6.1 → 0.6.2

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.
@@ -120,3 +120,37 @@ export declare function checkVideoStatus(operationName: string): Promise<VideoGe
120
120
  * Get the output directory path
121
121
  */
122
122
  export declare function getOutputDir(): string;
123
+ /**
124
+ * Token count result
125
+ */
126
+ export interface TokenCountResult {
127
+ totalTokens: number;
128
+ modelName: string;
129
+ }
130
+ /**
131
+ * Count tokens for content using specified model
132
+ */
133
+ export declare function countTokens(content: string, model?: 'pro' | 'flash'): Promise<TokenCountResult>;
134
+ /**
135
+ * Deep Research interaction result
136
+ */
137
+ export interface DeepResearchResult {
138
+ id: string;
139
+ status: 'pending' | 'processing' | 'completed' | 'failed';
140
+ outputs?: {
141
+ text?: string;
142
+ }[];
143
+ error?: string;
144
+ }
145
+ /**
146
+ * Start a deep research task
147
+ */
148
+ export declare function startDeepResearch(prompt: string): Promise<DeepResearchResult>;
149
+ /**
150
+ * Check deep research status
151
+ */
152
+ export declare function checkDeepResearch(researchId: string): Promise<DeepResearchResult>;
153
+ /**
154
+ * Follow up on completed research
155
+ */
156
+ export declare function followUpResearch(researchId: string, question: string): Promise<string>;
@@ -397,3 +397,93 @@ export async function checkVideoStatus(operationName) {
397
397
  export function getOutputDir() {
398
398
  return outputDir;
399
399
  }
400
+ /**
401
+ * Count tokens for content using specified model
402
+ */
403
+ export async function countTokens(content, model = 'flash') {
404
+ const modelName = model === 'pro' ? proModelName : flashModelName;
405
+ const result = await genAI.models.countTokens({
406
+ model: modelName,
407
+ contents: content,
408
+ });
409
+ return {
410
+ totalTokens: result.totalTokens || 0,
411
+ modelName,
412
+ };
413
+ }
414
+ // Deep Research agent model
415
+ const DEEP_RESEARCH_AGENT = 'deep-research-pro-preview-12-2025';
416
+ /**
417
+ * Start a deep research task
418
+ */
419
+ export async function startDeepResearch(prompt) {
420
+ try {
421
+ // The Interactions API - cast to any since it may not be in SDK types yet
422
+ const interaction = await genAI.interactions.create({
423
+ input: prompt,
424
+ agent: DEEP_RESEARCH_AGENT,
425
+ background: true,
426
+ agentConfig: {
427
+ type: 'deep-research',
428
+ thinkingSummaries: 'auto',
429
+ },
430
+ });
431
+ return {
432
+ id: interaction.id || `research-${Date.now()}`,
433
+ status: 'pending',
434
+ };
435
+ }
436
+ catch (error) {
437
+ const message = error instanceof Error ? error.message : String(error);
438
+ throw new Error(`Deep research not available: ${message}`);
439
+ }
440
+ }
441
+ /**
442
+ * Check deep research status
443
+ */
444
+ export async function checkDeepResearch(researchId) {
445
+ try {
446
+ const interaction = await genAI.interactions.get(researchId);
447
+ const status = interaction.status || 'unknown';
448
+ if (status === 'completed') {
449
+ return {
450
+ id: researchId,
451
+ status: 'completed',
452
+ outputs: interaction.outputs,
453
+ };
454
+ }
455
+ else if (status === 'failed') {
456
+ return {
457
+ id: researchId,
458
+ status: 'failed',
459
+ error: interaction.error || 'Unknown error',
460
+ };
461
+ }
462
+ return {
463
+ id: researchId,
464
+ status: 'processing',
465
+ };
466
+ }
467
+ catch (error) {
468
+ const message = error instanceof Error ? error.message : String(error);
469
+ throw new Error(`Failed to check research status: ${message}`);
470
+ }
471
+ }
472
+ /**
473
+ * Follow up on completed research
474
+ */
475
+ export async function followUpResearch(researchId, question) {
476
+ try {
477
+ const interaction = await genAI.interactions.create({
478
+ input: question,
479
+ model: proModelName,
480
+ previousInteractionId: researchId,
481
+ });
482
+ const outputs = interaction.outputs || [];
483
+ return outputs.length > 0 ? outputs[outputs.length - 1].text || 'No response' : 'No response received';
484
+ }
485
+ catch (error) {
486
+ const message = error instanceof Error ? error.message : String(error);
487
+ throw new Error(`Research follow-up failed: ${message}`);
488
+ }
489
+ }
package/dist/index.js CHANGED
@@ -113,7 +113,7 @@ async function main() {
113
113
  // Create MCP server
114
114
  const server = new McpServer({
115
115
  name: 'Gemini',
116
- version: '0.6.1',
116
+ version: '0.6.2',
117
117
  });
118
118
  // Register tools
119
119
  registerQueryTool(server);
@@ -4,7 +4,7 @@
4
4
  * Uses the Gemini Deep Research Agent for complex research tasks.
5
5
  * The agent autonomously plans, searches, reads, and synthesizes research.
6
6
  */
7
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
8
  /**
9
9
  * Register deep research tools with the MCP server
10
10
  */
@@ -4,20 +4,18 @@
4
4
  * Uses the Gemini Deep Research Agent for complex research tasks.
5
5
  * The agent autonomously plans, searches, reads, and synthesizes research.
6
6
  */
7
- import { z } from "zod";
8
- import { logger } from "../utils/logger.js";
9
- import { genAI } from "../gemini-client.js";
7
+ import { z } from 'zod';
8
+ import { logger } from '../utils/logger.js';
9
+ import { startDeepResearch, checkDeepResearch, followUpResearch, } from '../gemini-client.js';
10
10
  // Store active research operations for polling
11
11
  const activeResearchOperations = new Map();
12
- // Deep Research agent model
13
- const DEEP_RESEARCH_AGENT = "deep-research-pro-preview-12-2025";
14
12
  /**
15
13
  * Register deep research tools with the MCP server
16
14
  */
17
15
  export function registerDeepResearchTool(server) {
18
16
  // Start a deep research task
19
- server.tool("gemini-deep-research", {
20
- query: z.string().describe("The research question or topic to investigate"),
17
+ server.tool('gemini-deep-research', {
18
+ query: z.string().describe('The research question or topic to investigate'),
21
19
  format: z
22
20
  .string()
23
21
  .optional()
@@ -30,33 +28,22 @@ export function registerDeepResearchTool(server) {
30
28
  if (format) {
31
29
  researchPrompt = `${query}\n\nFormat the output as: ${format}`;
32
30
  }
33
- // Start the research task in the background
34
- // The Interactions API is accessed via genAI.interactions
35
- const interaction = await genAI.interactions.create({
36
- input: researchPrompt,
37
- agent: DEEP_RESEARCH_AGENT,
38
- background: true,
39
- agentConfig: {
40
- type: "deep-research",
41
- thinkingSummaries: "auto"
42
- }
43
- });
44
- const interactionId = interaction.id || `research-${Date.now()}`;
31
+ const result = await startDeepResearch(researchPrompt);
45
32
  // Store for later polling
46
- activeResearchOperations.set(interactionId, {
47
- interactionId,
33
+ activeResearchOperations.set(result.id, {
48
34
  startedAt: new Date(),
49
- prompt: query
35
+ prompt: query,
50
36
  });
51
- logger.info(`Deep research started: ${interactionId}`);
37
+ logger.info(`Deep research started: ${result.id}`);
52
38
  return {
53
- content: [{
54
- type: "text",
39
+ content: [
40
+ {
41
+ type: 'text',
55
42
  text: `**Deep Research Started**
56
43
 
57
44
  | Field | Value |
58
45
  |-------|-------|
59
- | **Research ID** | \`${interactionId}\` |
46
+ | **Research ID** | \`${result.id}\` |
60
47
  | **Query** | ${query.substring(0, 100)}${query.length > 100 ? '...' : ''} |
61
48
  | **Status** | In Progress |
62
49
  | **Started** | ${new Date().toISOString()} |
@@ -69,18 +56,20 @@ export function registerDeepResearchTool(server) {
69
56
  **To check progress:**
70
57
  Use \`gemini-check-research\` with the Research ID above.
71
58
 
72
- **Note:** Deep research tasks run in the background. You can continue working while waiting.`
73
- }]
59
+ **Note:** Deep research tasks run in the background. You can continue working while waiting.`,
60
+ },
61
+ ],
74
62
  };
75
63
  }
76
64
  catch (error) {
77
65
  const errorMessage = error instanceof Error ? error.message : String(error);
78
66
  logger.error(`Error starting deep research: ${errorMessage}`);
79
67
  // Check if it's an API availability issue
80
- if (errorMessage.includes("interactions") || errorMessage.includes("not found")) {
68
+ if (errorMessage.includes('not available') || errorMessage.includes('interactions')) {
81
69
  return {
82
- content: [{
83
- type: "text",
70
+ content: [
71
+ {
72
+ type: 'text',
84
73
  text: `**Deep Research Not Available**
85
74
 
86
75
  The Interactions API required for Deep Research may not be available yet in your SDK version or API access.
@@ -90,44 +79,42 @@ The Interactions API required for Deep Research may not be available yet in your
90
79
  **Alternatives:**
91
80
  - Use \`gemini-search\` for real-time web search
92
81
  - Use \`gemini-query\` with a detailed research prompt
93
- - Wait for Interactions API to become available in your region`
94
- }],
95
- isError: true
82
+ - Wait for Interactions API to become available in your region`,
83
+ },
84
+ ],
85
+ isError: true,
96
86
  };
97
87
  }
98
88
  return {
99
- content: [{ type: "text", text: `Error starting deep research: ${errorMessage}` }],
100
- isError: true
89
+ content: [{ type: 'text', text: `Error starting deep research: ${errorMessage}` }],
90
+ isError: true,
101
91
  };
102
92
  }
103
93
  });
104
94
  // Check research status
105
- server.tool("gemini-check-research", {
106
- researchId: z.string().describe("The research ID returned from gemini-deep-research")
95
+ server.tool('gemini-check-research', {
96
+ researchId: z.string().describe('The research ID returned from gemini-deep-research'),
107
97
  }, async ({ researchId }) => {
108
98
  logger.info(`Checking research status: ${researchId}`);
109
99
  try {
110
100
  // Get stored operation info
111
101
  const operationInfo = activeResearchOperations.get(researchId);
112
- // Get the current status
113
- const interaction = await genAI.interactions.get(researchId);
114
- const status = interaction.status || "unknown";
115
- const elapsedMs = operationInfo
116
- ? Date.now() - operationInfo.startedAt.getTime()
117
- : 0;
102
+ const elapsedMs = operationInfo ? Date.now() - operationInfo.startedAt.getTime() : 0;
118
103
  const elapsedMinutes = Math.floor(elapsedMs / 60000);
119
104
  const elapsedSeconds = Math.floor((elapsedMs % 60000) / 1000);
120
- if (status === "completed") {
105
+ const result = await checkDeepResearch(researchId);
106
+ if (result.status === 'completed') {
121
107
  // Research is done - extract the result
122
108
  activeResearchOperations.delete(researchId);
123
- const outputs = interaction.outputs || [];
124
- const result = outputs.length > 0
125
- ? outputs[outputs.length - 1].text || "No text output"
126
- : "Research completed but no output found";
109
+ const outputs = result.outputs || [];
110
+ const resultText = outputs.length > 0
111
+ ? outputs[outputs.length - 1].text || 'No text output'
112
+ : 'Research completed but no output found';
127
113
  logger.info(`Research completed: ${researchId}`);
128
114
  return {
129
- content: [{
130
- type: "text",
115
+ content: [
116
+ {
117
+ type: 'text',
131
118
  text: `**Deep Research Complete**
132
119
 
133
120
  | Field | Value |
@@ -140,50 +127,54 @@ The Interactions API required for Deep Research may not be available yet in your
140
127
 
141
128
  ## Research Results
142
129
 
143
- ${result}`
144
- }]
130
+ ${resultText}`,
131
+ },
132
+ ],
145
133
  };
146
134
  }
147
- else if (status === "failed") {
135
+ else if (result.status === 'failed') {
148
136
  activeResearchOperations.delete(researchId);
149
- const errorInfo = interaction.error || "Unknown error";
150
- logger.error(`Research failed: ${researchId} - ${errorInfo}`);
137
+ logger.error(`Research failed: ${researchId} - ${result.error}`);
151
138
  return {
152
- content: [{
153
- type: "text",
139
+ content: [
140
+ {
141
+ type: 'text',
154
142
  text: `**Deep Research Failed**
155
143
 
156
144
  | Field | Value |
157
145
  |-------|-------|
158
146
  | **Research ID** | \`${researchId}\` |
159
147
  | **Status** | ❌ Failed |
160
- | **Error** | ${errorInfo} |
148
+ | **Error** | ${result.error} |
161
149
 
162
150
  The research task encountered an error. You can try:
163
151
  - Starting a new research task with a different query
164
- - Using \`gemini-search\` for simpler web searches`
165
- }],
166
- isError: true
152
+ - Using \`gemini-search\` for simpler web searches`,
153
+ },
154
+ ],
155
+ isError: true,
167
156
  };
168
157
  }
169
158
  else {
170
159
  // Still in progress
171
160
  return {
172
- content: [{
173
- type: "text",
161
+ content: [
162
+ {
163
+ type: 'text',
174
164
  text: `**Deep Research In Progress**
175
165
 
176
166
  | Field | Value |
177
167
  |-------|-------|
178
168
  | **Research ID** | \`${researchId}\` |
179
- | **Status** | ⏳ ${status} |
169
+ | **Status** | ⏳ ${result.status} |
180
170
  | **Elapsed** | ${elapsedMinutes}m ${elapsedSeconds}s |
181
171
  | **Query** | ${operationInfo?.prompt.substring(0, 50) || 'Unknown'}... |
182
172
 
183
173
  The agent is still working. Deep research typically takes 2-10 minutes.
184
174
 
185
- Check again in 30-60 seconds using \`gemini-check-research\`.`
186
- }]
175
+ Check again in 30-60 seconds using \`gemini-check-research\`.`,
176
+ },
177
+ ],
187
178
  };
188
179
  }
189
180
  }
@@ -191,45 +182,39 @@ Check again in 30-60 seconds using \`gemini-check-research\`.`
191
182
  const errorMessage = error instanceof Error ? error.message : String(error);
192
183
  logger.error(`Error checking research status: ${errorMessage}`);
193
184
  return {
194
- content: [{ type: "text", text: `Error checking research status: ${errorMessage}` }],
195
- isError: true
185
+ content: [{ type: 'text', text: `Error checking research status: ${errorMessage}` }],
186
+ isError: true,
196
187
  };
197
188
  }
198
189
  });
199
190
  // Follow-up on completed research
200
- server.tool("gemini-research-followup", {
201
- researchId: z.string().describe("The research ID from a completed research task"),
202
- question: z.string().describe("Follow-up question about the research results")
191
+ server.tool('gemini-research-followup', {
192
+ researchId: z.string().describe('The research ID from a completed research task'),
193
+ question: z.string().describe('Follow-up question about the research results'),
203
194
  }, async ({ researchId, question }) => {
204
195
  logger.info(`Research follow-up on ${researchId}: ${question.substring(0, 50)}...`);
205
196
  try {
206
- const interaction = await genAI.interactions.create({
207
- input: question,
208
- model: "gemini-3-pro-preview",
209
- previousInteractionId: researchId
210
- });
211
- const outputs = interaction.outputs || [];
212
- const result = outputs.length > 0
213
- ? outputs[outputs.length - 1].text || "No response"
214
- : "No response received";
197
+ const result = await followUpResearch(researchId, question);
215
198
  return {
216
- content: [{
217
- type: "text",
199
+ content: [
200
+ {
201
+ type: 'text',
218
202
  text: `**Research Follow-up**
219
203
 
220
204
  **Question:** ${question}
221
205
 
222
206
  **Answer:**
223
- ${result}`
224
- }]
207
+ ${result}`,
208
+ },
209
+ ],
225
210
  };
226
211
  }
227
212
  catch (error) {
228
213
  const errorMessage = error instanceof Error ? error.message : String(error);
229
214
  logger.error(`Error with research follow-up: ${errorMessage}`);
230
215
  return {
231
- content: [{ type: "text", text: `Error with follow-up: ${errorMessage}` }],
232
- isError: true
216
+ content: [{ type: 'text', text: `Error with follow-up: ${errorMessage}` }],
217
+ isError: true,
233
218
  };
234
219
  }
235
220
  });
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Helps users estimate costs and manage context windows.
5
5
  */
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
7
  /**
8
8
  * Register token counting tool with the MCP server
9
9
  */
@@ -3,37 +3,31 @@
3
3
  *
4
4
  * Helps users estimate costs and manage context windows.
5
5
  */
6
- import { z } from "zod";
7
- import { logger } from "../utils/logger.js";
8
- import { genAI } from "../gemini-client.js";
6
+ import { z } from 'zod';
7
+ import { logger } from '../utils/logger.js';
8
+ import { countTokens } from '../gemini-client.js';
9
9
  /**
10
10
  * Register token counting tool with the MCP server
11
11
  */
12
12
  export function registerTokenCountTool(server) {
13
- server.tool("gemini-count-tokens", {
14
- content: z.string().describe("The text content to count tokens for"),
13
+ server.tool('gemini-count-tokens', {
14
+ content: z.string().describe('The text content to count tokens for'),
15
15
  model: z
16
- .enum(["pro", "flash"])
17
- .default("flash")
18
- .describe("Which model to use for counting (affects tokenization)")
19
- }, async ({ content, model = "flash" }) => {
16
+ .enum(['pro', 'flash'])
17
+ .default('flash')
18
+ .describe('Which model to use for counting (affects tokenization)'),
19
+ }, async ({ content, model = 'flash' }) => {
20
20
  logger.info(`Counting tokens for ${content.length} characters using ${model} model`);
21
21
  try {
22
- const modelName = model === "pro"
23
- ? (process.env.GEMINI_PRO_MODEL || "gemini-3-pro-preview")
24
- : (process.env.GEMINI_FLASH_MODEL || "gemini-3-flash-preview");
25
- const result = await genAI.models.countTokens({
26
- model: modelName,
27
- contents: content
28
- });
29
- const totalTokens = result.totalTokens || 0;
22
+ const result = await countTokens(content, model);
23
+ const totalTokens = result.totalTokens;
30
24
  // Estimate costs (approximate, based on typical pricing)
31
25
  // Gemini 3 Pro: ~$1.25 per 1M input tokens
32
26
  // Gemini 3 Flash: ~$0.075 per 1M input tokens
33
- const costPer1M = model === "pro" ? 1.25 : 0.075;
27
+ const costPer1M = model === 'pro' ? 1.25 : 0.075;
34
28
  const estimatedCost = (totalTokens / 1_000_000) * costPer1M;
35
29
  // Context window info
36
- const contextWindow = model === "pro" ? 1_000_000 : 1_000_000;
30
+ const contextWindow = model === 'pro' ? 1_000_000 : 1_000_000;
37
31
  const percentUsed = (totalTokens / contextWindow) * 100;
38
32
  const response = `**Token Count Results**
39
33
 
@@ -41,7 +35,7 @@ export function registerTokenCountTool(server) {
41
35
  |--------|-------|
42
36
  | **Total Tokens** | ${totalTokens.toLocaleString()} |
43
37
  | **Characters** | ${content.length.toLocaleString()} |
44
- | **Model** | ${modelName} |
38
+ | **Model** | ${result.modelName} |
45
39
 
46
40
  **Context Window Usage:**
47
41
  - Context window: ${contextWindow.toLocaleString()} tokens
@@ -55,15 +49,15 @@ export function registerTokenCountTool(server) {
55
49
  *Note: Actual costs may vary. Check [Google AI pricing](https://ai.google.dev/pricing) for current rates.*`;
56
50
  logger.info(`Token count: ${totalTokens}`);
57
51
  return {
58
- content: [{ type: "text", text: response }]
52
+ content: [{ type: 'text', text: response }],
59
53
  };
60
54
  }
61
55
  catch (error) {
62
56
  const errorMessage = error instanceof Error ? error.message : String(error);
63
57
  logger.error(`Error counting tokens: ${errorMessage}`);
64
58
  return {
65
- content: [{ type: "text", text: `Error counting tokens: ${errorMessage}` }],
66
- isError: true
59
+ content: [{ type: 'text', text: `Error counting tokens: ${errorMessage}` }],
60
+ isError: true,
67
61
  };
68
62
  }
69
63
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlabs-inc/gemini-mcp",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "MCP server for Gemini 3 integration with Claude Code - full frontier AI capabilities",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",