Repository: grpc-ecosystem/grpc-gateway

Issue:
Unused method request/response message rendered in OpenAPI document
## 🐛 Bug Report

I'm trying to generate Swagger based OpenAPI docs for my grpc services, proxied to REST endpoints via `grpc-gateway`. Please see details below:

1. I have an accounts.proto whoch has two RPC services - `Add` and `Show`. I want to ONLY expose `Show`( GET `/v1/account`) which is achieved by using annotation `google.api.http` and not using teh same on `Add`.

**accounts.proto**

```
syntax = "proto3";

package account;

import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";

option go_package = "accounts/pkg/account;account";

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
  info: {
    title: "Account Service";
    version: "1.0";
    contact: {
      name: "some-name";
      url: "https://github.com/some-name";
      email: "some-name@example.com";
    };
  };
  schemes: [HTTP, HTTPS];
  consumes: ["application/json"];
  produces: ["application/json"];
  responses: {
    key: "default";
    value: {
      description: "An unexpected error occurred.";
    }
  }
};


service AccountService {
  rpc Add(AddRequest) returns (AddResponse);
  rpc Show(ShowRequest) returns (ShowResponse) {
    option (google.api.http) = {
      get: "/v1/account"
    };
  }
}

message Money {
  option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
      json_schema: {
          title: "Money"
          description: "Money data structure"
      };
  };

  int64 amount = 1;
}


message AddRequest {
  Money money = 1;
}

message AddResponse {
}

message ShowRequest {
}

message ShowResponse {
  Money money = 1;
} 
```

2. But on the generated swagger.json, I can see unexpected messages in definitions

**swagger.json** (excerpt)

```
"definitions": {
    "accountAddResponse": {
      "type": "object"
    },
    "accountMoney": {
      "type": "object",
      "properties": {
        "amount": {
          "type": "string",
          "format": "int64"
        }
      },
      "description": "Money data structure",
      "title": "Money"
    },
    "accountShowResponse": {
      "type": "object",
      "properties": {
        "money": {
          "$ref": "#/definitions/accountMoney"
        }
      }
    },
    "protobufAny": {
      "type": "object",
      "properties": {
        "@type": {
          "type": "string"
        }
      },
      "additionalProperties": {}
    },
    "rpcStatus": {
      "type": "object",
      "properties": {
        "code": {
          "type": "integer",
          "format": "int32"
        },
        "message": {
          "type": "string"
        },
        "details": {
          "type": "array",
          "items": {
            "type": "object",
            "$ref": "#/definitions/protobufAny"
          }
        }
      }
    }
  }
```


## To Reproduce

I use `buf generate` to generate above file. See buf related files as below:

**buf.gen.yaml**

```
version: v1
plugins:
  - plugin: go
    out: gen/go/accounts
    opt:
      - paths=source_relative
  - plugin: go-grpc
    out: gen/go/accounts
    opt:
      - paths=source_relative
  - plugin: grpc-gateway
    out: gen/go/accounts
    opt:
      - paths=source_relative
  - plugin: openapiv2
    out: gen/go/accounts
    opt:
      - ignore_comments=true

```

**buf.yaml**

```
version: v1
deps:
  - buf.build/googleapis/googleapis
  - buf.build/grpc-ecosystem/grpc-gateway 
 
```

## Expected behavior

I get `accountAddResponse`(shouldn't be as Add is not exposed), `protobufAny` and `rpcStatus` as extra and no `accountShowRequest`

## Actual Behavior

I would have expected ONLY `accountShowRequest` and `accountShowResponse`.

## Your Environment

$ go version
go version go1.24.4 darwin/arm64

Is this a bug or am I missing some settings?



Discussion:
@johanbrandhorst: Hi, thanks for your bug report! I agree that I am surprised to see `accountAddResponse` in your rendered definition, but I don't necessarily think that `accountShowRequest` needs to show up since there are no actual fields in it. Do you think you could share an example repository that reproduces this scenario? I can't see what's wrong from just this snippet unfortunately.@boseabhishek: Thanks for your message @johanbrandhorst - I have created a minimal version now. 
https://github.com/boseabhishek/foo-accounts. The README is quite basic but has everything you need.@johanbrandhorst: Hi, thanks for the repo. I can't explain why `accountAddResponse` is showing up, but I did find a workaround. If you make use of our [visibility rules feature](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/customizing_openapi_output/#hiding-fields-methods-services-and-enum-values), you can hide the accountAdd method and its request and response message. See this diff:

```diff
diff --git a/buf.gen.yaml b/buf.gen.yaml
index 7f2f49a..53e297a 100644
--- a/buf.gen.yaml
+++ b/buf.gen.yaml
@@ -17,3 +17,4 @@ plugins:
     opt:
       - ignore_comments=true
       - disable_default_responses=true
+      - visibility_restriction_selectors=PUBLIC
diff --git a/gen/go/accounts/proto/account/account.swagger.json b/gen/go/accounts/proto/account/account.swagger.json
index 17ff308..758da50 100644
--- a/gen/go/accounts/proto/account/account.swagger.json
+++ b/gen/go/accounts/proto/account/account.swagger.json
@@ -49,9 +43,6 @@
     }
   },
   "definitions": {
-    "accountAddResponse": {
-      "type": "object"
-    },
     "accountMoney": {
       "type": "object",
       "properties": {
diff --git a/proto/account/account.proto b/proto/account/account.proto
index 52953f4..accb22d 100644
--- a/proto/account/account.proto
+++ b/proto/account/account.proto
@@ -3,6 +3,7 @@ syntax = "proto3";
 package account;
 
 import "google/api/annotations.proto";
+import "google/api/visibility.proto";
 import "protoc-gen-openapiv2/options/annotations.proto";
 
 option go_package = "accounts/pkg/account;account";
@@ -30,7 +31,9 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
 
 
 service AccountService {
-  rpc Add(AddRequest) returns (AddResponse);
+  rpc Add(AddRequest) returns (AddResponse) {
+    option (google.api.method_visibility).restriction = "INTERNAL";
+  }
   rpc Show(ShowRequest) returns (ShowResponse) {
     option (google.api.http) = {
       get: "/v1/account"
```

The reason you're seeing `protobufAny` and `rpcStatus` is because they are the default error response message type. If you don't want them, I think you can try setting `disable_default_errors=true`.

I still think there's probably a bug here -  `accountAddResponse` is showing up in the definitions when there is no method requiring it. With your reproducer it should be possible for someone to pick this up and work on it. Hopefully the workaround will work for you in the meantime.@boseabhishek: Many thanks @johanbrandhorst  - the workaround does a great job. I have pushed a commit for ref on that repo.@iamrajiv: hi @johanbrandhorst @boseabhishek i raised a pr to fix this issue i think adding a check that only includes request and response message definitions for methods with http bindings and skips methods without google.api.http annotations during openapi generation should solve the issue can you review #6030

Solution patch:
diff --git a/.github/Dockerfile b/.github/Dockerfile
index 067743df..311cfbaf 100644
--- a/.github/Dockerfile
+++ b/.github/Dockerfile
@@ -1,10 +1,10 @@
-FROM golang:1.25.3
+FROM golang:1.25.4
 
 RUN apt-get update && \
     DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \
         wget \
         unzip \
-        openjdk-17-jre \
+        openjdk-25-jre \
         bzip2 \
         patch && \
     apt-get clean -y && \
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aeef467b..b54c375c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,27 +11,27 @@ jobs:
         os: [ubuntu-latest, macos-latest, windows-latest]
     runs-on: ${{ matrix.os }}
     steps:
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           go-version: ${{ matrix.go-version }}
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
       - run: go build ./...
   test:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           check-latest: true
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
       - run: go test ./...
   node_test:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
       - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
         with:
           node-version: 10
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           check-latest: true
       - run: >
@@ -44,7 +44,7 @@ jobs:
       image: ghcr.io/grpc-ecosystem/grpc-gateway/build-env:latest
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
         # Required with newer versions of Git
         # https://github.com/actions/checkout/issues/766
       - run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
@@ -58,7 +58,7 @@ jobs:
       image: ghcr.io/grpc-ecosystem/grpc-gateway/build-env:latest
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
       - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
         with:
           path: /home/vscode/.cache/_grpc_gateway_bazel
@@ -91,8 +91,8 @@ jobs:
   gorelease:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           go-version: 1.25
           check-latest: true
@@ -100,8 +100,8 @@ jobs:
   proto_lint:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           check-latest: true
       - run: make install
@@ -112,8 +112,8 @@ jobs:
   lint:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           go-version: 1.25
           check-latest: true
@@ -138,7 +138,7 @@ jobs:
           dry-run: false
           language: go
       - name: Upload Crash
-        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
+        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
         if: failure() && steps.build.outcome == 'success'
         with:
           name: artifacts
diff --git a/.github/workflows/devcontainer.yml b/.github/workflows/devcontainer.yml
index 0a25d4c0..e5a001cf 100644
--- a/.github/workflows/devcontainer.yml
+++ b/.github/workflows/devcontainer.yml
@@ -12,7 +12,7 @@ jobs:
   rebuild:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
       - name: Login to GitHub Container Registry
         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
         with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9813b430..cf213a68 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,10 +17,10 @@ jobs:
     permissions:
       contents: write
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
         with:
           fetch-depth: 0
-      - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6
+      - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
         with:
           check-latest: true
       - name: Generate goreleaser args
diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml
index ec9338c5..ee865a5a 100644
--- a/.github/workflows/renovate.yml
+++ b/.github/workflows/renovate.yml
@@ -11,7 +11,7 @@ jobs:
       image: ghcr.io/grpc-ecosystem/grpc-gateway/build-env:latest
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
         with:
           fetch-depth: 0
           token: ${{ secrets.GH_PUSH_TOKEN }}
@@ -52,7 +52,7 @@ jobs:
       # git conflicts
       - update_repositoriesbzl
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
+      - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
         with:
           fetch-depth: 0
           token: ${{ secrets.GH_PUSH_TOKEN }}
diff --git a/MODULE.bazel b/MODULE.bazel
index b2317d4a..cd74198e 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -6,21 +6,21 @@ module(
 )
 
 # Bazel Central Registry modules.
-bazel_dep(name = "bazel_features", version = "1.37.0")
+bazel_dep(name = "bazel_features", version = "1.38.0")
 bazel_dep(name = "rules_license", version = "1.0.0")
-bazel_dep(name = "rules_python", version = "1.6.3")
+bazel_dep(name = "rules_python", version = "1.7.0")
 bazel_dep(name = "rules_proto", version = "7.1.0")
-bazel_dep(name = "rules_go", version = "0.57.0", repo_name = "io_bazel_rules_go")
+bazel_dep(name = "rules_go", version = "0.59.0", repo_name = "io_bazel_rules_go")
 bazel_dep(name = "rules_shell", version = "0.6.1")
-bazel_dep(name = "gazelle", version = "0.45.0", repo_name = "bazel_gazelle")
+bazel_dep(name = "gazelle", version = "0.47.0", repo_name = "bazel_gazelle")
 bazel_dep(name = "bazel_skylib", version = "1.8.2")
-bazel_dep(name = "protobuf", version = "33.0", repo_name = "com_google_protobuf")
-bazel_dep(name = "googleapis", version = "0.0.0-20251003-2193a2bf")
+bazel_dep(name = "protobuf", version = "33.1", repo_name = "com_google_protobuf")
+bazel_dep(name = "googleapis", version = "0.0.0-20251111-659ea6e9")
 
 # This is required as a transitive dependency and not directly needed by this module.
 # We have this version pinned to solve for differences in the MODULE.bazel.lock file
 # when running CI.
-bazel_dep(name = "rules_rust", version = "0.66.0")
+bazel_dep(name = "rules_rust", version = "0.67.0")
 
 go_sdk = use_extension("@io_bazel_rules_go//go:extensions.bzl", "go_sdk")
 go_sdk.download(version = "1.24.0")
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 9f992947..ca01e42b 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -15,9 +15,9 @@
     "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/source.json": "d725d73707d01bb46ab3ca59ba408b8e9bd336642ca77a2269d4bfb8bbfd413d",
     "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896",
     "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85",
-    "https://bcr.bazel.build/modules/apple_support/1.23.0/MODULE.bazel": "317d47e3f65b580e7fb4221c160797fda48e32f07d2dfff63d754ef2316dcd25",
     "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442",
-    "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2",
+    "https://bcr.bazel.build/modules/apple_support/1.24.1/MODULE.bazel": "f46e8ddad60aef170ee92b2f3d00ef66c147ceafea68b6877cb45bd91737f5f8",
+    "https://bcr.bazel.build/modules/apple_support/1.24.1/source.json": "cf725267cbacc5f028ef13bb77e7f2c2e0066923a4dab1025e4a0511b1ed258a",
     "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b",
     "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
     "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
@@ -32,8 +32,8 @@
     "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
     "https://bcr.bazel.build/modules/bazel_features/1.32.0/MODULE.bazel": "095d67022a58cb20f7e20e1aefecfa65257a222c18a938e2914fd257b5f1ccdc",
     "https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6",
-    "https://bcr.bazel.build/modules/bazel_features/1.37.0/MODULE.bazel": "bd8f7e20ce3125d3507eb24e672f8db0b7da203572705fc8df6160a927bd8ff4",
-    "https://bcr.bazel.build/modules/bazel_features/1.37.0/source.json": "b90ae265a662942485cdc153d5adbad8696666092c31ecba3ea12923a739bceb",
+    "https://bcr.bazel.build/modules/bazel_features/1.38.0/MODULE.bazel": "f9b8a9c890ebd216b4049fd12a31d3c2602e3403c7af636b04fbbd7453edc9c9",
+    "https://bcr.bazel.build/modules/bazel_features/1.38.0/source.json": "31ba776c122b54a2885e23651642e32f087a87bf025465f8040751894b571277",
     "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
     "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
     "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
@@ -56,13 +56,13 @@
     "https://bcr.bazel.build/modules/gazelle/0.33.0/MODULE.bazel": "a13a0f279b462b784fb8dd52a4074526c4a2afe70e114c7d09066097a46b3350",
     "https://bcr.bazel.build/modules/gazelle/0.34.0/MODULE.bazel": "abdd8ce4d70978933209db92e436deb3a8b737859e9354fb5fd11fb5c2004c8a",
     "https://bcr.bazel.build/modules/gazelle/0.36.0/MODULE.bazel": "e375d5d6e9a6ca59b0cb38b0540bc9a05b6aa926d322f2de268ad267a2ee74c0",
-    "https://bcr.bazel.build/modules/gazelle/0.45.0/MODULE.bazel": "ecd19ebe9f8e024e1ccffb6d997cc893a974bcc581f1ae08f386bdd448b10687",
-    "https://bcr.bazel.build/modules/gazelle/0.45.0/source.json": "111d182facc5f5e80f0b823d5f077b74128f40c3fd2eccc89a06f34191bd3392",
+    "https://bcr.bazel.build/modules/gazelle/0.47.0/MODULE.bazel": "b61bb007c4efad134aa30ee7f4a8e2a39b22aa5685f005edaa022fbd1de43ebc",
+    "https://bcr.bazel.build/modules/gazelle/0.47.0/source.json": "aeb2e5df14b7fb298625d75d08b9c65bdb0b56014c5eb89da9e5dd0572280ae6",
     "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
     "https://bcr.bazel.build/modules/googleapis-rules-registry/1.0.0/MODULE.bazel": "97c6a4d413b373d4cc97065da3de1b2166e22cbbb5f4cc9f05760bfa83619e24",
     "https://bcr.bazel.build/modules/googleapis-rules-registry/1.0.0/source.json": "cf611c836a60e98e2e2ab2de8004f119e9f06878dcf4ea2d95a437b1b7a89fe9",
-    "https://bcr.bazel.build/modules/googleapis/0.0.0-20251003-2193a2bf/MODULE.bazel": "cc9e5ed294ed9ebf42cdbbdddd2df29048519e3797004df1e3f369f31ff4f2d4",
-    "https://bcr.bazel.build/modules/googleapis/0.0.0-20251003-2193a2bf/source.json": "21558a194c519e27262cca9cf031bb166a666a8b7fb89993b700c828f8dc0857",
+    "https://bcr.bazel.build/modules/googleapis/0.0.0-20251111-659ea6e9/MODULE.bazel": "b53ec3119f1f4a783830b55b3fb421557c071fd38324be30172ed09a6e3ddc31",
+    "https://bcr.bazel.build/modules/googleapis/0.0.0-20251111-659ea6e9/source.json": "e7b9aad8db64889d6f3b41c537610a67ea9c424b2f3192d5d584d9d2ae369579",
     "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
     "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
     "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
@@ -98,8 +98,8 @@
     "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
     "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573",
     "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
-    "https://bcr.bazel.build/modules/protobuf/33.0/MODULE.bazel": "c5270efb4aad37a2f893536076518793f409ea7df07a06df995d848d1690f21c",
-    "https://bcr.bazel.build/modules/protobuf/33.0/source.json": "cd7ac80ad863190b9151281a85acc11d77b5bde2ba56443a7215da2d4ace6da3",
+    "https://bcr.bazel.build/modules/protobuf/33.1/MODULE.bazel": "982c8a0cceab4d790076f72b7677faf836b0dfadc2b66a34aab7232116c4ae39",
+    "https://bcr.bazel.build/modules/protobuf/33.1/source.json": "992c237a40899425648213bf79b05f08c6e8dcd619f96cd944b4511b0276fbd8",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34",
     "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680",
@@ -123,16 +123,18 @@
     "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
     "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
     "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
+    "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8",
     "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc",
-    "https://bcr.bazel.build/modules/rules_cc/0.2.4/source.json": "2bd87ef9b41d4753eadf65175745737135cba0e70b479bdc204ef0c67404d0c4",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.8/MODULE.bazel": "f1df20f0bf22c28192a794f29b501ee2018fa37a3862a1a2132ae2940a23a642",
+    "https://bcr.bazel.build/modules/rules_cc/0.2.8/source.json": "85087982aca15f31307bd52698316b28faa31bd2c3095a41f456afec0131344c",
     "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
     "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
     "https://bcr.bazel.build/modules/rules_go/0.41.0/MODULE.bazel": "55861d8e8bb0e62cbd2896f60ff303f62ffcb0eddb74ecb0e5c0cbe36fc292c8",
     "https://bcr.bazel.build/modules/rules_go/0.42.0/MODULE.bazel": "8cfa875b9aa8c6fce2b2e5925e73c1388173ea3c32a0db4d2b4804b453c14270",
     "https://bcr.bazel.build/modules/rules_go/0.46.0/MODULE.bazel": "3477df8bdcc49e698b9d25f734c4f3a9f5931ff34ee48a2c662be168f5f2d3fd",
     "https://bcr.bazel.build/modules/rules_go/0.53.0/MODULE.bazel": "a4ed760d3ac0dbc0d7b967631a9a3fd9100d28f7d9fcf214b4df87d4bfff5f9a",
-    "https://bcr.bazel.build/modules/rules_go/0.57.0/MODULE.bazel": "bee44028b527cd6d1b7699a2c78714bba237b40ee21f90a83b472c94bc53159d",
-    "https://bcr.bazel.build/modules/rules_go/0.57.0/source.json": "a782b756d87c68a223a48848eda4b0dac1c5fd1d925d648d7598b68aa1fb6d6d",
+    "https://bcr.bazel.build/modules/rules_go/0.59.0/MODULE.bazel": "b7e43e7414a3139a7547d1b4909b29085fbe5182b6c58cbe1ed4c6272815aeae",
+    "https://bcr.bazel.build/modules/rules_go/0.59.0/source.json": "1df17bb7865cfc029492c30163cee891d0dd8658ea0d5bfdf252c4b6db5c1ef6",
     "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
     "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
     "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
@@ -183,13 +185,12 @@
     "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
     "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
     "https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8",
-    "https://bcr.bazel.build/modules/rules_python/1.6.3/MODULE.bazel": "a7b80c42cb3de5ee2a5fa1abc119684593704fcd2fec83165ebe615dec76574f",
-    "https://bcr.bazel.build/modules/rules_python/1.6.3/source.json": "f0be74977e5604a6526c8a416cda22985093ff7d5d380d41722d7e44015cc419",
-    "https://bcr.bazel.build/modules/rules_rust/0.66.0/MODULE.bazel": "86ef763a582f4739a27029bdcc6c562258ed0ea6f8d58294b049e215ceb251b3",
-    "https://bcr.bazel.build/modules/rules_rust/0.66.0/source.json": "5c2252a61ccc19b4e420c7c06429c8f51d8edd7b743dcb4b60571e7d40b5aa57",
+    "https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8",
+    "https://bcr.bazel.build/modules/rules_python/1.7.0/source.json": "028a084b65dcf8f4dc4f82f8778dbe65df133f234b316828a82e060d81bdce32",
+    "https://bcr.bazel.build/modules/rules_rust/0.67.0/MODULE.bazel": "87c3816c4321352dcfd9e9e26b58e84efc5b21351ae3ef8fb5d0d57bde7237f5",
+    "https://bcr.bazel.build/modules/rules_rust/0.67.0/source.json": "a8ef4d3be30eb98e060cad9e5875a55b603195487f76e01b619b51a1df4641cc",
     "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
     "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
-    "https://bcr.bazel.build/modules/rules_shell/0.4.0/MODULE.bazel": "0f8f11bb3cd11755f0b48c1de0bbcf62b4b34421023aa41a2fc74ef68d9584f0",
     "https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b",
     "https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c",
     "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca",
@@ -305,10 +306,245 @@
         ]
       }
     },
+    "@@rules_python+//python/extensions:config.bzl%config": {
+      "general": {
+        "bzlTransitiveDigest": "xaCns8Qt+8bJqVLy8r6nc/eL2AjEIX/vOdjqoh5xYac=",
+        "usagesDigest": "ZVSXMAGpD+xzVNPuvF1IoLBkty7TROO0+akMapt1pAg=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "rules_python_internal": {
+            "repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo",
+            "attributes": {
+              "transition_setting_generators": {},
+              "transition_settings": []
+            }
+          },
+          "pypi__build": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl",
+              "sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__click": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl",
+              "sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__colorama": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl",
+              "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__importlib_metadata": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl",
+              "sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__installer": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl",
+              "sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__more_itertools": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl",
+              "sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__packaging": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl",
+              "sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__pep517": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl",
+              "sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__pip": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl",
+              "sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__pip_tools": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl",
+              "sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__pyproject_hooks": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl",
+              "sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__setuptools": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl",
+              "sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__tomli": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl",
+              "sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__wheel": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl",
+              "sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          },
+          "pypi__zipp": {
+            "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
+            "attributes": {
+              "url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl",
+              "sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e",
+              "type": "zip",
+              "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n    name = \"lib\",\n    srcs = glob([\"**/*.py\"]),\n    data = glob([\"**/*\"], exclude=[\n        # These entries include those put into user-installed dependencies by\n        # data_exclude to avoid non-determinism.\n        \"**/*.py\",\n        \"**/*.pyc\",\n        \"**/*.pyc.*\",  # During pyc creation, temp files named *.pyc.NNN are created\n        \"**/*.dist-info/RECORD\",\n        \"BUILD\",\n        \"WORKSPACE\",\n    ]),\n    # This makes this directory a top-level in the python import\n    # search path for anything that depends on this.\n    imports = [\".\"],\n)\n"
+            }
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "rules_python+",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "rules_python+",
+            "pypi__build",
+            "rules_python++config+pypi__build"
+          ],
+          [
+            "rules_python+",
+            "pypi__click",
+            "rules_python++config+pypi__click"
+          ],
+          [
+            "rules_python+",
+            "pypi__colorama",
+            "rules_python++config+pypi__colorama"
+          ],
+          [
+            "rules_python+",
+            "pypi__importlib_metadata",
+            "rules_python++config+pypi__importlib_metadata"
+          ],
+          [
+            "rules_python+",
+            "pypi__installer",
+            "rules_python++config+pypi__installer"
+          ],
+          [
+            "rules_python+",
+            "pypi__more_itertools",
+            "rules_python++config+pypi__more_itertools"
+          ],
+          [
+            "rules_python+",
+            "pypi__packaging",
+            "rules_python++config+pypi__packaging"
+          ],
+          [
+            "rules_python+",
+            "pypi__pep517",
+            "rules_python++config+pypi__pep517"
+          ],
+          [
+            "rules_python+",
+            "pypi__pip",
+            "rules_python++config+pypi__pip"
+          ],
+          [
+            "rules_python+",
+            "pypi__pip_tools",
+            "rules_python++config+pypi__pip_tools"
+          ],
+          [
+            "rules_python+",
+            "pypi__pyproject_hooks",
+            "rules_python++config+pypi__pyproject_hooks"
+          ],
+          [
+            "rules_python+",
+            "pypi__setuptools",
+            "rules_python++config+pypi__setuptools"
+          ],
+          [
+            "rules_python+",
+            "pypi__tomli",
+            "rules_python++config+pypi__tomli"
+          ],
+          [
+            "rules_python+",
+            "pypi__wheel",
+            "rules_python++config+pypi__wheel"
+          ],
+          [
+            "rules_python+",
+            "pypi__zipp",
+            "rules_python++config+pypi__zipp"
+          ]
+        ]
+      }
+    },
     "@@rules_python+//python/uv:uv.bzl%uv": {
       "general": {
-        "bzlTransitiveDigest": "PmZM/pIkZKEDDL68TohlKJrWPYKL5VwUw3MA7kmm6fk=",
-        "usagesDigest": "icnInV8HDGrRQf9x8RMfxWfBHgT3OgRlYovS/9POEJw=",
+        "bzlTransitiveDigest": "N8SCcKcL6KnzBLApxvY2jR9vhXjA2VCBZMLZfY3sDRA=",
+        "usagesDigest": "H8dQoNZcoqP+Mu0tHZTi4KHATzvNkM5ePuEqoQdklIU=",
         "recordedFileInputs": {},
         "recordedDirentsInputs": {},
         "envVariables": {},
diff --git a/WORKSPACE b/WORKSPACE
index ebb80c21..76bad367 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -5,9 +5,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
 
 http_archive(
     name = "bazel_features",
-    sha256 = "adc8ddf121917f197f75c5245dfa8d7b1619f10a1002e25062b093b7957f2798",
-    strip_prefix = "bazel_features-1.37.0",
-    url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.37.0/bazel_features-v1.37.0.tar.gz",
+    sha256 = "07271d0f6b12633777b69020c4cb1eb67b1939c0cf84bb3944dc85cc250c0c01",
+    strip_prefix = "bazel_features-1.38.0",
+    url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.38.0/bazel_features-v1.38.0.tar.gz",
 )
 
 load("@bazel_features//:deps.bzl", "bazel_features_deps")
@@ -16,9 +16,9 @@ bazel_features_deps()
 
 http_archive(
     name = "rules_python",
-    sha256 = "2f5c284fbb4e86045c2632d3573fc006facbca5d1fa02976e89dc0cd5488b590",
-    strip_prefix = "rules_python-1.6.3",
-    url = "https://github.com/bazelbuild/rules_python/releases/download/1.6.3/rules_python-1.6.3.tar.gz",
+    sha256 = "f609f341d6e9090b981b3f45324d05a819fd7a5a56434f849c761971ce2c47da",
+    strip_prefix = "rules_python-1.7.0",
+    url = "https://github.com/bazelbuild/rules_python/releases/download/1.7.0/rules_python-1.7.0.tar.gz",
 )
 
 load("@rules_python//python:repositories.bzl", "py_repositories")
@@ -35,17 +35,17 @@ http_archive(
 # Define before rules_proto, otherwise we receive the version of com_google_protobuf from there
 http_archive(
     name = "com_google_protobuf",
-    sha256 = "b6b03fbaa3a90f3d4f2a3fa4ecc41d7cd0326f92fcc920a7843f12206c8d52cd",
-    strip_prefix = "protobuf-33.0",
-    urls = ["https://github.com/protocolbuffers/protobuf/archive/v33.0.tar.gz"],
+    sha256 = "0c98bb704ceb4e68c92f93907951ca3c36130bc73f87264e8c0771a80362ac97",
+    strip_prefix = "protobuf-33.1",
+    urls = ["https://github.com/protocolbuffers/protobuf/archive/v33.1.tar.gz"],
 )
 
 http_archive(
     name = "googleapis",
-    sha256 = "fd80247a458ce00f1a6d2fa4bd3a5677dcd5cae68408f8ec6786b21817435134",
-    strip_prefix = "googleapis-b61fb21bbac838de7f55cf2c60d371a47fc2799e",
+    sha256 = "a7fcd63679a62be91e3467596d036bf61e3e6337fd036a5886fe10edadcb8383",
+    strip_prefix = "googleapis-8cd3749f4b98f2eeeef511c16431979aeb3a6502",
     urls = [
-        "https://github.com/googleapis/googleapis/archive/b61fb21bbac838de7f55cf2c60d371a47fc2799e.zip",
+        "https://github.com/googleapis/googleapis/archive/8cd3749f4b98f2eeeef511c16431979aeb3a6502.zip",
     ],
 )
 
@@ -87,10 +87,10 @@ rules_proto_toolchains()
 
 http_archive(
     name = "io_bazel_rules_go",
-    sha256 = "a729c8ed2447c90fe140077689079ca0acfb7580ec41637f312d650ce9d93d96",
+    sha256 = "68af54cb97fbdee5e5e8fe8d210d15a518f9d62abfd71620c3eaff3b26a5ff86",
     urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.57.0/rules_go-v0.57.0.zip",
-        "https://github.com/bazelbuild/rules_go/releases/download/v0.57.0/rules_go-v0.57.0.zip",
+        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.59.0/rules_go-v0.59.0.zip",
+        "https://github.com/bazelbuild/rules_go/releases/download/v0.59.0/rules_go-v0.59.0.zip",
     ],
 )
 
@@ -102,10 +102,10 @@ go_register_toolchains(version = "1.24.0")
 
 http_archive(
     name = "bazel_gazelle",
-    sha256 = "e467b801046b6598c657309b45d2426dc03513777bd1092af2c62eebf990aca5",
+    sha256 = "675114d8b433d0a9f54d81171833be96ebc4113115664b791e6f204d58e93446",
     urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.45.0/bazel-gazelle-v0.45.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.45.0/bazel-gazelle-v0.45.0.tar.gz",
+        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.47.0/bazel-gazelle-v0.47.0.tar.gz",
+        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.47.0/bazel-gazelle-v0.47.0.tar.gz",
     ],
 )
 
diff --git a/docs/docs/mapping/customizing_openapi_output.md b/docs/docs/mapping/customizing_openapi_output.md
index 1ff35226..7a81ef76 100644
--- a/docs/docs/mapping/customizing_openapi_output.md
+++ b/docs/docs/mapping/customizing_openapi_output.md
@@ -861,6 +861,99 @@ definitions:
         type: string
 ```
 
+### Omit array item type when $ref is a sibling
+
+By default, when generating OpenAPI schemas for array items that reference other definitions, both `type: object` and `$ref` properties are included. However, some strict OpenAPI validators enforce the `no-$ref-siblings` rule, which prohibits having `$ref` alongside other properties at the same level. To comply with this strict validation, you can use the `omit_array_item_type_when_ref_sibling` option. When enabled, this option omits the `type: object` property from array items when a `$ref` is present, since `$ref` already implies the type. Allowed values are: `true`, `false`.
+
+**Note**: This option only affects the generated OpenAPI specification and does not alter the behavior of the gateway itself.
+
+For example, if you are using `buf`:
+
+```yaml
+version: v1
+plugins:
+  - name: openapiv2
+    out: .
+    opt:
+      - omit_array_item_type_when_ref_sibling=true
+```
+
+or with `protoc`
+
+```sh
+protoc --openapiv2_out=. --openapiv2_opt=omit_array_item_type_when_ref_sibling=true ./path/to/file.proto
+```
+
+Input example:
+
+```protobuf
+syntax = "proto3";
+
+package example.v1;
+
+import "google/api/annotations.proto";
+
+option go_package = "example/v1;example";
+
+service ExampleService {
+  rpc GetItems(GetItemsRequest) returns (GetItemsResponse) {
+    option (google.api.http) = {get: "/api/items"};
+  }
+}
+
+message GetItemsRequest {}
+
+message GetItemsResponse {
+  repeated Item items = 1;
+}
+
+message Item {
+  string id = 1;
+  string name = 2;
+}
+```
+
+Output without the option (default behavior):
+
+```yaml
+definitions:
+  v1GetItemsResponse:
+    type: object
+    properties:
+      items:
+        type: array
+        items:
+          type: object  # This can violate strict no-$ref-siblings rule
+          $ref: "#/definitions/v1Item"
+  v1Item:
+    type: object
+    properties:
+      id:
+        type: string
+      name:
+        type: string
+```
+
+Output with `omit_array_item_type_when_ref_sibling=true`:
+
+```yaml
+definitions:
+  v1GetItemsResponse:
+    type: object
+    properties:
+      items:
+        type: array
+        items:
+          $ref: "#/definitions/v1Item"  # type: object is omitted
+  v1Item:
+    type: object
+    properties:
+      id:
+        type: string
+      name:
+        type: string
+```
+
 ### Custom HTTP Header Request Parameters
 
 By default the parameters for each operation are generated from the protocol buffer definition however you can extend the parameters to include extra HTTP headers if required.
@@ -1114,6 +1207,88 @@ definitions:
         type: string
 ```
 
+### Deprecating parameters
 
-{% endraw %}
+With the `enable_field_deprecation` option, OpenAPI parameters will be marked as deprecated based on the protobuf `deprecated` field option. Allowed values are: `true`, `false`.
+
+For example, if you are using `buf`:
+
+```yaml
+version: v1
+plugins:
+  - name: openapiv2
+    out: .
+    opt:
+      - enable_field_deprecation=true
+```
+
+or with `protoc`
 
+```sh
+protoc --openapiv2_out=. --openapiv2_opt=enable_field_deprecation=true ./path/to/file.proto
+```
+
+Input example:
+
+```protobuf
+message SearchRequest {
+  string legacy_filter = 1 [deprecated = true];
+  string query = 2;
+}
+```
+
+Output (excerpt):
+
+```yaml
+paths:
+  /v1/search:
+    get:
+      parameters:
+        - name: legacy_filter
+          in: query
+          required: false
+          type: string
+          deprecated: true
+        - name: query
+          in: query
+          required: false
+          type: string
+```
+
+If you prefer to leave the protobuf definition active, you can use the `field_configuration.deprecated` annotation on the `openapiv2_field` option instead:
+
+```protobuf
+message SearchRequest {
+  string legacy_filter = 1 [
+    (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+      description: "Legacy filter syntax. Prefer the new 'query' field."
+      field_configuration: { deprecated: true }
+    }
+  ];
+  string query = 2;
+}
+```
+
+This keeps the protobuf field untouched but still emits a deprecated parameter in the generated spec:
+
+```yaml
+paths:
+  /v1/search:
+    get:
+      parameters:
+        - name: legacy_filter
+          in: query
+          required: false
+          type: string
+          description: Legacy filter syntax. Prefer the new 'query' field.
+          deprecated: true
+        - name: query
+          in: query
+          required: false
+          type: string
+```
+
+If you set both the protobuf `deprecated = true` option and `field_configuration.deprecated`, the OpenAPI parameter is marked as deprecated regardless of the `enable_field_deprecation` option.
+
+
+{% endraw %}
diff --git a/examples/internal/clients/abe/api/swagger.yaml b/examples/internal/clients/abe/api/swagger.yaml
index 79cf4077..4a8c82cd 100644
--- a/examples/internal/clients/abe/api/swagger.yaml
+++ b/examples/internal/clients/abe/api/swagger.yaml
@@ -6500,6 +6500,15 @@ definitions:
     description: "Nested is nested type."
     example: "{\"ok\":\"TRUE\"}"
   A bit of everything_2:
+    required:
+    - "doubleValue"
+    - "floatValue"
+    - "int64Value"
+    - "requiredStringField1"
+    - "requiredStringField2"
+    - "requiredStringViaFieldBehaviorAnnotation"
+    - "required_field_behavior_json_name_custom"
+    - "required_field_schema_json_name_custom"
     properties:
       singleNested:
         $ref: "#/definitions/ABitOfEverythingNested"
diff --git a/examples/internal/clients/abe/model_a_bit_of_everything_2.go b/examples/internal/clients/abe/model_a_bit_of_everything_2.go
index 62c5847f..e6db0240 100644
--- a/examples/internal/clients/abe/model_a_bit_of_everything_2.go
+++ b/examples/internal/clients/abe/model_a_bit_of_everything_2.go
@@ -19,9 +19,9 @@ type ABitOfEverything2 struct {
 	SingleNested *ABitOfEverythingNested `json:"singleNested,omitempty"`
 	Nested []ABitOfEverythingNested `json:"nested,omitempty"`
 	// Float value field
-	FloatValue float32 `json:"floatValue,omitempty"`
-	DoubleValue float64 `json:"doubleValue,omitempty"`
-	Int64Value string `json:"int64Value,omitempty"`
+	FloatValue float32 `json:"floatValue"`
+	DoubleValue float64 `json:"doubleValue"`
+	Int64Value string `json:"int64Value"`
 	Uint64Value string `json:"uint64Value,omitempty"`
 	Int32Value int32 `json:"int32Value,omitempty"`
 	Fixed64Value string `json:"fixed64Value,omitempty"`
@@ -58,16 +58,16 @@ type ABitOfEverything2 struct {
 	// Nested object description.
 	NestedAnnotation *ABitOfEverythingNested `json:"nestedAnnotation,omitempty"`
 	Int64OverrideType int64 `json:"int64OverrideType,omitempty"`
-	RequiredStringViaFieldBehaviorAnnotation string `json:"requiredStringViaFieldBehaviorAnnotation,omitempty"`
+	RequiredStringViaFieldBehaviorAnnotation string `json:"requiredStringViaFieldBehaviorAnnotation"`
 	OutputOnlyStringViaFieldBehaviorAnnotation string `json:"outputOnlyStringViaFieldBehaviorAnnotation,omitempty"`
 	OptionalStringValue string `json:"optionalStringValue,omitempty"`
 	// Only digits are allowed.
 	ProductId []string `json:"productId,omitempty"`
 	OptionalStringField string `json:"optionalStringField,omitempty"`
-	RequiredStringField1 string `json:"requiredStringField1,omitempty"`
-	RequiredStringField2 string `json:"requiredStringField2,omitempty"`
-	RequiredFieldBehaviorJsonNameCustom string `json:"required_field_behavior_json_name_custom,omitempty"`
-	RequiredFieldSchemaJsonNameCustom string `json:"required_field_schema_json_name_custom,omitempty"`
+	RequiredStringField1 string `json:"requiredStringField1"`
+	RequiredStringField2 string `json:"requiredStringField2"`
+	RequiredFieldBehaviorJsonNameCustom string `json:"required_field_behavior_json_name_custom"`
+	RequiredFieldSchemaJsonNameCustom string `json:"required_field_schema_json_name_custom"`
 	TrailingOnly string `json:"trailingOnly,omitempty"`
 	// Trailing only dot.
 	TrailingOnlyDot string `json:"trailingOnlyDot,omitempty"`
diff --git a/examples/internal/proto/examplepb/a_bit_of_everything.swagger.json b/examples/internal/proto/examplepb/a_bit_of_everything.swagger.json
index d6a1cc5e..018b6e80 100644
--- a/examples/internal/proto/examplepb/a_bit_of_everything.swagger.json
+++ b/examples/internal/proto/examplepb/a_bit_of_everything.swagger.json
@@ -5071,10 +5071,7 @@
                 }
               },
               "description": "The book's `name` field is used to identify the book to be updated.\nFormat: publishers/{publisher}/books/{book}",
-              "title": "The book to update.",
-              "required": [
-                "book"
-              ]
+              "title": "The book to update."
             }
           },
           {
@@ -7049,6 +7046,16 @@
             "description": "Find out more about ABitOfEverything",
             "url": "https://github.com/grpc-ecosystem/grpc-gateway"
           },
+          "required": [
+            "int64Value",
+            "doubleValue",
+            "required_field_schema_json_name_custom",
+            "floatValue",
+            "requiredStringViaFieldBehaviorAnnotation",
+            "requiredStringField1",
+            "requiredStringField2",
+            "required_field_behavior_json_name_custom"
+          ],
           "x-a-bit-of-everything-foo": "bar"
         },
         "updateMask": {
@@ -7056,17 +7063,7 @@
           "description": "The paths to update."
         }
       },
-      "title": "UpdateV2Request request for update includes the message and the update mask",
-      "required": [
-        "int64Value",
-        "doubleValue",
-        "required_field_schema_json_name_custom",
-        "floatValue",
-        "requiredStringViaFieldBehaviorAnnotation",
-        "requiredStringField1",
-        "requiredStringField2",
-        "required_field_behavior_json_name_custom"
-      ]
+      "title": "UpdateV2Request request for update includes the message and the update mask"
     },
     "MessagePathEnumNestedPathEnum": {
       "type": "string",
diff --git a/go.mod b/go.mod
index b82fb0dd..3eb978b6 100644
--- a/go.mod
+++ b/go.mod
@@ -7,17 +7,17 @@ require (
 	github.com/google/go-cmp v0.7.0
 	github.com/rogpeppe/fastuuid v1.2.0
 	go.yaml.in/yaml/v3 v3.0.4
-	golang.org/x/oauth2 v0.32.0
-	golang.org/x/text v0.30.0
-	google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8
-	google.golang.org/grpc v1.76.0
+	golang.org/x/oauth2 v0.33.0
+	golang.org/x/text v0.31.0
+	google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846
+	google.golang.org/grpc v1.77.0
 	google.golang.org/protobuf v1.36.10
 )
 
 require (
 	github.com/kr/pretty v0.3.1 // indirect
-	golang.org/x/net v0.42.0 // indirect
-	golang.org/x/sys v0.34.0 // indirect
+	golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
+	golang.org/x/sys v0.37.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 )
diff --git a/go.sum b/go.sum
index ba7cd657..38ba94a4 100644
--- a/go.sum
+++ b/go.sum
@@ -23,36 +23,36 @@ github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
-go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
-go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
-go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
-go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
-go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
-go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
-go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
-go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
-go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
-go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
+go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
+go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
+go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
+go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
+go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
 go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
 go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
-golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
-golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
-golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
-golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
-golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
-golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
-golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
+golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
+golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
+golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
+golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
 gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
-google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
-google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
-google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
+google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 h1:ZdyUkS9po3H7G0tuh955QVyyotWvOD4W0aEapeGeUYk=
+google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846/go.mod h1:Fk4kyraUvqD7i5H6S43sj2W98fbZa75lpZz/eUyhfO0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
+google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
+google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
 google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
 google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/descriptor/registry.go b/internal/descriptor/registry.go
index 743cea8f..b057980a 100644
--- a/internal/descriptor/registry.go
+++ b/internal/descriptor/registry.go
@@ -157,6 +157,10 @@ type Registry struct {
 	// properties
 	useAllOfForRefs bool
 
+	// omitArrayItemTypeWhenRefSibling, if set, will omit 'type: object' in array items when $ref is present
+	// to avoid no-$ref-siblings violations in OpenAPI v2
+	omitArrayItemTypeWhenRefSibling bool
+
 	// allowPatchFeature determines whether to use PATCH feature involving update masks (using google.protobuf.FieldMask).
 	allowPatchFeature bool
 
@@ -167,6 +171,9 @@ type Registry struct {
 	// enableRpcDeprecation whether to process grpc method's deprecated option
 	enableRpcDeprecation bool
 
+	// enableFieldDeprecation whether to process proto field's deprecated option
+	enableFieldDeprecation bool
+
 	// expandSlashedPathPatterns, if true, for a path parameter carrying a sub-path, described via parameter pattern (i.e.
 	// the pattern contains forward slashes), this will expand the _pattern_ into the URI and will _replace_ the parameter
 	// with new path parameters inferred from patterns wildcards.
@@ -885,6 +892,16 @@ func (r *Registry) GetUseAllOfForRefs() bool {
 	return r.useAllOfForRefs
 }
 
+// SetOmitArrayItemTypeWhenRefSibling sets omitArrayItemTypeWhenRefSibling
+func (r *Registry) SetOmitArrayItemTypeWhenRefSibling(omit bool) {
+	r.omitArrayItemTypeWhenRefSibling = omit
+}
+
+// GetOmitArrayItemTypeWhenRefSibling returns omitArrayItemTypeWhenRefSibling
+func (r *Registry) GetOmitArrayItemTypeWhenRefSibling() bool {
+	return r.omitArrayItemTypeWhenRefSibling
+}
+
 // SetAllowPatchFeature sets allowPatchFeature
 func (r *Registry) SetAllowPatchFeature(allow bool) {
 	r.allowPatchFeature = allow
@@ -915,6 +932,16 @@ func (r *Registry) GetEnableRpcDeprecation() bool {
 	return r.enableRpcDeprecation
 }
 
+// SetEnableFieldDeprecation sets enableFieldDeprecation
+func (r *Registry) SetEnableFieldDeprecation(enable bool) {
+	r.enableFieldDeprecation = enable
+}
+
+// GetEnableFieldDeprecation returns enableFieldDeprecation
+func (r *Registry) GetEnableFieldDeprecation() bool {
+	return r.enableFieldDeprecation
+}
+
 func (r *Registry) SetExpandSlashedPathPatterns(expandSlashedPathPatterns bool) {
 	r.expandSlashedPathPatterns = expandSlashedPathPatterns
 }
diff --git a/protoc-gen-openapiv2/defs.bzl b/protoc-gen-openapiv2/defs.bzl
index 29e548c0..85d3dad8 100644
--- a/protoc-gen-openapiv2/defs.bzl
+++ b/protoc-gen-openapiv2/defs.bzl
@@ -74,8 +74,10 @@ def _run_proto_gen_openapi(
         generate_unbound_methods,
         visibility_restriction_selectors,
         use_allof_for_refs,
+        omit_array_item_type_when_ref_sibling,
         disable_default_responses,
         enable_rpc_deprecation,
+        enable_field_deprecation,
         expand_slashed_path_patterns,
         preserve_rpc_order,
         generate_x_go_type):
@@ -149,12 +151,18 @@ def _run_proto_gen_openapi(
     if use_allof_for_refs:
         args.add("--openapiv2_opt", "use_allof_for_refs=true")
 
+    if omit_array_item_type_when_ref_sibling:
+        args.add("--openapiv2_opt", "omit_array_item_type_when_ref_sibling=true")
+
     if disable_default_responses:
         args.add("--openapiv2_opt", "disable_default_responses=true")
 
     if enable_rpc_deprecation:
         args.add("--openapiv2_opt", "enable_rpc_deprecation=true")
 
+    if enable_field_deprecation:
+        args.add("--openapiv2_opt", "enable_field_deprecation=true")
+
     if expand_slashed_path_patterns:
         args.add("--openapiv2_opt", "expand_slashed_path_patterns=true")
 
@@ -269,8 +277,10 @@ def _proto_gen_openapi_impl(ctx):
                     generate_unbound_methods = ctx.attr.generate_unbound_methods,
                     visibility_restriction_selectors = ctx.attr.visibility_restriction_selectors,
                     use_allof_for_refs = ctx.attr.use_allof_for_refs,
+                    omit_array_item_type_when_ref_sibling = ctx.attr.omit_array_item_type_when_ref_sibling,
                     disable_default_responses = ctx.attr.disable_default_responses,
                     enable_rpc_deprecation = ctx.attr.enable_rpc_deprecation,
+                    enable_field_deprecation = ctx.attr.enable_field_deprecation,
                     expand_slashed_path_patterns = ctx.attr.expand_slashed_path_patterns,
                     preserve_rpc_order = ctx.attr.preserve_rpc_order,
                     generate_x_go_type = ctx.attr.generate_x_go_type,
@@ -422,6 +432,12 @@ protoc_gen_openapiv2 = rule(
             doc = "if set, will use allOf as container for $ref to preserve" +
                   " same-level properties.",
         ),
+        "omit_array_item_type_when_ref_sibling": attr.bool(
+            default = False,
+            mandatory = False,
+            doc = "if set, will omit 'type: object' in array items when $ref is present" +
+                  " to avoid strict no-$ref-siblings rule violations.",
+        ),
         "disable_default_responses": attr.bool(
             default = False,
             mandatory = False,
@@ -434,6 +450,11 @@ protoc_gen_openapiv2 = rule(
             mandatory = False,
             doc = "whether to process grpc method's deprecated option.",
         ),
+        "enable_field_deprecation": attr.bool(
+            default = False,
+            mandatory = False,
+            doc = "whether to process proto field's deprecated option.",
+        ),
         "expand_slashed_path_patterns": attr.bool(
             default = False,
             mandatory = False,
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template.go b/protoc-gen-openapiv2/internal/genopenapi/template.go
index 6265da8f..bb8cdd00 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template.go
@@ -345,6 +345,11 @@ func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, pre
 			}
 		}
 
+		// verify if the field is deprecated, either via proto or annotation
+		protoDeprecated := field.GetOptions().GetDeprecated() && reg.GetEnableFieldDeprecation()
+		annotationDeprecated := getFieldConfiguration(reg, field).GetDeprecated()
+		deprecated := protoDeprecated || annotationDeprecated
+
 		param := openapiParameterObject{
 			Description: desc,
 			In:          "query",
@@ -354,6 +359,7 @@ func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, pre
 			Format:      schema.Format,
 			Pattern:     schema.Pattern,
 			Required:    required,
+			Deprecated:  deprecated,
 			UniqueItems: schema.UniqueItems,
 			extensions:  schema.extensions,
 			Enum:        schema.Enum,
@@ -451,6 +457,12 @@ func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descripto
 					continue
 				}
 
+				// Only process methods with HTTP bindings (exposed via HTTP annotations)
+				// This prevents unused message definitions from appearing in the OpenAPI document
+				if len(meth.Bindings) == 0 {
+					continue
+				}
+
 				swgReqName, ok := fullyQualifiedNameToOpenAPIName(meth.RequestType.FQMN(), reg)
 				if !ok {
 					grpclog.Errorf("couldn't resolve OpenAPI name for FQMN %q", meth.RequestType.FQMN())
@@ -607,11 +619,38 @@ func renderMessageAsDefinition(msg *descriptor.Message, reg *descriptor.Registry
 		}
 
 		if fieldSchema.Required != nil {
-			schema.Required = getUniqueFields(schema.Required, fieldSchema.Required)
-			schema.Required = append(schema.Required, fieldSchema.Required...)
-			// To avoid populating both the field schema require and message schema require, unset the field schema require.
-			// See issue #2635.
-			fieldSchema.Required = nil
+			// Only hoist required fields to parent if there are no path params inside this field.
+			if len(subPathParams) == 0 {
+				schema.Required = getUniqueFields(schema.Required, fieldSchema.Required)
+				schema.Required = append(schema.Required, fieldSchema.Required...)
+				// To avoid populating both the field schema require and message schema require, unset the field schema require.
+				// See issue #2635.
+				fieldSchema.Required = nil
+			} else {
+				// When there are path params, we need to separate field-level required from nested required.
+				// The field name itself (if required) should be in parent's required, but nested field names
+				// should stay in the nested schema's required.
+				fieldName := f.GetName()
+				if reg.GetUseJSONNamesForFields() {
+					fieldName = f.GetJsonName()
+				}
+				// Check if the field name is in the fieldSchema.Required (it would be if the field is marked REQUIRED)
+				var nestedRequired []string
+				fieldIsRequired := false
+				for _, req := range fieldSchema.Required {
+					if req == fieldName {
+						fieldIsRequired = true
+					} else {
+						nestedRequired = append(nestedRequired, req)
+					}
+				}
+				// Add the field name to parent's required if the field itself is required
+				if fieldIsRequired && find(schema.Required, fieldName) == -1 {
+					schema.Required = append(schema.Required, fieldName)
+				}
+				// Keep only the nested required fields in the field schema
+				fieldSchema.Required = nestedRequired
+			}
 		}
 
 		if reg.GetUseAllOfForRefs() {
@@ -832,8 +871,13 @@ func schemaOfFieldBase(f *descriptor.Field, reg *descriptor.Registry, refs refMa
 
 	switch aggregate {
 	case array:
-		if _, ok := wktSchemas[fd.GetTypeName()]; !ok && fd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
-			core.Type = "object"
+		// Only set core.Type = "object" for MESSAGE types with $ref if the flag is not set.
+		// When omitArrayItemTypeWhenRefSibling is true, we omit "type: object" to avoid
+		// no-$ref-siblings violations in OpenAPI v2, since $ref already implies the type is object.
+		if !reg.GetOmitArrayItemTypeWhenRefSibling() {
+			if _, ok := wktSchemas[fd.GetTypeName()]; !ok && fd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
+				core.Type = "object"
+			}
 		}
 		ret = openapiSchemaObject{
 			schemaCore: schemaCore{
@@ -1454,18 +1498,26 @@ func renderServices(services []*descriptor.Service, paths *openapiPathsObject, r
 					if regExp, ok := pathParamRegexpMap[parameterString]; ok {
 						pattern = regExp
 					}
-					if fc := getFieldConfiguration(reg, parameter.Target); fc != nil {
+					fc := getFieldConfiguration(reg, parameter.Target)
+					if fc != nil {
 						pathParamName := fc.GetPathParamName()
 						if pathParamName != "" && pathParamName != parameterString {
 							pathParamNames["{"+parameterString+"}"] = "{" + pathParamName + "}"
 							parameterString, _, _ = strings.Cut(pathParamName, "=")
 						}
 					}
+
+					// verify if the parameter is deprecated, either via proto or annotation
+					protoDeprecated := parameter.Target.GetOptions().GetDeprecated() && reg.GetEnableFieldDeprecation()
+					annotationDeprecated := fc.GetDeprecated()
+					deprecated := protoDeprecated || annotationDeprecated
+
 					parameters = append(parameters, openapiParameterObject{
 						Name:        parameterString,
 						Description: desc,
 						In:          "path",
 						Required:    true,
+						Deprecated:  deprecated,
 						Default:     defaultValue,
 						// Parameters in gRPC-Gateway can only be strings?
 						Type:             paramType,
@@ -1551,6 +1603,39 @@ func renderServices(services []*descriptor.Service, paths *openapiPathsObject, r
 							if err != nil {
 								return err
 							}
+							// renderFieldAsDefinition may add the body field name to the schema's required array
+							// via updateSwaggerObjectFromFieldBehavior. However, for body parameters, the schema
+							// represents the field's type, not the containing message. The body field name should
+							// only be in the schema's required array if it's actually a property of the schema.
+							// Remove the body field name from required if it's not a property to avoid invalid entries.
+							if schema.Required != nil && schema.Properties != nil {
+								// Build a set of property names
+								propertyNames := make(map[string]bool)
+								for _, prop := range *schema.Properties {
+									propertyNames[prop.Key] = true
+								}
+								// Filter required array: keep field names that are either:
+								// 1. Not the body field name, OR
+								// 2. The body field name AND it's actually a property
+								filteredRequired := make([]string, 0, len(schema.Required))
+								seenBodyFieldName := false
+								for _, req := range schema.Required {
+									if req == bodyFieldName {
+										if propertyNames[req] {
+											// It's a property, keep it (but only once)
+											if !seenBodyFieldName {
+												filteredRequired = append(filteredRequired, req)
+												seenBodyFieldName = true
+											}
+										}
+										// else: It's not a property, skip it
+									} else {
+										// Not the body field name, keep it
+										filteredRequired = append(filteredRequired, req)
+									}
+								}
+								schema.Required = filteredRequired
+							}
 						}
 						if schema.Title != "" {
 							desc = mergeDescription(schema)
diff --git a/protoc-gen-openapiv2/internal/genopenapi/types.go b/protoc-gen-openapiv2/internal/genopenapi/types.go
index db9be4de..ab60dca4 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/types.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/types.go
@@ -149,6 +149,7 @@ type openapiParameterObject struct {
 	Description      string              `json:"description,omitempty" yaml:"description,omitempty"`
 	In               string              `json:"in,omitempty" yaml:"in,omitempty"`
 	Required         bool                `json:"required" yaml:"required"`
+	Deprecated       bool                `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
 	Type             string              `json:"type,omitempty" yaml:"type,omitempty"`
 	Format           string              `json:"format,omitempty" yaml:"format,omitempty"`
 	UniqueItems      bool                `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
diff --git a/protoc-gen-openapiv2/main.go b/protoc-gen-openapiv2/main.go
index 82062cc9..2450973b 100644
--- a/protoc-gen-openapiv2/main.go
+++ b/protoc-gen-openapiv2/main.go
@@ -17,42 +17,44 @@ import (
 )
 
 var (
-	importPrefix                   = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
-	file                           = flag.String("file", "-", "where to load data from")
-	allowDeleteBody                = flag.Bool("allow_delete_body", false, "unless set, HTTP DELETE methods may not have a body")
-	grpcAPIConfiguration           = flag.String("grpc_api_configuration", "", "path to file which describes the gRPC API Configuration in YAML format")
-	allowMerge                     = flag.Bool("allow_merge", false, "if set, generation one OpenAPI file out of multiple protos")
-	mergeFileName                  = flag.String("merge_file_name", "apidocs", "target OpenAPI file name prefix after merge")
-	useJSONNamesForFields          = flag.Bool("json_names_for_fields", true, "if disabled, the original proto name will be used for generating OpenAPI definitions")
-	repeatedPathParamSeparator     = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`")
-	versionFlag                    = flag.Bool("version", false, "print the current version")
-	_                              = flag.Bool("allow_repeated_fields_in_body", true, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option. DEPRECATED: the value is ignored and always behaves as `true`.")
-	includePackageInTags           = flag.Bool("include_package_in_tags", false, "if unset, the gRPC service name is added to the `Tags` field of each operation. If set and the `package` directive is shown in the proto file, the package name will be prepended to the service name")
-	useFQNForOpenAPIName           = flag.Bool("fqn_for_openapi_name", false, "if set, the object's OpenAPI names will use the fully qualified names from the proto definition (ie my.package.MyMessage.MyInnerMessage). DEPRECATED: prefer `openapi_naming_strategy=fqn`")
-	openAPINamingStrategy          = flag.String("openapi_naming_strategy", "", "use the given OpenAPI naming strategy. Allowed values are `legacy`, `fqn`, `simple`, `package`. If unset, either `legacy` or `fqn` are selected, depending on the value of the `fqn_for_openapi_name` flag")
-	useGoTemplate                  = flag.Bool("use_go_templates", false, "if set, you can use Go templates in protofile comments")
-	goTemplateArgs                 = utilities.StringArrayFlag(flag.CommandLine, "go_template_args", "provide a custom value that can override a key in the Go template. Requires the `use_go_templates` option to be set")
-	ignoreComments                 = flag.Bool("ignore_comments", false, "if set, all protofile comments are excluded from output")
-	removeInternalComments         = flag.Bool("remove_internal_comments", false, "if set, removes all substrings in comments that start with `(--` and end with `--)` as specified in https://google.aip.dev/192#internal-comments")
-	disableDefaultErrors           = flag.Bool("disable_default_errors", false, "if set, disables generation of default errors. This is useful if you have defined custom error handling")
-	enumsAsInts                    = flag.Bool("enums_as_ints", false, "whether to render enum values as integers, as opposed to string values")
-	simpleOperationIDs             = flag.Bool("simple_operation_ids", false, "whether to remove the service prefix in the operationID generation. Can introduce duplicate operationIDs, use with caution.")
-	proto3OptionalNullable         = flag.Bool("proto3_optional_nullable", false, "whether Proto3 Optional fields should be marked as x-nullable")
-	openAPIConfiguration           = flag.String("openapi_configuration", "", "path to file which describes the OpenAPI Configuration in YAML format")
-	generateUnboundMethods         = flag.Bool("generate_unbound_methods", false, "generate swagger metadata even for RPC methods that have no HttpRule annotation")
-	recursiveDepth                 = flag.Int("recursive-depth", 1000, "maximum recursion count allowed for a field type")
-	omitEnumDefaultValue           = flag.Bool("omit_enum_default_value", false, "if set, omit default enum value")
-	outputFormat                   = flag.String("output_format", string(genopenapi.FormatJSON), fmt.Sprintf("output content format. Allowed values are: `%s`, `%s`", genopenapi.FormatJSON, genopenapi.FormatYAML))
-	visibilityRestrictionSelectors = utilities.StringArrayFlag(flag.CommandLine, "visibility_restriction_selectors", "list of `google.api.VisibilityRule` visibility labels to include in the generated output when a visibility annotation is defined. Repeat this option to supply multiple values. Elements without visibility annotations are unaffected by this setting.")
-	disableServiceTags             = flag.Bool("disable_service_tags", false, "if set, disables generation of service tags. This is useful if you do not want to expose the names of your backend grpc services.")
-	disableDefaultResponses        = flag.Bool("disable_default_responses", false, "if set, disables generation of default responses. Useful if you have to support custom response codes that are not 200.")
-	useAllOfForRefs                = flag.Bool("use_allof_for_refs", false, "if set, will use allOf as container for $ref to preserve same-level properties.")
-	allowPatchFeature              = flag.Bool("allow_patch_feature", true, "whether to hide update_mask fields in PATCH requests from the generated swagger file.")
-	preserveRPCOrder               = flag.Bool("preserve_rpc_order", false, "if true, will ensure the order of paths emitted in openapi swagger files mirror the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically.")
-	enableRpcDeprecation           = flag.Bool("enable_rpc_deprecation", false, "whether to process grpc method's deprecated option.")
-	expandSlashedPathPatterns      = flag.Bool("expand_slashed_path_patterns", false, "if set, expands path parameters with URI sub-paths into the URI. For example, \"/v1/{name=projects/*}/resource\" becomes \"/v1/projects/{project}/resource\".")
-	useProto3FieldSemantics        = flag.Bool("use_proto3_field_semantics", false, "if set, uses proto3 field semantics for the OpenAPI schema. This means that fields are required by default.")
-	generateXGoType                = flag.Bool("generate_x_go_type", false, "if set, generates x-go-type extension using the go_package option from proto files")
+	importPrefix                    = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
+	file                            = flag.String("file", "-", "where to load data from")
+	allowDeleteBody                 = flag.Bool("allow_delete_body", false, "unless set, HTTP DELETE methods may not have a body")
+	grpcAPIConfiguration            = flag.String("grpc_api_configuration", "", "path to file which describes the gRPC API Configuration in YAML format")
+	allowMerge                      = flag.Bool("allow_merge", false, "if set, generation one OpenAPI file out of multiple protos")
+	mergeFileName                   = flag.String("merge_file_name", "apidocs", "target OpenAPI file name prefix after merge")
+	useJSONNamesForFields           = flag.Bool("json_names_for_fields", true, "if disabled, the original proto name will be used for generating OpenAPI definitions")
+	repeatedPathParamSeparator      = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`")
+	versionFlag                     = flag.Bool("version", false, "print the current version")
+	_                               = flag.Bool("allow_repeated_fields_in_body", true, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option. DEPRECATED: the value is ignored and always behaves as `true`.")
+	includePackageInTags            = flag.Bool("include_package_in_tags", false, "if unset, the gRPC service name is added to the `Tags` field of each operation. If set and the `package` directive is shown in the proto file, the package name will be prepended to the service name")
+	useFQNForOpenAPIName            = flag.Bool("fqn_for_openapi_name", false, "if set, the object's OpenAPI names will use the fully qualified names from the proto definition (ie my.package.MyMessage.MyInnerMessage). DEPRECATED: prefer `openapi_naming_strategy=fqn`")
+	openAPINamingStrategy           = flag.String("openapi_naming_strategy", "", "use the given OpenAPI naming strategy. Allowed values are `legacy`, `fqn`, `simple`, `package`. If unset, either `legacy` or `fqn` are selected, depending on the value of the `fqn_for_openapi_name` flag")
+	useGoTemplate                   = flag.Bool("use_go_templates", false, "if set, you can use Go templates in protofile comments")
+	goTemplateArgs                  = utilities.StringArrayFlag(flag.CommandLine, "go_template_args", "provide a custom value that can override a key in the Go template. Requires the `use_go_templates` option to be set")
+	ignoreComments                  = flag.Bool("ignore_comments", false, "if set, all protofile comments are excluded from output")
+	removeInternalComments          = flag.Bool("remove_internal_comments", false, "if set, removes all substrings in comments that start with `(--` and end with `--)` as specified in https://google.aip.dev/192#internal-comments")
+	disableDefaultErrors            = flag.Bool("disable_default_errors", false, "if set, disables generation of default errors. This is useful if you have defined custom error handling")
+	enumsAsInts                     = flag.Bool("enums_as_ints", false, "whether to render enum values as integers, as opposed to string values")
+	simpleOperationIDs              = flag.Bool("simple_operation_ids", false, "whether to remove the service prefix in the operationID generation. Can introduce duplicate operationIDs, use with caution.")
+	proto3OptionalNullable          = flag.Bool("proto3_optional_nullable", false, "whether Proto3 Optional fields should be marked as x-nullable")
+	openAPIConfiguration            = flag.String("openapi_configuration", "", "path to file which describes the OpenAPI Configuration in YAML format")
+	generateUnboundMethods          = flag.Bool("generate_unbound_methods", false, "generate swagger metadata even for RPC methods that have no HttpRule annotation")
+	recursiveDepth                  = flag.Int("recursive-depth", 1000, "maximum recursion count allowed for a field type")
+	omitEnumDefaultValue            = flag.Bool("omit_enum_default_value", false, "if set, omit default enum value")
+	outputFormat                    = flag.String("output_format", string(genopenapi.FormatJSON), fmt.Sprintf("output content format. Allowed values are: `%s`, `%s`", genopenapi.FormatJSON, genopenapi.FormatYAML))
+	visibilityRestrictionSelectors  = utilities.StringArrayFlag(flag.CommandLine, "visibility_restriction_selectors", "list of `google.api.VisibilityRule` visibility labels to include in the generated output when a visibility annotation is defined. Repeat this option to supply multiple values. Elements without visibility annotations are unaffected by this setting.")
+	disableServiceTags              = flag.Bool("disable_service_tags", false, "if set, disables generation of service tags. This is useful if you do not want to expose the names of your backend grpc services.")
+	disableDefaultResponses         = flag.Bool("disable_default_responses", false, "if set, disables generation of default responses. Useful if you have to support custom response codes that are not 200.")
+	useAllOfForRefs                 = flag.Bool("use_allof_for_refs", false, "if set, will use allOf as container for $ref to preserve same-level properties.")
+	omitArrayItemTypeWhenRefSibling = flag.Bool("omit_array_item_type_when_ref_sibling", false, "if set, will omit 'type: object' in array items when $ref is present to avoid strict no-$ref-siblings rule violations.")
+	allowPatchFeature               = flag.Bool("allow_patch_feature", true, "whether to hide update_mask fields in PATCH requests from the generated swagger file.")
+	preserveRPCOrder                = flag.Bool("preserve_rpc_order", false, "if true, will ensure the order of paths emitted in openapi swagger files mirror the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically.")
+	enableRpcDeprecation            = flag.Bool("enable_rpc_deprecation", false, "whether to process grpc method's deprecated option.")
+	enableFieldDeprecation          = flag.Bool("enable_field_deprecation", false, "whether to process proto field's deprecated option.")
+	expandSlashedPathPatterns       = flag.Bool("expand_slashed_path_patterns", false, "if set, expands path parameters with URI sub-paths into the URI. For example, \"/v1/{name=projects/*}/resource\" becomes \"/v1/projects/{project}/resource\".")
+	useProto3FieldSemantics         = flag.Bool("use_proto3_field_semantics", false, "if set, uses proto3 field semantics for the OpenAPI schema. This means that fields are required by default.")
+	generateXGoType                 = flag.Bool("generate_x_go_type", false, "if set, generates x-go-type extension using the go_package option from proto files")
 
 	_ = flag.Bool("logtostderr", false, "Legacy glog compatibility. This flag is a no-op, you can safely remove it")
 )
@@ -174,9 +176,11 @@ func main() {
 	reg.SetDisableServiceTags(*disableServiceTags)
 	reg.SetDisableDefaultResponses(*disableDefaultResponses)
 	reg.SetUseAllOfForRefs(*useAllOfForRefs)
+	reg.SetOmitArrayItemTypeWhenRefSibling(*omitArrayItemTypeWhenRefSibling)
 	reg.SetAllowPatchFeature(*allowPatchFeature)
 	reg.SetPreserveRPCOrder(*preserveRPCOrder)
 	reg.SetEnableRpcDeprecation(*enableRpcDeprecation)
+	reg.SetEnableFieldDeprecation(*enableFieldDeprecation)
 	reg.SetExpandSlashedPathPatterns(*expandSlashedPathPatterns)
 	reg.SetGenerateXGoType(*generateXGoType)
 
diff --git a/protoc-gen-openapiv2/options/openapiv2.pb.go b/protoc-gen-openapiv2/options/openapiv2.pb.go
index 3a34e664..5121dce3 100644
--- a/protoc-gen-openapiv2/options/openapiv2.pb.go
+++ b/protoc-gen-openapiv2/options/openapiv2.pb.go
@@ -3476,6 +3476,9 @@ type JSONSchema_FieldConfiguration struct {
 	// parameter. Use this to avoid having auto generated path parameter names
 	// for overlapping paths.
 	PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"`
+	// Declares this field to be deprecated. Allows for the generated OpenAPI
+	// parameter to be marked as deprecated without affecting the proto field.
+	Deprecated    bool `protobuf:"varint,49,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
 	unknownFields protoimpl.UnknownFields
 	sizeCache     protoimpl.SizeCache
 }
@@ -3512,10 +3515,21 @@ func (x *JSONSchema_FieldConfiguration) GetPathParamName() string {
 	return ""
 }
 
+func (x *JSONSchema_FieldConfiguration) GetDeprecated() bool {
+	if x != nil {
+		return x.Deprecated
+	}
+	return false
+}
+
 func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) {
 	x.PathParamName = v
 }
 
+func (x *JSONSchema_FieldConfiguration) SetDeprecated(v bool) {
+	x.Deprecated = v
+}
+
 type JSONSchema_FieldConfiguration_builder struct {
 	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
 
@@ -3524,6 +3538,9 @@ type JSONSchema_FieldConfiguration_builder struct {
 	// parameter. Use this to avoid having auto generated path parameter names
 	// for overlapping paths.
 	PathParamName string
+	// Declares this field to be deprecated. Allows for the generated OpenAPI
+	// parameter to be marked as deprecated without affecting the proto field.
+	Deprecated bool
 }
 
 func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration {
@@ -3531,6 +3548,7 @@ func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfigu
 	b, x := &b0, m0
 	_, _ = b, x
 	x.PathParamName = b.PathParamName
+	x.Deprecated = b.Deprecated
 	return m0
 }
 
@@ -3904,7 +3922,7 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
 	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
 	0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
 	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
-	0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7,
+	0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7,
 	0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a,
 	0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12,
 	0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
@@ -3968,11 +3986,13 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
 	0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x6f, 0x70,
 	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
 	0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12,
+	0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x5c, 0x0a, 0x12,
 	0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
 	0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
 	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74,
-	0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+	0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65,
+	0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
+	0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
 	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
 	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
 	0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
diff --git a/protoc-gen-openapiv2/options/openapiv2.proto b/protoc-gen-openapiv2/options/openapiv2.proto
index 5313f081..444a5687 100644
--- a/protoc-gen-openapiv2/options/openapiv2.proto
+++ b/protoc-gen-openapiv2/options/openapiv2.proto
@@ -612,6 +612,9 @@ message JSONSchema {
     // parameter. Use this to avoid having auto generated path parameter names
     // for overlapping paths.
     string path_param_name = 47;
+    // Declares this field to be deprecated. Allows for the generated OpenAPI
+    // parameter to be marked as deprecated without affecting the proto field.
+    bool deprecated = 49;
   }
   // Custom properties that start with "x-" such as "x-foo" used to describe
   // extra functionality that is not covered by the standard OpenAPI Specification.
diff --git a/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go b/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go
index 1f0e0c26..5316ed61 100644
--- a/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go
+++ b/protoc-gen-openapiv2/options/openapiv2_protoopaque.pb.go
@@ -3268,6 +3268,7 @@ func (b0 Scopes_builder) Build() *Scopes {
 type JSONSchema_FieldConfiguration struct {
 	state                    protoimpl.MessageState `protogen:"opaque.v1"`
 	xxx_hidden_PathParamName string                 `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"`
+	xxx_hidden_Deprecated    bool                   `protobuf:"varint,49,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
 	unknownFields            protoimpl.UnknownFields
 	sizeCache                protoimpl.SizeCache
 }
@@ -3304,10 +3305,21 @@ func (x *JSONSchema_FieldConfiguration) GetPathParamName() string {
 	return ""
 }
 
+func (x *JSONSchema_FieldConfiguration) GetDeprecated() bool {
+	if x != nil {
+		return x.xxx_hidden_Deprecated
+	}
+	return false
+}
+
 func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) {
 	x.xxx_hidden_PathParamName = v
 }
 
+func (x *JSONSchema_FieldConfiguration) SetDeprecated(v bool) {
+	x.xxx_hidden_Deprecated = v
+}
+
 type JSONSchema_FieldConfiguration_builder struct {
 	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
 
@@ -3316,6 +3328,9 @@ type JSONSchema_FieldConfiguration_builder struct {
 	// parameter. Use this to avoid having auto generated path parameter names
 	// for overlapping paths.
 	PathParamName string
+	// Declares this field to be deprecated. Allows for the generated OpenAPI
+	// parameter to be marked as deprecated without affecting the proto field.
+	Deprecated bool
 }
 
 func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration {
@@ -3323,6 +3338,7 @@ func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfigu
 	b, x := &b0, m0
 	_, _ = b, x
 	x.xxx_hidden_PathParamName = b.PathParamName
+	x.xxx_hidden_Deprecated = b.Deprecated
 	return m0
 }
 
@@ -3696,7 +3712,7 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
 	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
 	0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
 	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
-	0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7,
+	0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7,
 	0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a,
 	0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12,
 	0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
@@ -3760,11 +3776,13 @@ var file_protoc_gen_openapiv2_options_openapiv2_proto_rawDesc = []byte{
 	0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x6f, 0x70,
 	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
 	0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12,
+	0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x5c, 0x0a, 0x12,
 	0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
 	0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
 	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74,
-	0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+	0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65,
+	0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x31, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
+	0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
 	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
 	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
 	0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
diff --git a/repositories.bzl b/repositories.bzl
index d8d1d24a..3be234d7 100644
--- a/repositories.bzl
+++ b/repositories.bzl
@@ -139,8 +139,8 @@ def go_repositories():
     go_repository(
         name = "com_github_cncf_xds_go",
         importpath = "github.com/cncf/xds/go",
-        sum = "h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=",
-        version = "v0.0.0-20250501225837-2ac532fd4443",
+        sum = "h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0=",
+        version = "v0.0.0-20251022180443-0feb69152e9f",
     )
 
     go_repository(
@@ -207,14 +207,14 @@ def go_repositories():
     go_repository(
         name = "com_github_envoyproxy_go_control_plane",
         importpath = "github.com/envoyproxy/go-control-plane",
-        sum = "h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=",
-        version = "v0.13.4",
+        sum = "h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM=",
+        version = "v0.13.5-0.20251024222203-75eaa193e329",
     )
     go_repository(
         name = "com_github_envoyproxy_go_control_plane_envoy",
         importpath = "github.com/envoyproxy/go-control-plane/envoy",
-        sum = "h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=",
-        version = "v1.32.4",
+        sum = "h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo=",
+        version = "v1.35.0",
     )
     go_repository(
         name = "com_github_envoyproxy_go_control_plane_ratelimit",
@@ -263,8 +263,8 @@ def go_repositories():
     go_repository(
         name = "com_github_go_jose_go_jose_v4",
         importpath = "github.com/go-jose/go-jose/v4",
-        sum = "h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=",
-        version = "v4.1.2",
+        sum = "h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=",
+        version = "v4.1.3",
     )
 
     go_repository(
@@ -395,8 +395,8 @@ def go_repositories():
     go_repository(
         name = "com_github_googlecloudplatform_opentelemetry_operations_go_detectors_gcp",
         importpath = "github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp",
-        sum = "h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=",
-        version = "v1.29.0",
+        sum = "h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=",
+        version = "v1.30.0",
     )
 
     go_repository(
@@ -937,8 +937,8 @@ def go_repositories():
     go_repository(
         name = "com_github_spiffe_go_spiffe_v2",
         importpath = "github.com/spiffe/go-spiffe/v2",
-        sum = "h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=",
-        version = "v2.5.0",
+        sum = "h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=",
+        version = "v2.6.0",
     )
 
     go_repository(
@@ -1155,8 +1155,8 @@ def go_repositories():
     go_repository(
         name = "com_google_cloud_go_compute_metadata",
         importpath = "cloud.google.com/go/compute/metadata",
-        sum = "h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=",
-        version = "v0.7.0",
+        sum = "h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=",
+        version = "v0.9.0",
     )
     go_repository(
         name = "com_google_cloud_go_contactcenterinsights",
@@ -1816,44 +1816,44 @@ def go_repositories():
     go_repository(
         name = "io_opentelemetry_go_auto_sdk",
         importpath = "go.opentelemetry.io/auto/sdk",
-        sum = "h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=",
-        version = "v1.1.0",
+        sum = "h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=",
+        version = "v1.2.1",
     )
     go_repository(
         name = "io_opentelemetry_go_contrib_detectors_gcp",
         importpath = "go.opentelemetry.io/contrib/detectors/gcp",
-        sum = "h1:F7q2tNlCaHY9nMKHR6XH9/qkp8FktLnIcy6jJNyOCQw=",
-        version = "v1.36.0",
+        sum = "h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs=",
+        version = "v1.38.0",
     )
     go_repository(
         name = "io_opentelemetry_go_otel",
         importpath = "go.opentelemetry.io/otel",
-        sum = "h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=",
-        version = "v1.37.0",
+        sum = "h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=",
+        version = "v1.38.0",
     )
     go_repository(
         name = "io_opentelemetry_go_otel_metric",
         importpath = "go.opentelemetry.io/otel/metric",
-        sum = "h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=",
-        version = "v1.37.0",
+        sum = "h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=",
+        version = "v1.38.0",
     )
     go_repository(
         name = "io_opentelemetry_go_otel_sdk",
         importpath = "go.opentelemetry.io/otel/sdk",
-        sum = "h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=",
-        version = "v1.37.0",
+        sum = "h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=",
+        version = "v1.38.0",
     )
     go_repository(
         name = "io_opentelemetry_go_otel_sdk_metric",
         importpath = "go.opentelemetry.io/otel/sdk/metric",
-        sum = "h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=",
-        version = "v1.37.0",
+        sum = "h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=",
+        version = "v1.38.0",
     )
     go_repository(
         name = "io_opentelemetry_go_otel_trace",
         importpath = "go.opentelemetry.io/otel/trace",
-        sum = "h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=",
-        version = "v1.37.0",
+        sum = "h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=",
+        version = "v1.38.0",
     )
 
     go_repository(
@@ -1902,20 +1902,20 @@ def go_repositories():
     go_repository(
         name = "org_golang_google_genproto_googleapis_api",
         importpath = "google.golang.org/genproto/googleapis/api",
-        sum = "h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=",
-        version = "v0.0.0-20251022142026-3a174f9686a8",
+        sum = "h1:ZdyUkS9po3H7G0tuh955QVyyotWvOD4W0aEapeGeUYk=",
+        version = "v0.0.0-20251124214823-79d6a2a48846",
     )
     go_repository(
         name = "org_golang_google_genproto_googleapis_rpc",
         importpath = "google.golang.org/genproto/googleapis/rpc",
-        sum = "h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=",
-        version = "v0.0.0-20251022142026-3a174f9686a8",
+        sum = "h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=",
+        version = "v0.0.0-20251124214823-79d6a2a48846",
     )
     go_repository(
         name = "org_golang_google_grpc",
         importpath = "google.golang.org/grpc",
-        sum = "h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=",
-        version = "v1.76.0",
+        sum = "h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=",
+        version = "v1.77.0",
     )
 
     go_repository(
@@ -1933,8 +1933,8 @@ def go_repositories():
     go_repository(
         name = "org_golang_x_crypto",
         importpath = "golang.org/x/crypto",
-        sum = "h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=",
-        version = "v0.40.0",
+        sum = "h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=",
+        version = "v0.43.0",
     )
     go_repository(
         name = "org_golang_x_exp",
@@ -1965,44 +1965,44 @@ def go_repositories():
     go_repository(
         name = "org_golang_x_mod",
         importpath = "golang.org/x/mod",
-        sum = "h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=",
-        version = "v0.28.0",
+        sum = "h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=",
+        version = "v0.29.0",
     )
     go_repository(
         name = "org_golang_x_net",
         importpath = "golang.org/x/net",
-        sum = "h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=",
-        version = "v0.42.0",
+        sum = "h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=",
+        version = "v0.46.1-0.20251013234738-63d1a5100f82",
     )
     go_repository(
         name = "org_golang_x_oauth2",
         importpath = "golang.org/x/oauth2",
-        sum = "h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=",
-        version = "v0.32.0",
+        sum = "h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=",
+        version = "v0.33.0",
     )
     go_repository(
         name = "org_golang_x_sync",
         importpath = "golang.org/x/sync",
-        sum = "h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=",
-        version = "v0.17.0",
+        sum = "h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=",
+        version = "v0.18.0",
     )
     go_repository(
         name = "org_golang_x_sys",
         importpath = "golang.org/x/sys",
-        sum = "h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=",
-        version = "v0.34.0",
+        sum = "h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=",
+        version = "v0.37.0",
     )
     go_repository(
         name = "org_golang_x_term",
         importpath = "golang.org/x/term",
-        sum = "h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=",
-        version = "v0.33.0",
+        sum = "h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=",
+        version = "v0.36.0",
     )
     go_repository(
         name = "org_golang_x_text",
         importpath = "golang.org/x/text",
-        sum = "h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=",
-        version = "v0.30.0",
+        sum = "h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=",
+        version = "v0.31.0",
     )
 
     go_repository(
@@ -2014,8 +2014,8 @@ def go_repositories():
     go_repository(
         name = "org_golang_x_tools",
         importpath = "golang.org/x/tools",
-        sum = "h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=",
-        version = "v0.37.0",
+        sum = "h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=",
+        version = "v0.38.0",
     )
     go_repository(
         name = "org_golang_x_xerrors",



Test patch:
diff --git a/protoc-gen-openapiv2/internal/genopenapi/generator_test.go b/protoc-gen-openapiv2/internal/genopenapi/generator_test.go
index 8c52489d..1255d7ae 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/generator_test.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/generator_test.go
@@ -83,7 +83,11 @@ func TestGenerateExtension(t *testing.T) {
 				name: "Test"
 				input_type: ".example.v1.Foo"
 				output_type: ".example.v1.Foo"
-				options: {}
+				options: {
+					[google.api.http]: {
+						get: "/v1/test"
+					}
+				}
 			}
 		}
 		options: {
@@ -1938,3 +1942,136 @@ func TestGenerateXGoType(t *testing.T) {
 		})
 	}
 }
+
+// TestIssue5684_UnusedMethodsNotInOpenAPI tests that methods without HTTP bindings
+// do not appear in the OpenAPI definitions.
+// See https://github.com/grpc-ecosystem/grpc-gateway/issues/5684
+func TestIssue5684_UnusedMethodsNotInOpenAPI(t *testing.T) {
+	t.Parallel()
+
+	// Create a proto definition similar to the issue report:
+	// - Service with two methods: Add (no HTTP binding) and Show (with HTTP binding)
+	// - Only Show should appear in the OpenAPI output
+	// - AddRequest and AddResponse should NOT appear in definitions
+	const in = `
+	file_to_generate: "account/account.proto"
+	proto_file: {
+		name: "account/account.proto"
+		package: "account"
+
+		message_type: {
+			name: "Money"
+			field: {
+				name: "amount"
+				number: 1
+				label: LABEL_OPTIONAL
+				type: TYPE_INT64
+				json_name: "amount"
+			}
+		}
+
+		message_type: {
+			name: "AddRequest"
+			field: {
+				name: "money"
+				number: 1
+				label: LABEL_OPTIONAL
+				type: TYPE_MESSAGE
+				type_name: ".account.Money"
+				json_name: "money"
+			}
+		}
+
+		message_type: {
+			name: "AddResponse"
+		}
+
+		message_type: {
+			name: "ShowRequest"
+		}
+
+		message_type: {
+			name: "ShowResponse"
+			field: {
+				name: "money"
+				number: 1
+				label: LABEL_OPTIONAL
+				type: TYPE_MESSAGE
+				type_name: ".account.Money"
+				json_name: "money"
+			}
+		}
+
+		service: {
+			name: "AccountService"
+			method: {
+				name: "Add"
+				input_type: ".account.AddRequest"
+				output_type: ".account.AddResponse"
+			}
+			method: {
+				name: "Show"
+				input_type: ".account.ShowRequest"
+				output_type: ".account.ShowResponse"
+				options: {
+					[google.api.http]: {
+						get: "/v1/account"
+					}
+				}
+			}
+		}
+
+		options: {
+			go_package: "accounts/pkg/account;account"
+		}
+	}`
+
+	var req pluginpb.CodeGeneratorRequest
+	if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+		t.Fatalf("failed to unmarshal proto: %v", err)
+	}
+
+	resp := requireGenerate(t, &req, genopenapi.FormatYAML, false, false)
+	if len(resp) != 1 {
+		t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+	}
+
+	var openAPIDoc map[string]interface{}
+	if err := yaml.Unmarshal([]byte(resp[0].GetContent()), &openAPIDoc); err != nil {
+		t.Fatalf("failed to parse OpenAPI YAML: %v", err)
+	}
+
+	definitions, ok := openAPIDoc["definitions"].(map[string]interface{})
+	if !ok {
+		t.Fatalf("no definitions found in OpenAPI document")
+	}
+
+	if _, exists := definitions["accountAddRequest"]; exists {
+		t.Error("accountAddRequest found in definitions, but should be excluded (Add method has no HTTP binding)")
+	}
+
+	if _, exists := definitions["accountAddResponse"]; exists {
+		t.Error("accountAddResponse found in definitions, but should be excluded (Add method has no HTTP binding)")
+	}
+
+	if _, exists := definitions["accountShowResponse"]; !exists {
+		t.Error("accountShowResponse not found in definitions, but should be included (Show method has HTTP binding)")
+	}
+
+	if _, exists := definitions["accountMoney"]; !exists {
+		t.Error("accountMoney not found in definitions, but should be included (referenced by ShowResponse)")
+	}
+
+	paths, ok := openAPIDoc["paths"].(map[string]interface{})
+	if !ok {
+		t.Fatalf("no paths found in OpenAPI document")
+	}
+
+	if _, exists := paths["/v1/account"]; !exists {
+		t.Error("/v1/account path not found, but should be included (Show method)")
+	}
+
+	if len(paths) != 1 {
+		t.Errorf("expected exactly 1 path, got %d paths", len(paths))
+	}
+}
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template_test.go b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
index a766a852..a7ae2213 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template_test.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
@@ -1326,6 +1326,181 @@ func TestMessageToQueryParametersWithRequiredField(t *testing.T) {
 	}
 }
 
+func TestMessageToQueryParametersWithDeprecatedField(t *testing.T) {
+	annotationOptions := func() *descriptorpb.FieldOptions {
+		opts := &descriptorpb.FieldOptions{}
+		proto.SetExtension(opts, openapi_options.E_Openapiv2Field, &openapi_options.JSONSchema{
+			FieldConfiguration: &openapi_options.JSONSchema_FieldConfiguration{
+				Deprecated: true,
+			},
+		})
+		return opts
+	}
+
+	type test struct {
+		MsgDescs               []*descriptorpb.DescriptorProto
+		Message                string
+		Params                 []openapiParameterObject
+		enableFieldDeprecation bool
+	}
+
+	tests := []test{
+		{
+			MsgDescs: []*descriptorpb.DescriptorProto{
+				{
+					Name: proto.String("ExampleMessage"),
+					Field: []*descriptorpb.FieldDescriptorProto{
+						{
+							Name:   proto.String("deprecated_via_proto"),
+							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+							Number: proto.Int32(1),
+							Options: &descriptorpb.FieldOptions{
+								Deprecated: proto.Bool(true),
+							},
+						},
+						{
+							Name:    proto.String("deprecated_via_annotation"),
+							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+							Number:  proto.Int32(2),
+							Options: annotationOptions(),
+						},
+						{
+							Name:   proto.String("active_field"),
+							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+							Number: proto.Int32(3),
+						},
+					},
+				},
+			},
+			Message:                "ExampleMessage",
+			enableFieldDeprecation: true,
+			Params: []openapiParameterObject{
+				{
+					Name:       "deprecated_via_proto",
+					In:         "query",
+					Required:   false,
+					Type:       "string",
+					Deprecated: true,
+				},
+				{
+					Name:       "deprecated_via_annotation",
+					In:         "query",
+					Required:   false,
+					Type:       "string",
+					Deprecated: true,
+				},
+				{
+					Name:     "active_field",
+					In:       "query",
+					Required: false,
+					Type:     "string",
+				},
+			},
+		},
+		{
+			MsgDescs: []*descriptorpb.DescriptorProto{
+				{
+					Name: proto.String("ExampleMessage"),
+					Field: []*descriptorpb.FieldDescriptorProto{
+						{
+							Name:   proto.String("deprecated_via_proto"),
+							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+							Number: proto.Int32(1),
+							Options: &descriptorpb.FieldOptions{
+								Deprecated: proto.Bool(true),
+							},
+						},
+						{
+							Name:    proto.String("deprecated_via_annotation"),
+							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+							Number:  proto.Int32(2),
+							Options: annotationOptions(),
+						},
+						{
+							Name:   proto.String("active_field"),
+							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+							Number: proto.Int32(3),
+						},
+					},
+				},
+			},
+			Message:                "ExampleMessage",
+			enableFieldDeprecation: false,
+			Params: []openapiParameterObject{
+				{
+					Name:       "deprecated_via_proto",
+					In:         "query",
+					Required:   false,
+					Type:       "string",
+					Deprecated: false,
+				},
+				{
+					Name:       "deprecated_via_annotation",
+					In:         "query",
+					Required:   false,
+					Type:       "string",
+					Deprecated: true,
+				},
+				{
+					Name:     "active_field",
+					In:       "query",
+					Required: false,
+					Type:     "string",
+				},
+			},
+		},
+	}
+
+	// Enable or disable proto-field deprecation per test case before loading descriptors.
+	for _, test := range tests {
+		reg := descriptor.NewRegistry()
+		reg.SetEnableFieldDeprecation(test.enableFieldDeprecation)
+		msgs := []*descriptor.Message{}
+		for _, msgdesc := range test.MsgDescs {
+			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+		}
+		file := descriptor.File{
+			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+				Name:           proto.String("example.proto"),
+				Package:        proto.String("example"),
+				Dependency:     []string{},
+				MessageType:    test.MsgDescs,
+				Service:        []*descriptorpb.ServiceDescriptorProto{},
+				Options: &descriptorpb.FileOptions{
+					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+				},
+			},
+			GoPkg: descriptor.GoPackage{
+				Path: "example.com/path/to/example/example.pb",
+				Name: "example_pb",
+			},
+			Messages: msgs,
+		}
+		err := reg.Load(&pluginpb.CodeGeneratorRequest{
+			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+		})
+		if err != nil {
+			t.Fatalf("failed to load code generator request: %v", err)
+		}
+
+		message, err := reg.LookupMsg("", ".example."+test.Message)
+		if err != nil {
+			t.Fatalf("failed to lookup message: %s", err)
+		}
+		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+		if err != nil {
+			t.Fatalf("failed to convert message to query parameters: %s", err)
+		}
+		for i := range params {
+			params[i].Items = nil
+		}
+		if !reflect.DeepEqual(params, test.Params) {
+			t.Errorf("expected %v, got %v", test.Params, params)
+		}
+	}
+}
+
 func TestMessageToQueryParametersWithEnumFieldOption(t *testing.T) {
 	type test struct {
 		MsgDescs []*descriptorpb.DescriptorProto
@@ -10323,71 +10498,39 @@ func TestArrayMessageItemsType(t *testing.T) {
 	}
 }
 
-func TestQueryParameterType(t *testing.T) {
-	ntDesc := &descriptorpb.DescriptorProto{
-		Name: proto.String("AddressEntry"),
-		Field: []*descriptorpb.FieldDescriptorProto{
-			{
-				Name:     proto.String("key"),
-				Number:   proto.Int32(1),
-				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
-				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
-				JsonName: proto.String("key"),
-			},
-			{
-				Name:     proto.String("value"),
-				Number:   proto.Int32(2),
-				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
-				Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
-				JsonName: proto.String("value"),
-			},
-		},
-		Options: &descriptorpb.MessageOptions{
-			MapEntry: proto.Bool(true),
-		},
-	}
-
+func TestArrayMessageItemsTypeOmitWhenRefSibling(t *testing.T) {
 	msgDesc := &descriptorpb.DescriptorProto{
-		Name: proto.String("Person"),
+		Name: proto.String("ExampleMessage"),
 		Field: []*descriptorpb.FieldDescriptorProto{
 			{
-				Name:     proto.String("Address"),
-				Number:   proto.Int32(1),
+				Name:     proto.String("children"),
 				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
 				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
-				TypeName: proto.String(".example.com.Person.AddressEntry"),
-				JsonName: proto.String("Address"),
+				TypeName: proto.String(".example.ExampleMessage"),
+				Number:   proto.Int32(1),
+				JsonName: proto.String("children"),
 			},
 		},
-		NestedType: []*descriptorpb.DescriptorProto{
-			ntDesc,
-		},
 	}
 
-	nesteDesc := &descriptorpb.DescriptorProto{
-		Name: proto.String("ExampleResponse"),
+	nestDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("NestDescMessage"),
 		Field: []*descriptorpb.FieldDescriptorProto{
 			{
-				Name:     proto.String("Key"),
+				Name:     proto.String("children"),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".example.ExampleMessage"),
 				Number:   proto.Int32(1),
-				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
-				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
-				JsonName: proto.String("Key"),
-			},
-			{
-				Name:     proto.String("Value"),
-				Number:   proto.Int32(2),
-				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
-				Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
-				JsonName: proto.String("Value"),
+				JsonName: proto.String("children"),
 			},
 		},
 	}
 
 	meth := &descriptorpb.MethodDescriptorProto{
 		Name:       proto.String("Example"),
-		InputType:  proto.String("Person"),
-		OutputType: proto.String("ExampleResponse"),
+		InputType:  proto.String("ExampleMessage"),
+		OutputType: proto.String("NestDescMessage"),
 	}
 	svc := &descriptorpb.ServiceDescriptorProto{
 		Name:   proto.String("ExampleService"),
@@ -10396,11 +10539,8 @@ func TestQueryParameterType(t *testing.T) {
 	msg := &descriptor.Message{
 		DescriptorProto: msgDesc,
 	}
-	nt := &descriptor.Message{
-		DescriptorProto: ntDesc,
-	}
-	nest := &descriptor.Message{
-		DescriptorProto: nesteDesc,
+	nsg := &descriptor.Message{
+		DescriptorProto: nestDesc,
 	}
 	msg.Fields = []*descriptor.Field{
 		{
@@ -10408,28 +10548,18 @@ func TestQueryParameterType(t *testing.T) {
 			FieldDescriptorProto: msg.GetField()[0],
 		},
 	}
-	nt.Fields = []*descriptor.Field{
-		{
-			Message:              nt,
-			FieldDescriptorProto: msg.GetField()[0],
-		},
-	}
-	nest.Fields = []*descriptor.Field{
-		{
-			Message:              nest,
-			FieldDescriptorProto: nest.GetField()[0],
-		},
+	nsg.Fields = []*descriptor.Field{
 		{
-			Message:              nest,
-			FieldDescriptorProto: nest.GetField()[1],
+			Message:              nsg,
+			FieldDescriptorProto: nsg.GetField()[0],
 		},
 	}
 	file := descriptor.File{
 		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
 			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
-			Name:           proto.String("person.proto"),
-			Package:        proto.String("example.com"),
-			MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
+			Name:           proto.String("example.proto"),
+			Package:        proto.String("example"),
+			MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
 			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
 			Options: &descriptorpb.FileOptions{
 				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
@@ -10439,7 +10569,7 @@ func TestQueryParameterType(t *testing.T) {
 			Path: "example.com/path/to/example/example.pb",
 			Name: "example_pb",
 		},
-		Messages: []*descriptor.Message{msg, nest},
+		Messages: []*descriptor.Message{msg, nsg},
 		Services: []*descriptor.Service{
 			{
 				ServiceDescriptorProto: svc,
@@ -10447,10 +10577,13 @@ func TestQueryParameterType(t *testing.T) {
 					{
 						MethodDescriptorProto: meth,
 						RequestType:           msg,
-						ResponseType:          nest,
+						ResponseType:          nsg,
 						Bindings: []*descriptor.Binding{
 							{
-								HTTPMethod: "GET",
+								HTTPMethod: "POST",
+								Body: &descriptor.Body{
+									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+								},
 								PathTmpl: httprule.Template{
 									Version:  1,
 									OpCodes:  []int{0, 0},
@@ -10463,56 +10596,140 @@ func TestQueryParameterType(t *testing.T) {
 			},
 		},
 	}
-	expect := openapiPathsObject{{
-		Path: "/v1/echo",
-		PathItemObject: openapiPathItemObject{
-			Get: &openapiOperationObject{
-				Parameters: openapiParametersObject{
-					{
-						Name: "Address[string]",
-						In:   "query",
-						Type: "integer",
-					},
-				},
-			},
-		},
-	}}
-
 	reg := descriptor.NewRegistry()
-	reg.SetUseJSONNamesForFields(false)
+	reg.SetUseJSONNamesForFields(true)
+	// Enable the flag to omit type when $ref is present
+	reg.SetOmitArrayItemTypeWhenRefSibling(true)
 	if err := AddErrorDefs(reg); err != nil {
 		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
 		return
 	}
 	fileCL := crossLinkFixture(&file)
-	err := reg.Load(&pluginpb.CodeGeneratorRequest{
+	if err := reg.Load(&pluginpb.CodeGeneratorRequest{
 		ProtoFile: []*descriptorpb.FileDescriptorProto{
 			{
-				Name:           proto.String("person.proto"),
-				Package:        proto.String("example.com"),
 				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
-				MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
+				Name:           proto.String("acme/example.proto"),
+				Package:        proto.String("example"),
+				MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
 				Service:        []*descriptorpb.ServiceDescriptorProto{},
 				Options: &descriptorpb.FileOptions{
-					GoPackage: proto.String("person.proto"),
+					GoPackage: proto.String("acme/example"),
 				},
 			},
 		},
-	})
-	if err != nil {
-		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+	}); err != nil {
+		t.Errorf("reg.Load(%#v) failed with %v; want success", reg, err)
 		return
 	}
+	expect := openapiDefinitionsObject{
+		"rpcStatus": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "code",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type:   "integer",
+							Format: "int32",
+						},
+					},
+				},
+				keyVal{
+					Key: "message",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "string",
+						},
+					},
+				},
+				keyVal{
+					Key: "details",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "array",
+							Items: &openapiItemsObject{
+								schemaCore: schemaCore{
+									// Type should be omitted when flag is enabled
+									Ref: "#/definitions/protobufAny",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"exampleExampleMessage": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "children",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "array",
+							Items: &openapiItemsObject{
+								schemaCore: schemaCore{
+									// Type should be omitted when flag is enabled
+									Ref: "#/definitions/exampleExampleMessage",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"exampleNestDescMessage": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "children",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "array",
+							Items: &openapiItemsObject{
+								schemaCore: schemaCore{
+									// Type should be omitted when flag is enabled
+									Ref: "#/definitions/exampleExampleMessage",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"protobufAny": openapiSchemaObject{
+			schemaCore: schemaCore{
+				Type: "object",
+			},
+			Properties: &openapiSchemaObjectProperties{
+				keyVal{
+					Key: "@type",
+					Value: openapiSchemaObject{
+						schemaCore: schemaCore{
+							Type: "string",
+						},
+					},
+				},
+			},
+			AdditionalProperties: &openapiSchemaObject{},
+		},
+	}
+
 	result, err := applyTemplate(param{File: fileCL, reg: reg})
 	if err != nil {
-		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+		t.Errorf("applyTemplate(%#v) failed with %v; want success", reg, err)
 		return
 	}
 	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
 		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
 	}
-
-	if want, is, name := expect[0].PathItemObject.Get.Parameters, result.getPathItemObject("/v1/echo").Get.Parameters, "Produces"; !reflect.DeepEqual(is, want) {
+	if want, is, name := expect, result.Definitions, "Produces"; !reflect.DeepEqual(is, want) {
 		t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
 	}
 	// If there was a failure, print out the input and the json result for debugging.
@@ -10522,46 +10739,114 @@ func TestQueryParameterType(t *testing.T) {
 	}
 }
 
-func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
+func TestQueryParameterType(t *testing.T) {
+	ntDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("AddressEntry"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("key"),
+				Number:   proto.Int32(1),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				JsonName: proto.String("key"),
+			},
+			{
+				Name:     proto.String("value"),
+				Number:   proto.Int32(2),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+				JsonName: proto.String("value"),
+			},
+		},
+		Options: &descriptorpb.MessageOptions{
+			MapEntry: proto.Bool(true),
+		},
+	}
+
+	msgDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("Person"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("Address"),
+				Number:   proto.Int32(1),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".example.com.Person.AddressEntry"),
+				JsonName: proto.String("Address"),
+			},
+		},
+		NestedType: []*descriptorpb.DescriptorProto{
+			ntDesc,
+		},
+	}
+
+	nesteDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("ExampleResponse"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("Key"),
+				Number:   proto.Int32(1),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				JsonName: proto.String("Key"),
+			},
+			{
+				Name:     proto.String("Value"),
+				Number:   proto.Int32(2),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+				JsonName: proto.String("Value"),
+			},
+		},
+	}
+
 	meth := &descriptorpb.MethodDescriptorProto{
-		Name:            proto.String("Echo"),
-		InputType:       proto.String(".google.api.HttpBody"),
-		OutputType:      proto.String(".google.api.HttpBody"),
-		ClientStreaming: proto.Bool(false),
-		ServerStreaming: proto.Bool(true),
+		Name:       proto.String("Example"),
+		InputType:  proto.String("Person"),
+		OutputType: proto.String("ExampleResponse"),
 	}
 	svc := &descriptorpb.ServiceDescriptorProto{
 		Name:   proto.String("ExampleService"),
 		Method: []*descriptorpb.MethodDescriptorProto{meth},
 	}
-	httpBodyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/api/httpbody.proto")
-	if err != nil {
-		t.Fatal(err)
+	msg := &descriptor.Message{
+		DescriptorProto: msgDesc,
 	}
-	httpBodyFile.SourceLocations()
-	desc, err := protoregistry.GlobalFiles.FindDescriptorByName("google.api.HttpBody")
-	if err != nil {
-		t.Fatal(err)
+	nt := &descriptor.Message{
+		DescriptorProto: ntDesc,
 	}
-	msg := &descriptor.Message{
-		DescriptorProto: protodesc.ToDescriptorProto(desc.(protoreflect.MessageDescriptor)),
-		File: &descriptor.File{
-			FileDescriptorProto: protodesc.ToFileDescriptorProto(httpBodyFile),
+	nest := &descriptor.Message{
+		DescriptorProto: nesteDesc,
+	}
+	msg.Fields = []*descriptor.Field{
+		{
+			Message:              msg,
+			FieldDescriptorProto: msg.GetField()[0],
 		},
 	}
-	anyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/protobuf/any.proto")
-	if err != nil {
-		t.Fatal(err)
+	nt.Fields = []*descriptor.Field{
+		{
+			Message:              nt,
+			FieldDescriptorProto: msg.GetField()[0],
+		},
+	}
+	nest.Fields = []*descriptor.Field{
+		{
+			Message:              nest,
+			FieldDescriptorProto: nest.GetField()[0],
+		},
+		{
+			Message:              nest,
+			FieldDescriptorProto: nest.GetField()[1],
+		},
 	}
 	file := descriptor.File{
 		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
 			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
-			Name:           proto.String("example.proto"),
-			Package:        proto.String("example"),
-			Dependency: []string{
-				"google/api/httpbody.proto",
-			},
-			Service: []*descriptorpb.ServiceDescriptorProto{svc},
+			Name:           proto.String("person.proto"),
+			Package:        proto.String("example.com"),
+			MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
+			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
 			Options: &descriptorpb.FileOptions{
 				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
 			},
@@ -10570,6 +10855,7 @@ func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
 			Path: "example.com/path/to/example/example.pb",
 			Name: "example_pb",
 		},
+		Messages: []*descriptor.Message{msg, nest},
 		Services: []*descriptor.Service{
 			{
 				ServiceDescriptorProto: svc,
@@ -10577,10 +10863,10 @@ func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
 					{
 						MethodDescriptorProto: meth,
 						RequestType:           msg,
-						ResponseType:          msg,
+						ResponseType:          nest,
 						Bindings: []*descriptor.Binding{
 							{
-								HTTPMethod: "POST",
+								HTTPMethod: "GET",
 								PathTmpl: httprule.Template{
 									Version:  1,
 									OpCodes:  []int{0, 0},
@@ -10593,20 +10879,150 @@ func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
 			},
 		},
 	}
+	expect := openapiPathsObject{{
+		Path: "/v1/echo",
+		PathItemObject: openapiPathItemObject{
+			Get: &openapiOperationObject{
+				Parameters: openapiParametersObject{
+					{
+						Name: "Address[string]",
+						In:   "query",
+						Type: "integer",
+					},
+				},
+			},
+		},
+	}}
+
 	reg := descriptor.NewRegistry()
+	reg.SetUseJSONNamesForFields(false)
 	if err := AddErrorDefs(reg); err != nil {
 		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
 		return
 	}
-	err = reg.Load(&pluginpb.CodeGeneratorRequest{
+	fileCL := crossLinkFixture(&file)
+	err := reg.Load(&pluginpb.CodeGeneratorRequest{
 		ProtoFile: []*descriptorpb.FileDescriptorProto{
-			protodesc.ToFileDescriptorProto(anyFile),
-			protodesc.ToFileDescriptorProto(httpBodyFile),
-			file.FileDescriptorProto,
+			{
+				Name:           proto.String("person.proto"),
+				Package:        proto.String("example.com"),
+				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+				MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
+				Service:        []*descriptorpb.ServiceDescriptorProto{},
+				Options: &descriptorpb.FileOptions{
+					GoPackage: proto.String("person.proto"),
+				},
+			},
 		},
 	})
 	if err != nil {
-		t.Fatalf("failed to load code generator request: %v", err)
+		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+		return
+	}
+	result, err := applyTemplate(param{File: fileCL, reg: reg})
+	if err != nil {
+		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+		return
+	}
+	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
+		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+	}
+
+	if want, is, name := expect[0].PathItemObject.Get.Parameters, result.getPathItemObject("/v1/echo").Get.Parameters, "Produces"; !reflect.DeepEqual(is, want) {
+		t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
+	}
+	// If there was a failure, print out the input and the json result for debugging.
+	if t.Failed() {
+		t.Errorf("had: %s", file)
+		t.Errorf("got: %s", fmt.Sprint(result))
+	}
+}
+
+func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
+	meth := &descriptorpb.MethodDescriptorProto{
+		Name:            proto.String("Echo"),
+		InputType:       proto.String(".google.api.HttpBody"),
+		OutputType:      proto.String(".google.api.HttpBody"),
+		ClientStreaming: proto.Bool(false),
+		ServerStreaming: proto.Bool(true),
+	}
+	svc := &descriptorpb.ServiceDescriptorProto{
+		Name:   proto.String("ExampleService"),
+		Method: []*descriptorpb.MethodDescriptorProto{meth},
+	}
+	httpBodyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/api/httpbody.proto")
+	if err != nil {
+		t.Fatal(err)
+	}
+	httpBodyFile.SourceLocations()
+	desc, err := protoregistry.GlobalFiles.FindDescriptorByName("google.api.HttpBody")
+	if err != nil {
+		t.Fatal(err)
+	}
+	msg := &descriptor.Message{
+		DescriptorProto: protodesc.ToDescriptorProto(desc.(protoreflect.MessageDescriptor)),
+		File: &descriptor.File{
+			FileDescriptorProto: protodesc.ToFileDescriptorProto(httpBodyFile),
+		},
+	}
+	anyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/protobuf/any.proto")
+	if err != nil {
+		t.Fatal(err)
+	}
+	file := descriptor.File{
+		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+			Name:           proto.String("example.proto"),
+			Package:        proto.String("example"),
+			Dependency: []string{
+				"google/api/httpbody.proto",
+			},
+			Service: []*descriptorpb.ServiceDescriptorProto{svc},
+			Options: &descriptorpb.FileOptions{
+				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+			},
+		},
+		GoPkg: descriptor.GoPackage{
+			Path: "example.com/path/to/example/example.pb",
+			Name: "example_pb",
+		},
+		Services: []*descriptor.Service{
+			{
+				ServiceDescriptorProto: svc,
+				Methods: []*descriptor.Method{
+					{
+						MethodDescriptorProto: meth,
+						RequestType:           msg,
+						ResponseType:          msg,
+						Bindings: []*descriptor.Binding{
+							{
+								HTTPMethod: "POST",
+								PathTmpl: httprule.Template{
+									Version:  1,
+									OpCodes:  []int{0, 0},
+									Template: "/v1/echo",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	reg := descriptor.NewRegistry()
+	if err := AddErrorDefs(reg); err != nil {
+		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+		return
+	}
+	err = reg.Load(&pluginpb.CodeGeneratorRequest{
+		ProtoFile: []*descriptorpb.FileDescriptorProto{
+			protodesc.ToFileDescriptorProto(anyFile),
+			protodesc.ToFileDescriptorProto(httpBodyFile),
+			file.FileDescriptorProto,
+		},
+	})
+	if err != nil {
+		t.Fatalf("failed to load code generator request: %v", err)
 	}
 	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
 	if err != nil {
@@ -11357,6 +11773,178 @@ func TestRenderServicesOptionDeprecated(t *testing.T) {
 	}
 }
 
+func TestRenderServicesMarksDeprecatedParameters(t *testing.T) {
+	cases := []struct {
+		name                   string
+		fieldDeprecated        bool
+		enableFieldDeprecation bool
+		fieldConfigDeprecated  bool
+		expectedDeprecated     bool
+	}{
+		{
+			name:                   "proto field deprecated but feature disabled",
+			fieldDeprecated:        true,
+			enableFieldDeprecation: false,
+			expectedDeprecated:     false,
+		},
+		{
+			name:                   "proto field deprecated with feature enabled",
+			fieldDeprecated:        true,
+			enableFieldDeprecation: true,
+			expectedDeprecated:     true,
+		},
+
+		{
+			name:                  "field config annotation deprecated",
+			fieldConfigDeprecated: true,
+			expectedDeprecated:    true,
+		},
+		{
+			name:               "non-deprecated field",
+			expectedDeprecated: false,
+		},
+	}
+
+	for _, tc := range cases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			var fieldOptions *descriptorpb.FieldOptions
+			if tc.fieldDeprecated || tc.fieldConfigDeprecated {
+				fieldOptions = &descriptorpb.FieldOptions{}
+				if tc.fieldDeprecated {
+					fieldOptions.Deprecated = proto.Bool(true)
+				}
+				if tc.fieldConfigDeprecated {
+					proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, &openapi_options.JSONSchema{
+						FieldConfiguration: &openapi_options.JSONSchema_FieldConfiguration{
+							Deprecated: true,
+						},
+					})
+				}
+			}
+			fieldDesc := &descriptorpb.FieldDescriptorProto{
+				Name:     proto.String("name"),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:   proto.Int32(1),
+				Options:  fieldOptions,
+				JsonName: proto.String("name"),
+			}
+			reqMsgDesc := &descriptorpb.DescriptorProto{
+				Name:  proto.String("ExampleRequest"),
+				Field: []*descriptorpb.FieldDescriptorProto{fieldDesc},
+			}
+			respMsgDesc := &descriptorpb.DescriptorProto{
+				Name: proto.String("ExampleResponse"),
+			}
+
+			reqMsg := &descriptor.Message{
+				DescriptorProto: reqMsgDesc,
+			}
+			respMsg := &descriptor.Message{
+				DescriptorProto: respMsgDesc,
+			}
+			field := &descriptor.Field{
+				FieldDescriptorProto: fieldDesc,
+				Message:              reqMsg,
+			}
+			reqMsg.Fields = []*descriptor.Field{field}
+
+			method := &descriptorpb.MethodDescriptorProto{
+				Name:       proto.String("GetExample"),
+				InputType:  proto.String(".example.ExampleRequest"),
+				OutputType: proto.String(".example.ExampleResponse"),
+			}
+
+			compiler, err := httprule.Parse("/v1/{name}")
+			if err != nil {
+				t.Fatalf("failed to parse path template: %v", err)
+			}
+			pathTemplate := compiler.Compile()
+
+			binding := &descriptor.Binding{
+				HTTPMethod: "GET",
+				PathTmpl:   pathTemplate,
+				PathParams: []descriptor.Parameter{
+					{
+						Target: field,
+						FieldPath: descriptor.FieldPath{
+							{
+								Name:   "name",
+								Target: field,
+							},
+						},
+					},
+				},
+			}
+
+			methodDesc := &descriptor.Method{
+				MethodDescriptorProto: method,
+				RequestType:           reqMsg,
+				ResponseType:          respMsg,
+				Bindings:              []*descriptor.Binding{binding},
+			}
+
+			service := &descriptorpb.ServiceDescriptorProto{
+				Name:   proto.String("ExampleService"),
+				Method: []*descriptorpb.MethodDescriptorProto{method},
+			}
+
+			serviceDesc := &descriptor.Service{
+				ServiceDescriptorProto: service,
+				Methods:                []*descriptor.Method{methodDesc},
+			}
+
+			file := descriptor.File{
+				FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+					Name:           proto.String("example.proto"),
+					Package:        proto.String("example"),
+					MessageType:    []*descriptorpb.DescriptorProto{reqMsgDesc, respMsgDesc},
+					Service:        []*descriptorpb.ServiceDescriptorProto{service},
+					Options: &descriptorpb.FileOptions{
+						GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+					},
+				},
+				GoPkg: descriptor.GoPackage{
+					Path: "example.com/path/to/example/example.pb",
+					Name: "example_pb",
+				},
+				Messages: []*descriptor.Message{reqMsg, respMsg},
+				Services: []*descriptor.Service{serviceDesc},
+			}
+
+			reg := descriptor.NewRegistry()
+			reg.SetEnableFieldDeprecation(tc.enableFieldDeprecation)
+			fileCL := crossLinkFixture(&file)
+			if err := reg.Load(reqFromFile(fileCL)); err != nil {
+				t.Fatalf("failed to load code generator request: %v", err)
+			}
+
+			result, err := applyTemplate(param{File: fileCL, reg: reg})
+			if err != nil {
+				t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+			}
+
+			operation := result.getPathItemObject("/v1/{name}").Get
+			if operation == nil {
+				t.Fatalf("expected GET operation to be generated for /v1/{name}")
+			}
+			found := false
+			for _, param := range operation.Parameters {
+				if param.In == "path" && param.Name == "name" {
+					found = true
+					if param.Deprecated != tc.expectedDeprecated {
+						t.Fatalf("expected deprecated flag to be %v, got %v", tc.expectedDeprecated, param.Deprecated)
+					}
+				}
+			}
+			if !found {
+				t.Fatalf("expected to find path parameter named 'name'")
+			}
+		})
+	}
+}
+
 func Test_updateSwaggerObjectFromFieldBehavior(t *testing.T) {
 	type args struct {
 		s     *openapiSchemaObject
@@ -11437,3 +12025,702 @@ func Test_updateSwaggerObjectFromFieldBehavior(t *testing.T) {
 		})
 	}
 }
+// TestBodyParameterRequiredFieldBug tests the bug where the body parameter name
+// is incorrectly added to the schema's required array when using body: "field_name"
+func TestBodyParameterRequiredFieldBug(t *testing.T) {
+	fieldBehaviorRequired := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+	requiredFieldOptions := new(descriptorpb.FieldOptions)
+	proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
+
+	// Define the Comment message with REQUIRED fields including one named "comment"
+	commentDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("Comment"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:   proto.String("name"),
+				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number: proto.Int32(1),
+			},
+			{
+				Name:    proto.String("comment"),
+				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:  proto.Int32(2),
+				Options: requiredFieldOptions, // comment field is REQUIRED
+			},
+			{
+				Name:    proto.String("resource"),
+				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:  proto.Int32(3),
+				Options: requiredFieldOptions, // resource is REQUIRED
+			},
+			{
+				Name:    proto.String("author"),
+				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:  proto.Int32(4),
+				Options: requiredFieldOptions, // author is REQUIRED
+			},
+		},
+	}
+
+	// Define the UpdateCommentRequest message
+	updateCommentReqDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("UpdateCommentRequest"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("comment"),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".test.Comment"),
+				Number:   proto.Int32(1),
+				Options:  requiredFieldOptions, // comment field is REQUIRED
+			},
+		},
+	}
+
+	commentMsg := &descriptor.Message{
+		DescriptorProto: commentDesc,
+	}
+
+	updateCommentReqMsg := &descriptor.Message{
+		DescriptorProto: updateCommentReqDesc,
+	}
+
+	nameField := &descriptor.Field{
+		Message:              commentMsg,
+		FieldDescriptorProto: commentMsg.GetField()[0],
+	}
+	commentField := &descriptor.Field{
+		Message:              commentMsg,
+		FieldDescriptorProto: commentMsg.GetField()[1],
+	}
+	resourceField := &descriptor.Field{
+		Message:              commentMsg,
+		FieldDescriptorProto: commentMsg.GetField()[2],
+	}
+	authorField := &descriptor.Field{
+		Message:              commentMsg,
+		FieldDescriptorProto: commentMsg.GetField()[3],
+	}
+	commentMsg.Fields = []*descriptor.Field{nameField, commentField, resourceField, authorField}
+
+	commentReqField := &descriptor.Field{
+		Message:              updateCommentReqMsg,
+		FieldMessage:         commentMsg,
+		FieldDescriptorProto: updateCommentReqMsg.GetField()[0],
+	}
+	updateCommentReqMsg.Fields = []*descriptor.Field{commentReqField}
+
+	meth := &descriptorpb.MethodDescriptorProto{
+		Name:       proto.String("UpdateComment"),
+		InputType:  proto.String("UpdateCommentRequest"),
+		OutputType: proto.String("Comment"),
+	}
+
+	svc := &descriptorpb.ServiceDescriptorProto{
+		Name:   proto.String("CommentService"),
+		Method: []*descriptorpb.MethodDescriptorProto{meth},
+	}
+
+	file := descriptor.File{
+		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+			Name:           proto.String("comment.proto"),
+			Package:        proto.String("test"),
+			MessageType:    []*descriptorpb.DescriptorProto{commentDesc, updateCommentReqDesc},
+			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
+			Options: &descriptorpb.FileOptions{
+				GoPackage: proto.String("github.com/example/test;test"),
+			},
+		},
+		GoPkg: descriptor.GoPackage{
+			Path: "example.com/path/to/test/test.pb",
+			Name: "test_pb",
+		},
+		Messages: []*descriptor.Message{commentMsg, updateCommentReqMsg},
+		Services: []*descriptor.Service{
+			{
+				ServiceDescriptorProto: svc,
+				Methods: []*descriptor.Method{
+					{
+						MethodDescriptorProto: meth,
+						RequestType:           updateCommentReqMsg,
+						ResponseType:          commentMsg,
+						Bindings: []*descriptor.Binding{
+							{
+								HTTPMethod: "PATCH",
+								PathTmpl: httprule.Template{
+									Version:  1,
+									OpCodes:  []int{0, 0},
+									Template: "/api/v1/{comment.name}",
+								},
+								PathParams: []descriptor.Parameter{
+									{
+										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+											{
+												Name: "comment",
+											},
+											{
+												Name: "name",
+											},
+										}),
+										Target: nameField,
+									},
+								},
+								Body: &descriptor.Body{
+									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+										{
+											Name:   "comment",
+											Target: commentReqField,
+										},
+									}),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	reg := descriptor.NewRegistry()
+	fileCL := crossLinkFixture(&file)
+	err := reg.Load(reqFromFile(fileCL))
+	if err != nil {
+		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+		return
+	}
+
+	result, err := applyTemplate(param{File: fileCL, reg: reg})
+	if err != nil {
+		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+	}
+
+	paths := GetPaths(result)
+	if got, want := len(paths), 1; got != want {
+		t.Fatalf("Results path length differed, got %d want %d", got, want)
+	}
+	if got, want := paths[0], "/api/v1/{comment.name}"; got != want {
+		t.Fatalf("Wrong results path, got %s want %s", got, want)
+	}
+
+	operation := *result.getPathItemObject("/api/v1/{comment.name}").Patch
+	if len(operation.Parameters) < 2 {
+		t.Fatalf("Expected at least 2 parameters, got %d", len(operation.Parameters))
+	}
+
+	// Find the body parameter
+	var bodyParam *openapiParameterObject
+	for i := range operation.Parameters {
+		if operation.Parameters[i].In == "body" {
+			bodyParam = &operation.Parameters[i]
+			break
+		}
+	}
+
+	if bodyParam == nil {
+		t.Fatal("Body parameter not found")
+	}
+
+	if bodyParam.Name != "comment" {
+		t.Fatalf("Wrong body parameter name, got %s want comment", bodyParam.Name)
+	}
+
+	if bodyParam.Schema == nil {
+		t.Fatal("Body parameter schema is nil")
+	}
+
+	// Check the required array
+	// Expected: ["comment", "resource", "author"] (each appears once)
+	// Bug: ["comment", "resource", "author", "comment"] (comment appears twice)
+
+	expectedRequired := []string{"comment", "resource", "author"}
+
+	// Check for duplicates
+	seen := make(map[string]int)
+	for _, req := range bodyParam.Schema.Required {
+		seen[req]++
+	}
+
+	var duplicates []string
+	for field, count := range seen {
+		if count > 1 {
+			duplicates = append(duplicates, field)
+		}
+	}
+
+	if len(duplicates) > 0 {
+		t.Errorf("BUG REPRODUCED: Duplicate entries in required array: %v", duplicates)
+		t.Errorf("Full required array: %v", bodyParam.Schema.Required)
+	}
+
+	// Also check that we don't have extra fields that aren't properties
+	if bodyParam.Schema.Properties != nil {
+		propertyNames := make(map[string]bool)
+		for _, prop := range *bodyParam.Schema.Properties {
+			propertyNames[prop.Key] = true
+		}
+
+		var invalidRequired []string
+		for _, req := range bodyParam.Schema.Required {
+			if !propertyNames[req] {
+				invalidRequired = append(invalidRequired, req)
+			}
+		}
+
+		if len(invalidRequired) > 0 {
+			t.Errorf("BUG: Required array contains fields that are not properties: %v", invalidRequired)
+		}
+	}
+
+	// Verify the required array matches expectations (no duplicates, all valid)
+	requiredUnique := make([]string, 0, len(bodyParam.Schema.Required))
+	seenMap := make(map[string]bool)
+	for _, req := range bodyParam.Schema.Required {
+		if !seenMap[req] {
+			seenMap[req] = true
+			requiredUnique = append(requiredUnique, req)
+		}
+	}
+
+	if !reflect.DeepEqual(requiredUnique, expectedRequired) {
+		t.Errorf("Required array (after deduplication) doesn't match expected:\n"+
+			"  got  = %v\n"+
+			"  want = %v\n"+
+			"  diff = %s",
+			requiredUnique, expectedRequired, cmp.Diff(expectedRequired, requiredUnique))
+	}
+}
+
+// TestNestedRequiredFieldsNotHoisted tests the bug where nested required fields
+// are incorrectly hoisted to the parent body schema's required array when path
+// parameters reference nested fields
+func TestNestedRequiredFieldsNotHoisted(t *testing.T) {
+	fieldBehaviorRequired := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+	requiredFieldOptions := new(descriptorpb.FieldOptions)
+	proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
+
+	// Define the nested message (Foo) with REQUIRED fields
+	fooDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("Foo"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:    proto.String("name"),
+				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:  proto.Int32(1),
+				Options: requiredFieldOptions, // name is REQUIRED
+			},
+			{
+				Name:    proto.String("value"),
+				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:  proto.Int32(2),
+				Options: requiredFieldOptions, // value is REQUIRED
+			},
+		},
+	}
+
+	// Define the request message (UpdateFooRequest)
+	updateFooReqDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("UpdateFooRequest"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("thing"),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".test.Foo"),
+				Number:   proto.Int32(1),
+				Options:  requiredFieldOptions, // thing is REQUIRED
+			},
+			{
+				Name:   proto.String("update_mask"),
+				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), // Simplified - normally FieldMask
+				Number: proto.Int32(2),
+			},
+		},
+	}
+
+	fooMsg := &descriptor.Message{
+		DescriptorProto: fooDesc,
+	}
+
+	updateFooReqMsg := &descriptor.Message{
+		DescriptorProto: updateFooReqDesc,
+	}
+
+	nameField := &descriptor.Field{
+		Message:              fooMsg,
+		FieldDescriptorProto: fooMsg.GetField()[0],
+	}
+	valueField := &descriptor.Field{
+		Message:              fooMsg,
+		FieldDescriptorProto: fooMsg.GetField()[1],
+	}
+	fooMsg.Fields = []*descriptor.Field{nameField, valueField}
+
+	thingField := &descriptor.Field{
+		Message:              updateFooReqMsg,
+		FieldMessage:         fooMsg,
+		FieldDescriptorProto: updateFooReqMsg.GetField()[0],
+	}
+	updateMaskField := &descriptor.Field{
+		Message:              updateFooReqMsg,
+		FieldDescriptorProto: updateFooReqMsg.GetField()[1],
+	}
+	updateFooReqMsg.Fields = []*descriptor.Field{thingField, updateMaskField}
+
+	meth := &descriptorpb.MethodDescriptorProto{
+		Name:       proto.String("UpdateFoo"),
+		InputType:  proto.String("UpdateFooRequest"),
+		OutputType: proto.String("Foo"),
+	}
+
+	svc := &descriptorpb.ServiceDescriptorProto{
+		Name:   proto.String("FooService"),
+		Method: []*descriptorpb.MethodDescriptorProto{meth},
+	}
+
+	file := descriptor.File{
+		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+			Name:           proto.String("foo.proto"),
+			Package:        proto.String("test"),
+			MessageType:    []*descriptorpb.DescriptorProto{fooDesc, updateFooReqDesc},
+			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
+			Options: &descriptorpb.FileOptions{
+				GoPackage: proto.String("github.com/example/test;test"),
+			},
+		},
+		GoPkg: descriptor.GoPackage{
+			Path: "example.com/path/to/test/test.pb",
+			Name: "test_pb",
+		},
+		Messages: []*descriptor.Message{fooMsg, updateFooReqMsg},
+		Services: []*descriptor.Service{
+			{
+				ServiceDescriptorProto: svc,
+				Methods: []*descriptor.Method{
+					{
+						MethodDescriptorProto: meth,
+						RequestType:           updateFooReqMsg,
+						ResponseType:          fooMsg,
+						Bindings: []*descriptor.Binding{
+							{
+								HTTPMethod: "PATCH",
+								PathTmpl: httprule.Template{
+									Version:  1,
+									OpCodes:  []int{0, 0},
+									Template: "/api/v1/{thing.name}",
+								},
+								PathParams: []descriptor.Parameter{
+									{
+										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+											{
+												Name: "thing",
+											},
+											{
+												Name: "name",
+											},
+										}),
+										Target: nameField,
+									},
+								},
+								Body: &descriptor.Body{
+									FieldPath: []descriptor.FieldPathComponent{}, // body: "*"
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	reg := descriptor.NewRegistry()
+	fileCL := crossLinkFixture(&file)
+	err := reg.Load(reqFromFile(fileCL))
+	if err != nil {
+		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+		return
+	}
+
+	result, err := applyTemplate(param{File: fileCL, reg: reg})
+	if err != nil {
+		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+	}
+
+	paths := GetPaths(result)
+	if got, want := len(paths), 1; got != want {
+		t.Fatalf("Results path length differed, got %d want %d", got, want)
+	}
+	if got, want := paths[0], "/api/v1/{thing.name}"; got != want {
+		t.Fatalf("Wrong results path, got %s want %s", got, want)
+	}
+
+	operation := *result.getPathItemObject("/api/v1/{thing.name}").Patch
+	if len(operation.Parameters) < 2 {
+		t.Fatalf("Expected at least 2 parameters, got %d", len(operation.Parameters))
+	}
+
+	if got, want := operation.Parameters[0].Name, "thing.name"; got != want {
+		t.Fatalf("Wrong parameter name 0, got %s want %s", got, want)
+	}
+	if got, want := operation.Parameters[0].In, "path"; got != want {
+		t.Fatalf("Wrong parameter location 0, got %s want %s", got, want)
+	}
+
+	if got, want := operation.Parameters[1].Name, "body"; got != want {
+		t.Fatalf("Wrong parameter name 1, got %s want %s", got, want)
+	}
+	if got, want := operation.Parameters[1].In, "body"; got != want {
+		t.Fatalf("Wrong parameter location 1, got %s want %s", got, want)
+	}
+
+	bodySchemaRef := operation.Parameters[1].Schema.schemaCore.Ref
+	if bodySchemaRef == "" {
+		t.Fatal("Body schema reference is empty")
+	}
+
+	defName := strings.TrimPrefix(bodySchemaRef, "#/definitions/")
+	definition, found := result.Definitions[defName]
+	if !found {
+		t.Fatalf("expecting definition to contain %s", defName)
+	}
+
+	// Verify that nested required fields are NOT hoisted to parent level
+	correctRequiredFields := []string{"thing"}
+	if got, want := definition.Required, correctRequiredFields; !reflect.DeepEqual(got, want) {
+		t.Errorf("Nested required fields were incorrectly hoisted to parent level.\n"+
+			"Body definition required fields:\n"+
+			"  got  = %v\n"+
+			"  want = %v (only top-level field names)\n"+
+			"Nested field 'value' should be in 'thing' property's required array, not parent's.",
+			got, want)
+	}
+
+	var thingKV *keyVal
+	if definition.Properties != nil {
+		for i := range *definition.Properties {
+			if (*definition.Properties)[i].Key == "thing" {
+				thingKV = &(*definition.Properties)[i]
+				break
+			}
+		}
+	}
+
+	if thingKV == nil {
+		t.Fatal("'thing' property not found in body definition")
+	}
+
+	if _, ok := thingKV.Value.(openapiSchemaObject); !ok {
+		t.Fatal("'thing' property value is not an openapiSchemaObject")
+	}
+}
+
+// TestBodyParameterSelfReferentialBug tests the bug where the body parameter name
+// is added to the required array even though it's not a property of the schema
+func TestBodyParameterSelfReferentialBug(t *testing.T) {
+	fieldBehaviorRequired := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+	requiredFieldOptions := new(descriptorpb.FieldOptions)
+	proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
+
+	// Define the Direction message with REQUIRED fields (but NO field named "direction")
+	directionDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("Direction"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:   proto.String("name"),
+				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number: proto.Int32(1),
+			},
+			{
+				Name:    proto.String("title"),
+				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Number:  proto.Int32(2),
+				Options: requiredFieldOptions,
+			},
+		},
+	}
+
+	// Define the UpdateDirectionRequest message
+	updateDirectionReqDesc := &descriptorpb.DescriptorProto{
+		Name: proto.String("UpdateDirectionRequest"),
+		Field: []*descriptorpb.FieldDescriptorProto{
+			{
+				Name:     proto.String("direction"),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+				TypeName: proto.String(".test.Direction"),
+				Number:   proto.Int32(1),
+				Options:  requiredFieldOptions, // direction field is REQUIRED
+			},
+		},
+	}
+
+	directionMsg := &descriptor.Message{
+		DescriptorProto: directionDesc,
+	}
+
+	updateDirectionReqMsg := &descriptor.Message{
+		DescriptorProto: updateDirectionReqDesc,
+	}
+
+	nameField := &descriptor.Field{
+		Message:              directionMsg,
+		FieldDescriptorProto: directionMsg.GetField()[0],
+	}
+	titleField := &descriptor.Field{
+		Message:              directionMsg,
+		FieldDescriptorProto: directionMsg.GetField()[1],
+	}
+	directionMsg.Fields = []*descriptor.Field{nameField, titleField}
+
+	directionReqField := &descriptor.Field{
+		Message:              updateDirectionReqMsg,
+		FieldMessage:         directionMsg,
+		FieldDescriptorProto: updateDirectionReqMsg.GetField()[0],
+	}
+	updateDirectionReqMsg.Fields = []*descriptor.Field{directionReqField}
+
+	meth := &descriptorpb.MethodDescriptorProto{
+		Name:       proto.String("UpdateDirection"),
+		InputType:  proto.String("UpdateDirectionRequest"),
+		OutputType: proto.String("Direction"),
+	}
+
+	svc := &descriptorpb.ServiceDescriptorProto{
+		Name:   proto.String("DirectionService"),
+		Method: []*descriptorpb.MethodDescriptorProto{meth},
+	}
+
+	file := descriptor.File{
+		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+			Name:           proto.String("direction.proto"),
+			Package:        proto.String("test"),
+			MessageType:    []*descriptorpb.DescriptorProto{directionDesc, updateDirectionReqDesc},
+			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
+			Options: &descriptorpb.FileOptions{
+				GoPackage: proto.String("github.com/example/test;test"),
+			},
+		},
+		GoPkg: descriptor.GoPackage{
+			Path: "example.com/path/to/test/test.pb",
+			Name: "test_pb",
+		},
+		Messages: []*descriptor.Message{directionMsg, updateDirectionReqMsg},
+		Services: []*descriptor.Service{
+			{
+				ServiceDescriptorProto: svc,
+				Methods: []*descriptor.Method{
+					{
+						MethodDescriptorProto: meth,
+						RequestType:           updateDirectionReqMsg,
+						ResponseType:          directionMsg,
+						Bindings: []*descriptor.Binding{
+							{
+								HTTPMethod: "PATCH",
+								PathTmpl: httprule.Template{
+									Version:  1,
+									OpCodes:  []int{0, 0},
+									Template: "/api/v1/{direction.name}",
+								},
+								PathParams: []descriptor.Parameter{
+									{
+										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+											{
+												Name: "direction",
+											},
+											{
+												Name: "name",
+											},
+										}),
+										Target: nameField,
+									},
+								},
+								Body: &descriptor.Body{
+									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+										{
+											Name:   "direction",
+											Target: directionReqField,
+										},
+									}),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	reg := descriptor.NewRegistry()
+	fileCL := crossLinkFixture(&file)
+	err := reg.Load(reqFromFile(fileCL))
+	if err != nil {
+		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+		return
+	}
+
+	result, err := applyTemplate(param{File: fileCL, reg: reg})
+	if err != nil {
+		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+	}
+
+	paths := GetPaths(result)
+	if got, want := len(paths), 1; got != want {
+		t.Fatalf("Results path length differed, got %d want %d", got, want)
+	}
+
+	operation := *result.getPathItemObject("/api/v1/{direction.name}").Patch
+
+	// Find the body parameter
+	var bodyParam *openapiParameterObject
+	for i := range operation.Parameters {
+		if operation.Parameters[i].In == "body" {
+			bodyParam = &operation.Parameters[i]
+			break
+		}
+	}
+
+	if bodyParam == nil {
+		t.Fatal("Body parameter not found")
+	}
+
+	if bodyParam.Schema == nil {
+		t.Fatal("Body parameter schema is nil")
+	}
+
+	// Check that we don't have the parameter name in the required array
+	// when it's not actually a property of the schema
+	expectedRequired := []string{"title"}
+
+	if bodyParam.Schema.Properties != nil {
+		propertyNames := make(map[string]bool)
+		for _, prop := range *bodyParam.Schema.Properties {
+			propertyNames[prop.Key] = true
+		}
+
+		var invalidRequired []string
+		for _, req := range bodyParam.Schema.Required {
+			if !propertyNames[req] {
+				invalidRequired = append(invalidRequired, req)
+			}
+		}
+
+		if len(invalidRequired) > 0 {
+			t.Errorf("BUG REPRODUCED: Required array contains fields that are not properties: %v", invalidRequired)
+			t.Errorf("Full required array: %v", bodyParam.Schema.Required)
+			t.Errorf("Available properties: %v", func() []string {
+				keys := make([]string, 0, len(*bodyParam.Schema.Properties))
+				for _, prop := range *bodyParam.Schema.Properties {
+					keys = append(keys, prop.Key)
+				}
+				return keys
+			}())
+		}
+	}
+
+	if !reflect.DeepEqual(bodyParam.Schema.Required, expectedRequired) {
+		t.Errorf("Required array doesn't match expected:\n"+
+			"  got  = %v\n"+
+			"  want = %v\n"+
+			"  diff = %s",
+			bodyParam.Schema.Required, expectedRequired, cmp.Diff(expectedRequired, bodyParam.Schema.Required))
+	}
+}