Skip to content

[DRAFT] Restructure IMcpEndpoint, IMcpClient, and IMcpServer #723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

MackinnonBuck
Copy link

@MackinnonBuck MackinnonBuck commented Aug 21, 2025

Note: This PR is currently a work in progress. Future changes are expected.

Summary

Makes the following changes:

  • Removes the McpEndpoint abstract base class
  • Removes the IMcpClient interface
  • Renames the following types:
    • IMcpEndpoint -> IMcpSession
    • IMcpServer -> IMcpServerSession
    • McpClient -> McpClientSession
    • McpSession (internal) -> McpSessionHandler (still internal)
  • Makes McpClientSession public
    • i.e., usage of IMcpClient gets replaced with McpClientSession
  • Updates McpClientSession and McpServerSession to function without relying on a common base class implementing shared functionality

Note: The "session" naming is not final. We might end up keeping it, or replacing it with "connection", or dropping the suffix altogether and keeping the original McpClient and McpServer naming.

Description

The original issue comment suggested we remove the IMcpEndpoint, IMcpClient, and IMcpServer interfaces and make each corresponding class public.

This PR currently only removes the IMcpClient interface but keeps the other two (albeit with different names). However, I'd still like to pursue removing the other interfaces. Some notes about this:

  • McpServerSession is not the only implementation of IMcpServerSession. There's also DestinationBoundMcpServer, which wraps the underlying IMcpServerSession and augments it with the destination transport. This pattern wouldn't be possible if we were to remove IMcpServerSession. I need to think about and evaluate any alternative ways to achieve similar functionality for this specific case, without relying on an interface.
  • The base McpSession class would look different than the McpEndpoint abstract base class that exists today. This is because the current McpEndpoint class primarily serves as a way to share functionality among the existing McpClient and McpServer types. Simply making McpEndpoint public would drastically expand the public API surface, since each internal type exposed by a protected member would have to be made public (e.g., RequestHandlers, NotificationHandlers, and McpSessionHandler). What we'd end up doing instead is converting the IMcpEndpoint interface "directly" into an McpSession abstract class, replacing the current McpEndpoint abstract class. This PR already removes McpEndpoint and refactors McpServerSession and McpClientSession to not rely on a base class providing shared functionality, so it should be fairly staightforward to add back a new abstract base class whose purpose is more interface-like.

To Do

  • Evaluate whether it's feasible and desirable to remove IMcpServerSession and make McpServerSession public
    • If so, remove McpServerFactory and favor a public McpServerSession constructor
  • Remove McpClientFactory in favor of a non-static McpClient class, which:
    • Contains methods for instantiating connected McpClientSessions, such as:
      • ConnectAsync(string url)
      • LaunchAsync(params string[] commandLineArgs)
    • Manages all active McpClientSessions to enable disposing them all at once
    • Hold the McpClientOptions that configures each McpClientSession

Anticipated usage examples

Following are some examples for how I expect the usage of McpClient APIs to look:

Before

await using var sseClientTransport = new SseClientTransport(new()
{
    Endpoint = new("http://localhost:3001"),
    // ...
});
await using IMcpClient client = await McpClientFactory.CreateAsync(sseClientTransport);

After

// Disposing the client will terminate all sessions
await using McpClient client = new McpClient(options: /* ... */);

// Option 1: Provide full transport options directly
McpClientSession clientSession = await client.ConnectAsync(new SeeClientTransportOptions()
{
    Endpoint = new("http://localhost:3001"),
    // ...
});

// Option 2: Specify a URL only
McpClientSession clientSession = await client.ConnectAsync("http://localhost:3001");

// Option 3: Specify command line arguments to launch a local MCP server
McpClientSession clientSession = await client.LaunchAsync("-y", "--verbose", "@modelcontextprotocol/server-everything");

This resembles the changes proposed by @halter73 in #355 (comment)

Alternatively, we could rename McpClientFactory to McpClient and keep the existing factory approach. This would also work and is an approach worth considering. However, a non-static McpClient provides a convenient way to share configuration between sessions and dispose them all at once.


Fixes #355

@PederHP
Copy link
Collaborator

PederHP commented Aug 21, 2025

One downside to this renaming is that it distances the SDK from the TypeScript and Python SDKs, which makes it harder for developer to transition directly between. It's a very minor drift - adding postfixing -Session to McpClient. But I still think it's worth considering every time drift is introduced whether it is strictly necessary or a unified naming can be kept.

It is also preferable, I think, if McpClient aligns directly with the Client concept as described in: https://modelcontextprotocol.io/specification/2025-06-18/architecture.

I was never happy with McpClientFactory so I am glad to see it go, but definitely don't think it should be renamed to McpClient, as that was cause conceptual confusion with the Client concept in the specification. A client is a client session within a host.

Restructuring is definitely warranted, but I'd like to caution against drift from the other SDKs. There's a lot of polyglot development going in this space, and there are many upsides to having at least the specification concepts named identically in the SDKs.

With this change the SDK uses McpClient to mean something other than the specification and the other SDKs and uses McpClientSession to refer to the concept of a client. I can see the point of using the latter, but the former I am skeptical of. A client is a very well defined concept in the protocol spec and in the other SDKs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Convert IMcpEndpoint, IMcpClient, and IMcpServer types to classes.
2 participants