mirror of
https://github.com/vercel-labs/fx.git
synced 2026-10-28 10:15:17 +08:00
Label MCP tool calls in tool group summaries
A tool the registry does not hold was counted as a command, so a turn's MCP calls showed up as "2 commands" in its tool group summary and as commands started in the status line. Every MCP tool name has the mcp_ prefix, so those calls now have their own activity kind. The summary reads "1 MCP call" or "2 MCP calls", and the status line counts MCP calls made.
This commit is contained in:
5 files changed
+25
-7
No files matched your search
@@ -1338,6 +1338,7 @@ pub fn Runtime(comptime App: type) type {
|
||||
.open => stream.open_count += 1,
|
||||
.command => stream.command_count += 1,
|
||||
.subagent => stream.subagent_count += 1,
|
||||
.mcp => stream.mcp_count += 1,
|
||||
.ask => {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ fn streamStatusVerb(stream: StreamState) []const u8 {
|
||||
.subagent => "delegating",
|
||||
.open => "opening",
|
||||
.ask => "asking",
|
||||
.mcp => "calling",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -231,7 +232,7 @@ fn buildThinkingLabelFull(buf: []u8, stream: StreamState) []const u8 {
|
||||
return out.buffered();
|
||||
}
|
||||
|
||||
fn activityParts(stream: StreamState) [7]ActivityPart {
|
||||
fn activityParts(stream: StreamState) [8]ActivityPart {
|
||||
return .{
|
||||
.{ .count = stream.read_count, .singular = "file", .plural = "files", .verb = "read" },
|
||||
.{ .count = stream.list_count, .singular = "directory", .plural = "directories", .verb = "listed" },
|
||||
@@ -240,6 +241,7 @@ fn activityParts(stream: StreamState) [7]ActivityPart {
|
||||
.{ .count = stream.command_count, .singular = "command", .plural = "commands", .verb = "started" },
|
||||
.{ .count = stream.subagent_count, .singular = "subagent", .plural = "subagents", .verb = "created" },
|
||||
.{ .count = stream.open_count, .singular = "file", .plural = "files", .verb = "opened" },
|
||||
.{ .count = stream.mcp_count, .singular = "MCP call", .plural = "MCP calls", .verb = "made" },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -290,6 +290,7 @@ pub const StreamState = struct {
|
||||
open_count: usize = 0,
|
||||
command_count: usize = 0,
|
||||
subagent_count: usize = 0,
|
||||
mcp_count: usize = 0,
|
||||
token_progress: TurnTokenProgress = .{},
|
||||
last_activity_kind: ?ToolActivityKind = null,
|
||||
/// When the turn started; 0 hides the elapsed counter and activity blink.
|
||||
@@ -2476,6 +2477,8 @@ pub const ToolActivityKind = enum {
|
||||
command,
|
||||
subagent,
|
||||
ask,
|
||||
/// A call to an MCP server's tool.
|
||||
mcp,
|
||||
};
|
||||
|
||||
pub const QuestionOption = struct {
|
||||
|
||||
@@ -568,7 +568,13 @@ pub fn dispatchRunCommandCompatibility(
|
||||
}
|
||||
|
||||
pub fn toolActivityKind(registry: Registry, tool_name: []const u8) core_types.ToolActivityKind {
|
||||
return if (registry.lookup(tool_name)) |tool| tool.activity_kind else .command;
|
||||
return if (registry.lookup(tool_name)) |tool| tool.activity_kind else unregisteredActivityKind(tool_name);
|
||||
}
|
||||
|
||||
/// A name the registry does not hold is an MCP server's tool when it has the
|
||||
/// `mcp_` prefix every MCP tool name gets, and is otherwise treated as a command.
|
||||
fn unregisteredActivityKind(tool_name: []const u8) core_types.ToolActivityKind {
|
||||
return if (std.mem.startsWith(u8, tool_name, "mcp_")) .mcp else .command;
|
||||
}
|
||||
|
||||
pub fn staticPresentation(tool: Tool) CallPresentation {
|
||||
@@ -609,7 +615,7 @@ pub fn toolActivityKindForCall(
|
||||
call: core_types.ToolCall,
|
||||
) core_types.ToolActivityKind {
|
||||
const presentation = toolCallPresentation(alloc, registry, call) orelse
|
||||
return .command;
|
||||
return unregisteredActivityKind(call.name);
|
||||
return presentation.activity_kind;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,8 +149,10 @@ const category_labels = [_][]const u8{
|
||||
"command",
|
||||
"subagent",
|
||||
"browser",
|
||||
"MCP call",
|
||||
};
|
||||
const command_category_index = 5;
|
||||
const mcp_category_index = 8;
|
||||
|
||||
const Summary = struct {
|
||||
total: usize = 0,
|
||||
@@ -184,6 +186,7 @@ fn categoryIndex(kind: types.ToolActivityKind) ?usize {
|
||||
.command => command_category_index,
|
||||
.subagent => 6,
|
||||
.ask => null,
|
||||
.mcp => mcp_category_index,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -531,10 +534,13 @@ fn formatGroupSummary(alloc: std.mem.Allocator, summary: Summary) ![]u8 {
|
||||
const index = next_index orelse break;
|
||||
emitted[index] = true;
|
||||
const count = summary.categories[index];
|
||||
const label = if (index == command_category_index and count != 1)
|
||||
"commands"
|
||||
else
|
||||
category_labels[index];
|
||||
const label = if (count == 1)
|
||||
category_labels[index]
|
||||
else switch (index) {
|
||||
command_category_index => "commands",
|
||||
mcp_category_index => "MCP calls",
|
||||
else => category_labels[index],
|
||||
};
|
||||
try appendSegment(&out.writer, count, label);
|
||||
}
|
||||
try appendSegment(&out.writer, summary.completion_unreported, "unreported");
|
||||
|
||||
Reference in new issue
Block a user