This is page 1 of 8. Use http://codebase.md/bsmi021/mcp-gemini-server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .env.example
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .prettierrc.json
├── Dockerfile
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── review-prompt.txt
├── scripts
│ ├── gemini-review.sh
│ └── run-with-health-check.sh
├── smithery.yaml
├── src
│ ├── config
│ │ └── ConfigurationManager.ts
│ ├── createServer.ts
│ ├── index.ts
│ ├── resources
│ │ └── system-prompt.md
│ ├── server.ts
│ ├── services
│ │ ├── ExampleService.ts
│ │ ├── gemini
│ │ │ ├── GeminiCacheService.ts
│ │ │ ├── GeminiChatService.ts
│ │ │ ├── GeminiContentService.ts
│ │ │ ├── GeminiGitDiffService.ts
│ │ │ ├── GeminiPromptTemplates.ts
│ │ │ ├── GeminiTypes.ts
│ │ │ ├── GeminiUrlContextService.ts
│ │ │ ├── GeminiValidationSchemas.ts
│ │ │ ├── GitHubApiService.ts
│ │ │ ├── GitHubUrlParser.ts
│ │ │ └── ModelMigrationService.ts
│ │ ├── GeminiService.ts
│ │ ├── index.ts
│ │ ├── mcp
│ │ │ ├── index.ts
│ │ │ └── McpClientService.ts
│ │ ├── ModelSelectionService.ts
│ │ ├── session
│ │ │ ├── index.ts
│ │ │ ├── InMemorySessionStore.ts
│ │ │ ├── SessionStore.ts
│ │ │ └── SQLiteSessionStore.ts
│ │ └── SessionService.ts
│ ├── tools
│ │ ├── exampleToolParams.ts
│ │ ├── geminiCacheParams.ts
│ │ ├── geminiCacheTool.ts
│ │ ├── geminiChatParams.ts
│ │ ├── geminiChatTool.ts
│ │ ├── geminiCodeReviewParams.ts
│ │ ├── geminiCodeReviewTool.ts
│ │ ├── geminiGenerateContentConsolidatedParams.ts
│ │ ├── geminiGenerateContentConsolidatedTool.ts
│ │ ├── geminiGenerateImageParams.ts
│ │ ├── geminiGenerateImageTool.ts
│ │ ├── geminiGenericParamSchemas.ts
│ │ ├── geminiRouteMessageParams.ts
│ │ ├── geminiRouteMessageTool.ts
│ │ ├── geminiUrlAnalysisTool.ts
│ │ ├── index.ts
│ │ ├── mcpClientParams.ts
│ │ ├── mcpClientTool.ts
│ │ ├── registration
│ │ │ ├── index.ts
│ │ │ ├── registerAllTools.ts
│ │ │ ├── ToolAdapter.ts
│ │ │ └── ToolRegistry.ts
│ │ ├── schemas
│ │ │ ├── BaseToolSchema.ts
│ │ │ ├── CommonSchemas.ts
│ │ │ ├── index.ts
│ │ │ ├── ToolSchemas.ts
│ │ │ └── writeToFileParams.ts
│ │ └── writeToFileTool.ts
│ ├── types
│ │ ├── exampleServiceTypes.ts
│ │ ├── geminiServiceTypes.ts
│ │ ├── gitdiff-parser.d.ts
│ │ ├── googleGenAI.d.ts
│ │ ├── googleGenAITypes.ts
│ │ ├── index.ts
│ │ ├── micromatch.d.ts
│ │ ├── modelcontextprotocol-sdk.d.ts
│ │ ├── node-fetch.d.ts
│ │ └── serverTypes.ts
│ └── utils
│ ├── errors.ts
│ ├── filePathSecurity.ts
│ ├── FileSecurityService.ts
│ ├── geminiErrors.ts
│ ├── healthCheck.ts
│ ├── index.ts
│ ├── logger.ts
│ ├── RetryService.ts
│ ├── ToolError.ts
│ └── UrlSecurityService.ts
├── tests
│ ├── .env.test.example
│ ├── basic-router.test.vitest.ts
│ ├── e2e
│ │ ├── clients
│ │ │ └── mcp-test-client.ts
│ │ ├── README.md
│ │ └── streamableHttpTransport.test.vitest.ts
│ ├── integration
│ │ ├── dummyMcpServerSse.ts
│ │ ├── dummyMcpServerStdio.ts
│ │ ├── geminiRouterIntegration.test.vitest.ts
│ │ ├── mcpClientIntegration.test.vitest.ts
│ │ ├── multiModelIntegration.test.vitest.ts
│ │ └── urlContextIntegration.test.vitest.ts
│ ├── tsconfig.test.json
│ ├── unit
│ │ ├── config
│ │ │ └── ConfigurationManager.multimodel.test.vitest.ts
│ │ ├── server
│ │ │ └── transportLogic.test.vitest.ts
│ │ ├── services
│ │ │ ├── gemini
│ │ │ │ ├── GeminiChatService.test.vitest.ts
│ │ │ │ ├── GeminiGitDiffService.test.vitest.ts
│ │ │ │ ├── geminiImageGeneration.test.vitest.ts
│ │ │ │ ├── GeminiPromptTemplates.test.vitest.ts
│ │ │ │ ├── GeminiUrlContextService.test.vitest.ts
│ │ │ │ ├── GeminiValidationSchemas.test.vitest.ts
│ │ │ │ ├── GitHubApiService.test.vitest.ts
│ │ │ │ ├── GitHubUrlParser.test.vitest.ts
│ │ │ │ └── ThinkingBudget.test.vitest.ts
│ │ │ ├── mcp
│ │ │ │ └── McpClientService.test.vitest.ts
│ │ │ ├── ModelSelectionService.test.vitest.ts
│ │ │ └── session
│ │ │ └── SQLiteSessionStore.test.vitest.ts
│ │ ├── tools
│ │ │ ├── geminiCacheTool.test.vitest.ts
│ │ │ ├── geminiChatTool.test.vitest.ts
│ │ │ ├── geminiCodeReviewTool.test.vitest.ts
│ │ │ ├── geminiGenerateContentConsolidatedTool.test.vitest.ts
│ │ │ ├── geminiGenerateImageTool.test.vitest.ts
│ │ │ ├── geminiRouteMessageTool.test.vitest.ts
│ │ │ ├── mcpClientTool.test.vitest.ts
│ │ │ ├── mcpToolsTests.test.vitest.ts
│ │ │ └── schemas
│ │ │ ├── BaseToolSchema.test.vitest.ts
│ │ │ ├── ToolParamSchemas.test.vitest.ts
│ │ │ └── ToolSchemas.test.vitest.ts
│ │ └── utils
│ │ ├── errors.test.vitest.ts
│ │ ├── FileSecurityService.test.vitest.ts
│ │ ├── FileSecurityService.vitest.ts
│ │ ├── FileSecurityServiceBasics.test.vitest.ts
│ │ ├── healthCheck.test.vitest.ts
│ │ ├── RetryService.test.vitest.ts
│ │ └── UrlSecurityService.test.vitest.ts
│ └── utils
│ ├── assertions.ts
│ ├── debug-error.ts
│ ├── env-check.ts
│ ├── environment.ts
│ ├── error-helpers.ts
│ ├── express-mocks.ts
│ ├── integration-types.ts
│ ├── mock-types.ts
│ ├── test-fixtures.ts
│ ├── test-generators.ts
│ ├── test-setup.ts
│ └── vitest.d.ts
├── tsconfig.json
├── tsconfig.test.json
├── vitest-globals.d.ts
├── vitest.config.ts
└── vitest.setup.ts
```
# Files
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "semi": true,
3 | "trailingComma": "es5",
4 | "singleQuote": false,
5 | "printWidth": 80,
6 | "tabWidth": 2,
7 | "endOfLine": "lf"
8 | }
9 |
```
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
```
1 | # Compiled output
2 | dist/
3 | build/
4 |
5 | # Node modules
6 | node_modules/
7 |
8 | # Generated files
9 | *.generated.*
10 | *.d.ts.map
11 |
12 | # Coverage output
13 | coverage/
14 |
15 | # Other build artifacts
16 | .next/
17 | .nuxt/
18 |
19 | # Documentation and config files
20 | *.md
21 | *.txt
22 | tsconfig*.json
23 | package-lock.json
24 | *.yaml
25 | *.yml
26 | .DS_Store
27 | LICENSE
28 |
29 | # Environment and config
30 | .env*
31 | .prettierrc*
32 | .husky/
33 | .git/
```
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": [
5 | "@typescript-eslint",
6 | "prettier"
7 | ],
8 | "extends": [
9 | "eslint:recommended",
10 | "plugin:@typescript-eslint/recommended",
11 | "prettier"
12 | ],
13 | "rules": {
14 | "prettier/prettier": "warn",
15 | "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
16 | "@typescript-eslint/no-explicit-any": "warn",
17 | "no-console": "off"
18 | },
19 | "env": {
20 | "node": true,
21 | "es2022": true
22 | },
23 | "parserOptions": {
24 | "ecmaVersion": "latest",
25 | "sourceType": "module"
26 | },
27 | "overrides": [
28 | {
29 | "files": ["tests/**/*.ts", "tests/**/*.vitest.ts"],
30 | "rules": {
31 | "@typescript-eslint/no-explicit-any": "off"
32 | }
33 | }
34 | ]
35 | }
```
--------------------------------------------------------------------------------
/tests/.env.test.example:
--------------------------------------------------------------------------------
```
1 | # Test environment configuration
2 | # Copy this file to .env.test and fill in your API keys and other settings
3 |
4 | # Required: Google Gemini API key from Google AI Studio
5 | GOOGLE_GEMINI_API_KEY=your_api_key_here
6 |
7 | # Optional: Default model to use for tests (defaults to gemini-1.5-flash)
8 | GOOGLE_GEMINI_MODEL=gemini-1.5-flash
9 |
10 | # Optional: Base directory for file tests (defaults to current directory)
11 | GEMINI_SAFE_FILE_BASE_DIR=/Users/nicobailon/Documents/development/mcp-gemini-server/tests/resources
12 |
13 | # Optional: Image generation settings
14 | GOOGLE_GEMINI_IMAGE_RESOLUTION=1024x1024
15 | GOOGLE_GEMINI_MAX_IMAGE_SIZE_MB=10
16 | GOOGLE_GEMINI_SUPPORTED_IMAGE_FORMATS=["image/jpeg","image/png","image/webp"]
17 |
18 | # Optional: Model-specific configuration
19 | # Set this to configure specific models for image generation tests
20 | GOOGLE_GEMINI_IMAGE_MODEL=gemini-2.0-flash-exp-image-generation
21 |
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # vitepress build output
108 | **/.vitepress/dist
109 |
110 | # vitepress cache directory
111 | **/.vitepress/cache
112 |
113 | # Docusaurus cache and generated files
114 | .docusaurus
115 |
116 | # Serverless directories
117 | .serverless/
118 |
119 | # FuseBox cache
120 | .fusebox/
121 |
122 | # DynamoDB Local files
123 | .dynamodb/
124 |
125 | # TernJS port file
126 | .tern-port
127 |
128 | # Stores VSCode versions used for testing VSCode extensions
129 | .vscode-test
130 |
131 | # yarn v2
132 | .yarn/cache
133 | .yarn/unplugged
134 | .yarn/build-state.yml
135 | .yarn/install-state.gz
136 | .pnp.*
137 |
138 | docs/
139 | .clinerules
140 | .cursor/
141 | .husky/
142 | .vscode/
143 | .idea/
144 | .DS_Store
145 | *workspace*
146 | *.md
147 | !README.md
148 |
149 | **/.claude/settings.local.json
150 |
151 | # Session data
152 | data/
153 | *.db
154 | *.db-wal
155 | *.db-shm
156 | .env
```
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
```
1 | # Required API Configuration
2 | GOOGLE_GEMINI_API_KEY=your_api_key_here
3 |
4 | # Required for Production (unless NODE_ENV=test)
5 | MCP_SERVER_HOST=localhost
6 | MCP_SERVER_PORT=8080
7 | # Generate this token securely using the methods in Installation step 4
8 | # e.g., node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
9 | MCP_CONNECTION_TOKEN=your_generated_secure_token_from_step_4
10 |
11 | # Optional API Configuration
12 | GOOGLE_GEMINI_MODEL=gemini-1.5-pro-latest
13 | GOOGLE_GEMINI_DEFAULT_THINKING_BUDGET=4096
14 |
15 | # Security Configuration
16 | ALLOWED_OUTPUT_PATHS=/var/opt/mcp-gemini-server/outputs,/var/opt/mcp-gemini-server/tool_data # For mcpCallServerTool and writeToFileTool
17 |
18 | # URL Context Configuration
19 | GOOGLE_GEMINI_ENABLE_URL_CONTEXT=true # Enable URL context features
20 | GOOGLE_GEMINI_URL_MAX_COUNT=20 # Maximum URLs per request
21 | GOOGLE_GEMINI_URL_MAX_CONTENT_KB=100 # Maximum content size per URL in KB
22 | GOOGLE_GEMINI_URL_FETCH_TIMEOUT_MS=10000 # Fetch timeout per URL in milliseconds
23 | GOOGLE_GEMINI_URL_ALLOWED_DOMAINS=* # Allowed domains (* for all, or comma-separated list)
24 | GOOGLE_GEMINI_URL_BLOCKLIST=malicious.com,spam.net # Blocked domains (comma-separated)
25 | GOOGLE_GEMINI_URL_CONVERT_TO_MARKDOWN=true # Convert HTML to markdown
26 | GOOGLE_GEMINI_URL_INCLUDE_METADATA=true # Include URL metadata in context
27 | GOOGLE_GEMINI_URL_ENABLE_CACHING=true # Enable URL content caching
28 | GOOGLE_GEMINI_URL_USER_AGENT=MCP-Gemini-Server/1.0 # Custom User-Agent
29 |
30 | # Server Configuration
31 | MCP_CLIENT_ID=gemini-sdk-client # Optional: Default client ID for MCP connections (defaults to "gemini-sdk-client")
32 | MCP_TRANSPORT=stdio # Options: stdio, sse, streamable, http (replaced deprecated MCP_TRANSPORT_TYPE)
33 | MCP_LOG_LEVEL=info # Optional: Log level for MCP operations (debug, info, warn, error)
34 | MCP_ENABLE_STREAMING=true # Enable SSE streaming for HTTP transport
35 | MCP_SESSION_TIMEOUT=3600 # Session timeout in seconds for HTTP transport
36 | SESSION_STORE_TYPE=memory # Options: memory, sqlite
37 | SQLITE_DB_PATH=./data/sessions.db # Path to SQLite database file. For production, consider an absolute path to a persistent volume.
38 | ENABLE_HEALTH_CHECK=true
39 | HEALTH_CHECK_PORT=3000
40 |
41 | # GitHub Personal Access Token for API access (required for GitHub code review features)
42 | # For public repos, token needs 'public_repo' and 'read:user' scopes
43 | # For private repos, token needs 'repo' scope
44 | GITHUB_API_TOKEN=your_github_token_here
```
--------------------------------------------------------------------------------
/tests/e2e/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # E2E Tests for MCP Gemini Server
2 |
3 | ## Overview
4 |
5 | End-to-end tests should be separated from unit tests and run with real clients and servers. This directory contains examples and instructions for setting up E2E tests.
6 |
7 | ## Structure
8 |
9 | ```
10 | tests/e2e/
11 | ├── README.md # This file
12 | ├── fixtures/ # Test data and configurations
13 | ├── clients/ # Test client implementations
14 | │ └── mcp-test-client.ts # MCP protocol test client
15 | └── scenarios/ # Test scenarios
16 | ├── basic-flow.test.ts # Basic initialization and tool calling
17 | ├── streaming.test.ts # Streaming response tests
18 | └── session-management.test.ts # Session lifecycle tests
19 | ```
20 |
21 | ## Running E2E Tests
22 |
23 | E2E tests should be run separately from unit tests:
24 |
25 | ```bash
26 | # Run unit tests (fast, mocked)
27 | npm run test
28 |
29 | # Run E2E tests (slower, real servers)
30 | npm run test:e2e
31 | ```
32 |
33 | ## Example E2E Test
34 |
35 | ```typescript
36 | import { MCPTestClient } from './clients/mcp-test-client';
37 | import { startServer } from './helpers/server-helper';
38 |
39 | describe('E2E: Basic MCP Flow', () => {
40 | let server: any;
41 | let client: MCPTestClient;
42 |
43 | beforeAll(async () => {
44 | // Start real server
45 | server = await startServer({
46 | transport: 'streamable',
47 | port: 3001
48 | });
49 |
50 | // Create real client
51 | client = new MCPTestClient({
52 | url: 'http://localhost:3001/mcp'
53 | });
54 | });
55 |
56 | afterAll(async () => {
57 | await client.disconnect();
58 | await server.stop();
59 | });
60 |
61 | it('should complete full MCP flow', async () => {
62 | // 1. Initialize
63 | const initResult = await client.initialize();
64 | expect(initResult.protocolVersion).toBe('2024-11-05');
65 |
66 | // 2. List tools
67 | const tools = await client.listTools();
68 | expect(tools).toContain(
69 | expect.objectContaining({ name: 'gemini_generateContent' })
70 | );
71 |
72 | // 3. Call tool
73 | const result = await client.callTool('gemini_generateContent', {
74 | prompt: 'Hello',
75 | modelName: 'gemini-1.5-flash'
76 | });
77 | expect(result).toBeDefined();
78 | });
79 | });
80 | ```
81 |
82 | ## Test Client Implementation
83 |
84 | The test client should implement the full MCP protocol:
85 |
86 | ```typescript
87 | export class MCPTestClient {
88 | private sessionId?: string;
89 | private url: string;
90 |
91 | constructor(options: { url: string }) {
92 | this.url = options.url;
93 | }
94 |
95 | async initialize(): Promise<any> {
96 | const response = await fetch(this.url, {
97 | method: 'POST',
98 | headers: {
99 | 'Content-Type': 'application/json',
100 | 'Accept': 'application/json, text/event-stream'
101 | },
102 | body: JSON.stringify({
103 | jsonrpc: '2.0',
104 | id: 1,
105 | method: 'initialize',
106 | params: {
107 | protocolVersion: '2024-11-05',
108 | capabilities: {},
109 | clientInfo: {
110 | name: 'test-client',
111 | version: '1.0.0'
112 | }
113 | }
114 | })
115 | });
116 |
117 | this.sessionId = response.headers.get('Mcp-Session-Id') || undefined;
118 | return this.parseResponse(response);
119 | }
120 |
121 | // ... other methods
122 | }
123 | ```
124 |
125 | ## Benefits of E2E Testing
126 |
127 | 1. **Real Protocol Testing**: Tests actual MCP protocol implementation
128 | 2. **Integration Verification**: Ensures all components work together
129 | 3. **Performance Testing**: Can measure real-world performance
130 | 4. **Regression Prevention**: Catches issues unit tests might miss
131 |
132 | ## Current Status
133 |
134 | E2E tests are not yet implemented. When implementing:
135 |
136 | 1. Use a real MCP client library if available
137 | 2. Test against multiple transport types
138 | 3. Include error scenarios and edge cases
139 | 4. Add performance benchmarks
140 | 5. Consider using Docker for isolated test environments
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | [](https://mseep.ai/app/bsmi021-mcp-gemini-server)
2 |
3 | # MCP Gemini Server
4 |
5 | ## Table of Contents
6 | - [Overview](#overview)
7 | - [File Uploads vs URL-Based Analysis](#file-uploads-vs-url-based-analysis)
8 | - [Features](#features)
9 | - [Prerequisites](#prerequisites)
10 | - [Installation & Setup](#installation--setup)
11 | - [Configuration](#configuration)
12 | - [Available Tools](#available-tools)
13 | - [Usage Examples](#usage-examples)
14 | - [Supported Multimedia Analysis Use Cases](#supported-multimedia-analysis-use-cases)
15 | - [MCP Gemini Server and Gemini SDK's MCP Function Calling](#mcp-gemini-server-and-gemini-sdks-mcp-function-calling)
16 | - [Environment Variables](#environment-variables)
17 | - [Security Considerations](#security-considerations)
18 | - [Error Handling](#error-handling)
19 | - [Development and Testing](#development-and-testing)
20 | - [Contributing](#contributing)
21 | - [Code Review Tools](#code-review-tools)
22 | - [Server Features](#server-features)
23 | - [Known Issues](#known-issues)
24 |
25 | ## Overview
26 |
27 | This project provides a dedicated MCP (Model Context Protocol) server that wraps the `@google/genai` SDK (v0.10.0). It exposes Google's Gemini model capabilities as standard MCP tools, allowing other LLMs (like Claude) or MCP-compatible systems to leverage Gemini's features as a backend workhorse.
28 |
29 | This server aims to simplify integration with Gemini models by providing a consistent, tool-based interface managed via the MCP standard. It supports the latest Gemini models including `gemini-1.5-pro-latest`, `gemini-1.5-flash`, and `gemini-2.5-pro` models.
30 |
31 | **Important Note:** This server does not support direct file uploads. Instead, it focuses on URL-based multimedia analysis for images and videos. For text-based content processing, use the standard content generation tools.
32 |
33 | ## File Uploads vs URL-Based Analysis
34 |
35 | ### ❌ Not Supported: Direct File Uploads
36 |
37 | This MCP Gemini Server **does not support** the following file upload operations:
38 |
39 | - **Local file uploads**: Cannot upload files from your local filesystem to Gemini
40 | - **Base64 encoded files**: Cannot process base64-encoded image or video data
41 | - **Binary file data**: Cannot handle raw file bytes or binary data
42 | - **File references**: Cannot process file IDs or references from uploaded content
43 | - **Audio file uploads**: Cannot upload and transcribe audio files directly
44 |
45 | **Why File Uploads Are Not Supported:**
46 | - Simplified architecture focused on URL-based processing
47 | - Enhanced security by avoiding file handling complexities
48 | - Reduced storage and bandwidth requirements
49 | - Streamlined codebase maintenance
50 |
51 | ### ✅ Fully Supported: URL-Based Multimedia Analysis
52 |
53 | This server **fully supports** analyzing multimedia content from publicly accessible URLs:
54 |
55 | **Image Analysis from URLs:**
56 | - **Public image URLs**: Analyze images hosted on any publicly accessible web server
57 | - **Supported formats**: PNG, JPEG, WebP, HEIC, HEIF via direct URL access
58 | - **Multiple images**: Process multiple image URLs in a single request
59 | - **Security validation**: Automatic URL validation and security screening
60 |
61 | **YouTube Video Analysis:**
62 | - **Public YouTube videos**: Full analysis of any public YouTube video content
63 | - **Video understanding**: Extract insights, summaries, and detailed analysis
64 | - **Educational content**: Perfect for analyzing tutorials, lectures, and educational videos
65 | - **Multiple videos**: Process multiple YouTube URLs (up to 10 per request with Gemini 2.5+)
66 |
67 | **Web Content Processing:**
68 | - **HTML content**: Analyze and extract information from web pages
69 | - **Mixed media**: Combine text content with embedded images and videos
70 | - **Contextual analysis**: Process URLs alongside text prompts for comprehensive analysis
71 |
72 | ### Alternatives for Local Content
73 |
74 | **If you have local files to analyze:**
75 |
76 | 1. **Host on a web server**: Upload your files to a public web server and use the URL
77 | 2. **Use cloud storage**: Upload to services like Google Drive, Dropbox, or AWS S3 with public access
78 | 3. **Use GitHub**: Host images in a GitHub repository and use the raw file URLs
79 | 4. **Use image hosting services**: Upload to services like Imgur, ImageBB, or similar platforms
80 |
81 | **For audio content:**
82 | - Use external transcription services (Whisper API, Google Speech-to-Text, etc.)
83 | - Upload audio to YouTube and analyze the resulting video URL
84 | - Use other MCP servers that specialize in audio processing
85 |
86 | ## Features
87 |
88 | * **Core Generation:** Standard (`gemini_generateContent`) and streaming (`gemini_generateContentStream`) text generation with support for system instructions and cached content.
89 | * **Function Calling:** Enables Gemini models to request the execution of client-defined functions (`gemini_functionCall`).
90 | * **Stateful Chat:** Manages conversational context across multiple turns (`gemini_startChat`, `gemini_sendMessage`, `gemini_sendFunctionResult`) with support for system instructions, tools, and cached content.
91 | * **URL-Based Multimedia Analysis:** Analyze images from public URLs and YouTube videos without file uploads. Direct file uploads are not supported.
92 | * **Caching:** Create, list, retrieve, update, and delete cached content to optimize prompts with support for tools and tool configurations.
93 | * **Image Generation:** Generate images from text prompts using Gemini 2.0 Flash Experimental (`gemini_generateImage`) with control over resolution, number of images, and negative prompts. Also supports the latest Imagen 3.1 model for high-quality dedicated image generation with advanced style controls. Note that Gemini 2.5 models (Flash and Pro) do not currently support image generation.
94 | * **URL Context Processing:** Fetch and analyze web content directly from URLs with advanced security, caching, and content processing capabilities.
95 | * `gemini_generateContent`: Enhanced with URL context support for including web content in prompts
96 | * `gemini_generateContentStream`: Streaming generation with URL context integration
97 | * `gemini_url_analysis`: Specialized tool for advanced URL content analysis with multiple analysis types
98 | * **MCP Client:** Connect to and interact with external MCP servers.
99 | * `mcpConnectToServer`: Establishes a connection to an external MCP server.
100 | * `mcpListServerTools`: Lists available tools on a connected MCP server.
101 | * `mcpCallServerTool`: Calls a function on a connected MCP server, with an option for file output.
102 | * `mcpDisconnectFromServer`: Disconnects from an external MCP server.
103 | * `writeToFile`: Writes content directly to files within allowed directories.
104 |
105 |
106 | ## Prerequisites
107 |
108 | * Node.js (v18 or later)
109 | * An API Key from **Google AI Studio** (<https://aistudio.google.com/app/apikey>).
110 | * **Important:** The Caching API is **only compatible with Google AI Studio API keys** and is **not supported** when using Vertex AI credentials. This server does not currently support Vertex AI authentication.
111 |
112 | ## Installation & Setup
113 |
114 | ### Installing Manually
115 |
116 | 1. **Clone/Place Project:** Ensure the `mcp-gemini-server` project directory is accessible on your system.
117 | 2. **Install Dependencies:** Navigate to the project directory in your terminal and run:
118 |
119 | ```bash
120 | npm install
121 | ```
122 |
123 | 3. **Build Project:** Compile the TypeScript source code:
124 |
125 | ```bash
126 | npm run build
127 | ```
128 |
129 | This command uses the TypeScript compiler (`tsc`) and outputs the JavaScript files to the `./dist` directory (as specified by `outDir` in `tsconfig.json`). The main server entry point will be `dist/server.js`.
130 | 4. **Generate Connection Token:** Create a strong, unique connection token for secure communication between your MCP client and the server. This is a shared secret that you generate and configure on both the server and client sides.
131 |
132 | **Generate a secure token using one of these methods:**
133 |
134 | **Option A: Using Node.js crypto (Recommended)**
135 | ```bash
136 | node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
137 | ```
138 |
139 | **Option B: Using OpenSSL**
140 | ```bash
141 | openssl rand -hex 32
142 | ```
143 |
144 | **Option C: Using PowerShell (Windows)**
145 | ```powershell
146 | [System.Convert]::ToBase64String([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32))
147 | ```
148 |
149 | **Option D: Online Generator (Use with caution)**
150 | Use a reputable password generator like [1Password](https://1password.com/password-generator/) or [Bitwarden](https://bitwarden.com/password-generator/) to generate a 64-character random string.
151 |
152 | **Important Security Notes:**
153 | - The token should be at least 32 characters long and contain random characters
154 | - Never share this token or commit it to version control
155 | - Use a different token for each server instance
156 | - Store the token securely (environment variables, secrets manager, etc.)
157 | - Save this token - you'll need to use the exact same value in both server and client configurations
158 |
159 | 5. **Configure MCP Client:** Add the server configuration to your MCP client's settings file (e.g., `cline_mcp_settings.json` for Cline/VSCode, or `claude_desktop_config.json` for Claude Desktop App). Replace `/path/to/mcp-gemini-server` with the actual **absolute path** on your system, `YOUR_API_KEY` with your Google AI Studio key, and `YOUR_GENERATED_CONNECTION_TOKEN` with the token you generated in step 4.
160 |
161 | ```json
162 | {
163 | "mcpServers": {
164 | "gemini-server": { // Or your preferred name
165 | "command": "node",
166 | "args": ["/path/to/mcp-gemini-server/dist/server.js"], // Absolute path to the compiled server entry point
167 | "env": {
168 | "GOOGLE_GEMINI_API_KEY": "YOUR_API_KEY",
169 | "MCP_SERVER_HOST": "localhost", // Required: Server host
170 | "MCP_SERVER_PORT": "8080", // Required: Server port
171 | "MCP_CONNECTION_TOKEN": "YOUR_GENERATED_CONNECTION_TOKEN", // Required: Use the token from step 4
172 | "GOOGLE_GEMINI_MODEL": "gemini-1.5-flash", // Optional: Set a default model
173 | // Optional security configurations removed - file operations no longer supported
174 | "ALLOWED_OUTPUT_PATHS": "/var/opt/mcp-gemini-server/outputs,/tmp/mcp-gemini-outputs" // Optional: Comma-separated list of allowed output directories for mcpCallServerTool and writeToFileTool
175 | },
176 | "disabled": false,
177 | "autoApprove": []
178 | }
179 | // ... other servers
180 | }
181 | }
182 | ```
183 |
184 | **Important Notes:**
185 | - The path in `args` must be the **absolute path** to the compiled `dist/server.js` file
186 | - `MCP_SERVER_HOST`, `MCP_SERVER_PORT`, and `MCP_CONNECTION_TOKEN` are required unless `NODE_ENV` is set to `test`
187 | - `MCP_CONNECTION_TOKEN` must be the exact same value you generated in step 4
188 | - Ensure the path exists and the server has been built using `npm run build`
189 | 6. **Restart MCP Client:** Restart your MCP client application (e.g., VS Code with Cline extension, Claude Desktop App) to load the new server configuration. The MCP client will manage starting and stopping the server process.
190 |
191 | ## Configuration
192 |
193 | The server uses environment variables for configuration, passed via the `env` object in the MCP settings:
194 |
195 | * `GOOGLE_GEMINI_API_KEY` (**Required**): Your API key obtained from Google AI Studio.
196 | * `GOOGLE_GEMINI_MODEL` (*Optional*): Specifies a default Gemini model name (e.g., `gemini-1.5-flash`, `gemini-1.0-pro`). If set, tools that require a model name (like `gemini_generateContent`, `gemini_startChat`, etc.) will use this default when the `modelName` parameter is omitted in the tool call. This simplifies client calls when primarily using one model. If this environment variable is *not* set, the `modelName` parameter becomes required for those tools. See the [Google AI documentation](https://ai.google.dev/models/gemini) for available model names.
197 | * `ALLOWED_OUTPUT_PATHS` (*Optional*): A comma-separated list of absolute paths to directories where the `mcpCallServerTool` (with `outputToFile` parameter) and `writeToFileTool` are allowed to write files. If not set, file output will be disabled for these tools. This is a security measure to prevent arbitrary file writes.
198 |
199 | ## Available Tools
200 |
201 | This server provides the following MCP tools. Parameter schemas are defined using Zod for validation and description.
202 |
203 | **Validation and Error Handling:** All parameters are validated using Zod schemas at both the MCP tool level and service layer, providing consistent validation, detailed error messages, and type safety. The server implements comprehensive error mapping to provide clear, actionable error messages.
204 |
205 | **Retry Logic:** API requests automatically use exponential backoff retry for transient errors (network issues, rate limits, timeouts), improving reliability for unstable connections. The retry mechanism includes configurable parameters for maximum attempts, delay times, and jitter to prevent thundering herd effects.
206 |
207 | **Note on Optional Parameters:** Many tools accept complex optional parameters (e.g., `generationConfig`, `safetySettings`, `toolConfig`, `history`, `functionDeclarations`, `contents`). These parameters are typically objects or arrays whose structure mirrors the types defined in the underlying `@google/genai` SDK (v0.10.0). For the exact structure and available fields within these complex parameters, please refer to:
208 | 1. The corresponding `src/tools/*Params.ts` file in this project.
209 | 2. The official [Google AI JS SDK Documentation](https://github.com/google/generative-ai-js).
210 |
211 | ### Core Generation
212 |
213 | * **`gemini_generateContent`**
214 | * *Description:* Generates non-streaming text content from a prompt with optional URL context support.
215 | * *Required Params:* `prompt` (string)
216 | * *Optional Params:*
217 | * `modelName` (string) - Name of the model to use
218 | * `generationConfig` (object) - Controls generation parameters like temperature, topP, etc.
219 | * `thinkingConfig` (object) - Controls model reasoning process
220 | * `thinkingBudget` (number) - Maximum tokens for reasoning (0-24576)
221 | * `reasoningEffort` (string) - Simplified control: "none" (0 tokens), "low" (1K), "medium" (8K), "high" (24K)
222 | * `safetySettings` (array) - Controls content filtering by harm category
223 | * `systemInstruction` (string or object) - System instruction to guide model behavior
224 | * `cachedContentName` (string) - Identifier for cached content to use with this request
225 | * `urlContext` (object) - Fetch and include web content from URLs
226 | * `urls` (array) - URLs to fetch and include as context (max 20)
227 | * `fetchOptions` (object) - Configuration for URL fetching
228 | * `maxContentKb` (number) - Maximum content size per URL in KB (default: 100)
229 | * `timeoutMs` (number) - Fetch timeout per URL in milliseconds (default: 10000)
230 | * `includeMetadata` (boolean) - Include URL metadata in context (default: true)
231 | * `convertToMarkdown` (boolean) - Convert HTML to markdown (default: true)
232 | * `allowedDomains` (array) - Specific domains to allow for this request
233 | * `userAgent` (string) - Custom User-Agent header for URL requests
234 | * `modelPreferences` (object) - Model selection preferences
235 | * *Note:* Can handle multimodal inputs, cached content, and URL context for comprehensive content generation
236 | * *Thinking Budget:* Controls the token budget for model reasoning. Lower values provide faster responses, higher values improve complex reasoning.
237 | * **`gemini_generateContentStream`**
238 | * *Description:* Generates text content via streaming using Server-Sent Events (SSE) for real-time content delivery with URL context support.
239 | * *Required Params:* `prompt` (string)
240 | * *Optional Params:*
241 | * `modelName` (string) - Name of the model to use
242 | * `generationConfig` (object) - Controls generation parameters like temperature, topP, etc.
243 | * `thinkingConfig` (object) - Controls model reasoning process
244 | * `thinkingBudget` (number) - Maximum tokens for reasoning (0-24576)
245 | * `reasoningEffort` (string) - Simplified control: "none" (0 tokens), "low" (1K), "medium" (8K), "high" (24K)
246 | * `safetySettings` (array) - Controls content filtering by harm category
247 | * `systemInstruction` (string or object) - System instruction to guide model behavior
248 | * `cachedContentName` (string) - Identifier for cached content to use with this request
249 | * `urlContext` (object) - Same URL context options as `gemini_generateContent`
250 | * `modelPreferences` (object) - Model selection preferences
251 |
252 | ### Function Calling
253 |
254 | * **`gemini_functionCall`**
255 | * *Description:* Sends a prompt and function declarations to the model, returning either a text response or a requested function call object (as a JSON string).
256 | * *Required Params:* `prompt` (string), `functionDeclarations` (array)
257 | * *Optional Params:*
258 | * `modelName` (string) - Name of the model to use
259 | * `generationConfig` (object) - Controls generation parameters
260 | * `safetySettings` (array) - Controls content filtering
261 | * `toolConfig` (object) - Configures tool behavior like temperature and confidence thresholds
262 |
263 | ### Stateful Chat
264 |
265 | * **`gemini_startChat`**
266 | * *Description:* Initiates a new stateful chat session and returns a unique `sessionId`.
267 | * *Optional Params:*
268 | * `modelName` (string) - Name of the model to use
269 | * `history` (array) - Initial conversation history
270 | * `tools` (array) - Tool definitions including function declarations
271 | * `generationConfig` (object) - Controls generation parameters
272 | * `thinkingConfig` (object) - Controls model reasoning process
273 | * `thinkingBudget` (number) - Maximum tokens for reasoning (0-24576)
274 | * `reasoningEffort` (string) - Simplified control: "none" (0 tokens), "low" (1K), "medium" (8K), "high" (24K)
275 | * `safetySettings` (array) - Controls content filtering
276 | * `systemInstruction` (string or object) - System instruction to guide model behavior
277 | * `cachedContentName` (string) - Identifier for cached content to use with this session
278 | * **`gemini_sendMessage`**
279 | * *Description:* Sends a message within an existing chat session.
280 | * *Required Params:* `sessionId` (string), `message` (string)
281 | * *Optional Params:*
282 | * `generationConfig` (object) - Controls generation parameters
283 | * `thinkingConfig` (object) - Controls model reasoning process
284 | * `thinkingBudget` (number) - Maximum tokens for reasoning (0-24576)
285 | * `reasoningEffort` (string) - Simplified control: "none" (0 tokens), "low" (1K), "medium" (8K), "high" (24K)
286 | * `safetySettings` (array) - Controls content filtering
287 | * `tools` (array) - Tool definitions including function declarations
288 | * `toolConfig` (object) - Configures tool behavior
289 | * `cachedContentName` (string) - Identifier for cached content to use with this message
290 | * **`gemini_sendFunctionResult`**
291 | * *Description:* Sends the result of a function execution back to a chat session.
292 | * *Required Params:* `sessionId` (string), `functionResponse` (string) - The result of the function execution
293 | * *Optional Params:* `functionCall` (object) - Reference to the original function call
294 | * **`gemini_routeMessage`**
295 | * *Description:* Routes a message to the most appropriate model from a provided list based on message content. Returns both the model's response and which model was selected.
296 | * *Required Params:*
297 | * `message` (string) - The text message to be routed to the most appropriate model
298 | * `models` (array) - Array of model names to consider for routing (e.g., ['gemini-1.5-flash', 'gemini-1.5-pro']). The first model in the list will be used for routing decisions.
299 | * *Optional Params:*
300 | * `routingPrompt` (string) - Custom prompt to use for routing decisions. If not provided, a default routing prompt will be used.
301 | * `defaultModel` (string) - Model to fall back to if routing fails. If not provided and routing fails, an error will be thrown.
302 | * `generationConfig` (object) - Generation configuration settings to apply to the selected model's response.
303 | * `thinkingConfig` (object) - Controls model reasoning process
304 | * `thinkingBudget` (number) - Maximum tokens for reasoning (0-24576)
305 | * `reasoningEffort` (string) - Simplified control: "none" (0 tokens), "low" (1K), "medium" (8K), "high" (24K)
306 | * `safetySettings` (array) - Safety settings to apply to both routing and final response.
307 | * `systemInstruction` (string or object) - A system instruction to guide the model's behavior after routing.
308 |
309 | ### Remote File Operations (Removed)
310 |
311 | **Note:** Direct file upload operations are no longer supported by this server. The server now focuses exclusively on URL-based multimedia analysis for images and videos, and text-based content generation.
312 |
313 | **Alternative Approaches:**
314 | - **For Image Analysis:** Use publicly accessible image URLs with `gemini_generateContent` or `gemini_url_analysis` tools
315 | - **For Video Analysis:** Use publicly accessible YouTube video URLs for content analysis
316 | - **For Audio Content:** Audio transcription via file uploads is not supported - consider using URL-based services that provide audio transcripts
317 | - **For Document Analysis:** Use URL-based document analysis or convert documents to publicly accessible formats
318 |
319 | ### Caching (Google AI Studio Key Required)
320 |
321 | * **`gemini_createCache`**
322 | * *Description:* Creates cached content for compatible models (e.g., `gemini-1.5-flash`).
323 | * *Required Params:* `contents` (array), `model` (string)
324 | * *Optional Params:*
325 | * `displayName` (string) - Human-readable name for the cached content
326 | * `systemInstruction` (string or object) - System instruction to apply to the cached content
327 | * `ttl` (string - e.g., '3600s') - Time-to-live for the cached content
328 | * `tools` (array) - Tool definitions for use with the cached content
329 | * `toolConfig` (object) - Configuration for the tools
330 | * **`gemini_listCaches`**
331 | * *Description:* Lists existing cached content.
332 | * *Required Params:* None
333 | * *Optional Params:* `pageSize` (number), `pageToken` (string - Note: `pageToken` may not be reliably returned currently).
334 | * **`gemini_getCache`**
335 | * *Description:* Retrieves metadata for specific cached content.
336 | * *Required Params:* `cacheName` (string - e.g., `cachedContents/abc123xyz`)
337 | * **`gemini_updateCache`**
338 | * *Description:* Updates metadata and contents for cached content.
339 | * *Required Params:* `cacheName` (string), `contents` (array)
340 | * *Optional Params:*
341 | * `displayName` (string) - Updated display name
342 | * `systemInstruction` (string or object) - Updated system instruction
343 | * `ttl` (string) - Updated time-to-live
344 | * `tools` (array) - Updated tool definitions
345 | * `toolConfig` (object) - Updated tool configuration
346 | * **`gemini_deleteCache`**
347 | * *Description:* Deletes cached content.
348 | * *Required Params:* `cacheName` (string - e.g., `cachedContents/abc123xyz`)
349 |
350 | ### Image Generation
351 |
352 | * **`gemini_generateImage`**
353 | * *Description:* Generates images from text prompts using available image generation models.
354 | * *Required Params:* `prompt` (string - descriptive text prompt for image generation)
355 | * *Optional Params:*
356 | * `modelName` (string - defaults to "imagen-3.1-generate-003" for high-quality dedicated image generation or use "gemini-2.0-flash-exp-image-generation" for Gemini models)
357 | * `resolution` (string enum: "512x512", "1024x1024", "1536x1536")
358 | * `numberOfImages` (number - 1-8, default: 1)
359 | * `safetySettings` (array) - Controls content filtering for generated images
360 | * `negativePrompt` (string - features to avoid in the generated image)
361 | * `stylePreset` (string enum: "photographic", "digital-art", "cinematic", "anime", "3d-render", "oil-painting", "watercolor", "pixel-art", "sketch", "comic-book", "neon", "fantasy")
362 | * `seed` (number - integer value for reproducible generation)
363 | * `styleStrength` (number - strength of style preset, 0.0-1.0)
364 | * *Response:* Returns an array of base64-encoded images with metadata including dimensions and MIME type.
365 | * *Notes:* Image generation uses significant resources, especially at higher resolutions. Consider using smaller resolutions for faster responses and less resource usage.
366 |
367 |
368 | ### Audio Transcription (Removed)
369 |
370 | **Note:** Audio transcription via direct file uploads is no longer supported by this server. The server focuses on URL-based multimedia analysis for images and videos.
371 |
372 | **Alternative Approaches for Audio Content:**
373 | - **YouTube Videos:** Use the YouTube video analysis capabilities to analyze video content that includes audio
374 | - **External Services:** Use dedicated audio transcription services and analyze their output as text content
375 | - **URL-Based Audio:** If audio content is available via public URLs in supported formats, consider using external transcription services first, then analyze the resulting text
376 |
377 | ### URL Content Analysis
378 |
379 | * **`gemini_url_analysis`**
380 | * *Description:* Advanced URL analysis tool that fetches content from web pages and performs specialized analysis tasks with comprehensive security and performance optimizations.
381 | * *Required Params:*
382 | * `urls` (array) - URLs to analyze (1-20 URLs supported)
383 | * `analysisType` (string enum) - Type of analysis to perform:
384 | * `summary` - Comprehensive content summarization
385 | * `comparison` - Multi-URL content comparison
386 | * `extraction` - Structured information extraction
387 | * `qa` - Question-based content analysis
388 | * `sentiment` - Emotional tone analysis
389 | * `fact-check` - Credibility assessment
390 | * `content-classification` - Topic and type categorization
391 | * `readability` - Accessibility and complexity analysis
392 | * `seo-analysis` - Search optimization evaluation
393 | * *Optional Params:*
394 | * `query` (string) - Specific query or instruction for the analysis
395 | * `extractionSchema` (object) - JSON schema for structured data extraction
396 | * `questions` (array) - List of specific questions to answer (for Q&A analysis)
397 | * `compareBy` (array) - Specific aspects to compare when using comparison analysis
398 | * `outputFormat` (string enum: "text", "json", "markdown", "structured") - Desired output format
399 | * `includeMetadata` (boolean) - Include URL metadata in the analysis (default: true)
400 | * `fetchOptions` (object) - Advanced URL fetching options (same as urlContext fetchOptions)
401 | * `modelName` (string) - Specific Gemini model to use (auto-selected if not specified)
402 | * *Security Features:* Multi-layer URL validation, domain restrictions, private network protection, and rate limiting
403 | * *Performance Features:* Intelligent caching, concurrent processing, and optimal model selection based on content complexity
404 |
405 | ### MCP Client Tools
406 |
407 | * **`mcpConnectToServer`**
408 | * *Description:* Establishes a connection to an external MCP server and returns a connection ID.
409 | * *Required Params:*
410 | * `serverId` (string): A unique identifier for this server connection.
411 | * `connectionType` (string enum: "sse" | "stdio"): The transport protocol to use.
412 | * `sseUrl` (string, optional if `connectionType` is "stdio"): The URL for SSE connection.
413 | * `stdioCommand` (string, optional if `connectionType` is "sse"): The command to run for stdio connection.
414 | * `stdioArgs` (array of strings, optional): Arguments for the stdio command.
415 | * `stdioEnv` (object, optional): Environment variables for the stdio command.
416 | * *Important:* This tool returns a `connectionId` that must be used in subsequent calls to `mcpListServerTools`, `mcpCallServerTool`, and `mcpDisconnectFromServer`. This `connectionId` is generated internally and is different from the `serverId` parameter.
417 | * **`mcpListServerTools`**
418 | * *Description:* Lists available tools on a connected MCP server.
419 | * *Required Params:*
420 | * `connectionId` (string): The connection identifier returned by `mcpConnectToServer`.
421 | * **`mcpCallServerTool`**
422 | * *Description:* Calls a function on a connected MCP server.
423 | * *Required Params:*
424 | * `connectionId` (string): The connection identifier returned by `mcpConnectToServer`.
425 | * `toolName` (string): The name of the tool to call on the remote server.
426 | * `toolArgs` (object): The arguments to pass to the remote tool.
427 | * *Optional Params:*
428 | * `outputToFile` (string): If provided, the tool's output will be written to this file path. The path must be within one of the directories specified in the `ALLOWED_OUTPUT_PATHS` environment variable.
429 | * **`mcpDisconnectFromServer`**
430 | * *Description:* Disconnects from an external MCP server.
431 | * *Required Params:*
432 | * `connectionId` (string): The connection identifier returned by `mcpConnectToServer`.
433 | * **`writeToFile`**
434 | * *Description:* Writes content directly to a file.
435 | * *Required Params:*
436 | * `filePath` (string): The absolute path of the file to write to. Must be within one of the directories specified in the `ALLOWED_OUTPUT_PATHS` environment variable.
437 | * `content` (string): The content to write to the file.
438 | * *Optional Params:*
439 | * `overwrite` (boolean, default: false): If true, overwrite the file if it already exists. Otherwise, an error will be thrown if the file exists.
440 |
441 | ## Usage Examples
442 |
443 | Here are examples of how an MCP client (like Claude) might call these tools using the `use_mcp_tool` format:
444 |
445 |
446 | **Example 1: Simple Content Generation (Using Default Model)**
447 |
448 | ```xml
449 | <use_mcp_tool>
450 | <server_name>gemini-server</server_name>
451 | <tool_name>gemini_generateContent</tool_name>
452 | <arguments>
453 | {
454 | "prompt": "Write a short poem about a rubber duck."
455 | }
456 | </arguments>
457 | </use_mcp_tool>
458 | ```
459 |
460 | **Example 2: Content Generation (Specifying Model & Config)**
461 |
462 | ```xml
463 | <use_mcp_tool>
464 | <server_name>gemini-server</server_name>
465 | <tool_name>gemini_generateContent</tool_name>
466 | <arguments>
467 | {
468 | "modelName": "gemini-1.0-pro",
469 | "prompt": "Explain the concept of recursion in programming.",
470 | "generationConfig": {
471 | "temperature": 0.7,
472 | "maxOutputTokens": 500
473 | }
474 | }
475 | </arguments>
476 | </use_mcp_tool>
477 | ```
478 |
479 | **Example 2b: Content Generation with Thinking Budget Control**
480 |
481 | ```xml
482 | <use_mcp_tool>
483 | <server_name>gemini-server</server_name>
484 | <tool_name>gemini_generateContent</tool_name>
485 | <arguments>
486 | {
487 | "modelName": "gemini-1.5-pro",
488 | "prompt": "Solve this complex math problem: Find all values of x where 2sin(x) = x^2-x+1 in the range [0, 2π].",
489 | "generationConfig": {
490 | "temperature": 0.2,
491 | "maxOutputTokens": 1000,
492 | "thinkingConfig": {
493 | "thinkingBudget": 8192
494 | }
495 | }
496 | }
497 | </arguments>
498 | </use_mcp_tool>
499 | ```
500 |
501 | **Example 2c: Content Generation with Simplified Reasoning Effort**
502 |
503 | ```xml
504 | <use_mcp_tool>
505 | <server_name>gemini-server</server_name>
506 | <tool_name>gemini_generateContent</tool_name>
507 | <arguments>
508 | {
509 | "modelName": "gemini-1.5-pro",
510 | "prompt": "Solve this complex math problem: Find all values of x where 2sin(x) = x^2-x+1 in the range [0, 2π].",
511 | "generationConfig": {
512 | "temperature": 0.2,
513 | "maxOutputTokens": 1000,
514 | "thinkingConfig": {
515 | "reasoningEffort": "high"
516 | }
517 | }
518 | }
519 | </arguments>
520 | </use_mcp_tool>
521 | ```
522 |
523 | **Example 3: Starting and Continuing a Chat**
524 |
525 | *Start Chat:*
526 |
527 | ```xml
528 | <use_mcp_tool>
529 | <server_name>gemini-server</server_name>
530 | <tool_name>gemini_startChat</tool_name>
531 | <arguments>
532 | {}
533 | </arguments>
534 | </use_mcp_tool>
535 | ```
536 |
537 | *(Assume response contains `sessionId: "some-uuid-123"`)*
538 |
539 | *Send Message:*
540 |
541 | ```xml
542 | <use_mcp_tool>
543 | <server_name>gemini-server</server_name>
544 | <tool_name>gemini_sendMessage</tool_name>
545 | <arguments>
546 | {
547 | "sessionId": "some-uuid-123",
548 | "message": "Hello! Can you tell me about the Gemini API?"
549 | }
550 | </arguments>
551 | </use_mcp_tool>
552 | ```
553 |
554 | **Example 4: Content Generation with System Instructions (Simplified Format)**
555 |
556 | ```xml
557 | <use_mcp_tool>
558 | <server_name>gemini-server</server_name>
559 | <tool_name>gemini_generateContent</tool_name>
560 | <arguments>
561 | {
562 | "modelName": "gemini-2.5-pro-exp",
563 | "prompt": "What should I do with my day off?",
564 | "systemInstruction": "You are a helpful assistant that provides friendly and detailed advice. You should focus on outdoor activities and wellness."
565 | }
566 | </arguments>
567 | </use_mcp_tool>
568 | ```
569 |
570 | **Example 5: Content Generation with System Instructions (Object Format)**
571 |
572 | ```xml
573 | <use_mcp_tool>
574 | <server_name>gemini-server</server_name>
575 | <tool_name>gemini_generateContent</tool_name>
576 | <arguments>
577 | {
578 | "modelName": "gemini-1.5-pro-latest",
579 | "prompt": "What should I do with my day off?",
580 | "systemInstruction": {
581 | "parts": [
582 | {
583 | "text": "You are a helpful assistant that provides friendly and detailed advice. You should focus on outdoor activities and wellness."
584 | }
585 | ]
586 | }
587 | }
588 | </arguments>
589 | </use_mcp_tool>
590 | ```
591 |
592 | **Example 6: Using Cached Content with System Instruction**
593 |
594 | ```xml
595 | <use_mcp_tool>
596 | <server_name>gemini-server</server_name>
597 | <tool_name>gemini_generateContent</tool_name>
598 | <arguments>
599 | {
600 | "modelName": "gemini-2.5-pro-exp",
601 | "prompt": "Explain how these concepts relate to my product?",
602 | "cachedContentName": "cachedContents/abc123xyz",
603 | "systemInstruction": "You are a product expert who explains technical concepts in simple terms."
604 | }
605 | </arguments>
606 | </use_mcp_tool>
607 | ```
608 |
609 | **Example 6: Generating an Image**
610 |
611 | ```xml
612 | <use_mcp_tool>
613 | <server_name>gemini-server</server_name>
614 | <tool_name>gemini_generateImage</tool_name>
615 | <arguments>
616 | {
617 | "prompt": "A futuristic cityscape with flying cars and neon lights",
618 | "modelName": "gemini-2.0-flash-exp-image-generation",
619 | "resolution": "1024x1024",
620 | "numberOfImages": 1,
621 | "negativePrompt": "dystopian, ruins, dark, gloomy"
622 | }
623 | </arguments>
624 | </use_mcp_tool>
625 | ```
626 |
627 | **Example 6b: Generating a High-Quality Image with Imagen 3.1**
628 |
629 | ```xml
630 | <use_mcp_tool>
631 | <server_name>gemini-server</server_name>
632 | <tool_name>gemini_generateImage</tool_name>
633 | <arguments>
634 | {
635 | "prompt": "A futuristic cityscape with flying cars and neon lights",
636 | "modelName": "imagen-3.1-generate-003",
637 | "resolution": "1024x1024",
638 | "numberOfImages": 4,
639 | "negativePrompt": "dystopian, ruins, dark, gloomy"
640 | }
641 | </arguments>
642 | </use_mcp_tool>
643 | ```
644 |
645 | **Example 6c: Using Advanced Style Options**
646 |
647 | ```xml
648 | <use_mcp_tool>
649 | <server_name>gemini-server</server_name>
650 | <tool_name>gemini_generateImage</tool_name>
651 | <arguments>
652 | {
653 | "prompt": "A futuristic cityscape with flying cars and neon lights",
654 | "modelName": "imagen-3.1-generate-003",
655 | "resolution": "1024x1024",
656 | "numberOfImages": 2,
657 | "stylePreset": "anime",
658 | "styleStrength": 0.8,
659 | "seed": 12345
660 | }
661 | </arguments>
662 | </use_mcp_tool>
663 | ```
664 |
665 |
666 | **Example 7: Message Routing Between Models**
667 |
668 | ```xml
669 | <use_mcp_tool>
670 | <server_name>gemini-server</server_name>
671 | <tool_name>gemini_routeMessage</tool_name>
672 | <arguments>
673 | {
674 | "message": "Can you create a detailed business plan for a sustainable fashion startup?",
675 | "models": ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-2.5-pro"],
676 | "routingPrompt": "Analyze this message and determine which model would be best suited to handle it. Consider: gemini-1.5-flash for simpler tasks, gemini-1.5-pro for balanced capabilities, and gemini-2.5-pro for complex creative tasks.",
677 | "defaultModel": "gemini-1.5-pro",
678 | "generationConfig": {
679 | "temperature": 0.7,
680 | "maxOutputTokens": 1024
681 | }
682 | }
683 | </arguments>
684 | </use_mcp_tool>
685 | ```
686 |
687 | The response will be a JSON string containing both the text response and which model was chosen:
688 |
689 | ```json
690 | {
691 | "text": "# Business Plan for Sustainable Fashion Startup\n\n## Executive Summary\n...",
692 | "chosenModel": "gemini-2.5-pro"
693 | }
694 | ```
695 |
696 | **Example 8: Using URL Context with Content Generation**
697 |
698 | ```xml
699 | <use_mcp_tool>
700 | <server_name>gemini-server</server_name>
701 | <tool_name>gemini_generateContent</tool_name>
702 | <arguments>
703 | {
704 | "prompt": "Summarize the main points from these articles and compare their approaches to sustainable technology",
705 | "urlContext": {
706 | "urls": [
707 | "https://example.com/sustainable-tech-2024",
708 | "https://techblog.com/green-innovation"
709 | ],
710 | "fetchOptions": {
711 | "maxContentKb": 150,
712 | "includeMetadata": true,
713 | "convertToMarkdown": true
714 | }
715 | },
716 | "modelPreferences": {
717 | "preferQuality": true,
718 | "taskType": "reasoning"
719 | }
720 | }
721 | </arguments>
722 | </use_mcp_tool>
723 | ```
724 |
725 | **Example 9: Advanced URL Analysis**
726 |
727 | ```xml
728 | <use_mcp_tool>
729 | <server_name>gemini-server</server_name>
730 | <tool_name>gemini_url_analysis</tool_name>
731 | <arguments>
732 | {
733 | "urls": ["https://company.com/about", "https://company.com/products"],
734 | "analysisType": "extraction",
735 | "extractionSchema": {
736 | "companyName": "string",
737 | "foundedYear": "number",
738 | "numberOfEmployees": "string",
739 | "mainProducts": "array",
740 | "headquarters": "string",
741 | "financialInfo": "object"
742 | },
743 | "outputFormat": "json",
744 | "query": "Extract comprehensive company information including business details and product offerings"
745 | }
746 | </arguments>
747 | </use_mcp_tool>
748 | ```
749 |
750 | **Example 10: Multi-URL Content Comparison**
751 |
752 | ```xml
753 | <use_mcp_tool>
754 | <server_name>gemini-server</server_name>
755 | <tool_name>gemini_url_analysis</tool_name>
756 | <arguments>
757 | {
758 | "urls": [
759 | "https://site1.com/pricing",
760 | "https://site2.com/pricing",
761 | "https://site3.com/pricing"
762 | ],
763 | "analysisType": "comparison",
764 | "compareBy": ["pricing models", "features", "target audience", "value proposition"],
765 | "outputFormat": "markdown",
766 | "includeMetadata": true
767 | }
768 | </arguments>
769 | </use_mcp_tool>
770 | ```
771 |
772 | **Example 11: URL Content with Security Restrictions**
773 |
774 | ```xml
775 | <use_mcp_tool>
776 | <server_name>gemini-server</server_name>
777 | <tool_name>gemini_generateContent</tool_name>
778 | <arguments>
779 | {
780 | "prompt": "Analyze the content from these trusted news sources",
781 | "urlContext": {
782 | "urls": [
783 | "https://reuters.com/article/tech-news",
784 | "https://bbc.com/news/technology"
785 | ],
786 | "fetchOptions": {
787 | "allowedDomains": ["reuters.com", "bbc.com"],
788 | "maxContentKb": 200,
789 | "timeoutMs": 15000,
790 | "userAgent": "Research-Bot/1.0"
791 | }
792 | }
793 | }
794 | </arguments>
795 | </use_mcp_tool>
796 | ```
797 |
798 | ### URL-Based Image Analysis Examples
799 |
800 | These examples demonstrate how to analyze images from public URLs using Gemini's native image understanding capabilities. The server processes images by fetching them from URLs and converting them to the format required by the Gemini API. Note that this server does not support direct file uploads - all image analysis must be performed using publicly accessible image URLs.
801 |
802 | **Example 17: Basic Image Description and Analysis**
803 |
804 | ```xml
805 | <use_mcp_tool>
806 | <server_name>gemini-server</server_name>
807 | <tool_name>gemini_generateContent</tool_name>
808 | <arguments>
809 | {
810 | "prompt": "Please describe this image in detail, including objects, people, colors, setting, and any text you can see.",
811 | "urlContext": {
812 | "urls": ["https://example.com/images/photo.jpg"],
813 | "fetchOptions": {
814 | "includeMetadata": true,
815 | "timeoutMs": 15000
816 | }
817 | }
818 | }
819 | </arguments>
820 | </use_mcp_tool>
821 | ```
822 |
823 | **Example 18: Object Detection and Identification**
824 |
825 | ```xml
826 | <use_mcp_tool>
827 | <server_name>gemini-server</server_name>
828 | <tool_name>gemini_generateContent</tool_name>
829 | <arguments>
830 | {
831 | "prompt": "Identify and list all objects visible in this image. For each object, describe its location, size relative to other objects, and any notable characteristics.",
832 | "urlContext": {
833 | "urls": ["https://example.com/images/scene.png"],
834 | "fetchOptions": {
835 | "includeMetadata": false,
836 | "timeoutMs": 20000
837 | }
838 | }
839 | }
840 | </arguments>
841 | </use_mcp_tool>
842 | ```
843 |
844 | **Example 19: Chart and Data Visualization Analysis**
845 |
846 | ```xml
847 | <use_mcp_tool>
848 | <server_name>gemini-server</server_name>
849 | <tool_name>gemini_generateContent</tool_name>
850 | <arguments>
851 | {
852 | "prompt": "Analyze this chart or graph. What type of visualization is it? What are the main data points, trends, and insights? Extract any numerical values, labels, and time periods shown.",
853 | "urlContext": {
854 | "urls": ["https://example.com/charts/sales-data.png"]
855 | },
856 | "modelPreferences": {
857 | "preferQuality": true,
858 | "taskType": "reasoning"
859 | }
860 | }
861 | </arguments>
862 | </use_mcp_tool>
863 | ```
864 |
865 | **Example 20: Comparative Image Analysis**
866 |
867 | ```xml
868 | <use_mcp_tool>
869 | <server_name>gemini-server</server_name>
870 | <tool_name>gemini_generateContent</tool_name>
871 | <arguments>
872 | {
873 | "prompt": "Compare these two images side by side. Describe the differences and similarities in terms of objects, composition, colors, style, and any other notable aspects.",
874 | "urlContext": {
875 | "urls": [
876 | "https://example.com/before-renovation.jpg",
877 | "https://example.com/after-renovation.jpg"
878 | ],
879 | "fetchOptions": {
880 | "maxContentKb": 200,
881 | "includeMetadata": true
882 | }
883 | }
884 | }
885 | </arguments>
886 | </use_mcp_tool>
887 | ```
888 |
889 | **Example 21: Text Extraction from Images (OCR)**
890 |
891 | ```xml
892 | <use_mcp_tool>
893 | <server_name>gemini-server</server_name>
894 | <tool_name>gemini_generateContent</tool_name>
895 | <arguments>
896 | {
897 | "prompt": "Extract all text visible in this image. Include any signs, labels, captions, or written content. Maintain the original formatting and structure as much as possible.",
898 | "urlContext": {
899 | "urls": ["https://example.com/documents/screenshot.png"]
900 | }
901 | }
902 | </arguments>
903 | </use_mcp_tool>
904 | ```
905 |
906 | **Example 22: Technical Diagram or Flowchart Analysis**
907 |
908 | ```xml
909 | <use_mcp_tool>
910 | <server_name>gemini-server</server_name>
911 | <tool_name>gemini_generateContent</tool_name>
912 | <arguments>
913 | {
914 | "prompt": "Analyze this technical diagram, flowchart, or schematic. Explain the system architecture, identify components, describe the relationships and data flow, and interpret any symbols or notations used.",
915 | "urlContext": {
916 | "urls": ["https://docs.example.com/architecture-diagram.png"],
917 | "fetchOptions": {
918 | "maxContentKb": 100,
919 | "includeMetadata": true
920 | }
921 | },
922 | "modelPreferences": {
923 | "preferQuality": true,
924 | "taskType": "reasoning"
925 | }
926 | }
927 | </arguments>
928 | </use_mcp_tool>
929 | ```
930 |
931 | **Example 23: Image Analysis with Specific Questions**
932 |
933 | ```xml
934 | <use_mcp_tool>
935 | <server_name>gemini-server</server_name>
936 | <tool_name>gemini_generateContent</tool_name>
937 | <arguments>
938 | {
939 | "prompt": "Looking at this image, please answer these specific questions: 1) What is the main subject? 2) What colors dominate the scene? 3) Are there any people visible? 4) What appears to be the setting or location? 5) What mood or atmosphere does the image convey?",
940 | "urlContext": {
941 | "urls": ["https://example.com/images/landscape.jpg"]
942 | }
943 | }
944 | </arguments>
945 | </use_mcp_tool>
946 | ```
947 |
948 | **Example 24: Image Analysis with Security Restrictions**
949 |
950 | ```xml
951 | <use_mcp_tool>
952 | <server_name>gemini-server</server_name>
953 | <tool_name>gemini_generateContent</tool_name>
954 | <arguments>
955 | {
956 | "prompt": "Analyze the composition and design elements in this image, focusing on visual hierarchy, layout principles, and aesthetic choices.",
957 | "urlContext": {
958 | "urls": ["https://trusted-cdn.example.com/design-mockup.jpg"],
959 | "fetchOptions": {
960 | "allowedDomains": ["trusted-cdn.example.com", "assets.example.com"],
961 | "maxContentKb": 150,
962 | "timeoutMs": 25000,
963 | "includeMetadata": false
964 | }
965 | }
966 | }
967 | </arguments>
968 | </use_mcp_tool>
969 | ```
970 |
971 | **Important Notes for URL-Based Image Analysis:**
972 | - **Supported formats**: PNG, JPEG, WebP, HEIC, HEIF (as per Gemini API specifications)
973 | - **Image access**: Images must be accessible via public URLs without authentication
974 | - **Size considerations**: Large images are automatically processed in sections by Gemini
975 | - **Processing**: The server fetches images from URLs and converts them to the format required by Gemini API
976 | - **Security**: The server applies restrictions to prevent access to private networks or malicious domains
977 | - **Performance**: Image analysis may take longer for high-resolution images due to processing complexity
978 | - **Token usage**: Image dimensions affect token consumption - larger images use more tokens
979 |
980 | ### YouTube Video Analysis Examples
981 |
982 | These examples demonstrate how to analyze YouTube videos using Gemini's video understanding capabilities. The server can process publicly accessible YouTube videos by providing their URLs. Note that only public YouTube videos are supported - private, unlisted, or region-restricted videos cannot be analyzed.
983 |
984 | **Example 25: Basic YouTube Video Analysis**
985 |
986 | ```xml
987 | <use_mcp_tool>
988 | <server_name>gemini-server</server_name>
989 | <tool_name>gemini_generateContent</tool_name>
990 | <arguments>
991 | {
992 | "prompt": "Please analyze this YouTube video and provide a comprehensive summary including the main topics discussed, key points, and overall theme.",
993 | "urlContext": {
994 | "urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
995 | "fetchOptions": {
996 | "includeMetadata": true,
997 | "timeoutMs": 30000
998 | }
999 | }
1000 | }
1001 | </arguments>
1002 | </use_mcp_tool>
1003 | ```
1004 |
1005 | **Example 26: YouTube Video Content Extraction with Timestamps**
1006 |
1007 | ```xml
1008 | <use_mcp_tool>
1009 | <server_name>gemini-server</server_name>
1010 | <tool_name>gemini_generateContent</tool_name>
1011 | <arguments>
1012 | {
1013 | "prompt": "Analyze this educational YouTube video and create a detailed outline with key topics and approximate timestamps. Identify the main learning objectives and key concepts covered.",
1014 | "urlContext": {
1015 | "urls": ["https://www.youtube.com/watch?v=EXAMPLE_VIDEO_ID"],
1016 | "fetchOptions": {
1017 | "maxContentKb": 300,
1018 | "includeMetadata": true,
1019 | "timeoutMs": 45000
1020 | }
1021 | },
1022 | "modelPreferences": {
1023 | "preferQuality": true,
1024 | "taskType": "reasoning"
1025 | }
1026 | }
1027 | </arguments>
1028 | </use_mcp_tool>
1029 | ```
1030 |
1031 | **Example 27: YouTube Video Analysis with Specific Questions**
1032 |
1033 | ```xml
1034 | <use_mcp_tool>
1035 | <server_name>gemini-server</server_name>
1036 | <tool_name>gemini_generateContent</tool_name>
1037 | <arguments>
1038 | {
1039 | "prompt": "Watch this YouTube video and answer these specific questions: 1) What is the main message or thesis? 2) Who is the target audience? 3) What evidence or examples are provided? 4) What are the key takeaways? 5) How is the content structured?",
1040 | "urlContext": {
1041 | "urls": ["https://www.youtube.com/watch?v=EXAMPLE_VIDEO_ID"]
1042 | }
1043 | }
1044 | </arguments>
1045 | </use_mcp_tool>
1046 | ```
1047 |
1048 | **Example 28: Comparative Analysis of Multiple YouTube Videos**
1049 |
1050 | ```xml
1051 | <use_mcp_tool>
1052 | <server_name>gemini-server</server_name>
1053 | <tool_name>gemini_generateContent</tool_name>
1054 | <arguments>
1055 | {
1056 | "prompt": "Compare and contrast these YouTube videos. Analyze their different approaches to the topic, presentation styles, key arguments, and conclusions. Identify similarities and differences in their perspectives.",
1057 | "urlContext": {
1058 | "urls": [
1059 | "https://www.youtube.com/watch?v=VIDEO_ID_1",
1060 | "https://www.youtube.com/watch?v=VIDEO_ID_2"
1061 | ],
1062 | "fetchOptions": {
1063 | "maxContentKb": 400,
1064 | "includeMetadata": true,
1065 | "timeoutMs": 60000
1066 | }
1067 | },
1068 | "modelPreferences": {
1069 | "preferQuality": true,
1070 | "taskType": "reasoning"
1071 | }
1072 | }
1073 | </arguments>
1074 | </use_mcp_tool>
1075 | ```
1076 |
1077 | **Example 29: YouTube Video Technical Analysis**
1078 |
1079 | ```xml
1080 | <use_mcp_tool>
1081 | <server_name>gemini-server</server_name>
1082 | <tool_name>gemini_generateContent</tool_name>
1083 | <arguments>
1084 | {
1085 | "prompt": "Analyze this technical YouTube tutorial and extract step-by-step instructions, identify required tools or materials, note any code examples or commands shown, and highlight important warnings or best practices mentioned.",
1086 | "urlContext": {
1087 | "urls": ["https://www.youtube.com/watch?v=TECH_TUTORIAL_ID"],
1088 | "fetchOptions": {
1089 | "includeMetadata": true,
1090 | "timeoutMs": 40000
1091 | }
1092 | }
1093 | }
1094 | </arguments>
1095 | </use_mcp_tool>
1096 | ```
1097 |
1098 | **Example 30: YouTube Video Sentiment and Style Analysis**
1099 |
1100 | ```xml
1101 | <use_mcp_tool>
1102 | <server_name>gemini-server</server_name>
1103 | <tool_name>gemini_url_analysis</tool_name>
1104 | <arguments>
1105 | {
1106 | "urls": ["https://www.youtube.com/watch?v=EXAMPLE_VIDEO_ID"],
1107 | "analysisType": "sentiment",
1108 | "outputFormat": "structured",
1109 | "query": "Analyze the tone, mood, and presentation style of this YouTube video. Assess the speaker's credibility, engagement level, and overall effectiveness of communication."
1110 | }
1111 | </arguments>
1112 | </use_mcp_tool>
1113 | ```
1114 |
1115 | **Example 31: YouTube Video Educational Content Assessment**
1116 |
1117 | ```xml
1118 | <use_mcp_tool>
1119 | <server_name>gemini-server</server_name>
1120 | <tool_name>gemini_generateContent</tool_name>
1121 | <arguments>
1122 | {
1123 | "prompt": "Evaluate this educational YouTube video for accuracy, clarity, and pedagogical effectiveness. Identify the teaching methods used, assess how well complex concepts are explained, and suggest improvements if any.",
1124 | "urlContext": {
1125 | "urls": ["https://www.youtube.com/watch?v=EDUCATIONAL_VIDEO_ID"],
1126 | "fetchOptions": {
1127 | "maxContentKb": 250,
1128 | "includeMetadata": true
1129 | }
1130 | },
1131 | "modelPreferences": {
1132 | "preferQuality": true,
1133 | "taskType": "reasoning"
1134 | }
1135 | }
1136 | </arguments>
1137 | </use_mcp_tool>
1138 | ```
1139 |
1140 | **Example 32: YouTube Video with Domain Security Restrictions**
1141 |
1142 | ```xml
1143 | <use_mcp_tool>
1144 | <server_name>gemini-server</server_name>
1145 | <tool_name>gemini_generateContent</tool_name>
1146 | <arguments>
1147 | {
1148 | "prompt": "Analyze the content and key messages of this YouTube video, focusing on factual accuracy and source credibility.",
1149 | "urlContext": {
1150 | "urls": ["https://www.youtube.com/watch?v=TRUSTED_VIDEO_ID"],
1151 | "fetchOptions": {
1152 | "allowedDomains": ["youtube.com", "www.youtube.com"],
1153 | "maxContentKb": 200,
1154 | "timeoutMs": 35000,
1155 | "includeMetadata": true
1156 | }
1157 | }
1158 | }
1159 | </arguments>
1160 | </use_mcp_tool>
1161 | ```
1162 |
1163 | **Important Notes for YouTube Video Analysis:**
1164 | - **Public videos only**: Only publicly accessible YouTube videos can be analyzed
1165 | - **URL format**: Use standard YouTube URLs (youtube.com/watch?v=VIDEO_ID or youtu.be/VIDEO_ID)
1166 | - **Processing time**: Video analysis typically takes longer than text or image analysis
1167 | - **Content limitations**: Very long videos may have content truncated or processed in segments
1168 | - **Metadata**: Video metadata (title, description, duration) is included when `includeMetadata: true`
1169 | - **Language support**: Gemini can analyze videos in multiple languages
1170 | - **Content restrictions**: The server applies the same security restrictions as other URL content
1171 | - **Token usage**: Video analysis can consume significant tokens depending on video length and complexity
1172 |
1173 | ## Supported Multimedia Analysis Use Cases
1174 |
1175 | The MCP Gemini Server supports comprehensive multimedia analysis through URL-based processing, leveraging Google Gemini's advanced vision and video understanding capabilities. Below are the key use cases organized by content type:
1176 |
1177 | ### Image Analysis Use Cases
1178 |
1179 | **Content Understanding:**
1180 | - **Product Analysis**: Analyze product images for features, design elements, and quality assessment
1181 | - **Document OCR**: Extract and transcribe text from images of documents, receipts, and forms
1182 | - **Chart & Graph Analysis**: Interpret data visualizations, extract key insights, and explain trends
1183 | - **Technical Diagrams**: Understand architectural diagrams, flowcharts, and technical schematics
1184 | - **Medical Images**: Analyze medical charts, X-rays, and diagnostic images (for educational purposes)
1185 | - **Art & Design**: Analyze artistic compositions, color schemes, and design principles
1186 |
1187 | **Comparative Analysis:**
1188 | - **Before/After Comparisons**: Compare multiple images to identify changes and differences
1189 | - **Product Comparisons**: Analyze multiple product images for feature comparison
1190 | - **A/B Testing**: Evaluate design variations and visual differences
1191 |
1192 | **Security & Quality:**
1193 | - **Content Moderation**: Identify inappropriate or harmful visual content
1194 | - **Quality Assessment**: Evaluate image quality, resolution, and technical aspects
1195 | - **Brand Compliance**: Check images for brand guideline adherence
1196 |
1197 | ### Video Analysis Use Cases
1198 |
1199 | **Educational Content:**
1200 | - **Lecture Analysis**: Extract key concepts, create summaries, and identify important timestamps
1201 | - **Tutorial Understanding**: Break down step-by-step instructions and highlight key procedures
1202 | - **Training Materials**: Analyze corporate training videos and extract learning objectives
1203 | - **Academic Research**: Process research presentations and extract methodologies
1204 |
1205 | **Content Creation:**
1206 | - **Video Summarization**: Generate concise summaries of long-form video content
1207 | - **Transcript Generation**: Create detailed transcripts with speaker identification
1208 | - **Content Categorization**: Classify videos by topic, genre, or content type
1209 | - **Sentiment Analysis**: Assess emotional tone and audience engagement indicators
1210 |
1211 | **Technical Analysis:**
1212 | - **Software Demonstrations**: Extract software features and usage instructions
1213 | - **Product Reviews**: Analyze product demonstration videos and extract key insights
1214 | - **Troubleshooting Guides**: Parse technical support videos for problem-solving steps
1215 | - **Code Reviews**: Analyze programming tutorial videos and extract code examples
1216 |
1217 | **Business Intelligence:**
1218 | - **Market Research**: Analyze promotional videos and marketing content
1219 | - **Competitive Analysis**: Study competitor video content and strategies
1220 | - **Customer Feedback**: Process video testimonials and feedback sessions
1221 | - **Event Coverage**: Analyze conference presentations and keynote speeches
1222 |
1223 | ### Integration Capabilities
1224 |
1225 | **Multi-Modal Analysis:**
1226 | - Combine text prompts with image/video URLs for contextual analysis
1227 | - Process multiple media types in single requests for comprehensive insights
1228 | - Cross-reference visual content with textual instructions
1229 |
1230 | **Workflow Integration:**
1231 | - Chain multiple analysis operations for complex workflows
1232 | - Export results to files for further processing
1233 | - Integrate with external MCP servers for extended functionality
1234 |
1235 | **Security & Performance:**
1236 | - URL validation and security screening for safe content processing
1237 | - Caching support for frequently analyzed content
1238 | - Batch processing capabilities for multiple media items
1239 |
1240 | **Example 12: Connecting to an External MCP Server (SSE)**
1241 |
1242 | ```xml
1243 | <use_mcp_tool>
1244 | <server_name>gemini-server</server_name>
1245 | <tool_name>mcpConnectToServer</tool_name>
1246 | <arguments>
1247 | {
1248 | "serverId": "my-external-server",
1249 | "connectionType": "sse",
1250 | "sseUrl": "http://localhost:8080/mcp"
1251 | }
1252 | </arguments>
1253 | </use_mcp_tool>
1254 | ```
1255 |
1256 | *(Assume response contains a unique connection ID like: `connectionId: "12345-abcde-67890"`)*
1257 |
1258 | **Example 13: Calling a Tool on an External MCP Server and Writing Output to File**
1259 |
1260 | ```xml
1261 | <use_mcp_tool>
1262 | <server_name>gemini-server</server_name>
1263 | <tool_name>mcpCallServerTool</tool_name>
1264 | <arguments>
1265 | {
1266 | "connectionId": "12345-abcde-67890", // Use the connectionId returned by mcpConnectToServer
1267 | "toolName": "remote_tool_name",
1268 | "toolArgs": { "param1": "value1" },
1269 | "outputToFile": "/var/opt/mcp-gemini-server/outputs/result.json"
1270 | }
1271 | </arguments>
1272 | </use_mcp_tool>
1273 | ```
1274 |
1275 | **Important: The `connectionId` used in MCP client tools must be the connection identifier returned by `mcpConnectToServer`, not the original `serverId` parameter.**
1276 |
1277 | **Note:** The `outputToFile` path must be within one of the directories specified in the `ALLOWED_OUTPUT_PATHS` environment variable. For example, if `ALLOWED_OUTPUT_PATHS="/path/to/allowed/output,/another/allowed/path"`, then the file path must be a subdirectory of one of these paths.
1278 |
1279 | **Example 14: Writing Content Directly to a File**
1280 |
1281 | ```xml
1282 | <use_mcp_tool>
1283 | <server_name>gemini-server</server_name>
1284 | <tool_name>writeToFile</tool_name>
1285 | <arguments>
1286 | {
1287 | "filePath": "/path/to/allowed/output/my_notes.txt",
1288 | "content": "This is some important content.",
1289 | "overwrite": true
1290 | }
1291 | </arguments>
1292 | </use_mcp_tool>
1293 | ```
1294 |
1295 | **Note:** Like with `mcpCallServerTool`, the `filePath` must be within one of the directories specified in the `ALLOWED_OUTPUT_PATHS` environment variable. This is a critical security feature to prevent unauthorized file writes.
1296 |
1297 | ## `mcp-gemini-server` and Gemini SDK's MCP Function Calling
1298 |
1299 | The official Google Gemini API documentation includes examples (such as for [function calling with MCP structure](https://ai.google.dev/gemini-api/docs/function-calling?example=weather#model_context_protocol_mcp)) that demonstrate how you can use the client-side Gemini SDK (e.g., in Python or Node.js) to interact with the Gemini API. In such scenarios, particularly for function calling, the client SDK itself can be used to structure requests and handle responses in a manner that aligns with MCP principles.
1300 |
1301 | The `mcp-gemini-server` project offers a complementary approach by providing a **fully implemented, standalone MCP server**. Instead of your client application directly using the Gemini SDK to format MCP-style messages for the Gemini API, your client application (which could be another LLM like Claude, a custom script, or any MCP-compatible system) would:
1302 |
1303 | 1. Connect to an instance of this `mcp-gemini-server`.
1304 | 2. Call the pre-defined MCP tools exposed by this server, such as `gemini_functionCall`, `gemini_generateContent`, etc.
1305 |
1306 | This `mcp-gemini-server` then internally handles all the necessary interactions with the Google Gemini API, including structuring the requests, managing API keys, and processing responses, abstracting these details away from your MCP client.
1307 |
1308 | ### Benefits of using `mcp-gemini-server`:
1309 |
1310 | * **Abstraction & Simplicity:** Client applications don't need to integrate the Gemini SDK directly or manage the specifics of its API for MCP-style interactions. They simply make standard MCP tool calls.
1311 | * **Centralized Configuration:** API keys, default model choices, safety settings, and other configurations are managed centrally within the `mcp-gemini-server`.
1312 | * **Rich Toolset:** Provides a broad set of pre-defined MCP tools for various Gemini features (text generation, chat, file handling, image generation, etc.), not just function calling.
1313 | * **Interoperability:** Enables any MCP-compatible client to leverage Gemini's capabilities without needing native Gemini SDK support.
1314 |
1315 | ### When to Choose Which Approach:
1316 |
1317 | * **Direct SDK Usage (as in Google's MCP examples):**
1318 | * Suitable if you are building a client application (e.g., in Python or Node.js) and want fine-grained control over the Gemini API interaction directly within that client.
1319 | * Useful if you prefer to manage the Gemini SDK dependencies and logic within your client application and are primarily focused on function calling structured in an MCP-like way.
1320 | * **Using `mcp-gemini-server`:**
1321 | * Ideal if you want to expose Gemini capabilities to an existing MCP-compatible ecosystem (e.g., another LLM, a workflow automation system).
1322 | * Beneficial if you want to rapidly prototype or deploy Gemini features as tools without extensive client-side SDK integration.
1323 | * Preferable if you need a wider range of Gemini features exposed as consistent MCP tools and want to centralize the Gemini API interaction point.
1324 |
1325 | ### A Note on This Server's Own MCP Client Tools:
1326 |
1327 | The `mcp-gemini-server` also includes tools like `mcpConnectToServer`, `mcpListServerTools`, and `mcpCallServerTool`. These tools allow *this server* to act as an MCP *client* to *other external* MCP servers. This is a distinct capability from how an MCP client would connect *to* `mcp-gemini-server` to utilize Gemini features.
1328 |
1329 | ## Environment Variables
1330 |
1331 | ### Required:
1332 | - `GOOGLE_GEMINI_API_KEY`: Your Google Gemini API key (required)
1333 |
1334 | ### Required for Production (unless NODE_ENV=test):
1335 | - `MCP_SERVER_HOST`: Server host address (e.g., "localhost")
1336 | - `MCP_SERVER_PORT`: Port for network transports (e.g., "8080")
1337 | - `MCP_CONNECTION_TOKEN`: A strong, unique shared secret token that clients must provide when connecting to this server. This is NOT provided by Google or any external service - you must generate it yourself using a cryptographically secure method. See the installation instructions (step 4) for generation methods. This token must be identical on both the server and all connecting clients.
1338 |
1339 | ### Optional - Gemini API Configuration:
1340 | - `GOOGLE_GEMINI_MODEL`: Default model to use (e.g., `gemini-1.5-pro-latest`, `gemini-1.5-flash`)
1341 | - `GOOGLE_GEMINI_DEFAULT_THINKING_BUDGET`: Default thinking budget in tokens (0-24576) for controlling model reasoning
1342 |
1343 | ### Optional - URL Context Configuration:
1344 | - `GOOGLE_GEMINI_ENABLE_URL_CONTEXT`: Enable URL context features (options: `true`, `false`; default: `false`)
1345 | - `GOOGLE_GEMINI_URL_MAX_COUNT`: Maximum URLs per request (default: `20`)
1346 | - `GOOGLE_GEMINI_URL_MAX_CONTENT_KB`: Maximum content size per URL in KB (default: `100`)
1347 | - `GOOGLE_GEMINI_URL_FETCH_TIMEOUT_MS`: Fetch timeout per URL in milliseconds (default: `10000`)
1348 | - `GOOGLE_GEMINI_URL_ALLOWED_DOMAINS`: Comma-separated list or JSON array of allowed domains (default: `*` for all domains)
1349 | - `GOOGLE_GEMINI_URL_BLOCKLIST`: Comma-separated list or JSON array of blocked domains (default: empty)
1350 | - `GOOGLE_GEMINI_URL_CONVERT_TO_MARKDOWN`: Convert HTML content to markdown (options: `true`, `false`; default: `true`)
1351 | - `GOOGLE_GEMINI_URL_INCLUDE_METADATA`: Include URL metadata in context (options: `true`, `false`; default: `true`)
1352 | - `GOOGLE_GEMINI_URL_ENABLE_CACHING`: Enable URL content caching (options: `true`, `false`; default: `true`)
1353 | - `GOOGLE_GEMINI_URL_USER_AGENT`: Custom User-Agent header for URL requests (default: `MCP-Gemini-Server/1.0`)
1354 |
1355 | ### Optional - Security Configuration:
1356 | - `ALLOWED_OUTPUT_PATHS`: A comma-separated list of absolute paths to directories where tools like `mcpCallServerTool` (with outputToFile parameter) and `writeToFileTool` are allowed to write files. Critical security feature to prevent unauthorized file writes. If not set, file output will be disabled for these tools.
1357 |
1358 | ### Optional - Server Configuration:
1359 | - `MCP_CLIENT_ID`: Default client ID used when this server acts as a client to other MCP servers (defaults to "gemini-sdk-client")
1360 | - `MCP_TRANSPORT`: Transport to use for MCP server (options: `stdio`, `sse`, `streamable`, `http`; default: `stdio`)
1361 | - IMPORTANT: SSE (Server-Sent Events) is NOT deprecated and remains a critical component of the MCP protocol
1362 | - SSE is particularly valuable for bidirectional communication, enabling features like dynamic tool updates and sampling
1363 | - Each transport type has specific valid use cases within the MCP ecosystem
1364 | - `MCP_LOG_LEVEL`: Log level for MCP operations (options: `debug`, `info`, `warn`, `error`; default: `info`)
1365 | - `MCP_ENABLE_STREAMING`: Enable SSE streaming for HTTP transport (options: `true`, `false`; default: `false`)
1366 | - `MCP_SESSION_TIMEOUT`: Session timeout in seconds for HTTP transport (default: `3600` = 1 hour)
1367 | - `SESSION_STORE_TYPE`: Session storage backend (`memory` or `sqlite`; default: `memory`)
1368 | - `SQLITE_DB_PATH`: Path to SQLite database file when using sqlite store (default: `./data/sessions.db`)
1369 |
1370 | ### Optional - GitHub Integration:
1371 | - `GITHUB_API_TOKEN`: Personal Access Token for GitHub API access (required for GitHub code review features). For public repos, token needs 'public_repo' and 'read:user' scopes. For private repos, token needs 'repo' scope.
1372 |
1373 | ### Optional - Legacy Server Configuration (Deprecated):
1374 | - `MCP_TRANSPORT_TYPE`: Deprecated - Use `MCP_TRANSPORT` instead
1375 | - `MCP_WS_PORT`: Deprecated - Use `MCP_SERVER_PORT` instead
1376 | - `ENABLE_HEALTH_CHECK`: Enable health check server (options: `true`, `false`; default: `true`)
1377 | - `HEALTH_CHECK_PORT`: Port for health check HTTP server (default: `3000`)
1378 |
1379 | You can create a `.env` file in the root directory with these variables:
1380 |
1381 | ```env
1382 | # Required API Configuration
1383 | GOOGLE_GEMINI_API_KEY=your_api_key_here
1384 |
1385 | # Required for Production (unless NODE_ENV=test)
1386 | MCP_SERVER_HOST=localhost
1387 | MCP_SERVER_PORT=8080
1388 | MCP_CONNECTION_TOKEN=your_secure_token_here
1389 |
1390 | # Optional API Configuration
1391 | GOOGLE_GEMINI_MODEL=gemini-1.5-pro-latest
1392 | GOOGLE_GEMINI_DEFAULT_THINKING_BUDGET=4096
1393 |
1394 | # Security Configuration
1395 | ALLOWED_OUTPUT_PATHS=/var/opt/mcp-gemini-server/outputs,/tmp/mcp-gemini-outputs # For mcpCallServerTool and writeToFileTool
1396 |
1397 | # URL Context Configuration
1398 | GOOGLE_GEMINI_ENABLE_URL_CONTEXT=true # Enable URL context features
1399 | GOOGLE_GEMINI_URL_MAX_COUNT=20 # Maximum URLs per request
1400 | GOOGLE_GEMINI_URL_MAX_CONTENT_KB=100 # Maximum content size per URL in KB
1401 | GOOGLE_GEMINI_URL_FETCH_TIMEOUT_MS=10000 # Fetch timeout per URL in milliseconds
1402 | GOOGLE_GEMINI_URL_ALLOWED_DOMAINS=* # Allowed domains (* for all, or comma-separated list)
1403 | GOOGLE_GEMINI_URL_BLOCKLIST=malicious.com,spam.net # Blocked domains (comma-separated)
1404 | GOOGLE_GEMINI_URL_CONVERT_TO_MARKDOWN=true # Convert HTML to markdown
1405 | GOOGLE_GEMINI_URL_INCLUDE_METADATA=true # Include URL metadata in context
1406 | GOOGLE_GEMINI_URL_ENABLE_CACHING=true # Enable URL content caching
1407 | GOOGLE_GEMINI_URL_USER_AGENT=MCP-Gemini-Server/1.0 # Custom User-Agent
1408 |
1409 | # Server Configuration
1410 | MCP_CLIENT_ID=gemini-sdk-client # Optional: Default client ID for MCP connections (defaults to "gemini-sdk-client")
1411 | MCP_TRANSPORT=stdio # Options: stdio, sse, streamable, http (replaced deprecated MCP_TRANSPORT_TYPE)
1412 | MCP_LOG_LEVEL=info # Optional: Log level for MCP operations (debug, info, warn, error)
1413 | MCP_ENABLE_STREAMING=true # Enable SSE streaming for HTTP transport
1414 | MCP_SESSION_TIMEOUT=3600 # Session timeout in seconds for HTTP transport
1415 | SESSION_STORE_TYPE=memory # Options: memory, sqlite
1416 | SQLITE_DB_PATH=./data/sessions.db # Path to SQLite database file when using sqlite store
1417 | ENABLE_HEALTH_CHECK=true
1418 | HEALTH_CHECK_PORT=3000
1419 |
1420 | # GitHub Integration
1421 | GITHUB_API_TOKEN=your_github_token_here
1422 | ```
1423 |
1424 | ## Security Considerations
1425 |
1426 | This server implements several security measures to protect against common vulnerabilities. Understanding these security features is critical when deploying in production environments.
1427 |
1428 | ### File System Security
1429 |
1430 | 1. **Path Validation and Isolation**
1431 | - **ALLOWED_OUTPUT_PATHS**: Critical security feature that restricts where file writing tools can write files
1432 | - **Security Principle**: Files can only be created, read, or modified within explicitly allowed directories
1433 | - **Production Requirement**: Always use absolute paths to prevent potential directory traversal attacks
1434 |
1435 | 2. **Path Traversal Protection**
1436 | - The `FileSecurityService` implements robust path traversal protection by:
1437 | - Fully resolving paths to their absolute form
1438 | - Normalizing paths to handle ".." and "." segments properly
1439 | - Validating that normalized paths stay within allowed directories
1440 | - Checking both string-based prefixes and relative path calculations for redundant security
1441 |
1442 | 3. **Symlink Security**
1443 | - Symbolic links are fully resolved and checked against allowed directories
1444 | - Both the symlink itself and its target are validated
1445 | - Parent directory symlinks are iteratively checked to prevent circumvention
1446 | - Multi-level symlink chains are fully resolved before validation
1447 |
1448 | ### Authentication & Authorization
1449 |
1450 | 1. **Connection Tokens**
1451 | - `MCP_CONNECTION_TOKEN` provides basic authentication for clients connecting to this server
1452 | - Should be treated as a secret and use a strong, unique value in production
1453 |
1454 | 2. **API Key Security**
1455 | - `GOOGLE_GEMINI_API_KEY` grants access to Google Gemini API services
1456 | - Must be kept secure and never exposed in client-side code or logs
1457 | - Use environment variables or secure secret management systems to inject this value
1458 |
1459 | ### URL Context Security
1460 |
1461 | 1. **Multi-Layer URL Validation**
1462 | - **Protocol Validation**: Only HTTP/HTTPS protocols are allowed
1463 | - **Private Network Protection**: Blocks access to localhost, private IP ranges, and internal domains
1464 | - **Domain Control**: Configurable allowlist/blocklist with wildcard support
1465 | - **Suspicious Pattern Detection**: Identifies potential path traversal, dangerous characters, and malicious patterns
1466 | - **IDN Homograph Attack Prevention**: Detects potentially confusing Unicode domain names
1467 |
1468 | 2. **Rate Limiting and Resource Protection**
1469 | - **Per-domain rate limiting**: Default 10 requests per minute per domain
1470 | - **Content size limits**: Configurable maximum content size per URL (default 100KB)
1471 | - **Request timeout controls**: Prevents hanging requests (default 10 seconds)
1472 | - **Concurrent request limits**: Controlled batch processing to prevent overload
1473 |
1474 | 3. **Content Security**
1475 | - **Content type validation**: Only processes text-based content types
1476 | - **HTML sanitization**: Removes script tags, style blocks, and dangerous content
1477 | - **Metadata extraction**: Safely parses HTML metadata without executing code
1478 | - **Memory protection**: Content truncation prevents memory exhaustion attacks
1479 |
1480 | ### Network Security
1481 |
1482 | 1. **Transport Options**
1483 | - stdio: Provides process isolation when used as a spawned child process
1484 | - SSE/HTTP: Ensure proper network-level protection when exposing over networks
1485 |
1486 | 2. **Port Configuration**
1487 | - Configure firewall rules appropriately when exposing server ports
1488 | - Consider reverse proxies with TLS termination for production deployments
1489 |
1490 | ### Production Deployment Recommendations
1491 |
1492 | 1. **File Paths**
1493 | - Always use absolute paths for `ALLOWED_OUTPUT_PATHS`
1494 | - Use paths outside the application directory to prevent source code modification
1495 | - Restrict to specific, limited-purpose directories with appropriate permissions
1496 | - NEVER include sensitive system directories like "/", "/etc", "/usr", "/bin", or "/home"
1497 |
1498 | 2. **Process Isolation**
1499 | - Run the server with restricted user permissions
1500 | - Consider containerization (Docker) for additional isolation
1501 |
1502 | 3. **Secrets Management**
1503 | - Use a secure secrets management solution instead of .env files in production
1504 | - Rotate API keys and connection tokens regularly
1505 |
1506 | 4. **URL Context Security**
1507 | - Enable URL context only when needed: Set `GOOGLE_GEMINI_ENABLE_URL_CONTEXT=false` if not required
1508 | - Use restrictive domain allowlists: Avoid `GOOGLE_GEMINI_URL_ALLOWED_DOMAINS=*` in production
1509 | - Configure comprehensive blocklists: Add known malicious domains to `GOOGLE_GEMINI_URL_BLOCKLIST`
1510 | - Set conservative resource limits: Use appropriate values for `GOOGLE_GEMINI_URL_MAX_CONTENT_KB` and `GOOGLE_GEMINI_URL_MAX_COUNT`
1511 | - Monitor URL access patterns: Review logs for suspicious URL access attempts
1512 | - Consider network-level protection: Use firewalls or proxies to add additional URL filtering
1513 |
1514 | ## Error Handling
1515 |
1516 | The server provides enhanced error handling using the MCP standard `McpError` type when tool execution fails. This object contains:
1517 |
1518 | * `code`: An `ErrorCode` enum value indicating the type of error:
1519 | * `InvalidParams`: Parameter validation errors (wrong type, missing required field, etc.)
1520 | * `InvalidRequest`: General request errors, including safety blocks and not found resources
1521 | * `PermissionDenied`: Authentication or authorization failures
1522 | * `ResourceExhausted`: Rate limits, quotas, or resource capacity issues
1523 | * `FailedPrecondition`: Operations that require conditions that aren't met
1524 | * `InternalError`: Unexpected server or API errors
1525 | * `message`: A human-readable description of the error with specific details.
1526 | * `details`: (Optional) An object with more specific information from the Gemini SDK error.
1527 |
1528 | ### Implementation Details
1529 |
1530 | The server uses a multi-layered approach to error handling:
1531 |
1532 | 1. **Validation Layer**: Zod schemas validate all parameters at both the tool level (MCP request) and service layer (before API calls).
1533 | 2. **Error Classification**: A detailed error mapping system categorizes errors from the Google GenAI SDK into specific error types:
1534 | * `GeminiValidationError`: Parameter validation failures
1535 | * `GeminiAuthError`: Authentication issues
1536 | * `GeminiQuotaError`: Rate limiting and quota exhaustion
1537 | * `GeminiContentFilterError`: Content safety filtering
1538 | * `GeminiNetworkError`: Connection and timeout issues
1539 | * `GeminiModelError`: Model-specific problems
1540 | 3. **Retry Mechanism**: Automatic retry with exponential backoff for transient errors:
1541 | * Network issues, timeouts, and rate limit errors are automatically retried
1542 | * Configurable retry parameters (attempts, delay, backoff factor)
1543 | * Jitter randomization to prevent synchronized retry attempts
1544 | * Detailed logging of retry attempts for debugging
1545 |
1546 | **Common Error Scenarios:**
1547 |
1548 | * **Authentication Failures:** `PermissionDenied` - Invalid API key, expired credentials, or unauthorized access.
1549 | * **Parameter Validation:** `InvalidParams` - Missing required fields, wrong data types, invalid values.
1550 | * **Safety Blocks:** `InvalidRequest` - Content blocked by safety filters with details indicating `SAFETY` as the block reason.
1551 | * **File/Cache Not Found:** `InvalidRequest` - Resource not found, with details about the missing resource.
1552 | * **Rate Limits:** `ResourceExhausted` - API quota exceeded or rate limits hit, with details about limits.
1553 | * **File API Unavailable:** `FailedPrecondition` - When attempting File API operations without a valid Google AI Studio key.
1554 | * **Path Traversal Security:** `InvalidParams` - Attempts to access audio files outside the allowed directory with details about the security validation failure.
1555 | * **Image/Audio Processing Errors:**
1556 | * `InvalidParams` - For format issues, size limitations, or invalid inputs
1557 | * `InternalError` - For processing failures during analysis
1558 | * `ResourceExhausted` - For resource-intensive operations exceeding limits
1559 |
1560 | The server includes additional context in error messages to help with troubleshooting, including session IDs for chat-related errors and specific validation details for parameter errors.
1561 |
1562 | Check the `message` and `details` fields of the returned `McpError` for specific troubleshooting information.
1563 |
1564 | ## Development and Testing
1565 |
1566 | This server includes a comprehensive test suite to ensure functionality and compatibility with the Gemini API. The tests are organized into unit tests (for individual components) and integration tests (for end-to-end functionality).
1567 |
1568 | ### Test Structure
1569 |
1570 | - **Unit Tests**: Located in `tests/unit/` - Test individual components in isolation with mocked dependencies
1571 | - **Integration Tests**: Located in `tests/integration/` - Test end-to-end functionality with real server interaction
1572 | - **Test Utilities**: Located in `tests/utils/` - Helper functions and fixtures for testing
1573 |
1574 | ### Running Tests
1575 |
1576 | ```bash
1577 | # Install dependencies first
1578 | npm install
1579 |
1580 | # Run all tests
1581 | npm run test
1582 |
1583 | # Run only unit tests
1584 | npm run test:unit
1585 |
1586 | # Run only integration tests
1587 | npm run test:integration
1588 |
1589 | # Run a specific test file
1590 | node --test --loader ts-node/esm tests/path/to/test-file.test.ts
1591 | ```
1592 |
1593 | ### Testing Approach
1594 |
1595 | 1. **Service Mocking**: The tests use a combination of direct method replacement and mock interfaces to simulate the Gemini API response. This is particularly important for the `@google/genai` SDK (v0.10.0) which has a complex object structure.
1596 |
1597 | 2. **Environmental Variables**: Tests automatically check for required environment variables and will skip tests that require API keys if they're not available. This allows core functionality to be tested without credentials.
1598 |
1599 | 3. **Test Server**: Integration tests use a test server fixture that creates an isolated HTTP server instance with the MCP handler configured for testing.
1600 |
1601 | 4. **RetryService**: The retry mechanism is extensively tested to ensure proper handling of transient errors with exponential backoff, jitter, and configurable retry parameters.
1602 |
1603 | 5. **Image Generation**: Tests specifically address the complex interactions with the Gemini API for image generation, supporting both Gemini models and the dedicated Imagen 3.1 model.
1604 |
1605 | ### Test Environment Setup
1606 |
1607 | For running tests that require API access, create a `.env.test` file in the project root with the following variables:
1608 |
1609 | ```env
1610 | # Required for basic API tests
1611 | GOOGLE_GEMINI_API_KEY=your_api_key_here
1612 |
1613 | # Required for router tests
1614 | GOOGLE_GEMINI_MODEL=gemini-1.5-flash
1615 | ```
1616 |
1617 | The test suite will automatically detect available environment variables and skip tests that require missing configuration.
1618 |
1619 | ## Contributing
1620 |
1621 | We welcome contributions to improve the MCP Gemini Server! This section provides guidelines for contributing to the project.
1622 |
1623 | ### Development Environment Setup
1624 |
1625 | 1. **Fork and Clone the Repository**
1626 | ```bash
1627 | git clone https://github.com/yourusername/mcp-gemini-server.git
1628 | cd mcp-gemini-server
1629 | ```
1630 |
1631 | 2. **Install Dependencies**
1632 | ```bash
1633 | npm install
1634 | ```
1635 |
1636 | 3. **Set Up Environment Variables**
1637 | Create a `.env` file in the project root with the necessary variables as described in the Environment Variables section.
1638 |
1639 | 4. **Build and Run**
1640 | ```bash
1641 | npm run build
1642 | npm run dev
1643 | ```
1644 |
1645 | ### Development Process
1646 |
1647 | 1. **Create a Feature Branch**
1648 | ```bash
1649 | git checkout -b feature/your-feature-name
1650 | ```
1651 |
1652 | 2. **Make Your Changes**
1653 | Implement your feature or fix, following the code style guidelines.
1654 |
1655 | 3. **Write Tests**
1656 | Add tests for your changes to ensure functionality and prevent regressions.
1657 |
1658 | 4. **Run Tests and Linting**
1659 | ```bash
1660 | npm run test
1661 | npm run lint
1662 | npm run format
1663 | ```
1664 |
1665 | 5. **Commit Your Changes**
1666 | Use clear, descriptive commit messages that explain the purpose of your changes.
1667 |
1668 | ### Testing Guidelines
1669 |
1670 | - Write unit tests for all new functionality
1671 | - Update existing tests when modifying functionality
1672 | - Ensure all tests pass before submitting a pull request
1673 | - Include both positive and negative test cases
1674 | - Mock external dependencies to ensure tests can run without external services
1675 |
1676 | ### Pull Request Process
1677 |
1678 | 1. **Update Documentation**
1679 | Update the README.md and other documentation to reflect your changes.
1680 |
1681 | 2. **Submit a Pull Request**
1682 | - Provide a clear description of the changes
1683 | - Link to any related issues
1684 | - Explain how to test the changes
1685 | - Ensure all CI checks pass
1686 |
1687 | 3. **Code Review**
1688 | - Address any feedback from reviewers
1689 | - Make requested changes and update the PR
1690 |
1691 | ### Coding Standards
1692 |
1693 | - Follow the existing code style (PascalCase for classes/interfaces/types, camelCase for functions/variables)
1694 | - Use strong typing with TypeScript interfaces
1695 | - Document public APIs with JSDoc comments
1696 | - Handle errors properly by extending base error classes
1697 | - Follow the service-based architecture with dependency injection
1698 | - Use Zod for schema validation
1699 | - Format code according to the project's ESLint and Prettier configuration
1700 |
1701 | ## Code Review Tools
1702 |
1703 | The MCP Gemini Server provides powerful code review capabilities leveraging Gemini's models to analyze git diffs and GitHub repositories. These tools help identify potential issues, suggest improvements, and provide comprehensive feedback on code changes.
1704 |
1705 | ### Local Git Diff Review
1706 |
1707 | Review local git changes directly from your command line:
1708 |
1709 | ```bash
1710 | # Using the included CLI script
1711 | ./scripts/gemini-review.sh
1712 |
1713 | # Options
1714 | ./scripts/gemini-review.sh --focus=security --reasoning=high
1715 | ```
1716 |
1717 | The CLI script supports various options:
1718 | - `--focus=FOCUS`: Focus of the review (security, performance, architecture, bugs, general)
1719 | - `--model=MODEL`: Model to use (defaults to gemini-flash-2.0 for cost efficiency)
1720 | - `--reasoning=LEVEL`: Reasoning effort (none, low, medium, high)
1721 | - `--exclude=PATTERN`: Files to exclude using glob patterns
1722 |
1723 | ### GitHub Repository Review
1724 |
1725 | Review GitHub repositories, branches, and pull requests using the following tools:
1726 |
1727 | - **GitHub PR Review Tool**: Analyzes pull requests for issues and improvements
1728 | - **GitHub Repository Review Tool**: Analyzes entire repositories or branches
1729 |
1730 | ### Cost Optimization
1731 |
1732 | By default, code review tools use the more cost-efficient `gemini-flash-2.0` model, which offers a good balance between cost and capability for most code review tasks. For particularly complex code bases or when higher reasoning depth is needed, you can specify more powerful models:
1733 |
1734 | ```bash
1735 | # Using a more powerful model for complex code
1736 | ./scripts/gemini-review.sh --model=gemini-1.5-pro --reasoning=high
1737 | ```
1738 |
1739 | ### Running Tests
1740 |
1741 | Tests for the GitHub code review functionality can also use the cheaper model:
1742 |
1743 | ```bash
1744 | # Run tests with the default gemini-flash-2.0 model
1745 | npm run test:unit
1746 | ```
1747 |
1748 | ## Server Features
1749 |
1750 | ### Health Check Endpoint
1751 |
1752 | The server provides a built-in health check HTTP endpoint that can be used for monitoring and status checks. This is separate from the MCP server transport and runs as a lightweight HTTP server.
1753 |
1754 | When enabled, you can access the health check at:
1755 | ```
1756 | http://localhost:3000/health
1757 | ```
1758 |
1759 | The health check endpoint returns a JSON response with the following information:
1760 | ```json
1761 | {
1762 | "status": "running",
1763 | "uptime": 1234, // Seconds since the server started
1764 | "transport": "StdioServerTransport", // Current transport type
1765 | "version": "0.1.0" // Server version
1766 | }
1767 | ```
1768 |
1769 | You can check the health endpoint using curl:
1770 | ```bash
1771 | curl http://localhost:3000/health
1772 | ```
1773 |
1774 | You can configure the health check using these environment variables:
1775 | - `ENABLE_HEALTH_CHECK`: Set to "false" to disable the health check server (default: "true")
1776 | - `HEALTH_CHECK_PORT`: Port number for the health check server (default: 3000)
1777 |
1778 | ### Session Persistence
1779 |
1780 | The server supports persistent session storage for HTTP/SSE transports, allowing sessions to survive server restarts and enabling horizontal scaling.
1781 |
1782 | #### Storage Backends
1783 |
1784 | 1. **In-Memory Store (Default)**
1785 | - Sessions stored in server memory
1786 | - Fast performance for development
1787 | - Sessions lost on server restart
1788 | - No external dependencies
1789 |
1790 | 2. **SQLite Store**
1791 | - Sessions persisted to local SQLite database
1792 | - Survives server restarts
1793 | - Automatic cleanup of expired sessions
1794 | - Good for single-instance production deployments
1795 |
1796 | #### Configuration
1797 |
1798 | Enable SQLite session persistence:
1799 | ```bash
1800 | export SESSION_STORE_TYPE=sqlite
1801 | export SQLITE_DB_PATH=./data/sessions.db # Optional, this is the default
1802 | ```
1803 |
1804 | The SQLite database file and directory will be created automatically on first use. The database includes:
1805 | - Automatic indexing for performance
1806 | - Built-in cleanup of expired sessions
1807 | - ACID compliance for data integrity
1808 |
1809 | #### Session Lifecycle
1810 |
1811 | - Sessions are created when clients connect via HTTP/SSE transport
1812 | - Each session has a configurable timeout (default: 1 hour)
1813 | - Session expiration is extended on each activity
1814 | - Expired sessions are automatically cleaned up every minute
1815 |
1816 | ### Graceful Shutdown
1817 |
1818 | The server implements graceful shutdown handling for SIGTERM and SIGINT signals. When the server receives a shutdown signal:
1819 |
1820 | 1. It attempts to properly disconnect the MCP server transport
1821 | 2. It closes the health check server if running
1822 | 3. It logs the shutdown status
1823 | 4. It exits with the appropriate exit code (0 for successful shutdown, 1 if errors occurred)
1824 |
1825 | This ensures clean termination when the server is run in containerized environments or when stopped manually.
1826 |
1827 | ## Known Issues
1828 |
1829 | * **Pagination Issues:** `gemini_listCaches` may not reliably return `nextPageToken` due to limitations in iterating the SDK's Pager object. A workaround is implemented but has limited reliability.
1830 | * **Path Requirements:** Audio transcription operations require absolute paths when run from the server environment. Relative paths are not supported.
1831 | * **File Size Limitations:** Audio files for transcription are limited to 20MB (original file size, before base64 encoding). The server reads the file and converts it to base64 internally. Larger files will be rejected with an error message.
1832 | * **API Compatibility:** Caching API is **not supported with Vertex AI credentials**, only Google AI Studio API keys.
1833 | * **Model Support:** This server is primarily tested and optimized for the latest Gemini 1.5 and 2.5 models. While other models should work, these models are the primary focus for testing and feature compatibility.
1834 | * **TypeScript Build Issues:** The TypeScript build may show errors primarily in test files. These are type compatibility issues that don't affect the runtime functionality. The server itself will function properly despite these build warnings.
1835 | * **Resource Usage:**
1836 | * Image processing requires significant resource usage, especially for large resolution images. Consider using smaller resolutions (512x512) for faster responses.
1837 | * Generating multiple images simultaneously increases resource usage proportionally.
1838 | * Audio transcription is limited to files under 20MB (original file size). The server reads files from disk and handles base64 conversion internally. Processing may take significant time and resources depending on file size and audio complexity.
1839 | * **Content Handling:**
1840 | * Base64-encoded images are streamed in chunks to handle large file sizes efficiently.
1841 | * Visual content understanding may perform differently across various types of visual content (charts vs. diagrams vs. documents).
1842 | * Audio transcription accuracy depends on audio quality, number of speakers, and background noise.
1843 | * **URL Context Features:**
1844 | * URL context is disabled by default and must be explicitly enabled via `GOOGLE_GEMINI_ENABLE_URL_CONTEXT=true`
1845 | * JavaScript-rendered content is not supported - only static HTML content is processed
1846 | * Some websites may block automated access or require authentication that is not currently supported
1847 | * Content extraction quality may vary depending on website structure and formatting
1848 | * Rate limiting per domain (10 requests/minute by default) may affect bulk processing scenarios
1849 |
```
--------------------------------------------------------------------------------
/src/services/mcp/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | export * from "./McpClientService.js";
2 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Make sure to import type augmentation first
2 | import "./types/googleGenAITypes.js";
3 |
4 | // ... existing code ...
5 |
```
--------------------------------------------------------------------------------
/vitest.setup.ts:
--------------------------------------------------------------------------------
```typescript
1 | // This file sets up Vitest environment for TypeScript
2 | // With globals: true in vitest.config.ts, expect and vi are automatically available
3 |
```
--------------------------------------------------------------------------------
/src/services/session/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | export { SessionStore } from "./SessionStore.js";
2 | export { InMemorySessionStore } from "./InMemorySessionStore.js";
3 | export { SQLiteSessionStore } from "./SQLiteSessionStore.js";
4 |
```
--------------------------------------------------------------------------------
/src/tools/registration/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Tool Registration Index
3 | *
4 | * Exports the new tool registration system components.
5 | */
6 | export * from "./ToolRegistry.js";
7 | export * from "./ToolAdapter.js";
8 | export * from "./registerAllTools.js";
9 |
```
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "types": ["node", "vitest/globals"],
5 | "skipLibCheck": true,
6 | "noEmit": true,
7 | "rootDir": "."
8 | },
9 | "include": [
10 | "src/**/*",
11 | "tests/**/*",
12 | "vitest.setup.ts"
13 | ],
14 | "exclude": [
15 | "node_modules",
16 | "dist"
17 | ]
18 | }
```
--------------------------------------------------------------------------------
/src/services/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Export all services from this barrel file
2 | export * from "./ExampleService.js";
3 | export * from "./GeminiService.js"; // Export Gemini service
4 | export * from "./SessionService.js";
5 | export * from "./mcp/index.js"; // Export MCP services
6 | // export * from './YourService.js'; // Add new services here
7 |
```
--------------------------------------------------------------------------------
/scripts/run-with-health-check.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 | # Script to run the MCP server with health check enabled on a specific port
3 | # Usage: ./scripts/run-with-health-check.sh [port]
4 |
5 | # Default port if not specified
6 | HEALTH_PORT=${1:-3000}
7 |
8 | # Run the server with health check enabled
9 | ENABLE_HEALTH_CHECK=true HEALTH_CHECK_PORT=$HEALTH_PORT node dist/server.js
10 |
```
--------------------------------------------------------------------------------
/tests/tsconfig.test.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": true,
5 | "types": ["node", "vitest/globals"],
6 | "allowImportingTsExtensions": true,
7 | "rootDir": ".."
8 | },
9 | "include": [
10 | "../src/**/*",
11 | "./**/*",
12 | "../node_modules/vitest/globals.d.ts"
13 | ],
14 | "exclude": [
15 | "../node_modules",
16 | "../dist"
17 | ]
18 | }
19 |
```
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Export all utility functions and modules from this barrel file
2 | export * from "./logger.js";
3 | export * from "./errors.js";
4 | export * from "./filePathSecurity.js";
5 | export * from "./FileSecurityService.js";
6 | export * from "./RetryService.js";
7 | export * from "./healthCheck.js";
8 | // export * from './contextSanitizer.js'; // Add other utils here
9 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
2 | FROM node:lts-alpine
3 |
4 | WORKDIR /app
5 |
6 | # Copy package files and install dependencies
7 | COPY package.json package-lock.json ./
8 |
9 | # Install dependencies without running lifecycle scripts
10 | RUN npm ci --ignore-scripts
11 |
12 | # Copy the rest of the app
13 | COPY . .
14 |
15 | # Build the TypeScript code
16 | RUN npm run build
17 |
18 | # Expose port if needed (optional)
19 | # EXPOSE 3000
20 |
21 | # Start the server
22 | CMD ["node", "dist/server.js"]
23 |
```
--------------------------------------------------------------------------------
/tests/utils/vitest.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Type definitions for Vitest
3 | * This helps to ensure we have proper types for our tests
4 | */
5 |
6 | declare module "vitest" {
7 | interface Mock {
8 | <This, Args extends any[] = any[], Return = any>(
9 | this: This,
10 | ...args: Args
11 | ): Return;
12 | mockImplementation(fn: (...args: any[]) => any): this;
13 | mockReturnValue(val: any): this;
14 | mockResolvedValue(val: any): this;
15 | mockRejectedValue(val: any): this;
16 | }
17 |
18 | function mocked<T>(item: T, deep?: boolean): T;
19 | }
20 |
```
--------------------------------------------------------------------------------
/src/types/exampleServiceTypes.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Types specific to the ExampleService
2 |
3 | /**
4 | * Configuration options for ExampleService.
5 | */
6 | export interface ExampleServiceConfig {
7 | greeting: string;
8 | enableDetailedLogs: boolean;
9 | }
10 |
11 | /**
12 | * Data structure handled by ExampleService.
13 | */
14 | export interface ExampleServiceData {
15 | name: string;
16 | message: string;
17 | processedTimestamp: string;
18 | metrics?: ExampleServiceMetrics;
19 | }
20 |
21 | /**
22 | * Metrics collected during ExampleService processing.
23 | */
24 | export interface ExampleServiceMetrics {
25 | processingTimeMs: number;
26 | }
27 |
```
--------------------------------------------------------------------------------
/src/utils/ToolError.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Custom error class for tool-specific errors.
3 | * This is used by image feature tools and maintains compatibility
4 | * with the error mapping system.
5 | */
6 | export class ToolError extends Error {
7 | public code: string;
8 | public readonly details?: unknown;
9 |
10 | constructor(message: string, options?: { code?: string; details?: unknown }) {
11 | super(message);
12 | this.name = "ToolError";
13 | this.code = options?.code || "TOOL_ERROR";
14 | this.details = options?.details;
15 |
16 | // Capture stack trace (excluding constructor)
17 | Error.captureStackTrace(this, this.constructor);
18 | }
19 | }
20 |
```
--------------------------------------------------------------------------------
/vitest-globals.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | /// <reference types="vitest/globals" />
2 |
3 | // This file ensures vitest globals are available throughout the project
4 | // when using vitest with globals: true configuration
5 |
6 | declare global {
7 | const describe: typeof import("vitest").describe;
8 | const it: typeof import("vitest").it;
9 | const test: typeof import("vitest").test;
10 | const expect: typeof import("vitest").expect;
11 | const beforeAll: typeof import("vitest").beforeAll;
12 | const afterAll: typeof import("vitest").afterAll;
13 | const beforeEach: typeof import("vitest").beforeEach;
14 | const afterEach: typeof import("vitest").afterEach;
15 | const vi: typeof import("vitest").vi;
16 | }
17 |
18 | export {};
19 |
```
--------------------------------------------------------------------------------
/tests/utils/debug-error.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Simple helper to debug errors
2 | import { GeminiApiError, mapToMcpError } from "../../src/utils/errors.js";
3 |
4 | // Create a test error with details
5 | const errorWithDetails = new GeminiApiError("API error with details", {
6 | key: "value",
7 | });
8 | const mappedError = mapToMcpError(errorWithDetails, "DEBUG_TOOL");
9 |
10 | // Log the result for inspection
11 | console.log("Original Error:", {
12 | message: errorWithDetails.message,
13 | details: errorWithDetails.details,
14 | hasDetailsProperty: "details" in errorWithDetails,
15 | });
16 |
17 | console.log("Mapped Error:", {
18 | code: mappedError.code,
19 | message: mappedError.message,
20 | details: mappedError.details,
21 | hasDetailsProperty: "details" in mappedError,
22 | });
23 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "NodeNext",
5 | "moduleResolution": "NodeNext",
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "strict": true,
11 | "skipLibCheck": true,
12 | "resolveJsonModule": true,
13 | "sourceMap": true,
14 | "declaration": true,
15 | "declarationMap": true,
16 | "allowJs": true,
17 | "types": ["node", "vitest/globals"],
18 | "paths": {
19 | "@app/*": ["./src/*"],
20 | "@tests/*": ["./tests/*"]
21 | }
22 | },
23 | "ts-node": {
24 | "transpileOnly": true,
25 | "files": true
26 | },
27 | "include": [
28 | "src/**/*",
29 | "vitest-globals.d.ts"
30 | ],
31 | "exclude": [
32 | "node_modules",
33 | "dist"
34 | ]
35 | }
```
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { defineConfig } from "vitest/config";
2 | import path from "path";
3 |
4 | export default defineConfig({
5 | test: {
6 | globals: true,
7 | environment: "node",
8 | setupFiles: ["./vitest.setup.ts"],
9 | include: [
10 | "**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
11 | "**/*.test.vitest.ts",
12 | "**/*.spec.vitest.ts",
13 | ],
14 | coverage: {
15 | provider: "v8",
16 | reporter: ["text", "html", "lcov"],
17 | exclude: ["node_modules/", "dist/", "tests/"],
18 | },
19 | alias: {
20 | "@app": path.resolve(__dirname, "./src"),
21 | "@tests": path.resolve(__dirname, "./tests"),
22 | },
23 | },
24 | resolve: {
25 | alias: {
26 | "@app": path.resolve(__dirname, "./src"),
27 | "@tests": path.resolve(__dirname, "./tests"),
28 | },
29 | },
30 | });
31 |
```
--------------------------------------------------------------------------------
/src/tools/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { McpClientService } from "../services/mcp/McpClientService.js";
3 | import { registerAllTools } from "./registration/registerAllTools.js";
4 |
5 | /**
6 | * Register all defined tools with the MCP server instance.
7 | * This function serves as the central entry point for tool registration.
8 | * It uses the new tool registration system for improved organization and type safety.
9 | *
10 | * @param server The McpServer instance
11 | * @returns The McpClientService instance for managing MCP client connections
12 | */
13 | export function registerTools(server: McpServer): McpClientService {
14 | return registerAllTools(server);
15 | }
16 |
17 | // Re-export schema components
18 | export * from "./schemas/index.js";
19 |
20 | // Re-export registration utilities
21 | export * from "./registration/index.js";
22 |
```
--------------------------------------------------------------------------------
/tests/utils/integration-types.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Types for integration tests
3 | */
4 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5 | import { McpClientService } from "../../src/services/mcp/McpClientService.js";
6 |
7 | /**
8 | * Tool processor function type
9 | */
10 | export type ToolProcessor = (args: any) => Promise<{
11 | content: Array<{
12 | text: string;
13 | type: string;
14 | }>;
15 | functionCall?: any;
16 | }>;
17 |
18 | /**
19 | * Collection of tool processors for testing
20 | */
21 | export interface ToolProcessors {
22 | connect: ToolProcessor;
23 | listTools: ToolProcessor;
24 | callServerTool: ToolProcessor;
25 | disconnect: ToolProcessor;
26 | writeToFile: ToolProcessor;
27 | }
28 |
29 | /**
30 | * Mock server tool handler
31 | */
32 | export type MockServerToolHandler = (
33 | server: McpServer,
34 | mcpClientService: McpClientService
35 | ) => void;
36 |
37 | /**
38 | * Tool registration function
39 | */
40 | export type ToolRegistrationFn = (
41 | server: McpServer,
42 | service: McpClientService
43 | ) => unknown;
44 |
```
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Export all types and interfaces from this barrel file
2 | export * from "./exampleServiceTypes.js";
3 | export * from "./geminiServiceTypes.js"; // Exports GeminiServiceConfig, CachedContentMetadata, PartSchema, ContentSchema
4 | export * from "./serverTypes.js"; // Server state and service interface types
5 |
6 | // Export type-safe schema types from tool schemas
7 | export type {
8 | FunctionParameter,
9 | InferredFunctionParameter,
10 | } from "../tools/schemas/CommonSchemas.js";
11 |
12 | // Define common types used across services/tools if any
13 | export interface CommonContext {
14 | sessionId?: string;
15 | userId?: string;
16 | }
17 |
18 | /**
19 | * Represents the input structure for a function response sent from the client to the server.
20 | * Used by the gemini_sendFunctionResult tool.
21 | */
22 | export interface FunctionResponseInput {
23 | /** The name of the function that was called by the model. */
24 | name: string;
25 | /** The JSON object result returned by the function execution. */
26 | response: Record<string, unknown>;
27 | }
28 |
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
2 |
3 | startCommand:
4 | type: stdio
5 | configSchema:
6 | # JSON Schema defining the configuration options for the MCP.
7 | type: object
8 | required:
9 | - GOOGLE_GEMINI_API_KEY
10 | properties:
11 | GOOGLE_GEMINI_API_KEY:
12 | type: string
13 | description: Your API key from Google AI Studio.
14 | GOOGLE_GEMINI_MODEL:
15 | type: string
16 | default: gemini-1.5-flash
17 | description: Default Gemini model. Optional; if not provided, 'gemini-1.5-flash'
18 | is used.
19 | commandFunction:
20 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
21 | |-
22 | (config) => ({ command: 'node', args: ['dist/server.js'], env: { GOOGLE_GEMINI_API_KEY: config.GOOGLE_GEMINI_API_KEY, GOOGLE_GEMINI_MODEL: config.GOOGLE_GEMINI_MODEL || 'gemini-1.5-flash' } })
23 | exampleConfig:
24 | GOOGLE_GEMINI_API_KEY: your-api-key-here
25 | GOOGLE_GEMINI_MODEL: gemini-1.5-flash
26 |
```
--------------------------------------------------------------------------------
/src/tools/exampleToolParams.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from "zod";
2 |
3 | export const TOOL_NAME = "exampleTool";
4 |
5 | export const TOOL_DESCRIPTION =
6 | "An example tool that takes a name and returns a greeting message. Demonstrates the basic structure of an MCP tool using Zod for parameter definition.";
7 |
8 | // Define parameters using Zod for validation and description generation
9 | // We need to define this as a raw object with Zod validators (not wrapped in z.object)
10 | // to be compatible with the MCP server.tool() method
11 | export const TOOL_PARAMS = {
12 | name: z
13 | .string()
14 | .min(1, { message: "Name cannot be empty." })
15 | .max(50, { message: "Name cannot exceed 50 characters." })
16 | .describe(
17 | "The name to include in the greeting message. Required, 1-50 characters."
18 | ),
19 |
20 | // Example optional parameter
21 | language: z
22 | .enum(["en", "es", "fr"])
23 | .optional()
24 | .describe(
25 | "Optional language code for the greeting (e.g., 'en', 'es', 'fr'). Defaults to 'en' if not provided or invalid."
26 | ),
27 | };
28 |
29 | // For internal validation within the tool, we can create a complete schema
30 | export const exampleToolSchema = z.object(TOOL_PARAMS);
31 |
```
--------------------------------------------------------------------------------
/src/tools/geminiGenericParamSchemas.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from "zod";
2 | import {
3 | FunctionParameterTypeSchema,
4 | FunctionParameterSchema,
5 | FunctionParameterPropertiesSchema,
6 | FunctionDeclarationSchema,
7 | ToolConfigSchema,
8 | FunctionParameter,
9 | } from "./schemas/CommonSchemas.js";
10 | import { ToolSchema } from "./schemas/ToolSchemas.js";
11 |
12 | // Re-export centralized schemas for backward compatibility
13 | export const functionParameterTypeSchema = FunctionParameterTypeSchema;
14 | export const functionParameterSchema = FunctionParameterSchema;
15 | export const functionParameterPropertiesSchema =
16 | FunctionParameterPropertiesSchema;
17 | export const functionDeclarationSchema = FunctionDeclarationSchema;
18 | export const toolConfigSchema = ToolConfigSchema;
19 | export { ToolSchema };
20 |
21 | // Type exports for better type inference
22 | export type FunctionParameterType = z.infer<typeof FunctionParameterTypeSchema>;
23 | export type { FunctionParameter };
24 | export type FunctionParameterProperties = z.infer<
25 | typeof FunctionParameterPropertiesSchema
26 | >;
27 | export type FunctionDeclaration = z.infer<typeof FunctionDeclarationSchema>;
28 | export type Tool = z.infer<typeof ToolSchema>;
29 | export type ToolConfig = z.infer<typeof ToolConfigSchema>;
30 |
```
--------------------------------------------------------------------------------
/src/types/node-fetch.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | declare module "node-fetch" {
2 | export interface HeadersInit {
3 | [key: string]: string | string[];
4 | }
5 |
6 | export interface RequestInit {
7 | body?: string | Blob | ArrayBuffer | NodeJS.ReadableStream;
8 | headers?: HeadersInit;
9 | method?: string;
10 | redirect?: string;
11 | signal?: AbortSignal;
12 | timeout?: number;
13 | compress?: boolean;
14 | size?: number;
15 | follow?: number;
16 | agent?: import("http").Agent | import("https").Agent | false;
17 | }
18 |
19 | export default function fetch(
20 | url: string | URL,
21 | options?: RequestInit
22 | ): Promise<Response>;
23 |
24 | export class Response {
25 | ok: boolean;
26 | status: number;
27 | statusText: string;
28 | headers: Headers;
29 | json(): Promise<unknown>;
30 | text(): Promise<string>;
31 | buffer(): Promise<Buffer>;
32 | arrayBuffer(): Promise<ArrayBuffer>;
33 | clone(): Response;
34 | }
35 |
36 | export class Headers {
37 | constructor(init?: HeadersInit);
38 | get(name: string): string | null;
39 | has(name: string): boolean;
40 | set(name: string, value: string): void;
41 | append(name: string, value: string): void;
42 | delete(name: string): void;
43 | forEach(callback: (value: string, name: string) => void): void;
44 | }
45 | }
46 |
```
--------------------------------------------------------------------------------
/src/createServer.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { registerTools } from "./tools/index.js";
3 | import { logger } from "./utils/index.js";
4 | import { McpClientService } from "./services/mcp/McpClientService.js";
5 |
6 | /**
7 | * Result object containing the created server and client service instances
8 | */
9 | export interface ServerCreationResult {
10 | server: McpServer;
11 | mcpClientService: McpClientService;
12 | }
13 |
14 | /**
15 | * Creates and configures an MCP server instance.
16 | * This is the central function for server creation and tool registration.
17 | * Note: ConfigurationManager is imported directly by services as needed.
18 | * @returns {ServerCreationResult} Object containing the configured MCP server and client service instances
19 | */
20 | export function createServer(): ServerCreationResult {
21 | logger.info("Creating MCP server instance...");
22 |
23 | // Initialize the server
24 | const server = new McpServer({
25 | name: "mcp-server",
26 | version: "1.0.0",
27 | description: "MCP Server based on recommended practices",
28 | });
29 |
30 | // Register all tools and get the McpClientService instance
31 | const mcpClientService = registerTools(server);
32 |
33 | logger.info("MCP server instance created successfully.");
34 | return { server, mcpClientService };
35 | }
36 |
```
--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------
```typescript
1 | import chalk from "chalk"; // Using chalk for colored output
2 |
3 | // Simple console logger with levels and colors
4 |
5 | // Define log levels (optional, for potential filtering later)
6 | enum LogLevel {
7 | DEBUG,
8 | INFO,
9 | WARN,
10 | ERROR,
11 | }
12 |
13 | // Basic configuration (could be enhanced)
14 | const currentLogLevel = LogLevel.DEBUG; // Show all logs by default
15 |
16 | export const logger = {
17 | debug: (message: string, ...args: unknown[]) => {
18 | if (currentLogLevel <= LogLevel.DEBUG) {
19 | console.error(chalk.gray(`[DEBUG] `), ...args);
20 | }
21 | },
22 | info: (message: string, ...args: unknown[]) => {
23 | if (currentLogLevel <= LogLevel.INFO) {
24 | console.error(chalk.blue(`[INFO] `), ...args);
25 | }
26 | },
27 | warn: (message: string, ...args: unknown[]) => {
28 | if (currentLogLevel <= LogLevel.WARN) {
29 | console.error(chalk.yellow(`[WARN] `), ...args);
30 | }
31 | },
32 | error: (message: string, ...args: unknown[]) => {
33 | if (currentLogLevel <= LogLevel.ERROR) {
34 | // Log error message and stack trace if available
35 | console.error(chalk.red(`[ERROR] `), ...args);
36 | const errorArg = args.find((arg) => arg instanceof Error);
37 | if (errorArg instanceof Error && errorArg.stack) {
38 | console.error(chalk.red(errorArg.stack));
39 | }
40 | }
41 | },
42 | };
43 |
```
--------------------------------------------------------------------------------
/src/services/session/InMemorySessionStore.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { SessionStore } from "./SessionStore.js";
2 | import { SessionState } from "../SessionService.js";
3 |
4 | /**
5 | * In-memory implementation of SessionStore.
6 | * This is the default implementation that stores sessions in a Map.
7 | */
8 | export class InMemorySessionStore implements SessionStore {
9 | private sessions: Map<string, SessionState> = new Map();
10 |
11 | async initialize(): Promise<void> {
12 | // No initialization needed for in-memory store
13 | }
14 |
15 | async set(sessionId: string, session: SessionState): Promise<void> {
16 | this.sessions.set(sessionId, session);
17 | }
18 |
19 | async get(sessionId: string): Promise<SessionState | null> {
20 | return this.sessions.get(sessionId) || null;
21 | }
22 |
23 | async delete(sessionId: string): Promise<boolean> {
24 | return this.sessions.delete(sessionId);
25 | }
26 |
27 | async deleteExpired(now: number): Promise<number> {
28 | let deletedCount = 0;
29 | for (const [sessionId, session] of this.sessions.entries()) {
30 | if (now > session.expiresAt) {
31 | this.sessions.delete(sessionId);
32 | deletedCount++;
33 | }
34 | }
35 | return deletedCount;
36 | }
37 |
38 | async count(): Promise<number> {
39 | return this.sessions.size;
40 | }
41 |
42 | async close(): Promise<void> {
43 | // No cleanup needed for in-memory store
44 | this.sessions.clear();
45 | }
46 | }
47 |
```
--------------------------------------------------------------------------------
/src/tools/schemas/ToolSchemas.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Tool Schemas - Gemini API Specific Tool Types
3 | *
4 | * This file contains the schema definitions for tools compatible with the Gemini API.
5 | */
6 | import { z } from "zod";
7 | import {
8 | FunctionParameterTypeSchema,
9 | FunctionParameterSchema,
10 | FunctionParameterPropertiesSchema,
11 | FunctionDeclarationSchema,
12 | ToolConfigSchema,
13 | } from "./CommonSchemas.js";
14 |
15 | /**
16 | * Complete schema for a tool with function declarations
17 | */
18 | export const ToolSchema = z
19 | .object({
20 | functionDeclarations: z
21 | .array(FunctionDeclarationSchema)
22 | .optional()
23 | .describe("List of function declarations for this tool."),
24 | // Can add other tool types like Retrieval, GoogleSearchRetrieval if needed
25 | })
26 | .describe("Represents a tool definition containing function declarations.");
27 |
28 | /**
29 | * Schema for a tool response
30 | */
31 | export const ToolResponseSchema = z
32 | .object({
33 | name: z.string().describe("The name of the tool that was called"),
34 | response: z.any().describe("The response returned by the tool"),
35 | })
36 | .describe("Response from a tool execution");
37 |
38 | // Export for direct use in tool implementations
39 | export {
40 | FunctionParameterTypeSchema,
41 | FunctionParameterSchema,
42 | FunctionParameterPropertiesSchema,
43 | FunctionDeclarationSchema,
44 | ToolConfigSchema,
45 | };
46 |
```
--------------------------------------------------------------------------------
/src/tools/schemas/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Tool Schemas Index
3 | *
4 | * This barrel file exports all standardized schema definitions
5 | * for tools, providing a central access point.
6 | */
7 |
8 | // Base schema pattern
9 | export * from "./BaseToolSchema.js";
10 |
11 | // Common shared schema definitions
12 | export * from "./CommonSchemas.js";
13 |
14 | // Tool-specific schemas
15 | export * from "./ToolSchemas.js";
16 |
17 | // Note: Example tool params removed as per refactoring
18 |
19 | // Re-export other schemas with namespacing to avoid export conflicts
20 | import * as GeminiGenerateContentConsolidatedParamsModule from "../geminiGenerateContentConsolidatedParams.js";
21 | export { GeminiGenerateContentConsolidatedParamsModule };
22 |
23 | import * as GeminiChatParamsModule from "../geminiChatParams.js";
24 | export { GeminiChatParamsModule };
25 |
26 | import * as GeminiCacheParamsModule from "../geminiCacheParams.js";
27 | export { GeminiCacheParamsModule };
28 |
29 | import * as GeminiCodeReviewParamsModule from "../geminiCodeReviewParams.js";
30 | export { GeminiCodeReviewParamsModule };
31 |
32 | import * as McpClientParamsModule from "../mcpClientParams.js";
33 | export { McpClientParamsModule };
34 |
35 | import * as WriteToFileParamsModule from "./writeToFileParams.js";
36 | export { WriteToFileParamsModule };
37 |
38 | // Add exports for other tool parameter schemas as they are added
39 | // export * from "./yourNewToolParams.js";
40 |
```
--------------------------------------------------------------------------------
/src/services/session/SessionStore.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { SessionState } from "../SessionService.js";
2 |
3 | /**
4 | * Interface for session storage implementations.
5 | * This allows for different storage backends (in-memory, SQLite, Redis, etc.)
6 | */
7 | export interface SessionStore {
8 | /**
9 | * Store a session
10 | * @param sessionId The session identifier
11 | * @param session The session state to store
12 | */
13 | set(sessionId: string, session: SessionState): Promise<void>;
14 |
15 | /**
16 | * Retrieve a session
17 | * @param sessionId The session identifier
18 | * @returns The session state or null if not found
19 | */
20 | get(sessionId: string): Promise<SessionState | null>;
21 |
22 | /**
23 | * Delete a session
24 | * @param sessionId The session identifier
25 | * @returns True if the session was deleted, false if it didn't exist
26 | */
27 | delete(sessionId: string): Promise<boolean>;
28 |
29 | /**
30 | * Delete all expired sessions
31 | * @param now Current timestamp in milliseconds
32 | * @returns Number of sessions deleted
33 | */
34 | deleteExpired(now: number): Promise<number>;
35 |
36 | /**
37 | * Get the count of active sessions
38 | * @returns Number of sessions in the store
39 | */
40 | count(): Promise<number>;
41 |
42 | /**
43 | * Initialize the store (create tables, connect, etc.)
44 | */
45 | initialize(): Promise<void>;
46 |
47 | /**
48 | * Close/cleanup the store
49 | */
50 | close(): Promise<void>;
51 | }
52 |
```
--------------------------------------------------------------------------------
/src/tools/schemas/writeToFileParams.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from "zod";
2 | import { createToolSchema } from "./BaseToolSchema.js";
3 | import { FilePathSchema, FileOverwriteSchema } from "./CommonSchemas.js";
4 |
5 | const TOOL_NAME = "write_to_file";
6 |
7 | const TOOL_DESCRIPTION =
8 | "Writes the given text content to a specified file path. The path must be within an allowed directory.";
9 |
10 | // Text-only encoding schema for this tool
11 | const TextEncodingSchema = z
12 | .enum(["utf8"])
13 | .optional()
14 | .default("utf8")
15 | .describe("Encoding of the content. Only UTF-8 text encoding is supported.");
16 |
17 | const TOOL_PARAMS = {
18 | filePath: FilePathSchema.describe(
19 | "The path to the file where content will be written."
20 | ),
21 | content: z.string().describe("The text content to write to the file."),
22 | encoding: TextEncodingSchema,
23 | overwriteFile: FileOverwriteSchema,
24 | };
25 |
26 | // Create standardized schema with helper function
27 | const schema = createToolSchema(TOOL_NAME, TOOL_DESCRIPTION, TOOL_PARAMS);
28 |
29 | // Export all schema components
30 | export const {
31 | TOOL_NAME: exportedToolName,
32 | TOOL_DESCRIPTION: exportedToolDescription,
33 | TOOL_PARAMS: exportedToolParams,
34 | toolSchema: writeToFileSchema,
35 | ToolParams: WriteToFileParams,
36 | } = schema;
37 |
38 | // For backward compatibility
39 | export {
40 | exportedToolName as TOOL_NAME,
41 | exportedToolDescription as TOOL_DESCRIPTION,
42 | exportedToolParams as TOOL_PARAMS,
43 | };
44 |
```
--------------------------------------------------------------------------------
/src/services/gemini/GeminiTypes.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type {
2 | GenerationConfig,
3 | SafetySetting,
4 | Content,
5 | Part,
6 | Tool,
7 | ToolConfig,
8 | FunctionCall,
9 | } from "@google/genai";
10 |
11 | // Import ThinkingConfig and ExtendedGenerationConfig from our types
12 | import type { ThinkingConfig } from "../../types/googleGenAITypes.js";
13 |
14 | // Re-export types from Google GenAI SDK and our custom types
15 | // Note: We're re-exporting the ExtendedGenerationConfig as GenerationConfig
16 | // to ensure consistent usage across the codebase
17 | export type {
18 | GenerationConfig as BaseGenerationConfig,
19 | SafetySetting,
20 | Content,
21 | Part,
22 | Tool,
23 | ToolConfig,
24 | FunctionCall,
25 | ThinkingConfig,
26 | };
27 |
28 | // Re-export our extended GenerationConfig as the default GenerationConfig
29 | export type { GenerationConfig } from "../../types/googleGenAITypes.js";
30 |
31 | // Extend GenerationConfig to include thinkingBudget property
32 | declare module "@google/genai" {
33 | interface GenerationConfig {
34 | thinkingBudget?: number;
35 | }
36 | }
37 |
38 | // Type-safe resource IDs
39 | export type CacheId = `cachedContents/${string}`;
40 |
41 | // Export the ChatSession interface for use across services
42 | export interface ChatSession {
43 | model: string;
44 | config: {
45 | history?: Content[];
46 | generationConfig?: GenerationConfig;
47 | safetySettings?: SafetySetting[];
48 | tools?: Tool[];
49 | systemInstruction?: Content;
50 | cachedContent?: string;
51 | thinkingConfig?: ThinkingConfig;
52 | };
53 | history: Content[];
54 | }
55 |
```
--------------------------------------------------------------------------------
/src/types/serverTypes.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Server-related type definitions for the MCP Gemini Server
3 | */
4 |
5 | import type { Server as HttpServer } from "http";
6 | import type { Transport } from "@modelcontextprotocol/sdk/server/mcp.js";
7 |
8 | /**
9 | * Interface for session service implementations
10 | */
11 | export interface SessionServiceLike {
12 | stopCleanupInterval(): void;
13 | }
14 |
15 | /**
16 | * Interface for MCP client service implementations
17 | */
18 | export interface McpClientServiceLike {
19 | disconnect(serverId: string): boolean;
20 | closeAllConnections(): void;
21 | }
22 |
23 | /**
24 | * Interface for server implementations that can be disconnected
25 | */
26 | export interface ServerLike {
27 | disconnect?(): Promise<void> | void;
28 | }
29 |
30 | /**
31 | * Server state interface for tracking the overall server state
32 | */
33 | export interface ServerState {
34 | isRunning: boolean;
35 | startTime: number | null;
36 | transport: Transport | null;
37 | server: ServerLike | null;
38 | healthCheckServer: HttpServer | null;
39 | mcpClientService: McpClientServiceLike | null;
40 | sessionService?: SessionServiceLike | null;
41 | httpServer?: HttpServer | null;
42 | }
43 |
44 | /**
45 | * Test-specific server state type that makes mcpClientService optional
46 | */
47 | export type TestServerState = Omit<ServerState, "mcpClientService"> & {
48 | mcpClientService?: McpClientServiceLike | null;
49 | };
50 |
51 | /**
52 | * JSON-RPC 2.0 initialize request structure
53 | */
54 | export interface JsonRpcInitializeRequest {
55 | jsonrpc: "2.0";
56 | method: "initialize";
57 | id: string | number;
58 | params?: Record<string, unknown>;
59 | }
60 |
```
--------------------------------------------------------------------------------
/src/tools/schemas/BaseToolSchema.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Base Tool Schema Pattern
3 | *
4 | * This file establishes the standardized pattern for defining tool parameter schemas.
5 | * All tool parameter definitions should follow this pattern for consistency.
6 | */
7 | import { z } from "zod";
8 |
9 | /**
10 | * Interface that defines the standard exports for all tool parameter files
11 | */
12 | export interface ToolSchemaDefinition<T extends z.ZodRawShape> {
13 | /**
14 | * The tool name used for registration
15 | */
16 | TOOL_NAME: string;
17 |
18 | /**
19 | * The tool description
20 | */
21 | TOOL_DESCRIPTION: string;
22 |
23 | /**
24 | * The tool parameters as a Zod schema object for direct use with MCP server.tool()
25 | */
26 | TOOL_PARAMS: T;
27 |
28 | /**
29 | * Complete Zod schema for validation (z.object(TOOL_PARAMS))
30 | */
31 | toolSchema: z.ZodObject<T>;
32 |
33 | /**
34 | * TypeScript type for parameters derived from the schema
35 | */
36 | ToolParams: z.infer<z.ZodObject<T>>;
37 | }
38 |
39 | /**
40 | * Helper function to create a standardized tool schema definition
41 | * @param name The tool name
42 | * @param description The tool description
43 | * @param params The Zod schema parameters
44 | * @returns A standardized tool schema definition
45 | */
46 | export function createToolSchema<T extends z.ZodRawShape>(
47 | name: string,
48 | description: string,
49 | params: T
50 | ): ToolSchemaDefinition<T> {
51 | const toolSchema = z.object(params);
52 |
53 | return {
54 | TOOL_NAME: name,
55 | TOOL_DESCRIPTION: description,
56 | TOOL_PARAMS: params,
57 | toolSchema,
58 | // This is a type-level property, not a runtime value
59 | ToolParams: {} as z.infer<typeof toolSchema>,
60 | };
61 | }
62 |
```
--------------------------------------------------------------------------------
/src/types/gitdiff-parser.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Type definitions for gitdiff-parser
2 | declare module "gitdiff-parser" {
3 | export type ChangeType = "insert" | "delete" | "normal";
4 |
5 | export interface InsertChange {
6 | type: "insert";
7 | content: string;
8 | lineNumber: number;
9 | isInsert: true;
10 | }
11 |
12 | export interface DeleteChange {
13 | type: "delete";
14 | content: string;
15 | lineNumber: number;
16 | isDelete: true;
17 | }
18 |
19 | export interface NormalChange {
20 | type: "normal";
21 | content: string;
22 | isNormal: true;
23 | oldLineNumber: number;
24 | newLineNumber: number;
25 | }
26 |
27 | export type Change = InsertChange | DeleteChange | NormalChange;
28 |
29 | export interface Hunk {
30 | content: string;
31 | oldStart: number;
32 | newStart: number;
33 | oldLines: number;
34 | newLines: number;
35 | changes: Change[];
36 | }
37 |
38 | export type FileType = "add" | "delete" | "modify" | "rename" | "copy";
39 |
40 | export interface File {
41 | hunks: Hunk[];
42 | oldEndingNewLine: boolean;
43 | newEndingNewLine: boolean;
44 | oldMode: string;
45 | newMode: string;
46 | similarity?: number;
47 | oldRevision: string;
48 | newRevision: string;
49 | oldPath: string;
50 | newPath: string;
51 | isBinary?: boolean;
52 | type: FileType;
53 | }
54 |
55 | /**
56 | * Parse a git diff string into a structured format
57 | * @param diffStr Raw git diff string
58 | * @returns Array of File objects representing the parsed diff
59 | */
60 | export function parse(diffStr: string): File[];
61 |
62 | // Export as ES module default export
63 | const _default: {
64 | parse(source: string): File[];
65 | };
66 | export default _default;
67 | }
68 |
```
--------------------------------------------------------------------------------
/tests/basic-router.test.vitest.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Using vitest globals - see vitest.config.ts globals: true
2 | import { GoogleGenAI } from "@google/genai";
3 | import { GeminiChatService } from "../src/services/gemini/GeminiChatService.js";
4 | import { RouteMessageParams } from "../src/services/GeminiService.js";
5 | import { config } from "dotenv";
6 |
7 | // Load environment variables
8 | config();
9 |
10 | // Simple test to check the router functionality
11 | describe("Basic Router Test", () => {
12 | it("should route messages correctly", async () => {
13 | // Get API key from environment
14 | const apiKey = process.env.GOOGLE_GEMINI_API_KEY;
15 |
16 | // Skip if no API key
17 | if (!apiKey) {
18 | console.log("Skipping test, no API key");
19 | return;
20 | }
21 |
22 | // Initialize Google GenAI
23 | const genAI = new GoogleGenAI({ apiKey });
24 |
25 | // Create chat service
26 | const chatService = new GeminiChatService(genAI, "gemini-1.5-pro");
27 |
28 | // Create router params
29 | const params: RouteMessageParams = {
30 | message: "What is the capital of France?",
31 | models: ["gemini-1.5-pro", "gemini-1.5-flash"],
32 | defaultModel: "gemini-1.5-pro",
33 | };
34 |
35 | try {
36 | // Call route message
37 | const result = await chatService.routeMessage(params);
38 |
39 | // Check that we got a response
40 | expect(result.response).toBeTruthy();
41 | expect(result.chosenModel).toBeTruthy();
42 |
43 | // Should be one of our models
44 | expect(
45 | ["gemini-1.5-pro", "gemini-1.5-flash"].includes(result.chosenModel)
46 | ).toBeTruthy();
47 |
48 | console.log(`Chosen model: ${result.chosenModel}`);
49 | console.log(
50 | `Response text: ${result.response.text?.substring(0, 50)}...`
51 | );
52 | } catch (error) {
53 | console.error("Router test failed:", error);
54 | throw error;
55 | }
56 | });
57 | });
58 |
```
--------------------------------------------------------------------------------
/tests/unit/tools/schemas/BaseToolSchema.test.vitest.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Using vitest globals - see vitest.config.ts globals: true
2 | import { z } from "zod";
3 | import { createToolSchema } from "../../../../src/tools/schemas/BaseToolSchema.js";
4 |
5 | /**
6 | * Tests for the BaseToolSchema module, focusing on the createToolSchema factory
7 | * function and ensuring it produces correctly structured tool schema definitions.
8 | */
9 | describe("BaseToolSchema", () => {
10 | describe("createToolSchema", () => {
11 | it("should create a valid tool schema definition", () => {
12 | const testParams = {
13 | name: z.string().min(1),
14 | count: z.number().int().positive(),
15 | isEnabled: z.boolean().optional(),
16 | };
17 |
18 | const result = createToolSchema(
19 | "testTool",
20 | "A tool for testing",
21 | testParams
22 | );
23 |
24 | expect(result).toHaveProperty("TOOL_NAME", "testTool");
25 | expect(result).toHaveProperty("TOOL_DESCRIPTION", "A tool for testing");
26 | expect(result).toHaveProperty("TOOL_PARAMS");
27 | expect(result).toHaveProperty("toolSchema");
28 | });
29 |
30 | it("should create a schema that validates correctly", () => {
31 | const testParams = {
32 | name: z.string().min(1),
33 | count: z.number().int().positive(),
34 | };
35 |
36 | const { toolSchema } = createToolSchema(
37 | "testTool",
38 | "A tool for testing",
39 | testParams
40 | );
41 |
42 | // Valid data
43 | const validData = { name: "test", count: 42 };
44 | expect(toolSchema.safeParse(validData).success).toBe(true);
45 |
46 | // Invalid data - missing required field
47 | const missingField = { name: "test" };
48 | expect(toolSchema.safeParse(missingField).success).toBe(false);
49 |
50 | // Invalid data - wrong type
51 | const wrongType = { name: "test", count: "42" };
52 | expect(toolSchema.safeParse(wrongType).success).toBe(false);
53 | });
54 | });
55 | });
56 |
```
--------------------------------------------------------------------------------
/tests/utils/error-helpers.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Error testing utilities for MCP Gemini Server tests
3 | *
4 | * This module provides helpers for testing error handling and ensuring
5 | * proper instanceof checks work consistently.
6 | */
7 |
8 | import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
9 |
10 | /**
11 | * Helper function to reliably determine if an object is an McpError
12 | *
13 | * This works around possible ESM/TypeScript instanceof issues by checking
14 | * both constructor name and presence of expected properties.
15 | *
16 | * @param obj - Object to check
17 | * @returns True if the object appears to be an McpError
18 | */
19 | export function isMcpError(obj: unknown): boolean {
20 | if (obj === null || typeof obj !== "object") {
21 | return false;
22 | }
23 |
24 | // First try the direct instanceof check
25 | const isInstanceOf = obj instanceof McpError;
26 |
27 | // If that works, great! Otherwise fall back to checking properties
28 | if (isInstanceOf) {
29 | return true;
30 | }
31 |
32 | // Manual property checks as fallback
33 | const errorLike = obj as { code?: unknown; message?: unknown };
34 | return (
35 | obj !== null &&
36 | typeof obj === "object" &&
37 | "code" in obj &&
38 | "message" in obj &&
39 | typeof errorLike.code === "string" &&
40 | typeof errorLike.message === "string" &&
41 | Object.values(ErrorCode).includes(errorLike.code as ErrorCode)
42 | );
43 | }
44 |
45 | /**
46 | * Ensures that an object is treated as an McpError instance
47 | *
48 | * If the object is not originally recognized as an McpError,
49 | * this function attempts to reconstruct it properly.
50 | *
51 | * @param obj - Object to convert
52 | * @returns Same object or a reconstructed McpError
53 | */
54 | export function ensureMcpError(obj: unknown): McpError {
55 | if (obj instanceof McpError) {
56 | return obj;
57 | }
58 |
59 | if (obj && typeof obj === "object" && "code" in obj && "message" in obj) {
60 | const errObj = obj as {
61 | code: unknown;
62 | message: unknown;
63 | details?: unknown;
64 | };
65 | return new McpError(
66 | errObj.code as ErrorCode,
67 | errObj.message as string,
68 | errObj.details
69 | );
70 | }
71 |
72 | // If all else fails, create a generic error
73 | return new McpError(
74 | ErrorCode.InternalError,
75 | typeof obj === "string" ? obj : "Unknown error"
76 | );
77 | }
78 |
```
--------------------------------------------------------------------------------
/src/utils/healthCheck.ts:
--------------------------------------------------------------------------------
```typescript
1 | import http from "http";
2 | import { logger } from "./logger.js";
3 | import type { ServerState, TestServerState } from "../types/serverTypes.js";
4 |
5 | // Re-export types for backward compatibility
6 | export type { ServerState, TestServerState };
7 |
8 | // Reference to the server state from server.ts
9 | // This will be set from server.ts
10 | let serverStateRef: ServerState | null = null;
11 |
12 | export const setServerState = (state: ServerState | TestServerState) => {
13 | // For tests with TestServerState, add mcpClientService if not provided
14 | if (!("mcpClientService" in state)) {
15 | (state as ServerState).mcpClientService = null;
16 | }
17 | serverStateRef = state as ServerState;
18 | };
19 |
20 | export const getHealthStatus = () => {
21 | if (!serverStateRef || !serverStateRef.isRunning) {
22 | return {
23 | status: "stopped",
24 | uptime: 0,
25 | };
26 | }
27 |
28 | const uptime = serverStateRef.startTime
29 | ? Math.floor((Date.now() - serverStateRef.startTime) / 1000)
30 | : 0;
31 |
32 | return {
33 | status: "running",
34 | uptime,
35 | transport: serverStateRef.transport?.constructor.name || "unknown",
36 | version: process.env.npm_package_version || "unknown",
37 | };
38 | };
39 |
40 | /**
41 | * Starts an HTTP server for health checks
42 | * This runs independently of the MCP server transport
43 | */
44 | export const startHealthCheckServer = () => {
45 | const port = parseInt(process.env.HEALTH_CHECK_PORT || "3000", 10);
46 |
47 | const server = http.createServer((req, res) => {
48 | // Simple routing
49 | if (req.url === "/health" || req.url === "/") {
50 | const health = getHealthStatus();
51 | res.writeHead(200, { "Content-Type": "application/json" });
52 | res.end(JSON.stringify(health));
53 | } else {
54 | res.writeHead(404, { "Content-Type": "application/json" });
55 | res.end(JSON.stringify({ error: "Not found" }));
56 | }
57 | });
58 |
59 | server.on("error", (error: NodeJS.ErrnoException) => {
60 | if (error.code === "EADDRINUSE") {
61 | logger.error(`Health check server port ${port} is already in use`);
62 | } else {
63 | logger.error(`Health check server error: ${error.message}`);
64 | }
65 | });
66 |
67 | server.listen(port, () => {
68 | logger.info(`Health check server listening on port ${port}`);
69 | });
70 |
71 | return server;
72 | };
73 |
```
--------------------------------------------------------------------------------
/src/services/ExampleService.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ConfigurationManager } from "../config/ConfigurationManager.js";
2 | import { ExampleServiceConfig, ExampleServiceData } from "../types/index.js";
3 | import { logger } from "../utils/index.js";
4 | import { ValidationError } from "../utils/errors.js";
5 |
6 | /**
7 | * Example service demonstrating the pattern.
8 | */
9 | export class ExampleService {
10 | private readonly config: Required<ExampleServiceConfig>;
11 |
12 | constructor(config?: Partial<ExampleServiceConfig>) {
13 | const configManager = ConfigurationManager.getInstance();
14 | const defaultConfig = configManager.getExampleServiceConfig();
15 | // Allow overriding defaults via constructor injection
16 | this.config = { ...defaultConfig, ...config };
17 |
18 | logger.info("ExampleService initialized.");
19 | if (this.config.enableDetailedLogs) {
20 | logger.debug("ExampleService Config:", this.config);
21 | }
22 | }
23 |
24 | /**
25 | * Processes example data.
26 | * @param inputData - Input data, expected to match ExampleServiceData structure partially.
27 | * @returns A promise resolving to the processed ExampleServiceData.
28 | * @throws ValidationError if input is invalid.
29 | */
30 | public async processExample(inputData: unknown): Promise<ExampleServiceData> {
31 | const startTime = Date.now();
32 |
33 | // Basic validation (could use Zod here for more robustness)
34 | const data = inputData as Partial<ExampleServiceData>;
35 | if (!data || typeof data.name !== "string" || data.name.trim() === "") {
36 | throw new ValidationError(
37 | "Invalid input: 'name' property is required and must be a non-empty string."
38 | );
39 | }
40 |
41 | const name = data.name.trim();
42 | const message = `${this.config.greeting}, ${name}!`; // Escaped PowerShell variable syntax
43 |
44 | if (this.config.enableDetailedLogs) {
45 | logger.debug(`Processing name: ${name}`);
46 | }
47 |
48 | // Simulate some async work
49 | await new Promise((resolve) => setTimeout(resolve, 20));
50 |
51 | const result: ExampleServiceData = {
52 | name: name,
53 | message: message,
54 | processedTimestamp: new Date().toISOString(),
55 | metrics: {
56 | processingTimeMs: Date.now() - startTime,
57 | },
58 | };
59 |
60 | logger.info(`Example processed for: ${name}`);
61 | return result;
62 | }
63 | }
64 |
```
--------------------------------------------------------------------------------
/src/types/micromatch.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Type definitions for micromatch
2 | declare module "micromatch" {
3 | interface MicromatchOptions {
4 | basename?: boolean;
5 | bash?: boolean;
6 | dot?: boolean;
7 | posix?: boolean;
8 | nocase?: boolean;
9 | noextglob?: boolean;
10 | nonegate?: boolean;
11 | noglobstar?: boolean;
12 | nobrace?: boolean;
13 | regex?: boolean;
14 | unescape?: boolean;
15 | contains?: boolean;
16 | matchBase?: boolean;
17 | onMatch?: (match: string) => void;
18 | onResult?: (result: string) => void;
19 | [key: string]: unknown;
20 | }
21 |
22 | interface Micromatch {
23 | (
24 | list: string[],
25 | patterns: string | string[],
26 | options?: MicromatchOptions
27 | ): string[];
28 | match(
29 | list: string[],
30 | patterns: string | string[],
31 | options?: MicromatchOptions
32 | ): string[];
33 | isMatch(
34 | str: string,
35 | patterns: string | string[],
36 | options?: MicromatchOptions
37 | ): boolean;
38 | contains(
39 | str: string,
40 | pattern: string,
41 | options?: MicromatchOptions
42 | ): boolean;
43 | matcher(
44 | pattern: string,
45 | options?: MicromatchOptions
46 | ): (str: string) => boolean;
47 | any(str: string, patterns: string[], options?: MicromatchOptions): boolean;
48 | not(
49 | list: string[],
50 | patterns: string | string[],
51 | options?: MicromatchOptions
52 | ): string[];
53 | filter(
54 | patterns: string | string[],
55 | options?: MicromatchOptions
56 | ): (str: string) => boolean;
57 | some(
58 | list: string[],
59 | patterns: string | string[],
60 | options?: MicromatchOptions
61 | ): boolean;
62 | every(
63 | list: string[],
64 | patterns: string | string[],
65 | options?: MicromatchOptions
66 | ): boolean;
67 | all(str: string, patterns: string[], options?: MicromatchOptions): boolean;
68 | capture(
69 | str: string,
70 | pattern: string,
71 | options?: MicromatchOptions
72 | ): string[] | null;
73 | test(str: string, pattern: string, options?: MicromatchOptions): boolean;
74 | matchKeys(
75 | obj: object,
76 | patterns: string | string[],
77 | options?: MicromatchOptions
78 | ): object;
79 | braces(str: string, options?: MicromatchOptions): string[];
80 | braceExpand(str: string, options?: MicromatchOptions): string[];
81 | makeRe(pattern: string, options?: MicromatchOptions): RegExp;
82 | scan(str: string, options?: MicromatchOptions): string[];
83 | parse(str: string, options?: MicromatchOptions): object;
84 | compile(str: string, options?: MicromatchOptions): (str: string) => boolean;
85 | create(str: string, options?: MicromatchOptions): object;
86 | }
87 |
88 | const micromatch: Micromatch;
89 | export = micromatch;
90 | }
91 |
```
--------------------------------------------------------------------------------
/tests/unit/services/gemini/GitHubApiService.test.vitest.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Using vitest globals - see vitest.config.ts globals: true
2 |
3 | // Mock Octokit and related modules using vi.doMock to avoid hoisting issues
4 | const mockOctokit = {
5 | rest: {
6 | repos: {
7 | getContent: vi.fn(),
8 | get: vi.fn(),
9 | },
10 | pulls: {
11 | get: vi.fn(),
12 | listFiles: vi.fn(),
13 | },
14 | },
15 | };
16 |
17 | const mockGraphql = vi.fn() as any;
18 | mockGraphql.defaults = vi.fn().mockReturnValue(mockGraphql);
19 |
20 | vi.doMock("@octokit/rest", () => ({
21 | Octokit: vi.fn().mockImplementation(() => mockOctokit),
22 | }));
23 |
24 | vi.doMock("@octokit/graphql", () => ({
25 | graphql: mockGraphql,
26 | }));
27 |
28 | vi.doMock("@octokit/request-error", () => ({
29 | RequestError: class MockRequestError extends Error {
30 | status: number;
31 | response: any;
32 | constructor(message: string, status: number, response?: any) {
33 | super(message);
34 | this.status = status;
35 | this.response = response;
36 | }
37 | },
38 | }));
39 |
40 | vi.doMock("keyv", () => ({
41 | default: vi.fn().mockImplementation(() => ({
42 | get: vi.fn(),
43 | set: vi.fn(),
44 | delete: vi.fn(),
45 | clear: vi.fn(),
46 | })),
47 | }));
48 |
49 | describe("GitHubApiService", () => {
50 | let GitHubApiService: any;
51 | let logger: any;
52 | let service: any;
53 |
54 | beforeAll(async () => {
55 | // Dynamic imports after mocks are set up
56 | const githubApiModule = await import(
57 | "../../../../src/services/gemini/GitHubApiService.js"
58 | );
59 | GitHubApiService = githubApiModule.GitHubApiService;
60 |
61 | const loggerModule = await import("../../../../src/utils/logger.js");
62 | logger = loggerModule.logger;
63 | });
64 |
65 | beforeEach(() => {
66 | vi.clearAllMocks();
67 |
68 | // Mock logger
69 | vi.spyOn(logger, "info").mockImplementation(vi.fn());
70 | vi.spyOn(logger, "warn").mockImplementation(vi.fn());
71 | vi.spyOn(logger, "error").mockImplementation(vi.fn());
72 | vi.spyOn(logger, "debug").mockImplementation(vi.fn());
73 |
74 | service = new GitHubApiService();
75 | });
76 |
77 | describe("Constructor", () => {
78 | it("should initialize GitHubApiService", () => {
79 | expect(service).toBeDefined();
80 | expect(service).toBeInstanceOf(GitHubApiService);
81 | });
82 | });
83 |
84 | describe("Basic functionality", () => {
85 | it("should have required methods", () => {
86 | expect(typeof service.getFileContent).toBe("function");
87 | expect(typeof service.getRepositoryInfoFromUrl).toBe("function");
88 | expect(typeof service.getPullRequest).toBe("function");
89 | expect(typeof service.getPullRequestFiles).toBe("function");
90 | expect(typeof service.checkRateLimit).toBe("function");
91 | expect(typeof service.listDirectory).toBe("function");
92 | expect(typeof service.getDefaultBranch).toBe("function");
93 | });
94 | });
95 | });
96 |
```
--------------------------------------------------------------------------------
/tests/unit/tools/mcpToolsTests.test.vitest.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Using vitest globals - see vitest.config.ts globals: true
2 |
3 | /*
4 | * DEPRECATED TEST FILE
5 | *
6 | * This test file was written for the old individual MCP tools:
7 | * - mcpConnectToServerTool
8 | * - mcpListServerToolsTool
9 | * - mcpCallServerTool
10 | * - mcpDisconnectFromServerTool
11 | *
12 | * These tools have been refactored and consolidated into a single mcpClientTool.
13 | *
14 | * TODO: Rewrite these tests to test the new mcpClientTool functionality
15 | * or create separate test files for the new consolidated architecture.
16 | */
17 |
18 | // Import the current tools to be tested
19 | import { writeToFileTool } from "../../../src/tools/writeToFileTool.js";
20 |
21 | // Import relevant constants/types for testing
22 | import { TOOL_NAME as WRITE_FILE_TOOL_NAME } from "../../../src/tools/schemas/writeToFileParams.js";
23 |
24 | // Mock fs/promises
25 | vi.mock("fs/promises", () => ({
26 | writeFile: vi.fn().mockImplementation(async () => undefined),
27 | access: vi.fn().mockImplementation(async () => undefined),
28 | lstat: vi.fn().mockImplementation(async (_path: string) => ({
29 | isSymbolicLink: () => false,
30 | })),
31 | realpath: vi.fn().mockImplementation(async (path: string) => path),
32 | }));
33 |
34 | // Mock FileSecurityService
35 | vi.mock("../../../src/utils/FileSecurityService.js", () => {
36 | return {
37 | FileSecurityService: vi.fn().mockImplementation(() => {
38 | return {
39 | secureWriteFile: vi.fn().mockImplementation(async () => undefined),
40 | validateAndResolvePath: vi
41 | .fn()
42 | .mockImplementation((path: string) => path),
43 | isPathWithinAllowedDirs: vi.fn().mockReturnValue(true),
44 | setAllowedDirectories: vi.fn().mockImplementation(() => undefined),
45 | };
46 | }),
47 | };
48 | });
49 |
50 | describe("MCP Tools Tests (Legacy)", () => {
51 | describe("writeToFileTool", () => {
52 | it("should be a function that registers the tool", () => {
53 | expect(typeof writeToFileTool).toBe("function");
54 | });
55 |
56 | it("should register a tool with the correct name when called", () => {
57 | const mockServer = {
58 | tool: vi.fn(),
59 | };
60 |
61 | writeToFileTool(mockServer as any);
62 |
63 | expect(mockServer.tool).toHaveBeenCalledTimes(1);
64 | const [toolName] = mockServer.tool.mock.calls[0];
65 | expect(toolName).toBe(WRITE_FILE_TOOL_NAME);
66 | });
67 | });
68 |
69 | // Note: All other MCP tool tests have been disabled because the individual tools
70 | // (mcpConnectToServerTool, mcpListServerToolsTool, mcpCallServerTool, mcpDisconnectFromServerTool)
71 | // have been refactored into a consolidated mcpClientTool.
72 | //
73 | // The new mcpClientTool should be tested separately with tests that reflect
74 | // its consolidated architecture and unified interface.
75 | });
76 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "mcp-gemini-server",
3 | "version": "0.2.0",
4 | "description": "MCP server providing Google Gemini API integration with inline data processing and content generation capabilities",
5 | "main": "dist/server.js",
6 | "bin": {
7 | "mcp-gemini-server": "dist/server.js"
8 | },
9 | "type": "module",
10 | "scripts": {
11 | "start": "node dist/server.js",
12 | "build": "tsc",
13 | "build:test": "tsc -p tsconfig.test.json",
14 | "dev": "nodemon --watch src --ext ts --exec \"node --loader ts-node/esm src/server.ts\"",
15 | "lint": "eslint . --ext .js,.ts --fix",
16 | "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
17 | "test": "vitest run",
18 | "test:unit": "vitest run --dir tests/unit",
19 | "test:watch": "vitest watch",
20 | "test:coverage": "vitest run --coverage",
21 | "test:github": "vitest run tests/unit/services/gemini/GitHubApiService.test.ts",
22 | "test:integration": "vitest run --dir tests/integration",
23 | "test:e2e": "vitest run --dir tests/e2e",
24 | "prepare": "husky",
25 | "lint:watch": "nodemon --watch src --watch tests --ext ts,js --exec \"npm run lint -- --fix\"",
26 | "format:watch": "nodemon --watch src --watch tests --ext ts,js --exec \"npm run format\"",
27 | "typecheck": "tsc --noEmit --strict && tsc --noEmit --strict -p tsconfig.test.json",
28 | "check-all": "npm run format && npm run lint && npm run typecheck"
29 | },
30 | "lint-staged": {
31 | "*.{ts,js}": [
32 | "prettier --write",
33 | "eslint --fix"
34 | ]
35 | },
36 | "keywords": [
37 | "mcp",
38 | "model-context-protocol"
39 | ],
40 | "license": "ISC",
41 | "dependencies": {
42 | "@google/genai": "^0.10.0",
43 | "@modelcontextprotocol/sdk": "^1.11.5",
44 | "@octokit/graphql": "^8.2.2",
45 | "@octokit/rest": "^21.1.1",
46 | "@types/better-sqlite3": "^7.6.13",
47 | "@types/eventsource": "^1.1.15",
48 | "@types/inquirer": "^9.0.7",
49 | "@types/uuid": "^10.0.0",
50 | "better-sqlite3": "^11.10.0",
51 | "chalk": "^5.3.0",
52 | "eventsource": "^2.0.2",
53 | "express": "^5.1.0",
54 | "gitdiff-parser": "^0.3.1",
55 | "inquirer": "^12.5.0",
56 | "keyv": "^5.3.3",
57 | "micromatch": "^4.0.8",
58 | "node-fetch": "^2.6.9",
59 | "uuid": "^11.1.0",
60 | "zod": "^3.23.8"
61 | },
62 | "devDependencies": {
63 | "@types/cors": "^2.8.18",
64 | "@types/express": "^5.0.1",
65 | "@types/node": "^20.14.2",
66 | "@types/node-fetch": "^2.6.4",
67 | "@typescript-eslint/eslint-plugin": "^7.13.0",
68 | "@typescript-eslint/parser": "^7.13.0",
69 | "@vitest/coverage-v8": "^3.1.4",
70 | "dotenv": "^16.5.0",
71 | "eslint": "^8.57.0",
72 | "eslint-config-prettier": "^9.1.0",
73 | "eslint-plugin-prettier": "^5.1.3",
74 | "husky": "^9.1.7",
75 | "lint-staged": "^15.5.2",
76 | "nodemon": "^3.1.3",
77 | "prettier": "^3.3.2",
78 | "ts-node": "^10.9.2",
79 | "typescript": "^5.4.5",
80 | "vitest": "^3.1.4"
81 | }
82 | }
83 |
```
--------------------------------------------------------------------------------
/src/utils/filePathSecurity.ts:
--------------------------------------------------------------------------------
```typescript
1 | import * as fs from "fs";
2 | import * as path from "path";
3 | import { logger } from "./logger.js";
4 | import { ValidationError } from "./errors.js";
5 |
6 | /**
7 | * Validates that a file path is secure and resolves it to an absolute path
8 | *
9 | * Security checks:
10 | * 1. Ensures the path exists
11 | * 2. Ensures the path is within the allowed base directory
12 | * 3. Prevents path traversal attacks
13 | *
14 | * @param filePath - The file path to validate
15 | * @param options - Optional configuration
16 | * @returns The validated absolute file path
17 | * @throws ValidationError if the path is invalid or insecure
18 | */
19 | export function validateAndResolvePath(
20 | filePath: string,
21 | options: {
22 | mustExist?: boolean;
23 | } = {}
24 | ): string {
25 | const { mustExist = true } = options;
26 |
27 | // Get the safe base directory from environment variable
28 | const safeBaseDir = process.env.GEMINI_SAFE_FILE_BASE_DIR
29 | ? path.normalize(process.env.GEMINI_SAFE_FILE_BASE_DIR)
30 | : path.resolve(process.cwd());
31 |
32 | logger.debug(`Validating file path: ${filePath}`);
33 | logger.debug(`Safe base directory: ${safeBaseDir}`);
34 |
35 | // Resolve the absolute path
36 | const absolutePath = path.isAbsolute(filePath)
37 | ? filePath
38 | : path.resolve(safeBaseDir, filePath);
39 |
40 | // Check if the file exists (if required)
41 | if (mustExist && !fs.existsSync(absolutePath)) {
42 | logger.warn(`File not found: ${absolutePath}`);
43 | throw new ValidationError(`File not found: ${absolutePath}`);
44 | }
45 |
46 | // Check if the path is within the safe base directory
47 | const normalizedPath = path.normalize(absolutePath);
48 | const relativePath = path.relative(safeBaseDir, normalizedPath);
49 |
50 | // Path traversal check - if the relative path starts with '..' or is absolute,
51 | // it's attempting to access files outside the safe directory
52 | if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
53 | logger.warn(`Attempted path traversal: ${filePath}`);
54 | throw new ValidationError(
55 | `Access denied: The file path must be within the allowed directory`
56 | );
57 | }
58 |
59 | logger.debug(`Validated path: ${normalizedPath}`);
60 | return normalizedPath;
61 | }
62 |
63 | /**
64 | * Environment variable configuration for file path security
65 | */
66 | export function configureFilePathSecurity(): void {
67 | // Add the environment variable to the required vars if using custom base dir
68 | const customBaseDir = process.env.GEMINI_SAFE_FILE_BASE_DIR;
69 |
70 | if (customBaseDir) {
71 | // Validate that the custom base directory exists
72 | if (!fs.existsSync(customBaseDir)) {
73 | logger.warn(
74 | `Configured GEMINI_SAFE_FILE_BASE_DIR does not exist: ${customBaseDir}`
75 | );
76 | logger.warn(`Falling back to default directory: ${process.cwd()}`);
77 | } else {
78 | logger.info(`File operations restricted to: ${customBaseDir}`);
79 | }
80 | } else {
81 | logger.info(
82 | `File operations restricted to current working directory: ${process.cwd()}`
83 | );
84 | logger.info(
85 | `Set GEMINI_SAFE_FILE_BASE_DIR environment variable to customize this.`
86 | );
87 | }
88 | }
89 |
```