diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json
index bb1963ee6..f654837df 100644
--- a/arduino-ide-extension/package.json
+++ b/arduino-ide-extension/package.json
@@ -104,7 +104,8 @@
     "temp": "^0.9.1",
     "temp-dir": "^2.0.0",
     "tree-kill": "^1.2.1",
-    "util": "^0.12.5"
+    "util": "^0.12.5",
+    "vscode-arduino-api": "^0.1.2"
   },
   "devDependencies": {
     "@octokit/rest": "^18.12.0",
diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
index 89d13fd93..ea568d595 100644
--- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
+++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
@@ -354,6 +354,11 @@ import { FileResourceResolver as TheiaFileResourceResolver } from '@theia/filesy
 import { StylingParticipant } from '@theia/core/lib/browser/styling-service';
 import { MonacoEditorMenuContribution } from './theia/monaco/monaco-menu';
 import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
+import { UpdateArduinoState } from './contributions/update-arduino-state';
+import { TerminalWidgetImpl } from './theia/terminal/terminal-widget-impl';
+import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
+import { TerminalFrontendContribution } from './theia/terminal/terminal-frontend-contribution';
+import { TerminalFrontendContribution as TheiaTerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution'
 
 // Hack to fix copy/cut/paste issue after electron version update in Theia.
 // https://github.com/eclipse-theia/theia/issues/12487
@@ -747,6 +752,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
   Contribution.configure(bind, Account);
   Contribution.configure(bind, CloudSketchbookContribution);
   Contribution.configure(bind, CreateCloudCopy);
+  Contribution.configure(bind, UpdateArduinoState);
 
   bindContributionProvider(bind, StartupTaskProvider);
   bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
@@ -1024,4 +1030,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
   rebind(TheiaMonacoEditorMenuContribution).toService(
     MonacoEditorMenuContribution
   );
+
+  // Patch terminal issues.
+  rebind(TerminalWidget).to(TerminalWidgetImpl).inTransientScope();
+  bind(TerminalFrontendContribution).toSelf().inSingletonScope();
+  rebind(TheiaTerminalFrontendContribution).toService(TerminalFrontendContribution);
 });
diff --git a/arduino-ide-extension/src/browser/contributions/update-arduino-state.ts b/arduino-ide-extension/src/browser/contributions/update-arduino-state.ts
new file mode 100644
index 000000000..a227a51a0
--- /dev/null
+++ b/arduino-ide-extension/src/browser/contributions/update-arduino-state.ts
@@ -0,0 +1,179 @@
+import { DisposableCollection } from '@theia/core/lib/common/disposable';
+import URI from '@theia/core/lib/common/uri';
+import { inject, injectable } from '@theia/core/shared/inversify';
+import { HostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
+import type { ArduinoState } from 'vscode-arduino-api';
+import {
+  BoardsService,
+  CompileSummary,
+  Port,
+  isCompileSummary,
+} from '../../common/protocol';
+import {
+  toApiBoardDetails,
+  toApiCompileSummary,
+  toApiPort,
+} from '../../common/protocol/arduino-context-mapper';
+import type { BoardsConfig } from '../boards/boards-config';
+import { BoardsDataStore } from '../boards/boards-data-store';
+import { BoardsServiceProvider } from '../boards/boards-service-provider';
+import { CurrentSketch } from '../sketches-service-client-impl';
+import { SketchContribution } from './contribution';
+
+interface UpdateStateParams<T extends ArduinoState> {
+  readonly key: keyof T;
+  readonly value: T[keyof T];
+}
+
+/**
+ * Contribution for updating the Arduino state, such as the FQBN, selected port, and sketch path changes via commands, so other VS Code extensions can access it.
+ * See [`vscode-arduino-api`](https://github.com/dankeboy36/vscode-arduino-api#api) for more details.
+ */
+@injectable()
+export class UpdateArduinoState extends SketchContribution {
+  @inject(BoardsService)
+  private readonly boardsService: BoardsService;
+  @inject(BoardsServiceProvider)
+  private readonly boardsServiceProvider: BoardsServiceProvider;
+  @inject(BoardsDataStore)
+  private readonly boardsDataStore: BoardsDataStore;
+  @inject(HostedPluginSupport)
+  private readonly hostedPluginSupport: HostedPluginSupport;
+
+  private readonly toDispose = new DisposableCollection();
+
+  override onStart(): void {
+    this.toDispose.pushAll([
+      this.boardsServiceProvider.onBoardsConfigChanged((config) =>
+        this.updateBoardsConfig(config)
+      ),
+      this.sketchServiceClient.onCurrentSketchDidChange((sketch) =>
+        this.updateSketchPath(sketch)
+      ),
+      this.configService.onDidChangeDataDirUri((dataDirUri) =>
+        this.updateDataDirPath(dataDirUri)
+      ),
+      this.configService.onDidChangeSketchDirUri((userDirUri) =>
+        this.updateUserDirPath(userDirUri)
+      ),
+      this.commandService.onDidExecuteCommand(({ commandId, args }) => {
+        if (
+          commandId === 'arduino.languageserver.notifyBuildDidComplete' &&
+          isCompileSummary(args[0])
+        ) {
+          this.updateCompileSummary(args[0]);
+        }
+      }),
+      this.boardsDataStore.onChanged((fqbn) => {
+        const selectedFqbn =
+          this.boardsServiceProvider.boardsConfig.selectedBoard?.fqbn;
+        if (selectedFqbn && fqbn.includes(selectedFqbn)) {
+          this.updateBoardDetails(selectedFqbn);
+        }
+      }),
+    ]);
+  }
+
+  override onReady(): void {
+    this.boardsServiceProvider.reconciled.then(() => {
+      this.updateBoardsConfig(this.boardsServiceProvider.boardsConfig);
+    });
+    this.updateSketchPath(this.sketchServiceClient.tryGetCurrentSketch());
+    this.updateUserDirPath(this.configService.tryGetSketchDirUri());
+    this.updateDataDirPath(this.configService.tryGetDataDirUri());
+  }
+
+  onStop(): void {
+    this.toDispose.dispose();
+  }
+
+  private async updateSketchPath(
+    sketch: CurrentSketch | undefined
+  ): Promise<void> {
+    const sketchPath = CurrentSketch.isValid(sketch)
+      ? new URI(sketch.uri).path.fsPath()
+      : undefined;
+    return this.updateState({ key: 'sketchPath', value: sketchPath });
+  }
+
+  private async updateCompileSummary(
+    compileSummary: CompileSummary
+  ): Promise<void> {
+    const apiCompileSummary = toApiCompileSummary(compileSummary);
+    return this.updateState({
+      key: 'compileSummary',
+      value: apiCompileSummary,
+    });
+  }
+
+  private async updateBoardsConfig(
+    boardsConfig: BoardsConfig.Config
+  ): Promise<void> {
+    const fqbn = boardsConfig.selectedBoard?.fqbn;
+    const port = boardsConfig.selectedPort;
+    await this.updateFqbn(fqbn);
+    await this.updateBoardDetails(fqbn);
+    await this.updatePort(port);
+  }
+
+  private async updateFqbn(fqbn: string | undefined): Promise<void> {
+    await this.updateState({ key: 'fqbn', value: fqbn });
+  }
+
+  private async updateBoardDetails(fqbn: string | undefined): Promise<void> {
+    const unset = () =>
+      this.updateState({ key: 'boardDetails', value: undefined });
+    if (!fqbn) {
+      return unset();
+    }
+    const [details, persistedData] = await Promise.all([
+      this.boardsService.getBoardDetails({ fqbn }),
+      this.boardsDataStore.getData(fqbn),
+    ]);
+    if (!details) {
+      return unset();
+    }
+    const apiBoardDetails = toApiBoardDetails({
+      ...details,
+      configOptions:
+        BoardsDataStore.Data.EMPTY === persistedData
+          ? details.configOptions
+          : persistedData.configOptions.slice(),
+    });
+    return this.updateState({
+      key: 'boardDetails',
+      value: apiBoardDetails,
+    });
+  }
+
+  private async updatePort(port: Port | undefined): Promise<void> {
+    const apiPort = port && toApiPort(port);
+    return this.updateState({ key: 'port', value: apiPort });
+  }
+
+  private async updateUserDirPath(userDirUri: URI | undefined): Promise<void> {
+    const userDirPath = userDirUri?.path.fsPath();
+    return this.updateState({
+      key: 'userDirPath',
+      value: userDirPath,
+    });
+  }
+
+  private async updateDataDirPath(dataDirUri: URI | undefined): Promise<void> {
+    const dataDirPath = dataDirUri?.path.fsPath();
+    return this.updateState({
+      key: 'dataDirPath',
+      value: dataDirPath,
+    });
+  }
+
+  private async updateState<T extends ArduinoState>(
+    params: UpdateStateParams<T>
+  ): Promise<void> {
+    await this.hostedPluginSupport.didStart;
+    return this.commandService.executeCommand(
+      'arduinoAPI.updateState',
+      params
+    );
+  }
+}
diff --git a/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts b/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts
index 326e02ee4..8491357a4 100644
--- a/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts
+++ b/arduino-ide-extension/src/browser/theia/plugin-ext/hosted-plugin.ts
@@ -1,7 +1,10 @@
-import { Emitter, Event, JsonRpcProxy } from '@theia/core';
+import { DisposableCollection } from '@theia/core/lib/common/disposable';
+import { Emitter, Event } from '@theia/core/lib/common/event';
 import { injectable, interfaces } from '@theia/core/shared/inversify';
-import { HostedPluginServer } from '@theia/plugin-ext/lib/common/plugin-protocol';
-import { HostedPluginSupport as TheiaHostedPluginSupport } from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
+import {
+  PluginContributions,
+  HostedPluginSupport as TheiaHostedPluginSupport,
+} from '@theia/plugin-ext/lib/hosted/browser/hosted-plugin';
 
 @injectable()
 export class HostedPluginSupport extends TheiaHostedPluginSupport {
@@ -10,7 +13,7 @@ export class HostedPluginSupport extends TheiaHostedPluginSupport {
 
   override onStart(container: interfaces.Container): void {
     super.onStart(container);
-    this.hostedPluginServer.onDidCloseConnection(() =>
+    this['server'].onDidCloseConnection(() =>
       this.onDidCloseConnectionEmitter.fire()
     );
   }
@@ -28,8 +31,38 @@ export class HostedPluginSupport extends TheiaHostedPluginSupport {
     return this.onDidCloseConnectionEmitter.event;
   }
 
-  private get hostedPluginServer(): JsonRpcProxy<HostedPluginServer> {
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    return (this as any).server;
+  protected override startPlugins(
+    contributionsByHost: Map<string, PluginContributions[]>,
+    toDisconnect: DisposableCollection
+  ): Promise<void> {
+    reorderPlugins(contributionsByHost);
+    return super.startPlugins(contributionsByHost, toDisconnect);
   }
 }
+
+/**
+ * Force the `vscode-arduino-ide` API to activate before any Arduino IDE tool VSIX.
+ *
+ * Arduino IDE tool VISXs are not forced to declare the `vscode-arduino-api` as a `extensionDependencies`,
+ * but the API must activate before any tools. This in place sorting helps to bypass Theia's plugin resolution
+ * without forcing tools developers to add `vscode-arduino-api` to the `extensionDependencies`.
+ */
+function reorderPlugins(
+  contributionsByHost: Map<string, PluginContributions[]>
+): void {
+  for (const [, contributions] of contributionsByHost) {
+    const apiPluginIndex = contributions.findIndex(isArduinoAPI);
+    if (apiPluginIndex >= 0) {
+      const apiPlugin = contributions[apiPluginIndex];
+      contributions.splice(apiPluginIndex, 1);
+      contributions.unshift(apiPlugin);
+    }
+  }
+}
+
+function isArduinoAPI(pluginContribution: PluginContributions): boolean {
+  return (
+    pluginContribution.plugin.metadata.model.id ===
+    'dankeboy36.vscode-arduino-api'
+  );
+}
diff --git a/arduino-ide-extension/src/browser/theia/terminal/terminal-frontend-contribution.ts b/arduino-ide-extension/src/browser/theia/terminal/terminal-frontend-contribution.ts
new file mode 100644
index 000000000..13020175d
--- /dev/null
+++ b/arduino-ide-extension/src/browser/theia/terminal/terminal-frontend-contribution.ts
@@ -0,0 +1,38 @@
+import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
+import { CommandRegistry } from '@theia/core/lib/common/command';
+import { Widget } from '@theia/core/shared/@phosphor/widgets';
+import { injectable } from '@theia/core/shared/inversify';
+import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
+import {
+  TerminalCommands,
+  TerminalFrontendContribution as TheiaTerminalFrontendContribution,
+} from '@theia/terminal/lib/browser/terminal-frontend-contribution';
+
+// Patch for https://github.com/eclipse-theia/theia/pull/12626
+@injectable()
+export class TerminalFrontendContribution extends TheiaTerminalFrontendContribution {
+  override registerCommands(commands: CommandRegistry): void {
+    super.registerCommands(commands);
+    commands.unregisterCommand(TerminalCommands.SPLIT);
+    commands.registerCommand(TerminalCommands.SPLIT, {
+      execute: () => this.splitTerminal(),
+      isEnabled: (w) => this.withWidget(w, () => true),
+      isVisible: (w) => this.withWidget(w, () => true),
+    });
+  }
+
+  override registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
+    super.registerToolbarItems(toolbar);
+    toolbar.unregisterItem(TerminalCommands.SPLIT.id);
+  }
+
+  private withWidget<T>(
+    widget: Widget | undefined,
+    fn: (widget: TerminalWidget) => T
+  ): T | false {
+    if (widget instanceof TerminalWidget) {
+      return fn(widget);
+    }
+    return false;
+  }
+}
diff --git a/arduino-ide-extension/src/browser/theia/terminal/terminal-widget-impl.ts b/arduino-ide-extension/src/browser/theia/terminal/terminal-widget-impl.ts
new file mode 100644
index 000000000..310a659d1
--- /dev/null
+++ b/arduino-ide-extension/src/browser/theia/terminal/terminal-widget-impl.ts
@@ -0,0 +1,23 @@
+import { injectable } from '@theia/core/shared/inversify';
+import { TerminalWidgetImpl as TheiaTerminalWidgetImpl } from '@theia/terminal/lib/browser/terminal-widget-impl';
+import debounce from 'p-debounce';
+
+// Patch for https://github.com/eclipse-theia/theia/pull/12587
+@injectable()
+export class TerminalWidgetImpl extends TheiaTerminalWidgetImpl {
+  private readonly debouncedResizeTerminal = debounce(
+    () => this.doResizeTerminal(),
+    50
+  );
+
+  protected override resizeTerminal(): void {
+    this.debouncedResizeTerminal();
+  }
+
+  private doResizeTerminal(): void {
+    const geo = this.fitAddon.proposeDimensions();
+    const cols = geo.cols;
+    const rows = geo.rows - 1; // subtract one row for margin
+    this.term.resize(cols, rows);
+  }
+}
diff --git a/arduino-ide-extension/src/common/protocol/arduino-context-mapper.ts b/arduino-ide-extension/src/common/protocol/arduino-context-mapper.ts
new file mode 100644
index 000000000..ede24fe1c
--- /dev/null
+++ b/arduino-ide-extension/src/common/protocol/arduino-context-mapper.ts
@@ -0,0 +1,126 @@
+import type {
+  Port as APIPort,
+  BoardDetails as ApiBoardDetails,
+  BuildProperties as ApiBuildProperties,
+  CompileSummary as ApiCompileSummary,
+  ConfigOption as ApiConfigOption,
+  ConfigValue as ApiConfigValue,
+  Tool as ApiTool,
+} from 'vscode-arduino-api';
+import type {
+  BoardDetails,
+  CompileSummary,
+  ConfigOption,
+  ConfigValue,
+  Port,
+  Tool,
+} from '../protocol';
+
+export function toApiCompileSummary(
+  compileSummary: CompileSummary
+): ApiCompileSummary {
+  const {
+    buildPath,
+    buildProperties,
+    boardPlatform,
+    buildPlatform,
+    executableSectionsSize,
+    usedLibraries,
+  } = compileSummary;
+  return {
+    buildPath,
+    buildProperties: toApiBuildProperties(buildProperties),
+    executableSectionsSize: executableSectionsSize,
+    boardPlatform,
+    buildPlatform,
+    usedLibraries,
+  };
+}
+
+export function toApiPort(port: Port): APIPort | undefined {
+  const {
+    hardwareId = '',
+    properties = {},
+    address,
+    protocol,
+    protocolLabel,
+    addressLabel: label,
+  } = port;
+  return {
+    label,
+    address,
+    hardwareId,
+    properties,
+    protocol,
+    protocolLabel,
+  };
+}
+
+export function toApiBoardDetails(boardDetails: BoardDetails): ApiBoardDetails {
+  const { fqbn, programmers, configOptions, requiredTools } = boardDetails;
+  return {
+    buildProperties: toApiBuildProperties(boardDetails.buildProperties),
+    configOptions: configOptions.map(toApiConfigOption),
+    fqbn,
+    programmers,
+    toolsDependencies: requiredTools.map(toApiTool),
+  };
+}
+
+function toApiConfigOption(configOption: ConfigOption): ApiConfigOption {
+  const { label, values, option } = configOption;
+  return {
+    optionLabel: label,
+    option,
+    values: values.map(toApiConfigValue),
+  };
+}
+
+function toApiConfigValue(configValue: ConfigValue): ApiConfigValue {
+  const { label, selected, value } = configValue;
+  return {
+    selected,
+    value,
+    valueLabel: label,
+  };
+}
+
+function toApiTool(toolDependency: Tool): ApiTool {
+  const { name, packager, version } = toolDependency;
+  return {
+    name,
+    packager,
+    version,
+  };
+}
+
+const propertySep = '=';
+
+function parseProperty(
+  property: string
+): [key: string, value: string] | undefined {
+  const segments = property.split(propertySep);
+  if (segments.length < 2) {
+    console.warn(`Could not parse build property: ${property}.`);
+    return undefined;
+  }
+
+  const [key, ...rest] = segments;
+  if (!key) {
+    console.warn(`Could not determine property key from raw: ${property}.`);
+    return undefined;
+  }
+  const value = rest.join(propertySep);
+  return [key, value];
+}
+
+export function toApiBuildProperties(properties: string[]): ApiBuildProperties {
+  return properties.reduce((acc, curr) => {
+    const entry = parseProperty(curr);
+    if (entry) {
+      const [key, value] = entry;
+      acc[key] = value;
+    }
+    return acc;
+  }, <Record<string, string>>{});
+}
diff --git a/arduino-ide-extension/src/common/protocol/boards-service.ts b/arduino-ide-extension/src/common/protocol/boards-service.ts
index c955b9462..7e2f77555 100644
--- a/arduino-ide-extension/src/common/protocol/boards-service.ts
+++ b/arduino-ide-extension/src/common/protocol/boards-service.ts
@@ -441,6 +441,7 @@ export interface BoardDetails {
   readonly debuggingSupported: boolean;
   readonly VID: string;
   readonly PID: string;
+  readonly buildProperties: string[];
 }
 
 export interface Tool {
diff --git a/arduino-ide-extension/src/common/protocol/core-service.ts b/arduino-ide-extension/src/common/protocol/core-service.ts
index 7dfcfb896..2a683370d 100644
--- a/arduino-ide-extension/src/common/protocol/core-service.ts
+++ b/arduino-ide-extension/src/common/protocol/core-service.ts
@@ -5,13 +5,11 @@ import type {
   Range,
   Position,
 } from '@theia/core/shared/vscode-languageserver-protocol';
-import type {
-  BoardUserField,
-  Port,
-} from '../../common/protocol/boards-service';
+import type { BoardUserField, Port, Installable } from '../../common/protocol/';
 import type { Programmer } from './boards-service';
 import type { Sketch } from './sketches-service';
 import { IndexUpdateSummary } from './notification-service';
+import type { CompileSummary as ApiCompileSummary } from 'vscode-arduino-api';
 
 export const CompilerWarningLiterals = [
   'None',
@@ -19,7 +17,7 @@ export const CompilerWarningLiterals = [
   'More',
   'All',
 ] as const;
-export type CompilerWarnings = typeof CompilerWarningLiterals[number];
+export type CompilerWarnings = (typeof CompilerWarningLiterals)[number];
 export namespace CompilerWarnings {
   export function labelOf(warning: CompilerWarnings): string {
     return CompilerWarningLabels[warning];
@@ -103,6 +101,53 @@ export namespace CoreError {
   }
 }
 
+export interface InstalledPlatformReference {
+  readonly id: string;
+  readonly version: Installable.Version;
+  /**
+   * Absolute filesystem path.
+   */
+  readonly installDir: string;
+  readonly packageUrl: string;
+}
+
+export interface ExecutableSectionSize {
+  readonly name: string;
+  readonly size: number;
+  readonly maxSize: number;
+}
+
+export interface CompileSummary {
+  readonly buildPath: string;
+  /**
+   * To be compatible with the `vscode-arduino-tools` API.
+   * @deprecated Use `buildPath` instead. Use Theia or VS Code URI to convert to an URI string on the client side.
+   */
+  readonly buildOutputUri: string;
+  readonly usedLibraries: ApiCompileSummary['usedLibraries'];
+  readonly executableSectionsSize: ExecutableSectionSize[];
+  readonly boardPlatform?: InstalledPlatformReference | undefined;
+  readonly buildPlatform?: InstalledPlatformReference | undefined;
+  readonly buildProperties: string[];
+}
+
+export function isCompileSummary(arg: unknown): arg is CompileSummary {
+  return (
+    Boolean(arg) &&
+    typeof arg === 'object' &&
+    (<CompileSummary>arg).buildPath !== undefined &&
+    typeof (<CompileSummary>arg).buildPath === 'string' &&
+    (<CompileSummary>arg).buildOutputUri !== undefined &&
+    typeof (<CompileSummary>arg).buildOutputUri === 'string' &&
+    (<CompileSummary>arg).executableSectionsSize !== undefined &&
+    Array.isArray((<CompileSummary>arg).executableSectionsSize) &&
+    (<CompileSummary>arg).usedLibraries !== undefined &&
+    Array.isArray((<CompileSummary>arg).usedLibraries) &&
+    (<CompileSummary>arg).buildProperties !== undefined &&
+    Array.isArray((<CompileSummary>arg).buildProperties)
+  );
+}
+
 export const CoreServicePath = '/services/core-service';
 export const CoreService = Symbol('CoreService');
 export interface CoreService {
@@ -132,7 +177,7 @@ export interface CoreService {
 }
 
 export const IndexTypeLiterals = ['platform', 'library'] as const;
-export type IndexType = typeof IndexTypeLiterals[number];
+export type IndexType = (typeof IndexTypeLiterals)[number];
 export namespace IndexType {
   export function is(arg: unknown): arg is IndexType {
     return (
diff --git a/arduino-ide-extension/src/node/boards-service-impl.ts b/arduino-ide-extension/src/node/boards-service-impl.ts
index e04aa909f..a2db13193 100644
--- a/arduino-ide-extension/src/node/boards-service-impl.ts
+++ b/arduino-ide-extension/src/node/boards-service-impl.ts
@@ -155,6 +155,7 @@ export class BoardsServiceImpl
       VID = prop.get('vid') || '';
       PID = prop.get('pid') || '';
     }
+    const buildProperties = detailsResp.getBuildPropertiesList();
 
     return {
       fqbn,
@@ -164,6 +165,7 @@ export class BoardsServiceImpl
       debuggingSupported,
       VID,
       PID,
+      buildProperties
     };
   }
 
diff --git a/arduino-ide-extension/src/node/core-service-impl.ts b/arduino-ide-extension/src/node/core-service-impl.ts
index e5e3d05ac..253dcd383 100644
--- a/arduino-ide-extension/src/node/core-service-impl.ts
+++ b/arduino-ide-extension/src/node/core-service-impl.ts
@@ -8,6 +8,8 @@ import {
   CompilerWarnings,
   CoreService,
   CoreError,
+  CompileSummary,
+  isCompileSummary,
 } from '../common/protocol/core-service';
 import {
   CompileRequest,
@@ -35,12 +37,15 @@ import { firstToUpperCase, notEmpty } from '../common/utils';
 import { ServiceError } from './service-error';
 import { ExecuteWithProgress, ProgressResponse } from './grpc-progressible';
 import { BoardDiscovery } from './board-discovery';
+import { Mutable } from '@theia/core/lib/common/types';
 
 namespace Uploadable {
   export type Request = UploadRequest | UploadUsingProgrammerRequest;
   export type Response = UploadResponse | UploadUsingProgrammerResponse;
 }
 
+type CompileSummaryFragment = Partial<Mutable<CompileSummary>>;
+
 @injectable()
 export class CoreServiceImpl extends CoreClientAware implements CoreService {
   @inject(ResponseService)
@@ -58,23 +63,13 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
   async compile(options: CoreService.Options.Compile): Promise<void> {
     const coreClient = await this.coreClient;
     const { client, instance } = coreClient;
-    let buildPath: string | undefined = undefined;
+    const compileSummary = <CompileSummaryFragment>{};
     const progressHandler = this.createProgressHandler(options);
-    const buildPathHandler = (response: CompileResponse) => {
-      const currentBuildPath = response.getBuildPath();
-      if (currentBuildPath) {
-        buildPath = currentBuildPath;
-      } else {
-        if (!!buildPath && currentBuildPath !== buildPath) {
-          throw new Error(
-            `The CLI has already provided a build path: <${buildPath}>, and IDE received a new build path value: <${currentBuildPath}>.`
-          );
-        }
-      }
-    };
+    const compileSummaryHandler = (response: CompileResponse) =>
+      updateCompileSummary(compileSummary, response);
     const handler = this.createOnDataHandler<CompileResponse>(
       progressHandler,
-      buildPathHandler
+      compileSummaryHandler
     );
     const request = this.compileRequest(options, instance);
     return new Promise<void>((resolve, reject) => {
@@ -111,31 +106,35 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
         .on('end', resolve);
     }).finally(() => {
       handler.dispose();
-      if (!buildPath) {
+      if (!isCompileSummary(compileSummary)) {
         console.error(
-          `Have not received the build path from the CLI while running the compilation.`
+          `Have not received the full compile summary from the CLI while running the compilation. ${JSON.stringify(
+            compileSummary
+          )}`
         );
       } else {
-        this.fireBuildDidComplete(FileUri.create(buildPath).toString());
+        this.fireBuildDidComplete(compileSummary);
       }
     });
   }
 
   // This executes on the frontend, the VS Code extension receives it, and sends an `ino/buildDidComplete` notification to the language server.
-  private fireBuildDidComplete(buildOutputUri: string): void {
+  private fireBuildDidComplete(compileSummary: CompileSummary): void {
     const params = {
-      buildOutputUri,
+      ...compileSummary,
     };
     console.info(
       `Executing 'arduino.languageserver.notifyBuildDidComplete' with ${JSON.stringify(
-        params
+        params.buildOutputUri
       )}`
     );
     this.commandService
       .executeCommand('arduino.languageserver.notifyBuildDidComplete', params)
       .catch((err) =>
         console.error(
-          `Unexpected error when firing event on build did complete. ${buildOutputUri}`,
+          `Unexpected error when firing event on build did complete. ${JSON.stringify(
+            params.buildOutputUri
+          )}`,
           err
         )
       );
@@ -465,3 +464,62 @@ namespace StreamingResponse {
     readonly handlers?: ((response: R) => void)[];
   }
 }
+
+function updateCompileSummary(
+  compileSummary: CompileSummaryFragment,
+  response: CompileResponse
+): CompileSummaryFragment {
+  const buildPath = response.getBuildPath();
+  if (buildPath) {
+    compileSummary.buildPath = buildPath;
+    compileSummary.buildOutputUri = FileUri.create(buildPath).toString();
+  }
+  const executableSectionsSize = response.getExecutableSectionsSizeList();
+  if (executableSectionsSize) {
+    compileSummary.executableSectionsSize = executableSectionsSize.map((item) =>
+      item.toObject(false)
+    );
+  }
+  const usedLibraries = response.getUsedLibrariesList();
+  if (usedLibraries) {
+    compileSummary.usedLibraries = usedLibraries.map((item) => {
+      const object = item.toObject(false);
+      const library = {
+        ...object,
+        architectures: object.architecturesList,
+        types: object.typesList,
+        examples: object.examplesList,
+        providesIncludes: object.providesIncludesList,
+        properties: object.propertiesMap.reduce((acc, [key, value]) => {
+          acc[key] = value;
+          return acc;
+        }, {} as Record<string, string>),
+        compatibleWith: object.compatibleWithMap.reduce((acc, [key, value]) => {
+          acc[key] = value;
+          return acc;
+        }, {} as Record<string, boolean>),
+      } as const;
+      const mutable = <Partial<Mutable<typeof library>>>library;
+      delete mutable.architecturesList;
+      delete mutable.typesList;
+      delete mutable.examplesList;
+      delete mutable.providesIncludesList;
+      delete mutable.propertiesMap;
+      delete mutable.compatibleWithMap;
+      return library;
+    });
+  }
+  const boardPlatform = response.getBoardPlatform();
+  if (boardPlatform) {
+    compileSummary.buildPlatform = boardPlatform.toObject(false);
+  }
+  const buildPlatform = response.getBuildPlatform();
+  if (buildPlatform) {
+    compileSummary.buildPlatform = buildPlatform.toObject(false);
+  }
+  const buildProperties = response.getBuildPropertiesList();
+  if (buildProperties) {
+    compileSummary.buildProperties = buildProperties.slice();
+  }
+  return compileSummary;
+}
diff --git a/arduino-ide-extension/src/test/common/arduino-context-mapper.test.ts b/arduino-ide-extension/src/test/common/arduino-context-mapper.test.ts
new file mode 100644
index 000000000..23727bffc
--- /dev/null
+++ b/arduino-ide-extension/src/test/common/arduino-context-mapper.test.ts
@@ -0,0 +1,43 @@
+import { expect } from 'chai';
+import { toApiBuildProperties } from '../../common/protocol/arduino-context-mapper';
+
+describe('arduino-context-mapper', () => {
+  describe('toApiBuildProperties', () => {
+    it('should parse an array of build properties string into a record', () => {
+      const expected = {
+        foo: 'alma',
+        bar: '36',
+        baz: 'false',
+      };
+      const actual = toApiBuildProperties(['foo=alma', 'bar=36', 'baz=false']);
+      expect(actual).to.be.deep.equal(expected);
+    });
+
+    it('should not skip build property key with empty value', () => {
+      const expected = {
+        foo: '',
+      };
+      const actual = toApiBuildProperties(['foo=']);
+      expect(actual).to.be.deep.equal(expected);
+    });
+
+    it('should skip invalid entries', () => {
+      const expected = {
+        foo: 'alma',
+        bar: '36',
+        baz: '-DARDUINO_USB_CDC_ON_BOOT=0',
+      };
+      const actual = toApiBuildProperties([
+        'foo=alma',
+        'invalid',
+        '=invalid2',
+        '=invalid3=',
+        '=',
+        '==',
+        'bar=36',
+        'baz=-DARDUINO_USB_CDC_ON_BOOT=0',
+      ]);
+      expect(actual).to.be.deep.equal(expected);
+    });
+  });
+});
diff --git a/arduino-ide-extension/src/test/node/core-service-impl.slow-test.ts b/arduino-ide-extension/src/test/node/core-service-impl.slow-test.ts
index faac7b6c7..0a0fd253c 100644
--- a/arduino-ide-extension/src/test/node/core-service-impl.slow-test.ts
+++ b/arduino-ide-extension/src/test/node/core-service-impl.slow-test.ts
@@ -9,6 +9,7 @@ import {
   BoardsService,
   CoreService,
   SketchesService,
+  isCompileSummary,
 } from '../../common/protocol';
 import { createBaseContainer, startDaemon } from './test-bindings';
 
@@ -31,7 +32,7 @@ describe('core-service-impl', () => {
   afterEach(() => toDispose.dispose());
 
   describe('compile', () => {
-    it('should execute a command with the build path', async function () {
+    it('should execute a command with the compile summary, including the build path', async function () {
       this.timeout(testTimeout);
       const coreService = container.get<CoreService>(CoreService);
       const sketchesService = container.get<SketchesService>(SketchesService);
@@ -56,7 +57,7 @@ describe('core-service-impl', () => {
       const [, args] = executedBuildDidCompleteCommands[0];
       expect(args.length).to.be.equal(1);
       const arg = args[0];
-      expect(typeof arg).to.be.equal('object');
+      expect(isCompileSummary(arg)).to.be.true;
       expect('buildOutputUri' in arg).to.be.true;
       expect(arg.buildOutputUri).to.be.not.undefined;
 
diff --git a/package.json b/package.json
index 9c9c00f56..6cfd1c915 100644
--- a/package.json
+++ b/package.json
@@ -70,6 +70,7 @@
   "theiaPluginsDir": "plugins",
   "theiaPlugins": {
     "vscode-builtin-cpp": "https://open-vsx.org/api/vscode/cpp/1.52.1/file/vscode.cpp-1.52.1.vsix",
+    "vscode-arduino-api": "https://github.com/dankeboy36/vscode-arduino-api/releases/download/0.1.2/vscode-arduino-api-0.1.2.vsix",
     "vscode-arduino-tools": "https://downloads.arduino.cc/vscode-arduino-tools/vscode-arduino-tools-0.0.2-beta.8.vsix",
     "vscode-builtin-json": "https://open-vsx.org/api/vscode/json/1.46.1/file/vscode.json-1.46.1.vsix",
     "vscode-builtin-json-language-features": "https://open-vsx.org/api/vscode/json-language-features/1.46.1/file/vscode.json-language-features-1.46.1.vsix",
diff --git a/yarn.lock b/yarn.lock
index e5a69a4c7..74640bac9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3209,6 +3209,11 @@
   resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.5.tgz#b1d2f772142a301538fae9bdf9cf15b9f2573a29"
   integrity sha512-hKB88y3YHL8oPOs/CNlaXtjWn93+Bs48sDQR37ZUqG2tLeCS7EA1cmnkKsuQsub9OKEB/y/Rw9zqJqqNSbqVlQ==
 
+"@types/vscode@^1.78.0":
+  version "1.78.0"
+  resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.78.0.tgz#b5600abce8855cf21fb32d0857bcd084b1f83069"
+  integrity sha512-LJZIJpPvKJ0HVQDqfOy6W4sNKUBBwyDu1Bs8chHBZOe9MNuKTJtidgZ2bqjhmmWpUb0TIIqv47BFUcVmAsgaVA==
+
 "@types/which@^1.3.1":
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf"
@@ -3991,6 +3996,14 @@ arduino-serial-plotter-webapp@0.2.0:
   resolved "https://registry.yarnpkg.com/arduino-serial-plotter-webapp/-/arduino-serial-plotter-webapp-0.2.0.tgz#90d61ad7ed1452f70fd226ff25eccb36c1ab1a4f"
   integrity sha512-AxQIsKr6Mf8K1c3kj+ojjFvE9Vz8cUqJqRink6/myp/ranEGwsQQ83hziktkPKZvBQshqrMH8nzoGIY2Z3A2OA==
 
+ardunno-cli@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ardunno-cli/-/ardunno-cli-0.1.2.tgz#145d998231b34b33bf70f7fc6e5be6497191f708"
+  integrity sha512-8PTBMDS2ofe2LJZZKHw/MgfXgDwpiImXJcBeqeZ6lcTSDqQNMJpEIjcCdPcxbsQbJXRRfZZ4nn6G/gXwEuJPpw==
+  dependencies:
+    nice-grpc-common "^2.0.2"
+    protobufjs "^7.2.3"
+
 are-we-there-yet@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
@@ -10738,6 +10751,13 @@ nested-error-stacks@^2.1.1:
   resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5"
   integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==
 
+nice-grpc-common@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/nice-grpc-common/-/nice-grpc-common-2.0.2.tgz#e6aeebb2bd19d87114b351e291e30d79dd38acf7"
+  integrity sha512-7RNWbls5kAL1QVUOXvBsv1uO0wPQK3lHv+cY1gwkTzirnG1Nop4cBJZubpgziNbaVc/bl9QJcyvsf/NQxa3rjQ==
+  dependencies:
+    ts-error "^1.0.6"
+
 nice-try@^1.0.4:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@@ -12184,7 +12204,7 @@ proto-list@~1.2.1:
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
   integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
 
-protobufjs@^7.0.0:
+protobufjs@^7.0.0, protobufjs@^7.2.3:
   version "7.2.3"
   resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.3.tgz#01af019e40d9c6133c49acbb3ff9e30f4f0f70b2"
   integrity sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==
@@ -13083,6 +13103,11 @@ safe-regex@^1.1.0:
   dependencies:
     ret "~0.1.10"
 
+safe-stable-stringify@^2.4.3:
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886"
+  integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==
+
 "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
@@ -14350,6 +14375,11 @@ trough@^2.0.0:
   resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876"
   integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==
 
+ts-error@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/ts-error/-/ts-error-1.0.6.tgz#277496f2a28de6c184cfce8dfd5cdd03a4e6b0fc"
+  integrity sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==
+
 ts-md5@^1.2.2:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.3.1.tgz#f5b860c0d5241dd9bb4e909dd73991166403f511"
@@ -14947,6 +14977,15 @@ vinyl@^2.2.1:
     remove-trailing-separator "^1.0.1"
     replace-ext "^1.0.0"
 
+vscode-arduino-api@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/vscode-arduino-api/-/vscode-arduino-api-0.1.2.tgz#11d294fd72c36bbea1ccacd101f16c11df490b77"
+  integrity sha512-FxZllcBIUKxYMiakCSOZ2VSaxscQACxzo0tI5xu8HrbDBU5yvl4zvBzwss4PIYvBG0oZeSKDf950i37Qn7dcmA==
+  dependencies:
+    "@types/vscode" "^1.78.0"
+    ardunno-cli "^0.1.2"
+    safe-stable-stringify "^2.4.3"
+
 vscode-jsonrpc@8.1.0:
   version "8.1.0"
   resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz#cb9989c65e219e18533cc38e767611272d274c94"