BigQuery-Read-Only-MCP-Server
Um servidor MCP (Model Context Protocol) seguro e auto-hospedado para Google BigQuery. Listas de permissão de tabelas rigorosas, limites de varredura por consulta, limitação de taxa integrada e custos previsíveis no Cloud Run. Funciona com Claude, ChatGPT, Cursor, Gemini e qualquer agente de IA compatível com MCP.
Documentação
Servidor MCP Somente Leitura para BigQuery
Um servidor Model Context Protocol (MCP) para Google BigQuery seguro e auto-hospedado. Listas de permissão rígidas de tabelas, tetos de varredura por consulta, limitação de taxa integrada e custos previsíveis no Cloud Run. Funciona com Claude, ChatGPT, Cursor, Gemini e qualquer agente de IA compatível com MCP.
📚 Artigos relacionados
Publicações longas sobre este projeto em hugonissar.github.io:
- Auto-hospedado vs servidor MCP oficial do Google para BigQuery: uma comparação de segurança e custo — comparação lado a lado de recursos e quando escolher cada um.
- Como impedir a consulta de US$ 2.000: limitando o custo de varredura do BigQuery a partir de um servidor MCP — as três camadas de controle de custo com exemplos de vínculos de IAM.
Ou leia a visão geral do projeto.
📖 O que é isto
Um servidor MCP para BigQuery pronto para produção que você implanta no seu próprio projeto GCP. Agentes de IA se conectam via HTTPS e podem fazer exatamente duas coisas: ler os esquemas das tabelas que você explicitamente permite na lista, e executar consultas SELECT contra elas — sujeitas a um orçamento de varredura configurável, um limite de linhas de resultado e uma limitação de taxa por token-bucket. Nada mais. Sem DROP, sem INSERT, sem descoberta de esquema além do que você permite, sem cobranças surpresa.
Ele existe porque as alternativas — incluindo o servidor MCP oficial do Google para BigQuery — expõem todas as tabelas que a conta de serviço subjacente consegue acessar. Esse é o padrão correto para analistas internos confiáveis. É o padrão errado para um agente autônomo que pode ser invocado por um chatbot voltado ao cliente, uma ferramenta de terceiros ou um documento com injeção de prompt.
💬 Caso de uso
Você: Me dê a campanha com maior conversão nos últimos 30 dias
LLM: consulta seus dados e retorna o resultado
Conecte uma vez, consulte tudo. Aponte para sua exportação do Google Analytics 4, ou importe Google Ads, Meta e TikTok para o BigQuery para analisar todas as campanhas em todos os canais a partir de um único agente.
🏆 Por que escolher isto em vez do servidor MCP oficial do Google para BigQuery
-
Lista de permissão rígida de tabelas aplicada no código, não apenas no IAM. Você lista pares
(dataset, table)em variáveis de ambiente. Qualquer coisa fora da lista de permissão é rejeitada no parser SQL antes que um job seja sequer enviado — incluindo referências qualificadas comowrong_dataset.allowed_table. O servidor do Google permite que o agente veja todas as tabelas que o IAM da conta de serviço permite. -
Teto rígido de varredura por consulta (
MAX_SCAN_MB). Toda consulta passa primeiro por um dry-run. Se a varredura estimada exceder o teto, a consulta é rejeitada — nenhum job do BigQuery é criado, nenhum byte é cobrado. Isso elimina de vez o conhecido problema "a IA acabou de rodar uma consulta de US$ 2.000". O servidor do Google não tem teto de varredura integrado — você precisa aplicar isso via papéis IAM personalizados ou cotas no nível do BigQuery. -
Limitação de taxa integrada com token-bucket + burst. A documentação do Google é explícita: "O servidor MCP do BigQuery não tem cotas próprias. Não há limite no número de chamadas que podem ser feitas ao servidor MCP." Este servidor vem com
RATE_LIMIT_QPMeRATE_LIMIT_BURSTconfiguráveis de fábrica, além de semáforos de concorrência separados para consultas vs. chamadas de metadados. -
Somente leitura aplicado pelo parser SQL, não apenas pelo IAM. DDL, DML, scripting, corpos com múltiplas instruções e construções procedurais são rejeitados na camada de aplicação, além do que o IAM já oferece. Defesa em profundidade — uma concessão de IAM mal configurada não pode habilitar escritas acidentalmente.
-
Limite de linhas de resultado (
MAX_RESULT_ROWS). Uma consulta que retorna um milhão de linhas é truncada no servidor antes de tocar a janela de contexto de um LLM. Economiza tokens, economiza dinheiro e evita exfiltração acidental de PII por varreduras amplas. -
Cache de dry-run. Dry-runs repetidos do mesmo SQL passam por um cache LRU + TTL, então um agente que tenta novamente ou itera não gera chamadas redundantes à API do BigQuery. Consultas de esquema também são armazenadas em cache com TTL.
-
Custo previsível, quase zero em inatividade. O Cloud Run reduz a escala para zero. Você paga praticamente nada em inatividade e alguns dólares por mês sob carga moderada. Compare com um endpoint gerenciado onde você fica preso ao modelo de preços que o fornecedor adotar após o GA.
-
Multi-tenant via variáveis de ambiente, sem arquivo de configuração. Listas separadas por vírgula de
BQ_DATASET_IDeBQ_ALLOWED_TABLEsão pareadas posicionalmente —analytics.events,reporting.daily, etc. Adicione ou remova tabelas com um únicogcloud run deploy --update-env-vars. Sem arquivo de configuração para gravar na imagem, sem reimplantações de um serviço de configuração separado. -
Superfície de ferramentas mínima. Apenas duas ferramentas expostas:
get_table_schemaequery_assessments. Superfície de ataque menor, mais fácil de auditar, menos espaço para o agente usar indevidamente. O servidor do Google expõe umexecute_sqlgenérico além de ferramentas de descoberta de metadados que percorrem todo o grafo do projeto. -
Um único arquivo, licença MIT, ~1400 linhas. Leia o código-fonte. Faça um fork. Adicione um validador personalizado. Troque o esquema de autenticação. Você não consegue fazer nada disso com um servidor gerenciado de código fechado.
⚖️ Tabela comparativa
| Este servidor | Servidor MCP oficial do Google para BigQuery | |
|---|---|---|
| Modelo de implantação | Auto-hospedado no Cloud Run | Endpoint remoto gerenciado |
| Autenticação | Chave de API + chave de administrador | Google IAM / OAuth |
| Controle de acesso a tabelas | Lista de permissão rígida de pares (dataset, table), aplicada no código | Somente IAM — qualquer tabela acessível |
| Teto de varredura por consulta | Sim (MAX_SCAN_MB, aplicado via dry-run) | Não (depende de cotas IAM / BQ) |
| Limitação de taxa | Token bucket integrado + burst | Nenhuma (segundo a documentação do Google) |
| Limite de linhas de resultado | Sim (MAX_RESULT_ROWS) | Não |
| Cache de dry-run / esquema | Sim (LRU + TTL) | Não |
| Aplicação de somente leitura | Parser SQL + IAM | Somente IAM |
| Ferramentas expostas | 2 (esquema, consulta) | 5+ incluindo execute_sql, previsão, listagem de datasets/tabelas |
| Previsão / auxiliares de ML | Não (escreva seu próprio SQL) | Sim (forecast integrado) |
| Varredura de injeção de prompt | Não | Sim (via Model Armor, complemento pago) |
| Registro de auditoria | Cloud Logging + logs de auditoria do BQ | Cloud Audit Logs |
| Código-fonte | Aberto (MIT) | Fechado |
| Custo em inatividade | ~US$ 0 (reduz escala para zero) | N/A (gerenciado) |
| Fork e modificação | Sim | Não |
| Região fixa | Escolha qualquer região do Cloud Run | Somente regiões do Google |
Quando escolher o servidor do Google: se você quiser previsão e ARIMA prontos para uso, se precisar do Model Armor para varredura de injeção de prompt, ou se estiver confortável em deixar o agente ver tudo o que o IAM da conta de serviço alcança e não precisar de um teto rígido de varredura.
Quando escolher este servidor: qualquer outro caso — especialmente agentes de produção, implantações voltadas ao cliente, ambientes regulados, cenários multi-tenant e qualquer lugar onde as palavras "orçamento de varredura" ou "limite de taxa" importem.
🏗️ Arquitetura em resumo
┌─────────────┐ HTTPS + X-API-KEY ┌──────────────────────┐ IAM ┌───────────┐
│ MCP client │ ────────────────────────────▶ │ Cloud Run service │ ─────────────────▶ │ BigQuery │
│ (Claude / │ API KEY │ bigquery-readonly- │ service account │ datasets │
│ Cursor / │ │ mcp-server │ │ + tables │
│ ChatGPT) │ │ │ │ │
└─────────────┘ │ • SQL allowlist │ └───────────┘
│ • Dry-run scan cap │
│ • Rate limiter │ ┌───────────┐
│ • Schema cache │ ◀───── secrets ── │ Secret │
└──────────────────────┘ │ Manager │
└───────────┘
☁️ Serviços GCP necessários
Ative estas APIs no seu projeto:
| Serviço | Finalidade | Necessário |
|---|---|---|
Cloud Run (run.googleapis.com) | Hospeda o servidor MCP | Sim |
BigQuery (bigquery.googleapis.com) | O data warehouse que você está consultando | Sim |
Secret Manager (secretmanager.googleapis.com) | Armazena MCP_API_KEY e MCP_ADMIN_KEY | Sim |
Artifact Registry (artifactregistry.googleapis.com) | Hospeda a imagem do contêiner | Sim |
IAM (iam.googleapis.com) | Conta de serviço + vínculos de papéis | Sim |
Cloud Build (cloudbuild.googleapis.com) | Se você for compilar a imagem no GCP | Opcional |
| Cloud Logging | Captura logs estruturados do serviço | Automático |
| Cloud Monitoring | Captura métricas + alertas | Automático |
Ativação rápida:
gcloud services enable \
run.googleapis.com \
bigquery.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com \
iam.googleapis.com \
cloudbuild.googleapis.com
🔐 Permissões da conta de serviço (menor privilégio)
Crie uma conta de serviço dedicada — não reutilize uma existente. O serviço precisa do mínimo absoluto: acesso de leitura a tabelas específicas do BigQuery, capacidade de executar jobs de consulta e acesso de leitura a dois segredos.
PROJECT_ID="your-project"
SA_NAME="bigquery-readonly-mcp"
SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts create $SA_NAME \
--display-name="BigQuery Read-Only MCP Server"
🔑 Papéis IAM necessários
| Papel | Escopo | Por quê |
|---|---|---|
roles/bigquery.jobUser | Projeto | Permite que a conta de serviço envie jobs de consulta (não concede acesso a dados) |
roles/bigquery.dataViewer | Dataset (por dataset na lista de permissão) | Ler esquemas + linhas de tabelas |
roles/bigquery.metadataViewer | Dataset (por dataset na lista de permissão) | Ler esquemas sem acesso de leitura — opcional, apenas se você tiver datasets onde deseja visibilidade de esquema sem acesso a linhas |
roles/secretmanager.secretAccessor | Segredo (por segredo) | Ler chave de API + chave de administrador |
Conceder bigquery.jobUser no projeto
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/bigquery.jobUser"
Conceder bigquery.dataViewer no dataset específico (preferível em vez de no projeto inteiro)
# For each dataset in BQ_DATASET_ID:
DATASET="your_dataset"
bq add-iam-policy-binding \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/bigquery.dataViewer" \
"${PROJECT_ID}:${DATASET}"
Para controle mais rígido, conceda acesso no nível da tabela em vez do dataset:
DATASET="your_dataset"
TABLE="your_table"
bq add-iam-policy-binding \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/bigquery.dataViewer" \
"${PROJECT_ID}:${DATASET}.${TABLE}"
Conceder secretAccessor nos dois segredos
gcloud secrets add-iam-policy-binding mcp-api-key \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/secretmanager.secretAccessor"
gcloud secrets add-iam-policy-binding mcp-admin-key \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/secretmanager.secretAccessor"
Essa é toda a superfície de IAM. Não conceda bigquery.user, bigquery.admin, editor ou owner — nenhum deles é necessário e todos concedem estritamente mais do que o necessário.
🚀 Início rápido
1. Gerar e armazenar chaves de API
# MCP API key (clients use this in X-API-KEY)
openssl rand -hex 32 | gcloud secrets create mcp-api-key --data-file=-
# Admin key (for the /admin endpoint — schema cache invalidation)
openssl rand -hex 32 | gcloud secrets create mcp-admin-key --data-file=-
2. Compilar e enviar a imagem
REGION="europe-north2"
PROJECT_ID="your-project"
REPO="bigquery-readonly-mcp"
IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/server:latest"
gcloud artifacts repositories create $REPO \
--repository-format=docker \
--location=$REGION
gcloud builds submit --tag $IMAGE
3. Implantar no Cloud Run
gcloud run deploy bigquery-readonly-mcp \
--image="$IMAGE" \
--region="$REGION" \
--service-account="bigquery-readonly-mcp@${PROJECT_ID}.iam.gserviceaccount.com" \
--set-secrets="MCP_API_KEY=mcp-api-key:latest,MCP_ADMIN_KEY=mcp-admin-key:latest" \
--set-env-vars="\
GCP_PROJECT_ID=${PROJECT_ID},\
BQ_DATASET_ID=your_dataset,\
BQ_ALLOWED_TABLE=your_table,\
MAX_SCAN_MB=100,\
MAX_RESULT_ROWS=2000,\
BQ_JOB_TIMEOUT_SECS=60,\
MAX_SQL_LENGTH=2000,\
SCHEMA_TTL_SECS=300,\
DRY_RUN_CACHE_TTL_SECS=60,\
DRY_RUN_CACHE_MAX_ENTRIES=1000,\
RATE_LIMIT_QPM=20,\
RATE_LIMIT_BURST=5,\
QUERY_CONCURRENCY=10,\
META_CONCURRENCY=3,\
ADMIN_RATE_LIMIT_QPM=10,\
MAX_REQUEST_BODY_BYTES=65536" \
--allow-unauthenticated \
--port 8080
Para vários datasets/tabelas, o gcloud precisa de um delimitador alternativo para que vírgulas dentro dos valores não sejam divididas como variáveis separadas:
gcloud run deploy bigquery-readonly-mcp \
--image="$IMAGE" \
...
--set-env-vars="^@^\
GCP_PROJECT_ID=${PROJECT_ID}@\
BQ_DATASET_ID=analytics,reporting,raw@\
BQ_ALLOWED_TABLE=events,daily_summary,events@\
MAX_SCAN_MB=100"
Datasets e tabelas são pareados posicionalmente: o índice 0 é pareado com o índice 0. O exemplo acima permite analytics.events, reporting.daily_summary e raw.events. Os tamanhos das listas devem ser iguais.
4. Conectar a partir de um cliente MCP
// claude_desktop_config.json or equivalent
{
"mcpServers": {
"bigquery": {
"command": "/home/yourusername/.local/bin/uvx",
"args": [
"mcp-proxy",
"--transport", "streamablehttp",
"-H", "x-api-key", "YOUR_MCP_API_KEY",
"https://bigquery-readonly-mcp-XXXX.a.run.app/mcp"
]
}
}
}
⚙️ Referência de configuração
Toda a configuração é feita por variáveis de ambiente. Variáveis obrigatórias abortam a inicialização se ausentes; todo o resto tem um valor padrão.
| Variável | Obrigatória | Padrão | Descrição |
|---|---|---|---|
GCP_PROJECT_ID | Sim | — | O projeto onde os jobs do BigQuery são executados |
BQ_DATASET_ID | Sim | — | Lista de datasets separada por vírgulas |
BQ_ALLOWED_TABLE | Sim | — | Lista de tabelas separada por vírgulas; pareada posicionalmente com BQ_DATASET_ID |
MCP_API_KEY | Sim | — | Chave de API que os clientes devem apresentar |
MCP_ADMIN_KEY | Não | — | Chave de API para o endpoint /admin; se vazia, usa o padrão MCP_API_KEY |
MAX_SCAN_MB | Não | 100 | Teto rígido de bytes varridos por consulta (MB) |
MAX_RESULT_ROWS | Não | 2000 | Linhas de resultado são truncadas acima deste valor |
BQ_JOB_TIMEOUT_SECS | Não | 30 | Timeout do BigQuery por consulta |
MAX_SQL_LENGTH | Não | 2000 | Rejeita SQL mais longo que isto |
MAX_REQUEST_BODY_BYTES | Não | 65536 | Rejeita corpos HTTP maiores que isto |
SCHEMA_TTL_SECS | Não | 300 | TTL do cache de esquema |
DRY_RUN_CACHE_TTL_SECS | Não | 60 | TTL do cache de dry-run |
DRY_RUN_CACHE_MAX_ENTRIES | Não | 1000 | Tamanho do LRU para o cache de dry-run |
RATE_LIMIT_QPM | Não | 20 | Taxa de reabastecimento do token-bucket (consultas por minuto) |
RATE_LIMIT_BURST | Não | 5 | Capacidade de burst do token-bucket |
QUERY_CONCURRENCY | Não | 10 | Máximo de jobs de consulta BQ simultâneos |
META_CONCURRENCY | Não | 3 | Máximo de chamadas de metadados simultâneas |
ADMIN_RATE_LIMIT_QPM | Não | 10 | Limite de taxa separado para o endpoint de administrador |
🛡️ Modelo de segurança
O modelo de ameaça é: um agente de IA é parcial ou totalmente não confiável e pode ser invocado com entrada adversária. Os controles são em camadas.
- Camada de rede. O Cloud Run oferece terminação HTTPS, com opção de Cloud Armor na frente para allowlist de IP ou regras de WAF.
- Camada de autenticação. Cada requisição exige um cabeçalho
x-api-key: <key>comparado comMCP_API_KEYem tempo constante. O endpoint administrativo exige um cabeçalhox-admin-keyseparado. - Limite de tamanho do corpo. Requisições acima de
MAX_REQUEST_BODY_BYTESsão rejeitadas antes do parsing. - Limite de taxa. Token-bucket, por instância. Clientes com mau comportamento recebem
429s. - Camada de parser SQL.
sqlparsedecompõe cada consulta. Declarações não-SELECT, corpos com múltiplas declarações, DDL, DML, scripting e construções procedurais são rejeitados. - Aplicação da allowlist. Cada tabela referenciada em
FROM/JOIN/ junção por vírgula é validada contra a allowlist(dataset, table)nos seus dois últimos segmentos. Referências entre datasets comowrong_ds.allowed_tablesão rejeitadas. Nomes de tabela nus que existem em múltiplos datasets permitidos são rejeitados como ambíguos (o agente deve qualificar). - Consciência de CTE. Nomes de Common Table Expression são isentos — não são tabelas.
- Teto de varredura dry-run. O dry-run do BigQuery estima bytes varridos. Consultas que excedem
MAX_SCAN_MBsão rejeitadas antes da execução real do job. - Camada IAM. Mesmo que todas as verificações acima fossem contornadas, a conta de serviço só tem
dataViewerem datasets / tabelas específicos. - Truncamento de resultados. A saída é limitada a
MAX_RESULT_ROWSpara que uma única resposta não possa exfiltrar uma tabela inteira.
O que o servidor não faz (e você deve saber):
- Sem varredura de injeção de prompt. Se precisar disso, implante atrás de um gateway de segurança de modelo (ex.: Model Armor do Google, Lakera Guard, NeMo Guardrails) ou use o servidor MCP oficial do Google, que tem integração com Model Armor.
- Sem mascaramento em nível de coluna. A allowlist é na granularidade de tabela. Se suas tabelas contêm colunas de PII que você não quer expor, crie uma visão autorizada do BigQuery que projete apenas colunas seguras e coloque a visão na allowlist.
- Sem atribuição por usuário. A chave de API é compartilhada entre clientes. Para trilhas de auditoria por usuário, coloque um proxy ciente de identidade na frente, ou faça um fork e adicione OAuth.
- Sem suporte a escrita, nunca. Isso é intencional. Se precisar de escrita, use outro servidor.
💻 Desenvolvimento local
git clone https://github.com/hugonissar/bigquery-readonly-mcp-server.git
cd bigquery-readonly-mcp-server
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Authenticate to GCP
gcloud auth application-default login
# Set required env vars
export GCP_PROJECT_ID=your-project
export BQ_DATASET_ID=your_dataset
export BQ_ALLOWED_TABLE=your_table
export MCP_API_KEY=$(openssl rand -hex 32)
# Run
uvicorn main:app --host 0.0.0.0 --port 8080 --reload
O endpoint MCP estará em http://localhost:8080/mcp e /health retorna o status do serviço.
🛠️ Operações
🔄 Invalidar o cache de esquema após uma mudança de esquema
curl -X POST "https://your-service.run.app/admin/invalidate-cache" \
-H "x-admin-key: $MCP_ADMIN_KEY"
📜 Acompanhar logs
gcloud run services logs tail bigquery-readonly-mcp --region=$REGION
🔍 Consultar a trilha de auditoria
Cada job do BigQuery é registrado no Cloud Audit Logs. Para ver quais consultas o servidor MCP executou:
SELECT
protopayload_auditlog.authenticationInfo.principalEmail AS sa,
protopayload_auditlog.servicedata_v1_bigquery.jobCompletedEvent.job.jobConfiguration.query.query AS sql,
protopayload_auditlog.servicedata_v1_bigquery.jobCompletedEvent.job.jobStatistics.totalBilledBytes AS bytes,
timestamp
FROM `your-project.cloudaudit_logs.cloudaudit_googleapis_com_data_access`
WHERE protopayload_auditlog.authenticationInfo.principalEmail = 'bigquery-readonly-mcp@your-project.iam.gserviceaccount.com'
ORDER BY timestamp DESC
LIMIT 100;
⚠️ Limitações
- Uma região por implantação. O Cloud Run é regional. Para failover multi-região, implante múltiplas instâncias atrás de um balanceador de carga global.
- Cold starts. Escala para zero significa que a primeira requisição após ociosidade leva alguns segundos. Defina
--min-instances=1para eliminar isso ao custo de alguns dólares por mês. - Esquema de autenticação único. Apenas chave de API no cabeçalho
x-api-key. Sem OAuth, sem mTLS nativo (ambos são alcançáveis via autenticação baseada em IAM do Cloud Run + um proxy ciente de identidade — veja Operações). - Cache de esquema é por instância. Cada instância do Cloud Run mantém o seu próprio. O endpoint administrativo invalida o cache na instância que recebe a chamada; sob carga com múltiplas instâncias, talvez seja melhor lançar uma nova revisão.
- Sem resultados em streaming. As consultas são materializadas completamente no servidor antes do truncamento. Não aumente
MAX_RESULT_ROWSalém de alguns milhares sem considerar memória.
❓Perguntas frequentes
Funciona com o Claude Desktop? Sim, via a configuração url + headers acima. Também funciona com Cursor, Windsurf, Claude Code, a API Responses da OpenAI e qualquer outra coisa que fale MCP streamable-HTTP.
Posso usar isso com uma VPC privada / conector VPC serverless? Sim. Adicione --vpc-connector e --ingress=internal ao comando de deploy do Cloud Run. Depois anexe um balanceador de carga interno para acesso do cliente.
Como isso se compara ao MCP Toolbox for Databases? O MCP Toolbox é genérico para muitos bancos de dados e configurado via YAML. Este servidor é específico para BigQuery e configurado via variáveis de ambiente. Escolha Toolbox se precisar de multi-banco; escolha este se quiser guardrails específicos do BigQuery (limites de varredura, allowlists) embutidos.
Posso adicionar ferramentas personalizadas? Sim — é um único arquivo Python. Adicione um novo @mcp.tool e aplique o mesmo padrão de validação.
O formato de resposta do esquema é estável? get_table_schema retorna {"tables": [{dataset, table, partition_field, clustering_fields, schema}, ...]}. A forma é estável entre configurações de tabela única e múltiplas tabelas.
Ele suporta SQL legado bq? Não. Apenas SQL padrão (GoogleSQL).
🤝 Contribuição
PRs são bem-vindos. Issues com reproduções têm prioridade.
📄 Licença
MIT. Veja LICENSE.
Palavras-chave: BigQuery MCP server, Model Context Protocol BigQuery, Claude BigQuery integration, secure BigQuery MCP, self-hosted BigQuery MCP, Cloud Run MCP server, BigQuery AI agent, read-only BigQuery, BigQuery LLM, MCP server Cloud Run, BigQuery rate limiting, BigQuery cost control AI.