Add `transportContextProvider` to `McpClient.AsyncSpec`

未關閉
#1,075 3 則留言 0 個 reaction 已指派 1 人 在 GitHub 檢視

@Kehrlann 已經在處理了。

開始於 2026年8月7日。

評估

這個 Issue 還沒有評估資料。

描述

area/client area/transport waiting for user

Expected Behavior

AsyncSpec should accept a context provider the same way SyncSpec does, so a context can be attached to every client operation in one place:

McpClient.async(transport)
    .transportContextProvider(() -> McpTransportContext.create(Map.of("tenant", currentTenant())))
    .build();

The setter would mirror the sync one:

public AsyncSpec transportContextProvider(Supplier<McpTransportContext> contextProvider) {
    this.contextProvider = contextProvider;
    return this;
}

and McpAsyncClient would apply the same contextWrite that the sync client already applies. Defaulting to () -> McpTransportContext.EMPTY keeps existing behavior unchanged for anyone who doesn't set it.

One caveat worth documenting either way: the supplier is evaluated at subscribe time, so for an async client, it runs on whichever thread subscribes, which may not be the thread the application thinks of as the caller.

Current Behavior

transportContextProvider only exists on SyncSpec. On main at fd00498:

// McpClient
class SyncSpec { ... }                                                                      // 163
private Supplier<McpTransportContext> contextProvider = () -> McpTransportContext.EMPTY;    // 197
public SyncSpec transportContextProvider(Supplier<McpTransportContext> contextProvider)     // 503
class AsyncSpec { ... }                                                                     // 584

AsyncSpec has no equivalent field and no equivalent method. The javadoc is upfront about it, at line 497:

There is no direct equivalent in AsyncSpec. To achieve the same result, append contextWrite(McpTransportContext.KEY, context) to any McpAsyncClient call.

That instruction works. It just puts a cross-cutting concern at every call site.

Context

For a library that needs something attached to all requests, auth or tenancy or tracing, the documented workaround isn't usable, because the library doesn't own the call sites. The application does, and one missed call is a request that goes out without the context.

Nothing errors when that happens. The request just goes out without whatever the context was carrying, and you find out from the server's response, which typically won't say "your context was empty".

The change looks small. What SyncSpec does is mechanical:

// McpSyncClient:452
private <T> Mono<T> withProvidedContext(Mono<T> action) {
    return action.contextWrite(ctx -> ctx.put(McpTransportContext.KEY, this.contextProvider.get()));
}

Every public operation on McpSyncClient routes through it, 23 call sites, from initialize() at line 190 through completeCompletion(...) at 442. McpAsyncClient would need the same contextWrite in the same places.

Alternatives considered:

  • Follow the javadoc and contextWrite at every call site. Works for an application that owns all its call sites. Doesn't work for a library and doesn't survive the addition of a new call site later.
  • Wrap McpAsyncClient in a decorator that applies contextWrite to each method. Possible, but it has to be kept in sync with the interface by hand, and it doesn't help anyone who obtains the raw client from a framework.
  • Use the sync client instead. This is what we do today. That's a real functional restriction, not just an inconvenience.

Is the asymmetry deliberate, or can it be reconsidered?

主要語言
Java
星號
3.7k
分支
1.1k
平均合併
1 天 15 小時
30 天內合併 PR
9

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

modelcontextprotocol/java-sdk 的其他 Issue

查看 modelcontextprotocol/java-sdk 的全部 Issue

相似的 Issue

更多 Java Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。