mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-22 18:33:08 +00:00
fix: MCP App UI - use official ext-apps hook + align types with server responses (#579)
* fix: use official ext-apps useApp hook to fix blank MCP App rendering The custom useToolData hook had lifecycle issues that prevented the UI from rendering in Claude Desktop/web: no appInfo in App constructor, unhandled connect() Promise, app.close() on unmount conflicting with React Strict Mode. Switched to the official useApp hook from @modelcontextprotocol/ext-apps/react which handles initialization handshake, handler registration, and cleanup correctly. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: align MCP App UI types with actual server response format - useToolData hook now uses official useApp from ext-apps/react - OperationResultData uses success:boolean + data.id/name (matching McpToolResponse from handlers-n8n-manager.ts) - ValidationSummaryData handles both direct results (validate_node, validate_workflow) and wrapped results (n8n_validate_workflow) - Added visible error/connection states for debugging Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
a57b400bd0
commit
020bc3d43d
@@ -19,7 +19,12 @@ export default function App() {
|
||||
return <div style={{ padding: '16px', color: '#9ca3af' }}>Waiting for data...</div>;
|
||||
}
|
||||
|
||||
const isSuccess = data.status === 'success';
|
||||
const isSuccess = data.success === true;
|
||||
const workflowName = data.data?.name || data.data?.workflowName;
|
||||
const workflowId = data.data?.id || data.data?.workflowId;
|
||||
const nodeCount = data.data?.nodeCount;
|
||||
const isDeleted = data.data?.deleted === true;
|
||||
const operationsApplied = data.data?.operationsApplied;
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '480px' }}>
|
||||
@@ -27,65 +32,34 @@ export default function App() {
|
||||
<Badge variant={isSuccess ? 'success' : 'error'}>
|
||||
{isSuccess ? 'Success' : 'Error'}
|
||||
</Badge>
|
||||
<h2 style={{ fontSize: '16px', fontWeight: 600 }}>{data.operation}</h2>
|
||||
</div>
|
||||
|
||||
<Card title="Workflow">
|
||||
<div style={{ fontSize: '14px' }}>
|
||||
{data.workflowName && <div><strong>Name:</strong> {data.workflowName}</div>}
|
||||
{data.workflowId && <div><strong>ID:</strong> {data.workflowId}</div>}
|
||||
{data.timestamp && (
|
||||
<div style={{ color: 'var(--n8n-text-muted)', fontSize: '12px', marginTop: '4px' }}>
|
||||
{data.timestamp}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{data.message && (
|
||||
<Card>
|
||||
<div style={{ fontSize: '13px' }}>{data.message}</div>
|
||||
{(workflowName || workflowId) && (
|
||||
<Card title="Workflow">
|
||||
<div style={{ fontSize: '14px' }}>
|
||||
{workflowName && <div><strong>Name:</strong> {workflowName}</div>}
|
||||
{workflowId && <div><strong>ID:</strong> {workflowId}</div>}
|
||||
{nodeCount !== undefined && <div><strong>Nodes:</strong> {nodeCount}</div>}
|
||||
{isDeleted && <div style={{ color: 'var(--n8n-warning)', marginTop: '4px' }}>Deleted</div>}
|
||||
{operationsApplied !== undefined && (
|
||||
<div><strong>Operations applied:</strong> {operationsApplied}</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{data.changes && (
|
||||
<>
|
||||
{data.changes.nodesAdded && data.changes.nodesAdded.length > 0 && (
|
||||
<Expandable title="Nodes Added" count={data.changes.nodesAdded.length} defaultOpen>
|
||||
<ul style={{ listStyle: 'none', fontSize: '13px' }}>
|
||||
{data.changes.nodesAdded.map((node, i) => (
|
||||
<li key={i} style={{ padding: '4px 0', borderBottom: '1px solid var(--n8n-border)' }}>
|
||||
<span style={{ color: 'var(--n8n-success)' }}>+</span> {node}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Expandable>
|
||||
)}
|
||||
{(data.message || data.error) && (
|
||||
<Card>
|
||||
<div style={{ fontSize: '13px' }}>{data.message || data.error}</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{data.changes.nodesModified && data.changes.nodesModified.length > 0 && (
|
||||
<Expandable title="Nodes Modified" count={data.changes.nodesModified.length}>
|
||||
<ul style={{ listStyle: 'none', fontSize: '13px' }}>
|
||||
{data.changes.nodesModified.map((node, i) => (
|
||||
<li key={i} style={{ padding: '4px 0', borderBottom: '1px solid var(--n8n-border)' }}>
|
||||
<span style={{ color: 'var(--n8n-warning)' }}>~</span> {node}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Expandable>
|
||||
)}
|
||||
|
||||
{data.changes.nodesRemoved && data.changes.nodesRemoved.length > 0 && (
|
||||
<Expandable title="Nodes Removed" count={data.changes.nodesRemoved.length}>
|
||||
<ul style={{ listStyle: 'none', fontSize: '13px' }}>
|
||||
{data.changes.nodesRemoved.map((node, i) => (
|
||||
<li key={i} style={{ padding: '4px 0', borderBottom: '1px solid var(--n8n-border)' }}>
|
||||
<span style={{ color: 'var(--n8n-error)' }}>-</span> {node}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Expandable>
|
||||
)}
|
||||
</>
|
||||
{data.details && (
|
||||
<Expandable title="Details">
|
||||
<pre style={{ fontSize: '11px', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
|
||||
{JSON.stringify(data.details, null, 2)}
|
||||
</pre>
|
||||
</Expandable>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user