Skip to content

Automatic Sync: Merge changes from stable/mi to main - #2406

Open
choreo-cicd wants to merge 164 commits into
mainfrom
sync/main-stable/mi-1783314625
Open

Automatic Sync: Merge changes from stable/mi to main#2406
choreo-cicd wants to merge 164 commits into
mainfrom
sync/main-stable/mi-1783314625

Conversation

@choreo-cicd

@choreo-cicd choreo-cicd commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

This PR syncs changes from stable/mi to the main branch.

Automated PR created by GitHub Actions.

Summary by CodeRabbit

  • New Features

    • Added support for MCP servers, including creating, viewing, editing, and deleting them from the UI.
    • Added options to build tools from APIs or sequences, plus AI-assisted tool suggestions.
    • Added support for resource bindings and task “start on load” settings where available.
  • Bug Fixes

    • Improved handling of truncated context with visible warnings in chat.
    • Better file import/upload behavior for base64 data, larger attachments, and inbound endpoint imports.
    • Updated dependency handling and validation to reduce connector/download issues.

IsuruMaduranga and others added 30 commits May 27, 2026 00:25
Replace VALID_FILE_EXTENSIONS / VALID_SPECIAL_FILE_NAMES with
BLOCKED_BINARY_EXTENSIONS so the agent can read/write any text file
(including extensionless ones like Dockerfile, Makefile, .gitignore)
while still blocking archives, executables, office docs, images, etc.
Shell sandbox now enforces the same deny-list on mutation paths. The
@-mention picker keeps its own short UX allow-list, decoupled from
the security gate. Also enable XML validation for .dbs data services.
Trim filePath in getReadFileKind() to keep classification consistent with
validateReadableFilePath(), and update shell sandbox error message to
accurately describe all mutation types (write/edit/delete/rename).
Loads a root-level AGENTS.md (CLAUDE.md / Cursor convention) as a tracked
context block. Re-injected only on content drift via the existing
session-context hash system, so it does not waste tokens every turn.

Files over 30 KB are truncated before being shipped to the model. The
block carries an explicit truncation banner so the agent knows context is
missing (and can file_read the tail on demand), and a persistent in-chat
warning segment tells the user. Truncation warnings fire only when the
block is actually being injected, so an unchanged large file does not
re-warn each turn.
- agent.ts: make `saveAgentsMdWarning` failure non-fatal. The live event
  still fires so the user sees the warning this turn; only the JSONL
  replay across reconnects is sacrificed on persistence failure.
- prompt.ts: neutralize embedded `<system-reminder>` / `<user_query>`
  tags in user-authored AGENTS.md so they can't break out of the
  surrounding envelope. Replaces the angle brackets with U+27E8 / U+27E9.
- prompt.ts: fix the `SessionContextBlockHashes.agentsMd` doc comment to
  describe what the code actually hashes (surfaced bytes + truncation
  metadata) instead of the old "full untruncated raw bytes" claim.
- AIChatFooter.tsx: splice the warning before the optimistic streaming
  placeholder. Appending at the end displaced the placeholder, causing
  subsequent content_block / thinking_* events to land in the warning
  bubble instead of the assistant message.
- eventToMessageConverter.ts: drop `(event as any)` cast now that
  `warningMessage` is a typed field on AgentEvent / ChatHistoryEvent.
Fix trivy detected vulnerabilities
Support the startOnLoad attribute for tasks in MI 4.1.0
Instruct agent-mode to flip pom.xml <packaging> to jar and declare
synapse-core when creating a class mediator, otherwise the CApp ships
without the compiled jar and deployment silently fails.

Fixes wso2/product-integrator#1631
file_write and file_edit now append a <system-reminder> to the tool
result whenever the touched file is a class mediator java source
(src/main/java/**/*.java), telling the agent to verify root pom.xml
packaging is "jar" and synapse-core is declared. The prompt-level
guideline alone can be missed; a per-tool reminder makes it harder
to skip.

Related to wso2/product-integrator#1631
Read the project root pom.xml and skip the class mediator reminder
when <packaging>jar</packaging> is already set, so the agent is not
prompted to fix something that is already correct. When packaging is
something else (typically the default "pom"), the reminder reports
the current value to make the required change unambiguous.
…ging

Fix copilot-created class mediators not packed in CApp
- Clarify file_read offset/limit are line-based in AGENTS.md guidance
- Use Math.ceil for KB rounding and improve truncation warning copy
- Add aria-hidden to decorative warning icon
- Dedup context_warning on panel reconnect/event replay
Mirror the POSIX shell sandbox check so the agent cannot mutate
foo.jar, foo.dll, etc. via PowerShell on Windows.
Switch agent file tools to a binary deny-list
Fix trivy detected vulnerabilities
Sync latest changes from release/mi-4.0.2
Merge "micro-integrator-4.0.2" into "stable/mi"
ChinthakaJ98 and others added 25 commits June 26, 2026 15:50
…ails-1640

Fix agent-mode codegen guardrails (operator precedence, scope, connectors, variables)
Mirror of wso2/mi-vscode#1515 for the release/mi-4.1.1 line of this repo.

The MI Copilot proxy blocks Opus models and returns a 400 with an
explanatory body, but the chat error card only showed the SDK's generic
status text, and WSO2 users could still select Opus in Settings.

Settings (WSO2/MI_INTEL login only):
- Disable both the Main Agent and Sub-Agent model switches (greyed out)
  with a note: "Sign in with your own Anthropic API key or AWS Bedrock to
  change models." BYOK (Anthropic key / Bedrock) is unaffected.
- The Main switch always shows the non-Opus default; the high-intelligence
  warning is suppressed on the plan.

Backend:
- Clamp the main agent to Sonnet for MI_INTEL when an 'opus' preset carried
  over from a prior BYOK session, so it can't 400 every turn. Custom model
  IDs are left untouched.

Error surfacing:
- Add getDisplayErrorMessage() which extracts the upstream provider/proxy
  message from APICallError responseBody/data (walking the cause chain)
  instead of the bare HTTP status text, and wire it into the agent's stream
  'error' path, execution catch, and the rpc-manager fallback catch.

Mirrors: wso2/mi-vscode#1515
Add support to the binds-to attribute in APIs
Fix back button functionality issue
Mirror the review fixes applied to the mi-vscode PR so both repos carry an
identical change set:

stream_guard.ts:
- Extract shared extractProviderErrorMessageFromBody() that handles
  APICallError.data as a raw string, surfaces JSON-quoted string bodies,
  and guards JSON.parse against oversized bodies (MAX_PARSEABLE_BODY_LENGTH)
  on the already-failing path.

agent.ts:
- Prefer abortReason over the caught error when building the user-facing
  message, so watchdog-wrapped errors keep the upstream provider detail.

SettingsPanel.tsx / AICodeGenerator.tsx:
- Add a byokResolved flag; gate model-switch locking on it and keep the
  controls neutral (disabled, no lock note, no warning) while resolution
  is pending. Reset byokResolved before each lookup and settle it on the
  failure path so the UI can't leak stale state or hang pending.
- Make Reset to defaults respect the locked plan: on MI Copilot it no
  longer rewrites the (locked) model presets, closing the hidden
  model-switch path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AICodeGenerator.tsx:
- Clear isByok/isAwsBedrock (not just byokResolved) and run the re-gate
  before the rpcClient guard, so a failed or rpcClient-less lookup can't
  settle with a prior session's stale auth flags and wrongly unlock the
  model controls.

SettingsPanel.tsx:
- Use modelControlsDisabled (locked plan OR pending resolution) instead of
  isMiCopilotPlan for both handleResetDefaults and isDefault, so Reset to
  defaults can't rewrite presets before the plan is known.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y-errors

Lock model switching on WSO2 plan and surface proxy error messages
Merge "micro-integrator-4.1.1" into "stable/mi"
Rename inbound-connectors to inbound-endpoints
Fix table values loading issue in inbound-endpoints
Fix nested param manager rendering issue on edit
Merge "micro-integrator-4.1.2" into "stable/mi"
@choreo-cicd
choreo-cicd requested review from gigara and hevayo as code owners July 6, 2026 05:10
@CLAassistant

CLAassistant commented Jul 6, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
3 out of 4 committers have signed the CLA.

✅ IsuruMaduranga
✅ arunans23
✅ ChinthakaJ98
❌ choreo-cicd
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Dependency Version Bumps

Layer / File(s) Summary
Version bumps across pnpm overrides and package.json
common/config/rush/*, package.json, workspaces/**/package.json
Bumps pinned dependency versions (axios, js-yaml, hono, protobufjs, webpack-dev-server, @babel/core, undici, tmp, dompurify, qs, joi, markdown-it) across override configs and workspace manifests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

MI Extension & Visualizer Feature Work

Layer / File(s) Summary
MCP RPC contracts
workspaces/mi/mi-core/src/..., workspaces/mi/mi-rpc-client/...
Adds MCP tooling, agent context-warning, and importOpenAPISpec response type contracts.
MCP server backend
workspaces/mi/mi-extension/src/{project-explorer,rpc-managers,util}/*
Implements MCP project-explorer commands, RPC manager methods for MCP artifacts/tools, and related API/export fixes.
MCP server UI wizards
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/*, AddArtifact, Overview, MainPanel.tsx, PopupPanel.tsx
Adds MCP server creation/edit wizard, tool dialogs, list/type selectors, and view remounting wiring.
Inbound endpoint import & connector dependency
workspaces/mi/mi-visualizer/src/{Hooks.tsx,views/Forms/InboundEPform,PopupPanel}*, Keylookup.tsx, BaseNodeModel.ts
Adds connector dependency state machine and inbound connector zip import popup flow.
AGENTS.md context warning & error display
workspaces/mi/mi-extension/src/ai-features/agent-mode/*, workspaces/mi/mi-visualizer/src/views/AIPanel/*
Adds AGENTS.md truncation-aware context tracking, context_warning journaling, and improved provider error messaging.
Agent tool safety guardrails
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/*, context/*
Replaces file-type allowlist with a binary deny-list enforced in read/write/shell tools and updates guidance docs.
AI model bump & settings gating
connection.ts, auth.ts, SettingsPanel.tsx, AICodeGenerator.tsx
Upgrades Opus model to 4.8 and gates settings model controls on BYOK resolution.
API bindsTo feature
syntax-tree-interfaces.ts, APIform, ResourceForm, api.ts templates
Adds bindsTo binding support between API resources and inbound endpoints.
Debugger multi-project launch
debugger/activate.ts, debugAdapter.ts, debugHelper.ts
Moves project selection upstream and adds build-abort on disconnect.
Task startOnLoad
TaskForm.tsx, task RPC types/template, mi-diagram/rpc-manager.ts
Adds runtime-gated startOnLoad task option.
Data Service form validation
DataServiceForm/*
Adds uniqueness and required-field validation.
Connection form improvements
ConnectionForm/*
Improves import error handling and connector card UI.
Diagram/SidePanel misc fixes
Diagram.tsx, sidePanel/*, stateMachine.ts, RegistryResourceForm.tsx
Adds disableTryout plumbing, viewStacking state, and path-separator fixes.
Tests, changelog, versions
CHANGELOG.md, templates.ts, e2e tests
Updates release notes and adjusts Playwright test selectors/flows.

Estimated code review effort: 5 (Critical) | ~150 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AddArtifact
  participant MCPServerWizard
  participant MiDiagramRpcManager
  participant MCPServerToolsForm
  AddArtifact->>MCPServerWizard: open MCP server form
  MCPServerWizard->>MiDiagramRpcManager: getMcpUsedInboundPorts
  MCPServerWizard->>MiDiagramRpcManager: createEmptyLocalEntry + createInboundEndpoint
  MiDiagramRpcManager-->>MCPServerWizard: created endpoint paths
  MCPServerWizard->>MCPServerToolsForm: open MCPServerFromAPIsForm with editData
  MCPServerToolsForm->>MiDiagramRpcManager: buildMcpToolsXml / updateMcpInboundEndpoint
Loading
sequenceDiagram
  participant Agent
  participant ChatHistoryManager
  participant AIChatFooter
  Agent->>Agent: read/truncate AGENTS.md via readAgentsMd
  Agent->>ChatHistoryManager: saveAgentsMdWarning(warningMessage)
  Agent-->>AIChatFooter: emit context_warning event
  AIChatFooter->>AIChatFooter: render ContextWarningSegment
Loading

Possibly related PRs

  • wso2/vscode-extensions#2254: Both PRs modify PNPM override logic in common/config/rush/.pnpmfile.cjs and package.json bumping the same pinned dependencies.
  • wso2/vscode-extensions#2402: Both PRs change importOpenAPISpec from Promise<void> to a structured ImportOpenAPISpecResponse and update consuming UI flows.
  • wso2/vscode-extensions#2404: Both PRs modify ParameterManager.tsx's handleEditParameter normalization logic before form.reset.

Suggested labels: Extension/MI, Checks/Run MI UI Tests

Suggested reviewers: hevayo, gigara, arunans23

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is far too brief and misses the required template sections like Purpose, Goals, Approach, tests, and release notes. Rewrite the PR description using the repository template and fill the required sections, especially Purpose, Goals, Approach, tests, and release note.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly reflects an automated sync merge from stable/mi to main.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/main-stable/mi-1783314625
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch sync/main-stable/mi-1783314625

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.1)
common/config/rush/pnpm-config.json

File contains syntax errors that prevent linting: Line 1: JSON standard does not allow comments.; Line 5: End of file expected; Line 8: JSON standard does not allow comments.; Line 20: End of file expected; Line 20: End of file expected; Line 20: End of file expected; Line 20: End of file expected; Line 22: JSON standard does not allow comments.; Line 36: JSON standard does not allow comments.; Line 46: JSON standard does not allow comments.; Line 61: JSON standard does not allow comments.; Line 79: JSON standard does not allow comments.; Line 91: End of file expected; Line 91: End of file expected; Line 91: End of file expected; Line 94: End of file expected; Line 96: JSON standard does not allow comments.; Line 108: End of file expected; Line 108: End of file expected; Line 109: Expected a property but instead found '// "ignoreMissing": ["@eslint/*"],'.; Line 108: End of file expected; Line 109: End of file expected; Line 114: JSON standard does not allow comments.; Line 127: End of

... [truncated 344 characters] ...

an object, or a literal but instead found '// "fsevents"'.; Line 154: End of file expected; Line 155: End of file expected; Line 158: JSON standard does not allow comments.; Line 170: End of file expected; Line 170: End of file expected; Line 171: Expected a property but instead found '// "request": "*"'.; Line 170: End of file expected; Line 171: End of file expected; Line 175: JSON standard does not allow comments.; Line 182: End of file expected; Line 182: End of file expected; Line 182: End of file expected; Line 182: End of file expected; Line 184: JSON standard does not allow comments.; Line 196: End of file expected; Line 196: End of file expected; Line 197: Expected a property but instead found '// "dependencies": {'.; Line 196: End of file expected; Line 197: End of file expected


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
workspaces/mi/mi-extension/src/ai-features/auth.ts (1)

599-606: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stray period breaks the error message.

Line 602 reads "...are only available via the global. inference profile in the commercial AWS partition...". The period after "global" is a leftover artifact and garbles the sentence.

✏️ Proposed fix
         throw new Error(
             `AWS region "${region}" is not supported. The Anthropic models on Bedrock ` +
-            `(Haiku 4.5, Sonnet 4.6, Opus 4.8) are only available via the global. ` +
+            `(Haiku 4.5, Sonnet 4.6, Opus 4.8) are only available via the global ` +
             `inference profile in the commercial AWS partition — GovCloud and China ` +
             `partitions are not supported. Use a commercial region like us-east-1 or eu-west-1.`
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/ai-features/auth.ts` around lines 599 - 606,
The error text in auth.ts has a stray period in the region validation message,
causing “global. inference profile” to read incorrectly. Update the Error
message in the region check inside the auth.ts block to remove that extra
punctuation and keep the sentence continuous, preserving the rest of the warning
and the existing unsupported-region logic.
workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/ProjectExplorer.ts (1)

66-102: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add the explorer focus guard to goToAddArtifact() and addArtifact()
openFormFromArtifacts() calls goToAddArtifact() directly, and addArtifact() is also invoked without a prior goToOverview() in several paths. Reuse the same focusProjectExplorer()/retry flow here to avoid the same tree-visibility flake.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-extension/src/test/e2e-playwright-tests/components/ProjectExplorer.ts`
around lines 66 - 102, The Project Explorer interaction methods are missing the
same visibility/retry guard used by goToOverview, which can make the tree flaky
when called directly. Update goToAddArtifact() and addArtifact() to first focus
the MI Project Explorer via focusProjectExplorer(), then wait/retry for the
project root locator to become visible before hovering and clicking the Add
Artifact action. Reuse the existing projectExplorerRoot and locator flow so
openFormFromArtifacts() and other direct call paths are protected the same way
as goToOverview().
🟡 Minor comments (12)
workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts-30-30 (1)

30-30: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Use a separate cap for images. MAX_FILE_SIZE is enforced on both files and images in handleFileAttach, so 10 MB images can pass local validation and then be rejected by Anthropic’s per-image limit. Keep this constant for non-image uploads or add an image-specific limit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/AIPanel/constants.ts` at line 30,
`MAX_FILE_SIZE` is currently used for both file attachments and images in
`handleFileAttach`, which can let oversized images pass local validation and
fail later against Anthropic’s image limit. Update the AIPanel constants and
attach flow to use a separate image-specific cap while keeping `MAX_FILE_SIZE`
for non-image uploads; refer to `MAX_FILE_SIZE` and `handleFileAttach` when
wiring the new limit.
workspaces/mi/mi-extension/CHANGELOG.md-38-38 (1)

38-38: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Typo: "Automatons" → "Automations".

Fixed: Tooling Allow tryout feature for Event Integrations and Automatons (#1453) — should read "Automations" to match product terminology.

✏️ Proposed fix
-Fixed: Tooling Allow tryout feature for Event Integrations and Automatons ([`#1453`](https://github.com/wso2/mi-vscode/issues/1453))    
+Fixed: Tooling Allow tryout feature for Event Integrations and Automations ([`#1453`](https://github.com/wso2/mi-vscode/issues/1453))    
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/CHANGELOG.md` at line 38, Update the changelog
entry text to use the correct product term “Automations” instead of “Automatons”
in the listed feature description. Locate the existing release note entry in
CHANGELOG.md and replace the typo so the wording matches the rest of the product
terminology.
workspaces/mi/mi-visualizer/src/views/Forms/ConnectionForm/index.tsx-615-631 (1)

615-631: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard against repeated onError firing if the fallback icon also fails.

If connector.iconUrl/connectorFailoverIconUrl is also broken, onError will keep re-firing on the same failed URL. Clear the handler after the first fallback attempt.

🖼️ Proposed fix
                                                             onError={(e) => {
                                                                 const target = e.target as HTMLImageElement;
+                                                                target.onerror = null;
                                                                 target.src = connector.iconUrl || connector.connectorFailoverIconUrl;
                                                             }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/ConnectionForm/index.tsx` around
lines 615 - 631, The image fallback in ConnectionForm’s ConnectionCard icon can
loop if the replacement URL also fails. Update the img onError handler to stop
after the first fallback attempt by clearing the handler before switching the
src, using the existing ConnectionCard/icon render logic and the
connection.iconUrl, connector.iconUrl, and connector.connectorFailoverIconUrl
references to locate it.
workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts-593-617 (1)

593-617: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Truncation can split a multi-byte UTF-8 character.

readAgentsMd cuts the file at a fixed byte offset (AGENTS_MD_MAX_BYTES) and then decodes with buffer.subarray(0, bytesRead).toString('utf8'). If the cut lands mid-codepoint, the tail character is silently replaced with U+FFFD in the content that's injected into the prompt and referenced by the truncation footer — a safe but confusing edge case for non-ASCII AGENTS.md files.

🩹 Proposed fix: trim back to the last valid UTF-8 boundary
         const fd = fs.openSync(agentsMdPath, 'r');
         try {
             const buffer = Buffer.allocUnsafe(AGENTS_MD_MAX_BYTES);
             const bytesRead = fs.readSync(fd, buffer, 0, AGENTS_MD_MAX_BYTES, 0);
-            const content = buffer.subarray(0, bytesRead).toString('utf8');
+            let end = bytesRead;
+            // Back off from a truncated multi-byte UTF-8 sequence at the tail
+            // (continuation bytes are 0x80-0xBF; a lead byte >= 0xC0 needs more
+            // bytes than remain).
+            while (end > 0 && (buffer[end - 1] & 0xC0) === 0x80) {
+                end--;
+            }
+            const content = buffer.subarray(0, end).toString('utf8');
             return { content, truncated: true, originalSize };
         } finally {
             fs.closeSync(fd);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts`
around lines 593 - 617, The truncation logic in readAgentsMd can split a
multi-byte UTF-8 character when reading only AGENTS_MD_MAX_BYTES bytes, which
can inject replacement characters into the prompt content. Update the
oversized-file path to trim the read buffer back to the last valid UTF-8
boundary before decoding, then return that safely decoded content with
truncated=true. Keep the fix localized to readAgentsMd and its
buffer.subarray/toString handling.
workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatMessage.tsx-344-345 (1)

344-345: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Exclude context warnings from feedback eligibility.

isContextWarning now renders a system warning, but hasAnswerContent still treats its text as answer content. A warning-only final assistant message can show the feedback bar.

Proposed fix
             segment.isCompactSummary ||
             segment.isFileChanges ||
             segment.isPlan ||
-            segment.isThinking
+            segment.isThinking ||
+            segment.isContextWarning
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/AIPanel/component/AIChatMessage.tsx`
around lines 344 - 345, The feedback eligibility logic in AIChatMessage should
ignore context-warning segments, since isContextWarning now renders system text
but hasAnswerContent still counts it as answer content. Update the
answer-content check to exclude segment.isContextWarning so a warning-only final
assistant message does not qualify for feedback, while keeping normal assistant
content behavior unchanged.
workspaces/mi/mi-extension/src/ai-features/copilot/message-utils.ts-120-132 (1)

120-132: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject mismatched PDF data URI media types.

stripDataUriPrefix() accepts any data:*;base64, prefix, so a file marked application/pdf can pass validation with data:image/png;base64,... and then be sent as a PDF block. Validate the prefix media type when present.

Proposed fix
-function stripDataUriPrefix(content: string): string {
+function stripDataUriPrefix(content: string, expectedMediaType?: string): string | null {
     const match = content.match(/^data:[^;]+;base64,([\s\S]+)$/);
-    return match ? match[1] : content;
+    if (!match) {
+        return content;
+    }
+    const mediaType = content.match(/^data:([^;]+);base64,/i)?.[1]?.toLowerCase();
+    if (expectedMediaType && mediaType !== expectedMediaType.toLowerCase()) {
+        return null;
+    }
+    return match[1];
 }
-            else if (file.mimetype === "application/pdf" && !isValidBase64(stripDataUriPrefix(file.content))) {
+            else if (file.mimetype === "application/pdf") {
+                const pdfBase64 = stripDataUriPrefix(file.content, "application/pdf");
+                if (!pdfBase64 || !isValidBase64(pdfBase64)) {
+                    warnings.push(`Invalid base64 encoding: ${file.name}`);
+                }
+            }
-                warnings.push(`Invalid base64 encoding: ${file.name}`);
-            }

Also applies to: 282-282

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/ai-features/copilot/message-utils.ts` around
lines 120 - 132, The stripDataUriPrefix helper currently removes any
data:*;base64, prefix without checking the declared media type, which can let
mismatched content slip through as a PDF. Update stripDataUriPrefix and the call
path in message-utils.ts so that when a data URI is present it validates the
prefix media type against the expected document type before stripping it, and
rejects or preserves mismatched prefixes instead of normalizing them. Use the
stripDataUriPrefix symbol and the PDF message handling code nearby to keep the
validation aligned with the existing inline-data flow.
workspaces/mi/mi-visualizer/src/views/Forms/RegistryResourceForm.tsx-45-46 (1)

45-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Trailing backslash isn't normalized to /, risking mixed-separator paths and duplicate-check bypass.

When resourceDirPath/path ends with \ (only reachable on Windows per the validatePathSeparator schema rule), both formatResourcePath and formatRegistryPath append the filename directly onto the existing \-terminated string instead of normalizing to /. This can produce a mixed-separator path that won't match the /-delimited paths returned from the backend (resourcePaths/registryPaths), silently defeating the "Resource already exists" duplicate check.

🐛 Suggested fix (normalize separators up front)
     const formatResourcePath = (resourceDirPath: string) => {
         let resPath = 'resources:';
-        resPath = PATH_SEPARATORS.some(sep => resourceDirPath.startsWith(sep)) ? resPath + resourceDirPath.substring(1) : resPath + resourceDirPath;
+        const normalizedDirPath = resourceDirPath.replace(/\\/g, '/');
+        resPath = normalizedDirPath.startsWith('/') ? resPath + normalizedDirPath.substring(1) : resPath + normalizedDirPath;
         if (createOptionValue) {
-            PATH_SEPARATORS.some(sep => resPath.endsWith(sep)) ? resPath = resPath + getValues("resourceName") + getFileExtension(getValues('templateType'))
-                : resPath = resPath + '/' + getValues("resourceName") + getFileExtension(getValues('templateType'));
+            resPath = resPath.endsWith('/') ? resPath + getValues("resourceName") + getFileExtension(getValues('templateType'))
+                : resPath + '/' + getValues("resourceName") + getFileExtension(getValues('templateType'));
         } else {
             const filename = getValues("filePath").split(/[/\\]/).pop();
-            PATH_SEPARATORS.some(sep => resPath.endsWith(sep)) ? resPath = resPath + filename : resPath = resPath + '/' + filename;
+            resPath = resPath.endsWith('/') ? resPath + filename : resPath + '/' + filename;
         }
         return resPath;
     }

Apply the analogous fix to formatRegistryPath.

Also applies to: 277-306

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/RegistryResourceForm.tsx` around
lines 45 - 46, Normalize trailing path separators before building paths in both
formatResourcePath and formatRegistryPath, since a Windows-style trailing
backslash can leave mixed separators and bypass the duplicate check against
resourcePaths/registryPaths. Update the path formatting logic in
RegistryResourceForm so resourceDirPath/path is converted to a consistent
"/"-delimited form up front, then append the filename using that normalized
base. Make the same fix in both helpers referenced by PATH_SEPARATORS and the
path formatting block to keep duplicate detection reliable.
workspaces/mi/syntax-tree/src/syntax-tree-interfaces.ts-1158-1158 (1)

1158-1158: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Make APIResource.bindsTo optional
workspaces/mi/syntax-tree/src/syntax-tree-interfaces.ts:1158 — the resource schema doesn’t define bindsTo, and the form code already handles it as missing/empty, so this should be bindsTo?: string to match the actual contract.

🔧 Suggested fix
-    bindsTo: string;
+    bindsTo?: string;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/syntax-tree/src/syntax-tree-interfaces.ts` at line 1158, Make
APIResource.bindsTo optional in syntax-tree-interfaces.ts so the type matches
the actual resource contract and existing form handling. Update the APIResource
interface field from required to optional, and ensure any related references to
APIResource or bindsTo continue treating it as missing/empty when not provided.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/index.tsx-227-230 (1)

227-230: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Title and button text never reflect edit mode.

FormView title (line 230) and createButtonLabel (line 227) always say "Create MCP Server"/"Creating..." even when editData is provided.

♻️ Proposed fix
-        <FormView title="Create MCP Server" onClose={handleClose}>
+        <FormView title={editData ? "Edit MCP Server" : "Create MCP Server"} onClose={handleClose}>
-    const createButtonLabel = submitting ? 'Creating...' : (portDiscoveryLoading || depState === 'idle' || depState === 'checking') ? 'Checking...' : 'Create MCP Server';
+    const createButtonLabel = submitting ? (editData ? 'Updating...' : 'Creating...') : (portDiscoveryLoading || depState === 'idle' || depState === 'checking') ? 'Checking...' : (editData ? 'Update MCP Server' : 'Create MCP Server');

Also applies to: 319-320

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/index.tsx` around
lines 227 - 230, The MCPServerForm is not switching its UI copy when editData is
present, so both the FormView title and createButtonLabel still use create-mode
text. Update the conditional text in MCPServerForm (around createButtonLabel and
the FormView title) to derive labels from edit mode versus create mode, using
editData to choose “Edit/Update” copy and keeping the existing “Create/Creating”
copy for new servers. Also check the related submit/secondary button text near
the later referenced section so all displayed actions stay consistent with the
current mode.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/index.tsx-63-66 (1)

63-66: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Port field lacks valid-range bounds.

Only required/integer checks are applied; nothing constrains the value to a valid TCP port range, allowing e.g. negative numbers or values above 65535 to pass client-side validation.

🐛 Proposed fix
     port: yup.number()
         .typeError('Port must be a number')
         .required('Port is required')
-        .integer('Port must be an integer'),
+        .integer('Port must be an integer')
+        .min(1, 'Port must be between 1 and 65535')
+        .max(65535, 'Port must be between 1 and 65535'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/index.tsx` around
lines 63 - 66, The MCP server form port validation in MCPServerForm currently
only checks required and integer values, so invalid TCP ports can still pass.
Update the yup schema for the port field in the form validation to add explicit
minimum and maximum bounds so only valid port numbers are accepted, using the
existing port validator chain in the MCPServerForm component.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/AddAPIToolDialog.tsx-224-236 (1)

224-236: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Unguarded convertMcpJsonSchema/pickMcpJsonFile RPC calls can produce unhandled promise rejections.

validateSchema awaits convertMcpJsonSchema with no try/catch, and it's invoked fire-and-forget from the schema onChange handler (Line 460) and from handleImportFile (Line 294, which also has its own unguarded pickMcpJsonFile() call). If the RPC rejects, this becomes an unhandled rejection with no error surfaced to the user (the field just silently fails to validate/import).

🛡️ Proposed fix
 const validateSchema = async (id: string, value: string): Promise<boolean> => {
     if (!value.trim()) {
         clearErrors(`items.${id}.inputSchema` as const);
         return true;
     }
-    const { schema } = await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: value });
-    if (schema === null) {
-        setError(`items.${id}.inputSchema` as const, { message: INVALID_MCP_SCHEMA_MESSAGE });
-        return false;
-    }
-    clearErrors(`items.${id}.inputSchema` as const);
-    return true;
+    try {
+        const { schema } = await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: value });
+        if (schema === null) {
+            setError(`items.${id}.inputSchema` as const, { message: INVALID_MCP_SCHEMA_MESSAGE });
+            return false;
+        }
+        clearErrors(`items.${id}.inputSchema` as const);
+        return true;
+    } catch (error) {
+        console.error('Schema validation failed', error);
+        setError(`items.${id}.inputSchema` as const, { message: INVALID_MCP_SCHEMA_MESSAGE });
+        return false;
+    }
 };

Also applies to: 290-295, 459-461

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/AddAPIToolDialog.tsx`
around lines 224 - 236, Guard the RPC calls in AddAPIToolDialog’s validateSchema
and handleImportFile so rejected promises do not become unhandled rejections.
Wrap rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema and pickMcpJsonFile
in try/catch, surface a user-facing error via setError or existing import error
handling, and ensure the schema onChange path and import flow both await or
safely handle the async result from validateSchema/handleImportFile.
workspaces/mi/mi-visualizer/src/views/Overview/ProjectStructureView.tsx-269-287 (1)

269-287: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle MCP servers that only have an inbound endpoint path. The row can render from inboundEndpoint.path, but goToMcpServerTools returns immediately when localEntry.path is missing, so clicking those entries does nothing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Overview/ProjectStructureView.tsx`
around lines 269 - 287, The goToMcpServerTools handler currently exits early
when localEntry.path is missing, which breaks MCP server rows that render from
inboundEndpoint.path only. Update goToMcpServerTools in ProjectStructureView to
accept inboundEndpoint.path as a valid source for navigation, derive the
serverName and documentUri from whichever path is available, and keep passing
both localEntryPath and inboundEndpointPath in customProps for
MACHINE_VIEW.MCPServerFromAPIsForm.
🧹 Nitpick comments (16)
workspaces/mi/mi-visualizer/src/views/Forms/ConnectionForm/ImportConnectionFromOpenAPI.tsx (1)

68-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting shared import logic into a hook.

importWithOpenAPI is now nearly identical to importWithProto in ImportConnectionFromProto.tsx (same state, same result-check/error-handling flow). Given the codebase already introduces shared hooks for connector concerns (e.g., useConnectorDependency), consider extracting this into a useSpecImport-style hook parameterized by file path/type to avoid duplicated logic across both files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/ConnectionForm/ImportConnectionFromOpenAPI.tsx`
around lines 68 - 90, The import flow in importWithOpenAPI duplicates the same
state transitions, result checks, and error handling already present in
importWithProto, so extract that shared logic into a reusable hook such as
useSpecImport. Move the common async workflow into the hook, parameterize it
with the spec source details (for example file path/type and success callback),
and update ImportConnectionFromOpenAPI to use the shared hook instead of keeping
a separate copy of the logic.
workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts (1)

902-931: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding unit tests for the new injection-defense / truncation logic.

neutralizePromptEnvelopeTags and buildAgentsMdBlockText are new, security-relevant pure functions (prompt envelope-tag neutralization, truncation-footer wording) with no visible test coverage. Since they gate whether user-authored AGENTS.md content can break out of the <system-reminder> wrapper, a couple of focused unit tests (tag-breakout attempt, truncated vs. non-truncated, cleared status) would guard against regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/agents/main/prompt.ts`
around lines 902 - 931, Add focused unit tests for the new prompt-safety helpers
so regressions are caught early. Cover neutralizePromptEnvelopeTags with a
breakout attempt that contains system-reminder and user_query tags, and verify
buildAgentsMdBlockText in the cleared, truncated, and non-truncated paths. Use
the existing helper names neutralizePromptEnvelopeTags and
buildAgentsMdBlockText to locate the logic, and assert the returned text
contains the expected neutralized tags, truncation footer, or cleared-message
behavior.
workspaces/mi/mi-extension/src/ai-features/agent-mode/attachment-utils.ts (1)

91-108: 📐 Maintainability & Code Quality | 🔵 Trivial

Minor duplication between stripDataUriPrefix and toImageContentPart.

Both functions parse the same data:<mediaType>;base64,<payload> pattern independently. Consider having toImageContentPart reuse stripDataUriPrefix plus a small mediaType extraction, or extract a shared parseDataUri() helper returning { mediaType?, data }.

♻️ Possible consolidation
-function toImageContentPart(imageDataUri: string): { type: "image"; image: string; mediaType?: string } {
-    const match = imageDataUri.match(/^data:([^;]+);base64,([\s\S]+)$/);
-    if (match) {
-        return { type: "image", image: match[2], mediaType: match[1] };
-    }
-    // Fallback: pass through unchanged (already-raw base64 or unexpected format).
-    return { type: "image", image: imageDataUri };
-}
+function parseDataUri(value: string): { mediaType?: string; data: string } {
+    const match = value.match(/^data:([^;]+);base64,([\s\S]+)$/);
+    return match ? { mediaType: match[1], data: match[2] } : { data: value };
+}
+
+function stripDataUriPrefix(content: string): string {
+    return parseDataUri(content).data;
+}
+
+function toImageContentPart(imageDataUri: string): { type: "image"; image: string; mediaType?: string } {
+    const { mediaType, data } = parseDataUri(imageDataUri);
+    return { type: "image", image: data, mediaType };
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/attachment-utils.ts`
around lines 91 - 108, `stripDataUriPrefix` and `toImageContentPart` both parse
the same data URI format separately, so consolidate this logic in
`attachment-utils.ts`. Update `toImageContentPart` to reuse `stripDataUriPrefix`
for the payload and add a shared helper (or equivalent) to extract `mediaType`,
so the `data:<mediaType>;base64,<payload>` handling lives in one place and both
functions stay consistent.
workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/types.ts (1)

66-115: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

Add content-based binary detection before text reads/writes
The deny-list still treats extensionless and unlisted files as text, so a binary artifact with an unknown extension can be opened or overwritten as UTF-8. Keep the image/PDF multimodal paths, but add byte-level sniffing before the text path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/types.ts` around
lines 66 - 115, The current extension-only check in hasBlockedBinaryExtension
still lets extensionless or unknown binary files be treated as text, so add
content-based sniffing before any text read/write path. Update the file handling
flow in the agent tools around file_read and file_write/file_edit to inspect
initial bytes/magic signatures and route binaries to the blocked/multimodal path
even when the extension is not in BLOCKED_BINARY_EXTENSIONS. Keep the existing
image/PDF special cases intact, but ensure the new check runs before the UTF-8
text path is chosen.
workspaces/mi/mi-diagram/src/components/Form/GigaParamManager/ParameterManager.tsx (1)

112-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared expression-type lists. ParameterManager, FormGenerator, and DynamicFieldsHandler all hard-code the same OrExpression grouping, including the legacy stringOrExpresion alias. Move this to one shared helper so the categories don’t drift; keep the alias only if older saved forms still depend on it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-diagram/src/components/Form/GigaParamManager/ParameterManager.tsx`
around lines 112 - 117, The expression-type grouping is duplicated across
ParameterManager, FormGenerator, and DynamicFieldsHandler, which risks the lists
drifting out of sync. Extract the shared OrExpression categories, including the
legacy stringOrExpresion alias if it is still needed for backward compatibility,
into one common helper and have ParameterManager use that shared source instead
of its local EXPRESSION_INPUT_TYPES array.
workspaces/mi/mi-extension/src/debugger/debugHelper.ts (1)

437-442: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Minor: pid non-null assertion could throw if spawn failed to obtain a pid.

activeBuildProcess.pid! assumes pid is always defined. If the underlying process failed to spawn (e.g., ENOENT), pid could be undefined, and treeKill may throw or behave unexpectedly. Low risk given activeBuildProcess is only set after a successful spawn() call, but a guard would be more defensive.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/debugger/debugHelper.ts` around lines 437 -
442, The abortBuildAndRun helper is using a non-null assertion on
activeBuildProcess.pid, which can be unsafe if the process never spawned
correctly. Update abortBuildAndRun to defensively check that activeBuildProcess
and its pid are both defined before calling treeKill, and only clear
activeBuildProcess after a valid kill attempt; use the abortBuildAndRun and
activeBuildProcess symbols to locate the change.
workspaces/mi/mi-visualizer/src/Hooks.tsx (1)

54-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Register the download-progress listener in an effect. The current body-level subscription runs on every render, so each progress update can add another handler. Move it into useEffect with [rpcClient], and dispose the listener on cleanup if onNotification returns a disposable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/Hooks.tsx` around lines 54 - 56, The
download-progress subscription is being registered directly in the Hooks
component body, so it runs on every render and can accumulate duplicate
handlers. Move the rpcClient.onDownloadProgress registration into a useEffect
tied to [rpcClient], and keep the setDownloadProgress callback inside that
effect. If onDownloadProgress/onNotification returns a disposable or unsubscribe
handle, store it and invoke cleanup in the effect return to avoid leaking
listeners.
workspaces/mi/mi-visualizer/src/MainPanel.tsx (1)

177-330: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant per-case key props now dead code.

getUniqueKey(...) keys set on ResourceView, SequenceView, ProxyView, DataServiceView, etc. are now overwritten by React.cloneElement(..., { key: viewKey }) at Line 445, since viewKey is applied uniformly to every view. Not a functional bug (viewKey is a superset), but the per-case keys are effectively unused now.

♻️ Optional cleanup
-                        key={getUniqueKey(visualizerState.stNode, visualizerState.documentUri)}

(remove from each case now redundant with the outer cloneElement key, or drop the cloneElement wrapper for cases that already set an equivalent key.)

Also applies to: 444-446

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/MainPanel.tsx` around lines 177 - 330, The
per-case getUniqueKey(...) key props in MainPanel’s view switch are now
redundant because React.cloneElement later applies a uniform viewKey to every
rendered view. Clean up the affected cases such as ResourceView, SequenceView,
ProxyView, and DataServiceView by either removing the local key props or, if you
want to preserve them, stop wrapping the result in cloneElement for those views
so only one key source remains.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/AddAPIToolDialog.tsx (1)

318-336: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify the convoluted flatMap(...).map(fn => fn()) construction, and guard its own convertMcpJsonSchema call.

This can be a plain Promise.all(opIds.map(async opId => {...})). It also makes its own unguarded convertMcpJsonSchema call (Line 326) with no try/catch, so a rejection here fails the whole submit silently (unhandled rejection, form stuck).

♻️ Proposed simplification
-        const selectedOperations = await Promise.all(opIds.flatMap(opId => {
-            const operation = selectedAPI?.operations.find((op: APIOperation) => op.id === opId);
-            if (!operation) return [];
-            return [async () => {
-                const item = data.items?.[opId];
-                const raw = item?.inputSchema?.trim() || '';
-                let converted: string | null = null;
-                if (raw) {
-                    const { schema } = await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: raw });
-                    converted = schema;
-                }
-                return {
-                    id: opId,
-                    customName: item?.customName || getDefaultName(operation),
-                    description: item!.description.trim(),
-                    inputSchema: converted || EMPTY_MCP_SCHEMA,
-                };
-            }];
-        }).map(fn => fn()));
+        const validOpIds = opIds.filter(opId => selectedAPI?.operations.some((op: APIOperation) => op.id === opId));
+        const selectedOperations = await Promise.all(validOpIds.map(async opId => {
+            const operation = selectedAPI!.operations.find((op: APIOperation) => op.id === opId)!;
+            const item = data.items?.[opId];
+            const raw = item?.inputSchema?.trim() || '';
+            let converted: string | null = null;
+            if (raw) {
+                try {
+                    const { schema } = await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: raw });
+                    converted = schema;
+                } catch (error) {
+                    console.error('Schema conversion failed', error);
+                }
+            }
+            return {
+                id: opId,
+                customName: item?.customName || getDefaultName(operation),
+                description: item!.description.trim(),
+                inputSchema: converted || EMPTY_MCP_SCHEMA,
+            };
+        }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/AddAPIToolDialog.tsx`
around lines 318 - 336, The selectedOperations builder in AddAPIToolDialog is
overly convoluted and leaves convertMcpJsonSchema unguarded. Replace the current
flatMap(...).map(fn => fn()) pattern with a direct Promise.all over opIds using
a single async mapper in AddAPIToolDialog, and wrap the
rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema call in try/catch so a
schema conversion failure falls back to EMPTY_MCP_SCHEMA instead of rejecting
the whole submit flow.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/ToolsList.tsx (1)

213-217: 🚀 Performance & Scalability | 🔵 Trivial

Same on-keystroke RPC validation pattern.

validateSchema runs on every onChange (Line 215), same as the other MCP tool dialogs — consider debouncing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/ToolsList.tsx`
around lines 213 - 217, The `validateSchema` call in `ToolsList` is still firing
on every `onChange`, so update the `setEditToolInputSchema` handler to debounce
validation instead of running it on each keystroke. Use the existing
`validateSchema` path in `ToolsList.tsx` and apply the same debouncing approach
used in the other MCP tool dialogs so schema checks happen after typing settles.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/CreateScratchToolDialog.tsx (2)

60-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated tool-name sanitization logic.

This sanitizeToolName helper duplicates the inline name-derivation logic in MCPServerToolsForm.tsx's confirmAddScratchTool (name lowercasing + character replacement + _tool suffix). Since the collision check here validates against a name computed independently of the one actually used to create the sequence, any future change to one algorithm without the other risks silent collision-detection mismatches. Consider extracting a single shared helper (e.g. exported from this file or a small shared util) and reusing it in both places.

♻️ Suggested direction
+// e.g. export from a shared module and import in both files
+export const sanitizeToolName = (raw: string): string =>
     raw.trim().toLowerCase()
         .replace(/[^a-z0-9]/g, '_')
         .replace(/_{2,}/g, '_')
         .replace(/^_+|_+$/, '');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/CreateScratchToolDialog.tsx`
around lines 60 - 64, The tool-name sanitization logic is duplicated between
sanitizeToolName in CreateScratchToolDialog and confirmAddScratchTool in
MCPServerToolsForm, which can cause mismatch between collision checks and the
actual created sequence name. Extract the name-derivation into a single shared
helper and reuse it in both places so both the collision validation and tool
creation use the exact same lowercasing, replacement, and suffix rules.

225-235: 🚀 Performance & Scalability | 🔵 Trivial

Same on-keystroke RPC validation pattern as AddSequenceToolDialog.tsx.

validateSchema is called on every onChange (line 233) without debouncing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/CreateScratchToolDialog.tsx`
around lines 225 - 235, The `CreateScratchToolDialog` input schema field is
triggering `validateSchema` on every keystroke via the `TextArea` `onChange`
handler, which mirrors the same RPC-heavy pattern seen in
`AddSequenceToolDialog`. Update this to debounce or otherwise throttle the
`validateSchema` call so validation runs after typing pauses instead of on every
change. Keep the logic in the `register('inputSchema', ...)` handler and
preserve the existing schema validation behavior.
workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/AddSequenceToolDialog.tsx (1)

299-310: 🚀 Performance & Scalability | 🔵 Trivial

Schema validation fires an RPC on every keystroke.

convertMcpJsonSchema is invoked on every onChange of the schema textarea (Line 307) with no debounce, so each keystroke triggers a full RPC round-trip to the extension host. The same pattern recurs in CreateScratchToolDialog.tsx and ToolsList.tsx. Consider debouncing the validation call (e.g., 300-500ms) to reduce chattiness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/AddSequenceToolDialog.tsx`
around lines 299 - 310, The schema validation path in the textarea is firing on
every keystroke, causing repeated RPC calls from the `onChange` handler wired
through `register(..., { onChange: e => validateSchema(seq.id, e.target.value)
})`. Update `validateSchema` usage in `AddSequenceToolDialog` to debounce the
`convertMcpJsonSchema`-triggering validation, and apply the same debounce
pattern in `CreateScratchToolDialog` and `ToolsList` where the same validation
flow exists. Keep the validation behavior intact, but delay execution by a short
interval so typing does not spam the extension host.
workspaces/mi/mi-visualizer/src/views/ServiceDesigner/index.tsx (1)

326-326: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate bindsTo string-parsing logic.

The comma-split/trim/filter logic here duplicates getApiBindsToOptions's underlying parsing (form.ts, via bindsToToList) and the equivalent logic in ResourceForm.tsx's bindsToToParamConfig. Consider exporting and reusing a single bindsToToList/stringToList helper from form.ts across all three call sites to avoid drift if the delimiter or normalization rules ever change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/ServiceDesigner/index.tsx` at line 326,
The bindsTo parsing in ServiceDesigner is duplicated and should reuse the shared
list conversion logic instead of repeating split/trim/filter here. Update the
ServiceDesigner component to call the same helper used by getApiBindsToOptions
and ResourceForm.tsx, ideally exporting bindsToToList or a generic stringToList
from form.ts, so all three call sites stay consistent if the parsing rules
change.
workspaces/mi/mi-visualizer/src/views/Forms/ResourceForm.tsx (1)

171-200: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate “Inbound Endpoint” ParamField definition.

The ParamField config for the bindings AutoComplete is defined twice: once inside bindsToToParamConfig (Lines 178-187) and again inline in the JSX (Lines 476-484). Since the JSX always overrides paramFields from bindsToParams, the paramFields computed inside bindsToToParamConfig is effectively dead/unused at render time — only paramValues from that helper survives. This drift risk (e.g. someone updates one definition but not the other) can be avoided by extracting a single buildBindsToParamField(options: string[]): ParamField helper reused in both places.

♻️ Suggested consolidation
+const buildBindsToParamField = (options: string[]): ParamField => ({
+    id: 0,
+    type: "AutoComplete",
+    label: "Inbound Endpoint",
+    values: options,
+    isRequired: true,
+    allowItemCreate: false,
+});
+
 const bindsToToParamConfig = (bindsTo: string, options: string[]): ParamConfig => {
-    const paramFields: ParamField[] = [
-        {
-            id: 0,
-            type: "AutoComplete",
-            label: "Inbound Endpoint",
-            values: options,
-            isRequired: true,
-            allowItemCreate: false,
-        },
-    ];
     return {
-        paramFields,
+        paramFields: [buildBindsToParamField(options)],
         ...
     };
 };

And in the JSX:

                                                 <ParamManager
                                                     paramConfigs={{
                                                         ...bindsToParams,
-                                                        paramFields: [
-                                                            {
-                                                                id: 0,
-                                                                type: "AutoComplete",
-                                                                label: "Inbound Endpoint",
-                                                                values: bindsToOptions,
-                                                                isRequired: true,
-                                                                allowItemCreate: false,
-                                                            },
-                                                        ],
+                                                        paramFields: [buildBindsToParamField(bindsToOptions)],
                                                     }}

Also applies to: 469-491

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/ResourceForm.tsx` around lines
171 - 200, The “Inbound Endpoint” ParamField is duplicated between
bindsToToParamConfig and the JSX that builds bindsToParams, and the helper’s
paramFields are effectively overwritten at render time. Refactor ResourceForm so
there is a single shared builder for this field, such as a reusable
buildBindsToParamField(options) helper, and use it both in bindsToToParamConfig
and where the JSX currently defines the AutoComplete field. Keep the helper’s
ParamConfig focused on paramValues and avoid maintaining two separate ParamField
definitions.
workspaces/mi/mi-visualizer/src/views/Diagram/Resource.tsx (1)

55-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard against stale/out-of-order responses in the bindsTo fetch effect.

The async IIFE fetching bindsToOptions on documentUri change has no guard against out-of-order resolution — if documentUri changes again before the previous fetch resolves, a stale response can overwrite fresher state.

🔒 Suggested fix
     useEffect(() => {
+        let cancelled = false;
         (async () => {
-            setBindsToOptions(await getApiBindsToOptions(rpcClient, documentUri));
+            const options = await getApiBindsToOptions(rpcClient, documentUri);
+            if (!cancelled) {
+                setBindsToOptions(options);
+            }
         })();
+        return () => { cancelled = true; };
     }, [documentUri]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Diagram/Resource.tsx` around lines 55 -
61, The bindsToOptions fetch in Resource’s useEffect can apply stale results if
an older getApiBindsToOptions call resolves after a newer documentUri change.
Update the effect to guard against out-of-order async completion, using the
existing useEffect and setBindsToOptions flow in Resource.tsx. Add a
cancellation/active flag or similar cleanup so only the latest documentUri
request can update state, and ignore results from superseded requests.

Comment on lines +902 to +918
// Mirror the POSIX analyzer: block mutations targeting binary file paths
// (matches the file-tool deny-list). Basename is compared case-insensitively
// via `hasBlockedBinaryExtension`. We use `path.win32.basename` to handle
// both `/` and `\` separators in PowerShell tokens.
const blockedBinaryMutationPaths = dedupe(
extractedPathTokens.mutationPathTokens.filter(
(token) => hasBlockedBinaryExtension(path.win32.basename(stripWrappingQuotes(token)))
)
);
if (blockedBinaryMutationPaths.length > 0) {
blocked = true;
reasons.push(
`Mutating binary file paths is blocked (write/edit/delete/rename; matches the file-tool deny-list). ` +
`Blocked path(s): ${blockedBinaryMutationPaths.join(', ')}. ` +
`Use directory-level operations (e.g. \`Remove-Item -Recurse dist\`) instead of targeting binary files by name.`
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate PowerShell analyzer tests and add/verify a case where a variable target
# points to a deny-listed binary path and must be blocked, not approval-only.
rg -n -C3 "analyzePowerShellCommand|unresolvedPathLikeTokens|Remove-Item|blockedBinaryMutationPaths" \
  workspaces/mi/mi-extension/src/ai-features/agent-mode

Repository: wso2/vscode-extensions

Length of output: 12084


Block unresolved PowerShell mutation targets. Variable targets like Remove-Item $p can hide a deny-listed binary path and currently fall through to unresolvedPathLikeTokens, which only adds an approval reason. Mirror the POSIX branch’s fail-closed handling so ambiguous mutation targets are blocked, not just approved.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-extension/src/ai-features/agent-mode/tools/shell_sandbox_powershell.ts`
around lines 902 - 918, The PowerShell mutation analyzer in
shell_sandbox_powershell.ts currently lets unresolved mutation targets like
Remove-Item $p fall into unresolvedPathLikeTokens and only adds an approval
reason, which should be blocked instead. Update the mutation-path handling
around extractedPathTokens and blockedBinaryMutationPaths to mirror the POSIX
fail-closed behavior: detect ambiguous or unresolved mutation targets during
write/edit/delete/rename analysis, mark them as blocked, and add a rejection
reason instead of approving them when the target cannot be resolved.

Comment on lines +216 to 217
activeBuildProcess = buildProcess;
showServerOutputChannel();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Aborted builds will show a misleading "build failed" message.

The close handler now fires unconditionally (previously gated by shouldCopyTarget) and reports failure whenever code !== 0. When abortBuildAndRun() kills the process via SIGKILL on disconnect, Node reports code === null (not 0) on exit/close, so this handler will show "${project} build failed" even though the build was intentionally aborted by the user — contradicting the disconnect flow's intent to suppress error UI (_isDisconnecting is checked in debugAdapter.ts's executeTasks().catch(), but not here).

🛠️ Proposed fix: track abort state and suppress the message
+let isBuildAborted = false;
+
 export function abortBuildAndRun(): void {
     if (activeBuildProcess) {
+        isBuildAborted = true;
         treeKill(activeBuildProcess.pid!, 'SIGKILL');
         activeBuildProcess = undefined;
     }
 }
                 buildProcess.on('close', async (code) => {
-                    if (code === 0) {
+                    if (isBuildAborted) {
+                        isBuildAborted = false;
+                    } else if (code === 0) {
                         vscode.window.showInformationMessage(`${path.basename(project)} build was successful`);
                     } else {
                         vscode.window.showErrorMessage(`${path.basename(project)} build failed`);
                     }
                 });

Also applies to: 225-231

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/debugger/debugHelper.ts` around lines 216 -
217, The build process close handler in debugHelper.ts is now treating every
non-zero/undefined exit as a failure, which causes aborted builds from
abortBuildAndRun() to report a misleading “build failed” message. Update the
close/exit handling around activeBuildProcess and showServerOutputChannel so it
can detect an intentional abort (for example via a flag set when SIGKILL is sent
on disconnect) and suppress the failure UI in that case. Keep the failure
message only for genuine build errors, and align the logic with the disconnect
flow already handled in debugAdapter.ts.

Comment on lines +488 to +490
deleteApiMetadata(fileUri);
}
refreshViewsAfterDelete(fileUri);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target file around the reported lines
sed -n '430,520p' workspaces/mi/mi-extension/src/project-explorer/activate.ts

# Locate the helper definitions and all call sites
rg -n "deleteApiMetadata|refreshViewsAfterDelete|deleteSwagger|fileUri" workspaces/mi/mi-extension/src/project-explorer/activate.ts workspaces/mi/mi-extension/src -g '!**/node_modules/**'

Repository: wso2/vscode-extensions

Length of output: 17784


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the helper signatures and the tree item builders that feed DELETE_PROJECT_EXPLORER_ITEM
sed -n '1,120p' workspaces/mi/mi-extension/src/project-explorer/activate.ts
sed -n '600,660p' workspaces/mi/mi-extension/src/util/fileOperations.ts
sed -n '300,340p' workspaces/mi/mi-extension/src/util/swagger.ts

# Find where project explorer TreeItems attach command arguments or info.path
rg -n "DELETE_PROJECT_EXPLORER_ITEM|info\.path|command:.*DELETE_PROJECT_EXPLORER_ITEM|arguments:\s*\[" workspaces/mi/mi-extension/src -g '!**/node_modules/**'

Repository: wso2/vscode-extensions

Length of output: 12733


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the project explorer tree-item builders around the delete-related entries
sed -n '420,460p' workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts
sed -n '930,960p' workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts

# Also inspect any MCP-related tree item construction for context, if present
sed -n '860,900p' workspaces/mi/mi-extension/src/project-explorer/project-explorer-provider.ts

Repository: wso2/vscode-extensions

Length of output: 3911


Normalize fileUri before cleanup calls. fileUri can be a Uri, and deleteSwagger, deleteApiMetadata, and refreshViewsAfterDelete all expect a string path. Convert once to filePath and pass that through so API metadata cleanup and the post-delete refresh run on the right resource.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/project-explorer/activate.ts` around lines 488
- 490, Normalize the resource before cleanup in the delete flow: in the code
around deleteSwagger, deleteApiMetadata, and refreshViewsAfterDelete, convert
fileUri to a string path once (for example, a filePath variable) and pass that
consistently to all three calls. This ensures the cleanup and refresh logic in
activate.ts targets the correct resource regardless of whether fileUri is a Uri
or string.

Comment on lines +686 to +705
if (!localEntryPath || !inboundEndpointPath) {
window.showErrorMessage('Could not determine MCP server paths');
return;
}

const confirmation = await window.showWarningMessage(
`Are you sure you want to delete MCP Server - ${item.label}?`,
{ modal: true },
'Yes'
);

if (confirmation === 'Yes') {
try {
// Delete local entry file
await vscode.workspace.fs.delete(Uri.file(localEntryPath), { recursive: true, useTrash: true });

// Delete inbound endpoint file if it exists
if (inboundEndpointPath) {
await vscode.workspace.fs.delete(Uri.file(inboundEndpointPath), { recursive: true, useTrash: true });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allow MCP server deletion when only the local-entry config exists.

inboundEndpoint is optional in MCPServerTreeItem, but this branch refuses to delete unless inboundEndpointPath is present. That leaves local-entry-only or partially-created MCP servers undeletable from the explorer.

Proposed fix
-					if (!localEntryPath || !inboundEndpointPath) {
+					if (!localEntryPath) {
 						window.showErrorMessage('Could not determine MCP server paths');
 						return;
 					}
@@
-							// Delete inbound endpoint file if it exists
 							if (inboundEndpointPath) {
-								await vscode.workspace.fs.delete(Uri.file(inboundEndpointPath), { recursive: true, useTrash: true });
+								try {
+									await vscode.workspace.fs.delete(Uri.file(inboundEndpointPath), { recursive: true, useTrash: true });
+								} catch {
+									// The inbound endpoint is optional; continue deleting the MCP config.
+								}
 							}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!localEntryPath || !inboundEndpointPath) {
window.showErrorMessage('Could not determine MCP server paths');
return;
}
const confirmation = await window.showWarningMessage(
`Are you sure you want to delete MCP Server - ${item.label}?`,
{ modal: true },
'Yes'
);
if (confirmation === 'Yes') {
try {
// Delete local entry file
await vscode.workspace.fs.delete(Uri.file(localEntryPath), { recursive: true, useTrash: true });
// Delete inbound endpoint file if it exists
if (inboundEndpointPath) {
await vscode.workspace.fs.delete(Uri.file(inboundEndpointPath), { recursive: true, useTrash: true });
}
if (!localEntryPath) {
window.showErrorMessage('Could not determine MCP server paths');
return;
}
const confirmation = await window.showWarningMessage(
`Are you sure you want to delete MCP Server - ${item.label}?`,
{ modal: true },
'Yes'
);
if (confirmation === 'Yes') {
try {
// Delete local entry file
await vscode.workspace.fs.delete(Uri.file(localEntryPath), { recursive: true, useTrash: true });
if (inboundEndpointPath) {
try {
await vscode.workspace.fs.delete(Uri.file(inboundEndpointPath), { recursive: true, useTrash: true });
} catch {
// The inbound endpoint is optional; continue deleting the MCP config.
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/project-explorer/activate.ts` around lines 686
- 705, The MCP server delete flow in activate.ts is too strict because it
requires both localEntryPath and inboundEndpointPath, even though
inboundEndpoint is optional in MCPServerTreeItem. Update the deletion guard so
it only blocks when localEntryPath is missing, then keep deleting the local
entry file and delete the inbound endpoint file conditionally only when
inboundEndpointPath exists. Use the existing delete branch around
window.showWarningMessage and vscode.workspace.fs.delete to ensure
local-entry-only or partially created MCP servers can still be removed.

Comment on lines +1852 to +1854
taskProperties: [],
startOnLoad: jsonData.task["@_"]["startOnLoad"] !== undefined ?
String(jsonData.task["@_"]["startOnLoad"]) : undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return the parsed task response even when there are no task properties.

startOnLoad is added to response, but tasks without jsonData.task.property still fall through to the default empty response below, so the edit form loses the parsed task fields.

Proposed fix
-                if (jsonData.task.property) {
+                if (jsonData.task.property) {
                     response.taskProperties = Array.isArray(jsonData.task.property) ?
                         jsonData.task.property.map((prop: any) => ({
@@
-                    resolve(response);
                 }
 
-                resolve({
-                    name: '',
-                    group: '',
-                    implementation: '',
-                    pinnedServers: '',
-                    triggerType: 'simple',
-                    triggerCount: 1,
-                    triggerInterval: 1,
-                    triggerCron: '',
-                    taskProperties: []
-                });
+                resolve(response);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
taskProperties: [],
startOnLoad: jsonData.task["@_"]["startOnLoad"] !== undefined ?
String(jsonData.task["@_"]["startOnLoad"]) : undefined
if (jsonData.task.property) {
response.taskProperties = Array.isArray(jsonData.task.property) ?
jsonData.task.property.map((prop: any) => ({
@@
}
resolve(response);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-extension/src/rpc-managers/mi-diagram/rpc-manager.ts` around
lines 1852 - 1854, The parsed task data is being discarded for tasks that have
no properties because the branch in rpc-manager.ts under the task parsing logic
still falls through to the default empty response. Update the task response
handling in the same parsing path so the object built with startOnLoad and other
parsed task fields is returned immediately even when jsonData.task.property is
missing, instead of being replaced by the fallback empty response.

Comment on lines +124 to +142
useEffect(() => {
const loadUsedPorts = async () => {
setPortDiscoveryLoading(true);
setPortDiscoveryError(null);
try {
const projectRootResp = await rpcClient.getMiDiagramRpcClient().getProjectRoot({ path: props.path });
const { ports } = await rpcClient.getMiDiagramRpcClient().getMcpUsedInboundPorts({
projectUri: projectRootResp.path,
});
setUsedPorts(new Set(ports));
setPortDiscoveryLoading(false);
} catch (err) {
console.error('[MCPServerForm] Port discovery error:', err);
setPortDiscoveryError(`Failed to check existing ports: ${err instanceof Error ? err.message : String(err)}`);
setPortDiscoveryLoading(false);
}
};
loadUsedPorts();
}, [rpcClient, props.path]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Editing an existing server can falsely flag its own port as "in use".

getMcpUsedInboundPorts is called without excludePath (lines 130-132), even though the request type supports it specifically for this scenario. When editing an existing MCP server, its own currently-bound port will be included in usedPorts, causing the submit-time check at line 162 to reject the unchanged port as "already in use by another inbound endpoint."

🐛 Proposed fix
                 const { ports } = await rpcClient.getMiDiagramRpcClient().getMcpUsedInboundPorts({
                     projectUri: projectRootResp.path,
+                    excludePath: editData?.inboundEndpointPath,
                 });

Also applies to: 162-165

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/index.tsx` around
lines 124 - 142, The port lookup in MCPServerForm is including the server being
edited, so its own bound port gets marked as used and the submit validation
rejects an unchanged port. Update loadUsedPorts in MCPServerForm to pass the
current server’s path as excludePath when calling getMcpUsedInboundPorts, using
the existing props/path data available in the component so the current endpoint
is omitted from usedPorts during edit flows.

Comment on lines +476 to +532
const onSubmit = async (data: any) => {
setSubmitting(true);
setError(null);
try {
const projectRootResp = await rpcClient.getMiDiagramRpcClient().getProjectRoot({ path: props.path });
const projectDir = projectRootResp.path;
const localEntriesDir = path.join(projectDir, 'src', 'main', 'wso2mi', 'artifacts', 'local-entries').toString();
const inboundEndpointsDir = path.join(projectDir, 'src', 'main', 'wso2mi', 'artifacts', 'inbound-endpoints').toString();

const { xml } = await rpcClient.getMiDiagramRpcClient().buildMcpToolsXml({ projectRoot: projectDir, tools });
const localEntryName = `${data.serverName}${MCP_CONFIG_NAME_SUFFIX}`;

await rpcClient.getMiDiagramRpcClient().createLocalEntry({
directory: localEntriesDir,
name: localEntryName,
type: 'In-Line XML Entry',
value: xml,
URL: '',
getContentOnly: false,
});

await rpcClient.getMiDiagramRpcClient().createInboundEndpoint({
directory: inboundEndpointsDir,
attributes: {
name: `${data.serverName}`,
sequence: '',
onError: '',
class: inboundListenerClass,
},
parameters: {
'inbound.mcp.port': data.port,
'inbound.http.port': data.port,
'inbound.http.context': '/mcp',
'mcp.tools.localentry': localEntryName,
'inbound.behavior': 'listening',
'inbound.cors.allow.origin': corsSettings.corsAllowOrigin,
'inbound.cors.allow.methods': corsSettings.corsAllowMethods,
'inbound.cors.allow.headers': corsSettings.corsAllowHeaders,
'inbound.cors.expose.headers': corsSettings.corsExposeHeaders,
'inbound.sse.keepalive.interval': corsSettings.keepAliveInterval,
},
});

rpcClient.getMiVisualizerRpcClient().showNotification({
message: `MCP Server "${data.serverName}" created with ${tools.length} tool(s)`,
type: 'info',
});
rpcClient.getMiVisualizerRpcClient().openView({
type: EVENT_TYPE.OPEN_VIEW,
location: { view: MACHINE_VIEW.Overview },
});
} catch (err) {
setError(`Failed to create MCP Server: ${err instanceof Error ? err.message : String(err)}`);
} finally {
setSubmitting(false);
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Missing port-uniqueness validation in the create flow.

usedPorts is loaded (Line 247-251) and enforced in handleUpdate for edit mode (Line 444: usedPorts.has(portValue) && portValue !== originalPort), but onSubmit (create mode) never checks it before calling createInboundEndpoint. Users can create a new MCP server on a port that's already bound by another inbound endpoint in the same project, which will likely fail at MI runtime/deployment instead of being caught in the UI.

🐛 Suggested fix
 const onSubmit = async (data: any) => {
     setSubmitting(true);
     setError(null);
     try {
+        const portValue = Number(data.port);
+        if (usedPorts.has(portValue)) {
+            setFieldError('port', { message: `Port ${portValue} is already in use by another inbound endpoint in this project` });
+            setSubmitting(false);
+            return;
+        }
         const projectRootResp = await rpcClient.getMiDiagramRpcClient().getProjectRoot({ path: props.path });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onSubmit = async (data: any) => {
setSubmitting(true);
setError(null);
try {
const projectRootResp = await rpcClient.getMiDiagramRpcClient().getProjectRoot({ path: props.path });
const projectDir = projectRootResp.path;
const localEntriesDir = path.join(projectDir, 'src', 'main', 'wso2mi', 'artifacts', 'local-entries').toString();
const inboundEndpointsDir = path.join(projectDir, 'src', 'main', 'wso2mi', 'artifacts', 'inbound-endpoints').toString();
const { xml } = await rpcClient.getMiDiagramRpcClient().buildMcpToolsXml({ projectRoot: projectDir, tools });
const localEntryName = `${data.serverName}${MCP_CONFIG_NAME_SUFFIX}`;
await rpcClient.getMiDiagramRpcClient().createLocalEntry({
directory: localEntriesDir,
name: localEntryName,
type: 'In-Line XML Entry',
value: xml,
URL: '',
getContentOnly: false,
});
await rpcClient.getMiDiagramRpcClient().createInboundEndpoint({
directory: inboundEndpointsDir,
attributes: {
name: `${data.serverName}`,
sequence: '',
onError: '',
class: inboundListenerClass,
},
parameters: {
'inbound.mcp.port': data.port,
'inbound.http.port': data.port,
'inbound.http.context': '/mcp',
'mcp.tools.localentry': localEntryName,
'inbound.behavior': 'listening',
'inbound.cors.allow.origin': corsSettings.corsAllowOrigin,
'inbound.cors.allow.methods': corsSettings.corsAllowMethods,
'inbound.cors.allow.headers': corsSettings.corsAllowHeaders,
'inbound.cors.expose.headers': corsSettings.corsExposeHeaders,
'inbound.sse.keepalive.interval': corsSettings.keepAliveInterval,
},
});
rpcClient.getMiVisualizerRpcClient().showNotification({
message: `MCP Server "${data.serverName}" created with ${tools.length} tool(s)`,
type: 'info',
});
rpcClient.getMiVisualizerRpcClient().openView({
type: EVENT_TYPE.OPEN_VIEW,
location: { view: MACHINE_VIEW.Overview },
});
} catch (err) {
setError(`Failed to create MCP Server: ${err instanceof Error ? err.message : String(err)}`);
} finally {
setSubmitting(false);
}
};
const onSubmit = async (data: any) => {
setSubmitting(true);
setError(null);
try {
const portValue = Number(data.port);
if (usedPorts.has(portValue)) {
setFieldError('port', { message: `Port ${portValue} is already in use by another inbound endpoint in this project` });
setSubmitting(false);
return;
}
const projectRootResp = await rpcClient.getMiDiagramRpcClient().getProjectRoot({ path: props.path });
const projectDir = projectRootResp.path;
const localEntriesDir = path.join(projectDir, 'src', 'main', 'wso2mi', 'artifacts', 'local-entries').toString();
const inboundEndpointsDir = path.join(projectDir, 'src', 'main', 'wso2mi', 'artifacts', 'inbound-endpoints').toString();
const { xml } = await rpcClient.getMiDiagramRpcClient().buildMcpToolsXml({ projectRoot: projectDir, tools });
const localEntryName = `${data.serverName}${MCP_CONFIG_NAME_SUFFIX}`;
await rpcClient.getMiDiagramRpcClient().createLocalEntry({
directory: localEntriesDir,
name: localEntryName,
type: 'In-Line XML Entry',
value: xml,
URL: '',
getContentOnly: false,
});
await rpcClient.getMiDiagramRpcClient().createInboundEndpoint({
directory: inboundEndpointsDir,
attributes: {
name: `${data.serverName}`,
sequence: '',
onError: '',
class: inboundListenerClass,
},
parameters: {
'inbound.mcp.port': data.port,
'inbound.http.port': data.port,
'inbound.http.context': '/mcp',
'mcp.tools.localentry': localEntryName,
'inbound.behavior': 'listening',
'inbound.cors.allow.origin': corsSettings.corsAllowOrigin,
'inbound.cors.allow.methods': corsSettings.corsAllowMethods,
'inbound.cors.allow.headers': corsSettings.corsAllowHeaders,
'inbound.cors.expose.headers': corsSettings.corsExposeHeaders,
'inbound.sse.keepalive.interval': corsSettings.keepAliveInterval,
},
});
rpcClient.getMiVisualizerRpcClient().showNotification({
message: `MCP Server "${data.serverName}" created with ${tools.length} tool(s)`,
type: 'info',
});
rpcClient.getMiVisualizerRpcClient().openView({
type: EVENT_TYPE.OPEN_VIEW,
location: { view: MACHINE_VIEW.Overview },
});
} catch (err) {
setError(`Failed to create MCP Server: ${err instanceof Error ? err.message : String(err)}`);
} finally {
setSubmitting(false);
}
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/MCPServerToolsForm.tsx`
around lines 476 - 532, The create flow in onSubmit currently skips the same
port-uniqueness check that handleUpdate uses, so a new MCP server can be created
with a port already present in usedPorts. Add a validation step in onSubmit
before buildMcpToolsXml/createInboundEndpoint that checks the submitted port
against usedPorts and blocks submission when it is already taken. Reuse the
existing port parsing/validation pattern from handleUpdate and surface the error
via setError so the user sees the conflict before any RPC calls are made.

Comment on lines +95 to +109
const saveEdit = async () => {
if (!editingTool) return;
if (schemaError) return;

const normalizedSchema = editToolInputSchema.trim()
? (await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: editToolInputSchema })).schema
: null;

const updatedTools = tools.map(t => {
if (t.id !== editingTool.id) return t;
return { ...t, name: editToolName.trim() || t.name, description: editToolDescription, inputSchema: normalizedSchema || t.inputSchema };
});
onSave(updatedTools);
setEditingTool(null);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Clearing the input schema in Edit Tool has no effect.

When a user empties the schema textarea and saves, normalizedSchema resolves to null (Line 99-101), but Line 105's normalizedSchema || t.inputSchema falls back to the tool's old schema instead of clearing it. Since the Save button doesn't require a non-empty schema, users have no way to remove a previously-set schema. Compare with AddSequenceToolDialog.tsx/CreateScratchToolDialog.tsx, which use converted || EMPTY_MCP_SCHEMA for the equivalent empty case.

🐛 Suggested fix
+import { EMPTY_MCP_SCHEMA, INVALID_MCP_SCHEMA_MESSAGE } from '../../../constants';
...
         const updatedTools = tools.map(t => {
             if (t.id !== editingTool.id) return t;
-            return { ...t, name: editToolName.trim() || t.name, description: editToolDescription, inputSchema: normalizedSchema || t.inputSchema };
+            return { ...t, name: editToolName.trim() || t.name, description: editToolDescription, inputSchema: editToolInputSchema.trim() ? (normalizedSchema ?? t.inputSchema) : EMPTY_MCP_SCHEMA };
         });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const saveEdit = async () => {
if (!editingTool) return;
if (schemaError) return;
const normalizedSchema = editToolInputSchema.trim()
? (await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: editToolInputSchema })).schema
: null;
const updatedTools = tools.map(t => {
if (t.id !== editingTool.id) return t;
return { ...t, name: editToolName.trim() || t.name, description: editToolDescription, inputSchema: normalizedSchema || t.inputSchema };
});
onSave(updatedTools);
setEditingTool(null);
};
const saveEdit = async () => {
if (!editingTool) return;
if (schemaError) return;
const normalizedSchema = editToolInputSchema.trim()
? (await rpcClient.getMiDiagramRpcClient().convertMcpJsonSchema({ input: editToolInputSchema })).schema
: null;
const updatedTools = tools.map(t => {
if (t.id !== editingTool.id) return t;
return { ...t, name: editToolName.trim() || t.name, description: editToolDescription, inputSchema: editToolInputSchema.trim() ? (normalizedSchema ?? t.inputSchema) : EMPTY_MCP_SCHEMA };
});
onSave(updatedTools);
setEditingTool(null);
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/MCPServerForm/ToolsList.tsx`
around lines 95 - 109, The edit flow in saveEdit currently falls back to the
existing tool schema, so clearing the schema textarea never removes it. Update
the updatedTools mapping for the editingTool case so an empty schema is
preserved as an explicit cleared value instead of using normalizedSchema ||
t.inputSchema. Use the same empty-schema handling pattern as
AddSequenceToolDialog and CreateScratchToolDialog, and keep the logic tied to
saveEdit, editingTool, and normalizedSchema.

Comment on lines +267 to +269
const projectDetails = await rpcClient.getMiVisualizerRpcClient().getProjectDetails();
const runtimeVersion = projectDetails.primaryDetails.runtimeVersion.value;
setIsStartOnLoadSupported(compareVersions(runtimeVersion, RUNTIME_VERSION_410) === 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm compareVersions semantics (returns -1/0/1?) and check how it's used elsewhere for version gating.
rg -n -B2 -A2 'compareVersions\(' workspaces/mi --type=ts

# Locate the implementation to confirm return value semantics.
fd -a commons.ts workspaces/mi/mi-diagram/src/utils

# Confirm RUNTIME_VERSION_410 definition/usage pattern.
rg -n 'RUNTIME_VERSION_410' workspaces/mi --type=ts

Repository: wso2/vscode-extensions

Length of output: 45184


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the compareVersions implementation in both relevant places.
sed -n '92,150p' workspaces/mi/mi-diagram/src/utils/commons.ts
printf '\n----\n'
sed -n '1007,1065p' workspaces/mi/mi-extension/src/util/onboardingUtils.ts

printf '\n==== TaskForm surrounding logic ====\n'
sed -n '240,340p' workspaces/mi/mi-visualizer/src/views/Forms/TaskForm.tsx

printf '\n==== Search for startOnLoad usage ====\n'
rg -n 'startOnLoad|isStartOnLoadSupported|RUNTIME_VERSION_410' workspaces/mi/mi-visualizer/src/views/Forms/TaskForm.tsx workspaces/mi/mi-visualizer/src -g '!**/node_modules/**'

Repository: wso2/vscode-extensions

Length of output: 12453


Use a minimum-version gate for startOnLoad

compareVersions(runtimeVersion, RUNTIME_VERSION_410) === 0 only enables this for exactly 4.1.0; 4.1.1+ will still hide the checkbox and drop startOnLoad from the task request.

Suggested fix
-            setIsStartOnLoadSupported(compareVersions(runtimeVersion, RUNTIME_VERSION_410) === 0);
+            setIsStartOnLoadSupported(compareVersions(runtimeVersion, RUNTIME_VERSION_410) >= 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const projectDetails = await rpcClient.getMiVisualizerRpcClient().getProjectDetails();
const runtimeVersion = projectDetails.primaryDetails.runtimeVersion.value;
setIsStartOnLoadSupported(compareVersions(runtimeVersion, RUNTIME_VERSION_410) === 0);
const projectDetails = await rpcClient.getMiVisualizerRpcClient().getProjectDetails();
const runtimeVersion = projectDetails.primaryDetails.runtimeVersion.value;
setIsStartOnLoadSupported(compareVersions(runtimeVersion, RUNTIME_VERSION_410) >= 0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Forms/TaskForm.tsx` around lines 267 -
269, The `startOnLoad` gating in `TaskForm` is using an exact version match, so
versions newer than 4.1.0 are incorrectly treated as unsupported. Update the
version check around `getProjectDetails`, `runtimeVersion`, and
`setIsStartOnLoadSupported` to use a minimum-version comparison so `4.1.0` and
any later runtime versions still enable the checkbox and preserve `startOnLoad`
in the task request.

Comment on lines +310 to +325
const deleteMCPServer = (entry: any) => {
const inboundPath = entry.inboundEndpoint?.path;
const localEntryPath = entry.localEntry?.path;

const confirmed = window.confirm(`Are you sure you want to delete MCP Server "${entry.name}"? This action cannot be undone.`);
if (!confirmed) {
return;
}

if (inboundPath) {
rpcClient.getMiDiagramRpcClient().deleteArtifact({ path: inboundPath, enableUndo: false });
}
if (localEntryPath) {
rpcClient.getMiDiagramRpcClient().deleteArtifact({ path: localEntryPath, enableUndo: false });
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether window.confirm/alert is used elsewhere in the mi-visualizer/mi-extension webview code,
# and whether any webview panel option enables modals.
rg -nP '\bwindow\.(confirm|alert)\s*\(' workspaces/mi --type=ts --type=tsx
rg -nP 'allow-modals|retainContextWhenHidden|WebviewPanelOptions' workspaces/mi/mi-extension/src

Repository: wso2/vscode-extensions

Length of output: 610


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant webview panel setup =="
sed -n '1,140p' workspaces/mi/mi-extension/src/visualizer/webview.ts
printf '\n== project structure view around delete flow ==\n'
sed -n '250,340p' workspaces/mi/mi-visualizer/src/views/Overview/ProjectStructureView.tsx
printf '\n== search for dialog primitives in mi-visualizer ==\n'
rg -n "AddAPIToolDialog|Dialog|Alert|confirm\\(" workspaces/mi/mi-visualizer/src workspaces/mi/mi-extension/src

Repository: wso2/vscode-extensions

Length of output: 45794


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== webview html generation snippets =="
rg -n "sandbox|allow-modals|webview.*html|iframe" workspaces/mi/mi-extension/src/visualizer/webview.ts workspaces/mi/mi-extension/src/runtime-services-panel/webview.ts workspaces/mi/mi-extension/src/ai-features/webview.ts workspaces/mi/mi-extension/src/swagger/webview.ts

Repository: wso2/vscode-extensions

Length of output: 753


window.confirm() won’t work here

deleteMCPServer relies on a modal confirm, but this webview is created without modal support. The delete action will return early every time, so MCP servers can’t be deleted from this view. Use the existing Dialog pattern instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workspaces/mi/mi-visualizer/src/views/Overview/ProjectStructureView.tsx`
around lines 310 - 325, The delete flow in deleteMCPServer currently uses
window.confirm, which does not work in this webview and blocks deletion. Replace
it with the existing Dialog-based confirmation pattern used in
ProjectStructureView so the user can confirm deletion without relying on browser
modal support. Update the deleteMCPServer entry point and its surrounding
state/handlers to open the Dialog, and only call
rpcClient.getMiDiagramRpcClient().deleteArtifact after the dialog confirms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants