The notes server in C# with the official ModelContextProtocol package — attributes on static methods.
WHEN TO USE IT
When you live in .NET and want your tools as ordinary C# methods with dependency injection.
Formats change. If a field stops working, check it against Anthropic's documentation.
Read it before you install it. A MCP server runs with your user account's permissions, on its own, every time it is triggered. That is what makes it useful, and why a stranger's should never be installed unread — this one included.
ModelContextProtocol brings hosting and DI. Microsoft.Extensions.Hosting is there because an MCP server is a host with a transport, not a bare console app.
dotnet new console -o NotesMcp
cd NotesMcp
dotnet add package ModelContextProtocol
dotnet add package Microsoft.Extensions.Hosting
[McpServerToolType] marks the class, [McpServerTool] each method, [Description] is what Claude reads. The logging line sends every log to stderr, because stdout is the protocol.
using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
var builder = Host.CreateApplicationBuilder(args);
// stdout carries the protocol — send all logging to stderr.
builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace);
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
[McpServerToolType]
public static class NotesTools
{
private static readonly string Root =
Environment.GetEnvironmentVariable("NOTES_DIR")
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "notes");
[McpServerTool, Description("Find notes containing the query (case-insensitive). Returns file:line: text.")]
public static string SearchNotes(
[Description("Text to look for")] string query,
int limit = 10)
{
var hits = new List<string>();
foreach (var file in Directory.EnumerateFiles(Root, "*.md", SearchOption.AllDirectories).Order())
{
var lines = File.ReadAllLines(file);
for (var i = 0; i < lines.Length && hits.Count < limit; i++)
{
if (lines[i].Contains(query, StringComparison.OrdinalIgnoreCase))
{
hits.Add($"{Path.GetRelativePath(Root, file)}:{i + 1}: {lines[i].Trim()}");
}
}
}
return hits.Count == 0 ? "No matches." : string.Join('\n', hits);
}
}
Build once and point Claude Code at the output, rather than dotnet run — a server that compiles on every start is slow to connect.
dotnet build -c Release
claude mcp add --transport stdio --env NOTES_DIR=$HOME/notes notes \
-- dotnet /absolute/path/to/NotesMcp/bin/Release/net10.0/NotesMcp.dll
claude mcp list
MORE MCP SERVERS
The authority
Anthropic's documentation for MCP Servers
This template is a starting point. Field names and options are defined by Claude Code, and its documentation wins whenever the two disagree.
https://code.claude.com/docs/en/mcp