DEV Community

Cover image for Smart Home MCP Server with VSCode Copilot
Victorio Berra
Victorio Berra

Posted on

Smart Home MCP Server with VSCode Copilot

What is MCP?

My short definition is: A protocol that lets LLMs talk to your code.

For example, if you have an API that returns a list of warehouses, how could you get Copilot to call your API and return a list of those warehouses? Or use the list internally to answer your questions about your warehouses?

The answer is MCP. These clients can use it https://modelcontextprotocol.io/clients

VSCode is in that list. They have added support for MCP https://code.visualstudio.com/docs/copilot/chat/mcp-servers

What can I write my MCP server in?

  • Python SDK
  • TypeScript SDK
  • Java SDK
  • Kotlin SDK
  • C# SDK

Using CSharp

I followed this tutorial, I will simplify here and adapt for my Smart Home

https://devblogs.microsoft.com/dotnet/build-a-model-context-protocol-mcp-server-in-csharp/

Create a .NET app:

  1. dotnet new console -n SmartHomeMCPServer
  2. dotnet add package ModelContextProtocol
  3. dotnet add package Microsoft.Extensions.Logging.Console
  4. dotnet add package Microsoft.Extensions.Hosting
  5. dotnet add package RestEase (http client helper https://github.com/canton7/RestEase)

I am using a Hubitat with an API plugin https://hubitat.com/.

This is what the API plugin shows me:

Hubitat Makrer API Endpoint List

I used Copilot edit mode to generate my app after feeding in the API spec above. I also fed it the RestEase docs which is just the entire README.md. This is the HTTP client it generated with RestEase:

public interface IHubitatApi
{
    [Get("apps/api/60/devices/all")]
    Task<List<Device>> GetAllDevicesAsync([Query("access_token")] string accessToken);

    [Get("apps/api/60/devices/{deviceId}/{command}/{secondaryValue}")]
    Task SendDeviceCommandAsync(
        [Path("deviceId")] string deviceId,
        [Path("command")] string command,
        [Path("secondaryValue")] string secondaryValue,
        [Query("access_token")] string accessToken
    );

    [Get("apps/api/60/devices/{deviceId}/{command}")]
    Task SendDeviceCommandAsync(
        [Path("deviceId")] string deviceId,
        [Path("command")] string command,
        [Query("access_token")] string accessToken
    );
}

public class Device
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Label { get; set; }
    public string Type { get; set; }
    public string Room { get; set; }
    public string Date { get; set; }
    public string Model { get; set; }
    public string Manufacturer { get; set; }
    public List<string> Capabilities { get; set; }
    public Attributes Attributes { get; set; }
    public List<Command> Commands { get; set; }
}

// ...
Enter fullscreen mode Exit fullscreen mode

After that, I wrapped the client around an MCP server:

[McpServerToolType]
public static class HubitatTool
{
    [McpServerTool, Description("Fetches all devices from the Hubitat API.")]
    public static async Task<List<Device>> GetAllDevices()
    {
        const string baseUrl = "http://192.168.1.120";
        const string accessToken = "YOUR-TOKEN";

        var api = RestClient.For<IHubitatApi>(baseUrl);
        return await api.GetAllDevicesAsync(accessToken);
    }

    [McpServerTool, Description("Send a command to a specific device.")]
    public static async Task SendDeviceCommand(string deviceId, string command, string secondaryValue = null)
    {
        const string baseUrl = "http://192.168.1.120";
        const string accessToken = "YOUR-TOKEN";

        var api = RestClient.For<IHubitatApi>(baseUrl);

        // Ensure secondaryValue is handled properly
        if (string.IsNullOrWhiteSpace(secondaryValue))
        {
            await api.SendDeviceCommandAsync(deviceId, command, accessToken);
        }
        else
        {
            await api.SendDeviceCommandAsync(deviceId, command, secondaryValue.Trim(), accessToken);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

I "vibe coded" the above, in a real world scenario id have a .env file for tokens and URLs.

Finally, you can ask the LLM to list your devices!

VS Code Copilot Conversation showing MCP usage

Top comments (0)