Friday, June 20, 2025

MCP server that connects VS Code to SQLite Database

In this tutorial we will configure VS Code to use an MCP Server that connects to a SQLite database. In a similar manner, you can also connect to PostgreSQL, MySQL, and SQL Server. This is a very compelling proposition because it allows developers to use AI to assist in generating code that dynamically interacts with data in a relational database.

Prerequisites

You will need to install the following software to proceed:

  • Visual Studio Code with the GitHub Copilot Chat extension
  • Docker Desktop
  • Latest versions of node.js, npm, and npx

The Database MCP Server

We will be using the MCP Server from the mcp-database-server GitHub Repo. Visit https://github.com/executeautomation/mcp-database-server for more details

Install and configure the SQLite MCP server

In a suitable working directory, clone the repo, then build, and publish the code by executing these commands in a terminal window:

git clone https://github.com/executeautomation/mcp-database-server.git
cd mcp-database-server
npm install
npm run build

We will next install the MCP server globally with:

npm install -g @executeautomation/database-server

We will run an SQLite using a sample Northwind database.

To use the MCP server with our SQLite database, run the following terminal window command:

node dist/src/index.js ./northwind-sqlite.db

Keep the above terminal window open and running.

Configuring VS Code

Open VS Code. Click on the settings gear in the bottom-left corner, followed by Settings.

In the search field, enter MCP, then click on "Edit in settings.json".

Under the mcp >> servers section, add the following MCP server settings:

"sqlite": {
  "command": "npx",
  "args": [
    "-y",
    "@executeautomation/database-server",
    "/path/to/your/northwind-sqlite.db"
  ]
},

Click on Start:

Open the GitHub Copilot Chat panel:

In the GitHub Copilot Chat panel, choose any Claude model followed by Agent Mode.

Click on the tools icon in the prompt window.

You will see a list of commands that the MCP server can carry out with SQLite.

We can now start querying the database using natural language. Start with this prompt:


You have access to the Northwind database through an MCP server. What are the tables in the database?

It detects that it can use the list_tables command.

Click on Continue. I got the following output:

Similarly, you can ask another question like: 

Display the contents of the suppliers table.

Yet, another question:

What are the products supplied by "Exotic Liquids"?

Conclusion

It is very easy to connect VS Code with a relational database MCP server. In addition, you can similarly connect MCP Servers any client C# application. MCP Servers open a ton of possibilities for AI aided software development.

MCP server that connects VS Code to PostgreSQL Database

In this tutorial we will configure VS Code to use an MCP Server that connects to a PostgreSQL database. In a similar manner, you can also connect to SQLite, MySQL, and SQL Server. This is a very compelling proposition because it allows developers to use AI to assist in generating code that dynamically interacts with data in a relational database.

Prerequisites

You will need to install the following software to proceed:

  • Visual Studio Code with the GitHub Copilot Chat extension
  • Docker Desktop
  • Latest versions of node.js, npm, and npx

The Database

We will run a PostgreSQL database with a sample Northwind database in a Docker container. Therefore:

  • Start Docker Desktop on your computer
  • Run a PostgreSQL container by executing this command in a terminal window:


docker run --name psqlnw -e POSTGRES_PASSWORD=VerySecret -p 5433:5432 -d melmasry/nw-psql:1.0.0

The Database MCP Server

We will be using the MCP Server from the mcp-database-server GitHub Repo. Visit https://github.com/executeautomation/mcp-database-server for more details.

Install and configure the PostgreSQL MCP server

In a suitable working directory, clone the repo, then build, and publish the code by executing these commands in a terminal window:


git clone https://github.com/executeautomation/mcp-database-server.git
cd mcp-database-server
npm install
npm run build

We will next install the MCP server globally with:


npm install -g @executeautomation/database-server

To use the MCP server with our MySQL database, run the following terminal window command:


node dist/src/index.js --postgresql --host localhost --port 5433 --database northwind --user postgres --password VerySecret

Keep the above terminal window open and running.

Configuring VS Code

Open VS Code. Click on the settings gear in the bottom-left corner, followed by Settings.

In the search field, enter MCP, then click on "Edit in settings.json".

Under the mcp >> servers section, add the following MCP server settings:

"postgresql": {
  "command": "npx",
  "args": [
    "-y",
    "@executeautomation/database-server",
    "--postgresql",
    "--host", "localhost",
    "--port", "5433",
    "--database", "northwind",
    "--user", "postgres",
    "--password", "VerySecret"
  ]
},

Click on Start:

Open the GitHub Copilot Chat panel:

In the GitHub Copilot Chat panel, choose any Claude model followed by Agent Mode.

Click on the tools icon in the prompt window.

You will see a list of commands that the MCP server can carry out with PostgreSQL.

We can now start querying the database using natural language. Start with this prompt:


You have access to the Northwind database through an MCP server. What are the tables in the database?

It detects that it can use the list_tables command.

Click on Continue. I got the following output:

Similarly, you can ask another question like: 

 
Display the contents of the suppliers table.

Yet, another question:

     
What are the products supplied by "Exotic Liquids"?

Conclusion

It is very easy to connect VS Code with a relational database MCP server. In addition, you can similarly connect MCP Servers any client C# application. MCP Servers open a ton of possibilities for AI aided software development. 

Thursday, June 19, 2025

MCP server that connects VS Code to MySQL database

In this tutorial we will configure VS Code to use an MCP Server that connects to a MySQL database. In a similar manner, you can also connect to SQLite, PostgresDB, and SQL Server. This is a very compelling proposition because it allows developers to use AI to assist in generating code that dynamically interacts with data in a relational database.

Prerequisites

You will need to install the following software to proceed:

  • Visual Studio Code with the GitHub Copilot Chat extension
  • Docker Desktop
  • Latest versions of node.js, npm, and npx

The Database

We will run a MySQL database with a sample Northwind database in a Docker container. Therefore:

  • Start Docker Desktop on your computer
  • Run a MySQL container by executing this command in a terminal window:


docker run -p 3366:3306 --name mariadb-nw -e MYSQL_ROOT_PASSWORD=secret -d melmasry/maria-nw:1.0.0

The Database MCP Server

We will be using the MCP Server from the mcp-database-server GitHub Repo. Visit https://github.com/executeautomation/mcp-database-server for more details.

Install and configure the MySQL MCP server

In a suitable working directory, clone the repo, then build, and publish the code by executing these commands in a terminal window:


git clone https://github.com/executeautomation/mcp-database-server.git
cd mcp-database-server
npm install
npm run build

We will next install the MCP server globally with:


npm install -g @executeautomation/database-server

To use the MCP server with our MySQL database, run the following terminal window command:

node dist/src/index.js --mysql --host localhost --database northwind --port 3366 --user root --password secret

Keep the above terminal window open and running.

Configuring VS Code

Open VS Code. Click on the settings gear in the bottom-left corner, followed by Settings.

In the search field, enter MCP, then click on "Edit in settings.json".

Under the mcp >> servers section, add the following MCP server settings:

 
"mysql": {
  "command": "npx",
  "args": [
    "-y",
    "@executeautomation/database-server",
    "--mysql",
    "--host", "localhost",
    "--database", "northwind",
    "--port", "3366",
    "--user", "root",
    "--password", "secret"
  ]
}

Click on Start:

Open the GitHub Copilot Chat panel:

In the GitHub Copilot Chat panel, choose any Claude model followed by Agent Mode.

Click on the tools icon in the prompt window.

You will see a list of commands that the MCP server can carry out with MySQL.

We can now start querying the database using natural language. Start with this prompt:


You have access to the Northwind database through an MCP server. What are the tables in the database?

Click on Continue. I got the following output:

Similarly, you can ask another question like: 

  
Display the contents of the suppliers table.

Yet, another question:


What are the products supplied by "Exotic Liquids"?

Conclusion

It is very easy to connect VS Code with a relational database MCP server. In addition, you can similarly connect MCP Servers any client C# application. MCP Servers open a ton of possibilities for AI aided software development. 

Monday, June 16, 2025

Create your own C# MCP server and client

This article provides a step-by-step guide on creating and configuring a C# MCP server in Visual Studio Code, enabling seamless integration with a students API for development purposes. It covers the entire process, from setting up the server, deploying it Docker Hub,  to consuming its functionality within a command-line C# application.

Source Code: https://github.com/medhatelmasry/McpStudents

Prerequisites

Install the following in order to proceed:

  • Visual Studio Code with the GitHub Copilot Chat extension
  • Docker Desktop
  • .NET 9.0

Getting Started

We will first create a solution containing an MCP server project. In a working directory, run the following .NET commands:

mkdir McpStudents
cd McpStudents
dotnet new sln
dotnet new console -n StudentsMcpServer
dotnet sln add ./StudentsMcpServer/StudentsMcpServer.csproj
cd StudentsMcpServer
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
dotnet add package System.Text.Json

Open the above solution source code in VS Code, with:

cd ..
code .

Visit https://github.com/modelcontextprotocol/csharp-sdk. Replace the code in Program.cs with the code under “Getting Started (Server)”.

Let us add additional functionality for reversing text and displaying the length of text. Inside the EchoTool class in Program.cs, add these two additional methods.


[McpServerTool, Description("Echoes back message in reverse.")]
public static string Reverse(string message) => new string(message.Reverse().ToArray());

[McpServerTool, Description("Returns length of the message.")]
public static int Length(string message) => message.Length;

Configuring VS Code

The next step is to get VS Code to recognize the MCP server.

In the root of your solution (not project), create a folder named .vscode. Then, inside the .vscode folder, create a text file named mcp.json. You will see a button "Add Server...". If you do not see the "Add Server..." button, do the above using the default tab in VS Code, instead of Solution Explorer. 

Select “Command (stdio) Run a local command that implements the MCP server”:

Command to run: dotnet run

Server ID: student-mcp-server

The following content is created in mcp.json file:

{
    "servers": {
        "student-msp-server": {
            "type": "stdio",
            "command": "dotnet",
            "args": [
                "run"
            ]
        }
    }
}

Right-click on the .csproj file and copy path:

With the path saved in your computer’s clipboard, update the args section in mcp.json so it looks like this:


{
	"servers": {
        "student-mcp-server": {
            "type": "stdio",
            "command": "dotnet",
            "args": [
                "run",
                "--project",
                "ABSOLUTE_PATH"
            ]
        }
    }
}


Start the server.

Open the GitHub Copilot Chat panel:

In the GitHub Copilot Chat panel, choose any Claude model followed by Agent Mode.

Click on the tools icon in the prompt window.

You will see that the student-mcp-server server has started:

Enter this message in the chat window:


Get me the length of the following message: The rain in Spain stays mainly on the plain.

The response:

Next ask it: 


Can you reverse that string?

 

The response is:

Something more real

We will add an MCP server that we can use to query data from an API source at: https://apipool.azurewebsites.net/api/students

In a Models folder, add a Student class that represents each item of data in the API endpoint, with this content:

public class Student {
  public int StudentId { get; set; }
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
  public string? School { get; set; }

  public override string ToString() {
    return $"Student ID: {StudentId}, First Name: {FirstName}, Last Name: {LastName}, School: {School}";
  }
}

In the Models folder, add a class named StudentContext with this code:

[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(List<Student>))]
[JsonSerializable(typeof(Student))]
internal sealed partial class StudentContext : JsonSerializerContext { }

The StudentContext partial class is used for source generation of optimized JSON serialization and deserialization code with System.Text.Json in .NET.

Also inside the Models folder, add another class named StudentService with this code:

public class StudentService {
  readonly HttpClient _httpClient = new();
  private List<Student>? _studentsCache = null;
  private DateTime _cacheTime;
  private readonly TimeSpan _cacheDuration = TimeSpan.FromMinutes(10); // adjust as needed

  private async Task<List<Student>> FetchStudentsFromApi() {
    try {
      var response = await _httpClient.GetAsync("https://apipool.azurewebsites.net/api/students");
      if (response.IsSuccessStatusCode) {
        var studentsFromApi = await response.Content.ReadFromJsonAsync<List<Student>>(StudentContext.Default.ListStudent);
        return studentsFromApi ?? [];
        }
    } catch (Exception ex) {
      await Console.Error.WriteLineAsync($"Error fetching students from API: {ex.Message}");
    }
    return [];
  }

  public async Task<List<Student>> GetStudents() {
    if (_studentsCache == null || DateTime.UtcNow - _cacheTime > _cacheDuration) {
      _studentsCache = await FetchStudentsFromApi();
      _cacheTime = DateTime.UtcNow;
    }
    return _studentsCache;
  }

  public async Task<Student?> GetStudentByFullName(string name) {
    var students = await GetStudents();

    var nameParts = name.Split(' ', 2);
    if (nameParts.Length != 2) {
      Console.WriteLine("Name does not contain two parts");
      return null;
    }

    var firstName = nameParts[0].Trim();
    var lastName = nameParts[1].Trim();

    foreach (var s in students.Where(s => s.FirstName?.Contains(firstName, StringComparison.OrdinalIgnoreCase) == true)) {
      Console.WriteLine($"Found partial first name match: '{s.FirstName}' '{s.LastName}'");
    }

    var student = students.FirstOrDefault(m => {
      var firstNameMatch = string.Equals(m.FirstName, firstName, StringComparison.OrdinalIgnoreCase);
      var lastNameMatch = string.Equals(m.LastName, lastName, StringComparison.OrdinalIgnoreCase);
      return firstNameMatch && lastNameMatch;
    });

    return student;
  }

  public async Task<Student?> GetStudentById(int id) {
    var students = await GetStudents();
    var student = students.FirstOrDefault(s => s.StudentId == id);

    Console.WriteLine(student == null ? $"No student found with ID {id}" : $"Found student: {student}");
    return student;
  }

  public async Task<List<Student>> GetStudentsBySchoolJson(string school) {
    var students = await GetStudents();
    var filteredStudents = students.Where(s => s.School?.Equals(school, StringComparison.OrdinalIgnoreCase) == true).ToList();

    Console.WriteLine(filteredStudents.Count == 0
        ? $"No students found for school: {school}"
        : $"Found {filteredStudents.Count} students for school: {school}");

    return filteredStudents;
  }

  public async Task<List<Student>> GetStudentsByLastName(string lastName) {
    var students = await GetStudents();
    var filteredStudents = students.Where(s => s.LastName?.Equals(lastName, StringComparison.OrdinalIgnoreCase) == true).ToList();

    Console.WriteLine(filteredStudents.Count == 0
      ? $"No students found with last name: {lastName}"
      : $"Found {filteredStudents.Count} students with last name: {lastName}");

    return filteredStudents;
  }

  public async Task<List<Student>> GetStudentsByFirstName(string firstName) {
    var students = await GetStudents();
    var filteredStudents = students.Where(s => s.FirstName?.Equals(firstName, StringComparison.OrdinalIgnoreCase) == true).ToList();

    Console.WriteLine(filteredStudents.Count == 0
      ? $"No students found with first name: {firstName}"
      : $"Found {filteredStudents.Count} students with first name: {firstName}");

    return filteredStudents;
  }

  public async Task<string> GetStudentsJson() {
    var students = await GetStudents();
    return System.Text.Json.JsonSerializer.Serialize(students);
  }
}

Again, in the Models folder, add a class named StudentTools with this content:


[McpServerToolType]
public class StudentTools(StudentService studentService) {
  [McpServerTool, Description("Get a list of students and return as JSON array")]
  public Task<string> GetStudentsJson() {
    return studentService.GetStudentsJson();
  }    
  
  [McpServerTool, Description("Get a list of students")]
  public Task<List<Student>> GetStudents() {
    return studentService.GetStudents();
  }

  [McpServerTool, Description("Get a student by name")]
  public Task<Student?> GetStudent([Description("The name of the student to get details for")] string name) {
    return studentService.GetStudentByFullName(name);
  }
  
  [McpServerTool, Description("Get a student by ID")]
  public Task<Student?> GetStudentById([Description("The ID of the student to get details for")] int id) {
    return studentService.GetStudentById(id);
  }
  
  [McpServerTool, Description("Get a student by ID and return as JSON")]
  public async Task<string?> GetStudentByIdJson([Description("The ID of the student to get details for")] int id){
    var student = await studentService.GetStudentById(id);
    if (student == null) {
      return null;
    }
    
    return System.Text.Json.JsonSerializer.Serialize(student, StudentContext.Default.Student);
  }
  
  [McpServerTool, Description("Get a student by name and return as JSON")]
  public async Task<string?> GetStudentJson([Description("The name of the student to get details for")] string name) {
    var student = await studentService.GetStudentByFullName(name);
    if (student == null) {
      return null;
    }
    
    return System.Text.Json.JsonSerializer.Serialize(student, StudentContext.Default.Student);
  }
  
  [McpServerTool, Description("Get students by school")]
  public async Task<List<Student>> GetStudentsBySchool([Description("The name of the school to filter students by")] string school) {
    var students = await studentService.GetStudentsBySchoolJson(school);
    return students;
  }

  [McpServerTool, Description("Get a student by Last Name")]
  public async Task<List<Student>> GetStudentsByLastName([Description("The last name of the student to filter by")] string lastName) {
    var students = await studentService.GetStudentsByLastName(lastName);
    return students;
  }

  [McpServerTool, Description("Get a student by First Name")]
  public async Task<List<Student>> GetStudentsByFirstName([Description("The first name of the student to filter by")] string firstName) {
    var students = await studentService.GetStudentsByFirstName(firstName);
    return students;
  }
}

Register StudentService in Program.cs by adding this code right before “await builder.Build().RunAsync();”:


builder.Services.AddSingleton<StudentService>();

In mcp.json, restart the student-mcp-server. It should now show that there are 12 tools.

Enter this prompt in the chat:


Get me a list of students and display them in a table for easy reading here.

Sample output:

Deploy Server as MCP Docker Image

Add this section to StudentMcpServer.csproj:

<PropertyGroup>
    <EnableSdkContainerSupport>true</EnableSdkContainerSupport>
    <ContainerRepository>your-docker-user-name/studentmcp</ContainerRepository>
    <ContainerFamily>alpine</ContainerFamily>
    <RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
</PropertyGroup>

Publish to an mage on hub.docker.com by running this command inside the server project.

docker login --username=your-docker-user-name
dotnet publish /t:PublishContainer -p ContainerRegistry=docker.io

If 2FA is enabled on your Docker Hub account, then login with:

docker login -u your-docker-user-name --password=your-docker-hub-personal-access-token

To use the published image, replace the current content in mcp.json with:


{
    "servers": {
        "student-mcp-server": {
        "command": "docker",
        "args": [
            "run",
            "-i",
            "--rm",
            "your-docker-user-name/studentmcp"
        ],
        "env": {}
        }
    }
}

You can double-check that the docker image has indeed been published by searching for it in Docker Desktop:

Restart the MCP server. You will notice a new image and running container:



Try out the students MCP server by asking it some questions about students. For example:

List students in the school of mining.

Simple MCP C# client that uses Ollama

Up to now, we have relied on VS Code as our client. In fact, you can make any of your applications a client to an MCP server. Let us create a simple command-line application that takes on the role of a client to our C# MCP Server. 

The client app will use ollama to host the small language model (LLM) llama3.2:3b our local computer. If you do not already have ollama installed on your computer, you can get it from https://ollama.com/download.   

Pull the LLM with:

ollama pull llama3.2:3b

Inside the root solution folder, McpStudents, add a client console application with the following terminal window commands:

ollama pull llama3.2:3b
dotnet new console -n OllamaMcpClient
dotnet sln add ./OllamaMcpClient/OllamaMcpClient.csproj
cd OllamaMcpClient
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.AI.Ollama --prerelease
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.Logging
dotnet add package Microsoft.Extensions.Logging.Console
cd ..

Replace contents of Program.cs in the OllamaMcpClient project with:


using ModelContextProtocol.Client;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.AI;
using System.Text.Json;

// MCP Client Transport
string currentDirectory = Directory.GetCurrentDirectory();
string serverDirectory = Path.GetFullPath(Path.Combine(currentDirectory, "..", "StudentsMcpServer"));
var clientTransport = new StdioClientTransport(new StdioClientTransportOptions
{
    Name = "Students MCP Server",
    Command = "dotnet",
    Arguments = ["run"],
    WorkingDirectory = serverDirectory,
});

// Logger
using var loggerFactory = LoggerFactory.Create(builder =>
    builder.AddConsole().SetMinimumLevel(LogLevel.Information));

// Create MCP Client
var mcpClient = await McpClientFactory.CreateAsync(clientTransport);

// Get available tools from MCP Server
var mcpTools = await mcpClient.ListToolsAsync();

var toolsJson = JsonSerializer.Serialize(mcpTools, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine("\nAvailable Tools:\n" + toolsJson);

await Task.Delay(100);

// Configure Ollama LLM Client
var ollamaChatClient = new OllamaChatClient(
    new Uri("http://localhost:11434/"),
    "llama3.2:3b"
);

var chatClient = new ChatClientBuilder(ollamaChatClient)
    .UseLogging(loggerFactory)
    .UseFunctionInvocation()
    .Build();

// Prompt loop
Console.WriteLine("Type your message below (type 'exit' to quit):");

while (true)
{
    Console.Write("\n You: ");
    var userInput = Console.ReadLine();

    if (string.IsNullOrWhiteSpace(userInput))
        continue;

    if (userInput.Trim().ToLower() == "exit")
    {
        Console.WriteLine("Exiting chat...");
        break;
    }

    var messages = new List<ChatMessage>
    {
        new(ChatRole.System, "You are a helpful assistant."),
        new(ChatRole.User, userInput)
    };

    try
    {
        var response = await chatClient.GetResponseAsync(
            messages,
            new ChatOptions { Tools = mcpTools.ToArray<AITool>() });

        var assistantMessage = response.Messages.LastOrDefault(m => m.Role == ChatRole.Assistant);

        if (assistantMessage != null)
        {
            var textOutput = string.Join($" ", assistantMessage.Contents.Select(c => c.ToString()));
            Console.WriteLine("\n AI: " + textOutput);
        }
        else
        {
            Console.WriteLine("\n AI: (no assistant message received)");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"\n Error: {ex.Message}");
    }
}

Let's find out if our client app works. Inside a terminal window in the OllamaMcpClient folder, run the console app with:


dotnet run

I asked for details about the student with ID = 15 and got this output:

MCP Client using OpenAI or Azure OpenAI

Visit source code on GitHub to view MCP client code that works with  OpenAI or Azure OpenAI.

Conclusion

By now, you have experienced the power of MCP and how it can be used to connect your custom resources which can be connected to AI. This not only helps with the process of software development, but it also opens up the opportunity to link your apps to live resources.