Mcp.Gateway.Client
Robust .NET client library for the Model Context Protocol (MCP). Connect to MCP servers via HTTP, SSE, WebSocket, and stdio.
Ask AI about Mcp.Gateway.Client
Powered by Claude Β· Grounded in docs
I know everything about Mcp.Gateway.Client. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
π Mcp.Gateway.Tools
Build MCP servers in .NET 10 β production-ready in minutes
Mcp.Gateway.Tools is a .NET library for building Model Context Protocol (MCP) servers. It makes it easy to expose C# code as tools that can be discovered and invoked by clients like GitHub Copilot and Claude Desktop.
The main product in this repo is Mcp.Gateway.Tools.
DevTestServer and Mcp.Gateway.Tests are used for development and verification only β they are not part of the product.
Complete documentation: https://eyjolfurgudnivatne.github.io/mcp.gateway/
β‘ Getting started
1. Install the package
dotnet new web -n MyMcpServer
cd MyMcpServer
dotnet add package Mcp.Gateway.Tools
2. Configure the server (Program.cs)
Minimal HTTP + WebSocket server (v1.7.0 with MCP 2025-11-25 support):
using Mcp.Gateway.Tools;
var builder = WebApplication.CreateBuilder(args);
// Register ToolService + ToolInvoker + Session Management (v1.7.0)
builder.AddToolsService();
var app = builder.Build();
// stdio mode for GitHub Copilot (optional)
if (args.Contains("--stdio"))
{
await ToolInvoker.RunStdioModeAsync(app.Services);
return;
}
// WebSockets for streaming
app.UseWebSockets();
// MCP 2025-11-25 Streamable HTTP (v1.7.0 - RECOMMENDED)
app.UseProtocolVersionValidation(); // Protocol version validation
app.MapStreamableHttpEndpoint("/mcp"); // Unified endpoint (POST + GET + DELETE)
// Legacy endpoints (still work, deprecated)
app.MapHttpRpcEndpoint("/rpc"); // HTTP POST only (deprecated)
app.MapWsRpcEndpoint("/ws"); // WebSocket (keep for binary streaming)
app.MapSseRpcEndpoint("/sse"); // SSE only (deprecated, use /mcp GET instead)
app.Run();
See DevTestServer/Program.cs for a more complete setup with health endpoint and stdio logging.
3. Create your first tool
3.1. Simplest Tool (Auto-generated Schema)
The easiest way to create a tool using strongly-typed parameters and automatic schema generation:
using Mcp.Gateway.Tools;
public class MyTools
{
[McpTool("greet")]
public TypedJsonRpc<GreetResponse> Greet(TypedJsonRpc<GreetParams> request)
{
var name = request.Params.Name;
return TypedJsonRpc<GreetResponse>.Success(
request.Id,
new GreetResponse($"Hello, {name}!"));
}
}
public record GreetParams(string Name);
public record GreetResponse(string Message);
Benefits:
- β
No manual JSON Schema - automatically generated from
GreetParams(Input) andGreetResponse(Output) - β Strongly-typed - IntelliSense and compile-time safety
- β Clean code - easy to read and maintain
3.2. Advanced Tool (Custom Schema)
For complex validation or when you need full control over the JSON Schema:
using Mcp.Gateway.Tools;
public class MyTools
{
[McpTool("greet",
Title = "Greet user",
Description = "Greets a user by name with custom validation.",
InputSchema = @"{
""type"":""object"",
""properties"":
{
""name"":{
""type"":""string"",
""description"":""Name of the user"",
""minLength"": 2,
""maxLength"": 50
}
},
""required"": [""name""]
}")]
public JsonRpcMessage Greet(JsonRpcMessage message)
{
var name = message.GetParams().GetProperty("name").GetString();
return ToolResponse.Success(
message.Id,
new { message = $"Hello, {name}!" });
}
}
When to use:
- β Custom validation rules (minLength, maxLength, pattern, etc.)
- β Complex schema features not supported by auto-generation
- β Full control over JSON Schema
More complete tool examples:
Examples/CalculatorMcpServer/Tools/CalculatorTools.csExamples/DateTimeMcpServer/Tools/DateTimeTools.csDevTestServer/Tools/Calculator.cs(with DI)
π Connect from MCP clients
GitHub Copilot (VS Code / Visual Studio)
Create or update .mcp.json for Copilot (global or per workspace):
{
"mcpServers": {
"my_server": {
"command": "dotnet",
"args": [
"run",
"--project",
"C:\\path\\to\\MyMcpServer",
"--",
"--stdio"
]
}
}
}
Then in Copilot Chat:
@my_server call greet with name = "Alice"
Claude Desktop
Point Claude to your HTTP endpoint:
{
"mcpServers": {
"my_server": {
"transport": "http",
"url": "https://your-server.example.com/rpc"
}
}
}
Claude can also use WebSocket (/ws) for full duplex and binary streaming.
π Pagination (v1.6.0)
When you have many tools, prompts, or resources, use cursor-based pagination:
Request with pagination
{
"jsonrpc": "2.0",
"method": "tools/list",
"params": {
"cursor": "eyJvZmZzZXQiOjEwMH0=",
"pageSize": 50
},
"id": 1
}
Response with nextCursor
{
"jsonrpc": "2.0",
"result": {
"tools": [ /* 50 tools */ ],
"nextCursor": "eyJvZmZzZXQiOjE1MH0="
},
"id": 1
}
Features:
- β Optional parameters β works without pagination by default
- β
Base64-encoded cursor format:
{"offset": 100} - β Default page size: 100 items
- β
Works with
tools/list,prompts/list,resources/list - β Alphabetically sorted for consistent results
See Examples/PaginationMcpServer for a demo with 120+ tools.
π Notifications (v1.7.0 - MCP 2025-11-25 Compliant!)
Get real-time updates when tools, prompts, or resources change via SSE (Server-Sent Events):
Server sends notification via SSE
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed",
"params": {}
}
Client receives notification via SSE stream
GET /mcp HTTP/1.1
Accept: text/event-stream
MCP-Session-Id: abc123
MCP-Protocol-Version: 2025-11-25
HTTP/1.1 200 OK
Content-Type: text/event-stream
id: 1
event: message
data: {"jsonrpc":"2.0","method":"notifications/tools/list_changed","params":{}}
id: 2
event: message
data: {"jsonrpc":"2.0","method":"notifications/prompts/list_changed","params":{}}
Notification types:
notifications/tools/list_changedβ Tools added, removed, or modifiednotifications/prompts/list_changedβ Prompts updatednotifications/resources/updatedβ Resources changed (optionaluriparameter)
How to send notifications:
public class MyTools(INotificationSender notificationSender)
{
[McpTool("reload_tools")]
public async Task<JsonRpcMessage> ReloadTools(JsonRpcMessage request)
{
// Your tool logic...
// Notify all active SSE streams (v1.7.0)
await notificationSender.SendNotificationAsync(
NotificationMessage.ToolsChanged());
return ToolResponse.Success(request.Id, new { reloaded = true });
}
}
Features (v1.7.0):
- β SSE-based notifications (MCP 2025-11-25 compliant)
- β Message buffering per session (100 messages)
- β
Last-Event-IDresumption on reconnect - β Automatic broadcast to all active sessions
- β WebSocket notifications still work (deprecated)
Migration from v1.6.x:
// v1.6.x - WebSocket only (still works!):
notificationService.AddSubscriber(webSocket);
// v1.7.0 - SSE (recommended, automatic):
// Client opens: GET /mcp with MCP-Session-Id header
// Server automatically broadcasts via SSE to all sessions
// No code changes needed!
See Examples/NotificationMcpServer for a demo with manual notification triggers.
π Lifecycle Hooks (v1.8.0)
Monitor and track tool invocations with Lifecycle Hooks - perfect for metrics, logging, authorization, and production monitoring:
Built-in Hooks
LoggingToolLifecycleHook - Simple logging integration:
var builder = WebApplication.CreateBuilder(args);
builder.AddToolsService();
builder.AddToolLifecycleHook<LoggingToolLifecycleHook>(); // Log all invocations
var app = builder.Build();
MetricsToolLifecycleHook - In-memory metrics tracking:
builder.AddToolLifecycleHook<MetricsToolLifecycleHook>();
// Expose metrics via HTTP endpoint
app.MapGet("/metrics", (IEnumerable<IToolLifecycleHook> hooks) =>
{
var metricsHook = hooks.OfType<MetricsToolLifecycleHook>().FirstOrDefault();
var metrics = metricsHook?.GetMetrics();
return Results.Json(new
{
timestamp = DateTime.UtcNow,
metrics = metrics?.Select(kvp => new
{
tool = kvp.Key,
invocations = kvp.Value.InvocationCount,
successes = kvp.Value.SuccessCount,
failures = kvp.Value.FailureCount,
successRate = Math.Round(kvp.Value.SuccessRate * 100, 2),
avgDuration = Math.Round(kvp.Value.AverageDuration.TotalMilliseconds, 2)
})
});
});
Authorization Hooks
Implement role-based authorization using lifecycle hooks:
using Mcp.Gateway.Tools.Lifecycle;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class RequireRoleAttribute : Attribute
{
public string Role { get; }
public RequireRoleAttribute(string role) => Role = role;
}
public class AuthorizationHook : IToolLifecycleHook
{
private readonly IHttpContextAccessor _httpContextAccessor;
public Task OnToolInvokingAsync(string toolName, JsonRpcMessage request)
{
var httpContext = _httpContextAccessor.HttpContext;
var requiredRoles = GetRequiredRoles(toolName);
var userRoles = httpContext?.Items["UserRoles"] as List<string>;
if (!HasRequiredRole(requiredRoles, userRoles))
{
throw new ToolInvalidParamsException(
$"Insufficient permissions to invoke '{toolName}'.",
toolName);
}
return Task.CompletedTask;
}
// ... other methods
}
// Usage on tools:
[McpTool("delete_user")]
[RequireRole("Admin")]
public JsonRpcMessage DeleteUser(JsonRpcMessage request) { /* ... */ }
// Register authorization hook:
builder.AddToolLifecycleHook<AuthorizationHook>();
See docs/Authorization.md for complete authorization guide and production patterns.
Custom Hooks
Implement IToolLifecycleHook for custom monitoring:
using Mcp.Gateway.Tools.Lifecycle;
public class PrometheusHook : IToolLifecycleHook
{
private readonly Counter _invocations;
private readonly Histogram _duration;
public PrometheusHook()
{
_invocations = Metrics.CreateCounter(
"mcp_tool_invocations_total",
"Total tool invocations",
new CounterConfiguration { LabelNames = new[] { "tool", "status" } });
_duration = Metrics.CreateHistogram(
"mcp_tool_duration_seconds",
"Tool execution duration",
new HistogramConfiguration { LabelNames = new[] { "tool" } });
}
public Task OnToolInvokingAsync(string toolName, JsonRpcMessage request)
{
_invocations.WithLabels(toolName, "started").Inc();
return Task.CompletedTask;
}
public Task OnToolCompletedAsync(string toolName, JsonRpcMessage response, TimeSpan duration)
{
_invocations.WithLabels(toolName, "success").Inc();
_duration.WithLabels(toolName).Observe(duration.TotalSeconds);
return Task.CompletedTask;
}
public Task OnToolFailedAsync(string toolName, Exception error, TimeSpan duration)
{
_invocations.WithLabels(toolName, "failure").Inc();
return Task.CompletedTask;
}
}
// Register custom hook
builder.AddToolLifecycleHook<PrometheusHook>();
Tracked Metrics
Per tool, MetricsToolLifecycleHook tracks:
- Invocation count - Total calls (success + failures)
- Success/Failure count - Breakdown by outcome
- Success rate - Percentage of successful invocations
- Duration - Min, Max, Average execution time
- Error types - Count of errors by exception type
Example Output
{
"timestamp": "2025-12-19T16:30:00Z",
"metrics": [
{
"tool": "add_numbers",
"invocations": 150,
"successes": 150,
"failures": 0,
"successRate": 100.0,
"avgDuration": 1.23
},
{
"tool": "divide",
"invocations": 50,
"successes": 48,
"failures": 2,
"successRate": 96.0,
"avgDuration": 1.15
}
]
}
Features:
- β Fire-and-forget - Hooks don't block tool execution
- β
Exception-safe - Hook errors are logged, not propagated (except
ToolInvalidParamsExceptionfor authorization) - β Multiple hooks - Register as many as needed
- β Zero config - Optional, backward compatible
- β Production-ready - Thread-safe metrics tracking
- β Authorization support - Use for role-based access control
See:
Examples/MetricsMcpServer- Metrics endpoint demoExamples/AuthorizationMcpServer- Role-based authorizationdocs/LifecycleHooks.md- Complete API referencedocs/Authorization.md- Authorization patterns and best practices
π¦ Resource Subscriptions (v1.8.0)
Subscribe to specific resources for targeted notifications - reduces bandwidth and improves performance for high-frequency updates:
Subscribe to Resources
// Client subscribes to a specific resource URI
{
"jsonrpc": "2.0",
"method": "resources/subscribe",
"params": {
"uri": "file://data/users.json"
},
"id": 1
}
// Server confirms subscription
{
"jsonrpc": "2.0",
"result": {
"subscribed": true,
"uri": "file://data/users.json"
},
"id": 1
}
Filtered Notifications
Only subscribers receive notifications for their subscribed resources:
// Server notifies ONLY sessions subscribed to this URI
await notificationSender.SendNotificationAsync(
NotificationMessage.ResourcesUpdated("file://data/users.json"));
// Notification sent ONLY to subscribed sessions
{
"jsonrpc": "2.0",
"method": "notifications/resources/updated",
"params": {
"uri": "file://data/users.json"
}
}
Unsubscribe
{
"jsonrpc": "2.0",
"method": "resources/unsubscribe",
"params": {
"uri": "file://data/users.json"
},
"id": 2
}
Features:
- β Exact URI matching - Subscribe to specific resources only (no wildcards in v1.8.0)
- β Session-based - Subscriptions tied to MCP sessions
- β Automatic cleanup - Subscriptions cleared on session expiry/deletion
- β Notification filtering - Server only sends to subscribed sessions
- β
Requires session management - Must use
/mcpendpoint withMCP-Session-Id
When to use:
- β High-frequency resource updates (e.g., live metrics)
- β Many resources, few clients interested in each
- β Reducing notification bandwidth
Limitations (v1.8.0):
- β Exact URI matching only (wildcards planned for v1.9.0)
- β Requires session management (not available on
/rpcendpoint)
See Examples/ResourceMcpServer/README.md for complete subscription workflow and examples.
π‘ Features
- β MCP 2025β11β25 β 100% compliant with latest MCP specification (v1.7.0)
- β
Transports
- Unified /mcp endpoint (v1.7.0): POST + GET + DELETE for MCP 2025-11-25 compliance
- HTTP (
/rpc), WebSocket (/ws), SSE (/sse), stdio - Legacy endpoints still work (deprecated)
- β
Session Management (v1.7.0)
MCP-Session-Idheader support- Configurable timeout (30 min default)
- Session-scoped event IDs
- β
Protocol Version Validation (v1.7.0)
MCP-Protocol-Versionheader validation- Supports 2025-11-25, 2025-06-18, 2025-03-26
- β Autoβdiscovery β tools, prompts, and resources discovered via attributes
- β
Transportβaware filtering (v1.2.0)
- HTTP/stdio: standard tools only
- SSE: standard + text streaming
- WebSocket: all tools (incl. binary streaming)
- β
Typed tools & optional schema generation (v1.3.0)
TypedJsonRpc<T>helper for strongly-typed tool implementations- Optional JSON Schema auto-generation when
InputSchemais omitted and the tool usesTypedJsonRpc<T>
- β
MCP Prompts (v1.4.0)
[McpPrompt]attribute and prompt models for defining reusable prompt templates- MCP prompt protocol support:
prompts/list,prompts/get, and prompt capabilities ininitialize
- β
MCP Resources (v1.5.0)
[McpResource]attribute for exposing data and content (files, databases, APIs, system metrics)- MCP resource protocol support:
resources/list,resources/read, and resource capabilities ininitialize - URI-based addressing:
file://,db://,system://,http://
- β
Cursor-based pagination (v1.6.0)
- Optional
cursorandpageSizeparameters fortools/list,prompts/list,resources/list nextCursorin response when more results are available- Default page size: 100 items (configurable)
- Alphabetically sorted results for consistent pagination
- Optional
- β
SSE-based Notifications (v1.7.0 - MCP 2025-11-25 compliant!)
- Real-time updates via Server-Sent Events
notifications/tools/list_changed,notifications/prompts/list_changed,notifications/resources/updated- Message buffering (100 messages per session)
Last-Event-IDresumption on reconnect- Automatic broadcast to all active sessions
NotificationServicewith thread-safe subscriber management- WebSocket notifications still work (deprecated)
- β
Lifecycle Hooks (v1.8.0)
- Monitor tool invocations with
IToolLifecycleHook - Built-in hooks:
LoggingToolLifecycleHook,MetricsToolLifecycleHook - Track invocation count, success rate, duration, errors
- Fire-and-forget pattern (exception-safe)
- Production-ready metrics for Prometheus, Application Insights
- Monitor tool invocations with
- β
Resource Subscriptions (v1.8.0)
- Optional MCP 2025-11-25 feature
resources/subscribeandresources/unsubscribemethods- Notification filtering by subscribed URI
- Session-based with automatic cleanup
- Exact URI matching (wildcards in v1.9.0)
- β
Streaming β text and binary streaming via
ToolConnector - β DI support β tools, prompts, and resources can take services as parameters
- β Tested β 273 tests covering HTTP, WS, SSE and stdio
π Learn more
π Documentation
Complete documentation available at: https://eyjolfurgudnivatne.github.io/mcp.gateway/
- π€ AI Assistant Quickstart - Quick reference for AI assistants (GitHub Copilot, Claude, ChatGPT)
- Getting Started - Installation and first tool
- API Reference - Complete API documentation
- Examples - Working examples with explanations
- Features - Advanced features guide
π In this repository
- Library README:
Mcp.Gateway.Tools/README.md
Details for the tools API (attributes, JsonRpc models, etc.) - Documentation:
docs/MCP-Protocol.md- MCP protocol specificationdocs/StreamingProtocol.md- Streaming protocol detailsdocs/JSON-RPC-2.0-spec.md- JSON-RPC 2.0 specificationdocs/LifecycleHooks.md- Lifecycle hooks API reference (v1.8.0)docs/Authorization.md- Authorization patterns and best practices (v1.8.0)
- Examples:
Examples/CalculatorMcpServerβ calculator serverExamples/DateTimeMcpServerβ date/time toolsExamples/PromptMcpServerβ prompt templatesExamples/ResourceMcpServerβ file, system, and database resourcesExamples/PaginationMcpServerβ pagination with 120+ mock tools (v1.6.0)Examples/NotificationMcpServerβ WebSocket notifications demo (v1.6.0)Examples/MetricsMcpServerβ lifecycle hooks with metrics endpoint (v1.8.0)Examples/AuthorizationMcpServerβ role-based authorization (v1.8.0)
π§ͺ Testing
dotnet test
The tests use DevTestServer as the host for endβtoβend scenarios:
- HTTP:
Mcp.Gateway.Tests/Endpoints/Http/* - WebSocket:
Mcp.Gateway.Tests/Endpoints/Ws/* - SSE:
Mcp.Gateway.Tests/Endpoints/Sse/* - stdio:
Mcp.Gateway.Tests/Endpoints/Stdio/*
π¦ Projects in this repo
| Project | Description |
|---|---|
Mcp.Gateway.Tools | Core library / published NuGet package |
DevTestServer | Internal test server used by the tests |
Mcp.Gateway.Tests | Test project (70+ integration tests) |
Examples/* | Small focused sample servers (calculator, datetime) |
Only Mcp.Gateway.Tools is intended as a product / NuGet dependency. The other projects are for development, verification and examples.
π€ Contributing
See CONTRIBUTING.md for guidelines, code style and test requirements.
π License
MIT Β© 2024β2025 ARKo AS β AHelse Development Team
