MCP (Model Context Protocol) Integration - gunpal5/Google_GenerativeAI GitHub Wiki
Install the GenerativeAI.Tools NuGet package:
dotnet add package Google_GenerativeAI.ToolsModel 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
MCP supports three transport protocols:
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);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);Include authentication tokens:
var transport = McpTransportFactory.CreateHttpTransportWithAuth(
baseUrl: "https://api.example.com/mcp",
authToken: "your-api-key"
);
var mcpTool = await McpTool.CreateAsync(transport);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.
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);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 }
);- @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
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"
);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}");
}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());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();
}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 callsReload tools from the MCP server if they change:
await mcpTool.RefreshToolsAsync();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
);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 } }
}
}
}
);
}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}");
}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}");
}Always dispose MCP tools to free resources:
await using var mcpTool = await McpTool.CreateAsync(transport);
// Tool automatically disposed at end of scopeOr manually:
var mcpTool = await McpTool.CreateAsync(transport);
try
{
// Use mcpTool
}
finally
{
await mcpTool.DisposeAsync();
}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
};Enable detailed errors during development:
var options = new McpToolOptions
{
IncludeDetailedErrors = true, // Development
// IncludeDetailedErrors = false // Production
};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
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"
);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()}");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
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()
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()
Problem: Connection lost during operation
Solutions:
- Enable auto-reconnect:
AutoReconnect = true - Increase
MaxReconnectAttempts - Use transport factory for better reconnection
- Check server stability