Skip to content

Add client sample w/ guidance on wiring up dependency injectionΒ #147

Open
@buvinghausen

Description

@buvinghausen

Is your feature request related to a problem? Please describe.
Not at all :)

Describe the solution you'd like
I realize this repo is nascent to the extreme and l genuinely appreciate all the work everyone is putting in to make this available to the .NET community. The samples are great and it's awesome you can bootstrap it so quickly from the console but it might be helpful to provide a sample that demonstrates how to best wire up clients for dependency injection. I'm more than happy to submit one if you would like a PR just let me know.

Additional context
I was thinking due to the ephemeral nature of the MCP docker containers something like this...

public abstract class DockerMcpServiceBase
{
    private readonly Task<IMcpClient> _clientTask;

    protected DockerMcpServiceBase(string container, string[]? flags = null,
        Dictionary<string, string>? environmentVariables = null,
        IReadOnlyDictionary<string, string>? parameters = null)
    {
        // Build the arguments array
        var args = new List<string>();
        if (flags is { Length: > 0 }) args.AddRange(flags);
        if (environmentVariables is { Count: > 0 })
            foreach (var (key, _) in environmentVariables)
            {
                args.Add("-e");
                args.Add(key);
            }
        args.Add(container);
        if (parameters is { Count: > 0 })
            foreach (var (key, value) in parameters)
                args.Add($"--{key}={value}");

        _clientTask = McpClientFactory.CreateAsync(
            new StdioClientTransport(new()
            {
                Name = container.Split('/')[1],
                Command = "docker",
                Arguments = ["run", "-i", "--rm", .. args],
                EnvironmentVariables = environmentVariables ?? []
            }));
    }

    public async Task<IEnumerable<McpClientTool>> GetToolsAsync(string[]? toolsToInclude = null,
        CancellationToken cancellationToken = default)
    {
        var client = await _clientTask;
        var tools = await client.ListToolsAsync(cancellationToken: cancellationToken);
        return toolsToInclude is { Length: > 0 } ? tools.Where(t => toolsToInclude.Contains(t.Name)) : tools;
    }
}

public class AtlassianMcpService(IConfiguration configuration) : DockerMcpServiceBase("mcp/atlassian",
    parameters: new Dictionary<string, string>
    {
        ["confluence-url"] =
            $"{configuration[Secrets.AtlassianServer] ?? throw new InvalidOperationException($"{Secrets.AtlassianServer} is not set")}wiki",
        ["confluence-username"] =
            configuration[Secrets.AtlassianEmail] ??
            throw new InvalidOperationException($"{Secrets.AtlassianEmail} is not set"),
        ["confluence-token"] =
            configuration[Secrets.AtlassianToken] ??
            throw new InvalidOperationException($"{Secrets.AtlassianToken} is not set"),
        ["jira-url"] =
            configuration[Secrets.AtlassianServer] ??
            throw new InvalidOperationException($"{Secrets.AtlassianServer} is not set"),
        ["jira-username"] =
            configuration[Secrets.AtlassianEmail] ??
            throw new InvalidOperationException($"{Secrets.AtlassianEmail} is not set"),
        ["jira-token"] =
            configuration[Secrets.AtlassianToken] ??
            throw new InvalidOperationException($"{Secrets.AtlassianToken} is not set")
    });

public class BraveSearchMcpService(IConfiguration configuration) : DockerMcpServiceBase("mcp/brave-search",
    environmentVariables: new Dictionary<string, string>
    {
        ["BRAVE_API_KEY"] = configuration[Secrets.BraveSearchToken] ??
                            throw new InvalidOperationException($"{Secrets.BraveSearchToken} is not set")
    });

public class FetchMcpService() : DockerMcpServiceBase("mcp/fetch");

public class GitHubMcpService(IConfiguration configuration) : DockerMcpServiceBase("mcp/github",
    environmentVariables: new Dictionary<string, string>
    {
        ["GITHUB_PERSONAL_ACCESS_TOKEN"] = configuration[Secrets.GitHubToken] ??
                                           throw new InvalidOperationException($"{Secrets.GitHubToken} is not set")
    });

public class GoogleMapsMcpService(IConfiguration configuration) : DockerMcpServiceBase("mcp/google-maps",
    environmentVariables: new Dictionary<string, string>
    {
        ["GOOGLE_MAPS_API_KEY"] = configuration[Secrets.GoogleMapsToken] ??
                                  throw new InvalidOperationException($"{Secrets.GoogleMapsToken} is not set")
    });

public class MemoryMcpService() : DockerMcpServiceBase("mcp/memory");

public class PuppeteerMcpService() : DockerMcpServiceBase("mcp/puppeteer", ["--init"],
    new Dictionary<string, string> { ["DOCKER_CONTAINER"] = "true" });

public class SequentialThinkingMcpService() : DockerMcpServiceBase("mcp/sequentialthinking");

public class TimeMcpService() : DockerMcpServiceBase("mcp/time");

Then it's simply wire up a singleton to the service collection and off you go.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions