-
-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Feat/mcp progress tracking #11192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feat/mcp progress tracking #11192
Changes from all commits
a948987
56ae80b
a5e87ff
bb95bce
92a9ec5
2e7387d
1900314
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { useMemo } from 'react'; | ||
| import { useAtomValue } from 'jotai'; | ||
| import { getMCPProgressAtom } from '~/store/mcp'; | ||
|
|
||
| interface MCPProgressProps { | ||
| conversationId: string; | ||
| serverName: string; | ||
| toolName: string; | ||
| } | ||
|
|
||
| export default function MCPProgress({ conversationId, serverName, toolName }: MCPProgressProps) { | ||
| const progressData = useAtomValue(getMCPProgressAtom(conversationId)); | ||
|
|
||
| // Get the latest progress for this specific tool call | ||
| const latestProgress = useMemo(() => { | ||
| if (!progressData || progressData.length === 0) { | ||
| return null; | ||
| } | ||
|
|
||
| // Filter for this specific server and tool | ||
| const toolProgress = progressData.filter( | ||
| (p) => p.serverName === serverName && p.toolName === toolName, | ||
| ); | ||
|
|
||
| if (toolProgress.length === 0) { | ||
| return null; | ||
| } | ||
|
|
||
| // Get the most recent progress update | ||
| return toolProgress[toolProgress.length - 1]; | ||
| }, [progressData, serverName, toolName]); | ||
|
|
||
| if (!latestProgress) { | ||
| return null; | ||
| } | ||
|
|
||
| const { progress, total, message } = latestProgress; | ||
| const percentage = total ? Math.round((progress / total) * 100) : Math.round(progress * 100); | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-1.5 rounded-lg border border-gray-200 bg-white p-3 dark:border-gray-700 dark:bg-gray-800"> | ||
| <div className="flex items-center justify-between text-sm"> | ||
| <span className="font-medium text-gray-700 dark:text-gray-300"> | ||
| {serverName} - {toolName} | ||
| </span> | ||
| <span className="text-gray-500 dark:text-gray-400">{percentage}%</span> | ||
| </div> | ||
|
|
||
| <div className="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700"> | ||
| <div | ||
| className="h-full rounded-full bg-blue-500 transition-all duration-300 ease-out" | ||
| style={{ width: `${percentage}%` }} | ||
| /> | ||
| </div> | ||
|
|
||
| {message && <div className="text-xs text-gray-600 dark:text-gray-400">{message}</div>} | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| import { AttachmentGroup } from './Parts'; | ||
| import ToolCallInfo from './ToolCallInfo'; | ||
| import ProgressText from './ProgressText'; | ||
| import MCPProgress from './MCPProgress'; | ||
| import { logger, cn } from '~/utils'; | ||
|
|
||
| export default function ToolCall({ | ||
|
|
@@ -18,6 +19,8 @@ | |
| output, | ||
| attachments, | ||
| auth, | ||
| conversationId, | ||
| isError: toolIsError, | ||
|
||
| }: { | ||
| initialProgress: number; | ||
| isLast?: boolean; | ||
|
|
@@ -28,6 +31,8 @@ | |
| attachments?: TAttachment[]; | ||
| auth?: string; | ||
| expires_at?: number; | ||
| conversationId?: string | null; | ||
| isError?: boolean; | ||
| }) { | ||
| const localize = useLocalize(); | ||
| const [showInfo, setShowInfo] = useState(false); | ||
|
|
@@ -101,6 +106,9 @@ | |
| const progress = useProgress(initialProgress); | ||
| const cancelled = (!isSubmitting && progress < 1) || error === true; | ||
|
|
||
| // Type guard for conversationId | ||
| const hasConversationId = conversationId != null && conversationId !== ''; | ||
|
|
||
| const getFinishedText = () => { | ||
| if (cancelled) { | ||
| return localize('com_ui_cancelled'); | ||
|
|
@@ -181,6 +189,15 @@ | |
| error={cancelled} | ||
| /> | ||
| </div> | ||
| {isMCPToolCall && hasConversationId && !cancelled && progress < 1 && ( | ||
| <div className="mt-2"> | ||
| <MCPProgress | ||
| conversationId={conversationId!} | ||
| serverName={domain ?? ''} | ||
| toolName={function_name} | ||
| /> | ||
| </div> | ||
| )} | ||
| <div | ||
| className="relative" | ||
| style={{ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The percentage calculation could produce values greater than 100% if
progressexceedstotal, or ifprogressis a value greater than 1.0 whentotalis undefined. Consider clamping the percentage between 0 and 100 to ensure valid display values.