feat: wire MCPAppRenderer into ChatPanel for MCP App tool results
This commit is contained in:
@@ -3,6 +3,7 @@ import type { KeyboardEvent } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { IconSend } from '@tabler/icons-react'
|
||||
import { ToolCallCard } from './ToolCallCard'
|
||||
import { MCPAppRenderer } from './MCPAppRenderer'
|
||||
import type { ChatMessage } from '../hooks/useChat'
|
||||
|
||||
interface ChatPanelProps {
|
||||
@@ -61,9 +62,19 @@ export function ChatPanel({ messages, isStreaming, onSend, sessionId }: ChatPane
|
||||
{message.role === 'assistant' ? (
|
||||
<div>
|
||||
<ReactMarkdown>{message.content}</ReactMarkdown>
|
||||
{message.toolCalls?.map(tc => (
|
||||
<ToolCallCard key={tc.id} toolCall={tc} />
|
||||
))}
|
||||
{message.toolCalls?.map(tc =>
|
||||
tc._meta?.ui?.resourceUri ? (
|
||||
<MCPAppRenderer
|
||||
key={tc.id}
|
||||
resourceUri={tc._meta.ui.resourceUri}
|
||||
data={tc.structuredContent ?? {}}
|
||||
csp={tc._meta.ui.csp}
|
||||
onSendMessage={(content) => onSend(content)}
|
||||
/>
|
||||
) : (
|
||||
<ToolCallCard key={tc.id} toolCall={tc} />
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>{message.content}</div>
|
||||
|
||||
@@ -14,6 +14,13 @@ vi.mock('react-markdown', () => ({
|
||||
default: ({ children }: { children: string }) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
// Mock MCPAppRenderer to avoid fetch/iframe in test environment
|
||||
vi.mock('../components/MCPAppRenderer', () => ({
|
||||
MCPAppRenderer: ({ resourceUri }: { resourceUri: string }) => (
|
||||
<div data-testid='mcp-app-renderer' data-resource-uri={resourceUri} />
|
||||
),
|
||||
}))
|
||||
|
||||
describe('ChatPanel', () => {
|
||||
const noop = () => undefined
|
||||
|
||||
@@ -90,6 +97,72 @@ describe('ChatPanel', () => {
|
||||
expect(container.querySelector('.tool-call-card')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('renders MCPAppRenderer for tool calls with _meta.ui.resourceUri', () => {
|
||||
const messages: ChatMessage[] = [
|
||||
{
|
||||
id: '1',
|
||||
role: 'assistant',
|
||||
content: 'Running an MCP app',
|
||||
toolCalls: [
|
||||
{
|
||||
id: 'tc-mcp',
|
||||
name: 'mcp_app',
|
||||
args: {},
|
||||
status: 'complete',
|
||||
_meta: { ui: { resourceUri: 'app://chart' } },
|
||||
structuredContent: { value: 42 },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
render(
|
||||
<ChatPanel messages={messages} isStreaming={false} onSend={noop} sessionId={sessionId} />
|
||||
)
|
||||
expect(screen.getByTestId('mcp-app-renderer')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('does not render ToolCallCard for tool calls with _meta.ui.resourceUri', () => {
|
||||
const messages: ChatMessage[] = [
|
||||
{
|
||||
id: '1',
|
||||
role: 'assistant',
|
||||
content: 'Running an MCP app',
|
||||
toolCalls: [
|
||||
{
|
||||
id: 'tc-mcp',
|
||||
name: 'mcp_app',
|
||||
args: {},
|
||||
status: 'complete',
|
||||
_meta: { ui: { resourceUri: 'app://chart' } },
|
||||
structuredContent: { value: 42 },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
const { container } = render(
|
||||
<ChatPanel messages={messages} isStreaming={false} onSend={noop} sessionId={sessionId} />
|
||||
)
|
||||
expect(container.querySelector('.tool-call-card')).toBeNull()
|
||||
})
|
||||
|
||||
it('renders ToolCallCard for tool calls without _meta.ui.resourceUri', () => {
|
||||
const messages: ChatMessage[] = [
|
||||
{
|
||||
id: '1',
|
||||
role: 'assistant',
|
||||
content: 'Running a plain tool',
|
||||
toolCalls: [
|
||||
{ id: 'tc-plain', name: 'bash', args: {}, status: 'complete' },
|
||||
],
|
||||
},
|
||||
]
|
||||
const { container } = render(
|
||||
<ChatPanel messages={messages} isStreaming={false} onSend={noop} sessionId={sessionId} />
|
||||
)
|
||||
expect(container.querySelector('.tool-call-card')).toBeTruthy()
|
||||
expect(screen.queryByTestId('mcp-app-renderer')).toBeNull()
|
||||
})
|
||||
|
||||
it('shows Researching... when isStreaming and messages exist', () => {
|
||||
const messages: ChatMessage[] = [
|
||||
{ id: '1', role: 'user', content: 'Question' },
|
||||
|
||||
Reference in New Issue
Block a user