diff --git a/CHANGELOG.md b/CHANGELOG.md index f54b2a1..f062727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- **MCP Apps: Fix blank UI rendering in Claude**: Rewrote `useToolData` hook to use the official `useApp` hook from `@modelcontextprotocol/ext-apps/react` instead of manually managing `App` lifecycle - - Proper initialization handshake with host via `appInfo` and `capabilities` - - Handlers registered via `onAppCreated` callback (before `connect()`) to avoid race conditions - - Removed `app.close()` on unmount which caused issues with React Strict Mode - - Added visible error and connection states with inline colors for debugging +- **MCP Apps: Fix blank UI and wrong status badge in Claude**: Rewrote `useToolData` hook to use the official `useApp` hook from `@modelcontextprotocol/ext-apps/react` for proper lifecycle management. Updated UI types and components to match actual server response format (`success: boolean` instead of `status: string`, nested `data` object for workflow details). Validation summary now handles both direct and wrapped (`n8n_validate_workflow`) response shapes. Conceived by Romuald Czlonkowski - https://www.aiadvisors.pl/en diff --git a/ui-apps/src/apps/operation-result/App.tsx b/ui-apps/src/apps/operation-result/App.tsx index 231103e..fb24c04 100644 --- a/ui-apps/src/apps/operation-result/App.tsx +++ b/ui-apps/src/apps/operation-result/App.tsx @@ -19,7 +19,12 @@ export default function App() { return
Waiting for data...
; } - 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 (
@@ -27,65 +32,34 @@ export default function App() { {isSuccess ? 'Success' : 'Error'} -

{data.operation}

- -
- {data.workflowName &&
Name: {data.workflowName}
} - {data.workflowId &&
ID: {data.workflowId}
} - {data.timestamp && ( -
- {data.timestamp} -
- )} -
-
- - {data.message && ( - -
{data.message}
+ {(workflowName || workflowId) && ( + +
+ {workflowName &&
Name: {workflowName}
} + {workflowId &&
ID: {workflowId}
} + {nodeCount !== undefined &&
Nodes: {nodeCount}
} + {isDeleted &&
Deleted
} + {operationsApplied !== undefined && ( +
Operations applied: {operationsApplied}
+ )} +
)} - {data.changes && ( - <> - {data.changes.nodesAdded && data.changes.nodesAdded.length > 0 && ( - - - - )} + {(data.message || data.error) && ( + +
{data.message || data.error}
+
+ )} - {data.changes.nodesModified && data.changes.nodesModified.length > 0 && ( - - - - )} - - {data.changes.nodesRemoved && data.changes.nodesRemoved.length > 0 && ( - - - - )} - + {data.details && ( + +
+            {JSON.stringify(data.details, null, 2)}
+          
+
)} ); diff --git a/ui-apps/src/apps/validation-summary/App.tsx b/ui-apps/src/apps/validation-summary/App.tsx index 08f98a7..7fa9083 100644 --- a/ui-apps/src/apps/validation-summary/App.tsx +++ b/ui-apps/src/apps/validation-summary/App.tsx @@ -2,10 +2,10 @@ import React from 'react'; import '@shared/styles/theme.css'; import { Card, Badge, Expandable } from '@shared/components'; import { useToolData } from '@shared/hooks/useToolData'; -import type { ValidationSummaryData } from '@shared/types'; +import type { ValidationSummaryData, ValidationError, ValidationWarning } from '@shared/types'; export default function App() { - const { data, error, isConnected } = useToolData(); + const { data: raw, error, isConnected } = useToolData(); if (error) { return
Error: {error}
; @@ -15,35 +15,46 @@ export default function App() { return
Connecting...
; } - if (!data) { + if (!raw) { return
Waiting for data...
; } + // n8n_validate_workflow wraps result in { success, data: {...} } + // validate_node and validate_workflow return data directly + const inner = raw.data || raw; + const valid = inner.valid ?? raw.valid ?? false; + const displayName = raw.displayName || raw.data?.workflowName; + const errors: ValidationError[] = inner.errors || raw.errors || []; + const warnings: ValidationWarning[] = inner.warnings || raw.warnings || []; + const suggestions: string[] = inner.suggestions || raw.suggestions || []; + const errorCount = raw.summary?.errorCount ?? inner.summary?.errorCount ?? errors.length; + const warningCount = raw.summary?.warningCount ?? inner.summary?.warningCount ?? warnings.length; + return (
- - {data.valid ? 'Valid' : 'Invalid'} + + {valid ? 'Valid' : 'Invalid'} - {data.displayName && ( - {data.displayName} + {displayName && ( + {displayName} )}
- {data.errorCount} errors + {errorCount} errors
- {data.warningCount} warnings + {warningCount} warnings
- {data.errors.length > 0 && ( - - {data.errors.map((err, i) => ( + {errors.length > 0 && ( + + {errors.map((err, i) => (
-
{err.type}
+ {(err.type || err.node) && ( +
{err.type || err.node}
+ )} {err.property &&
Property: {err.property}
}
{err.message}
{err.fix && ( @@ -63,9 +76,9 @@ export default function App() { )} - {data.warnings.length > 0 && ( - - {data.warnings.map((warn, i) => ( + {warnings.length > 0 && ( + + {warnings.map((warn, i) => (
-
{warn.type}
+ {(warn.type || warn.node) && ( +
{warn.type || warn.node}
+ )} {warn.property &&
Property: {warn.property}
}
{warn.message}
@@ -82,10 +97,10 @@ export default function App() {
)} - {data.suggestions && data.suggestions.length > 0 && ( - + {suggestions.length > 0 && ( +
    - {data.suggestions.map((suggestion, i) => ( + {suggestions.map((suggestion, i) => (
  • {suggestion}
  • ))}
diff --git a/ui-apps/src/shared/types.ts b/ui-apps/src/shared/types.ts index 07e1ede..6a81e88 100644 --- a/ui-apps/src/shared/types.ts +++ b/ui-apps/src/shared/types.ts @@ -1,38 +1,67 @@ +// Matches the McpToolResponse format from handlers-n8n-manager.ts export interface OperationResultData { - status: 'success' | 'error'; - operation: string; - workflowName?: string; - workflowId?: string; - timestamp?: string; - message?: string; - changes?: { - nodesAdded?: string[]; - nodesModified?: string[]; - nodesRemoved?: string[]; + success: boolean; + data?: { + id?: string; + name?: string; + active?: boolean; + nodeCount?: number; + workflowId?: string; + workflowName?: string; + deleted?: boolean; + operationsApplied?: number; + [key: string]: unknown; }; + message?: string; + error?: string; details?: Record; } export interface ValidationError { - type: string; + type?: string; property?: string; message: string; fix?: string; + node?: string; + details?: unknown; } export interface ValidationWarning { - type: string; + type?: string; property?: string; message: string; + node?: string; + details?: unknown; } +// Matches the validate_node / validate_workflow response format from server.ts export interface ValidationSummaryData { valid: boolean; - errorCount: number; - warningCount: number; + nodeType?: string; + displayName?: string; errors: ValidationError[]; warnings: ValidationWarning[]; suggestions?: string[]; - nodeType?: string; - displayName?: string; + summary?: { + errorCount?: number; + warningCount?: number; + hasErrors?: boolean; + suggestionCount?: number; + [key: string]: unknown; + }; + // n8n_validate_workflow wraps result in success/data + success?: boolean; + data?: { + valid?: boolean; + workflowId?: string; + workflowName?: string; + errors?: ValidationError[]; + warnings?: ValidationWarning[]; + suggestions?: string[]; + summary?: { + errorCount?: number; + warningCount?: number; + [key: string]: unknown; + }; + }; }