Content
# MyFirstMcpServer
A small [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server written in C# / .NET 9.
It exposes a set of **tools** that an AI client (Claude Code, Claude Desktop, VS Code, etc.) can call
over **stdio**. Tools include simple demos (`add`, `greet`) and a full **CRUD API over a SQLite `Book`
table** so an AI can store and manage books in a local database.
---
## Features
- **stdio transport** — the server speaks JSON-RPC over stdin/stdout, the standard way MCP clients launch local servers.
- **Auto-discovered tools** — any method marked `[McpServerTool]` is registered automatically.
- **SQLite persistence** — books are stored in a `books.db` file that survives restarts.
- **Parameterized SQL** — all queries use bound parameters (no SQL injection).
---
## Tools
| Tool (as the AI sees it) | Arguments | Returns | Description |
|--------------------------|-----------|---------|-------------|
| `add` | `a: double`, `b: double` | `double` | Adds two numbers. |
| `greet` | `name: string` | `string` | Returns a friendly greeting. |
| `get_books` | — | `Book[]` | Lists all books. |
| `get_book` | `id: long` | `Book \| null` | Gets a single book by id. |
| `add_book` | `name: string`, `price: double` | `Book` | Inserts a book, returns it with its generated `id`. |
| `update_book` | `id: long`, `name: string`, `price: double` | `string` | Updates a book; returns a status message. |
| `delete_book` | `id: long` | `string` | Deletes a book; returns a status message. |
> **Naming note:** MCP lowercases/snake-cases method names. The C# method `AddBook` is exposed as `add_book`,
> `GetBooks` as `get_books`, and so on. You rarely type these directly — you ask the AI in natural language
> and it picks the right tool.
### The `Book` model
```jsonc
{
"id": 1, // long, auto-generated primary key
"name": "Dune", // string
"price": 19.99 // double
}
```
---
## Project layout
```
MyFirstMcpServer/
├─ MyFirstMcpServer.sln
├─ README.md
├─ connectMcp/
│ └─ mcp.json # Example client config (stdio)
└─ MyFirstMcpServer/
├─ Program.cs # Host setup + DB init + stdio transport
├─ Tools.cs # Demo tools: add, greet
├─ Book.cs # Book model
├─ BookRepository.cs # SQLite data-access layer (schema + CRUD)
├─ BookTools.cs # MCP tools wrapping the repository
├─ Dockerfile
└─ MyFirstMcpServer.csproj
```
---
## Prerequisites
- [.NET SDK 9.0](https://dotnet.microsoft.com/download) or later
---
## Build & run
```bash
cd MyFirstMcpServer
dotnet build
dotnet run --no-launch-profile
```
The server reads JSON-RPC from **stdin** and writes responses to **stdout**. It's not meant to be used
interactively from a terminal — an MCP client launches it for you (see below). On startup it creates
`books.db` next to the built assembly if it doesn't already exist.
> **Why `--no-launch-profile`?** With stdio transport, **stdout must contain only JSON-RPC messages**.
> Plain `dotnet run` prints a `Using launch settings from ...` banner to stdout, which corrupts the
> protocol stream. `--no-launch-profile` suppresses it. (All diagnostic logging in this project is
> already routed to **stderr** for the same reason.)
---
## Connect it to a client
### Claude Code (CLI)
Register the server once:
```bash
claude mcp add myFirstMcp --transport stdio -- \
dotnet run --no-launch-profile --project "D:\workspace\practice\MyFirstMcpServer\MyFirstMcpServer\MyFirstMcpServer.csproj"
```
Verify it connected:
```bash
claude mcp list
# myFirstMcp: dotnet run --no-launch-profile --project ... - ✓ Connected
```
Then **talk to Claude in natural language** — you don't type tool names yourself:
```bash
claude # interactive
> add a book called Dune priced 19.99
> list all books
> change book 1's price to 29.99
> delete book 1
```
Or one-shot, non-interactive:
```bash
claude -p "add a book called The Hobbit priced 12.50, then list all books"
```
**Skip the permission prompt** by allowing the server in `~/.claude/settings.json`:
```json
{
"permissions": {
"allow": ["mcp__myFirstMcp"]
}
}
```
`mcp__myFirstMcp` (no tool name after it) allows every tool from this server. Restart Claude after editing settings.
### Other clients (VS Code, Claude Desktop, etc.)
Point the client's MCP config at the project. See [connectMcp/mcp.json](connectMcp/mcp.json):
```json
{
"servers": {
"myFirstMcp": {
"type": "stdio",
"command": "dotnet",
"args": [
"run", "--no-launch-profile",
"--project", "D:\\workspace\\practice\\MyFirstMcpServer\\MyFirstMcpServer\\MyFirstMcpServer.csproj"
]
}
}
}
```
---
## Testing the protocol manually
You can drive the server directly by piping JSON-RPC into it (useful for debugging). Keep stdin open
long enough for it to respond:
```bash
cd MyFirstMcpServer
{
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}'
echo '{"jsonrpc":"2.0","method":"notifications/initialized"}'
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"add_book","arguments":{"name":"Dune","price":19.99}}}'
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_books","arguments":{}}}'
sleep 5
} | dotnet run --no-launch-profile 2>/dev/null
```
You should see clean JSON-RPC responses on stdout — the new book, then the full list.
---
## Adding your own tool
1. Add a `public static` method to a class marked `[McpServerToolType]` (e.g. `Tools.cs` or `BookTools.cs`).
2. Mark the method `[McpServerTool]` and give it a `[Description]`.
3. Describe each parameter with `[Description]` — the AI uses these to fill in arguments.
```csharp
[McpServerTool, Description("Counts how many books are stored.")]
public static int CountBooks() => BookRepository.GetAll().Count;
```
Rebuild, and the tool is auto-discovered on next launch — no registration needed.
---
## Database
- **File:** `books.db`, created next to the built assembly (`bin/Debug/net9.0/books.db`).
- **Reset:** delete the file; it's recreated empty on the next run.
- **Schema:**
```sql
CREATE TABLE IF NOT EXISTS Book (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL,
Price REAL NOT NULL
);
```
---
## Tech stack
- .NET 9 (`net9.0`)
- [`ModelContextProtocol`](https://www.nuget.org/packages/ModelContextProtocol) 1.4.0
- [`Microsoft.Data.Sqlite`](https://www.nuget.org/packages/Microsoft.Data.Sqlite)
- `Microsoft.Extensions.Hosting`