DateTime Server Example
A complete MCP server for date and time operations with timezone support.
Overview
The DateTime server demonstrates:
- ✅ Current date/time - Multiple timezones
- ✅ ISO 8601 timestamps - Standard format
- ✅ Date arithmetic - Add/subtract days
- ✅ Weekend checking - Saturday/Sunday detection
- ✅ Week numbers - ISO 8601 week of year
- ✅ Timezone handling - TimeZoneInfo integration
- ✅ **TypedJsonRpc
** - Type-safe parameter handling - ✅ Auto-generated tool names - From method names
Complete Code
Program.cs
using Mcp.Gateway.Tools;
var builder = WebApplication.CreateBuilder(args);
// Register MCP Gateway
builder.AddToolsService();
var app = builder.Build();
// stdio mode for GitHub Copilot
if (args.Contains("--stdio"))
{
await ToolInvoker.RunStdioModeAsync(app.Services);
return;
}
// HTTP mode
app.UseWebSockets();
app.UseProtocolVersionValidation();
app.MapStreamableHttpEndpoint("/mcp");
app.Run();
DateTimeTools.cs
using Mcp.Gateway.Tools;
using DateTimeMcpServer.Models;
namespace DateTimeMcpServer.Tools;
public class DateTimeTools
{
[McpTool(Title = "Get current date and time",
Description = "Get current date and time in specified timezone (default: local).")]
public JsonRpcMessage GetCurrentDatetime(TypedJsonRpc<CurrentDateTimeRequest> message)
{
var request = message.GetParams();
TimeZoneInfo tz;
try
{
tz = string.IsNullOrWhiteSpace(request?.TimezoneName)
? TimeZoneInfo.Local
: TimeZoneInfo.FindSystemTimeZoneById(request.TimezoneName);
}
catch
{
// Fallback to local timezone if invalid
tz = TimeZoneInfo.Local;
}
var now = TimeZoneInfo.ConvertTime(DateTime.Now, tz);
return ToolResponse.Success(
message.Id,
new CurrentDateTimeResponse(
DateTime: now.ToString("o"), // ISO 8601 format
Date: now.ToString("yyyy-MM-dd"),
Time: now.ToString("HH:mm:ss"),
TimeZone: tz.Id,
DayOfWeek: now.ToString("dddd"),
WeekNumber: System.Globalization.ISOWeek.GetWeekOfYear(now),
Year: now.Year,
Month: now.Month,
Day: now.Day
));
}
[McpTool("get_iso8601_timestamp",
Title = "Get current timestamp",
Description = "Get current timestamp in ISO 8601 format (UTC)")]
public JsonRpcMessage GetIso8601Timestamp(JsonRpcMessage message)
{
return ToolResponse.Success(
message.Id,
new Iso8601TimestampResponse(
Timestamp: DateTime.UtcNow.ToString("o")
));
}
[McpTool(Title = "Add or subtract days",
Description = "Add or subtract days from a date")]
public JsonRpcMessage AddDays(TypedJsonRpc<AddDaysRequest> message)
{
var request = message.GetParams();
DateTime baseDate = DateTime.Now;
if (!string.IsNullOrWhiteSpace(request?.Date))
{
DateTime.TryParse(request.Date, out baseDate);
}
var result = baseDate.AddDays(request?.Days ?? 0);
return ToolResponse.Success(
message.Id,
new AddDaysResponse(
Original: baseDate.ToString("yyyy-MM-dd"),
Result: result.ToString("yyyy-MM-dd"),
DaysAdded: request?.Days ?? 0,
DayOfWeek: result.ToString("dddd")
));
}
[McpTool(Title = "Check if weekend",
Description = "Check if a date is a weekend (Saturday or Sunday)")]
public JsonRpcMessage IsWeekend(TypedJsonRpc<IsWeekendRequest> message)
{
var request = message.GetParams();
DateTime baseDate = DateTime.Now;
if (!string.IsNullOrWhiteSpace(request?.Date))
{
DateTime.TryParse(request.Date, out baseDate);
}
var isWeekend = baseDate.DayOfWeek == DayOfWeek.Saturday ||
baseDate.DayOfWeek == DayOfWeek.Sunday;
return ToolResponse.Success(
message.Id,
new IsWeekendResponse(
Date: baseDate.ToString("yyyy-MM-dd"),
DayOfWeek: baseDate.ToString("dddd"),
IsWeekend: isWeekend
));
}
[McpTool(Title = "Get week number",
Description = "Get ISO 8601 week number for a date")]
public JsonRpcMessage GetWeeknumber(TypedJsonRpc<GetWeeknumberRequest> message)
{
var request = message.GetParams();
DateTime baseDate = DateTime.Now;
if (!string.IsNullOrWhiteSpace(request?.Date))
{
DateTime.TryParse(request.Date, out baseDate);
}
var weekNumber = System.Globalization.ISOWeek.GetWeekOfYear(baseDate);
return ToolResponse.Success(
message.Id,
new GetWeekNumberResponse(
Date: baseDate.ToString("yyyy-MM-dd"),
WeekNumber: weekNumber,
Year: baseDate.Year
));
}
}
Models (DateTimeModels.cs)
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace DateTimeMcpServer.Models;
// Requests
public sealed record CurrentDateTimeRequest(
[property: JsonPropertyName("timezoneName")]
[property: Description("Timezone name (e.g., 'Europe/Oslo', 'UTC')")]
string? TimezoneName);
public sealed record AddDaysRequest(
[property: JsonPropertyName("date")]
[property: Description("Starting date in YYYY-MM-DD format (defaults to today)")]
string? Date,
[property: JsonPropertyName("days")]
[property: Description("Number of days to add (positive) or subtract (negative)")]
int Days = 0);
public sealed record IsWeekendRequest(
[property: JsonPropertyName("date")]
[property: Description("Date in YYYY-MM-DD format (defaults to today)")]
string? Date);
public sealed record GetWeeknumberRequest(
[property: JsonPropertyName("date")]
[property: Description("Starting date in YYYY-MM-DD format (defaults to today)")]
string? Date);
// Responses
public sealed record CurrentDateTimeResponse(
[property: JsonPropertyName("datetime")] string DateTime,
[property: JsonPropertyName("date")] string Date,
[property: JsonPropertyName("time")] string Time,
[property: JsonPropertyName("timezone")] string TimeZone,
[property: JsonPropertyName("dayOfWeek")] string DayOfWeek,
[property: JsonPropertyName("weekNumber")] int WeekNumber,
[property: JsonPropertyName("year")] int Year,
[property: JsonPropertyName("month")] int Month,
[property: JsonPropertyName("day")] int Day);
public sealed record Iso8601TimestampResponse(
[property: JsonPropertyName("timestamp")] string Timestamp);
public sealed record AddDaysResponse(
[property: JsonPropertyName("original")] string Original,
[property: JsonPropertyName("result")] string Result,
[property: JsonPropertyName("daysAdded")] int DaysAdded,
[property: JsonPropertyName("dayOfWeek")] string DayOfWeek);
public sealed record IsWeekendResponse(
[property: JsonPropertyName("date")] string Date,
[property: JsonPropertyName("dayOfWeek")] string DayOfWeek,
[property: JsonPropertyName("isWeekend")] bool IsWeekend);
public sealed record GetWeekNumberResponse(
[property: JsonPropertyName("date")] string Date,
[property: JsonPropertyName("weekNumber")] int WeekNumber,
[property: JsonPropertyName("year")] int Year);
Running the Server
HTTP Mode
dotnet run
Server runs at: http://localhost:5000/mcp
stdio Mode (GitHub Copilot)
dotnet run -- --stdio
Testing
Get Current DateTime
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_current_datetime",
"arguments": {
"timezoneName": "Europe/Oslo"
}
},
"id": 1
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "{\"datetime\":\"2025-12-20T13:45:30+01:00\",\"date\":\"2025-12-20\",\"time\":\"13:45:30\",\"timezone\":\"Europe/Oslo\",\"dayOfWeek\":\"Friday\",\"weekNumber\":51,\"year\":2025,\"month\":12,\"day\":20}"
}
]
},
"id": 1
}
Add Days
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "add_days",
"arguments": {
"date": "2025-12-20",
"days": 7
}
},
"id": 2
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "{\"original\":\"2025-12-20\",\"result\":\"2025-12-27\",\"daysAdded\":7,\"dayOfWeek\":\"Friday\"}"
}
]
},
"id": 2
}
Check Weekend
curl -X POST http://localhost:5000/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "is_weekend",
"arguments": {
"date": "2025-12-21"
}
},
"id": 3
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"content": [
{
"type": "text",
"text": "{\"date\":\"2025-12-21\",\"dayOfWeek\":\"Sunday\",\"isWeekend\":true}"
}
]
},
"id": 3
}
Key Concepts
1. TypedJsonRpc
Type-safe parameter handling with automatic deserialization:
public JsonRpcMessage AddDays(TypedJsonRpc<AddDaysRequest> message)
{
var request = message.GetParams(); // Returns AddDaysRequest
// request.Date and request.Days are strongly typed!
}
Benefits:
- ✅ Compile-time type safety
- ✅ Automatic JSON deserialization
- ✅ IntelliSense support
- ✅ Cleaner code
2. Description Attributes
Use [Description] on record properties for documentation:
public sealed record AddDaysRequest(
[property: Description("Starting date in YYYY-MM-DD format")]
string? Date,
[property: Description("Number of days to add")]
int Days = 0);
Generates InputSchema automatically!
3. JsonPropertyName
Control JSON serialization names:
public sealed record CurrentDateTimeResponse(
[property: JsonPropertyName("datetime")] string DateTime,
[property: JsonPropertyName("timezone")] string TimeZone);
JSON output:
{
"datetime": "2025-12-20T13:45:30+01:00",
"timezone": "Europe/Oslo"
}
4. Auto-Generated Tool Names
Method names automatically convert to snake_case:
[McpTool] // No explicit name!
public JsonRpcMessage GetCurrentDatetime(...) { }
// Auto-generates: "get_current_datetime"
[McpTool]
public JsonRpcMessage AddDays(...) { }
// Auto-generates: "add_days"
5. Sealed Records
Use sealed record for immutable data models:
public sealed record AddDaysRequest(
string? Date,
int Days = 0); // Default value
Benefits:
- ✅ Immutable
- ✅ Value semantics
- ✅ Built-in equality
- ✅ Sealed for performance
6. Timezone Handling
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Europe/Oslo");
var now = TimeZoneInfo.ConvertTime(DateTime.Now, tz);
Common timezones:
UTC- Coordinated Universal TimeEurope/Oslo- NorwayAmerica/New_York- US EasternAsia/Tokyo- JapanAustralia/Sydney- Australia
7. ISO 8601 Format
DateTime.UtcNow.ToString("o") // "2025-12-20T13:45:30.1234567Z"
8. ISO Week Numbers
System.Globalization.ISOWeek.GetWeekOfYear(date)
ISO 8601 week numbers:
- Week starts on Monday
- Week 1 contains January 4th
- Returns 1-53
Using with GitHub Copilot
Configure .mcp.json:
{
"mcpServers": {
"datetime": {
"command": "dotnet",
"args": [
"run",
"--project",
"C:\\path\\to\\DateTimeMcpServer",
"--",
"--stdio"
]
}
}
}
Then in Copilot Chat:
@datetime what's the current time in Tokyo?
@datetime add 30 days to 2025-12-20
@datetime is 2025-12-25 a weekend?
@datetime what week number is today?
Enhancements
1. Date Formatting
[McpTool("format_date")]
public JsonRpcMessage FormatDate(TypedJsonRpc<FormatDateRequest> request)
{
var args = request.GetParams()!;
var date = DateTime.Parse(args.Date);
return ToolResponse.Success(request.Id, new
{
shortDate = date.ToString("d"), // 12/20/2025
longDate = date.ToString("D"), // Friday, December 20, 2025
fullDateTime = date.ToString("F"), // Friday, December 20, 2025 1:45 PM
custom = date.ToString(args.Format ?? "yyyy-MM-dd")
});
}
public sealed record FormatDateRequest(
[property: Description("Date to format")] string Date,
[property: Description("Custom format string")] string? Format = null);
2. Business Days Calculator
[McpTool("add_business_days")]
public JsonRpcMessage AddBusinessDays(TypedJsonRpc<BusinessDaysRequest> request)
{
var args = request.GetParams()!;
var date = DateTime.Parse(args.Date);
var daysToAdd = args.Days;
while (daysToAdd > 0)
{
date = date.AddDays(1);
if (date.DayOfWeek != DayOfWeek.Saturday &&
date.DayOfWeek != DayOfWeek.Sunday)
{
daysToAdd--;
}
}
return ToolResponse.Success(request.Id, new
{
resultDate = date.ToString("yyyy-MM-dd"),
dayOfWeek = date.ToString("dddd")
});
}
public sealed record BusinessDaysRequest(
[property: Description("Starting date")] string Date,
[property: Description("Business days to add")] int Days);
3. Time Until/Since
[McpTool("time_until")]
public JsonRpcMessage TimeUntil(TypedJsonRpc<TimeUntilRequest> request)
{
var args = request.GetParams()!;
var targetDate = DateTime.Parse(args.Date);
var now = DateTime.Now;
var timeSpan = targetDate - now;
return ToolResponse.Success(request.Id, new
{
days = timeSpan.Days,
hours = timeSpan.Hours,
minutes = timeSpan.Minutes,
totalDays = timeSpan.TotalDays,
isPast = timeSpan < TimeSpan.Zero
});
}
public sealed record TimeUntilRequest(
[property: Description("Target date")] string Date);
Integration Tests
[Fact]
public async Task GetCurrentDateTime_WithTimezone_ReturnsFormattedDate()
{
// Arrange
using var server = new McpGatewayFixture();
var client = server.CreateClient();
var request = new
{
jsonrpc = "2.0",
method = "tools/call",
@params = new
{
name = "get_current_datetime",
arguments = new { timezoneName = "UTC" }
},
id = 1
};
// Act
var response = await client.PostAsJsonAsync("/mcp", request);
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
// Assert
Assert.NotNull(result);
var content = result.RootElement
.GetProperty("result")
.GetProperty("content")[0]
.GetProperty("text")
.GetString();
Assert.Contains("datetime", content);
Assert.Contains("UTC", content);
}
[Fact]
public async Task IsWeekend_Sunday_ReturnsTrue()
{
// Arrange
var request = new
{
jsonrpc = "2.0",
method = "tools/call",
@params = new
{
name = "is_weekend",
arguments = new { date = "2025-12-21" } // Sunday
},
id = 2
};
// Act
var response = await client.PostAsJsonAsync("/mcp", request);
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
// Assert
var content = JsonSerializer.Deserialize<IsWeekendResponse>(
result.RootElement
.GetProperty("result")
.GetProperty("content")[0]
.GetProperty("text")
.GetString()!);
Assert.True(content.IsWeekend);
}
Source Code
Full source code available at:
- GitHub: Examples/DateTimeMcpServer
- Tests: Examples/DateTimeMcpServerTests
See Also
- Getting Started - Build your first tool
- Tools API - Complete Tools API reference
- Calculator Example - Basic arithmetic operations
- Metrics Example - Add metrics tracking