BigQuery (Service Account)

Live

SERVICE ACCOUNT

DATA WAREHOUSE

Analytics

Every dataset, table, and analytical pipeline your backend connects to lives in BigQuery. BigQuery (Service Account) MCP gives your agent server-to-server authenticated access using a GCP service account — no user OAuth required.

  • Service account scoped at setup: A GCP service account key is configured once and vaulted. The agent calls BigQuery under that identity.
  • Credentials stay vaulted: AES-256, resolved at request time, never in LLM context. No key file on disk.
  • IAM enforced before every call: Dataset-level access, table ACLs, and row-level security all apply. 90-day audit trail.
BigQuery (Service Account)
agent · Acme Q3
Run
Run a query for monthly active users by product tier over the last 6 months.
S
bqsa_query_run
1.4s
Analytics agent
Query ran in 1.4s, scanned 11.2 GB. 6-month MAU: Enterprise avg 3,840; Pro 12,200; Starter 28,400. Enterprise up 14% vs prior period.
Sources: analytics.active_users table, 6 months
bigqueryserviceaccountmcp
1 query
18:29
Message Claude...

Tools your analytics agent reaches for on BigQuery (Service Account), enforced by service account IAM.

CALL ANY TOOL
List datasets and tables, inspect schemas, run SQL queries, stream rows, and monitor jobs — all under service account IAM.
bqsa_datasets_list
List datasets
List all datasets in a GCP project the service account has access to.
Parameters
Name
Type
Required
Description
project_id
string
Required
GCP project ID
max_results
integer
Optional
Max datasets to return
bqsa_tables_list
List tables
bqsa_table_schema
Get table schema
bqsa_query_run
Run query
bqsa_job_get
Get job status
bqsa_table_insert_rows
Insert rows
Build your Agent
Drop the toolkit in, configure the service account once, and your analytics agent can query BigQuery from the first run.
import { ScalekitClient } from "@scalekit-sdk/node";
import { DynamicStructuredTool } from "@langchain/core/tools";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { z } from "zod";

const sk = new ScalekitClient(envUrl, clientId, clientSecret);

const { tools } = await sk.tools.listScopedTools("user_123", {
filter: { connectionNames: ["bigqueryserviceaccount"], toolNames: ["bqsa_datasets_list", "bqsa_table_schema", "bqsa_query_run"] },
pageSize: 100,
});

const lcTools = tools.map((t) => new DynamicStructuredTool({
name: t.tool.definition.name,
description: t.tool.definition.description,
schema: z.object({}).passthrough(),
func: async (args) => {
const { data } = await sk.tools.executeTool({
toolName: t.tool.definition.name,
identifier: "user_123",
params: args,
});
return JSON.stringify(data);
},
}));

const agent = createReactAgent({ llm, tools: lcTools });
import { ScalekitClient } from "@scalekit-sdk/node";
import OpenAI from "openai";

const sk = new ScalekitClient(envUrl, clientId, clientSecret);
const openai = new OpenAI();

const { tools } = await sk.tools.listScopedTools("user_123", {
filter: { connectionNames: ["bigqueryserviceaccount"], toolNames: ["bqsa_datasets_list", "bqsa_table_schema", "bqsa_query_run"] },
pageSize: 100,
});

const llmTools = tools.map((t) => ({
type: "function",
function: {
name: t.tool.definition.name,
description: t.tool.definition.description,
parameters: t.tool.definition.input_schema,
},
}));

const resp = await openai.responses.create({
model: "gpt-4o", input: prompt, tools: llmTools,
});
import { ScalekitClient } from "@scalekit-sdk/node";
import Anthropic from "@anthropic-ai/sdk";

const sk = new ScalekitClient(envUrl, clientId, clientSecret);
const anthropic = new Anthropic();

const { tools } = await sk.tools.listScopedTools("user_123", {
filter: { connectionNames: ["bigqueryserviceaccount"], toolNames: ["bqsa_datasets_list", "bqsa_table_schema", "bqsa_query_run"] },
pageSize: 100,
});

const llmTools = tools.map((t) => ({
name: t.tool.definition.name,
description: t.tool.definition.description,
input_schema: t.tool.definition.input_schema,
}));

const msg = await anthropic.messages.create({
model: "claude-sonnet-4-6", max_tokens: 1024,
tools: llmTools,
messages: [{ role: "user", content: prompt }],
});
import { Agent } from "@google/adk/agents";
import {
MCPToolset, StreamableHTTPConnectionParams,
} from "@google/adk/tools/mcp";

const toolset = new MCPToolset({
connectionParams: new StreamableHTTPConnectionParams({
url: "https://mcp.scalekit.com/bigqueryserviceaccount",
headers: { Authorization: `Bearer ${userScopedToken}` },
}),
});

const agent = new Agent({
name: "agent", model: "gemini-2.0-flash",
tools: await toolset.getTools(),
});
Try these prompts
Paste any prompt into your agent to start querying BigQuery via service account.
Schema & discovery
Copy the prompt
Copied
List all datasets in [project].
Copy the prompt
Copied
Show the schema of [dataset.table].
Copy the prompt
Copied
Which tables are in [dataset]?
Copy the prompt
Copied
How many rows are in [table_id]?
Query & analysis
Copy the prompt
Copied
Run: SELECT DATE(event_time), COUNT(*) FROM events.signups GROUP BY 1.
Copy the prompt
Copied
Monthly active users by tier for the last 6 months.
Copy the prompt
Copied
Top 10 accounts by revenue this quarter.
Copy the prompt
Copied
Daily churn rate for the last 30 days.
Monitoring & ops
Copy the prompt
Copied
Which queries scanned the most data today?
Copy the prompt
Copied
Job status for [job_id].
Copy the prompt
Copied
List tables updated in the last 24 hours.
Copy the prompt
Copied
Estimated cost for query: [SQL].
SEE HOW AUTH WORKS
Configure a GCP service account once. Scalekit vaults the key, resolves it at request time, and logs every call with the service account identity.
1
Authorize
Your user connects
BigQuery (Service Account)
once. We tie it to their identity and the meetings they approved — no shared bot account, no org-wide access
Who:
user ‘A’
when:
Once per user
access:
Limited to user
2
Store
Their
BigQuery (Service Account)
token lives in a vault scoped to them. User A's meetings are never reachable by an agent acting for user B, even on the same connection
vault:
encrypted
scope:
per-user
tokens:
auto-refreshed
3
Resolve
When your agent calls a
BigQuery (Service Account)
tool, we fetch the right token server-side. It never touches your agent, never appears in the LLM context, never shows up in your logs
speed:
~40ms
check:
before every call
seen by:
nobody
4
Audit
Every
BigQuery (Service Account)
tool call is logged — who triggered it, which meeting was fetched, what came back. 90 days of history, tied to the user who authorized it
history:
90 days
export:
SIEM-ready
logged:
every call
Test other agents
Same vault-and-enforce pattern across other analytics agents and MCP connectors. Working code, live demos, fork what fits.
GTM
HubSpot to Slack updates agent
Watch HubSpot deal stage changes and post structured updates to the right Slack channel. Reps stop checking the CRM all day.
ENGINEERING
Engineering standup agent
Aggregate GitHub and GitLab activity, link to Jira, and post a daily standup digest to Slack. No async updates.
Why Scalekit
Secure your agent's access. Connectors ship in minutes
Other connector libraries treat auth as a demo afterthought. Scalekit starts with identity, scope enforcement, and audit.
01.
Shared tokens break per-user analytics
A shared token looks fine in a demo. In production every call looks like a service account. Scalekit resolves the real user credential so attribution, audit, and scope stay accurate.
// shared token
 audit → bot_service_account
 user_filter → broken

 // scalekit
 audit → user_abc
 scope → enforced ✓
02.
Authentication is not authorization
03.
Multi-tenancy is architectural
04.
BigQuery (Service Account) today. Others tomorrow.
“Our agents act across Salesforce, Gong, Google Drive, and more, on behalf of every customer. Scalekit behind the scenes meant we can keep adding tools without ever rebuilding how credentials or tool calling work.”
Venu Madhav Kattagoni
Head of Engineering / Von
FAQs
Frequently Asked Questions

Does the agent use a service account or a user credential?
A service account. Unlike OAuth connectors, no user authorization flow is required. A GCP service account key is configured once by an admin and vaulted in Scalekit. Every call runs under that service account identity.

Where is the BigQuery (Service Account) service account stored?
In Scalekit's managed AES-256 token vault. The JSON key file is never stored on disk or passed to the LLM. Rotation is a single vault update. The key never appears in prompts, logs, or agent context.

Can I limit what the agent is allowed to query in BigQuery?
Yes. Pass a tool name filter to listScopedTools so the analytics agent only sees the subset you authorize. Additionally, scope the service account IAM role to the minimum datasets required — Scalekit enforces both layers.

What happens if the service account key is rotated?
Update the key in the Scalekit vault. No code changes required. Subsequent calls resolve the new key automatically. The old key can be disabled in GCP immediately after the vault update. The event is logged for audit.

Does the agent respect BigQuery IAM, dataset ACLs, and row-level security?
Yes. All queries run under the service account IAM role. Dataset-level access, table ACLs, column-level security, and row-level security policies all apply. The agent cannot read or write what the service account is not granted.

Start in your coding agent
Up and running in one command
Install the Scalekit skill in your editor of choice. Connector, auth, tools, prompt, all wired up
Claude Code REPL
/plugin marketplace add scalekit-inc/claude-code-authstack
/plugin install agentkit@scalekit-auth-stack
Cursor Code REPL
# ~/.cursor/mcp.json
{
""mcpServers"": {
""bigqueryserviceaccount"": {
""url"": ""https://mcp.scalekit.com/bigqueryserviceaccount"",
""headers"": { ""Authorization"": ""Bearer $SCALEKIT_TOKEN"" }
}
}
}
Codex Code REPL
# ~/.codex/config.toml
[mcp_servers.bigqueryserviceaccount]
url = ""https://mcp.scalekit.com/bigqueryserviceaccount""
auth_env = ""SCALEKIT_TOKEN""
Copilot Code REPL
# .vscode/mcp.json
{
""servers"": {
""bigqueryserviceaccount"": {
""url"": ""https://mcp.scalekit.com/bigqueryserviceaccount"",
""type"": ""http""
}
}
}