1
>
/*---------------------------------------------------------------------------------------------
agentServerToolHost.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import type { IAgentServerToolHost } from '../../common/agentServerTools.js';
7
>
import { ActionType } from '../../common/state/protocol/common/actions.js';
8
>
import type { StringOrMarkdown, ToolDefinition, URI } from '../../common/state/sessionState.js';
9
>
import type { AgentHostStateManager } from '../agentHostStateManager.js';
10
>
11
>
/**
12
>
* Result of a server tool, passed to {@link IServerToolGroup.getDisplay} so the
13
>
* owning group can tailor its past-tense message to what the tool returned
14
>
* (for example a count parsed from the textual result). Absent while the tool
15
>
* is still running.
16
>
*/
17
>
export interface IServerToolDisplayResult {
18
>
/** The textual tool result (the string the group's `execute` returned). */
19
>
readonly text?: string;
20
>
/** Whether the tool completed successfully. */
21
>
readonly success: boolean;
22
>
}
23
>
24
>
/**
25
>
* Display strings for a server tool, authored by the group that owns the tool
26
>
* so every provider renders it identically (instead of each provider's display
27
>
* layer re-deriving the strings from the tool name). Each field is optional: a
28
>
* provider uses the returned value where present and falls back to its own
29
>
* generic display otherwise.
30
>
*/
31
>
export interface IServerToolDisplay {
32
>
/** Human-readable tool name (e.g. "List Comments"). */
33
>
readonly displayName?: string;
34
>
/** Present-tense message shown while the tool runs (e.g. "Checking comments"). */
35
>
readonly invocationMessage?: StringOrMarkdown;
36
>
/** Past-tense message shown once the tool completes (e.g. "Checked 3 comments"). */
37
>
readonly pastTenseMessage?: StringOrMarkdown;
38
>
}
39
>
40
>
/**
41
>
* A group of related server tools owned and executed by the agent host. Each
42
>
* group bundles the {@link ToolDefinition}s it advertises with an executor
43
>
* that runs one of its tools by name against the session's state.
44
>
*
45
>
* Groups are the unit of extension and are **contributed from outside** — they
46
>
* are passed to {@link AgentServerToolHost} at construction (startup), so this
47
>
* module stays provider- and feature-agnostic (it knows nothing about
48
>
* feedback, annotations, etc.). The feedback group, for example, lives in
49
>
* `agentFeedbackServerTools.ts` and is wired in by the agent host. Everything
50
>
* downstream — advertising, the Claude in-process MCP server and allow-list,
51
>
* and the Copilot SDK tools and auto-approval — derives from the host's
52
>
* contributed groups, so no provider code changes are needed to add a group.
53
>
*/
54
>
export interface IServerToolGroup {
55
>
/** Tool definitions this group advertises on the session's `serverTools`. */
56
>
readonly definitions: readonly ToolDefinition[];
57
>
/**
58
>
* Whether {@link toolName} (one of this group's {@link definitions}) must be
59
>
* confirmed by the user before it runs. Providers exclude such tools from
60
>
* their server-tool auto-approve lists so the call surfaces a confirmation.
61
>
* Absent or `false` means the tool is auto-approved like every other server
62
>
* tool.
63
>
*/
64
>
requiresConfirmation?(toolName: string): boolean;
65
>
/**
66
>
* Executes {@link toolName} (one of this group's {@link definitions})
67
>
* against the session's state, dispatching any resulting actions through
68
>
* the state manager (the single writer), and returns the textual tool
69
>
* result.
70
>
*
71
>
* @throws if {@link toolName} is not owned by this group or the arguments
72
>
* are invalid.
73
>
*/
74
>
execute(stateManager: AgentHostStateManager, sessionUri: URI, toolName: string, rawArgs: unknown): string | Promise<string>;
75
>
76
>
/**
77
>
* Display strings for {@link toolName} (one of this group's
78
>
* {@link definitions}), authored here so every provider renders this tool
79
>
* identically rather than re-deriving the strings from the tool name. The
80
>
* caller passes the parsed tool arguments and, once the tool has completed,
81
>
* its {@link IServerToolDisplayResult result}. Returns `undefined` (or
82
>
* individually-absent fields) to let the provider fall back to its generic
83
>
* display. Optional: a group without bespoke display omits this.
84
>
*
85
>
* `toolName` is the bare tool name (the provider strips any transport
86
>
* prefix such as Claude's `mcp__<server>__` before calling).
87
>
*/
88
>
getDisplay?(toolName: string, args: unknown, result?: IServerToolDisplayResult): IServerToolDisplay | undefined;
89
>
}
90
>
91
>
/**
92
>
* Bridges the agent host's server tools to the authoritative state tree.
93
>
* Agents execute a server tool by name; the host routes it to the owning
94
>
* {@link IServerToolGroup}, which reads the relevant session state, applies the
95
>
* tool, dispatches any resulting actions through the state manager (the single
96
>
* writer), and returns the textual tool result to the agent.
97
>
*
98
>
* The groups are contributed at construction; the host itself is generic and
99
>
* has no knowledge of any specific tool group. It also advertises every server
100
>
* tool on a session's {@link SessionState.serverTools} so clients see them as
101
>
* server-provided.
102
>
*/
103
>
export class AgentServerToolHost implements IAgentServerToolHost {
104
>
105
>
private readonly _groupByToolName = new Map<string, IServerToolGroup>();
106
>
107
>
readonly definitions: readonly ToolDefinition[];
108
>
readonly toolNames: readonly string[];
109
>
110
>
constructor(
111
private readonly _stateManager: AgentHostStateManager,
112
groups: readonly IServerToolGroup[],