You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by zh...@apache.org on 2026/08/29 15:59:46 UTC
(maka) 02/02: feat(runtime-host): select providers by Client owner
This is an automated email from the ASF dual-hosted git repository.
me2seeks pushed a commit to branch feat/3838-mcp-provider-association
in repository https://gitbox.apache.org/repos/asf/maka.git
commit 4f4b159fd450cae7d512ab68d8134a7fb0419be9
Author: me2seeks <me...@gmail.com>
AuthorDate: Sat Aug 29 23:50:38 2026 +0800
feat(runtime-host): select providers by Client owner
Prefer the initiating Client's direct provider, then one exact Host-bound companion. Keep unrelated providers invisible to remote Clients and fail closed when more than one companion claims the same owner.
Generated-by: Codex
---
.../client-capability-coordinator.test.ts | 130 +++++++++++++++++++++
.../src/__tests__/fixtures/client-capability.ts | 9 +-
.../src/server/client-capability-coordinator.ts | 108 ++++++++++++++---
3 files changed, 227 insertions(+), 20 deletions(-)
diff --git a/packages/runtime-host/src/__tests__/client-capability-coordinator.test.ts b/packages/runtime-host/src/__tests__/client-capability-coordinator.test.ts
index b8537d82e..262acc2f7 100644
--- a/packages/runtime-host/src/__tests__/client-capability-coordinator.test.ts
+++ b/packages/runtime-host/src/__tests__/client-capability-coordinator.test.ts
@@ -301,6 +301,119 @@ describe('Host Client Capability coordinator', () => {
await coordinator.close();
});
+ test('selects only the provider bound to the exact initiating Client identity', async () => {
+ const coordinator = createCoordinator();
+ const owner = coordinator.attachConnection(
+ clientCapabilityConnectionIdentity(
+ 'owner-connection',
+ 'owner-client',
+ 'owner-principal',
+ 'remote_owner',
+ ),
+ { send: async () => {} },
+ );
+ const unrelated = coordinator.attachConnection(
+ clientCapabilityConnectionIdentity(
+ 'unrelated-provider',
+ 'unrelated-provider-client',
+ 'unrelated-provider-principal',
+ 'capability_provider',
+ { principalId: 'other-principal', clientInstanceId: 'other-client' },
+ ),
+ { send: async () => {} },
+ );
+ await replaceTrustedProvider(
+ coordinator,
+ 'unrelated-provider',
+ 'unrelated-registration',
+ 'inspect',
+ );
+
+ assert.deepEqual(await coordinator.bindSession('unrelated-only', 'owner-connection'), {
+ ok: true,
+ });
+ assert.equal(coordinator.snapshotForSession('unrelated-only'), undefined);
+
+ const associated = coordinator.attachConnection(
+ clientCapabilityConnectionIdentity(
+ 'associated-provider',
+ 'associated-provider-client',
+ 'associated-provider-principal',
+ 'capability_provider',
+ { principalId: 'owner-principal', clientInstanceId: 'owner-client' },
+ ),
+ { send: async () => {} },
+ );
+ await replaceTrustedProvider(
+ coordinator,
+ 'associated-provider',
+ 'associated-registration',
+ 'inspect',
+ );
+ assert.throws(
+ () =>
+ coordinator.attachConnection(
+ clientCapabilityConnectionIdentity(
+ 'changed-owner-provider',
+ 'associated-provider-client',
+ 'associated-provider-principal',
+ 'capability_provider',
+ { principalId: 'other-principal', clientInstanceId: 'other-client' },
+ ),
+ { send: async () => {} },
+ ),
+ /provider owner changed/u,
+ );
+
+ assert.deepEqual(await coordinator.bindSession('associated', 'owner-connection'), { ok: true });
+ const snapshot = coordinator.snapshotForSession('associated');
+ assert.deepEqual(snapshot?.registrationIds, ['associated-registration']);
+ snapshot?.release();
+
+ const otherClient = coordinator.attachConnection(
+ clientCapabilityConnectionIdentity(
+ 'other-client-connection',
+ 'different-client',
+ 'owner-principal',
+ 'remote_owner',
+ ),
+ { send: async () => {} },
+ );
+ assert.deepEqual(await coordinator.bindSession('different-client', 'other-client-connection'), {
+ ok: true,
+ });
+ assert.equal(coordinator.snapshotForSession('different-client'), undefined);
+
+ const duplicate = coordinator.attachConnection(
+ clientCapabilityConnectionIdentity(
+ 'duplicate-provider',
+ 'duplicate-provider-client',
+ 'duplicate-provider-principal',
+ 'capability_provider',
+ { principalId: 'owner-principal', clientInstanceId: 'owner-client' },
+ ),
+ { send: async () => {} },
+ );
+ await replaceTrustedProvider(
+ coordinator,
+ 'duplicate-provider',
+ 'duplicate-registration',
+ 'inspect',
+ );
+ const ambiguous = await coordinator.bindSession('duplicate-owner', 'owner-connection');
+ assert.equal(ambiguous.ok, false);
+ if (!ambiguous.ok) assert.match(ambiguous.message, /bound to the initiating Client/u);
+
+ await Promise.all([
+ owner.close(),
+ unrelated.close(),
+ associated.close(),
+ otherClient.close(),
+ duplicate.close(),
+ ]);
+ await coordinator.close();
+ });
+
test('previews the initiating provider without persisting a Session binding', async () => {
const coordinator = createCoordinator();
const connection = coordinator.attachConnection(
@@ -1190,6 +1303,23 @@ async function replace(
assert.equal(outcome.ok, true);
}
+async function replaceTrustedProvider(
+ coordinator: HostClientCapabilityCoordinator,
+ connectionId: string,
+ registrationId: string,
+ toolName: string,
+): Promise<void> {
+ const input = replacementInput(registrationId, toolName);
+ const outcome = await coordinator.handlers['client.capability.replace'](
+ {
+ ...input,
+ offers: input.offers.map((offer) => ({ ...offer, hostPathAccess: 'none' as const })),
+ },
+ connectionContext(connectionId),
+ );
+ assert.equal(outcome.ok, true);
+}
+
function replacementInput(
registrationId: string,
toolName: string,
diff --git a/packages/runtime-host/src/__tests__/fixtures/client-capability.ts b/packages/runtime-host/src/__tests__/fixtures/client-capability.ts
index eb4ced75d..d743609b7 100644
--- a/packages/runtime-host/src/__tests__/fixtures/client-capability.ts
+++ b/packages/runtime-host/src/__tests__/fixtures/client-capability.ts
@@ -24,6 +24,13 @@ export function clientCapabilityConnectionIdentity(
clientInstanceId = connectionId,
principalId = 'test-principal',
principalKind: ClientCapabilityConnectionIdentity['principalKind'] = 'local_owner',
+ capabilityOwner?: ClientCapabilityConnectionIdentity['capabilityOwner'],
): ClientCapabilityConnectionIdentity {
- return { connectionId, principalId, clientInstanceId, principalKind };
+ return {
+ connectionId,
+ principalId,
+ clientInstanceId,
+ principalKind,
+ ...(capabilityOwner ? { capabilityOwner } : {}),
+ };
}
diff --git a/packages/runtime-host/src/server/client-capability-coordinator.ts b/packages/runtime-host/src/server/client-capability-coordinator.ts
index 1e54236d9..f036185b5 100644
--- a/packages/runtime-host/src/server/client-capability-coordinator.ts
+++ b/packages/runtime-host/src/server/client-capability-coordinator.ts
@@ -25,6 +25,7 @@ import type { RootExecutionDescriptor } from '@maka/core/agent-run';
import { type ToolGroup } from '@maka/runtime/tool-availability';
import {
type ClientCapabilityOffer,
+ type ClientCapabilityOwnerIdentity,
type ClientCapabilityReplaceInput,
type ClientCapabilityServiceOffer,
type ClientCapabilityToolDescriptor,
@@ -65,7 +66,9 @@ interface ClientProviderState {
readonly providerId: string;
readonly principalId: string;
readonly clientInstanceId: string;
+ readonly principalKind: ClientCapabilityConnectionIdentity['principalKind'];
readonly trustedProvider: boolean;
+ readonly capabilityOwner?: ClientCapabilityOwnerIdentity;
activeConnectionId?: string;
current?: CapabilityRegistration;
readonly registrations: Map<string, CapabilityRegistration>;
@@ -333,14 +336,41 @@ export class HostClientCapabilityCoordinator implements ClientCapabilityService
initiatingConnectionId: string,
mode: SessionBindingMode,
): SessionBindingSelection {
- const initiatingProviderId = this.#connections.get(initiatingConnectionId)?.provider.providerId;
- const initiatingProvider = initiatingProviderId
- ? this.#providers.get(initiatingProviderId)
- : undefined;
+ const initiatingProvider = this.#connections.get(initiatingConnectionId)?.provider;
+ const directProvider =
+ initiatingProvider?.current && this.#activeConnection(initiatingProvider)
+ ? initiatingProvider
+ : undefined;
+ const associatedProviders = initiatingProvider
+ ? [...this.#providers.values()].filter(
+ (provider) =>
+ provider.trustedProvider &&
+ provider.current !== undefined &&
+ this.#activeConnection(provider) !== undefined &&
+ provider.capabilityOwner?.principalId === initiatingProvider.principalId &&
+ provider.capabilityOwner.clientInstanceId === initiatingProvider.clientInstanceId,
+ )
+ : [];
+ if (!directProvider && associatedProviders.length > 1) {
+ return {
+ ok: false,
+ message: 'Multiple Client Capability providers are bound to the initiating Client',
+ };
+ }
+ const selectedInitiatingProvider = directProvider ?? associatedProviders[0];
+ // A remote Client must never inherit an unrelated provider merely because
+ // it is the only candidate. Local-owner and recovery flows retain their
+ // existing provider-independent fallback when no provider was selected.
+ const initiatingProviderId =
+ selectedInitiatingProvider?.providerId ??
+ (initiatingProvider?.principalKind === 'remote_owner'
+ ? initiatingProvider.providerId
+ : undefined);
const previousState = this.#sessions.get(sessionId);
const serviceProviderId =
previousState?.serviceProviderId ??
- (initiatingProvider?.current && initiatingProvider.current.servicesByContract.size > 0
+ (selectedInitiatingProvider?.current &&
+ selectedInitiatingProvider.current.servicesByContract.size > 0
? initiatingProviderId
: undefined);
const previous = previousState?.sessionBindings ?? new Map();
@@ -388,10 +418,8 @@ export class HostClientCapabilityCoordinator implements ClientCapabilityService
};
}
} else {
- candidate =
- candidates.find((entry) => entry.registration.providerId === initiatingProviderId) ??
- (candidates.length === 1 ? candidates[0] : undefined);
- if (!candidate && candidates.length > 1) {
+ candidate = selectProviderCandidate(candidates, initiatingProviderId);
+ if (!candidate && initiatingProviderId === undefined && candidates.length > 1) {
if (mode === 'degrade') continue;
return {
ok: false,
@@ -436,9 +464,7 @@ export class HostClientCapabilityCoordinator implements ClientCapabilityService
left.localeCompare(right),
)) {
if (candidates[0]?.offer.offer.affinity !== 'turn') continue;
- const candidate =
- candidates.find((entry) => entry.registration.providerId === initiatingProviderId) ??
- (candidates.length === 1 ? candidates[0] : undefined);
+ const candidate = selectProviderCandidate(candidates, initiatingProviderId);
if (!candidate || offerConflictsWithProxyNames(candidate.offer, proxyNames)) continue;
nextTurn.set(contractId, {
kind: 'bound',
@@ -510,7 +536,13 @@ export class HostClientCapabilityCoordinator implements ClientCapabilityService
for (const [contractId, candidates] of [...eligible].sort(([left], [right]) =>
left.localeCompare(right),
)) {
- const offer = candidates[0]?.offer;
+ // A provider-independent snapshot keeps one representative descriptor so
+ // a dynamic call can report ambiguity. A remote Client's explicit
+ // selector must instead hide every unrelated provider.
+ const offer =
+ state?.initiatingProviderId === undefined
+ ? candidates[0]?.offer
+ : selectProviderCandidate(candidates, state.initiatingProviderId)?.offer;
if (
!offer ||
offer.offer.affinity !== 'call' ||
@@ -987,18 +1019,32 @@ export class HostClientCapabilityCoordinator implements ClientCapabilityService
#provider(identity: ClientCapabilityConnectionIdentity): ClientProviderState {
const providerId = clientProviderId(identity.principalId, identity.clientInstanceId);
const trustedProvider = identity.principalKind === 'capability_provider';
+ if (identity.capabilityOwner && !trustedProvider) {
+ throw new Error('Only a capability provider may declare a Client Capability owner');
+ }
let provider = this.#providers.get(providerId);
if (!provider) {
provider = {
providerId,
principalId: identity.principalId,
clientInstanceId: identity.clientInstanceId,
+ principalKind: identity.principalKind,
trustedProvider,
+ ...(identity.capabilityOwner
+ ? { capabilityOwner: Object.freeze({ ...identity.capabilityOwner }) }
+ : {}),
registrations: new Map(),
};
this.#providers.set(providerId, provider);
- } else if (provider.trustedProvider !== trustedProvider) {
+ } else if (
+ provider.principalKind !== identity.principalKind ||
+ provider.trustedProvider !== trustedProvider
+ ) {
throw new Error('Client Capability provider authority changed across connections');
+ } else if (
+ !clientCapabilityOwnerIdentitiesEqual(provider.capabilityOwner, identity.capabilityOwner)
+ ) {
+ throw new Error('Client Capability provider owner changed across connections');
}
return provider;
}
@@ -1033,13 +1079,13 @@ export class HostClientCapabilityCoordinator implements ClientCapabilityService
readonly tool: FrozenToolBinding;
} {
const candidates = this.#eligibleOffersByContract().get(contractId) ?? [];
- const candidate =
- candidates.find((entry) => entry.registration.providerId === initiatingProviderId) ??
- (candidates.length === 1 ? candidates[0] : undefined);
+ const candidate = selectProviderCandidate(candidates, initiatingProviderId);
if (!candidate) {
throw new ClientCapabilityInvocationError(
- candidates.length > 1 ? 'capability_ambiguous' : 'capability_lost',
- candidates.length > 1
+ initiatingProviderId === undefined && candidates.length > 1
+ ? 'capability_ambiguous'
+ : 'capability_lost',
+ initiatingProviderId === undefined && candidates.length > 1
? 'Multiple Client Capability providers offer this call-affine contract'
: 'Client Capability provider is unavailable',
);
@@ -1370,6 +1416,30 @@ function rememberOfferProxyNames(offer: FrozenOfferBinding, proxyNames: Map<stri
}
}
+function selectProviderCandidate(
+ candidates: readonly SelectedOfferBinding[],
+ initiatingProviderId: string | undefined,
+): SelectedOfferBinding | undefined {
+ return initiatingProviderId === undefined
+ ? candidates.length === 1
+ ? candidates[0]
+ : undefined
+ : candidates.find((candidate) => candidate.registration.providerId === initiatingProviderId);
+}
+
+function clientCapabilityOwnerIdentitiesEqual(
+ left: ClientProviderState['capabilityOwner'],
+ right: ClientCapabilityConnectionIdentity['capabilityOwner'],
+): boolean {
+ return (
+ left === right ||
+ (left !== undefined &&
+ right !== undefined &&
+ left.principalId === right.principalId &&
+ left.clientInstanceId === right.clientInstanceId)
+ );
+}
+
function canonicalJson(value: unknown): string {
if (value === null || typeof value === 'boolean' || typeof value === 'number') {
return JSON.stringify(value);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org