Skip to content

Commit e9a222d

Browse files
Copilotdkhalife
andauthored
Add MCP server implementation in C# with HTTP transport and modern attributes (#198)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: dkhalife <[email protected]>
1 parent 954a269 commit e9a222d

File tree

9 files changed

+431
-0
lines changed

9 files changed

+431
-0
lines changed

.github/workflows/dotnet-build.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: .NET Build
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Setup .NET
20+
uses: actions/setup-dotnet@v4
21+
with:
22+
dotnet-version: '9.0.x'
23+
24+
- name: Restore dependencies
25+
run: |
26+
cd mcpserver
27+
dotnet restore
28+
29+
- name: Build
30+
run: |
31+
cd mcpserver
32+
dotnet build --no-restore --configuration Release

mcpserver/.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Build results
2+
[Dd]ebug/
3+
[Dd]ebugPublic/
4+
[Rr]elease/
5+
[Rr]eleases/
6+
x64/
7+
x86/
8+
[Aa][Rr][Mm]/
9+
[Aa][Rr][Mm]64/
10+
bld/
11+
[Bb]in/
12+
[Oo]bj/
13+
[Ll]og/
14+
[Ll]ogs/
15+
16+
# Visual Studio cache/options directory
17+
.vs/
18+
.vscode/
19+
20+
# User-specific files
21+
*.rsuser
22+
*.suo
23+
*.user
24+
*.userosscache
25+
*.sln.docstates
26+
27+
# Build Results
28+
**/bin/
29+
**/obj/

mcpserver/Models/Label.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace TaskWizard.McpServer.Models;
2+
3+
public class Label
4+
{
5+
public int Id { get; set; }
6+
public string Name { get; set; } = string.Empty;
7+
public string Color { get; set; } = string.Empty;
8+
}
9+
10+
public class CreateLabelRequest
11+
{
12+
public string Name { get; set; } = string.Empty;
13+
public string Color { get; set; } = string.Empty;
14+
}
15+
16+
public class UpdateLabelRequest
17+
{
18+
public int Id { get; set; }
19+
public string Name { get; set; } = string.Empty;
20+
public string Color { get; set; } = string.Empty;
21+
}

mcpserver/Models/Task.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace TaskWizard.McpServer.Models;
2+
3+
public class Task
4+
{
5+
public int Id { get; set; }
6+
public string Title { get; set; } = string.Empty;
7+
public DateTime? NextDueDate { get; set; }
8+
public DateTime? EndDate { get; set; }
9+
public string Frequency { get; set; } = "once";
10+
public bool IsRolling { get; set; }
11+
public List<Label> Labels { get; set; } = new();
12+
}
13+
14+
public class CreateTaskRequest
15+
{
16+
public string Title { get; set; } = string.Empty;
17+
public DateTime? NextDueDate { get; set; }
18+
public DateTime? EndDate { get; set; }
19+
public string Frequency { get; set; } = "once";
20+
public bool IsRolling { get; set; }
21+
public List<int> Labels { get; set; } = new();
22+
}
23+
24+
public class UpdateTaskRequest
25+
{
26+
public int Id { get; set; }
27+
public string Title { get; set; } = string.Empty;
28+
public DateTime? NextDueDate { get; set; }
29+
public DateTime? EndDate { get; set; }
30+
public string Frequency { get; set; } = "once";
31+
public bool IsRolling { get; set; }
32+
public List<int> Labels { get; set; } = new();
33+
}

mcpserver/Program.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using ModelContextProtocol.Server;
2+
using TaskWizard.McpServer.Services;
3+
4+
var builder = WebApplication.CreateBuilder(args);
5+
6+
// Register services
7+
builder.Services.AddSingleton<StubDataService>();
8+
9+
// Configure MCP server with HTTP transport
10+
builder.Services
11+
.AddMcpServer()
12+
.WithHttpTransport()
13+
.WithToolsFromAssembly();
14+
15+
var app = builder.Build();
16+
17+
app.MapMcp();
18+
19+
app.Run("http://localhost:3001");
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using TaskWizard.McpServer.Models;
2+
3+
namespace TaskWizard.McpServer.Services;
4+
5+
public class StubDataService
6+
{
7+
private readonly List<Label> _labels = new()
8+
{
9+
new Label { Id = 1, Name = "Work", Color = "#FF5733" },
10+
new Label { Id = 2, Name = "Personal", Color = "#33FF57" },
11+
new Label { Id = 3, Name = "Urgent", Color = "#3357FF" }
12+
};
13+
14+
private readonly List<Models.Task> _tasks = new()
15+
{
16+
new Models.Task
17+
{
18+
Id = 1,
19+
Title = "Complete project documentation",
20+
NextDueDate = DateTime.UtcNow.AddDays(3),
21+
Frequency = "once",
22+
IsRolling = false,
23+
Labels = new List<Label>()
24+
},
25+
new Models.Task
26+
{
27+
Id = 2,
28+
Title = "Weekly team meeting",
29+
NextDueDate = DateTime.UtcNow.AddDays(1),
30+
Frequency = "weekly",
31+
IsRolling = false,
32+
Labels = new List<Label>()
33+
}
34+
};
35+
36+
private int _nextTaskId = 3;
37+
private int _nextLabelId = 4;
38+
39+
// Task CRUD operations
40+
public List<Models.Task> GetAllTasks() => _tasks.ToList();
41+
42+
public Models.Task? GetTask(int id) => _tasks.FirstOrDefault(t => t.Id == id);
43+
44+
public Models.Task CreateTask(CreateTaskRequest request)
45+
{
46+
var task = new Models.Task
47+
{
48+
Id = _nextTaskId++,
49+
Title = request.Title,
50+
NextDueDate = request.NextDueDate,
51+
EndDate = request.EndDate,
52+
Frequency = request.Frequency,
53+
IsRolling = request.IsRolling,
54+
Labels = request.Labels.Select(id => _labels.FirstOrDefault(l => l.Id == id))
55+
.Where(l => l != null)
56+
.Cast<Label>()
57+
.ToList()
58+
};
59+
_tasks.Add(task);
60+
return task;
61+
}
62+
63+
public Models.Task? UpdateTask(UpdateTaskRequest request)
64+
{
65+
var task = _tasks.FirstOrDefault(t => t.Id == request.Id);
66+
if (task == null) return null;
67+
68+
task.Title = request.Title;
69+
task.NextDueDate = request.NextDueDate;
70+
task.EndDate = request.EndDate;
71+
task.Frequency = request.Frequency;
72+
task.IsRolling = request.IsRolling;
73+
task.Labels = request.Labels.Select(id => _labels.FirstOrDefault(l => l.Id == id))
74+
.Where(l => l != null)
75+
.Cast<Label>()
76+
.ToList();
77+
return task;
78+
}
79+
80+
public bool DeleteTask(int id)
81+
{
82+
var task = _tasks.FirstOrDefault(t => t.Id == id);
83+
if (task == null) return false;
84+
_tasks.Remove(task);
85+
return true;
86+
}
87+
88+
// Label CRUD operations
89+
public List<Label> GetAllLabels() => _labels.ToList();
90+
91+
public Label? GetLabel(int id) => _labels.FirstOrDefault(l => l.Id == id);
92+
93+
public Label CreateLabel(CreateLabelRequest request)
94+
{
95+
var label = new Label
96+
{
97+
Id = _nextLabelId++,
98+
Name = request.Name,
99+
Color = request.Color
100+
};
101+
_labels.Add(label);
102+
return label;
103+
}
104+
105+
public Label? UpdateLabel(UpdateLabelRequest request)
106+
{
107+
var label = _labels.FirstOrDefault(l => l.Id == request.Id);
108+
if (label == null) return null;
109+
110+
label.Name = request.Name;
111+
label.Color = request.Color;
112+
return label;
113+
}
114+
115+
public bool DeleteLabel(int id)
116+
{
117+
var label = _labels.FirstOrDefault(l => l.Id == id);
118+
if (label == null) return false;
119+
_labels.Remove(label);
120+
return true;
121+
}
122+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<RootNamespace>TaskWizard.McpServer</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.2" />
13+
</ItemGroup>
14+
15+
</Project>

mcpserver/Tools/LabelTools.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System.ComponentModel;
2+
using System.Text.Json;
3+
using ModelContextProtocol.Server;
4+
using TaskWizard.McpServer.Models;
5+
using TaskWizard.McpServer.Services;
6+
7+
namespace TaskWizard.McpServer.Tools;
8+
9+
[McpServerToolType]
10+
public class LabelTools
11+
{
12+
private readonly StubDataService _dataService;
13+
14+
public LabelTools(StubDataService dataService)
15+
{
16+
_dataService = dataService;
17+
}
18+
19+
[McpServerTool, Description("List all labels")]
20+
public string ListLabels()
21+
{
22+
return JsonSerializer.Serialize(_dataService.GetAllLabels(), new JsonSerializerOptions { WriteIndented = true });
23+
}
24+
25+
[McpServerTool, Description("Get a specific label by ID")]
26+
public string GetLabel([Description("Label ID")] int id)
27+
{
28+
var label = _dataService.GetLabel(id);
29+
return label != null
30+
? JsonSerializer.Serialize(label, new JsonSerializerOptions { WriteIndented = true })
31+
: JsonSerializer.Serialize(new { error = "Label not found" });
32+
}
33+
34+
[McpServerTool, Description("Create a new label")]
35+
public string CreateLabel(
36+
[Description("Label name")] string name,
37+
[Description("Label color (hex format)")] string color = "#000000")
38+
{
39+
var createRequest = new CreateLabelRequest
40+
{
41+
Name = name,
42+
Color = color
43+
};
44+
var label = _dataService.CreateLabel(createRequest);
45+
return JsonSerializer.Serialize(label, new JsonSerializerOptions { WriteIndented = true });
46+
}
47+
48+
[McpServerTool, Description("Update an existing label")]
49+
public string UpdateLabel(
50+
[Description("Label ID")] int id,
51+
[Description("Label name")] string name,
52+
[Description("Label color (hex format)")] string color = "#000000")
53+
{
54+
var updateRequest = new UpdateLabelRequest
55+
{
56+
Id = id,
57+
Name = name,
58+
Color = color
59+
};
60+
var label = _dataService.UpdateLabel(updateRequest);
61+
return label != null
62+
? JsonSerializer.Serialize(label, new JsonSerializerOptions { WriteIndented = true })
63+
: JsonSerializer.Serialize(new { error = "Label not found" });
64+
}
65+
66+
[McpServerTool, Description("Delete a label")]
67+
public string DeleteLabel([Description("Label ID")] int id)
68+
{
69+
var success = _dataService.DeleteLabel(id);
70+
return JsonSerializer.Serialize(new { success });
71+
}
72+
}

0 commit comments

Comments
 (0)