Skip to content

Commit 6ef84ec

Browse files
authored
Merge pull request #2690 from vitaliyboykocontributor/mcp-stuff
MCP enhancements
2 parents 226ad80 + 9ae2bbc commit 6ef84ec

27 files changed

Lines changed: 924 additions & 282 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0).
3131
`find_acl_or_menu`
3232
- Magento MCP CLI environment discovery for AI agents. The `describe_magento_cli_environment` tool exposes project-local wrapper commands, including Mark Shust Docker-style `bin/*` scripts such as `bin/magento` and `bin/n98-magerun2`, so agents can prefer those wrappers over global binaries.
3333

34+
### Fixed
35+
36+
- Illegal char <|> [#2669](https://github.com/magento/magento2-phpstorm-plugin/issues/2669)
37+
3438
## 2026.1.1
3539

3640
### Added

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</p>
66

77
<!-- Plugin description -->
8-
# Magento 2 and Adobe Commerce Support
8+
# Magento 2 and Adobe Commerce
99

1010
<table align="center" style="border-collapse: collapse; width: 100%; text-align: center;">
1111
<caption style="font-size: 1.2em; margin-bottom: 10px;">
@@ -66,7 +66,7 @@
6666

6767
1. Go to `Settings > Preferences` in the PhpStorm IDE
6868
2. Navigate to `Plugins`
69-
3. Click the `Browse repositories...` button and search for "Magento 2 and Adobe Commerce Support"
69+
3. Click the `Browse repositories...` button and search for "Magento 2 and Adobe Commerce"
7070
4. Install the plugin and restart PhpStorm
7171
5. Go to `Settings > Preferences > Languages & Frameworks > PHP > Frameworks > Magento` in the PhpStorm IDE
7272
6. Check `Enable` and click the `OK` button
@@ -103,17 +103,18 @@ Available query and inspection tools:
103103
* `find_layout_entities`: finds layout handles, block names, and container names by exact or partial value such as `catalog_product_view`, `product.info.main`, or `checkout`.
104104
* `find_ui_component`: finds UI component XML definitions by exact or partial component file name such as `product_form`, `sales_order_grid`, or `category_form` without the `.xml` extension.
105105
* `find_acl_or_menu`: finds ACL resource IDs and admin menu entries by exact or partial identifier such as `Magento_Catalog::catalog` or `Foo_Bar::manage_items`.
106-
* `describe_magento_cli_environment`: detects project-local CLI wrappers such as Mark Shust Docker scripts under `bin/`, returns the exact command paths an agent should use, and includes example invocations for Magento CLI, PHP, Composer, and `n98-magerun`.
106+
* `describe_magento_cli_environment`: detects project-local CLI wrappers such as Mark Shust Docker scripts under project-level or Magento-root `bin/`, returns the exact command paths an agent should use, and includes example invocations for Magento CLI, PHP, Composer, `n98-magerun`, and stack lifecycle commands such as `./bin/start`.
107107

108108
Notes:
109109

110110
* The IDE MCP server must be enabled in the JetBrains IDE.
111+
* Only one JetBrains IDE instance should have the MCP server enabled at a time so the configured MCP port stays free; if another IDE is already using that port, the MCP server will not start correctly.
111112
* The IDE MCP server entry must be added to the agent MCP configuration.
112113
* MCP tools work against the currently opened IDE project.
113114
* Magento plugin support must be enabled for the project.
114115
* Indexing must be finished before MCP queries and generators can run.
115116
* Category EAV attribute generation creates both the data patch and `view/adminhtml/ui_component/category_form.xml`.
116-
* `describe_magento_cli_environment` detects project-local wrappers such as Mark Shust Docker scripts under `bin/` and returns the exact command paths an agent should use.
117+
* `describe_magento_cli_environment` detects project-local wrappers such as Mark Shust Docker scripts under project-level or Magento-root `bin/` and returns the exact command paths an agent should use, including stack lifecycle wrappers such as `bin/start`, `bin/stop`, and `bin/restart`.
117118

118119
### MCP CLI wrapper configuration
119120

@@ -123,13 +124,15 @@ If your Magento project uses local wrapper scripts such as Mark Shust Docker com
123124

124125
Use a comma-separated list of relative paths, for example:
125126

126-
`bin/magento, bin/n98-magerun2, bin/cli`
127+
`bin/magento, bin/n98-magerun2, bin/cli, bin/start`
127128

128129
Agent usage pattern:
129130

130131
1. Call `describe_magento_cli_environment`.
131-
2. Use the returned wrapper path exactly, for example `./bin/magento cache:flush` or `./bin/n98-magerun2 sys:info`.
132-
3. Prefer the wrapper over global binaries because these scripts often enter Docker containers or a project-specific runtime.
132+
2. Use the returned wrapper path exactly, for example `./bin/magento cache:flush`, `./bin/n98-magerun2 sys:info`, or `./bin/start`.
133+
3. Keep Magento code edits and generators under the configured Magento root.
134+
4. If the tool reports a wrapper outside that root, still run it from the returned project-relative path; that is valid for nested Magento roots.
135+
5. Prefer the wrapper over global binaries because these scripts often enter Docker containers or a project-specific runtime.
133136

134137
## Setting up development environment
135138

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
pluginGroup = com.magento.idea.magento2plugin
2-
pluginName = Magento 2 and Adobe Commerce Support
2+
pluginName = Magento 2 and Adobe Commerce
33
pluginRepositoryUrl = https://github.com/magento/magento2-phpstorm-plugin
44
pluginVersion = 2026.2.0
55
pluginSinceBuild = 261.21525.38

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* See COPYING.txt for license details.
44
*/
55

6-
rootProject.name = "Magento 2 and Adobe Commerce Support"
6+
rootProject.name = "Magento 2 and Adobe Commerce"
77

88
plugins {
99
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"

src/main/java/com/magento/idea/magento2plugin/init/ConfigurationManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ private static void showPopup(Project project, String message, Function<Notifica
239239
Runnable runnable = () -> {
240240
notifyGlobally(
241241
project,
242-
"Magento 2 and Adobe Commerce Support",
242+
"Magento 2 and Adobe Commerce",
243243
message,
244244
NotificationType.INFORMATION,
245245
actions
@@ -250,7 +250,7 @@ private static void showPopup(Project project, String message, Function<Notifica
250250

251251
public static void notifyGlobally(@Nullable Project project, String title, String message, NotificationType notificationType, Function<Notification, AnAction>... actions) {
252252
Notification notification = new Notification(
253-
"Magento 2 and Adobe Commerce Support",
253+
"Magento 2 and Adobe Commerce",
254254
title,
255255
message,
256256
notificationType

src/main/java/com/magento/idea/magento2plugin/reference/provider/PhpClassReferenceProvider.java

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.magento.idea.magento2plugin.util.RegExUtil;
2121
import java.util.ArrayList;
2222
import java.util.Collection;
23+
import java.util.Collections;
2324
import java.util.List;
2425
import java.util.Locale;
2526
import java.util.regex.Matcher;
@@ -51,33 +52,33 @@ public class PhpClassReferenceProvider extends PsiReferenceProvider {
5152
String namespacePart;
5253
final List<PsiReference> psiReferences = new ArrayList<>();
5354

54-
for (int i = 0; i < fqnParts.length - 1; i++) {
55-
namespacePart = fqnParts[i];
56-
namespace.append('\\');
57-
namespace.append(namespacePart);
58-
59-
final String namespaceId = namespace
60-
.toString()
61-
.toLowerCase(Locale.ROOT);
62-
63-
final Collection<PhpNamespace> references = hasNamespaceInIndex(
64-
namespaceId,
65-
element.getProject()
66-
) ? phpIndex.getNamespacesByName(namespaceId) : new ArrayList<>();
67-
68-
if (!references.isEmpty()) {
69-
final TextRange range = new TextRange(
70-
origValue.indexOf(classFQN) + namespace.toString().lastIndexOf(92),
71-
origValue.indexOf(classFQN) + namespace.toString().lastIndexOf(92)
72-
+ namespacePart.length()
73-
);
74-
psiReferences.add(new PolyVariantReferenceBase(element, range, references));
55+
try {
56+
for (int i = 0; i < fqnParts.length - 1; i++) {
57+
namespacePart = fqnParts[i];
58+
namespace.append('\\');
59+
namespace.append(namespacePart);
60+
61+
final String namespaceId = namespace
62+
.toString()
63+
.toLowerCase(Locale.ROOT);
64+
65+
final Collection<PhpNamespace> references = hasNamespaceInIndex(
66+
namespaceId,
67+
element.getProject()
68+
) ? getNamespacesByName(phpIndex, namespaceId) : Collections.emptyList();
69+
70+
if (!references.isEmpty()) {
71+
final TextRange range = new TextRange(
72+
origValue.indexOf(classFQN) + namespace.toString().lastIndexOf(92),
73+
origValue.indexOf(classFQN) + namespace.toString().lastIndexOf(92)
74+
+ namespacePart.length()
75+
);
76+
psiReferences.add(new PolyVariantReferenceBase(element, range, references));
77+
}
7578
}
76-
}
77-
final String className = classFQN.substring(classFQN.lastIndexOf(92) + 1);
7879

79-
try {
80-
final Collection<PhpClass> classes = phpIndex.getAnyByFQN(classFQN);
80+
final String className = classFQN.substring(classFQN.lastIndexOf(92) + 1);
81+
final Collection<PhpClass> classes = getClassesByFqn(phpIndex, classFQN);
8182

8283
if (!classes.isEmpty()) {
8384
final TextRange range = new TextRange(
@@ -93,6 +94,20 @@ public class PhpClassReferenceProvider extends PsiReferenceProvider {
9394
return psiReferences.toArray(new PsiReference[0]);
9495
}
9596

97+
protected Collection<PhpNamespace> getNamespacesByName(
98+
final @NotNull PhpIndex phpIndex,
99+
final @NotNull String namespaceId
100+
) {
101+
return phpIndex.getNamespacesByName(namespaceId);
102+
}
103+
104+
protected Collection<PhpClass> getClassesByFqn(
105+
final @NotNull PhpIndex phpIndex,
106+
final @NotNull String classFqn
107+
) {
108+
return phpIndex.getAnyByFQN(classFqn);
109+
}
110+
96111
/**
97112
* Check if php namespace index has specified identifier.
98113
*
@@ -101,7 +116,7 @@ public class PhpClassReferenceProvider extends PsiReferenceProvider {
101116
*
102117
* @return boolean
103118
*/
104-
private boolean hasNamespaceInIndex(
119+
protected boolean hasNamespaceInIndex(
105120
final @NotNull String namespaceIdentifier,
106121
final @NotNull Project project
107122
) {

src/main/kotlin/com/magento/idea/magento2plugin/mcp/MagentoBlockCommands.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal object MagentoBlockCommands {
2222
resolveRequest(project, moduleName, blockClassFqn)
2323
}
2424
} catch (_: ReadAction.CannotReadException) {
25-
return "The request was cancelled by a pending write action. Retry."
25+
return MagentoMcpReadActionSupport.cancellationMessage()
2626
} catch (exception: BlockValidationException) {
2727
return exception.message ?: "Block creation request is invalid."
2828
}

src/main/kotlin/com/magento/idea/magento2plugin/mcp/MagentoCliCommandCommands.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal object MagentoCliCommandCommands {
4545
)
4646
}
4747
} catch (_: ReadAction.CannotReadException) {
48-
return "The request was cancelled by a pending write action. Retry."
48+
return MagentoMcpReadActionSupport.cancellationMessage()
4949
} catch (exception: CliCommandValidationException) {
5050
return exception.message ?: "CLI command creation request is invalid."
5151
}

0 commit comments

Comments
 (0)