diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/category.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/category.xml index 77c9e2c63ef..f087f9f0c99 100644 --- a/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/category.xml +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/category.xml @@ -27,9 +27,12 @@ + + + Provides support for developing projects that help build, test and deploy Java applications for the Microsoft Azure cloud in Eclipse. - + \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/pom.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/pom.xml index 4765d6cdabb..5e43944d48c 100644 --- a/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/pom.xml +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/pom.xml @@ -13,7 +13,7 @@ eclipse-repository UTF-8 - 2.5.0 + 4.0.7 diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/site.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/site.xml index a9d52d59153..a97051379db 100644 --- a/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/site.xml +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/WindowsAzurePlugin4EJ/site.xml @@ -31,9 +31,12 @@ + + + Provides support for developing projects that help build, test and deploy Java applications for the Microsoft Azure cloud in Eclipse and via command line. - + \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/build.properties b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/build.properties new file mode 100644 index 00000000000..64f93a9f0b7 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/feature.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/feature.xml new file mode 100644 index 00000000000..43e22b36534 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/feature.xml @@ -0,0 +1,104 @@ + + + + + Auto configures Azure MCP Server in Eclipse for GitHub Copilot Plugin. + + + + Copyright (c) Microsoft Corp. + + + +MICROSOFT SOFTWARE LICENSE TERMS +Azure Toolkit for Eclipse + +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. + +1. INSTALLATION AND USE RIGHTS. +a. General. This software is comprised of various modules. Unless otherwise specified in these license terms or a module is accompanied by other Microsoft software license terms, these license terms apply to the software. +b. Installation and Use. You may install and use any number of copies of the software to develop and test your applications. +c. Demo use. The uses permitted above include use of the software in demonstrating your applications. +d. Backup copy. You may make one or more backup copies of the software, for reinstalling the software. +e. Third Party Components. The software may include third party components with separate legal notices or governed by other agreements, as may be described in the ThirdPartyNotices file accompanying the software. +f. Additional Programs. Enabling certain functionality in your applications for Microsoft Azure will cause this software to automatically download additional software to your Microsoft Azure cloud account, as follows: Microsoft Web Platform Installer, Microsoft Application Request Routing for Windows Operating Systems, Microsoft Web Farm Framework for Windows Operating Systems, and Microsoft External Cache. The license terms with those programs apply to your use of them. + +2. DATA. + a. Data Collection. The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may opt-out of many of these scenarios, but not all, as described in the product documentation. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft¡¯s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. + b. Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal data in connection with the software, Microsoft makes the commitments in the European Union General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 25, 2018, at http://go.microsoft.com/?linkid=9840733. + +3. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. +a. Distribution. You are permitted to distribute the software in applications you develop only if you comply with the terms below. +i. Right to Use and Distribute. + * Distribution. You may copy and distribute the object code form of the software. + * Third Party Distribution. You may permit distributors of your applications to copy and distribute the software as part of those applications. +ii. Distribution Requirements. For the software you distribute, you must + * add significant primary functionality to it in your applications; + * require distributors and external end users to agree to terms that protect it at least as much as this agreement; + * display your valid copyright notice on your applications; and + * indemnify, defend, and hold harmless Microsoft from any claims, including attorneys¡¯ fees, related to the distribution or use of your applications. +iii. Distribution Restrictions. You may not + * alter any copyright, trademark or patent notice in the software; + * use Microsoft¡¯s trademarks in your applications¡¯ names or in a way that suggests your applications come from or are endorsed by Microsoft; + * include software in malicious, deceptive or unlawful applications; or + * access, modify or distribute the source code of any software so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that + * the code be disclosed or distributed in source code form; or + * others have the right to modify it. + +4. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not + * work around any technical limitations in the software; + * reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source code for the software, except and to the extent required by third party license terms governing use of certain open source components that may be included in the software; + * remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; + * use the software in any way that is against the law; or + * share, publish, or lend the software (except for any distributable code, subject to the applicable terms above), provide the software as a stand-alone offering for others to use, or transfer the software or this agreement to any third party. + +5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations that apply to the software, which include restrictions on destinations, end users, and end use. For further information on export restrictions, visit (aka.ms/exporting). + +6. SUPPORT SERVICES. Because this software is ¡°as is,¡± we will not provide support services for it. + +7. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. + +8. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. + +9. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state or country. Separate and apart from your relationship with Microsoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state or country do not permit it to do so. For example, if you acquired the software in one of the below regions, or mandatory country law applies, then the following provisions apply to you: +a. Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. +b. Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. +c. Germany and Austria. + (i) Warranty. The properly licensed software will perform substantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. + (ii) Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law. Subject to the foregoing clause (ii), Microsoft will only be liable for slight negligence if Microsoft is in breach of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. + +10. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED ¡°AS IS.¡± YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. + +11. LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. This limitation applies to +(a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and +(b) claims for breach of contract, warranty, guarantee, or condition; strict liability, negligence, or other tort; or any other claim; in each case to the extent permitted by applicable law. It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential, or other damages. + +Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. + +Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français. + +EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues. + +LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. + +Cette limitation concerne: +·     tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et +·     les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur. + +Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard. + +EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas. + + + + + diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/pom.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/pom.xml new file mode 100644 index 00000000000..38792c0eb4b --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp.feature/pom.xml @@ -0,0 +1,13 @@ + + + 4.0.0 + + ../pom.xml + com.microsoft.azuretools.wap4ej.build + parent + ${azuretool.eclipse.version} + + com.microsoft.azuretools.azuremcp.feature + eclipse-feature + diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/META-INF/MANIFEST.MF b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..3a985fe22d6 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/META-INF/MANIFEST.MF @@ -0,0 +1,24 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Azure MCP Server for Eclipse +Bundle-SymbolicName: com.microsoft.azuretools.azuremcp;singleton:=true +Bundle-Version: 3.32.0.qualifier +Bundle-Activator: com.microsoft.azuretools.azuremcp.Activator +Bundle-Vendor: Microsoft Corp. +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + com.microsoft.azuretools.sdk;bundle-version="3.32.0", + org.eclipse.ui.forms, + org.eclipse.ui.editors, + org.eclipse.text, + com.microsoft.azuretools.core;bundle-version="3.32.0", + com.microsoft.azuretools.container;bundle-version="3.32.0", + com.microsoft.copilot.eclipse.ui;resolution:=optional +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy +Bundle-ClassPath: . +Import-Package: com.microsoft.tooling.msservices.serviceexplorer.azure, + org.apache.commons.lang3, + org.eclipse.core.resources, + org.eclipse.jface.text.revisions +Export-Package: com.microsoft.azuretools.azuremcp diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/build.properties b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/build.properties new file mode 100644 index 00000000000..2b0d95b6b02 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + . diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/plugin.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/plugin.xml new file mode 100644 index 00000000000..52b55dd489a --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/plugin.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/pom.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/pom.xml new file mode 100644 index 00000000000..914d8fec483 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + com.microsoft.azuretools.wap4ej.build + parent + ${azuretool.eclipse.version} + + + com.microsoft.azuretools.azuremcp + eclipse-plugin + + com.microsoft.azuretools.azuremcp + http://maven.apache.org + + + UTF-8 + + + + + junit + junit + 3.8.1 + test + + + + + \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/Activator.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/Activator.java new file mode 100644 index 00000000000..18a3e5ec631 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/Activator.java @@ -0,0 +1,7 @@ +package com.microsoft.azuretools.azuremcp; + +import org.eclipse.ui.plugin.AbstractUIPlugin; + +public class Activator extends AbstractUIPlugin { + +} diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/AzureMcpPackageManager.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/AzureMcpPackageManager.java new file mode 100644 index 00000000000..cced423bf49 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/AzureMcpPackageManager.java @@ -0,0 +1,212 @@ +package com.microsoft.azuretools.azuremcp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Optional; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.annotation.Nullable; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; +import org.eclipse.core.runtime.Platform; +import org.eclipse.osgi.service.datalocation.Location; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AzureMcpPackageManager { + + private static final Logger log = LoggerFactory.getLogger(AzureMcpPackageManager.class); + private final GithubClient gitHubClient; + private final String platform; + + public AzureMcpPackageManager() { + this.gitHubClient = new GithubClient(); + this.platform = getPlatformIdentifier(); + } + + @Nullable + public synchronized File getAzureMcpExecutable() { + try { + final GithubRelease latestRelease = gitHubClient.getLatestAzureMcpRelease(); + if (latestRelease != null && latestRelease.getAssets() != null) { + final String tagName = latestRelease.getTagName(); + log.info("Latest version of Azure MCP: " + tagName); + + final Optional githubAsset = latestRelease.getAssets() + .stream() + .filter(asset -> asset.getName().startsWith("Azure.Mcp.Server-" + platform)) + .findFirst(); + + if (githubAsset.isPresent()) { + final GithubAsset asset = githubAsset.get(); + log.info("Azure MCP package for current platform: " + asset.getName()); + final long startTime = System.currentTimeMillis(); + + final File azMcpDirFile = getPluginDirectory(); + if (azMcpDirFile.exists() || azMcpDirFile.mkdirs()) { + final Path versionFile = Path.of(azMcpDirFile.getAbsolutePath() + "/version.txt"); + + final File extractedDir = new File(azMcpDirFile, "/azmcp_package_" + tagName); + extractedDir.mkdirs(); + final String executablePath = extractedDir.getAbsolutePath() + getExecutableRelativePath(); + final File azMcpExe = new File(executablePath); + if (!azMcpExe.exists()) { + Files.writeString(versionFile, tagName, StandardOpenOption.CREATE, StandardOpenOption.WRITE); + final File azMcpZip = new File(azMcpDirFile.getAbsolutePath(), "azmcp_" + tagName + ".zip"); + log.info("Downloading Azure MCP Server to: " + azMcpZip.getAbsolutePath()); + final boolean downloaded = gitHubClient.downloadToFile(asset.getBrowserDownloadUrl(), azMcpZip); + if (downloaded && digestMatches(azMcpZip, asset.getDigest())) { + log.info("Downloaded Azure MCP Server successfully in " + (System.currentTimeMillis() - startTime) + " ms"); + log.info("Extracting Azure MCP Server to: " + extractedDir.getAbsolutePath()); + extractZip(azMcpZip, extractedDir); + log.info("Azure MCP Server extracted successfully to: " + extractedDir.getAbsolutePath()); + } + } + + + boolean exists = azMcpExe.exists(); + boolean canExecute = azMcpExe.canExecute(); + + + if (azMcpExe.exists() && (azMcpExe.canExecute() || azMcpExe.setExecutable(true))) { + log.info("Azure MCP Server executable found at: " + azMcpExe.getAbsolutePath()); + return azMcpExe; + } + } + } + } + } catch (final IOException e) { + log.error("Error getting Azure MCP executable: " + e.getMessage()); + } + return null; + } + + private File getPluginDirectory() { + + Location configArea = Platform.getConfigurationLocation(); + URL configURL = configArea.getURL(); + File configDir = new File(configURL.getPath(), "com.microsoft.azuretools.azuremcp"); + configDir.mkdirs(); + File azMcpDirectory = new File(configDir, "azmcp"); + return azMcpDirectory; + } + + private boolean digestMatches(File azMcpZip, String expectedDigest) { + try { + // GitHub releases API computes the SHA-256 digest of the file contents. + // https://github.blog/changelog/2025-06-03-releases-now-expose-digests-for-release-assets/ + final String downloadFileDigest = DigestUtils.sha256Hex(new FileInputStream(azMcpZip)); + return StringUtils.equalsIgnoreCase("sha256:" + downloadFileDigest, expectedDigest); + } catch (final Exception e) { + log.error("Failed to calculate file digest", e); + return false; + } + } + + public synchronized void cleanup() { + try { + + final File azMcpDirFile = getPluginDirectory(); + if (!azMcpDirFile.exists()) { + return; + } + + final Path versionFile = Path.of(azMcpDirFile.getAbsolutePath() + "/version.txt"); + String currentVersion = null; + if (versionFile.toFile().exists()) { + currentVersion = new String(Files.readAllBytes(versionFile)); + } + + final Path currentPackage = Path.of(azMcpDirFile.getAbsolutePath() + "/azmcp_package_" + currentVersion).toAbsolutePath(); + Files.list(Path.of(azMcpDirFile.getAbsolutePath())) + .filter(path -> !path.equals(currentPackage)) + .filter(path -> !path.equals(versionFile)) + .forEach(path -> { + delete(path); + }); + + } catch (final Exception exception) { + System.err.println("Error cleaning up Azure MCP Server: " + exception.getMessage()); + } + } + + private static void delete(Path path) { + try { + if (path.toFile().isDirectory()) { + Files.list(path).forEach(AzureMcpPackageManager::delete); + } + Files.delete(path); + } catch (final IOException e) { + System.err.println("Error deleting file: " + path.toString()); + } + } + + private String getExecutableRelativePath() { + String executablePath = "/azmcp"; + if (SystemUtils.IS_OS_WINDOWS) { + executablePath += ".exe"; + } + return executablePath; + } + + private void extractZip(File zipFile, File destDir) throws IOException { + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + File outputFile = new File(destDir, entry.getName()); + if (entry.isDirectory()) { + if (!outputFile.exists()) { + outputFile.mkdirs(); + } + } else { + outputFile.getParentFile().mkdirs(); + try (FileOutputStream fos = new FileOutputStream(outputFile)) { + byte[] buffer = new byte[4096]; + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + zis.closeEntry(); + } + } + } + + private static String getPlatformIdentifier() { + // Operating System detection + String os = null; + if (SystemUtils.IS_OS_WINDOWS) { + os = "win"; + } else if (SystemUtils.IS_OS_LINUX) { + os = "linux"; + } else if (SystemUtils.IS_OS_MAC) { + os = "osx"; + } else { + throw new RuntimeException("Unsupported OS " + SystemUtils.OS_NAME); + } + final String arch = getArch(); + return os + "-" + arch; + } + + private static String getArch() { + final String arch = SystemUtils.OS_ARCH.toLowerCase(); + if (arch.contains("amd64") || arch.contains("x86_64") || arch.contains("x64")) { + return "x64"; + } + if (arch.contains("aarch64") || arch.contains("arm64")) { + return "arm64"; + } + throw new RuntimeException("Unsupported architecture: " + arch); + } + +} \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubAsset.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubAsset.java new file mode 100644 index 00000000000..83f8a8cdb7c --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubAsset.java @@ -0,0 +1,120 @@ +package com.microsoft.azuretools.azuremcp; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class GithubAsset { + + @JsonProperty("url") + private String url; + @JsonProperty("browser_download_url") + private String browserDownloadUrl; + @JsonProperty("id") + private long id; + @JsonProperty("node_id") + private String nodeId; + @JsonProperty("name") + private String name; + @JsonProperty("label") + private String label; + @JsonProperty("state") + private String state; + @JsonProperty("content_type") + private String contentType; + @JsonProperty("size") + private long size; + @JsonProperty("digest") + private String digest; + @JsonProperty("download_count") + private int downloadCount; + @JsonProperty("created_at") + private String createdAt; + @JsonProperty("updated_at") + private String updatedAt; + @JsonProperty("uploader") + private GithubUser uploader; + + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public String getBrowserDownloadUrl() { + return browserDownloadUrl; + } + public void setBrowserDownloadUrl(String browserDownloadUrl) { + this.browserDownloadUrl = browserDownloadUrl; + } + public long getId() { + return id; + } + public void setId(long id) { + this.id = id; + } + public String getNodeId() { + return nodeId; + } + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getLabel() { + return label; + } + public void setLabel(String label) { + this.label = label; + } + public String getState() { + return state; + } + public void setState(String state) { + this.state = state; + } + public String getContentType() { + return contentType; + } + public void setContentType(String contentType) { + this.contentType = contentType; + } + public long getSize() { + return size; + } + public void setSize(long size) { + this.size = size; + } + public String getDigest() { + return digest; + } + public void setDigest(String digest) { + this.digest = digest; + } + public int getDownloadCount() { + return downloadCount; + } + public void setDownloadCount(int downloadCount) { + this.downloadCount = downloadCount; + } + public String getCreatedAt() { + return createdAt; + } + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + public String getUpdatedAt() { + return updatedAt; + } + public void setUpdatedAt(String updatedAt) { + this.updatedAt = updatedAt; + } + public GithubUser getUploader() { + return uploader; + } + public void setUploader(GithubUser uploader) { + this.uploader = uploader; + } +} \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubClient.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubClient.java new file mode 100644 index 00000000000..210306255cb --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubClient.java @@ -0,0 +1,65 @@ +package com.microsoft.azuretools.azuremcp; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +public class GithubClient implements Closeable { + + private static final Logger log = LoggerFactory.getLogger(GithubClient.class); + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() + .configure(JsonParser.Feature.ALLOW_COMMENTS, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .enable(SerializationFeature.INDENT_OUTPUT); + private static final String AZURE_MCP_RELEASE_URL = "https://api.github.com/repos/microsoft/mcp/releases"; + private static final TypeReference> GITHUB_RELEASE_LIST_TYPE = new TypeReference>() { + }; + + private final CloseableHttpClient httpClient = HttpClients.createDefault(); + + public GithubRelease getLatestAzureMcpRelease() { + final HttpUriRequest request = RequestBuilder.get().setUri(AZURE_MCP_RELEASE_URL).build(); + try (final CloseableHttpResponse response = httpClient.execute(request)) { + final List releases = OBJECT_MAPPER.readValue(response.getEntity().getContent(), GITHUB_RELEASE_LIST_TYPE); + return releases.stream().findFirst().orElse(null); + } catch (final IOException exception) { + log.error("Error getting latest Azure MCP release details: " + exception.getMessage()); + return null; + } + } + + public boolean downloadToFile(String downloadUrl, File downloadFile) { + final HttpUriRequest downloadRequest = RequestBuilder.get().setUri(downloadUrl).build(); + try (final CloseableHttpResponse downloadResponse = httpClient.execute(downloadRequest); + final FileOutputStream fos = new FileOutputStream(downloadFile)) { + downloadResponse.getEntity().getContent().transferTo(fos); + return true; + } catch (final IOException exception) { + log.error("Error downloading Azure MCP: " + exception.getMessage()); + return false; + } + } + + @Override + public void close() throws IOException { + this.httpClient.close(); + } +} \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubCopilotAzureMcpInitializer.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubCopilotAzureMcpInitializer.java new file mode 100644 index 00000000000..2d6890897b2 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubCopilotAzureMcpInitializer.java @@ -0,0 +1,44 @@ +package com.microsoft.azuretools.azuremcp; + +import java.io.File; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.runtime.ILog; + +import com.microsoft.copilot.eclipse.ui.extensions.IMcpRegistrationProvider; + +public class GithubCopilotAzureMcpInitializer implements IMcpRegistrationProvider { + + private static final ILog log = ILog.of(GithubCopilotAzureMcpInitializer.class); + private static final String PLUGIN_ID = "com.microsoft.azuretools.azuremcp"; + + private AzureMcpPackageManager azureMcpPackageManager; + private static final String MCP_CONFIG_TEMPLATE = "{ \"servers\": { \"azuremcp\": { \"command\": \"%s\", \"args\": [server, start], \"description\": \"Azure MCP Server\" } } }"; + + public GithubCopilotAzureMcpInitializer() { + this.azureMcpPackageManager = new AzureMcpPackageManager(); + log.error("Azure MCP Initializer created"); + } + + @Override + public CompletableFuture getMcpServerConfigurations() { + log.error("getMcpServerConfigurations invoked"); + return CompletableFuture.supplyAsync(() -> getAzureMcpConfig()); + } + + private String getAzureMcpConfig() { + File azureMcpExe = azureMcpPackageManager.getAzureMcpExecutable(); + + if(azureMcpExe != null) { + log.info("Azure MCP executable available"); + + String mcpConfig = String.format(MCP_CONFIG_TEMPLATE, azureMcpExe.getAbsolutePath().replace("\\", "\\\\")); + azureMcpPackageManager.cleanup(); + return mcpConfig; + } else { + log.error("Azure MCP executable not available"); + return null; + } + } + +} \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubRelease.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubRelease.java new file mode 100644 index 00000000000..1316fe5c5f1 --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubRelease.java @@ -0,0 +1,164 @@ +package com.microsoft.azuretools.azuremcp; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class GithubRelease { + @JsonProperty("url") + private String url; + @JsonProperty("html_url") + private String htmlUrl; + @JsonProperty("assets_url") + private String assetsUrl; + @JsonProperty("upload_url") + private String uploadUrl; + @JsonProperty("tarball_url") + private String tarballUrl; + @JsonProperty("zipball_url") + private String zipballUrl; + @JsonProperty("id") + private long id; + @JsonProperty("node_id") + private String nodeId; + @JsonProperty("tag_name") + private String tagName; + @JsonProperty("target_commitish") + private String targetCommitish; + @JsonProperty("name") + private String name; + @JsonProperty("body") + private String body; + @JsonProperty("draft") + private boolean draft; + @JsonProperty("prerelease") + private boolean prerelease; + @JsonProperty("immutable") + private boolean immutable; + @JsonProperty("created_at") + private String createdAt; + @JsonProperty("published_at") + private String publishedAt; + @JsonProperty("author") + private GithubUser author; + @JsonProperty("assets") + private List assets; + + + // Getters and Setters + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public String getHtmlUrl() { + return htmlUrl; + } + public void setHtmlUrl(String htmlUrl) { + this.htmlUrl = htmlUrl; + } + public String getAssetsUrl() { + return assetsUrl; + } + public void setAssetsUrl(String assetsUrl) { + this.assetsUrl = assetsUrl; + } + public String getUploadUrl() { + return uploadUrl; + } + public void setUploadUrl(String uploadUrl) { + this.uploadUrl = uploadUrl; + } + public String getTarballUrl() { + return tarballUrl; + } + public void setTarballUrl(String tarballUrl) { + this.tarballUrl = tarballUrl; + } + public String getZipballUrl() { + return zipballUrl; + } + public void setZipballUrl(String zipballUrl) { + this.zipballUrl = zipballUrl; + } + public long getId() { + return id; + } + public void setId(long id) { + this.id = id; + } + public String getNodeId() { + return nodeId; + } + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + public String getTagName() { + return tagName; + } + public void setTagName(String tagName) { + this.tagName = tagName; + } + public String getTargetCommitish() { + return targetCommitish; + } + public void setTargetCommitish(String targetCommitish) { + this.targetCommitish = targetCommitish; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public String getBody() { + return body; + } + public void setBody(String body) { + this.body = body; + } + public boolean isDraft() { + return draft; + } + public void setDraft(boolean draft) { + this.draft = draft; + } + public boolean isPrerelease() { + return prerelease; + } + public void setPrerelease(boolean prerelease) { + this.prerelease = prerelease; + } + public boolean isImmutable() { + return immutable; + } + public void setImmutable(boolean immutable) { + this.immutable = immutable; + } + public String getCreatedAt() { + return createdAt; + } + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + public String getPublishedAt() { + return publishedAt; + } + public void setPublishedAt(String publishedAt) { + this.publishedAt = publishedAt; + } + public GithubUser getAuthor() { + return author; + } + public void setAuthor(GithubUser author) { + this.author = author; + } + public List getAssets() { + return assets; + } + public void setAssets(List assets) { + this.assets = assets; + } + +} \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubUser.java b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubUser.java new file mode 100644 index 00000000000..4f7720f2eaf --- /dev/null +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/com.microsoft.azuretools.azuremcp/src/com/microsoft/azuretools/azuremcp/GithubUser.java @@ -0,0 +1,152 @@ +package com.microsoft.azuretools.azuremcp; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class GithubUser { + @JsonProperty("login") + public String login; + @JsonProperty("id") + public long id; + @JsonProperty("node_id") + public String nodeId; + @JsonProperty("avatar_url") + public String avatarUrl; + @JsonProperty("gravatar_id") + public String gravatarId; + @JsonProperty("url") + private String url; + @JsonProperty("html_url") + private String htmlUrl; + @JsonProperty("followers_url") + private String followersUrl; + @JsonProperty("following_url") + private String followingUrl; + @JsonProperty("gists_url") + private String gistsUrl; + @JsonProperty("starred_url") + private String starredUrl; + @JsonProperty("subscriptions_url") + private String subscriptionsUrl; + @JsonProperty("organizations_url") + private String organizationsUrl; + @JsonProperty("repos_url") + private String reposUrl; + @JsonProperty("events_url") + private String eventsUrl; + @JsonProperty("received_events_url") + private String receivedEventsUrl; + @JsonProperty("type") + private String type; + @JsonProperty("site_admin") + private boolean siteAdmin; + + public String getLogin() { + return login; + } + public void setLogin(String login) { + this.login = login; + } + public long getId() { + return id; + } + public void setId(long id) { + this.id = id; + } + public String getNodeId() { + return nodeId; + } + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + public String getAvatarUrl() { + return avatarUrl; + } + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + public String getGravatarId() { + return gravatarId; + } + public void setGravatarId(String gravatarId) { + this.gravatarId = gravatarId; + } + public String getUrl() { + return url; + } + public void setUrl(String url) { + this.url = url; + } + public String getHtmlUrl() { + return htmlUrl; + } + public void setHtmlUrl(String htmlUrl) { + this.htmlUrl = htmlUrl; + } + public String getFollowersUrl() { + return followersUrl; + } + public void setFollowersUrl(String followersUrl) { + this.followersUrl = followersUrl; + } + public String getFollowingUrl() { + return followingUrl; + } + public void setFollowingUrl(String followingUrl) { + this.followingUrl = followingUrl; + } + public String getGistsUrl() { + return gistsUrl; + } + public void setGistsUrl(String gistsUrl) { + this.gistsUrl = gistsUrl; + } + public String getStarredUrl() { + return starredUrl; + } + public void setStarredUrl(String starredUrl) { + this.starredUrl = starredUrl; + } + public String getSubscriptionsUrl() { + return subscriptionsUrl; + } + public void setSubscriptionsUrl(String subscriptionsUrl) { + this.subscriptionsUrl = subscriptionsUrl; + } + public String getOrganizationsUrl() { + return organizationsUrl; + } + public void setOrganizationsUrl(String organizationsUrl) { + this.organizationsUrl = organizationsUrl; + } + public String getReposUrl() { + return reposUrl; + } + public void setReposUrl(String reposUrl) { + this.reposUrl = reposUrl; + } + public String getEventsUrl() { + return eventsUrl; + } + public void setEventsUrl(String eventsUrl) { + this.eventsUrl = eventsUrl; + } + public String getReceivedEventsUrl() { + return receivedEventsUrl; + } + public void setReceivedEventsUrl(String receivedEventsUrl) { + this.receivedEventsUrl = receivedEventsUrl; + } + public String getType() { + return type; + } + public void setType(String type) { + this.type = type; + } + public boolean isSiteAdmin() { + return siteAdmin; + } + public void setSiteAdmin(boolean siteAdmin) { + this.siteAdmin = siteAdmin; + } + +} \ No newline at end of file diff --git a/PluginsAndFeatures/azure-toolkit-for-eclipse/pom.xml b/PluginsAndFeatures/azure-toolkit-for-eclipse/pom.xml index d996adb3d71..a65acd5d0f2 100644 --- a/PluginsAndFeatures/azure-toolkit-for-eclipse/pom.xml +++ b/PluginsAndFeatures/azure-toolkit-for-eclipse/pom.xml @@ -28,11 +28,13 @@ com.microsoft.azuretools.container.feature com.microsoft.azuretools.springcloud com.microsoft.azuretools.springcloud.feature + com.microsoft.azuretools.azuremcp + com.microsoft.azuretools.azuremcp.feature WindowsAzurePlugin4EJ UTF-8 - 2.5.0 + 4.0.7 3.32.0-SNAPSHOT @@ -47,6 +49,21 @@ p2 https://azuredownloads.blob.core.windows.net/scalaidesdk/lithium/e44/scala211/stable + + copilot + p2 + https://azuredownloads-g3ahgwb5b8bkbxhd.b01.azurefd.net/github-copilot/ + + + lsp4e + p2 + https://download.eclipse.org/lsp4e/snapshots/ + + + releases + p2 + https://download.eclipse.org/releases/latest/ + @@ -100,6 +117,8 @@ ${tycho-version} -warn:+discouraged,forbidden + 11 + 11