diff --git a/.eslintrc.yml b/.eslintrc.yml
index d2bae1b21b34eca270ad9a863511c66847648939..4a7197e3bd5c1a8aa35128031ea6645df86ee2df 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -193,6 +193,6 @@ overrides:
       '@graphql-eslint/no-unused-fragments': error
       '@graphql-eslint/no-duplicate-fields': error
   - files:
-    - 'spec/contracts/consumer/**/*'
+    - '{,ee/}spec/contracts/consumer/**/*'
     rules:
       '@gitlab/require-i18n-strings': off
diff --git a/doc/development/testing_guide/contract/index.md b/doc/development/testing_guide/contract/index.md
index 8412a260c7d4a85787541e4ff1ae732b14d730b9..08a21e58a52862321fdced4350ddf213a0d97993 100644
--- a/doc/development/testing_guide/contract/index.md
+++ b/doc/development/testing_guide/contract/index.md
@@ -26,6 +26,8 @@ The contracts themselves are stored in [`/spec/contracts/contracts`](https://git
 
 Before running the consumer tests, go to `spec/contracts/consumer` and run `npm install`. To run all the consumer tests, you just need to run `npm test -- /specs`. Otherwise, to run a specific spec file, replace `/specs` with the specific spec filename.
 
+You can also run tests from the root directory of the project, using the command `yarn jest:contract`.
+
 ### Run the provider tests
 
 Before running the provider tests, make sure your GDK (GitLab Development Kit) is fully set up and running. You can follow the setup instructions detailed in the [GDK repository](https://gitlab.com/gitlab-org/gitlab-development-kit/-/tree/main). To run the provider tests, you use Rake tasks that can be found in [`./lib/tasks/contracts`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/tasks/contracts). To get a list of all the Rake tasks related to the provider tests, run `bundle exec rake -T contracts`. For example:
diff --git a/ee/lib/tasks/contracts/merge_requests.rake b/ee/lib/tasks/contracts/merge_requests.rake
new file mode 100644
index 0000000000000000000000000000000000000000..6017be8d7b85a52e80e2cb992cd861b1eb692ee3
--- /dev/null
+++ b/ee/lib/tasks/contracts/merge_requests.rake
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+return if Rails.env.production?
+
+require 'pact/tasks/verification_task'
+
+contracts = File.expand_path('../../../spec/contracts/contracts/project/merge_request', __dir__)
+provider = File.expand_path('../../../spec/contracts/provider', __dir__)
+
+namespace :contracts do
+  namespace :merge_requests do
+    Pact::VerificationTask.new(:suggested_reviewers) do |pact|
+      pact.uri(
+        "#{contracts}/show/mergerequest#show-merge_request_suggested_reviewers_endpoint.json",
+        pact_helper: "#{provider}/pact_helpers/project/merge_request/show/suggested_reviewers_helper.rb"
+      )
+    end
+
+    desc 'Run all merge request contract tests'
+    task 'test:merge_requests', :contract_merge_requests do |_t, arg|
+      errors = %w[suggested_reviewers].each_with_object([]) do |task, err|
+        Rake::Task["contracts:merge_requests:pact:verify:#{task}"].execute
+      rescue StandardError, SystemExit
+        err << "contracts:merge_requests:pact:verify:#{task}"
+      end
+
+      raise StandardError, "Errors in tasks #{errors.join(', ')}" unless errors.empty?
+    end
+  end
+end
diff --git a/ee/spec/contracts/.gitignore b/ee/spec/contracts/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..cb89d4102d384ea13d3a1a176379a0e796a9fd1d
--- /dev/null
+++ b/ee/spec/contracts/.gitignore
@@ -0,0 +1,2 @@
+logs/
+consumer/node_modules
diff --git a/ee/spec/contracts/consumer/.eslintrc.yml b/ee/spec/contracts/consumer/.eslintrc.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e4b380714d3536eb7c853bd634a9b8cf2e74afb4
--- /dev/null
+++ b/ee/spec/contracts/consumer/.eslintrc.yml
@@ -0,0 +1,7 @@
+---
+extends:
+  - 'plugin:@gitlab/jest'
+settings:
+  import/core-modules:
+    - '@pact-foundation/pact'
+    - jest-pact
diff --git a/ee/spec/contracts/consumer/fixtures/project/merge_request/suggested_reviewers.fixture.js b/ee/spec/contracts/consumer/fixtures/project/merge_request/suggested_reviewers.fixture.js
new file mode 100644
index 0000000000000000000000000000000000000000..06841998c97441e4aebddfe175eb58fb47855700
--- /dev/null
+++ b/ee/spec/contracts/consumer/fixtures/project/merge_request/suggested_reviewers.fixture.js
@@ -0,0 +1,56 @@
+import { Matchers } from '@pact-foundation/pact';
+import {
+  AUTOCOMPLETE_USERS_URL,
+  TEST_PROJECT_ID,
+  TEST_MERGE_REQUEST_IID,
+} from '../../../test_constants';
+
+const userIdMatchExample1 = 6954442;
+const userIdMatchExample2 = 6954441;
+
+const body = [
+  {
+    id: Matchers.integer(userIdMatchExample1),
+    username: Matchers.string('user1'),
+    name: Matchers.string('A User'),
+  },
+  {
+    id: Matchers.integer(userIdMatchExample2),
+    username: Matchers.string('gitlab-qa'),
+    name: Matchers.string('Contract Test User'),
+    suggested: Matchers.boolean(true),
+  },
+];
+
+export const suggestedReviewersFixture = {
+  body: Matchers.extractPayload(body),
+
+  success: {
+    status: 200,
+    headers: {
+      'Content-Type': 'application/json; charset=utf-8',
+    },
+    body,
+  },
+
+  scenario: {
+    state: 'a merge request exists with suggested reviewers available for selection',
+    uponReceiving: 'a request for suggested reviewers',
+  },
+
+  request: {
+    withRequest: {
+      method: 'GET',
+      path: AUTOCOMPLETE_USERS_URL,
+      query: {
+        active: 'true',
+        project_id: Matchers.string(TEST_PROJECT_ID),
+        merge_request_iid: Matchers.string(TEST_MERGE_REQUEST_IID),
+        current_user: 'true',
+      },
+      headers: {
+        Accept: '*/*',
+      },
+    },
+  },
+};
diff --git a/ee/spec/contracts/consumer/resources/api/project/autocomplete_users.js b/ee/spec/contracts/consumer/resources/api/project/autocomplete_users.js
new file mode 100644
index 0000000000000000000000000000000000000000..875e39bba5dec33492d40d8a0ddac24efbfc1410
--- /dev/null
+++ b/ee/spec/contracts/consumer/resources/api/project/autocomplete_users.js
@@ -0,0 +1,24 @@
+import axios from 'axios';
+
+import {
+  AUTOCOMPLETE_USERS_URL,
+  TEST_PROJECT_ID,
+  TEST_MERGE_REQUEST_IID,
+} from '../../../test_constants';
+
+export async function getSuggestedReviewers(endpoint) {
+  const { url } = endpoint;
+
+  return axios({
+    method: 'GET',
+    baseURL: url,
+    url: AUTOCOMPLETE_USERS_URL,
+    headers: { Accept: '*/*' },
+    params: {
+      active: 'true',
+      project_id: TEST_PROJECT_ID,
+      merge_request_iid: TEST_MERGE_REQUEST_IID,
+      current_user: 'true',
+    },
+  }).then((response) => response.data);
+}
diff --git a/ee/spec/contracts/consumer/specs/project/merge_request/show.spec.js b/ee/spec/contracts/consumer/specs/project/merge_request/show.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a0ef108ed5a9906bb1f8dd38a64ab66db7ff1c38
--- /dev/null
+++ b/ee/spec/contracts/consumer/specs/project/merge_request/show.spec.js
@@ -0,0 +1,41 @@
+import path from 'path';
+import { pactWith } from 'jest-pact';
+import { suggestedReviewersFixture } from '../../../fixtures/project/merge_request/suggested_reviewers.fixture';
+import { getSuggestedReviewers } from '../../../resources/api/project/autocomplete_users';
+
+const ROOT_PATH = path.resolve(__dirname, '../../..');
+const CONSUMER_NAME = 'MergeRequest#show';
+const CONSUMER_LOG = path.join(ROOT_PATH, '../logs/consumer.log');
+const CONTRACT_DIR = path.join(ROOT_PATH, '../contracts/project/merge_request/show');
+const SUGGESTED_REVIEWERS_PROVIDER_NAME = 'Merge Request Suggested Reviewers Endpoint';
+
+// API endpoint: /autocomplete/users.json
+pactWith(
+  {
+    consumer: CONSUMER_NAME,
+    provider: SUGGESTED_REVIEWERS_PROVIDER_NAME,
+    log: CONSUMER_LOG,
+    dir: CONTRACT_DIR,
+  },
+
+  (provider) => {
+    describe(SUGGESTED_REVIEWERS_PROVIDER_NAME, () => {
+      beforeEach(() => {
+        const interaction = {
+          ...suggestedReviewersFixture.scenario,
+          ...suggestedReviewersFixture.request,
+          willRespondWith: suggestedReviewersFixture.success,
+        };
+        provider.addInteraction(interaction);
+      });
+
+      it('return a successful body', async () => {
+        const suggestedReviewers = await getSuggestedReviewers({
+          url: provider.mockService.baseUrl,
+        });
+
+        expect(suggestedReviewers).toEqual(suggestedReviewersFixture.body);
+      });
+    });
+  },
+);
diff --git a/ee/spec/contracts/consumer/test_constants.js b/ee/spec/contracts/consumer/test_constants.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1db52945baffbba40704ef0fe232f1dc44109c5
--- /dev/null
+++ b/ee/spec/contracts/consumer/test_constants.js
@@ -0,0 +1,3 @@
+export const AUTOCOMPLETE_USERS_URL = '/-/autocomplete/users.json';
+export const TEST_PROJECT_ID = '12345';
+export const TEST_MERGE_REQUEST_IID = '54321';
diff --git a/ee/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_suggested_reviewers_endpoint.json b/ee/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_suggested_reviewers_endpoint.json
new file mode 100644
index 0000000000000000000000000000000000000000..e52945acb6bb27b32700facbe88a92a57d9f1816
--- /dev/null
+++ b/ee/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_suggested_reviewers_endpoint.json
@@ -0,0 +1,77 @@
+{
+  "consumer": {
+    "name": "MergeRequest#show"
+  },
+  "provider": {
+    "name": "Merge Request Suggested Reviewers Endpoint"
+  },
+  "interactions": [
+    {
+      "description": "a request for suggested reviewers",
+      "providerState": "a merge request exists with suggested reviewers available for selection",
+      "request": {
+        "method": "GET",
+        "path": "/-/autocomplete/users.json",
+        "query": "active=true&project_id=12345&merge_request_iid=54321&current_user=true",
+        "headers": {
+          "Accept": "*/*"
+        },
+        "matchingRules": {
+          "$.query.project_id[0]": {
+            "match": "type"
+          },
+          "$.query.merge_request_iid[0]": {
+            "match": "type"
+          }
+        }
+      },
+      "response": {
+        "status": 200,
+        "headers": {
+          "Content-Type": "application/json; charset=utf-8"
+        },
+        "body": [
+          {
+            "id": 6954442,
+            "username": "user1",
+            "name": "A User"
+          },
+          {
+            "id": 6954441,
+            "username": "gitlab-qa",
+            "name": "Contract Test User",
+            "suggested": true
+          }
+        ],
+        "matchingRules": {
+          "$.body[0].id": {
+            "match": "type"
+          },
+          "$.body[0].username": {
+            "match": "type"
+          },
+          "$.body[0].name": {
+            "match": "type"
+          },
+          "$.body[1].id": {
+            "match": "type"
+          },
+          "$.body[1].username": {
+            "match": "type"
+          },
+          "$.body[1].name": {
+            "match": "type"
+          },
+          "$.body[1].suggested": {
+            "match": "type"
+          }
+        }
+      }
+    }
+  ],
+  "metadata": {
+    "pactSpecification": {
+      "version": "2.0.0"
+    }
+  }
+}
\ No newline at end of file
diff --git a/ee/spec/contracts/provider/pact_helpers/project/merge_request/show/suggested_reviewers_helper.rb b/ee/spec/contracts/provider/pact_helpers/project/merge_request/show/suggested_reviewers_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b0ad789aa8628db6f3e5094176fb4c6eece23786
--- /dev/null
+++ b/ee/spec/contracts/provider/pact_helpers/project/merge_request/show/suggested_reviewers_helper.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_relative '../../../../states/project/merge_request/show_state'
+require_relative '../../../../../../../../spec/contracts/provider/spec_helper'
+require_relative '../../../../../../../../spec/contracts/provider/environments/test'
+
+module Provider
+  module SuggestedReviewersHelper
+    Pact.service_provider "Merge Request Suggested Reviewers Endpoint" do
+      app { Environments::Test.app }
+
+      honours_pact_with 'MergeRequest#show' do
+        pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_suggested_reviewers_endpoint.json' # rubocop:disable Layout/LineLength
+      end
+    end
+  end
+end
diff --git a/ee/spec/contracts/provider/spec_helper.rb b/ee/spec/contracts/provider/spec_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ea703683987c6be6d4e033681459653fc26a1e22
--- /dev/null
+++ b/ee/spec/contracts/provider/spec_helper.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require Rails.root.join("config/initializers/0_inject_enterprise_edition_module.rb")
+require Rails.root.join("ee/spec/support/helpers/ee/license_helpers.rb")
+require Rails.root.join("spec/support/helpers/license_helper.rb")
+
+Pact.configure do |config|
+  config.include LicenseHelpers
+end
diff --git a/ee/spec/contracts/provider/states/project/merge_request/show_state.rb b/ee/spec/contracts/provider/states/project/merge_request/show_state.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7c67af8fce064288e37cdccdf84573be39afaedc
--- /dev/null
+++ b/ee/spec/contracts/provider/states/project/merge_request/show_state.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+Pact.provider_states_for "MergeRequest#show" do
+  provider_state "a merge request exists with suggested reviewers available for selection" do
+    set_up do
+      # Suggested Reviewers is a SaaS feature, but we can't use the `:saas` RSpec metadata like we do in other specs
+      allow(::Gitlab).to receive(:com?).and_return(true)
+
+      stub_licensed_features(suggested_reviewers: true)
+      stub_feature_flags(suggested_reviewers_control: true)
+
+      user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+      namespace = create(:namespace, name: 'gitlab-org')
+      project = create(:project, id: 12345, name: 'gitlab-qa', namespace: namespace)
+      project.add_maintainer(user)
+      project.project_setting.update!(suggested_reviewers_enabled: true)
+      merge_request = create(:merge_request, iid: 54321, source_project: project, author: user)
+
+      merge_request.build_predictions
+      merge_request.predictions.update!(suggested_reviewers: { reviewers: [user.username] })
+    end
+  end
+end
diff --git a/jest.config.base.js b/jest.config.base.js
index de9bff774e18b218c8b056b9df86790f8faf9a2b..05967b51b88d51e5286c069d7c0416dd2c9ddb3d 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -26,7 +26,7 @@ module.exports = (path, options = {}) => {
     ]);
   }
 
-  const glob = `${path}/**/*_spec.js`;
+  const glob = `${path}/**/*@([._])spec.js`;
   let testMatch = [`<rootDir>/${glob}`];
   if (IS_EE) {
     testMatch.push(`<rootDir>/ee/${glob}`);
diff --git a/jest.config.contract.js b/jest.config.contract.js
new file mode 100644
index 0000000000000000000000000000000000000000..224d50f87d657913099b859b1014fdebc54528f4
--- /dev/null
+++ b/jest.config.contract.js
@@ -0,0 +1,6 @@
+module.exports = () => {
+  return {
+    modulePaths: ['<rootDir>/spec/contracts/consumer/node_modules/'],
+    roots: ['spec/contracts/consumer', 'ee/spec/contracts/consumer'],
+  };
+};
diff --git a/package.json b/package.json
index f1fe4b54d933051dd11e0d60ba1d8d8becd381cb..8b8af78c47af1030a857149c09297805469f6238 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
     "jest-debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
     "jest:ci": "jest --config jest.config.js --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
     "jest:ci:minimal": "jest --config jest.config.js --ci --coverage --findRelatedTests $(cat $RSPEC_CHANGED_FILES_PATH) --passWithNoTests --testSequencer ./scripts/frontend/parallel_ci_sequencer.js",
+    "jest:contract": "PACT_DO_NOT_TRACK=true jest --config jest.config.contract.js --runInBand",
     "jest:integration": "jest --config jest.config.integration.js",
     "lint:eslint": "node scripts/frontend/eslint.js",
     "lint:eslint:fix": "node scripts/frontend/eslint.js --fix",
diff --git a/spec/contracts/consumer/.node-version b/spec/contracts/consumer/.node-version
deleted file mode 100644
index 18711d290eac4daf7696dd86d5b0a0a8475d5754..0000000000000000000000000000000000000000
--- a/spec/contracts/consumer/.node-version
+++ /dev/null
@@ -1 +0,0 @@
-14.17.5
diff --git a/spec/contracts/consumer/package.json b/spec/contracts/consumer/package.json
index 6d3feaa6d4c870e01f5da36c55731d7da232bc34..60f268806de19715655343a9aa7b36105ab17c33 100644
--- a/spec/contracts/consumer/package.json
+++ b/spec/contracts/consumer/package.json
@@ -22,5 +22,8 @@
   "devDependencies": {
     "@babel/preset-env": "^7.18.2",
     "babel-jest": "^28.1.1"
+  },
+  "config": {
+    "pact_do_not_track": true
   }
 }
diff --git a/spec/contracts/provider/spec_helper.rb b/spec/contracts/provider/spec_helper.rb
index 6009d6524e115734bbeb7a0113b40ce73b695a9d..44e4d29c18ea7b0874258481e4fa29a27d3c877c 100644
--- a/spec/contracts/provider/spec_helper.rb
+++ b/spec/contracts/provider/spec_helper.rb
@@ -3,6 +3,13 @@
 require 'spec_helper'
 require 'zeitwerk'
 require_relative 'helpers/users_helper'
+require_relative('../../../ee/spec/contracts/provider/spec_helper') if Gitlab.ee?
+require Rails.root.join("spec/support/helpers/rails_helpers.rb")
+require Rails.root.join("spec/support/helpers/stub_env.rb")
+
+# Opt out of telemetry collection. We can't allow all engineers, and users who install GitLab from source, to be
+# automatically enrolled in sending data on their usage without their knowledge.
+ENV['PACT_DO_NOT_TRACK'] = 'true'
 
 RSpec.configure do |config|
   config.include Devise::Test::IntegrationHelpers
@@ -19,6 +26,8 @@
 
 Pact.configure do |config|
   config.include FactoryBot::Syntax::Methods
+  config.include RailsHelpers
+  config.include StubENV
 end
 
 module SpecHelper