Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/vs/platform/agentHost/common/agentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IAuthorizationProtectedResourceMetadata } from '../../../base/common/oa
import { URI } from '../../../base/common/uri.js';
import { createDecorator } from '../../instantiation/common/instantiation.js';
import type { ISyncedCustomization } from './agentPluginManager.js';
import { IProtectedResourceMetadata } from './state/protocol/state.js';
import { IProtectedResourceMetadata, type IToolDefinition } from './state/protocol/state.js';
import type { IActionEnvelope, INotification, ISessionAction, ITerminalAction } from './state/sessionActions.js';
import type { IAgentSubscription } from './state/agentSubscription.js';
import type { ICreateTerminalParams, IResolveSessionConfigResult, ISessionConfigCompletionsResult } from './state/protocol/commands.js';
Expand Down Expand Up @@ -215,6 +215,12 @@ export interface IAgentToolStartEvent extends IAgentProgressEventBase {
readonly mcpServerName?: string;
readonly mcpToolName?: string;
readonly parentToolCallId?: string;
/**
* If set, this tool is provided by a client and the identified client
* is responsible for executing it. Maps to `toolClientId` in the
* protocol `session/toolCallStart` action.
*/
readonly toolClientId?: string;
}

/** A tool has finished executing (`tool.execution_complete`). */
Expand Down Expand Up @@ -432,6 +438,28 @@ export interface IAgent {
*/
setClientCustomizations(clientId: string, customizations: ICustomizationRef[], progress?: (results: ISyncedCustomization[]) => void): Promise<ISyncedCustomization[]>;

/**
* Receives client-provided tool definitions to make available in a
* specific session. The agent registers these as custom tools so the
* LLM can call them; execution is routed back to the owning client.
*
* Always called on `activeClientChanged`, even with an empty array,
* to clear a previous client's tools.
*
* @param session The session URI this tool set applies to.
* @param clientId The client that owns these tools.
* @param tools The tool definitions (full replacement).
*/
setClientTools(session: URI, clientId: string, tools: IToolDefinition[]): void;

/**
* Called when a client completes a client-provided tool call.
* Resolves the tool handler's deferred promise so the SDK can continue.
*
* @param session The session the tool call belongs to.
*/
onClientToolCallComplete(session: URI, toolCallId: string, result: IToolCallResult): void;

/**
* Notifies the agent that a customization has been toggled on or off.
* The agent MAY restart its client before the next message is sent.
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7c0c693
249a721
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export type IClientSessionAction =
| ISessionToolCallConfirmedAction
| ISessionToolCallCompleteAction
| ISessionToolCallResultConfirmedAction
| ISessionToolCallContentChangedAction
| ISessionTurnCancelledAction
| ISessionTitleChangedAction
| ISessionModelChangedAction
Expand All @@ -92,7 +93,6 @@ export type IServerSessionAction =
| ISessionToolCallStartAction
| ISessionToolCallDeltaAction
| ISessionToolCallReadyAction
| ISessionToolCallContentChangedAction
| ISessionTurnCompleteAction
| ISessionErrorAction
| ISessionUsageAction
Expand Down Expand Up @@ -152,7 +152,7 @@ export const IS_CLIENT_DISPATCHABLE: { readonly [K in IStateAction['type']]: boo
[ActionType.SessionToolCallConfirmed]: true,
[ActionType.SessionToolCallComplete]: true,
[ActionType.SessionToolCallResultConfirmed]: true,
[ActionType.SessionToolCallContentChanged]: false,
[ActionType.SessionToolCallContentChanged]: true,
[ActionType.SessionTurnComplete]: false,
[ActionType.SessionTurnCancelled]: true,
[ActionType.SessionError]: false,
Expand Down
6 changes: 6 additions & 0 deletions src/vs/platform/agentHost/common/state/protocol/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,14 @@ export interface ISessionToolCallResultConfirmedAction extends IToolCallActionBa
* use this to display live feedback (e.g. a terminal reference) before the
* tool completes.
*
* For client-provided tools (where `toolClientId` is set on the tool call state),
* the owning client dispatches this action to stream intermediate content while
* executing. The server SHOULD reject this action if the dispatching client does
* not match `toolClientId`.
*
* @category Session Actions
* @version 1
* @clientDispatchable
*/
export interface ISessionToolCallContentChangedAction extends IToolCallActionBase {
type: ActionType.SessionToolCallContentChanged;
Expand Down
3 changes: 0 additions & 3 deletions src/vs/platform/agentHost/common/state/protocol/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1064,9 +1064,6 @@ export type IToolCallState =
/**
* Describes a tool available in a session, provided by either the server or the active client.
*
* This type mirrors the MCP `Tool` type from the Model Context Protocol specification
* (2025-11-25 draft) and will continue to track it.
*
* @category Tool Definition Types
*/
export interface IToolDefinition {
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/agentHost/node/agentEventMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class AgentEventMapper {
toolCallId: e.toolCallId,
toolName: e.toolName,
displayName: e.displayName,
toolClientId: e.toolClientId,
_meta: meta,
};
const readyAction: IToolCallReadyAction = {
Expand Down
25 changes: 24 additions & 1 deletion src/vs/platform/agentHost/node/agentSideEffects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,15 @@ export class AgentSideEffects extends Disposable {
}
case ActionType.SessionActiveClientChanged: {
const agent = this._options.getAgent(action.session);
if (!agent) {
break;
}
// Always forward client tools, even if empty, to clear previous client's tools
const clientId = action.activeClient?.clientId ?? '';
agent.setClientTools(URI.parse(action.session), clientId, action.activeClient?.tools ?? []);

const refs = action.activeClient?.customizations;
if (!agent?.setClientCustomizations || !refs?.length) {
if (!refs?.length) {
break;
}
// Publish initial "loading" status for all customizations
Expand Down Expand Up @@ -675,6 +682,17 @@ export class AgentSideEffects extends Disposable {
});
break;
}
case ActionType.SessionActiveClientToolsChanged: {
const agent = this._options.getAgent(action.session);
if (agent) {
const sessionState = this._stateManager.getSessionState(action.session);
const toolClientId = sessionState?.activeClient?.clientId;
if (toolClientId) {
agent.setClientTools(URI.parse(action.session), toolClientId, action.tools);
}
}
break;
}
case ActionType.SessionCustomizationToggled: {
const agent = this._options.getAgent(action.session);
agent?.setCustomizationEnabled?.(action.uri, action.enabled);
Expand All @@ -688,6 +706,11 @@ export class AgentSideEffects extends Disposable {
this._persistSessionFlag(action.session, 'isDone', action.isDone ? 'true' : '');
break;
}
case ActionType.SessionToolCallComplete: {
const agent = this._options.getAgent(action.session);
agent?.onClientToolCallComplete(URI.parse(action.session), action.toolCallId, action.result);
break;
}
}
}

Expand Down
Loading
Loading