diff --git a/README.md b/README.md
index 904521da..7f774100 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ The `setup-java` action provides the following functionality for GitHub Actions
 - Caching dependencies managed by Apache Maven
 - Caching dependencies managed by Gradle
 - Caching dependencies managed by sbt
+- [Maven Toolchains declaration](https://maven.apache.org/guides/mini/guide-using-toolchains.html) for specified JDK versions
 
 This action allows you to work with Java and Scala projects.
 
@@ -37,7 +38,7 @@ This action allows you to work with Java and Scala projects.
   - `cache`: Quick [setup caching](#caching-packages-dependencies) for the dependencies managed through one of the predifined package managers. It can be one of "maven", "gradle" or "sbt". 
 
   #### Maven options
-  The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin. See [advanced usage](docs/advanced-usage.md) for more.
+  The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin as well as Apache Maven Toolchains. See [advanced usage](docs/advanced-usage.md) for more.
 
   - `overwrite-settings`: By default action overwrites the settings.xml. In order to skip generation of file if it exists set this to `false`.
 
@@ -53,6 +54,10 @@ This action allows you to work with Java and Scala projects.
 
   - `gpg-passphrase`: description: 'Environment variable name for the GPG private key passphrase. Default is GPG_PASSPHRASE.
 
+  - `mvn-toolchain-id`: Name of Maven Toolchain ID if the default name of `${distribution}_${java-version}` is not wanted.
+
+  - `mvn-toolchain-vendor`: Name of Maven Toolchain Vendor if the default name of `${distribution}` is not wanted.
+
 ### Basic Configuration
 
 #### Eclipse Temurin
@@ -203,7 +208,11 @@ All versions are added to the PATH. The last version will be used and available
             15
 ```
 
+### Using Maven Toolchains
+In the example above multiple JDKs are installed for the same job. The result after the last JDK is installed is a Maven Toolchains declaration containing references to all three JDKs. The values for `id`, `version`, and `vendor` of the individual Toolchain entries are the given input values for `distribution` and `java-version` (`vendor` being the combination of `${distribution}_${java-version}`) by default.
+
 ### Advanced Configuration
+
 - [Selecting a Java distribution](docs/advanced-usage.md#Selecting-a-Java-distribution)
   - [Eclipse Temurin](docs/advanced-usage.md#Eclipse-Temurin)
   - [Adopt](docs/advanced-usage.md#Adopt)
@@ -219,6 +228,7 @@ All versions are added to the PATH. The last version will be used and available
 - [Publishing using Apache Maven](docs/advanced-usage.md#Publishing-using-Apache-Maven)
 - [Publishing using Gradle](docs/advanced-usage.md#Publishing-using-Gradle)
 - [Hosted Tool Cache](docs/advanced-usage.md#Hosted-Tool-Cache)
+- [Modifying Maven Toolchains](docs/advanced-usage.md#Modifying-Maven-Toolchains)
 
 ## License
 
diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts
index 94ad7e1c..c5d2ffc3 100644
--- a/__tests__/auth.test.ts
+++ b/__tests__/auth.test.ts
@@ -5,9 +5,10 @@ import * as core from '@actions/core';
 import os from 'os';
 
 import * as auth from '../src/auth';
+import { M2_DIR, MVN_SETTINGS_FILE } from '../src/constants';
 
-const m2Dir = path.join(__dirname, auth.M2_DIR);
-const settingsFile = path.join(m2Dir, auth.SETTINGS_FILE);
+const m2Dir = path.join(__dirname, M2_DIR);
+const settingsFile = path.join(m2Dir, MVN_SETTINGS_FILE);
 
 describe('auth tests', () => {
   let spyOSHomedir: jest.SpyInstance;
@@ -38,7 +39,7 @@ describe('auth tests', () => {
     const password = 'TOLKIEN';
 
     const altHome = path.join(__dirname, 'runner', 'settings');
-    const altSettingsFile = path.join(altHome, auth.SETTINGS_FILE);
+    const altSettingsFile = path.join(altHome, MVN_SETTINGS_FILE);
     await io.rmRF(altHome); // ensure it doesn't already exist
 
     await auth.createAuthenticationSettings(id, username, password, altHome, true);
diff --git a/__tests__/toolchains.test.ts b/__tests__/toolchains.test.ts
new file mode 100644
index 00000000..ff6fdab0
--- /dev/null
+++ b/__tests__/toolchains.test.ts
@@ -0,0 +1,292 @@
+import * as fs from 'fs';
+import os from 'os';
+import * as path from 'path';
+import * as core from '@actions/core';
+import * as io from '@actions/io';
+import * as toolchains from '../src/toolchains';
+import { M2_DIR, MVN_TOOLCHAINS_FILE } from '../src/constants';
+
+const m2Dir = path.join(__dirname, M2_DIR);
+const toolchainsFile = path.join(m2Dir, MVN_TOOLCHAINS_FILE);
+
+describe('toolchains tests', () => {
+  let spyOSHomedir: jest.SpyInstance;
+  let spyInfo: jest.SpyInstance;
+
+  beforeEach(async () => {
+    await io.rmRF(m2Dir);
+    spyOSHomedir = jest.spyOn(os, 'homedir');
+    spyOSHomedir.mockReturnValue(__dirname);
+    spyInfo = jest.spyOn(core, 'info');
+    spyInfo.mockImplementation(() => null);
+  }, 300000);
+
+  afterAll(async () => {
+    try {
+      await io.rmRF(m2Dir);
+    } catch {
+      console.log('Failed to remove test directories');
+    }
+    jest.resetAllMocks();
+    jest.clearAllMocks();
+    jest.restoreAllMocks();
+  }, 100000);
+
+  it('creates toolchains.xml in alternate locations', async () => {
+    const jdkInfo = {
+      version: '17',
+      vendor: 'Eclipse Temurin',
+      id: 'temurin_17',
+      jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+    };
+
+    const altHome = path.join(__dirname, 'runner', 'toolchains');
+    const altToolchainsFile = path.join(altHome, MVN_TOOLCHAINS_FILE);
+    await io.rmRF(altHome); // ensure it doesn't already exist
+
+    await toolchains.createToolchainsSettings({
+      jdkInfo,
+      settingsDirectory: altHome,
+      overwriteSettings: true
+    });
+
+    expect(fs.existsSync(m2Dir)).toBe(false);
+    expect(fs.existsSync(toolchainsFile)).toBe(false);
+
+    expect(fs.existsSync(altHome)).toBe(true);
+    expect(fs.existsSync(altToolchainsFile)).toBe(true);
+    expect(fs.readFileSync(altToolchainsFile, 'utf-8')).toEqual(
+      toolchains.generateToolchainDefinition(
+        '',
+        jdkInfo.version,
+        jdkInfo.vendor,
+        jdkInfo.id,
+        jdkInfo.jdkHome
+      )
+    );
+
+    await io.rmRF(altHome);
+  }, 100000);
+
+  it('creates toolchains.xml with minimal configuration', async () => {
+    const jdkInfo = {
+      version: '17',
+      vendor: 'Eclipse Temurin',
+      id: 'temurin_17',
+      jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+    };
+
+    const result = `<?xml version="1.0"?>
+<toolchains xmlns="https://maven.apache.org/TOOLCHAINS/1.1.0"
+  xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
+  <toolchain>
+    <type>jdk</type>
+    <provides>
+      <version>17</version>
+      <vendor>Eclipse Temurin</vendor>
+      <id>temurin_17</id>
+    </provides>
+    <configuration>
+      <jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
+    </configuration>
+  </toolchain>
+</toolchains>`;
+
+    await toolchains.createToolchainsSettings({
+      jdkInfo,
+      settingsDirectory: m2Dir,
+      overwriteSettings: true
+    });
+
+    expect(fs.existsSync(m2Dir)).toBe(true);
+    expect(fs.existsSync(toolchainsFile)).toBe(true);
+    expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
+      toolchains.generateToolchainDefinition(
+        '',
+        jdkInfo.version,
+        jdkInfo.vendor,
+        jdkInfo.id,
+        jdkInfo.jdkHome
+      )
+    );
+    expect(
+      toolchains.generateToolchainDefinition(
+        '',
+        jdkInfo.version,
+        jdkInfo.vendor,
+        jdkInfo.id,
+        jdkInfo.jdkHome
+      )
+    ).toEqual(result);
+  }, 100000);
+
+  it('reuses existing toolchains.xml files', async () => {
+    const jdkInfo = {
+      version: '17',
+      vendor: 'Eclipse Temurin',
+      id: 'temurin_17',
+      jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+    };
+
+    const originalFile = `<toolchains>
+        <toolchain>
+            <type>jdk</type>
+        <provides>
+        <version>1.6</version>
+        <vendor>Sun</vendor>
+        <id>sun_1.6</id>
+        </provides>
+        <configuration>
+        <jdkHome>/opt/jdk/sun/1.6</jdkHome>
+        </configuration>
+        </toolchain>
+      </toolchains>`;
+    const result = `<?xml version="1.0"?>
+<toolchains>
+  <toolchain>
+    <type>jdk</type>
+    <provides>
+      <version>1.6</version>
+      <vendor>Sun</vendor>
+      <id>sun_1.6</id>
+    </provides>
+    <configuration>
+      <jdkHome>/opt/jdk/sun/1.6</jdkHome>
+    </configuration>
+  </toolchain>
+  <toolchain>
+    <type>jdk</type>
+    <provides>
+      <version>17</version>
+      <vendor>Eclipse Temurin</vendor>
+      <id>temurin_17</id>
+    </provides>
+    <configuration>
+      <jdkHome>/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64</jdkHome>
+    </configuration>
+  </toolchain>
+</toolchains>`;
+
+    fs.mkdirSync(m2Dir, { recursive: true });
+    fs.writeFileSync(toolchainsFile, originalFile);
+    expect(fs.existsSync(m2Dir)).toBe(true);
+    expect(fs.existsSync(toolchainsFile)).toBe(true);
+
+    await toolchains.createToolchainsSettings({
+      jdkInfo,
+      settingsDirectory: m2Dir,
+      overwriteSettings: true
+    });
+
+    expect(fs.existsSync(m2Dir)).toBe(true);
+    expect(fs.existsSync(toolchainsFile)).toBe(true);
+    expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
+      toolchains.generateToolchainDefinition(
+        originalFile,
+        jdkInfo.version,
+        jdkInfo.vendor,
+        jdkInfo.id,
+        jdkInfo.jdkHome
+      )
+    );
+    expect(
+      toolchains.generateToolchainDefinition(
+        originalFile,
+        jdkInfo.version,
+        jdkInfo.vendor,
+        jdkInfo.id,
+        jdkInfo.jdkHome
+      )
+    ).toEqual(result);
+  }, 100000);
+
+  it('does not overwrite existing toolchains.xml files', async () => {
+    const jdkInfo = {
+      version: '17',
+      vendor: 'Eclipse Temurin',
+      id: 'temurin_17',
+      jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+    };
+
+    const originalFile = `<toolchains>
+        <toolchain>
+            <type>jdk</type>
+        <provides>
+        <version>1.6</version>
+        <vendor>Sun</vendor>
+        <id>sun_1.6</id>
+        </provides>
+        <configuration>
+        <jdkHome>/opt/jdk/sun/1.6</jdkHome>
+        </configuration>
+        </toolchain>
+      </toolchains>`;
+
+    fs.mkdirSync(m2Dir, { recursive: true });
+    fs.writeFileSync(toolchainsFile, originalFile);
+    expect(fs.existsSync(m2Dir)).toBe(true);
+    expect(fs.existsSync(toolchainsFile)).toBe(true);
+
+    await toolchains.createToolchainsSettings({
+      jdkInfo,
+      settingsDirectory: m2Dir,
+      overwriteSettings: false
+    });
+
+    expect(fs.existsSync(m2Dir)).toBe(true);
+    expect(fs.existsSync(toolchainsFile)).toBe(true);
+    expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(originalFile);
+  }, 100000);
+
+  it('generates valid toolchains.xml with minimal configuration', () => {
+    const jdkInfo = {
+      version: 'JAVA_VERSION',
+      vendor: 'JAVA_VENDOR',
+      id: 'VENDOR_VERSION',
+      jdkHome: 'JAVA_HOME'
+    };
+
+    const expectedToolchains = `<?xml version="1.0"?>
+<toolchains xmlns="https://maven.apache.org/TOOLCHAINS/1.1.0"
+  xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd">
+  <toolchain>
+    <type>jdk</type>
+    <provides>
+      <version>${jdkInfo.version}</version>
+      <vendor>${jdkInfo.vendor}</vendor>
+      <id>${jdkInfo.id}</id>
+    </provides>
+    <configuration>
+      <jdkHome>${jdkInfo.jdkHome}</jdkHome>
+    </configuration>
+  </toolchain>
+</toolchains>`;
+
+    expect(
+      toolchains.generateToolchainDefinition(
+        '',
+        jdkInfo.version,
+        jdkInfo.vendor,
+        jdkInfo.id,
+        jdkInfo.jdkHome
+      )
+    ).toEqual(expectedToolchains);
+  }, 100000);
+
+  it('creates toolchains.xml with correct id when none is supplied', async () => {
+    const version = '17';
+    const distributionName = 'temurin';
+    const id = 'temurin_17';
+    const jdkHome = '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64';
+
+    await toolchains.configureToolchains(version, distributionName, jdkHome, undefined);
+
+    expect(fs.existsSync(m2Dir)).toBe(true);
+    expect(fs.existsSync(toolchainsFile)).toBe(true);
+    expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
+      toolchains.generateToolchainDefinition('', version, distributionName, id, jdkHome)
+    );
+  }, 100000);
+});
diff --git a/action.yml b/action.yml
index 499eae3f..7bd7180f 100644
--- a/action.yml
+++ b/action.yml
@@ -62,6 +62,12 @@ inputs:
   token:
     description: Used to pull java versions from setup-java. Since there is a default value, token is typically not supplied by the user.
     default: ${{ github.token }}
+  mvn-toolchain-id:
+    description: 'Name of Maven Toolchain ID if the default name of "${distribution}_${java-version}" is not wanted. See examples of supported syntax in Advanced Usage file'
+    required: false
+  mvn-toolchain-vendor:
+    description: 'Name of Maven Toolchain Vendor if the default name of "${distribution}" is not wanted. See examples of supported syntax in Advanced Usage file'
+    required: false
 outputs:
   distribution:
     description: 'Distribution of Java that has been installed'
diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js
index e4fcbd59..df9ec30c 100644
--- a/dist/cleanup/index.js
+++ b/dist/cleanup/index.js
@@ -68406,7 +68406,7 @@ else {
 "use strict";
 
 Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
+exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
 exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home';
 exports.INPUT_JAVA_VERSION = 'java-version';
 exports.INPUT_ARCHITECTURE = 'architecture';
@@ -68426,6 +68426,11 @@ exports.INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE';
 exports.INPUT_CACHE = 'cache';
 exports.INPUT_JOB_STATUS = 'job-status';
 exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
+exports.M2_DIR = '.m2';
+exports.MVN_SETTINGS_FILE = 'settings.xml';
+exports.MVN_TOOLCHAINS_FILE = 'toolchains.xml';
+exports.INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id';
+exports.INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor';
 
 
 /***/ }),
diff --git a/dist/setup/index.js b/dist/setup/index.js
index e19da558..0bdc9dcd 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -103216,7 +103216,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
     });
 };
 Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.generate = exports.createAuthenticationSettings = exports.configureAuthentication = exports.SETTINGS_FILE = exports.M2_DIR = void 0;
+exports.generate = exports.createAuthenticationSettings = exports.configureAuthentication = void 0;
 const path = __importStar(__nccwpck_require__(1017));
 const core = __importStar(__nccwpck_require__(2186));
 const io = __importStar(__nccwpck_require__(7436));
@@ -103226,14 +103226,12 @@ const xmlbuilder2_1 = __nccwpck_require__(151);
 const constants = __importStar(__nccwpck_require__(9042));
 const gpg = __importStar(__nccwpck_require__(3759));
 const util_1 = __nccwpck_require__(2629);
-exports.M2_DIR = '.m2';
-exports.SETTINGS_FILE = 'settings.xml';
 function configureAuthentication() {
     return __awaiter(this, void 0, void 0, function* () {
         const id = core.getInput(constants.INPUT_SERVER_ID);
         const username = core.getInput(constants.INPUT_SERVER_USERNAME);
         const password = core.getInput(constants.INPUT_SERVER_PASSWORD);
-        const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), exports.M2_DIR);
+        const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
         const overwriteSettings = util_1.getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
         const gpgPrivateKey = core.getInput(constants.INPUT_GPG_PRIVATE_KEY) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
         const gpgPassphrase = core.getInput(constants.INPUT_GPG_PASSPHRASE) ||
@@ -103252,7 +103250,7 @@ function configureAuthentication() {
 exports.configureAuthentication = configureAuthentication;
 function createAuthenticationSettings(id, username, password, settingsDirectory, overwriteSettings, gpgPassphrase = undefined) {
     return __awaiter(this, void 0, void 0, function* () {
-        core.info(`Creating ${exports.SETTINGS_FILE} with server-id: ${id}`);
+        core.info(`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${id}`);
         // when an alternate m2 location is specified use only that location (no .m2 directory)
         // otherwise use the home/.m2/ path
         yield io.mkdirP(settingsDirectory);
@@ -103294,7 +103292,7 @@ function generate(id, username, password, gpgPassphrase) {
 exports.generate = generate;
 function write(directory, settings, overwriteSettings) {
     return __awaiter(this, void 0, void 0, function* () {
-        const location = path.join(directory, exports.SETTINGS_FILE);
+        const location = path.join(directory, constants.MVN_SETTINGS_FILE);
         const settingsExists = fs.existsSync(location);
         if (settingsExists && overwriteSettings) {
             core.info(`Overwriting existing file ${location}`);
@@ -103510,7 +103508,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) {
 "use strict";
 
 Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
+exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
 exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home';
 exports.INPUT_JAVA_VERSION = 'java-version';
 exports.INPUT_ARCHITECTURE = 'architecture';
@@ -103530,6 +103528,11 @@ exports.INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE';
 exports.INPUT_CACHE = 'cache';
 exports.INPUT_JOB_STATUS = 'job-status';
 exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
+exports.M2_DIR = '.m2';
+exports.MVN_SETTINGS_FILE = 'settings.xml';
+exports.MVN_TOOLCHAINS_FILE = 'toolchains.xml';
+exports.INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id';
+exports.INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor';
 
 
 /***/ }),
@@ -104952,6 +104955,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
 const core = __importStar(__nccwpck_require__(2186));
 const auth = __importStar(__nccwpck_require__(3497));
 const util_1 = __nccwpck_require__(2629);
+const toolchains = __importStar(__nccwpck_require__(9322));
 const constants = __importStar(__nccwpck_require__(9042));
 const cache_1 = __nccwpck_require__(4810);
 const path = __importStar(__nccwpck_require__(1017));
@@ -104966,8 +104970,12 @@ function run() {
             const jdkFile = core.getInput(constants.INPUT_JDK_FILE);
             const cache = core.getInput(constants.INPUT_CACHE);
             const checkLatest = util_1.getBooleanInput(constants.INPUT_CHECK_LATEST, false);
+            let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
+            if (versions.length !== toolchainIds.length) {
+                toolchainIds = [];
+            }
             core.startGroup('Installed distributions');
-            for (const version of versions) {
+            for (const [index, version] of versions.entries()) {
                 const installerOptions = {
                     architecture,
                     packageType,
@@ -104979,6 +104987,7 @@ function run() {
                     throw new Error(`No supported distribution was found for input ${distributionName}`);
                 }
                 const result = yield distribution.setupJava();
+                yield toolchains.configureToolchains(version, distributionName, result.path, toolchainIds[index]);
                 core.info('');
                 core.info('Java configuration:');
                 core.info(`  Distribution: ${distributionName}`);
@@ -105002,6 +105011,166 @@ function run() {
 run();
 
 
+/***/ }),
+
+/***/ 9322:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+"use strict";
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+    if (k2 === undefined) k2 = k;
+    o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+    Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+    o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+    __setModuleDefault(result, mod);
+    return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.generateToolchainDefinition = exports.createToolchainsSettings = exports.configureToolchains = void 0;
+const fs = __importStar(__nccwpck_require__(7147));
+const os = __importStar(__nccwpck_require__(2037));
+const path = __importStar(__nccwpck_require__(1017));
+const core = __importStar(__nccwpck_require__(2186));
+const io = __importStar(__nccwpck_require__(7436));
+const constants = __importStar(__nccwpck_require__(9042));
+const util_1 = __nccwpck_require__(2629);
+const xmlbuilder2_1 = __nccwpck_require__(151);
+function configureToolchains(version, distributionName, jdkHome, toolchainId) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const vendor = core.getInput(constants.INPUT_MVN_TOOLCHAIN_VENDOR) || distributionName;
+        const id = toolchainId || `${vendor}_${version}`;
+        const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
+        const overwriteSettings = util_1.getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
+        yield createToolchainsSettings({
+            jdkInfo: {
+                version,
+                vendor,
+                id,
+                jdkHome
+            },
+            settingsDirectory,
+            overwriteSettings
+        });
+    });
+}
+exports.configureToolchains = configureToolchains;
+function createToolchainsSettings({ jdkInfo, settingsDirectory, overwriteSettings }) {
+    return __awaiter(this, void 0, void 0, function* () {
+        core.info(`Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}`);
+        // when an alternate m2 location is specified use only that location (no .m2 directory)
+        // otherwise use the home/.m2/ path
+        yield io.mkdirP(settingsDirectory);
+        const originalToolchains = yield readExistingToolchainsFile(settingsDirectory);
+        const updatedToolchains = generateToolchainDefinition(originalToolchains, jdkInfo.version, jdkInfo.vendor, jdkInfo.id, jdkInfo.jdkHome);
+        yield writeToolchainsFileToDisk(settingsDirectory, updatedToolchains, overwriteSettings);
+    });
+}
+exports.createToolchainsSettings = createToolchainsSettings;
+// only exported for testing purposes
+function generateToolchainDefinition(original, version, vendor, id, jdkHome) {
+    let xmlObj;
+    if (original === null || original === void 0 ? void 0 : original.length) {
+        xmlObj = xmlbuilder2_1.create(original)
+            .root()
+            .ele({
+            toolchain: {
+                type: 'jdk',
+                provides: {
+                    version: `${version}`,
+                    vendor: `${vendor}`,
+                    id: `${id}`
+                },
+                configuration: {
+                    jdkHome: `${jdkHome}`
+                }
+            }
+        });
+    }
+    else
+        xmlObj = xmlbuilder2_1.create({
+            toolchains: {
+                '@xmlns': 'https://maven.apache.org/TOOLCHAINS/1.1.0',
+                '@xmlns:xsi': 'https://www.w3.org/2001/XMLSchema-instance',
+                '@xsi:schemaLocation': 'https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
+                toolchain: [
+                    {
+                        type: 'jdk',
+                        provides: {
+                            version: `${version}`,
+                            vendor: `${vendor}`,
+                            id: `${id}`
+                        },
+                        configuration: {
+                            jdkHome: `${jdkHome}`
+                        }
+                    }
+                ]
+            }
+        });
+    return xmlObj.end({
+        format: 'xml',
+        wellFormed: false,
+        headless: false,
+        prettyPrint: true,
+        width: 80
+    });
+}
+exports.generateToolchainDefinition = generateToolchainDefinition;
+function readExistingToolchainsFile(directory) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+        if (fs.existsSync(location)) {
+            return fs.readFileSync(location, {
+                encoding: 'utf-8',
+                flag: 'r'
+            });
+        }
+        return '';
+    });
+}
+function writeToolchainsFileToDisk(directory, settings, overwriteSettings) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+        const settingsExists = fs.existsSync(location);
+        if (settingsExists && overwriteSettings) {
+            core.info(`Overwriting existing file ${location}`);
+        }
+        else if (!settingsExists) {
+            core.info(`Writing to ${location}`);
+        }
+        else {
+            core.info(`Skipping generation of ${location} because file already exists and overwriting is not enabled`);
+            return;
+        }
+        return fs.writeFileSync(location, settings, {
+            encoding: 'utf-8',
+            flag: 'w'
+        });
+    });
+}
+
+
 /***/ }),
 
 /***/ 2629:
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index befe52df..93ebd854 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -14,6 +14,7 @@
 - [Publishing using Apache Maven](#Publishing-using-Apache-Maven)
 - [Publishing using Gradle](#Publishing-using-Gradle)
 - [Hosted Tool Cache](#Hosted-Tool-Cache)
+- [Modifying Maven Toolchains](#Modifying-Maven-Toolchains)
 
 See [action.yml](../action.yml) for more details on task inputs.
 
@@ -350,3 +351,100 @@ GitHub Hosted Runners have a tool cache that comes with some Java versions pre-i
 Currently, LTS versions of Adopt OpenJDK (`adopt`) are cached on the GitHub Hosted Runners.
 
 The tools cache gets updated on a weekly basis. For information regarding locally cached versions of Java on GitHub hosted runners, check out [GitHub Actions Virtual Environments](https://github.com/actions/virtual-environments).
+
+## Modifying Maven Toolchains
+The `setup-java` action generates a basic [Maven Toolchains declaration](https://maven.apache.org/guides/mini/guide-using-toolchains.html) for specified Java versions by either creating a minimal toolchains file or extending an existing declaration with the additional JDKs.
+
+### Installing Multiple JDKs With Toolchains
+Subsequent calls to `setup-java` with distinct distribution and version parameters will continue to extend the toolchains declaration and make all specified Java versions available.
+
+```yaml
+steps:
+- uses: actions/setup-java@v3
+  with:
+    distribution: '<distribution>'
+    java-version: |
+      8
+      11
+
+- uses: actions/setup-java@v3
+  with:
+    distribution: '<distribution>'
+    java-version: 15
+```
+
+The result is a Toolchain with entries for JDKs 8, 11 and 15. You can even combine this with custom JDKs of arbitrary versions:
+
+```yaml
+- run: |
+    download_url="https://example.com/java/jdk/6u45-b06/jdk-6u45-linux-x64.tar.gz"
+    wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
+- uses: actions/setup-java@v3
+  with:
+    distribution: 'jdkfile'
+    jdkFile: ${{ runner.temp }}/java_package.tar.gz
+    java-version: '1.6'
+    architecture: x64
+```
+
+This will generate a Toolchains entry with the following values: `version: 1.6`, `vendor: jkdfile`, `id: Oracle_1.6`.
+
+### Modifying The Toolchain Vendor For JDKs
+Each JDK provider will receive a default `vendor` using the `distribution` input value but this can be overridden with the `mvn-toolchain-vendor` parameter as follows.
+
+```yaml
+- run: |
+    download_url="https://example.com/java/jdk/6u45-b06/jdk-6u45-linux-x64.tar.gz"
+    wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
+- uses: actions/setup-java@v3
+  with:
+    distribution: 'jdkfile'
+    jdkFile: ${{ runner.temp }}/java_package.tar.gz
+    java-version: '1.6'
+    architecture: x64
+    mvn-toolchain-vendor: 'Oracle'
+```
+
+This will generate a Toolchains entry with the following values: `version: 1.6`, `vendor: Oracle`, `id: Oracle_1.6`.
+
+In case you install multiple versions of Java at once with multi-line `java-version` input setting the `mvn-toolchain-vendor` still only accepts one value and will use this value for installed JDKs as expected when installing multiple versions of the same `distribution`.
+
+```yaml
+steps:
+- uses: actions/setup-java@v3
+  with:
+    distribution: '<distribution>'
+    java-version: |
+      8
+      11
+    mvn-toolchain-vendor: Eclipse Temurin
+```
+
+### Modifying The Toolchain ID For JDKs
+Each JDK provider will receive a default `id` based on the combination of `distribution` and `java-version` in the format of `distribution_java-version` (e.g. `temurin_11`) but this can be overridden with the `mvn-toolchain-id` parameter as follows.
+
+```yaml
+steps:
+- uses: actions/checkout@v3
+- uses: actions/setup-java@v3
+  with:
+    distribution: 'temurin'
+    java-version: '11'
+    mvn-toolchain-id: 'some_other_id'
+- run: java -cp java HelloWorldApp
+```
+
+In case you install multiple versions of Java at once you can use the same syntax as used in `java-versions`. Please note that you have to declare an ID for all Java versions that will be installed or the `mvn-toolchain-id` instruction will be skipped wholesale due to mapping ambiguities.
+
+```yaml
+steps:
+- uses: actions/setup-java@v3
+  with:
+    distribution: '<distribution>'
+    java-version: |
+      8
+      11
+    mvn-toolchain-id: |
+      something_else
+      something_other
+```
diff --git a/src/auth.ts b/src/auth.ts
index a52306b0..f697ddcd 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -10,15 +10,12 @@ import * as constants from './constants';
 import * as gpg from './gpg';
 import { getBooleanInput } from './util';
 
-export const M2_DIR = '.m2';
-export const SETTINGS_FILE = 'settings.xml';
-
 export async function configureAuthentication() {
   const id = core.getInput(constants.INPUT_SERVER_ID);
   const username = core.getInput(constants.INPUT_SERVER_USERNAME);
   const password = core.getInput(constants.INPUT_SERVER_PASSWORD);
   const settingsDirectory =
-    core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), M2_DIR);
+    core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
   const overwriteSettings = getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
   const gpgPrivateKey =
     core.getInput(constants.INPUT_GPG_PRIVATE_KEY) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
@@ -54,7 +51,7 @@ export async function createAuthenticationSettings(
   overwriteSettings: boolean,
   gpgPassphrase: string | undefined = undefined
 ) {
-  core.info(`Creating ${SETTINGS_FILE} with server-id: ${id}`);
+  core.info(`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${id}`);
   // when an alternate m2 location is specified use only that location (no .m2 directory)
   // otherwise use the home/.m2/ path
   await io.mkdirP(settingsDirectory);
@@ -106,7 +103,7 @@ export function generate(
 }
 
 async function write(directory: string, settings: string, overwriteSettings: boolean) {
-  const location = path.join(directory, SETTINGS_FILE);
+  const location = path.join(directory, constants.MVN_SETTINGS_FILE);
   const settingsExists = fs.existsSync(location);
   if (settingsExists && overwriteSettings) {
     core.info(`Overwriting existing file ${location}`);
diff --git a/src/constants.ts b/src/constants.ts
index 94d7667f..ad8709e7 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -20,3 +20,9 @@ export const INPUT_CACHE = 'cache';
 export const INPUT_JOB_STATUS = 'job-status';
 
 export const STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
+
+export const M2_DIR = '.m2';
+export const MVN_SETTINGS_FILE = 'settings.xml';
+export const MVN_TOOLCHAINS_FILE = 'toolchains.xml';
+export const INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id';
+export const INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor';
diff --git a/src/setup-java.ts b/src/setup-java.ts
index 621883a8..048c5dd6 100644
--- a/src/setup-java.ts
+++ b/src/setup-java.ts
@@ -1,6 +1,7 @@
 import * as core from '@actions/core';
 import * as auth from './auth';
 import { getBooleanInput, isCacheFeatureAvailable } from './util';
+import * as toolchains from './toolchains';
 import * as constants from './constants';
 import { restore } from './cache';
 import * as path from 'path';
@@ -16,9 +17,14 @@ async function run() {
     const jdkFile = core.getInput(constants.INPUT_JDK_FILE);
     const cache = core.getInput(constants.INPUT_CACHE);
     const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false);
+    let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
+
+    if (versions.length !== toolchainIds.length) {
+      toolchainIds = [];
+    }
 
     core.startGroup('Installed distributions');
-    for (const version of versions) {
+    for (const [index, version] of versions.entries()) {
       const installerOptions: JavaInstallerOptions = {
         architecture,
         packageType,
@@ -32,6 +38,12 @@ async function run() {
       }
 
       const result = await distribution.setupJava();
+      await toolchains.configureToolchains(
+        version,
+        distributionName,
+        result.path,
+        toolchainIds[index]
+      );
 
       core.info('');
       core.info('Java configuration:');
diff --git a/src/toolchains.ts b/src/toolchains.ts
new file mode 100644
index 00000000..8c7d72c4
--- /dev/null
+++ b/src/toolchains.ts
@@ -0,0 +1,158 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import * as core from '@actions/core';
+import * as io from '@actions/io';
+import * as constants from './constants';
+
+import { getBooleanInput } from './util';
+import { create as xmlCreate } from 'xmlbuilder2';
+
+interface JdkInfo {
+  version: string;
+  vendor: string;
+  id: string;
+  jdkHome: string;
+}
+
+export async function configureToolchains(
+  version: string,
+  distributionName: string,
+  jdkHome: string,
+  toolchainId?: string
+) {
+  const vendor = core.getInput(constants.INPUT_MVN_TOOLCHAIN_VENDOR) || distributionName;
+  const id = toolchainId || `${vendor}_${version}`;
+  const settingsDirectory =
+    core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
+  const overwriteSettings = getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
+
+  await createToolchainsSettings({
+    jdkInfo: {
+      version,
+      vendor,
+      id,
+      jdkHome
+    },
+    settingsDirectory,
+    overwriteSettings
+  });
+}
+
+export async function createToolchainsSettings({
+  jdkInfo,
+  settingsDirectory,
+  overwriteSettings
+}: {
+  jdkInfo: JdkInfo;
+  settingsDirectory: string;
+  overwriteSettings: boolean;
+}) {
+  core.info(
+    `Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}`
+  );
+  // when an alternate m2 location is specified use only that location (no .m2 directory)
+  // otherwise use the home/.m2/ path
+  await io.mkdirP(settingsDirectory);
+  const originalToolchains = await readExistingToolchainsFile(settingsDirectory);
+  const updatedToolchains = generateToolchainDefinition(
+    originalToolchains,
+    jdkInfo.version,
+    jdkInfo.vendor,
+    jdkInfo.id,
+    jdkInfo.jdkHome
+  );
+  await writeToolchainsFileToDisk(settingsDirectory, updatedToolchains, overwriteSettings);
+}
+
+// only exported for testing purposes
+export function generateToolchainDefinition(
+  original: string,
+  version: string,
+  vendor: string,
+  id: string,
+  jdkHome: string
+) {
+  let xmlObj;
+  if (original?.length) {
+    xmlObj = xmlCreate(original)
+      .root()
+      .ele({
+        toolchain: {
+          type: 'jdk',
+          provides: {
+            version: `${version}`,
+            vendor: `${vendor}`,
+            id: `${id}`
+          },
+          configuration: {
+            jdkHome: `${jdkHome}`
+          }
+        }
+      });
+  } else
+    xmlObj = xmlCreate({
+      toolchains: {
+        '@xmlns': 'https://maven.apache.org/TOOLCHAINS/1.1.0',
+        '@xmlns:xsi': 'https://www.w3.org/2001/XMLSchema-instance',
+        '@xsi:schemaLocation':
+          'https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
+        toolchain: [
+          {
+            type: 'jdk',
+            provides: {
+              version: `${version}`,
+              vendor: `${vendor}`,
+              id: `${id}`
+            },
+            configuration: {
+              jdkHome: `${jdkHome}`
+            }
+          }
+        ]
+      }
+    });
+
+  return xmlObj.end({
+    format: 'xml',
+    wellFormed: false,
+    headless: false,
+    prettyPrint: true,
+    width: 80
+  });
+}
+
+async function readExistingToolchainsFile(directory: string) {
+  const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+  if (fs.existsSync(location)) {
+    return fs.readFileSync(location, {
+      encoding: 'utf-8',
+      flag: 'r'
+    });
+  }
+  return '';
+}
+
+async function writeToolchainsFileToDisk(
+  directory: string,
+  settings: string,
+  overwriteSettings: boolean
+) {
+  const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+  const settingsExists = fs.existsSync(location);
+  if (settingsExists && overwriteSettings) {
+    core.info(`Overwriting existing file ${location}`);
+  } else if (!settingsExists) {
+    core.info(`Writing to ${location}`);
+  } else {
+    core.info(
+      `Skipping generation of ${location} because file already exists and overwriting is not enabled`
+    );
+    return;
+  }
+
+  return fs.writeFileSync(location, settings, {
+    encoding: 'utf-8',
+    flag: 'w'
+  });
+}