@confluentinc/mcp-confluent 1.2.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -240
- package/assets/oauth-templates/callback-failure.html +22 -0
- package/assets/oauth-templates/callback-success.html +99 -0
- package/assets/oauth-templates/logo.html +91 -0
- package/assets/oauth-templates/styles.html +110 -0
- package/config.example.yaml +149 -0
- package/config.oauth.example.yaml +45 -0
- package/dist/cli.d.ts +59 -12
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +131 -61
- package/dist/cli.js.map +1 -1
- package/dist/config/env-config.d.ts +33 -0
- package/dist/config/env-config.d.ts.map +1 -0
- package/dist/config/env-config.js +478 -0
- package/dist/config/env-config.js.map +1 -0
- package/dist/config/index.d.ts +31 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +76 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/interpolation.d.ts +9 -0
- package/dist/config/interpolation.d.ts.map +1 -0
- package/dist/config/interpolation.js +51 -0
- package/dist/config/interpolation.js.map +1 -0
- package/dist/config/models.d.ts +399 -0
- package/dist/config/models.d.ts.map +1 -0
- package/dist/config/models.js +406 -0
- package/dist/config/models.js.map +1 -0
- package/dist/config/validation.d.ts +9 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +46 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/confluent/base-client-manager.d.ts +133 -0
- package/dist/confluent/base-client-manager.d.ts.map +1 -0
- package/dist/confluent/base-client-manager.js +188 -0
- package/dist/confluent/base-client-manager.js.map +1 -0
- package/dist/confluent/client-manager.d.ts +42 -77
- package/dist/confluent/client-manager.d.ts.map +1 -1
- package/dist/confluent/client-manager.js +5 -213
- package/dist/confluent/client-manager.js.map +1 -1
- package/dist/confluent/config-telemetry.d.ts +41 -0
- package/dist/confluent/config-telemetry.d.ts.map +1 -0
- package/dist/confluent/config-telemetry.js +17 -0
- package/dist/confluent/config-telemetry.js.map +1 -0
- package/dist/confluent/direct-client-manager.d.ts +43 -0
- package/dist/confluent/direct-client-manager.d.ts.map +1 -0
- package/dist/confluent/direct-client-manager.js +154 -0
- package/dist/confluent/direct-client-manager.js.map +1 -0
- package/dist/confluent/middleware.d.ts +32 -4
- package/dist/confluent/middleware.d.ts.map +1 -1
- package/dist/confluent/middleware.js +46 -2
- package/dist/confluent/middleware.js.map +1 -1
- package/dist/confluent/node-deps.d.ts +33 -39
- package/dist/confluent/node-deps.d.ts.map +1 -1
- package/dist/confluent/node-deps.js +48 -7
- package/dist/confluent/node-deps.js.map +1 -1
- package/dist/confluent/oauth/auth-context.d.ts +94 -0
- package/dist/confluent/oauth/auth-context.d.ts.map +1 -0
- package/dist/confluent/oauth/auth-context.js +323 -0
- package/dist/confluent/oauth/auth-context.js.map +1 -0
- package/dist/confluent/oauth/auth0-config.d.ts +12 -0
- package/dist/confluent/oauth/auth0-config.d.ts.map +1 -0
- package/dist/confluent/oauth/auth0-config.js +43 -0
- package/dist/confluent/oauth/auth0-config.js.map +1 -0
- package/dist/confluent/oauth/callback-pages.d.ts +13 -0
- package/dist/confluent/oauth/callback-pages.d.ts.map +1 -0
- package/dist/confluent/oauth/callback-pages.js +47 -0
- package/dist/confluent/oauth/callback-pages.js.map +1 -0
- package/dist/confluent/oauth/crypto-utils.d.ts +16 -0
- package/dist/confluent/oauth/crypto-utils.d.ts.map +1 -0
- package/dist/confluent/oauth/crypto-utils.js +23 -0
- package/dist/confluent/oauth/crypto-utils.js.map +1 -0
- package/dist/confluent/oauth/errors.d.ts +27 -0
- package/dist/confluent/oauth/errors.d.ts.map +1 -0
- package/dist/confluent/oauth/errors.js +6 -0
- package/dist/confluent/oauth/errors.js.map +1 -0
- package/dist/confluent/oauth/oauth-holder.d.ts +34 -0
- package/dist/confluent/oauth/oauth-holder.d.ts.map +1 -0
- package/dist/confluent/oauth/oauth-holder.js +155 -0
- package/dist/confluent/oauth/oauth-holder.js.map +1 -0
- package/dist/confluent/oauth/pkce-login.d.ts +45 -0
- package/dist/confluent/oauth/pkce-login.d.ts.map +1 -0
- package/dist/confluent/oauth/pkce-login.js +270 -0
- package/dist/confluent/oauth/pkce-login.js.map +1 -0
- package/dist/confluent/oauth/token-chain.d.ts +53 -0
- package/dist/confluent/oauth/token-chain.d.ts.map +1 -0
- package/dist/confluent/oauth/token-chain.js +141 -0
- package/dist/confluent/oauth/token-chain.js.map +1 -0
- package/dist/confluent/oauth/token-lifetimes.d.ts +13 -0
- package/dist/confluent/oauth/token-lifetimes.d.ts.map +1 -0
- package/dist/confluent/oauth/token-lifetimes.js +13 -0
- package/dist/confluent/oauth/token-lifetimes.js.map +1 -0
- package/dist/confluent/oauth/types.d.ts +68 -0
- package/dist/confluent/oauth/types.d.ts.map +1 -0
- package/dist/confluent/oauth/types.js +2 -0
- package/dist/confluent/oauth/types.js.map +1 -0
- package/dist/confluent/oauth-client-manager.d.ts +78 -0
- package/dist/confluent/oauth-client-manager.d.ts.map +1 -0
- package/dist/confluent/oauth-client-manager.js +265 -0
- package/dist/confluent/oauth-client-manager.js.map +1 -0
- package/dist/confluent/oauth-resource-resolvers.d.ts +6 -0
- package/dist/confluent/oauth-resource-resolvers.d.ts.map +1 -0
- package/dist/confluent/oauth-resource-resolvers.js +65 -0
- package/dist/confluent/oauth-resource-resolvers.js.map +1 -0
- package/dist/confluent/schema-registry-helper.d.ts +5 -9
- package/dist/confluent/schema-registry-helper.d.ts.map +1 -1
- package/dist/confluent/schema-registry-helper.js +1 -9
- package/dist/confluent/schema-registry-helper.js.map +1 -1
- package/dist/confluent/telemetry.d.ts +38 -1
- package/dist/confluent/telemetry.d.ts.map +1 -1
- package/dist/confluent/telemetry.js +37 -8
- package/dist/confluent/telemetry.js.map +1 -1
- package/dist/confluent/tools/base-tools.d.ts +140 -23
- package/dist/confluent/tools/base-tools.d.ts.map +1 -1
- package/dist/confluent/tools/base-tools.js +98 -4
- package/dist/confluent/tools/base-tools.js.map +1 -1
- package/dist/confluent/tools/cluster-arg-resolvers.d.ts +75 -0
- package/dist/confluent/tools/cluster-arg-resolvers.d.ts.map +1 -0
- package/dist/confluent/tools/cluster-arg-resolvers.js +127 -0
- package/dist/confluent/tools/cluster-arg-resolvers.js.map +1 -0
- package/dist/confluent/tools/connection-predicates.d.ts +176 -0
- package/dist/confluent/tools/connection-predicates.d.ts.map +1 -0
- package/dist/confluent/tools/connection-predicates.js +284 -0
- package/dist/confluent/tools/connection-predicates.js.map +1 -0
- package/dist/confluent/tools/handlers/billing/list-billing-costs-handler.d.ts +5 -6
- package/dist/confluent/tools/handlers/billing/list-billing-costs-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/billing/list-billing-costs-handler.js +40 -11
- package/dist/confluent/tools/handlers/billing/list-billing-costs-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/catalog/add-tags-to-topic.d.ts +6 -6
- package/dist/confluent/tools/handlers/catalog/add-tags-to-topic.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/catalog/add-tags-to-topic.js +6 -9
- package/dist/confluent/tools/handlers/catalog/add-tags-to-topic.js.map +1 -1
- package/dist/confluent/tools/handlers/catalog/create-topic-tags.d.ts +6 -6
- package/dist/confluent/tools/handlers/catalog/create-topic-tags.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/catalog/create-topic-tags.js +6 -9
- package/dist/confluent/tools/handlers/catalog/create-topic-tags.js.map +1 -1
- package/dist/confluent/tools/handlers/catalog/delete-tag.d.ts +6 -6
- package/dist/confluent/tools/handlers/catalog/delete-tag.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/catalog/delete-tag.js +6 -9
- package/dist/confluent/tools/handlers/catalog/delete-tag.js.map +1 -1
- package/dist/confluent/tools/handlers/catalog/list-tags.d.ts +6 -6
- package/dist/confluent/tools/handlers/catalog/list-tags.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/catalog/list-tags.js +6 -9
- package/dist/confluent/tools/handlers/catalog/list-tags.js.map +1 -1
- package/dist/confluent/tools/handlers/catalog/remove-tag-from-entity.d.ts +6 -6
- package/dist/confluent/tools/handlers/catalog/remove-tag-from-entity.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/catalog/remove-tag-from-entity.js +6 -9
- package/dist/confluent/tools/handlers/catalog/remove-tag-from-entity.js.map +1 -1
- package/dist/confluent/tools/handlers/clusters/list-clusters-handler.d.ts +5 -6
- package/dist/confluent/tools/handlers/clusters/list-clusters-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/clusters/list-clusters-handler.js +11 -13
- package/dist/confluent/tools/handlers/clusters/list-clusters-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/connect/connect-tool-handler.d.ts +34 -0
- package/dist/confluent/tools/handlers/connect/connect-tool-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/connect-tool-handler.js +47 -0
- package/dist/confluent/tools/handlers/connect/connect-tool-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/create-connector-handler.d.ts +11 -7
- package/dist/confluent/tools/handlers/connect/create-connector-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/connect/create-connector-handler.js +21 -21
- package/dist/confluent/tools/handlers/connect/create-connector-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/connect/delete-connector-handler.d.ts +6 -7
- package/dist/confluent/tools/handlers/connect/delete-connector-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/connect/delete-connector-handler.js +10 -15
- package/dist/confluent/tools/handlers/connect/delete-connector-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/connect/get-connector-config-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-config-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-config-handler.js +35 -0
- package/dist/confluent/tools/handlers/connect/get-connector-config-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-recommendations-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-recommendations-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-recommendations-handler.js +66 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-recommendations-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-summary-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-summary-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-summary-handler.js +131 -0
- package/dist/confluent/tools/handlers/connect/get-connector-error-summary-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-logs-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-logs-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-logs-handler.js +244 -0
- package/dist/confluent/tools/handlers/connect/get-connector-logs-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-offsets-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-offsets-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-offsets-handler.js +35 -0
- package/dist/confluent/tools/handlers/connect/get-connector-offsets-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-status-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-status-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-status-handler.js +44 -0
- package/dist/confluent/tools/handlers/connect/get-connector-status-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-tasks-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/get-connector-tasks-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/get-connector-tasks-handler.js +35 -0
- package/dist/confluent/tools/handlers/connect/get-connector-tasks-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/list-connectors-handler.d.ts +6 -7
- package/dist/confluent/tools/handlers/connect/list-connectors-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/connect/list-connectors-handler.js +15 -17
- package/dist/confluent/tools/handlers/connect/list-connectors-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/connect/pause-connector-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/pause-connector-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/pause-connector-handler.js +35 -0
- package/dist/confluent/tools/handlers/connect/pause-connector-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/restart-connector-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/restart-connector-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/restart-connector-handler.js +35 -0
- package/dist/confluent/tools/handlers/connect/restart-connector-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/resume-connector-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/resume-connector-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/resume-connector-handler.js +35 -0
- package/dist/confluent/tools/handlers/connect/resume-connector-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/connect/update-connector-config-handler.d.ts +9 -0
- package/dist/confluent/tools/handlers/connect/update-connector-config-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/connect/update-connector-config-handler.js +44 -0
- package/dist/confluent/tools/handlers/connect/update-connector-config-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/diagnostics/explain-disabled-tools-handler.d.ts +58 -0
- package/dist/confluent/tools/handlers/diagnostics/explain-disabled-tools-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/diagnostics/explain-disabled-tools-handler.js +135 -0
- package/dist/confluent/tools/handlers/diagnostics/explain-disabled-tools-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/docs/get-product-doc-page-handler.d.ts +18 -0
- package/dist/confluent/tools/handlers/docs/get-product-doc-page-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/docs/get-product-doc-page-handler.js +235 -0
- package/dist/confluent/tools/handlers/docs/get-product-doc-page-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/docs/search-product-docs-handler.d.ts +14 -0
- package/dist/confluent/tools/handlers/docs/search-product-docs-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/docs/search-product-docs-handler.js +286 -0
- package/dist/confluent/tools/handlers/docs/search-product-docs-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/environments/list-environments-handler.d.ts +11 -12
- package/dist/confluent/tools/handlers/environments/list-environments-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/environments/list-environments-handler.js +11 -14
- package/dist/confluent/tools/handlers/environments/list-environments-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/environments/read-environment-handler.d.ts +5 -6
- package/dist/confluent/tools/handlers/environments/read-environment-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/environments/read-environment-handler.js +6 -9
- package/dist/confluent/tools/handlers/environments/read-environment-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/catalog-resolver.d.ts +32 -11
- package/dist/confluent/tools/handlers/flink/catalog/catalog-resolver.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/catalog-resolver.js +22 -16
- package/dist/confluent/tools/handlers/flink/catalog/catalog-resolver.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/describe-table-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/catalog/describe-table-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/describe-table-handler.js +34 -30
- package/dist/confluent/tools/handlers/flink/catalog/describe-table-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/flink-catalog-tool-handler.d.ts +14 -0
- package/dist/confluent/tools/handlers/flink/catalog/flink-catalog-tool-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/flink/catalog/flink-catalog-tool-handler.js +15 -0
- package/dist/confluent/tools/handlers/flink/catalog/flink-catalog-tool-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/flink/catalog/get-table-info-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/catalog/get-table-info-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/get-table-info-handler.js +34 -30
- package/dist/confluent/tools/handlers/flink/catalog/get-table-info-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/list-catalogs-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/catalog/list-catalogs-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/list-catalogs-handler.js +21 -24
- package/dist/confluent/tools/handlers/flink/catalog/list-catalogs-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/list-databases-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/catalog/list-databases-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/list-databases-handler.js +21 -24
- package/dist/confluent/tools/handlers/flink/catalog/list-databases-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/list-tables-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/catalog/list-tables-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/catalog/list-tables-handler.js +22 -33
- package/dist/confluent/tools/handlers/flink/catalog/list-tables-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/create-flink-statement-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/create-flink-statement-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/create-flink-statement-handler.js +18 -23
- package/dist/confluent/tools/handlers/flink/create-flink-statement-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/delete-flink-statement-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/delete-flink-statement-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/delete-flink-statement-handler.js +7 -13
- package/dist/confluent/tools/handlers/flink/delete-flink-statement-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/check-health-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/diagnostics/check-health-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/check-health-handler.js +7 -13
- package/dist/confluent/tools/handlers/flink/diagnostics/check-health-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/detect-issues-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/diagnostics/detect-issues-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/detect-issues-handler.js +8 -15
- package/dist/confluent/tools/handlers/flink/diagnostics/detect-issues-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/metrics-helper.d.ts +2 -2
- package/dist/confluent/tools/handlers/flink/diagnostics/metrics-helper.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/metrics-helper.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/query-profiler-handler.d.ts +7 -7
- package/dist/confluent/tools/handlers/flink/diagnostics/query-profiler-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/diagnostics/query-profiler-handler.js +11 -17
- package/dist/confluent/tools/handlers/flink/diagnostics/query-profiler-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/flink-sql-helper.d.ts +15 -2
- package/dist/confluent/tools/handlers/flink/flink-sql-helper.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/flink-sql-helper.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/flink-tool-handler.d.ts +22 -0
- package/dist/confluent/tools/handlers/flink/flink-tool-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/flink/flink-tool-handler.js +32 -0
- package/dist/confluent/tools/handlers/flink/flink-tool-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/flink/get-flink-exceptions-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/get-flink-exceptions-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/get-flink-exceptions-handler.js +7 -13
- package/dist/confluent/tools/handlers/flink/get-flink-exceptions-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/list-flink-statements-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/list-flink-statements-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/list-flink-statements-handler.js +9 -17
- package/dist/confluent/tools/handlers/flink/list-flink-statements-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/flink/read-flink-statement-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/flink/read-flink-statement-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/flink/read-flink-statement-handler.js +7 -13
- package/dist/confluent/tools/handlers/flink/read-flink-statement-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/alter-topic-config.d.ts +5 -6
- package/dist/confluent/tools/handlers/kafka/alter-topic-config.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/alter-topic-config.js +22 -20
- package/dist/confluent/tools/handlers/kafka/alter-topic-config.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/consume-kafka-messages-handler.d.ts +216 -30
- package/dist/confluent/tools/handlers/kafka/consume-kafka-messages-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/consume-kafka-messages-handler.js +797 -70
- package/dist/confluent/tools/handlers/kafka/consume-kafka-messages-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/consumer-group-enums.d.ts +48 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-enums.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-enums.js +84 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-enums.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-helpers.d.ts +54 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-helpers.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-helpers.js +89 -0
- package/dist/confluent/tools/handlers/kafka/consumer-group-helpers.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/create-topics-handler.d.ts +5 -5
- package/dist/confluent/tools/handlers/kafka/create-topics-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/create-topics-handler.js +52 -15
- package/dist/confluent/tools/handlers/kafka/create-topics-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/delete-topics-handler.d.ts +5 -5
- package/dist/confluent/tools/handlers/kafka/delete-topics-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/delete-topics-handler.js +26 -8
- package/dist/confluent/tools/handlers/kafka/delete-topics-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/describe-consumer-group-handler.d.ts +64 -0
- package/dist/confluent/tools/handlers/kafka/describe-consumer-group-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/describe-consumer-group-handler.js +140 -0
- package/dist/confluent/tools/handlers/kafka/describe-consumer-group-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/get-consumer-group-lag-handler.d.ts +73 -0
- package/dist/confluent/tools/handlers/kafka/get-consumer-group-lag-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/get-consumer-group-lag-handler.js +337 -0
- package/dist/confluent/tools/handlers/kafka/get-consumer-group-lag-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/get-partition-offsets-handler.d.ts +43 -0
- package/dist/confluent/tools/handlers/kafka/get-partition-offsets-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/get-partition-offsets-handler.js +123 -0
- package/dist/confluent/tools/handlers/kafka/get-partition-offsets-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/get-topic-config.d.ts +5 -6
- package/dist/confluent/tools/handlers/kafka/get-topic-config.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/get-topic-config.js +22 -20
- package/dist/confluent/tools/handlers/kafka/get-topic-config.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/list-consumer-groups-handler.d.ts +45 -0
- package/dist/confluent/tools/handlers/kafka/list-consumer-groups-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/list-consumer-groups-handler.js +86 -0
- package/dist/confluent/tools/handlers/kafka/list-consumer-groups-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/list-topics-handler.d.ts +5 -5
- package/dist/confluent/tools/handlers/kafka/list-topics-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/list-topics-handler.js +25 -10
- package/dist/confluent/tools/handlers/kafka/list-topics-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/partition-watermarks.d.ts +54 -0
- package/dist/confluent/tools/handlers/kafka/partition-watermarks.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/partition-watermarks.js +56 -0
- package/dist/confluent/tools/handlers/kafka/partition-watermarks.js.map +1 -0
- package/dist/confluent/tools/handlers/kafka/produce-kafka-message-handler.d.ts +8 -8
- package/dist/confluent/tools/handlers/kafka/produce-kafka-message-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/kafka/produce-kafka-message-handler.js +57 -46
- package/dist/confluent/tools/handlers/kafka/produce-kafka-message-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/kafka/topic-metadata.d.ts +18 -0
- package/dist/confluent/tools/handlers/kafka/topic-metadata.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/kafka/topic-metadata.js +18 -0
- package/dist/confluent/tools/handlers/kafka/topic-metadata.js.map +1 -0
- package/dist/confluent/tools/handlers/metrics/list-metrics-handler.d.ts +6 -6
- package/dist/confluent/tools/handlers/metrics/list-metrics-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/metrics/list-metrics-handler.js +6 -9
- package/dist/confluent/tools/handlers/metrics/list-metrics-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/metrics/query-metrics-handler.d.ts +24 -6
- package/dist/confluent/tools/handlers/metrics/query-metrics-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/metrics/query-metrics-handler.js +88 -53
- package/dist/confluent/tools/handlers/metrics/query-metrics-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/organizations/list-organizations-handler.d.ts +50 -0
- package/dist/confluent/tools/handlers/organizations/list-organizations-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/organizations/list-organizations-handler.js +116 -0
- package/dist/confluent/tools/handlers/organizations/list-organizations-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/schema/delete-schema-handler.d.ts +5 -5
- package/dist/confluent/tools/handlers/schema/delete-schema-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/schema/delete-schema-handler.js +13 -8
- package/dist/confluent/tools/handlers/schema/delete-schema-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/schema/list-schemas-handler.d.ts +5 -5
- package/dist/confluent/tools/handlers/schema/list-schemas-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/schema/list-schemas-handler.js +13 -7
- package/dist/confluent/tools/handlers/schema/list-schemas-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/search/search-topic-by-tag-handler.d.ts +6 -6
- package/dist/confluent/tools/handlers/search/search-topic-by-tag-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/search/search-topic-by-tag-handler.js +6 -9
- package/dist/confluent/tools/handlers/search/search-topic-by-tag-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/search/search-topics-by-name-handler.d.ts +6 -6
- package/dist/confluent/tools/handlers/search/search-topics-by-name-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/search/search-topics-by-name-handler.js +6 -9
- package/dist/confluent/tools/handlers/search/search-topics-by-name-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/create-tableflow-catalog-integration-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/catalog/create-tableflow-catalog-integration-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/create-tableflow-catalog-integration-handler.js +17 -13
- package/dist/confluent/tools/handlers/tableflow/catalog/create-tableflow-catalog-integration-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/delete-tableflow-catalog-integration-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/catalog/delete-tableflow-catalog-integration-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/delete-tableflow-catalog-integration-handler.js +6 -12
- package/dist/confluent/tools/handlers/tableflow/catalog/delete-tableflow-catalog-integration-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/list-tableflow-catalog-integrations-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/catalog/list-tableflow-catalog-integrations-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/list-tableflow-catalog-integrations-handler.js +7 -13
- package/dist/confluent/tools/handlers/tableflow/catalog/list-tableflow-catalog-integrations-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/read-tableflow-catalog-integration-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/catalog/read-tableflow-catalog-integration-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/read-tableflow-catalog-integration-handler.js +6 -12
- package/dist/confluent/tools/handlers/tableflow/catalog/read-tableflow-catalog-integration-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/update-tableflow-catalog-integration-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/catalog/update-tableflow-catalog-integration-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/catalog/update-tableflow-catalog-integration-handler.js +5 -9
- package/dist/confluent/tools/handlers/tableflow/catalog/update-tableflow-catalog-integration-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/list-tableflow-regions-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/list-tableflow-regions-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/list-tableflow-regions-handler.js +18 -21
- package/dist/confluent/tools/handlers/tableflow/list-tableflow-regions-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/tableflow-tool-handler.d.ts +26 -0
- package/dist/confluent/tools/handlers/tableflow/tableflow-tool-handler.d.ts.map +1 -0
- package/dist/confluent/tools/handlers/tableflow/tableflow-tool-handler.js +28 -0
- package/dist/confluent/tools/handlers/tableflow/tableflow-tool-handler.js.map +1 -0
- package/dist/confluent/tools/handlers/tableflow/topic/create-tableflow-topic-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/topic/create-tableflow-topic-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/create-tableflow-topic-handler.js +17 -13
- package/dist/confluent/tools/handlers/tableflow/topic/create-tableflow-topic-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/delete-tableflow-topic-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/topic/delete-tableflow-topic-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/delete-tableflow-topic-handler.js +6 -12
- package/dist/confluent/tools/handlers/tableflow/topic/delete-tableflow-topic-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/list-tableflow-topics-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/topic/list-tableflow-topics-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/list-tableflow-topics-handler.js +7 -13
- package/dist/confluent/tools/handlers/tableflow/topic/list-tableflow-topics-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/read-tableflow-topic-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/topic/read-tableflow-topic-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/read-tableflow-topic-handler.js +6 -12
- package/dist/confluent/tools/handlers/tableflow/topic/read-tableflow-topic-handler.js.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/update-tableflow-topic-handler.d.ts +5 -7
- package/dist/confluent/tools/handlers/tableflow/topic/update-tableflow-topic-handler.d.ts.map +1 -1
- package/dist/confluent/tools/handlers/tableflow/topic/update-tableflow-topic-handler.js +5 -9
- package/dist/confluent/tools/handlers/tableflow/topic/update-tableflow-topic-handler.js.map +1 -1
- package/dist/confluent/tools/tool-availability.d.ts +91 -0
- package/dist/confluent/tools/tool-availability.d.ts.map +1 -0
- package/dist/confluent/tools/tool-availability.js +130 -0
- package/dist/confluent/tools/tool-availability.js.map +1 -0
- package/dist/confluent/tools/tool-name.d.ts +20 -2
- package/dist/confluent/tools/tool-name.d.ts.map +1 -1
- package/dist/confluent/tools/tool-name.js +19 -1
- package/dist/confluent/tools/tool-name.js.map +1 -1
- package/dist/confluent/tools/tool-registry.d.ts +17 -0
- package/dist/confluent/tools/tool-registry.d.ts.map +1 -0
- package/dist/confluent/tools/{tool-factory.js → tool-registry.js} +66 -22
- package/dist/confluent/tools/tool-registry.js.map +1 -0
- package/dist/env-schema.d.ts +6 -7
- package/dist/env-schema.d.ts.map +1 -1
- package/dist/env-schema.js +30 -44
- package/dist/env-schema.js.map +1 -1
- package/dist/env.d.ts +17 -7
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +16 -6
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +74 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +302 -166
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +21 -20
- package/dist/logger.js.map +1 -1
- package/dist/mcp/server.d.ts +29 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +53 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/transports/auth.d.ts +20 -4
- package/dist/mcp/transports/auth.d.ts.map +1 -1
- package/dist/mcp/transports/auth.js +27 -7
- package/dist/mcp/transports/auth.js.map +1 -1
- package/dist/mcp/transports/http.d.ts +8 -2
- package/dist/mcp/transports/http.d.ts.map +1 -1
- package/dist/mcp/transports/http.js +57 -42
- package/dist/mcp/transports/http.js.map +1 -1
- package/dist/mcp/transports/manager.d.ts +23 -9
- package/dist/mcp/transports/manager.d.ts.map +1 -1
- package/dist/mcp/transports/manager.js +27 -24
- package/dist/mcp/transports/manager.js.map +1 -1
- package/dist/mcp/transports/server.d.ts +1 -1
- package/dist/mcp/transports/server.d.ts.map +1 -1
- package/dist/mcp/transports/server.js +17 -5
- package/dist/mcp/transports/server.js.map +1 -1
- package/dist/mcp/transports/session-registry.d.ts +33 -0
- package/dist/mcp/transports/session-registry.d.ts.map +1 -0
- package/dist/mcp/transports/session-registry.js +65 -0
- package/dist/mcp/transports/session-registry.js.map +1 -0
- package/dist/mcp/transports/sse.d.ts +13 -2
- package/dist/mcp/transports/sse.d.ts.map +1 -1
- package/dist/mcp/transports/sse.js +58 -46
- package/dist/mcp/transports/sse.js.map +1 -1
- package/dist/server-runtime.d.ts +28 -0
- package/dist/server-runtime.d.ts.map +1 -0
- package/dist/server-runtime.js +73 -0
- package/dist/server-runtime.js.map +1 -0
- package/package.json +26 -9
- package/dist/confluent/helpers.d.ts +0 -11
- package/dist/confluent/helpers.d.ts.map +0 -1
- package/dist/confluent/helpers.js +0 -17
- package/dist/confluent/helpers.js.map +0 -1
- package/dist/confluent/tools/handlers/connect/read-connectors-handler.d.ts +0 -11
- package/dist/confluent/tools/handlers/connect/read-connectors-handler.d.ts.map +0 -1
- package/dist/confluent/tools/handlers/connect/read-connectors-handler.js +0 -59
- package/dist/confluent/tools/handlers/connect/read-connectors-handler.js.map +0 -1
- package/dist/confluent/tools/tool-factory.d.ts +0 -9
- package/dist/confluent/tools/tool-factory.d.ts.map +0 -1
- package/dist/confluent/tools/tool-factory.js.map +0 -1
|
@@ -1,28 +1,127 @@
|
|
|
1
|
-
import { KafkaJS } from "@confluentinc/kafka-javascript";
|
|
2
1
|
import { SerdeType } from "@confluentinc/schemaregistry";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import { nodeCrypto } from "../../../../confluent/node-deps.js";
|
|
3
|
+
import * as schemaRegistryHelper from "../../../../confluent/schema-registry-helper.js";
|
|
4
|
+
import { BaseToolHandler, READ_ONLY, ToolCategory, } from "../../../../confluent/tools/base-tools.js";
|
|
5
|
+
import { disposeIfOAuth, formatKafkaError, resolveKafkaClusterArgs, } from "../../../../confluent/tools/cluster-arg-resolvers.js";
|
|
6
|
+
import { hasSchemaRegistryOrOAuth, kafkaBootstrapOrOAuth, } from "../../../../confluent/tools/connection-predicates.js";
|
|
7
|
+
import { createWatermarkCache, } from "../../../../confluent/tools/handlers/kafka/partition-watermarks.js";
|
|
8
|
+
import { normalizeFetchTopicMetadataResponse } from "../../../../confluent/tools/handlers/kafka/topic-metadata.js";
|
|
5
9
|
import { ToolName } from "../../../../confluent/tools/tool-name.js";
|
|
6
10
|
import { logger } from "../../../../logger.js";
|
|
7
11
|
import { z } from "zod";
|
|
8
|
-
const
|
|
9
|
-
|
|
12
|
+
const schemaRegistryOptions = z
|
|
13
|
+
.object({
|
|
14
|
+
disableSchemaRegistry: z
|
|
10
15
|
.boolean()
|
|
11
16
|
.optional()
|
|
12
17
|
.default(false)
|
|
13
|
-
.describe("
|
|
18
|
+
.describe("Set to true to skip Schema Registry decoding and return raw UTF-8 bytes. " +
|
|
19
|
+
"When false (the default), messages are auto-decoded via the registered " +
|
|
20
|
+
"AVRO / JSON / PROTOBUF schema if the connection has Schema Registry " +
|
|
21
|
+
"configured and a schema exists for the subject; otherwise left as raw bytes."),
|
|
14
22
|
subject: z
|
|
15
23
|
.string()
|
|
16
24
|
.optional()
|
|
17
|
-
.describe("Schema registry subject. Defaults to '
|
|
25
|
+
.describe("Schema registry subject. Defaults to '<topic>-value' or '<topic>-key'."),
|
|
26
|
+
})
|
|
27
|
+
// The optional() + default() wrapping lives here on the shared
|
|
28
|
+
// schema so `valueFormat` and `keyFormat` declarations downstream
|
|
29
|
+
// are bare references — one place to change if the omit-by-default
|
|
30
|
+
// contract ever evolves.
|
|
31
|
+
.optional()
|
|
32
|
+
.default({ disableSchemaRegistry: false });
|
|
33
|
+
/**
|
|
34
|
+
* Per-entry "where to start consuming" tagged union. Collapses the prior
|
|
35
|
+
* `offset` and `timestamp` peer fields plus the top-level `offsetReset`
|
|
36
|
+
* knob into a single per-topic position control with five arms:
|
|
37
|
+
*
|
|
38
|
+
* - `"earliest"` — begin at the partition low watermark (entire retained
|
|
39
|
+
* history). This is the default when `start` is omitted.
|
|
40
|
+
* - `"latest"` — begin at the partition high watermark (only
|
|
41
|
+
* newly-produced messages).
|
|
42
|
+
* - `{ offset: "..." }` — begin at an absolute partition offset
|
|
43
|
+
* (digit-only string; requires `partition`).
|
|
44
|
+
* - `{ timestamp: ... }` — begin at the broker-resolved offset for the
|
|
45
|
+
* supplied time (ISO 8601 preferred; ms-since-epoch also accepted).
|
|
46
|
+
* - `{ tail: N }` — begin at `max(low, high - N)` on the partition, so
|
|
47
|
+
* the consumer returns up to the last N already-written messages
|
|
48
|
+
* without waiting for new traffic (requires `partition`).
|
|
49
|
+
*
|
|
50
|
+
* The object arms are `strictObject` so `{offset, timestamp}`,
|
|
51
|
+
* `{tail, offset}`, etc. together are a union miss rather than a silent
|
|
52
|
+
* strip of one key.
|
|
53
|
+
*/
|
|
54
|
+
const startOption = z
|
|
55
|
+
.union([
|
|
56
|
+
z.literal("earliest"),
|
|
57
|
+
z.literal("latest"),
|
|
58
|
+
z.strictObject({
|
|
59
|
+
offset: z
|
|
60
|
+
.string()
|
|
61
|
+
.regex(/^\d+$/, "offset must be a non-negative integer (digit-only string)")
|
|
62
|
+
.describe("Absolute starting offset within the partition, as a digit-only string."),
|
|
63
|
+
}),
|
|
64
|
+
z.strictObject({
|
|
65
|
+
timestamp: z
|
|
66
|
+
.union([
|
|
67
|
+
z.iso.datetime({ offset: true }),
|
|
68
|
+
z.number().int().nonnegative(),
|
|
69
|
+
])
|
|
70
|
+
.describe('ISO 8601 timestamp (e.g. "2026-05-14T17:00:00Z" or ' +
|
|
71
|
+
'"2026-05-14T13:00:00-04:00") or ms-since-epoch number. The ' +
|
|
72
|
+
"broker resolves this to a per-partition offset."),
|
|
73
|
+
}),
|
|
74
|
+
z.strictObject({
|
|
75
|
+
tail: z
|
|
76
|
+
.number()
|
|
77
|
+
.int()
|
|
78
|
+
.positive()
|
|
79
|
+
.describe("Consume the most recent N already-written messages from this partition. " +
|
|
80
|
+
"REQUIRES `partition` to be set on the same topic entry — there " +
|
|
81
|
+
"is no implicit partition default; consumption is " +
|
|
82
|
+
"single-partition only. N must be a positive integer. The " +
|
|
83
|
+
"consumer seeks to max(lowWatermark, highWatermark - N) and " +
|
|
84
|
+
"returns immediately with whatever is already there — it does " +
|
|
85
|
+
"not block waiting for new writes."),
|
|
86
|
+
}),
|
|
87
|
+
])
|
|
88
|
+
.describe("Where to begin consuming this topic. Use 'earliest' for the entire " +
|
|
89
|
+
"retained history; 'latest' for " +
|
|
90
|
+
"newly-produced messages only; {offset: 'N'} for an absolute " +
|
|
91
|
+
"partition offset (requires `partition`); {timestamp: '...'} " +
|
|
92
|
+
"to seek to the broker-resolved offset for a point in time; or " +
|
|
93
|
+
"{tail: N} for the last N already-written messages on the " +
|
|
94
|
+
"partition (requires `partition`, returns without waiting for new " +
|
|
95
|
+
"traffic).");
|
|
96
|
+
/**
|
|
97
|
+
* Per-topic consume options. `name` is the only required field. `partition`
|
|
98
|
+
* optionally restricts to one partition; `start` optionally picks the
|
|
99
|
+
* starting position. Both are independent — `partition` answers WHICH
|
|
100
|
+
* partition(s), `start` answers WHERE in them to begin.
|
|
101
|
+
*/
|
|
102
|
+
const topicConsumeOptions = z.object({
|
|
103
|
+
name: z.string().min(1).describe("Kafka topic name to consume from."),
|
|
104
|
+
partition: z
|
|
105
|
+
.number()
|
|
106
|
+
.int()
|
|
107
|
+
.nonnegative()
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("Optional. Restrict consumption to this partition (0-indexed). " +
|
|
110
|
+
"Other partitions in the topic are paused after assignment. " +
|
|
111
|
+
"Omit to consume all partitions of the topic."),
|
|
112
|
+
start: startOption.optional().default("earliest"),
|
|
18
113
|
});
|
|
19
|
-
const valueOptions = z.object({}).extend(messageOptions.shape);
|
|
20
|
-
const keyOptions = z.object({}).extend(messageOptions.shape);
|
|
21
114
|
export const consumeKafkaMessagesArgs = z.object({
|
|
22
|
-
|
|
23
|
-
.array(
|
|
115
|
+
topics: z
|
|
116
|
+
.array(topicConsumeOptions)
|
|
24
117
|
.nonempty()
|
|
25
|
-
.describe("
|
|
118
|
+
.describe("Topics to consume from. Each entry is an object with at least " +
|
|
119
|
+
"`name` (the Kafka topic name). Example simple call: " +
|
|
120
|
+
'[{name: "orders"}]. The `partition` and `start` fields let ' +
|
|
121
|
+
"callers restrict to a specific partition and/or pick where in " +
|
|
122
|
+
"the topic to begin consuming (e.g. " +
|
|
123
|
+
'[{name: "orders", partition: 0, start: {offset: "42"}}], ' +
|
|
124
|
+
'[{name: "orders", start: {timestamp: "2026-05-14T17:00:00Z"}}]).'),
|
|
26
125
|
maxMessages: z
|
|
27
126
|
.number()
|
|
28
127
|
.int()
|
|
@@ -37,9 +136,504 @@ export const consumeKafkaMessagesArgs = z.object({
|
|
|
37
136
|
.optional()
|
|
38
137
|
.default(10000)
|
|
39
138
|
.describe("Maximum time in milliseconds to wait for messages before stopping."),
|
|
40
|
-
|
|
41
|
-
|
|
139
|
+
valueFormat: schemaRegistryOptions.describe("VALUE format. Default: auto-decode via Schema Registry when configured. " +
|
|
140
|
+
"Set `disableSchemaRegistry: true` for raw UTF-8."),
|
|
141
|
+
keyFormat: schemaRegistryOptions.describe("KEY format. Default: auto-decode via Schema Registry when configured. " +
|
|
142
|
+
"Set `disableSchemaRegistry: true` for raw UTF-8."),
|
|
143
|
+
cluster_id: z
|
|
144
|
+
.string()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("Confluent Cloud logical Kafka cluster ID (lkc-...). Discover via list-clusters."),
|
|
147
|
+
environment_id: z
|
|
148
|
+
.string()
|
|
149
|
+
.optional()
|
|
150
|
+
.describe("Confluent Cloud environment ID (env-...) that owns the cluster. Discover via list-environments."),
|
|
42
151
|
});
|
|
152
|
+
/**
|
|
153
|
+
* Format a Kafka message's `message.timestamp` (a string of ms-since-epoch
|
|
154
|
+
* per kafkajs) into an ISO 8601 UTC string. Returns `"(no timestamp)"` for
|
|
155
|
+
* undefined or Kafka's `-1` sentinel (message format pre-0.10.0 or
|
|
156
|
+
* timestamp unset by the producer). Exported for direct unit-test coverage
|
|
157
|
+
* of the four branches plus the empty-string edge case (`Number("")` is
|
|
158
|
+
* `0`, which is finite, so an empty timestamp string formats as epoch).
|
|
159
|
+
*/
|
|
160
|
+
export function formatMessageTimestamp(ts) {
|
|
161
|
+
if (ts === undefined || ts === "-1")
|
|
162
|
+
return "(no timestamp)";
|
|
163
|
+
const ms = Number(ts);
|
|
164
|
+
if (!Number.isFinite(ms))
|
|
165
|
+
return ts;
|
|
166
|
+
return new Date(ms).toISOString();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Collapse the parsed `start` union into the internal tagged form.
|
|
170
|
+
* ISO 8601 timestamps normalize to ms-since-epoch at this boundary so
|
|
171
|
+
* downstream code only sees numbers. Exported so the timestamp-arm's
|
|
172
|
+
* string-vs-number branch can be unit-tested directly (the handler-level
|
|
173
|
+
* tests only exercise the string path).
|
|
174
|
+
*/
|
|
175
|
+
export function normalizeStart(start) {
|
|
176
|
+
if (start === "earliest")
|
|
177
|
+
return { kind: "earliest" };
|
|
178
|
+
if (start === "latest")
|
|
179
|
+
return { kind: "latest" };
|
|
180
|
+
if ("offset" in start)
|
|
181
|
+
return { kind: "offset", value: start.offset };
|
|
182
|
+
if ("tail" in start)
|
|
183
|
+
return { kind: "tail", count: start.tail };
|
|
184
|
+
return {
|
|
185
|
+
kind: "timestamp",
|
|
186
|
+
ms: typeof start.timestamp === "number"
|
|
187
|
+
? start.timestamp
|
|
188
|
+
: Date.parse(start.timestamp),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Normalize the parsed tool args into a uniform list of per-topic targets.
|
|
193
|
+
*/
|
|
194
|
+
function normalizeTopicTargets(parsed) {
|
|
195
|
+
return parsed.topics.map((entry) => ({
|
|
196
|
+
name: entry.name,
|
|
197
|
+
partition: entry.partition,
|
|
198
|
+
start: normalizeStart(entry.start),
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Pick the consumer's `auto.offset.reset` value from the call. The
|
|
203
|
+
* consumer-wide setting governs any partition that doesn't get an
|
|
204
|
+
* explicit seek — so we want to align it with the call's intent and
|
|
205
|
+
* avoid emitting watermark seeks unnecessarily.
|
|
206
|
+
*
|
|
207
|
+
* Rule: if every direction-only entry (`start: "earliest"` or
|
|
208
|
+
* `start: "latest"`) agrees on `"earliest"`, use `"earliest"`; otherwise
|
|
209
|
+
* use `"latest"`. The choice of `"latest"` as the mixed-direction
|
|
210
|
+
* tiebreaker matches librdkafka's own `auto.offset.reset` default and
|
|
211
|
+
* keeps the explicit-seek work localized to the `"earliest"` minority
|
|
212
|
+
* (which gets per-partition low-watermark seeks during preflight); the
|
|
213
|
+
* `"latest"` majority inherits the consumer-wide default and needs no
|
|
214
|
+
* extra admin round-trips.
|
|
215
|
+
*/
|
|
216
|
+
function deriveConsumerOffsetReset(targets) {
|
|
217
|
+
const directions = targets
|
|
218
|
+
.map((t) => t.start.kind === "earliest" || t.start.kind === "latest"
|
|
219
|
+
? t.start.kind
|
|
220
|
+
: null)
|
|
221
|
+
.filter((d) => d !== null);
|
|
222
|
+
if (directions.length > 0 && directions.every((d) => d === "earliest")) {
|
|
223
|
+
return "earliest";
|
|
224
|
+
}
|
|
225
|
+
return "latest";
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Returns true when any target requires the admin pre-flight + seek/pause
|
|
229
|
+
* dance. Bare-name-only calls (no partition restrictions, no explicit
|
|
230
|
+
* offset/timestamp/tail seeks, and a `start` whose direction matches the
|
|
231
|
+
* consumer's chosen reset) skip the whole dance — the consumer's
|
|
232
|
+
* `auto.offset.reset` handles them naturally.
|
|
233
|
+
*/
|
|
234
|
+
function planNeedsPreflight(targets, consumerOffsetReset) {
|
|
235
|
+
return targets.some((t) => t.partition !== undefined ||
|
|
236
|
+
t.start.kind === "offset" ||
|
|
237
|
+
t.start.kind === "timestamp" ||
|
|
238
|
+
t.start.kind === "tail" ||
|
|
239
|
+
(t.start.kind === "earliest" && consumerOffsetReset === "latest"));
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Reject any target whose `start` arm is partition-scoped but supplies
|
|
243
|
+
* no `partition`. Both `{offset}` and `{tail}` need partition context
|
|
244
|
+
* (different partitions have different offset spaces and different
|
|
245
|
+
* message counts), so picking a default is footgun-prone; rejecting at
|
|
246
|
+
* the boundary is louder.
|
|
247
|
+
*/
|
|
248
|
+
function guardScopedStartRequiresPartition(targets) {
|
|
249
|
+
for (const t of targets) {
|
|
250
|
+
if (t.start.kind === "offset" && t.partition === undefined) {
|
|
251
|
+
throw new Error(`Topic "${t.name}" has an explicit offset (${t.start.value}) but no partition. ` +
|
|
252
|
+
`Absolute offsets are partition-scoped — different partitions have different offset spaces. ` +
|
|
253
|
+
`Either also supply a partition for this entry, or use a timestamp (which resolves per-partition).`);
|
|
254
|
+
}
|
|
255
|
+
if (t.start.kind === "tail" && t.partition === undefined) {
|
|
256
|
+
throw new Error(`Topic "${t.name}" requested tail of ${t.start.count} messages but no partition. ` +
|
|
257
|
+
`Tail is partition-scoped — different partitions have different message counts, and ` +
|
|
258
|
+
`cross-partition freshness ordering is not defined for this tool. ` +
|
|
259
|
+
`Supply a partition for this entry.`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Group targets by topic name preserving entry order. Used downstream
|
|
265
|
+
* for both the all-or-nothing partition-mode check and the keep-set
|
|
266
|
+
* derivation; computing it once means subsequent iterations stay O(N).
|
|
267
|
+
*/
|
|
268
|
+
/**
|
|
269
|
+
* Fetch partition counts for the supplied topics via
|
|
270
|
+
* `admin.fetchTopicMetadata`. Throws if any requested topic returned no
|
|
271
|
+
* metadata (typically: the topic doesn't exist on this cluster). The
|
|
272
|
+
* upstream type/runtime mismatch on the response shape is handled by
|
|
273
|
+
* {@link normalizeFetchTopicMetadataResponse}.
|
|
274
|
+
*/
|
|
275
|
+
async function fetchPartitionCounts(admin, topicNames) {
|
|
276
|
+
const topicMetadata = normalizeFetchTopicMetadataResponse(await admin.fetchTopicMetadata({ topics: topicNames }));
|
|
277
|
+
const counts = new Map();
|
|
278
|
+
for (const t of topicMetadata) {
|
|
279
|
+
counts.set(t.name, t.partitions.length);
|
|
280
|
+
}
|
|
281
|
+
for (const name of topicNames) {
|
|
282
|
+
if (!counts.has(name)) {
|
|
283
|
+
throw new Error(`Topic "${name}" returned no partition metadata (does it exist on this cluster?).`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return counts;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Reject any target whose `partition` index is out of range for its
|
|
290
|
+
* topic. The error message cites the actual partition count so the
|
|
291
|
+
* caller can correct without guessing.
|
|
292
|
+
*/
|
|
293
|
+
function validateRequestedPartitions(targets, numPartitionsByTopic) {
|
|
294
|
+
for (const t of targets) {
|
|
295
|
+
if (t.partition !== undefined) {
|
|
296
|
+
const numParts = numPartitionsByTopic.get(t.name);
|
|
297
|
+
if (t.partition >= numParts) {
|
|
298
|
+
throw new Error(`Topic "${t.name}" has ${numParts} partition(s) (0..${numParts - 1}); ` +
|
|
299
|
+
`requested partition ${t.partition} is out of range.`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Validate per-topic consistency and derive the keep-partitions map in
|
|
306
|
+
* a single pass over `byTopic`. Three rejections fire here, each a
|
|
307
|
+
* shape of "starting position is ambiguous" the consumer can't honor:
|
|
308
|
+
*
|
|
309
|
+
* - **Mixed mode**: a topic with some entries restricting to a partition
|
|
310
|
+
* and others not — does the bare entry coexist with or override the
|
|
311
|
+
* partitioned one? Reject loudly.
|
|
312
|
+
* - **Duplicate unrestricted**: a topic with multiple entries that all
|
|
313
|
+
* omit `partition`. Two "every partition" entries with conflicting
|
|
314
|
+
* `start` directions silently let one win the seek/reset race; reject.
|
|
315
|
+
* - **Duplicate (topic, partition)**: the same partition index referenced
|
|
316
|
+
* twice within one topic — same ambiguity at finer granularity.
|
|
317
|
+
*
|
|
318
|
+
* Topics whose entries are all unrestricted are omitted from the
|
|
319
|
+
* returned map (downstream: "keep every assigned partition for that
|
|
320
|
+
* topic"). For topics that restrict, the keep-set is exactly the
|
|
321
|
+
* requested partition indices.
|
|
322
|
+
*/
|
|
323
|
+
function validateAndBuildKeepPartitions(byTopic) {
|
|
324
|
+
const keepPartitions = new Map();
|
|
325
|
+
for (const [topic, list] of byTopic) {
|
|
326
|
+
const explicit = list
|
|
327
|
+
.map((t) => t.partition)
|
|
328
|
+
.filter((p) => p !== undefined);
|
|
329
|
+
const allPartitioned = explicit.length === list.length;
|
|
330
|
+
const nonePartitioned = explicit.length === 0;
|
|
331
|
+
if (!allPartitioned && !nonePartitioned) {
|
|
332
|
+
throw new Error(`Topic "${topic}" mixes entries with explicit partitions and entries without one. ` +
|
|
333
|
+
`Pick one mode per topic: either every entry restricts to a partition, or none do.`);
|
|
334
|
+
}
|
|
335
|
+
if (nonePartitioned) {
|
|
336
|
+
if (list.length > 1) {
|
|
337
|
+
throw new Error(`Topic "${topic}" has ${list.length} entries without partition restrictions; ` +
|
|
338
|
+
`specify each topic at most once at the unrestricted level (or restrict each entry to a distinct partition).`);
|
|
339
|
+
}
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const seen = new Set();
|
|
343
|
+
for (const p of explicit) {
|
|
344
|
+
if (seen.has(p)) {
|
|
345
|
+
throw new Error(`Topic "${topic}" has multiple entries for partition ${p}; ` +
|
|
346
|
+
`specify each (topic, partition) pair at most once.`);
|
|
347
|
+
}
|
|
348
|
+
seen.add(p);
|
|
349
|
+
}
|
|
350
|
+
keepPartitions.set(topic, seen);
|
|
351
|
+
}
|
|
352
|
+
return keepPartitions;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Validate an explicit `start: {offset}` against the partition's
|
|
356
|
+
* `[low, high)` watermarks and return the seek target. Throws if the
|
|
357
|
+
* partition has no offset metadata or the requested offset is out of
|
|
358
|
+
* range.
|
|
359
|
+
*/
|
|
360
|
+
async function resolveExplicitOffsetSeek(topic, partition, offset, getWatermarks) {
|
|
361
|
+
const offsets = await getWatermarks(topic);
|
|
362
|
+
const partOffsets = offsets.find((o) => o.partition === partition);
|
|
363
|
+
if (!partOffsets) {
|
|
364
|
+
throw new Error(`Topic "${topic}" partition ${partition} returned no offset metadata.`);
|
|
365
|
+
}
|
|
366
|
+
const low = BigInt(partOffsets.low);
|
|
367
|
+
const high = BigInt(partOffsets.high);
|
|
368
|
+
const target = BigInt(offset);
|
|
369
|
+
if (target < low || target >= high) {
|
|
370
|
+
throw new Error(`Topic "${topic}" partition ${partition} offset ${offset} is out of range ` +
|
|
371
|
+
`[low=${partOffsets.low}, high=${partOffsets.high}). ` +
|
|
372
|
+
`An empty partition has low === high; pick an offset already on the partition.`);
|
|
373
|
+
}
|
|
374
|
+
return { topic, partition, offset };
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Resolve a `start: {tail: N}` to a single seek target on the requested
|
|
378
|
+
* partition. Computes `target = max(low, high - N)` using `BigInt`
|
|
379
|
+
* (Kafka offsets are int64; `N` may legitimately be large).
|
|
380
|
+
*
|
|
381
|
+
* Empty-partition contract (`low === high`): the seek target collapses
|
|
382
|
+
* to `high` (`max(high, high - N) === high`), which parks the consumer
|
|
383
|
+
* at `OFFSET_END`. No records flow, the orchestrator's `timeoutMs`
|
|
384
|
+
* budget fires, and the call returns an empty success response — the
|
|
385
|
+
* issue's "tail on empty partition returns zero, no error" path. The
|
|
386
|
+
* `BigInt` subtraction stays well-defined for huge `N` because the
|
|
387
|
+
* arithmetic compares signed values and `max` clamps back into the
|
|
388
|
+
* `[low, high)` range before serializing.
|
|
389
|
+
*/
|
|
390
|
+
async function resolveTailSeek(topic, partition, count, getWatermarks) {
|
|
391
|
+
const offsets = await getWatermarks(topic);
|
|
392
|
+
const partOffsets = offsets.find((o) => o.partition === partition);
|
|
393
|
+
if (!partOffsets) {
|
|
394
|
+
throw new Error(`Topic "${topic}" partition ${partition} returned no offset metadata.`);
|
|
395
|
+
}
|
|
396
|
+
const low = BigInt(partOffsets.low);
|
|
397
|
+
const high = BigInt(partOffsets.high);
|
|
398
|
+
const candidate = high - BigInt(count);
|
|
399
|
+
// Manual clamp instead of Math.max: Math.max rejects BigInt operands
|
|
400
|
+
// (TypeError: Cannot convert a BigInt value to a number), and the
|
|
401
|
+
// BigInt path is load-bearing because Kafka offsets are int64.
|
|
402
|
+
const target = candidate < low ? low : candidate;
|
|
403
|
+
return { topic, partition, offset: target.toString() };
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Resolve a `start: {timestamp}` to per-partition offsets via
|
|
407
|
+
* `admin.fetchTopicOffsetsByTimestamp` AND filter out the binding's
|
|
408
|
+
* silent high-watermark substitutions.
|
|
409
|
+
*
|
|
410
|
+
* `@confluentinc/kafka-javascript`'s `fetchTopicOffsetsByTimestamp`
|
|
411
|
+
* silently substitutes the partition's high watermark when no message
|
|
412
|
+
* exists at or after the requested timestamp (see the method's
|
|
413
|
+
* docstring in node_modules/.../kafkajs/_admin.js). Seeking to a
|
|
414
|
+
* position == high watermark parks the consumer at OFFSET_END and we'd
|
|
415
|
+
* time out with zero messages with no signal as to why. The defense:
|
|
416
|
+
* cross-check each resolved offset against the partition's actual
|
|
417
|
+
* watermarks, skip silent partitions, and emit a diagnostic log so
|
|
418
|
+
* operators can distinguish the three resolution modes (silent /
|
|
419
|
+
* broker-low-fallback / real-index-hit).
|
|
420
|
+
*/
|
|
421
|
+
async function resolveTimestampSeeks(admin, topic, restrictToPartition, timestampMs, getWatermarks) {
|
|
422
|
+
const resolved = await admin.fetchTopicOffsetsByTimestamp(topic, timestampMs);
|
|
423
|
+
const watermarks = await getWatermarks(topic);
|
|
424
|
+
const watermarkByPartition = new Map();
|
|
425
|
+
for (const w of watermarks) {
|
|
426
|
+
watermarkByPartition.set(w.partition, { low: w.low, high: w.high });
|
|
427
|
+
}
|
|
428
|
+
const candidates = restrictToPartition === undefined
|
|
429
|
+
? resolved
|
|
430
|
+
: resolved.filter((r) => r.partition === restrictToPartition);
|
|
431
|
+
const active = [];
|
|
432
|
+
const silent = [];
|
|
433
|
+
for (const r of candidates) {
|
|
434
|
+
const wm = watermarkByPartition.get(r.partition);
|
|
435
|
+
// Diagnostic log so debugging timestamp-to-offset resolution
|
|
436
|
+
// doesn't require adding instrumentation later. Lets a reader
|
|
437
|
+
// distinguish three failure modes from one log inspection:
|
|
438
|
+
// - resolvedOffset == high → binding's silent substitution
|
|
439
|
+
// (already filtered below; the warn-level log fires too).
|
|
440
|
+
// - resolvedOffset == low → broker fell back to the earliest
|
|
441
|
+
// offset (e.g. requested timestamp is older than every
|
|
442
|
+
// indexed message, or tiered-storage index quirk).
|
|
443
|
+
// - resolvedOffset in (low, high) → real timestamp resolution.
|
|
444
|
+
logger.info({
|
|
445
|
+
topic,
|
|
446
|
+
partition: r.partition,
|
|
447
|
+
resolvedOffset: r.offset,
|
|
448
|
+
low: wm?.low,
|
|
449
|
+
high: wm?.high,
|
|
450
|
+
requestedMs: timestampMs,
|
|
451
|
+
requestedIso: new Date(timestampMs).toISOString(),
|
|
452
|
+
}, `Timestamp → offset resolution for ${topic} partition ${r.partition}`);
|
|
453
|
+
if (wm !== undefined && BigInt(r.offset) >= BigInt(wm.high)) {
|
|
454
|
+
silent.push(r.partition);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
active.push(r);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (active.length === 0) {
|
|
461
|
+
const scopePhrase = restrictToPartition === undefined
|
|
462
|
+
? "every partition"
|
|
463
|
+
: `partition ${restrictToPartition}`;
|
|
464
|
+
throw new Error(`Topic "${topic}" has no messages at or after timestamp ` +
|
|
465
|
+
`${new Date(timestampMs).toISOString()} ` +
|
|
466
|
+
`(${scopePhrase} has no record produced past that point). ` +
|
|
467
|
+
`Try an earlier timestamp, or use \`start: "latest"\` to consume ` +
|
|
468
|
+
`from the live position instead.`);
|
|
469
|
+
}
|
|
470
|
+
if (silent.length > 0) {
|
|
471
|
+
logger.warn({ topic, timestampMs, silentPartitions: silent }, `Skipping seek for partitions of "${topic}" with no record at or after the requested timestamp; they will idle at OFFSET_END.`);
|
|
472
|
+
}
|
|
473
|
+
return active.map((r) => ({
|
|
474
|
+
topic,
|
|
475
|
+
partition: r.partition,
|
|
476
|
+
offset: r.offset,
|
|
477
|
+
}));
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Resolve a `start: "earliest"` entry that lost the direction-derivation
|
|
481
|
+
* race — i.e. another entry pushed the consumer-wide
|
|
482
|
+
* `auto.offset.reset` to `"latest"`. Returns explicit low-watermark
|
|
483
|
+
* seeks for every partition (or the single restricted partition) so
|
|
484
|
+
* this topic replays its history instead of inheriting the
|
|
485
|
+
* consumer-wide "latest" default.
|
|
486
|
+
*/
|
|
487
|
+
async function resolveEarliestMinoritySeeks(topic, restrictToPartition, getWatermarks) {
|
|
488
|
+
const offsets = await getWatermarks(topic);
|
|
489
|
+
const partitionsToSeek = restrictToPartition === undefined
|
|
490
|
+
? offsets
|
|
491
|
+
: offsets.filter((o) => o.partition === restrictToPartition);
|
|
492
|
+
return partitionsToSeek.map((p) => ({
|
|
493
|
+
topic,
|
|
494
|
+
partition: p.partition,
|
|
495
|
+
offset: p.low,
|
|
496
|
+
}));
|
|
497
|
+
}
|
|
498
|
+
async function buildPreflightPlan(admin, targets, consumerOffsetReset) {
|
|
499
|
+
guardScopedStartRequiresPartition(targets);
|
|
500
|
+
const byTopic = new Map();
|
|
501
|
+
for (const t of targets) {
|
|
502
|
+
const list = byTopic.get(t.name);
|
|
503
|
+
if (list)
|
|
504
|
+
list.push(t);
|
|
505
|
+
else
|
|
506
|
+
byTopic.set(t.name, [t]);
|
|
507
|
+
}
|
|
508
|
+
const topicNames = [...byTopic.keys()];
|
|
509
|
+
const numPartitionsByTopic = await fetchPartitionCounts(admin, topicNames);
|
|
510
|
+
validateRequestedPartitions(targets, numPartitionsByTopic);
|
|
511
|
+
const keepPartitions = validateAndBuildKeepPartitions(byTopic);
|
|
512
|
+
const getWatermarks = createWatermarkCache(admin);
|
|
513
|
+
const seeks = [];
|
|
514
|
+
for (const t of targets) {
|
|
515
|
+
if (t.start.kind === "offset" && t.partition !== undefined) {
|
|
516
|
+
seeks.push(await resolveExplicitOffsetSeek(t.name, t.partition, t.start.value, getWatermarks));
|
|
517
|
+
}
|
|
518
|
+
else if (t.start.kind === "timestamp") {
|
|
519
|
+
seeks.push(...(await resolveTimestampSeeks(admin, t.name, t.partition, t.start.ms, getWatermarks)));
|
|
520
|
+
}
|
|
521
|
+
else if (t.start.kind === "tail" && t.partition !== undefined) {
|
|
522
|
+
seeks.push(await resolveTailSeek(t.name, t.partition, t.start.count, getWatermarks));
|
|
523
|
+
}
|
|
524
|
+
else if (t.start.kind === "earliest" &&
|
|
525
|
+
consumerOffsetReset === "latest") {
|
|
526
|
+
seeks.push(...(await resolveEarliestMinoritySeeks(t.name, t.partition, getWatermarks)));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return { topicNames, keepPartitions, seeks };
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Poll `consumer.assignment()` until it returns a non-empty array or the
|
|
533
|
+
* supplied deadline passes. Returns the assignment, or `null` if the
|
|
534
|
+
* deadline was hit first. The kafkajs-compat consumer only populates its
|
|
535
|
+
* assignment after `consumer.run()` triggers the first poll, so this is
|
|
536
|
+
* the well-defined seam to wait on before issuing per-partition seeks.
|
|
537
|
+
* Exported so the three branches (immediate-return, polled-populate,
|
|
538
|
+
* polled-timeout) can be exercised directly under fake timers.
|
|
539
|
+
*/
|
|
540
|
+
export async function waitForAssignment(consumer, deadline) {
|
|
541
|
+
for (;;) {
|
|
542
|
+
const assignment = consumer.assignment();
|
|
543
|
+
if (assignment.length > 0) {
|
|
544
|
+
return assignment;
|
|
545
|
+
}
|
|
546
|
+
if (Date.now() >= deadline) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* After the consumer assignment lands, pause any assigned partition that
|
|
554
|
+
* isn't in the keep-set. Seeks were stashed before `consumer.run()` (see
|
|
555
|
+
* the inline comment in the orchestrator) so the per-partition seek
|
|
556
|
+
* targets are already baked into the assignment by the time this runs.
|
|
557
|
+
*/
|
|
558
|
+
async function applyPauseAfterAssignment(consumer, plan, assignment) {
|
|
559
|
+
const toPause = [];
|
|
560
|
+
for (const [topic, keepSet] of plan.keepPartitions) {
|
|
561
|
+
const pauseForTopic = assignment
|
|
562
|
+
.filter((a) => a.topic === topic && !keepSet.has(a.partition))
|
|
563
|
+
.map((a) => a.partition);
|
|
564
|
+
if (pauseForTopic.length > 0) {
|
|
565
|
+
toPause.push({ topic, partitions: pauseForTopic });
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (toPause.length > 0) {
|
|
569
|
+
consumer.pause(toPause);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Wrap a promise so its successful resolution is ignored (never settles
|
|
574
|
+
* downstream) while its rejection still propagates. Used in the
|
|
575
|
+
* orchestrator's `Promise.race` to let "engine still running" branches
|
|
576
|
+
* (`consumer.run`, `applyPostAssignmentHook`) participate only on
|
|
577
|
+
* failure — successful completion of those promises shouldn't end the
|
|
578
|
+
* consume loop, but their errors must surface as the race's rejection.
|
|
579
|
+
*/
|
|
580
|
+
function rejectOnly(p) {
|
|
581
|
+
return p.then(() => new Promise(() => { }));
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Drive the post-`consumer.run()` pause step: poll for an assignment up
|
|
585
|
+
* to `deadlineMs`, then pause partitions outside the keep-set. Signals
|
|
586
|
+
* completion via callbacks rather than its return value because the
|
|
587
|
+
* orchestrator's `Promise.race` only cares about success (`onApplied`)
|
|
588
|
+
* vs. assignment-deadline-elapsed (`onAssignmentTimedOut`); internal
|
|
589
|
+
* pause failures propagate as a rejection so the race can surface them
|
|
590
|
+
* via `rejectOnly`.
|
|
591
|
+
*/
|
|
592
|
+
export async function applyPostAssignmentHook(opts) {
|
|
593
|
+
const assignment = await waitForAssignment(opts.consumer, opts.deadlineMs);
|
|
594
|
+
if (!assignment) {
|
|
595
|
+
opts.onAssignmentTimedOut();
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
await applyPauseAfterAssignment(opts.consumer, opts.plan, assignment);
|
|
599
|
+
opts.onApplied();
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Build the `eachMessage` callback `consumer.run` invokes per record.
|
|
603
|
+
* Pulled out of the orchestrator so the synchronous gates and the
|
|
604
|
+
* "did we hit `maxMessages`?" signal are individually testable.
|
|
605
|
+
*
|
|
606
|
+
* The synchronous boolean/predicate getters (`isAccepting`,
|
|
607
|
+
* `isPreflightApplied`, `shouldKeepDuringPrePause`) intentionally stay
|
|
608
|
+
* synchronous — `await`ing a Deferred in their place would change
|
|
609
|
+
* behavior, because pre-pause deliveries from librdkafka's fetch buffer
|
|
610
|
+
* must be filtered on arrival rather than queued through.
|
|
611
|
+
*
|
|
612
|
+
* Pre-pause gate semantics: before the post-assignment pause step has
|
|
613
|
+
* run, drop a delivery **only** when its `(topic, partition)` is one
|
|
614
|
+
* we'd pause anyway (i.e. outside the topic's keep-set). Keep-set
|
|
615
|
+
* deliveries arriving during the brief window between assignment-landing
|
|
616
|
+
* and pause-being-applied are real records the caller asked for; the
|
|
617
|
+
* pre-run-stashed seeks have already positioned the consumer, so
|
|
618
|
+
* librdkafka can hand them to `eachMessage` immediately. Dropping them
|
|
619
|
+
* would be silent data loss. After `preflightApplied` flips, the gate
|
|
620
|
+
* doesn't fire (pause has already kept the unwanted partitions silent).
|
|
621
|
+
*/
|
|
622
|
+
export function createEachMessageHandler(opts) {
|
|
623
|
+
return async ({ topic, partition, message }) => {
|
|
624
|
+
if (!opts.state.isAccepting())
|
|
625
|
+
return;
|
|
626
|
+
if (!opts.state.isPreflightApplied() &&
|
|
627
|
+
!opts.state.shouldKeepDuringPrePause(topic, partition)) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const processed = await opts.processMessage(topic, partition, message);
|
|
631
|
+
opts.state.consumedMessages.push(processed);
|
|
632
|
+
if (opts.state.consumedMessages.length >= opts.maxMessages) {
|
|
633
|
+
opts.onMaxReached();
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
}
|
|
43
637
|
/**
|
|
44
638
|
* Handler for consuming messages from Kafka topics with support for Schema Registry deserialization.
|
|
45
639
|
* This handler allows consuming messages from one or more topics with configurable message limits and timeouts.
|
|
@@ -52,25 +646,25 @@ export class ConsumeKafkaMessagesHandler extends BaseToolHandler {
|
|
|
52
646
|
* @param partition - The partition the message was consumed from
|
|
53
647
|
* @param message - The raw Kafka message
|
|
54
648
|
* @param registry - Optional Schema Registry client for deserialization
|
|
55
|
-
* @param valueOptions - Options for value deserialization
|
|
56
|
-
* @param keyOptions -
|
|
649
|
+
* @param valueOptions - Options for value-side deserialization
|
|
650
|
+
* @param keyOptions - Options for key-side deserialization
|
|
57
651
|
* @returns A processed message with deserialized key and value
|
|
58
652
|
*/
|
|
59
653
|
async processMessage(topic, partition, message, registry, valueOptions, keyOptions) {
|
|
60
654
|
let processedKey = message.key?.toString();
|
|
61
655
|
let processedValue = message.value?.toString();
|
|
62
656
|
const deserializeWithOptions = async (buffer, options, serdeType) => {
|
|
63
|
-
if (
|
|
657
|
+
if (options.disableSchemaRegistry || !registry) {
|
|
64
658
|
return buffer?.toString();
|
|
65
659
|
}
|
|
66
660
|
const subject = options.subject ||
|
|
67
661
|
`${topic}-${serdeType === SerdeType.KEY ? "key" : "value"}`;
|
|
68
|
-
const schema = await getLatestSchemaIfExists(registry, subject);
|
|
662
|
+
const schema = await schemaRegistryHelper.getLatestSchemaIfExists(registry, subject);
|
|
69
663
|
if (!schema || !schema.schemaType) {
|
|
70
664
|
return buffer?.toString();
|
|
71
665
|
}
|
|
72
666
|
try {
|
|
73
|
-
return await deserializeMessage(topic, buffer, schema.schemaType, registry, serdeType);
|
|
667
|
+
return await schemaRegistryHelper.deserializeMessage(topic, buffer, schema.schemaType, registry, serdeType);
|
|
74
668
|
}
|
|
75
669
|
catch (err) {
|
|
76
670
|
logger.error({ error: err, topic, schemaType: schema.schemaType, serdeType }, `Error deserializing message ${serdeType} for topic ${topic}`);
|
|
@@ -78,13 +672,13 @@ export class ConsumeKafkaMessagesHandler extends BaseToolHandler {
|
|
|
78
672
|
}
|
|
79
673
|
};
|
|
80
674
|
processedValue = await deserializeWithOptions(message.value, valueOptions, SerdeType.VALUE);
|
|
81
|
-
if (message.key
|
|
675
|
+
if (message.key) {
|
|
82
676
|
processedKey = await deserializeWithOptions(message.key, keyOptions, SerdeType.KEY);
|
|
83
677
|
}
|
|
84
678
|
return {
|
|
85
679
|
key: processedKey,
|
|
86
680
|
value: processedValue,
|
|
87
|
-
timestamp: message.timestamp,
|
|
681
|
+
timestamp: formatMessageTimestamp(message.timestamp),
|
|
88
682
|
offset: message.offset,
|
|
89
683
|
headers: message.headers
|
|
90
684
|
? Object.fromEntries(Object.entries(message.headers).map(([key, value]) => [
|
|
@@ -97,60 +691,193 @@ export class ConsumeKafkaMessagesHandler extends BaseToolHandler {
|
|
|
97
691
|
};
|
|
98
692
|
}
|
|
99
693
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
694
|
+
* Consume messages from one or more Kafka topics, honoring per-topic
|
|
695
|
+
* `partition` and `start` controls. Resolves when one of four exit
|
|
696
|
+
* conditions wins a `Promise.race`: `maxMessages` records have been
|
|
697
|
+
* collected, the `timeoutMs` budget elapses, `consumer.run()` rejects,
|
|
698
|
+
* or the post-assignment hook rejects. On the timeout path the
|
|
699
|
+
* partial set collected so far is returned (success-shaped response,
|
|
700
|
+
* not an error).
|
|
701
|
+
*
|
|
702
|
+
* @param runtime - The {@link ServerRuntime} (config + active client
|
|
703
|
+
* manager + OAuth holder). Supplied by the MCP server dispatcher;
|
|
704
|
+
* the handler resolves cluster/env/registry clients off of it.
|
|
705
|
+
* @param toolArguments - Parsed args matching `consumeKafkaMessagesArgs`.
|
|
706
|
+
* The handler re-parses internally to apply defaults at the
|
|
707
|
+
* boundary.
|
|
708
|
+
* @returns A {@link CallToolResult}. Success path emits a text block
|
|
709
|
+
* summarizing the consumed messages; failures (build/preflight/run
|
|
710
|
+
* errors) surface via `createResponse(text, true)` with the
|
|
711
|
+
* formatted Kafka error.
|
|
105
712
|
*/
|
|
106
|
-
async handle(
|
|
107
|
-
const
|
|
713
|
+
async handle(runtime, toolArguments) {
|
|
714
|
+
const parsed = consumeKafkaMessagesArgs.parse(toolArguments);
|
|
715
|
+
const { maxMessages, timeoutMs, valueFormat, keyFormat } = parsed;
|
|
716
|
+
const { connId, conn, clientManager } = this.resolveSoleConnection(runtime);
|
|
717
|
+
const resolved = resolveKafkaClusterArgs(parsed, runtime, connId);
|
|
718
|
+
// Auto-decode when SR is reachable on this connection (OAuth always; direct
|
|
719
|
+
// requires a `schema_registry` block — single source of truth lives in the
|
|
720
|
+
// `hasSchemaRegistryOrOAuth` predicate) and the caller hasn't opted out on
|
|
721
|
+
// BOTH sides. Either side wanting decode triggers the SR fetch.
|
|
722
|
+
const userDisabled = valueFormat.disableSchemaRegistry && keyFormat.disableSchemaRegistry;
|
|
723
|
+
const srReachable = hasSchemaRegistryOrOAuth(conn).enabled;
|
|
724
|
+
let registry;
|
|
725
|
+
if (!userDisabled && srReachable) {
|
|
726
|
+
try {
|
|
727
|
+
registry = await clientManager.getSchemaRegistrySdkClient(resolved.envId);
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
// Graceful fallback: user didn't explicitly opt in, so an SR-transport
|
|
731
|
+
// failure shouldn't fail an otherwise-satisfiable consume. Per-message
|
|
732
|
+
// decode errors already fall back to raw inside processMessage.
|
|
733
|
+
logger.warn({ error, connId }, "Schema Registry client unavailable; consuming as raw bytes.");
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const targets = normalizeTopicTargets(parsed);
|
|
737
|
+
const offsetReset = deriveConsumerOffsetReset(targets);
|
|
738
|
+
const needsPreflight = planNeedsPreflight(targets, offsetReset);
|
|
739
|
+
let plan;
|
|
740
|
+
if (needsPreflight) {
|
|
741
|
+
// `admin` is acquired INSIDE the try so a failed
|
|
742
|
+
// getKafkaAdminClient() (auth/network) becomes a tool-error
|
|
743
|
+
// response rather than propagating up unhandled. The finally
|
|
744
|
+
// guards against running disposeIfOAuth on an undefined admin
|
|
745
|
+
// (which would happen if the acquisition itself threw).
|
|
746
|
+
let admin;
|
|
747
|
+
try {
|
|
748
|
+
admin = await clientManager.getKafkaAdminClient(resolved.clusterId, resolved.envId);
|
|
749
|
+
plan = await buildPreflightPlan(admin, targets, offsetReset);
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
return this.createResponse(`Failed to consume messages: ${formatKafkaError(error)}`, true);
|
|
753
|
+
}
|
|
754
|
+
finally {
|
|
755
|
+
if (admin !== undefined) {
|
|
756
|
+
await disposeIfOAuth(runtime, connId, admin);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
plan = {
|
|
762
|
+
topicNames: [...new Set(targets.map((t) => t.name))],
|
|
763
|
+
keepPartitions: new Map(),
|
|
764
|
+
seeks: [],
|
|
765
|
+
};
|
|
766
|
+
}
|
|
108
767
|
const consumedMessages = [];
|
|
109
|
-
|
|
768
|
+
// `accepting` is the synchronous gate that suppresses further
|
|
769
|
+
// processing once any terminal condition fires. `preflightApplied`
|
|
770
|
+
// is the synchronous gate that drops pre-pause fetch-buffer
|
|
771
|
+
// deliveries. Both stay booleans because eachMessage needs to check
|
|
772
|
+
// them on arrival — a `Promise` would require `await`, which would
|
|
773
|
+
// queue rather than drop the message.
|
|
774
|
+
let accepting = true;
|
|
775
|
+
let preflightApplied = !needsPreflight;
|
|
110
776
|
let consumer;
|
|
111
|
-
const registry = value.useSchemaRegistry || (key && key.useSchemaRegistry)
|
|
112
|
-
? clientManager.getSchemaRegistryClient()
|
|
113
|
-
: undefined;
|
|
114
777
|
try {
|
|
115
|
-
consumer = await clientManager.
|
|
778
|
+
consumer = await clientManager.buildKafkaConsumer({
|
|
779
|
+
clusterId: resolved.clusterId,
|
|
780
|
+
envId: resolved.envId,
|
|
781
|
+
// Per-invocation unique group id: each consume call becomes
|
|
782
|
+
// the sole member of its own Kafka consumer group, so two
|
|
783
|
+
// concurrent calls can't race for the same partition
|
|
784
|
+
// assignment via rebalance. (How the value reaches `group.id`
|
|
785
|
+
// depends on the manager: `DirectClientManager` appends it as
|
|
786
|
+
// a suffix to its base `mcp-confluent` group; `OAuthClientManager`
|
|
787
|
+
// uses it as the literal group id. Both yield a unique-per-call
|
|
788
|
+
// group, which is what the no-contention contract requires.)
|
|
789
|
+
groupId: nodeCrypto.randomUUID(),
|
|
790
|
+
offsetReset,
|
|
791
|
+
});
|
|
116
792
|
await consumer.connect();
|
|
117
|
-
await consumer.subscribe({ topics: topicNames });
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
.
|
|
137
|
-
reject(error);
|
|
793
|
+
await consumer.subscribe({ topics: plan.topicNames });
|
|
794
|
+
// Stash per-partition seeks on the consumer BEFORE `consumer.run()`
|
|
795
|
+
// is invoked. The kafkajs-compat library queues these as "pending
|
|
796
|
+
// seeks" via its `#addPendingOperation` → `#seekInternal` path; when
|
|
797
|
+
// the first rebalance fires inside `run()`, the assignment handler
|
|
798
|
+
// calls `#assignAsPerSeekedOffsets` and modifies the assignment to
|
|
799
|
+
// include the seeked offsets BEFORE calling `assignmentFn`, so the
|
|
800
|
+
// partition is assigned at the seek target atomically.
|
|
801
|
+
//
|
|
802
|
+
// This avoids the `ERR__STATE` race that the post-assignment seek
|
|
803
|
+
// path trips: even after `consumer.assignment()` returns the
|
|
804
|
+
// partition, the native librdkafka client can briefly reject seeks
|
|
805
|
+
// because its internal state hasn't fully transitioned from
|
|
806
|
+
// "rebalancing" to "ready." The pre-run path bypasses native seek
|
|
807
|
+
// calls entirely.
|
|
808
|
+
for (const s of plan.seeks) {
|
|
809
|
+
consumer.seek({
|
|
810
|
+
topic: s.topic,
|
|
811
|
+
partition: s.partition,
|
|
812
|
+
offset: s.offset,
|
|
138
813
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
814
|
+
}
|
|
815
|
+
// Named exit conditions. Each Deferred resolves only its own
|
|
816
|
+
// promise — the prior shared `timeoutReached` boolean was written
|
|
817
|
+
// by three sites with overlapping semantics; this splits them into
|
|
818
|
+
// independent signals the race observes.
|
|
819
|
+
const maxReached = Promise.withResolvers();
|
|
820
|
+
const timedOut = Promise.withResolvers();
|
|
821
|
+
const consumerActive = consumer;
|
|
822
|
+
const eachMessage = createEachMessageHandler({
|
|
823
|
+
state: {
|
|
824
|
+
consumedMessages,
|
|
825
|
+
isAccepting: () => accepting,
|
|
826
|
+
isPreflightApplied: () => preflightApplied,
|
|
827
|
+
// Pre-pause gate predicate: a (topic, partition) is "keep" if
|
|
828
|
+
// the topic has no partition restriction (keepPartitions omits
|
|
829
|
+
// it) or the partition is in the topic's keep-set. The
|
|
830
|
+
// orchestrator's `plan.keepPartitions` is built so absence of
|
|
831
|
+
// an entry means "no restriction"; we honor that contract here.
|
|
832
|
+
shouldKeepDuringPrePause: (topic, partition) => {
|
|
833
|
+
const keepSet = plan.keepPartitions.get(topic);
|
|
834
|
+
return keepSet === undefined || keepSet.has(partition);
|
|
835
|
+
},
|
|
836
|
+
},
|
|
837
|
+
maxMessages,
|
|
838
|
+
onMaxReached: () => {
|
|
839
|
+
accepting = false;
|
|
840
|
+
maxReached.resolve();
|
|
841
|
+
},
|
|
842
|
+
processMessage: (topic, partition, message) => this.processMessage(topic, partition, message, registry, valueFormat, keyFormat),
|
|
143
843
|
});
|
|
144
|
-
|
|
145
|
-
|
|
844
|
+
const timer = setTimeout(() => {
|
|
845
|
+
accepting = false;
|
|
846
|
+
timedOut.resolve();
|
|
847
|
+
}, timeoutMs);
|
|
848
|
+
const preflightHook = needsPreflight
|
|
849
|
+
? applyPostAssignmentHook({
|
|
850
|
+
consumer: consumerActive,
|
|
851
|
+
plan,
|
|
852
|
+
deadlineMs: Date.now() + timeoutMs,
|
|
853
|
+
onApplied: () => {
|
|
854
|
+
preflightApplied = true;
|
|
855
|
+
},
|
|
856
|
+
onAssignmentTimedOut: () => {
|
|
857
|
+
accepting = false;
|
|
858
|
+
timedOut.resolve();
|
|
859
|
+
},
|
|
860
|
+
})
|
|
861
|
+
: Promise.resolve();
|
|
862
|
+
const runPromise = consumerActive.run({ eachMessage });
|
|
863
|
+
try {
|
|
864
|
+
await Promise.race([
|
|
865
|
+
maxReached.promise,
|
|
866
|
+
timedOut.promise,
|
|
867
|
+
// Success of either branch means "engine still running" — only
|
|
868
|
+
// their rejection is a terminal condition for the race.
|
|
869
|
+
rejectOnly(runPromise),
|
|
870
|
+
rejectOnly(preflightHook),
|
|
871
|
+
]);
|
|
872
|
+
}
|
|
873
|
+
finally {
|
|
874
|
+
clearTimeout(timer);
|
|
875
|
+
accepting = false;
|
|
876
|
+
}
|
|
877
|
+
return this.createResponse(`Consumed ${consumedMessages.length} messages from topics ${plan.topicNames.join(", ")}.\nConsumed messages: ${JSON.stringify(consumedMessages, null, 2)}`, false);
|
|
146
878
|
}
|
|
147
879
|
catch (error) {
|
|
148
|
-
|
|
149
|
-
? error.message
|
|
150
|
-
: error instanceof KafkaJS.KafkaJSError
|
|
151
|
-
? `Kafka error (${error.code}): ${error.message}`
|
|
152
|
-
: String(error);
|
|
153
|
-
return this.createResponse(`Failed to consume messages: ${errorMessage}`, true);
|
|
880
|
+
return this.createResponse(`Failed to consume messages: ${formatKafkaError(error)}`, true);
|
|
154
881
|
}
|
|
155
882
|
finally {
|
|
156
883
|
if (consumer) {
|
|
@@ -166,13 +893,13 @@ export class ConsumeKafkaMessagesHandler extends BaseToolHandler {
|
|
|
166
893
|
getToolConfig() {
|
|
167
894
|
return {
|
|
168
895
|
name: ToolName.CONSUME_MESSAGES,
|
|
169
|
-
description: "
|
|
896
|
+
description: "Consume messages from Kafka topics. Optionally restrict to a partition, start from an offset, timestamp, earliest, latest, or tailmost pre-existing messages." +
|
|
897
|
+
" Auto-deserializes Schema Registry messages (AVRO/JSON/PROTOBUF) when SR is configured on the connection; pass `valueFormat: {disableSchemaRegistry: true}` (or `keyFormat`) to skip decoding per side.",
|
|
170
898
|
inputSchema: consumeKafkaMessagesArgs.shape,
|
|
171
899
|
annotations: READ_ONLY,
|
|
172
900
|
};
|
|
173
901
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
902
|
+
category = ToolCategory.Kafka;
|
|
903
|
+
predicate = kafkaBootstrapOrOAuth;
|
|
177
904
|
}
|
|
178
905
|
//# sourceMappingURL=consume-kafka-messages-handler.js.map
|