@imazhar101/mcp-bigquery-server 1.0.1 → 1.0.3

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.
@@ -54,7 +54,7 @@ class BigQueryServer {
54
54
  isError: true,
55
55
  };
56
56
  }
57
- const { rows, rowCount, schema, bytesProcessed, cacheHit, strippedFields } = result.data;
57
+ const { rows, rowCount, schema, bytesProcessed, cacheHit, strippedFields, } = result.data;
58
58
  const headers = schema?.map((f) => f.name) ?? Object.keys(rows[0] ?? {});
59
59
  const body = args.format === 'json'
60
60
  ? formatAsJson(rows)
@@ -26,7 +26,6 @@ export class BigQueryService {
26
26
  query: sql,
27
27
  dryRun: true,
28
28
  location: this.config.location,
29
- defaultDataset: { projectId: billingProject },
30
29
  });
31
30
  const statementType = dryRunJob.metadata?.statistics?.query?.statementType ?? '';
32
31
  if (statementType !== 'SELECT') {
@@ -41,7 +40,6 @@ export class BigQueryService {
41
40
  const [job] = await this.client.createQueryJob({
42
41
  query: sql,
43
42
  location: this.config.location,
44
- defaultDataset: { projectId: billingProject },
45
43
  maximumBytesBilled: String(10 * 1024 * 1024 * 1024), // 10 GB safety cap
46
44
  });
47
45
  const [rows] = await job.getQueryResults({ maxResults: clampedRows });
@@ -94,11 +92,23 @@ export class BigQueryService {
94
92
  if (prevented.length === 0) {
95
93
  return { filteredRows: rows, filteredSchema: schema, strippedFields: [] };
96
94
  }
97
- // Case-insensitive match against full field name or glob-style suffix (*_email, email_*)
98
- const isBlocked = (name) => prevented.some((p) => name.toLowerCase() === p.toLowerCase());
95
+ // Case-insensitive substring match: a prevented token strips any column
96
+ // whose name contains it (e.g. `grm_email` strips `grm_email_one`,
97
+ // `grm_email_primary`). Substring is the only semantics that reliably
98
+ // catches real column sprawl; the BIGQUERY_PREVENTED_FIELDS list is curated
99
+ // to avoid over-broad tokens (e.g. `first_name`/`last_name`, never bare
100
+ // `name`). NOTE: output-column filtering is an accident guard, not an
101
+ // access boundary — it is bypassed by aliasing and does not cover PII used
102
+ // only in WHERE/JOIN/aggregates. Use allowedTables + governed views for that.
103
+ const isBlocked = (name) => {
104
+ const n = name.toLowerCase();
105
+ return prevented.some((p) => n.includes(p.toLowerCase()));
106
+ };
99
107
  const allFieldNames = schema.length > 0
100
108
  ? schema.map((f) => f.name)
101
- : rows.length > 0 ? Object.keys(rows[0]) : [];
109
+ : rows.length > 0
110
+ ? Object.keys(rows[0])
111
+ : [];
102
112
  const strippedFields = allFieldNames.filter(isBlocked);
103
113
  if (strippedFields.length === 0) {
104
114
  return { filteredRows: rows, filteredSchema: schema, strippedFields: [] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imazhar101/mcp-bigquery-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "BigQuery MCP server — read-only query execution, schema exploration, dataset discovery",
5
5
  "type": "module",
6
6
  "main": "dist/servers/bigquery/src/index.js",