Skip to content

feat(source/bigquery): add apiEndpoint for custom API host#3358

Open
hannank-rounds wants to merge 1 commit into
googleapis:mainfrom
hannank-rounds:feat/source-bigquery-api-endpoint
Open

feat(source/bigquery): add apiEndpoint for custom API host#3358
hannank-rounds wants to merge 1 commit into
googleapis:mainfrom
hannank-rounds:feat/source-bigquery-api-endpoint

Conversation

@hannank-rounds

Copy link
Copy Markdown

Summary

Adds optional apiEndpoint configuration to the BigQuery source so MCP clients can route BigQuery API calls through a proxy or alternate front-end (equivalent to Python ClientOptions(api_endpoint=...)).

  • tools.yaml: apiEndpoint: "https://my-proxy.example.com"
  • Prebuilt (--prebuilt bigquery): BIGQUERY_ENDPOINT environment variable (empty, direct, or bigquery.googleapis.com → default Google API)
  • Applies to ADC and OAuth client paths; Dataplex catalog is unchanged
  • URL values are normalized to host:port for option.WithEndpoint (default port 443)

Motivation

Teams that use a BigQuery API proxy (e.g. for compliance or routing) already configure this in application code (ClientOptions / env BIGQUERY_ENDPOINT). MCP Toolbox prebuilt BigQuery had no equivalent, so IDE agents could not use the same endpoint as batch generators.

Test plan

  • TestNormalizeAPIEndpoint unit tests for alias and URL normalization
  • YAML parse test for apiEndpoint field
  • CI: go test ./internal/sources/bigquery/...

Made with Cursor

@hannank-rounds hannank-rounds requested review from a team as code owners June 4, 2026 14:24
@google-cla

google-cla Bot commented Jun 4, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for overriding the BigQuery API endpoint via a new apiEndpoint configuration option and BIGQUERY_ENDPOINT environment variable, allowing users to route requests through a proxy or alternate front-end. Feedback from the review highlights an issue in the endpoint normalization logic where stripping the URL scheme and defaulting to port 443 breaks HTTP-only proxies and local emulators. The reviewer suggests preserving the scheme and dynamically assigning the default port (80 for HTTP, 443 for HTTPS/no scheme), along with corresponding updates to the unit tests and documentation.

Comment on lines +35 to +71
func normalizeAPIEndpoint(raw string) string {
s := strings.TrimSpace(raw)
if s == "" {
return ""
}
host := stripURLScheme(s)
host = strings.TrimSuffix(host, "/")
if isDirectAPIEndpoint(host) {
return ""
}
if !strings.Contains(host, ":") {
host = host + ":443"
}
return host
}

func isDirectAPIEndpoint(host string) bool {
lower := strings.ToLower(host)
if _, ok := directAPIEndpointAliases[lower]; ok {
return true
}
if i := strings.LastIndex(lower, ":"); i != -1 {
if _, ok := directAPIEndpointAliases[lower[:i]]; ok {
return true
}
}
return false
}

func stripURLScheme(s string) string {
for _, prefix := range []string{"https://", "http://"} {
if len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix) {
return s[len(prefix):]
}
}
return s
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of normalizeAPIEndpoint strips the URL scheme (e.g., http:// or https://) and appends :443 if no port is specified. This causes issues when a user explicitly configures an HTTP endpoint (such as http://localhost:8080 or http://proxy.example.com for local emulators or HTTP proxies).

When the scheme is stripped, the Google API client library's HTTP transport automatically prepends https:// because the endpoint does not contain ://. This forces the client to use HTTPS/TLS, which will fail against HTTP-only proxies or emulators.

We should preserve the scheme if it is present, and only append the default port (80 for HTTP, 443 for HTTPS or when no scheme is specified) if no port is explicitly provided.

func normalizeAPIEndpoint(raw string) string {
	s := strings.TrimSpace(raw)
	if s == "" {
		return ""
	}
	var scheme string
	host := s
	for _, prefix := range []string{"https://", "http://"} {
		if len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix) {
			scheme = prefix
			host = s[len(prefix):]
			break
		}
	}
	host = strings.TrimSuffix(host, "/")
	if isDirectAPIEndpoint(host) {
		return ""
	}
	if !strings.Contains(host, ":") {
		if scheme == "http://" {
			host = host + ":80"
		} else {
			host = host + ":443"
		}
	}
	return scheme + host
}

func isDirectAPIEndpoint(host string) bool {
	lower := strings.ToLower(host)
	if _, ok := directAPIEndpointAliases[lower]; ok {
		return true
	}
	if i := strings.LastIndex(lower, ":"); i != -1 {
		if _, ok := directAPIEndpointAliases[lower[:i]]; ok {
			return true
		}
	}
	return false
}

Comment on lines +33 to +35
{"https://proxy.example.com", "proxy.example.com:443"},
{"https://proxy.example.com/", "proxy.example.com:443"},
{"proxy.example.com:8443", "proxy.example.com:8443"},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the test cases to align with the new scheme-preserving normalization behavior, and add test cases for HTTP endpoints to ensure they are handled correctly.

		{"https://proxy.example.com", "https://proxy.example.com:443"},
		{"https://proxy.example.com/", "https://proxy.example.com:443"},
		{"http://proxy.example.com", "http://proxy.example.com:80"},
		{"http://proxy.example.com:8080", "http://proxy.example.com:8080"},
		{"proxy.example.com:8443", "proxy.example.com:8443"},

Comment thread docs/en/integrations/bigquery/source.md Outdated
| impersonateServiceAccount | string | false | Service account email to impersonate when making BigQuery and Dataplex API calls. The authenticated principal must have the `roles/iam.serviceAccountTokenCreator` role on the target service account. [Learn More](https://cloud.google.com/iam/docs/service-account-impersonation) |
| maxQueryResultRows | int | false | The maximum number of rows to return from a query. Defaults to 50. |
| maximumBytesBilled | int64 | false | The maximum bytes billed per query. When set, queries that exceed this limit fail before executing. |
| apiEndpoint | string | false | Overrides the BigQuery API endpoint (URL or `host:port`) for proxy or alternate front-ends. Unset, empty, `direct`, `google`, `default`, or `bigquery.googleapis.com` use the default Google endpoint. HTTPS URLs are normalized to `host:port` (default port `443`). Dataplex catalog calls are not affected. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Update the documentation to reflect that URLs are normalized to include a default port based on their scheme (80 for HTTP, 443 for HTTPS/no scheme) rather than always stripping the scheme and forcing 443.

Suggested change
| apiEndpoint | string | false | Overrides the BigQuery API endpoint (URL or `host:port`) for proxy or alternate front-ends. Unset, empty, `direct`, `google`, `default`, or `bigquery.googleapis.com` use the default Google endpoint. HTTPS URLs are normalized to `host:port` (default port `443`). Dataplex catalog calls are not affected. |
| apiEndpoint | string | false | Overrides the BigQuery API endpoint (URL or host:port) for proxy or alternate front-ends. Unset, empty, direct, google, default, or bigquery.googleapis.com use the default Google endpoint. URLs are normalized to include a default port (80 for HTTP, 443 for HTTPS/no scheme) if none is specified. Dataplex catalog calls are not affected. |

@hannank-rounds

Copy link
Copy Markdown
Author

I signed the CLA

Add an optional apiEndpoint (BIGQUERY_ENDPOINT) that overrides the BigQuery
API host for proxies, alternate front-ends, and local emulators. The endpoint
is applied across all three auth paths (ADC, impersonation, OAuth) and to both
the high-level client and the bigquery/v2 REST service.

normalizeAPIEndpoint preserves the URL scheme so http-only proxies and
emulators (e.g. http://localhost:9050) keep working, defaults a missing scheme
to https, appends a default port when absent (:80 for http, else :443), and
strips a trailing slash. Dataplex and ask_data_insights use different API
surfaces and are intentionally out of scope.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hannank-rounds hannank-rounds force-pushed the feat/source-bigquery-api-endpoint branch from 9c3cdec to f60a64a Compare June 4, 2026 15:19
@hannank-rounds

Copy link
Copy Markdown
Author

I signed the CLA

@averikitsch

Copy link
Copy Markdown
Contributor

I signed the CLA

The following contributors were found for this pull request:

f60a64a Author: @hannank-rounds <ha****.k​@rounds.com>
f60a64a Co-Author: <cur******nt​@cursor.com>

You will need to remove the cursor commit or change the author.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants