MCP (Model Context Protocol) Integration - gunpal5/Google_GenerativeAI GitHub Wiki

Prerequisites

Install the GenerativeAI.Tools NuGet package:

dotnet add package Google_GenerativeAI.Tools

What is MCP?

Model Context Protocol (MCP) is a standardized protocol for connecting AI models to external tools, data sources, and services. It provides a universal way for AI assistants to:

  • Access external APIs and services
  • Read and manipulate files
  • Query databases
  • Interact with custom business logic
  • Connect to third-party integrations

Quick Start

1. Connect to an MCP Server

MCP supports three transport protocols:

Stdio Transport (Subprocess)

Launch an MCP server as a subprocess:

using GenerativeAI.Tools.Mcp;

var transport = McpTransportFactory.CreateStdioTransport(
    name: "my-server",
    command: "npx",
    arguments: new[] { "-y", "@modelcontextprotocol/server-filesystem", "/path/to/files" }
);

var mcpTool = await McpTool.CreateAsync(transport);

HTTP/SSE Transport

Connect to an MCP server over HTTP with Server-Sent Events:

var transport = McpTransportFactory.CreateHttpTransport(
    baseUrl: "http://localhost:8080"
);

var mcpTool = await McpTool.CreateAsync(transport);

HTTP with Authentication

Include authentication tokens:

var transport = McpTransportFactory.CreateHttpTransportWithAuth(
    baseUrl: "https://api.example.com/mcp",
    authToken: "your-api-key"
);

var mcpTool = await McpTool.CreateAsync(transport);

2. Integrate with Gemini

Once connected, add the MCP tool to your Gemini model:

var model = new GenerativeModel(
    apiKey: "your-api-key",
    model: GoogleAIModels.Gemini2Flash
);

model.AddFunctionTool(mcpTool);

var response = await model.GenerateContentAsync(
    "List the files in the current directory"
);

Gemini will automatically discover available MCP tools, call them when needed, and incorporate the results into its responses.

Configuration Options

McpToolOptions

Configure connection behavior and error handling:

var options = new McpToolOptions
{
    ConnectionTimeoutMs = 30000,      // Connection timeout
    AutoReconnect = true,              // Auto-reconnect on failure
    MaxReconnectAttempts = 3,          // Maximum reconnection attempts
    ThrowOnToolCallFailure = false,    // Throw exceptions on errors
    IncludeDetailedErrors = true       // Include error details in responses
};

var mcpTool = await McpTool.CreateAsync(transport, options);

Transport Factory Functions

For auto-reconnection support, use a transport factory:

var mcpTool = await McpTool.CreateAsync(
    transportFactory: () => McpTransportFactory.CreateStdioTransport(
        "server-name",
        "npx",
        new[] { "-y", "@modelcontextprotocol/server-everything" }
    ),
    options: new McpToolOptions { AutoReconnect = true }
);

Available MCP Servers

Official MCP Servers

  • @modelcontextprotocol/server-filesystem - File system operations
  • @modelcontextprotocol/server-github - GitHub integration
  • @modelcontextprotocol/server-google-drive - Google Drive access
  • @modelcontextprotocol/server-slack - Slack integration
  • @modelcontextprotocol/server-postgres - PostgreSQL database
  • @modelcontextprotocol/server-everything - Demo server with multiple tools

Example: Filesystem Server

var transport = McpTransportFactory.CreateStdioTransport(
    name: "filesystem",
    command: "npx",
    arguments: new[] {
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/documents"  // Root directory
    }
);

var mcpTool = await McpTool.CreateAsync(transport);
var model = new GenerativeModel("your-api-key", GoogleAIModels.Gemini2Flash);
model.AddFunctionTool(mcpTool);

// Gemini can now read, write, and search files
var response = await model.GenerateContentAsync(
    "Find all Python files in the current directory and summarize their contents"
);

Advanced Usage

Discovering Available Tools

Inspect what tools an MCP server provides:

var mcpTool = await McpTool.CreateAsync(transport);

// Get list of available function names
var functionNames = mcpTool.GetAvailableFunctions();

foreach (var name in functionNames)
{
    var info = mcpTool.GetFunctionInfo(name);
    Console.WriteLine($"Tool: {name}");
    Console.WriteLine($"Description: {info.Description}");
    Console.WriteLine($"Parameters: {info.ParametersJsonSchema}");
}

Manual Tool Calls

Call MCP tools directly without Gemini:

var functionCall = new FunctionCall
{
    Name = "echo",
    Args = new JsonObject
    {
        ["message"] = "Hello, MCP!"
    }
};

var response = await mcpTool.CallAsync(functionCall);
Console.WriteLine(response.Response.ToJsonString());

Multiple MCP Servers

Connect to multiple MCP servers simultaneously:

var transports = new List<IClientTransport>
{
    McpTransportFactory.CreateStdioTransport("filesystem", "npx",
        new[] { "-y", "@modelcontextprotocol/server-filesystem", "/path" }),
    McpTransportFactory.CreateStdioTransport("github", "npx",
        new[] { "-y", "@modelcontextprotocol/server-github" })
};

var mcpTools = await McpTool.CreateMultipleAsync(transports);

var model = new GenerativeModel("your-api-key", GoogleAIModels.Gemini2Flash);

foreach (var tool in mcpTools)
{
    model.AddFunctionTool(tool);
}

// Gemini can now use tools from both servers
var response = await model.GenerateContentAsync(
    "Read the README.md file and create a GitHub issue summarizing it"
);

// Cleanup
foreach (var tool in mcpTools)
{
    await tool.DisposeAsync();
}

Chat Sessions with MCP

Use MCP tools in multi-turn conversations:

var model = new GenerativeModel("your-api-key", GoogleAIModels.Gemini2Flash);
model.AddFunctionTool(mcpTool);

var chat = model.StartChat();

var response1 = await chat.GenerateContentAsync(
    "List the files in the current directory"
);
Console.WriteLine(response1.Text());

var response2 = await chat.GenerateContentAsync(
    "Read the contents of the largest file you found"
);
Console.WriteLine(response2.Text());

// Chat maintains context across tool calls

Refreshing Tools

Reload tools from the MCP server if they change:

await mcpTool.RefreshToolsAsync();

Custom HTTP Headers

Add custom headers for authentication or configuration:

var headers = new Dictionary<string, string>
{
    { "X-API-Key", "your-api-key" },
    { "X-Custom-Header", "custom-value" }
};

var transport = McpTransportFactory.CreateHttpTransportWithHeaders(
    baseUrl: "https://api.example.com/mcp",
    headers: headers
);

Function Calling Behavior

Control how Gemini interacts with MCP tools:

var model = new GenerativeModel("your-api-key", GoogleAIModels.Gemini2Flash);
model.AddFunctionTool(mcpTool);

// Automatic function calling (default)
model.FunctionCallingBehaviour.AutoCallFunction = true;
model.FunctionCallingBehaviour.AutoReplyFunction = true;

var response = await model.GenerateContentAsync(
    "What's the weather in San Francisco?"
);
// Gemini automatically calls weather tool and includes result
// Manual function calling
model.FunctionCallingBehaviour.AutoCallFunction = false;

var response = await model.GenerateContentAsync(
    "What's the weather in San Francisco?"
);

// Check if Gemini wants to call a function
if (response.Candidates[0].Content.Parts.Any(p => p.FunctionCall != null))
{
    var functionCall = response.Candidates[0].Content.Parts
        .First(p => p.FunctionCall != null).FunctionCall;

    // Call manually
    var result = await mcpTool.CallAsync(functionCall);

    // Continue conversation with result
    var finalResponse = await model.GenerateContentAsync(
        new GenerateContentRequest
        {
            Contents = new List<Content>
            {
                response.Candidates[0].Content,
                new Content
                {
                    Role = "function",
                    Parts = new List<Part> { new Part { FunctionResponse = result } }
                }
            }
        }
    );
}

Error Handling

Graceful Error Handling

var options = new McpToolOptions
{
    ThrowOnToolCallFailure = false,
    IncludeDetailedErrors = true
};

var mcpTool = await McpTool.CreateAsync(transport, options);

try
{
    var response = await mcpTool.CallAsync(new FunctionCall
    {
        Name = "invalid-tool",
        Args = new JsonObject()
    });
    // Response will include error details instead of throwing
}
catch (Exception ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}

Connection Error Handling

var options = new McpToolOptions
{
    AutoReconnect = true,
    MaxReconnectAttempts = 5,
    ConnectionTimeoutMs = 60000
};

try
{
    var mcpTool = await McpTool.CreateAsync(transport, options);
}
catch (TimeoutException)
{
    Console.WriteLine("Connection timeout - server may not be running");
}
catch (Exception ex)
{
    Console.WriteLine($"Failed to connect: {ex.Message}");
}

Best Practices

1. Resource Management

Always dispose MCP tools to free resources:

await using var mcpTool = await McpTool.CreateAsync(transport);
// Tool automatically disposed at end of scope

Or manually:

var mcpTool = await McpTool.CreateAsync(transport);
try
{
    // Use mcpTool
}
finally
{
    await mcpTool.DisposeAsync();
}

2. Connection Timeouts

Set appropriate timeouts for your use case:

var options = new McpToolOptions
{
    ConnectionTimeoutMs = 30000,  // 30 seconds for network servers
    // or
    ConnectionTimeoutMs = 5000    // 5 seconds for local servers
};

3. Error Context

Enable detailed errors during development:

var options = new McpToolOptions
{
    IncludeDetailedErrors = true,  // Development
    // IncludeDetailedErrors = false  // Production
};

4. Server Selection

Choose the right MCP server for your needs:

  • Local servers (stdio): Faster, more secure, better for file operations
  • Remote servers (HTTP): Better for APIs, shared services, cloud deployments

5. Tool Discovery

Let Gemini discover tools automatically rather than hard-coding tool names:

// Good - Gemini discovers and uses available tools
var response = await model.GenerateContentAsync(
    "Help me with this task using whatever tools you have"
);

// Less flexible - Hard-coded tool usage
var response = await model.GenerateContentAsync(
    "Use the echo tool to say hello"
);

Complete Example

Here's a full example integrating an MCP filesystem server with Gemini:

using GenerativeAI;
using GenerativeAI.Tools.Mcp;
using GenerativeAI.Types;

// Configure MCP connection
var transport = McpTransportFactory.CreateStdioTransport(
    name: "filesystem",
    command: "npx",
    arguments: new[] {
        "-y",
        "@modelcontextprotocol/server-filesystem",
        Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
    }
);

var options = new McpToolOptions
{
    ConnectionTimeoutMs = 30000,
    AutoReconnect = true,
    IncludeDetailedErrors = true
};

await using var mcpTool = await McpTool.CreateAsync(transport, options);

// Inspect available tools
Console.WriteLine("Available tools:");
foreach (var toolName in mcpTool.GetAvailableFunctions())
{
    var info = mcpTool.GetFunctionInfo(toolName);
    Console.WriteLine($"  - {toolName}: {info.Description}");
}

// Create Gemini model
var model = new GenerativeModel(
    apiKey: Environment.GetEnvironmentVariable("GOOGLE_API_KEY"),
    model: GoogleAIModels.Gemini2Flash
);

// Add MCP tools
model.AddFunctionTool(mcpTool);

// Configure function calling
model.FunctionCallingBehaviour.AutoCallFunction = true;
model.FunctionCallingBehaviour.AutoReplyFunction = true;

// Use in conversation
var chat = model.StartChat();

var response1 = await chat.GenerateContentAsync(
    "What files are in my Documents folder? Focus on text files."
);
Console.WriteLine($"Gemini: {response1.Text()}\n");

var response2 = await chat.GenerateContentAsync(
    "Can you create a summary of the README.md file if it exists?"
);
Console.WriteLine($"Gemini: {response2.Text()}\n");

var response3 = await chat.GenerateContentAsync(
    "Create a new file called 'summary.txt' with your summary"
);
Console.WriteLine($"Gemini: {response3.Text()}");

Troubleshooting

Server Not Starting

Problem: MCP server fails to launch

Error: Failed to connect to MCP server

Solutions:

  • Verify the command is correct: npx -y @modelcontextprotocol/server-name
  • Check if Node.js is installed: node --version
  • Try running the server manually first
  • Increase connection timeout

Tools Not Discovered

Problem: GetAvailableFunctions() returns empty list

Solutions:

  • Wait for connection to establish fully
  • Check server logs for errors
  • Verify server supports the protocol version
  • Try RefreshToolsAsync()

Function Calls Failing

Problem: Tool calls return errors

Solutions:

  • Enable detailed errors: IncludeDetailedErrors = true
  • Check parameter schemas with GetFunctionInfo()
  • Verify parameter names and types match exactly
  • Test tool call manually with CallAsync()

Connection Drops

Problem: Connection lost during operation

Solutions:

  • Enable auto-reconnect: AutoReconnect = true
  • Increase MaxReconnectAttempts
  • Use transport factory for better reconnection
  • Check server stability

Additional Resources

⚠️ **GitHub.com Fallback** ⚠️