MCP servers are powerful tools that give GitHub Copilot access to your systems, data, and workflows. Everyone I installed and worked with initially used Node or Python, but you can build them with .NET and it was so much easier than I imagined.
In this post, I’m going to show you how. You’ll see exactly what I built for myself (connecting Copilot to my Obsidian notes), but more importantly, you’ll learn the pattern. That same pattern works for almost anything: connecting to your company’s internal APIs, syncing your task management system, pulling data from your databases, or accessing custom tools only you need.
The beauty of building your own MCP server is that you’re not limited to what someone else thought would be useful. You can make Copilot work the way you want to work.…
MCP servers are powerful tools that give GitHub Copilot access to your systems, data, and workflows. Everyone I installed and worked with initially used Node or Python, but you can build them with .NET and it was so much easier than I imagined.
In this post, I’m going to show you how. You’ll see exactly what I built for myself (connecting Copilot to my Obsidian notes), but more importantly, you’ll learn the pattern. That same pattern works for almost anything: connecting to your company’s internal APIs, syncing your task management system, pulling data from your databases, or accessing custom tools only you need.
The beauty of building your own MCP server is that you’re not limited to what someone else thought would be useful. You can make Copilot work the way you want to work.
Microsoft made a great intro post on making an MCP server this past spring (April 2025). It gives the basics. I wanted to show you a real-world example of what is possible and how quickly you can get there.
So here is what I wanted to solve: I keep thousands of notes in Obsidian — daily notes, tasks, project details, code snippets, random thoughts, everything. I’d be working in VS Code and think, "I know I saved something like this somewhere," but I’d have to stop, search my notes, and lose context. I might want to capture a snippet or check the next task and then I’m switching. Wouldn’t it be great if Copilot could do all this for me? Now Copilot is not just my coding partner, it is also my personal assistant. That’s where the idea came from.
Getting started
I quickly built a proof-of-concept app. A console app with a few packages and linked them up. It takes just a few minutes.
dotnet new console -n MyMCP
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
Once you have your project created, you can start your proof-of-concept app by replacing Program.cs and adding a simple service class.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
using System.ComponentModel;
using ModelContextProtocol.Server;
[McpServerToolType]
public static class ObsidianService
{
[McpServerTool, Description("Give user a special greeting")]
public static string GetGreeting(
[Description("Person's name")] string name)
{
if (string.IsNullOrWhiteSpace(name))
return "Error: Name cannot be empty";
return $"Hello {name}!";
}
}
Now that you have your code in place, do a build to make sure we have everything in place and then we are going to connect it up in VS Code and test it out.
If you have other MCP servers setup, open your mcp.json file. If you don’t have one, you’ll need to add one. I always use a global mcp.json file, but you can just put one in your .vscode folder if you are unsure how to set it up and just want to move on.
You’ll want to add the new MCP in the json like below.
{
"inputs": [],
"servers": {
"myobsidianmcp": {
"type": "stdio",
"command": "dotnet",
"args": [
"run",
"--project",
"/Users/al/Data/GitHub/MyObsidianMcp/MyObsidianMcp.csproj"
]
}
}
}
We are almost done now. We need to enable Copilot to use the new tool and we are ready to test. Click the Tool icon as shown below and then click the checkbox next to the tool to enable it.
Lastly we just need to ask Copilot to try it out.
Making something real
Now that we have a proof of concept running, let’s make it useful. Hopefully, you are brimming with ideas of what you want to do, but here is my list. I wanted some simple functions to work with my notes. I started with the top 3 but quickly expanded to these 7.
- SearchNotes - Searches all markdown files in your vault for content matching a query
- ReadNote - Reads the full content of a specific note by file path
- ListRecentNotes - Lists the most recently modified notes (default 10)
- GetDailyNote - Retrieves a daily note for a specific date (‘today’, ‘yesterday’, or ‘YYYY-MM-DD’)
- CreateNote - Creates a new markdown note with optional tags and folder
- MarkTask - Marks a task with a status (Completed, InProgress, Forwarded, Scheduled, Open)
- AddTaskToDaily - Adds a new task to the Short List section of a daily note
Note that the last 3 functions are write functions. Most MCP servers will give read access to your data. As this is my stuff though, I’m creating write routines to make the exact changes I want. (My vault is backed up.)
In addition to adding these functions, I also needed to set the path to my Obsidian vault somewhere. It is different on the various machines I use, so I moved this to be an argument that gets passed in via the mcp config.
I’m not going to go through coding all of these up, but the source is all available on Github. I’ll just highlight one of the simple functions so you can see what it looks like. Simple code with descriptions in plain text explaining how to use it.
[McpServerTool, Description("Reads the full content of a specific markdown note")]
public static async Task<string> ReadNote(
[Description("The file path to the note to read")] string path)
{
try
{
if (string.IsNullOrWhiteSpace(path))
return "Error: Path cannot be empty";
var fileInfo = new FileInfo(path);
if (!fileInfo.Exists)
return $"Error: File not found at {path}";
if (!fileInfo.FullName.StartsWith(VaultPath, StringComparison.OrdinalIgnoreCase))
return "Error: File is outside the vault directory";
var content = await File.ReadAllTextAsync(fileInfo.FullName);
return content;
}
catch (Exception ex)
{
return $"Error reading note: {ex.Message}";
}
}
As mentioned, you can see the complete code and even try it out, but remember, this was written for how I use my notes. Your mileage may vary.
Finishing touches
Once I completed my little MCP server, I didn’t want to have the project required on every machine I use. I switch back and forth between Mac and Windows and find myself on 6 different devices over the course of a typical week.
First, I built a release version of the app for Mac (arm64) and Windows (x64), and placed them in my synced Obsidian vault. (Might as well let Obsidian sync the MCP server everywhere I need it.)
Next, I updated my mcp.json, to use the new location on each machine I use. I now point to the dll instead of the project. You can see in my GitHub read me how it looks different between Windows and Mac.
Lastly, I updated my Github Copilot instructions to tell it some details on how I expect it to use these tools while we work. I got this idea from Claude Agent Skills. Copilot doesn’t really have something like this yet, so for now I just put that information in my instructions so my agents can use these tools the way I expect them to be used.
Below is a sample from my instructions file.
## Skill Instructions
### When to Use SearchNotes
**Purpose**: Find relevant information in your obsidian notes without leaving your editor.
**Best practices**:
- Use SearchNotes when the user asks "I remember saving something about..." or "Do I have notes on..."
- Search for specific keywords or concepts, not exact phrases
- If the first search doesn't yield results, try broader or related terms
- Use ReadNote to read relevant notes to determine usefulness
- For code snippets or patterns, search for specific syntax or function names
**Examples**:
- User: "I think I saved a regex pattern for validating emails"
→ SearchNotes(query: "email regex")
- User: "Do I have any notes on authentication patterns?"
→ SearchNotes(query: "authentication")
Conclusion
When I first started thinking about how I wished I could use my Obsidian notes with Copilot, it felt daunting. There were some open-source options, but none quite fit what I wanted. Once I looked into making my own, I was surprised at how straightforward it was. In a shockingly short amount of time, I had something genuinely useful to me.
I think that is what makes this so cool. An MCP server doesn’t have to be a big corporate project or cover every use case for everyone; it just has to work for you. For me, it was my Obsidian notes. Yours might be something completely different.
Give it a try. Build something small that allows AI to make your life better. It is probably much easier than you expect and you might really appreciate the results.
This post is part of the C# Advent Calendar for 2025. Check the link for other great C# content all through December!