search-console-mcp 1.11.1 → 1.12.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 CHANGED
@@ -13,17 +13,23 @@ A Model Context Protocol (MCP) server that transforms how you interact with **Go
13
13
  ## Why use this?
14
14
 
15
15
  ### ❌ The Old Way
16
- 1. Open Search Console -> Performance Tab
17
- 2. Filter by "Last 28 days"
18
- 3. Export to CSV
19
- 4. Open in Excel/Sheets
20
- 5. Create a filter for "Position > 10" AND "Impressions > 1000"
21
- 6. Analyze manually to find opportunities
16
+ 1. Open Google Search Console Export CSV
17
+ 2. Open Bing Webmaster Tools → Export CSV
18
+ 3. Paste both into a spreadsheet
19
+ 4. Manually compare keywords across engines
20
+ 5. Switch between accounts for different clients
21
+ 6. Repeat weekly
22
22
 
23
23
  ### ✅ The New Way
24
24
  **Just ask:**
25
25
  > "Find low-hanging fruit keywords (positions 11-20) with high impressions that I should optimize."
26
26
 
27
+ > "Compare my Google and Bing performance — where am I leaving Bing traffic on the table?"
28
+
29
+ > "Run a health check across all my sites and give me 3 actions for next week."
30
+
31
+ **One server. Both engines. Multiple accounts. Zero spreadsheets.**
32
+
27
33
  ---
28
34
  ## 🎯 Magic Prompts
29
35
 
@@ -47,15 +53,21 @@ Copy and paste these into your MCP client (Claude Desktop, etc.) to see the inte
47
53
  #### ⚡ The Speed vs. Ranking Correlator
48
54
  > "Fetch the top 5 pages by impressions. For these pages, run a PageSpeed audit. Is there any correlation between low performance scores and recently declining positions?"
49
55
 
50
- #### 🔍 Multi-Engine Comparison
56
+ #### 🔀 Multi-Engine Comparison
51
57
  > "Compare my performance between Google and Bing for the last 30 days. Which keywords are ranking better on Bing but have lower traffic on Google?"
52
58
 
59
+ #### 🎯 Bing Opportunity Finder
60
+ > "Show me keywords where I'm in the top 5 on Google but not ranking on Bing. These are my easy Bing wins."
61
+
62
+ #### ⚠️ Google Dependency Check
63
+ > "Am I too dependent on Google? Check my click share across both engines and flag any keywords where over 85% of traffic comes from Google."
64
+
53
65
  ---
54
66
 
55
67
  ## 🔐 Authentication (Desktop Flow)
56
68
 
57
69
  Search Console MCP uses a **Secure Desktop Flow**. This provides high-security, professional grade authentication for your Google account:
58
- - **Multi-Account Support**: Automatically detects and stores separate tokens for different Google accounts based on your email.
70
+ - **Multi-Account Support**: Connect multiple Google and Bing accounts. The server automatically picks the right one for each site.
59
71
  - **System Keychain Primary**: Tokens are stored in your OS's native credential manager (macOS Keychain, Windows Credential Manager, or Linux Secret Service).
60
72
  - **AES-256-GCM Hardware-Bound Encryption**: Fallback storage is encrypted with AES-256-GCM using a key derived from your unique hardware machine ID. Tokens stolen from your machine cannot be decrypted on another computer.
61
73
  - **Silent Background Refresh**: Tokens auto-refresh silently when they expire.
@@ -112,16 +124,36 @@ To access Bing data, you simply need an API Key.
112
124
 
113
125
  ---
114
126
 
127
+ ## 👥 Multi-Account Management
128
+
129
+ Manage multiple Google and Bing accounts from the CLI:
130
+
131
+ ```bash
132
+ # List all connected accounts
133
+ npx search-console-mcp accounts list
134
+
135
+ # Remove an account
136
+ npx search-console-mcp accounts remove --account=marketing@company.com
137
+
138
+ # Add a site boundary to an account
139
+ npx search-console-mcp accounts add-site --account=marketing@company.com --site=example.com
140
+ ```
141
+
142
+ When your AI agent queries a site, the server automatically resolves which account to use. [Learn more →](https://searchconsolemcp.mintlify.app/getting-started/multi-account)
143
+
144
+ ---
145
+
115
146
 
116
147
  ## 🛡️ Fort Knox Security
117
148
 
118
149
  This MCP server implements a multi-layered security architecture:
119
150
 
120
151
  * **Keychain Integration**: Primarily uses the **macOS Keychain**, **Windows Credential Manager**, or **libsecret (Linux)** to store tokens.
121
- * **Hardware-Bound Vault**: Fallback tokens are stored in `~/.search-console-mcp-tokens.enc` and encrypted with **AES-256-GCM**.
152
+ * **Encrypted Config**: Account configuration is stored in `~/.search-console-mcp-config.enc` using **AES-256-GCM** encryption.
122
153
  * **Machine Fingerprinting**: The encryption key is derived from your unique hardware UUID and OS user. The encrypted file is useless if moved to another machine.
123
154
  * **Minimalist Storage**: Only the `refresh_token` and `expiry_date` are stored.
124
- * **Strict Unix Permissions**: The fallback file is created with `mode 600` (read/write only by your user).
155
+ * **Legacy Support**: Automatically detects credentials from older versions (tokens files, environment variables).
156
+ * **Strict Unix Permissions**: Config files are created with `mode 600` (read/write only by your user).
125
157
 
126
158
  ---
127
159
 
@@ -0,0 +1,209 @@
1
+ import { loadConfig, removeAccount, updateAccount } from './common/auth/config.js';
2
+ function parseFlags(args) {
3
+ const flags = {};
4
+ for (const arg of args) {
5
+ const match = arg.match(/^--([^=]+)=(.+)$/);
6
+ if (match) {
7
+ flags[match[1]] = match[2];
8
+ }
9
+ }
10
+ return flags;
11
+ }
12
+ function findAccountByAliasOrId(accounts, identifier) {
13
+ // Try exact ID match first
14
+ if (accounts[identifier])
15
+ return accounts[identifier];
16
+ // Then try alias match (case-insensitive)
17
+ return Object.values(accounts).find((a) => a.alias?.toLowerCase() === identifier.toLowerCase() || a.id === identifier);
18
+ }
19
+ export async function main(args) {
20
+ const subcommand = args[0] || 'list';
21
+ const flags = parseFlags(args.slice(1));
22
+ // Positional args (non-flag args after the subcommand)
23
+ const positional = args.slice(1).filter(a => !a.startsWith('--'));
24
+ if (subcommand === 'list') {
25
+ try {
26
+ const config = await loadConfig();
27
+ const accounts = Object.values(config.accounts);
28
+ if (accounts.length === 0) {
29
+ console.log(JSON.stringify({
30
+ accounts: [],
31
+ message: "No accounts connected.",
32
+ resolution: {
33
+ command: "search-console-mcp setup",
34
+ google: "search-console-mcp setup --engine=google",
35
+ bing: "search-console-mcp setup --engine=bing"
36
+ },
37
+ setup_instructions: {
38
+ google: [
39
+ "Run: search-console-mcp setup --engine=google",
40
+ "Choose OAuth 2.0 login or Service Account",
41
+ "Authorize in browser and select sites to restrict (or allow all)"
42
+ ],
43
+ bing: [
44
+ "Run: search-console-mcp setup --engine=bing",
45
+ "Get your API key from https://www.bing.com/webmasters/settings/api",
46
+ "Enter the API key when prompted"
47
+ ]
48
+ }
49
+ }, null, 2));
50
+ return;
51
+ }
52
+ const siteRows = [];
53
+ for (const a of accounts) {
54
+ const alias = a.alias || '[Unnamed]';
55
+ const engine = a.engine === 'google' ? 'Google' : 'Bing';
56
+ if (!a.websites || a.websites.length === 0) {
57
+ siteRows.push({
58
+ site: '[All Sites Authorized]',
59
+ account: alias,
60
+ engine: engine,
61
+ id: a.id
62
+ });
63
+ }
64
+ else {
65
+ for (const site of a.websites) {
66
+ siteRows.push({
67
+ site: site,
68
+ account: alias,
69
+ engine: engine,
70
+ id: a.id
71
+ });
72
+ }
73
+ }
74
+ }
75
+ console.log(JSON.stringify({ accounts: siteRows }, null, 2));
76
+ }
77
+ catch (error) {
78
+ console.log(JSON.stringify({
79
+ error: "Failed to list accounts",
80
+ message: error.message
81
+ }, null, 2));
82
+ }
83
+ return;
84
+ }
85
+ if (subcommand === 'remove') {
86
+ // Flags (agent): --account=alias --site=url | Positional (human): remove <alias_or_site>
87
+ const identifier = flags['account'] || flags['id'] || positional[0];
88
+ const siteToRemove = flags['site'];
89
+ if (!identifier && !siteToRemove) {
90
+ console.log(JSON.stringify({
91
+ error: "Missing arguments",
92
+ message: "Provide --account=<alias|id> to remove an account, or --site=<url> to remove a specific site boundary.",
93
+ resolution: {
94
+ by_account: "search-console-mcp accounts remove --account=<alias_or_id>",
95
+ by_site: "search-console-mcp accounts remove --site=<siteUrl>"
96
+ }
97
+ }, null, 2));
98
+ return;
99
+ }
100
+ try {
101
+ const config = await loadConfig();
102
+ if (identifier) {
103
+ // Remove entire account by alias or ID
104
+ const account = findAccountByAliasOrId(config.accounts, identifier);
105
+ if (!account) {
106
+ console.log(JSON.stringify({
107
+ error: "Account not found",
108
+ message: `No account matching '${identifier}' was found.`,
109
+ resolution: "Run: search-console-mcp accounts list"
110
+ }, null, 2));
111
+ return;
112
+ }
113
+ await removeAccount(account.id);
114
+ console.log(JSON.stringify({
115
+ success: true,
116
+ message: `Account '${account.alias}' (${account.id}) removed successfully.`
117
+ }, null, 2));
118
+ }
119
+ else if (siteToRemove) {
120
+ // Remove a specific site boundary from whichever account owns it
121
+ let found = false;
122
+ for (const account of Object.values(config.accounts)) {
123
+ if (account.websites?.includes(siteToRemove)) {
124
+ account.websites = account.websites.filter((w) => w !== siteToRemove);
125
+ await updateAccount(account);
126
+ found = true;
127
+ console.log(JSON.stringify({
128
+ success: true,
129
+ message: `Site '${siteToRemove}' removed from account '${account.alias}'.`
130
+ }, null, 2));
131
+ break;
132
+ }
133
+ }
134
+ if (!found) {
135
+ console.log(JSON.stringify({
136
+ error: "Site not found",
137
+ message: `No account has '${siteToRemove}' in its site boundaries.`,
138
+ resolution: "Run: search-console-mcp accounts list"
139
+ }, null, 2));
140
+ }
141
+ }
142
+ }
143
+ catch (error) {
144
+ console.log(JSON.stringify({
145
+ error: "Failed to remove",
146
+ message: error.message
147
+ }, null, 2));
148
+ }
149
+ return;
150
+ }
151
+ if (subcommand === 'add-site') {
152
+ // Flags (agent): --account=alias --site=url | Positional (human): add-site <alias> <site>
153
+ const identifier = flags['account'] || flags['id'] || positional[0];
154
+ const site = flags['site'] || positional[1];
155
+ if (!identifier || !site) {
156
+ console.log(JSON.stringify({
157
+ error: "Missing arguments",
158
+ message: "Both --account and --site are required.",
159
+ resolution: "search-console-mcp accounts add-site --account=<alias_or_id> --site=<siteUrl>"
160
+ }, null, 2));
161
+ return;
162
+ }
163
+ try {
164
+ const config = await loadConfig();
165
+ const account = findAccountByAliasOrId(config.accounts, identifier);
166
+ if (!account) {
167
+ console.log(JSON.stringify({
168
+ error: "Account not found",
169
+ message: `No account matching '${identifier}' was found.`,
170
+ resolution: "Run: search-console-mcp accounts list"
171
+ }, null, 2));
172
+ return;
173
+ }
174
+ if (!account.websites)
175
+ account.websites = [];
176
+ if (!account.websites.includes(site)) {
177
+ account.websites.push(site);
178
+ await updateAccount(account);
179
+ console.log(JSON.stringify({
180
+ success: true,
181
+ message: `Site '${site}' added to account '${account.alias}'.`
182
+ }, null, 2));
183
+ }
184
+ else {
185
+ console.log(JSON.stringify({
186
+ success: true,
187
+ message: `Account '${account.alias}' already has '${site}' in its boundaries.`
188
+ }, null, 2));
189
+ }
190
+ }
191
+ catch (error) {
192
+ console.log(JSON.stringify({
193
+ error: "Failed to add site",
194
+ message: error.message
195
+ }, null, 2));
196
+ }
197
+ return;
198
+ }
199
+ console.log(JSON.stringify({
200
+ error: "Unknown command",
201
+ message: `'${subcommand}' is not a valid subcommand.`,
202
+ resolution: {
203
+ list: "search-console-mcp accounts list",
204
+ remove_account: "search-console-mcp accounts remove --account=<alias_or_id>",
205
+ remove_site: "search-console-mcp accounts remove --site=<siteUrl>",
206
+ add_site: "search-console-mcp accounts add-site --account=<alias_or_id> --site=<siteUrl>"
207
+ }
208
+ }, null, 2));
209
+ }
@@ -1,3 +1,5 @@
1
+ import { resolveAccount } from '../common/auth/resolver.js';
2
+ import { loadConfig } from '../common/auth/config.js';
1
3
  export class BingClient {
2
4
  apiKey;
3
5
  baseUrl = 'https://ssl.bing.com/webmaster/api.svc/json';
@@ -93,14 +95,41 @@ export class BingClient {
93
95
  return this.request('GetRelatedKeywords', { q, country, language });
94
96
  }
95
97
  }
96
- let cachedBingClient = null;
97
- export async function getBingClient() {
98
- if (cachedBingClient)
99
- return cachedBingClient;
100
- const apiKey = process.env.BING_API_KEY;
98
+ let cachedBingClients = {};
99
+ export async function getBingClient(siteUrl, accountId) {
100
+ // 1. Resolve Account
101
+ let apiKey;
102
+ let cacheKey;
103
+ if (accountId) {
104
+ const config = await loadConfig();
105
+ const account = config.accounts[accountId];
106
+ if (!account || account.engine !== 'bing') {
107
+ throw new Error(`Bing account ${accountId} not found.`);
108
+ }
109
+ apiKey = account.apiKey;
110
+ cacheKey = account.id;
111
+ }
112
+ else {
113
+ try {
114
+ const account = await resolveAccount(siteUrl || '', 'bing');
115
+ apiKey = account.apiKey;
116
+ cacheKey = account.id;
117
+ }
118
+ catch (error) {
119
+ // Fallback to environment variable for legacy support if resolution fails or no site specified
120
+ apiKey = process.env.BING_API_KEY;
121
+ cacheKey = 'env_fallback';
122
+ if (!apiKey) {
123
+ throw error; // Re-throw the resolution error if no ENV fallback either
124
+ }
125
+ }
126
+ }
127
+ if (cachedBingClients[cacheKey])
128
+ return cachedBingClients[cacheKey];
101
129
  if (!apiKey) {
102
- throw new Error('Bing API Key not found. Please set BING_API_KEY environment variable or run setup.');
130
+ throw new Error('Bing API Key not found. Please run setup to add an account.');
103
131
  }
104
- cachedBingClient = new BingClient(apiKey);
105
- return cachedBingClient;
132
+ const client = new BingClient(apiKey);
133
+ cachedBingClients[cacheKey] = client;
134
+ return client;
106
135
  }
@@ -1,37 +1,69 @@
1
1
  import { getBingClient } from '../client.js';
2
2
  /**
3
- * Get query performance stats for a Bing site.
3
+ * Get query performance stats for a Bing site with optional date filtering.
4
4
  */
5
- export async function getQueryStats(siteUrl) {
6
- const client = await getBingClient();
7
- return client.getQueryStats(siteUrl);
5
+ export async function getQueryStats(siteUrl, startDate, endDate) {
6
+ const client = await getBingClient(siteUrl);
7
+ const stats = await client.getQueryStats(siteUrl);
8
+ if (!startDate && !endDate)
9
+ return stats;
10
+ const start = startDate ? new Date(startDate) : new Date(0);
11
+ const end = endDate ? new Date(endDate) : new Date();
12
+ return stats.filter(row => {
13
+ const d = new Date(row.Date);
14
+ return d >= start && d <= end;
15
+ });
8
16
  }
9
17
  /**
10
- * Get page performance stats (top pages) for a Bing site.
18
+ * Get page performance stats (top pages) for a Bing site with optional date filtering.
11
19
  */
12
- export async function getPageStats(siteUrl) {
13
- const client = await getBingClient();
14
- return client.getPageStats(siteUrl);
20
+ export async function getPageStats(siteUrl, startDate, endDate) {
21
+ const client = await getBingClient(siteUrl);
22
+ const stats = await client.getPageStats(siteUrl);
23
+ if (!startDate && !endDate)
24
+ return stats;
25
+ const start = startDate ? new Date(startDate) : new Date(0);
26
+ const end = endDate ? new Date(endDate) : new Date();
27
+ return stats.filter(row => {
28
+ const d = new Date(row.Date);
29
+ return d >= start && d <= end;
30
+ });
15
31
  }
16
32
  /**
17
- * Get query stats for a specific page on a Bing site.
33
+ * Get query stats for a specific page on a Bing site with optional date filtering.
18
34
  */
19
- export async function getPageQueryStats(siteUrl, pageUrl) {
20
- const client = await getBingClient();
21
- return client.getPageQueryStats(siteUrl, pageUrl);
35
+ export async function getPageQueryStats(siteUrl, pageUrl, startDate, endDate) {
36
+ const client = await getBingClient(siteUrl);
37
+ const stats = await client.getPageQueryStats(siteUrl, pageUrl);
38
+ if (!startDate && !endDate)
39
+ return stats;
40
+ const start = startDate ? new Date(startDate) : new Date(0);
41
+ const end = endDate ? new Date(endDate) : new Date();
42
+ return stats.filter(row => {
43
+ const d = new Date(row.Date);
44
+ return d >= start && d <= end;
45
+ });
22
46
  }
23
47
  /**
24
- * Get combined query and page performance stats.
48
+ * Get combined query and page performance stats with optional date filtering.
25
49
  */
26
- export async function getQueryPageStats(siteUrl) {
27
- const client = await getBingClient();
28
- return client.getQueryPageStats(siteUrl);
50
+ export async function getQueryPageStats(siteUrl, startDate, endDate) {
51
+ const client = await getBingClient(siteUrl);
52
+ const stats = await client.getQueryPageStats(siteUrl);
53
+ if (!startDate && !endDate)
54
+ return stats;
55
+ const start = startDate ? new Date(startDate) : new Date(0);
56
+ const end = endDate ? new Date(endDate) : new Date();
57
+ return stats.filter(row => {
58
+ const d = new Date(row.Date);
59
+ return d >= start && d <= end;
60
+ });
29
61
  }
30
62
  /**
31
63
  * Get historical rank and traffic statistics for a site.
32
64
  */
33
65
  export async function getRankAndTrafficStats(siteUrl) {
34
- const client = await getBingClient();
66
+ const client = await getBingClient(siteUrl);
35
67
  return client.getRankAndTrafficStats(siteUrl);
36
68
  }
37
69
  /**
@@ -10,6 +10,6 @@ export async function getCrawlIssues(siteUrl) {
10
10
  * Get crawl statistics for a site.
11
11
  */
12
12
  export async function getCrawlStats(siteUrl) {
13
- const client = await getBingClient();
13
+ const client = await getBingClient(siteUrl);
14
14
  return client.getCrawlStats(siteUrl);
15
15
  }
@@ -3,6 +3,6 @@ import { getBingClient } from '../client.js';
3
3
  * Get detailed indexing and crawl information for a URL.
4
4
  */
5
5
  export async function getUrlInfo(siteUrl, url) {
6
- const client = await getBingClient();
6
+ const client = await getBingClient(siteUrl);
7
7
  return client.getUrlInfo(siteUrl, url);
8
8
  }
@@ -3,6 +3,6 @@ import { getBingClient } from '../client.js';
3
3
  * Get link counts for a site.
4
4
  */
5
5
  export async function getLinkCounts(siteUrl) {
6
- const client = await getBingClient();
6
+ const client = await getBingClient(siteUrl);
7
7
  return client.getLinkCounts(siteUrl);
8
8
  }
@@ -6,7 +6,7 @@ import { getBingClient } from '../client.js';
6
6
  * @returns A list of sitemaps.
7
7
  */
8
8
  export async function listSitemaps(siteUrl) {
9
- const client = await getBingClient();
9
+ const client = await getBingClient(siteUrl);
10
10
  return client.getFeeds(siteUrl);
11
11
  }
12
12
  /**
@@ -16,7 +16,7 @@ export async function listSitemaps(siteUrl) {
16
16
  * @param sitemapUrl - The URL of the sitemap file.
17
17
  */
18
18
  export async function submitSitemap(siteUrl, sitemapUrl) {
19
- const client = await getBingClient();
19
+ const client = await getBingClient(siteUrl);
20
20
  await client.submitSitemap(siteUrl, sitemapUrl);
21
21
  return `Successfully submitted sitemap: ${sitemapUrl} for site ${siteUrl}`;
22
22
  }
@@ -27,7 +27,7 @@ export async function submitSitemap(siteUrl, sitemapUrl) {
27
27
  * @param sitemapUrl - The URL of the sitemap to remove.
28
28
  */
29
29
  export async function deleteSitemap(siteUrl, sitemapUrl) {
30
- const client = await getBingClient();
30
+ const client = await getBingClient(siteUrl);
31
31
  await client.deleteSitemap(siteUrl, sitemapUrl);
32
32
  return `Successfully removed sitemap: ${sitemapUrl} for site ${siteUrl}`;
33
33
  }
@@ -103,7 +103,7 @@ export async function healthCheck(siteUrl) {
103
103
  if (allSites.length === 0) {
104
104
  return [];
105
105
  }
106
- const reports = await Promise.all(allSites.map(site => checkSite(site.Url)));
106
+ const reports = await Promise.all(allSites.map((site) => checkSite(site.Url)));
107
107
  const order = { critical: 0, warning: 1, healthy: 2 };
108
- return reports.sort((a, b) => order[a.status] - order[b.status]);
108
+ return reports.sort((a, b) => (order[a.status] || 0) - (order[b.status] || 0));
109
109
  }
@@ -1,11 +1,12 @@
1
1
  import { getBingClient } from '../client.js';
2
2
  /**
3
- * List all sites verified in the user's Bing Webmaster Tools.
3
+ * List all sites verified in the specified Bing account.
4
4
  *
5
+ * @param accountId - Optional. The account to list sites for.
5
6
  * @returns A list of verified site properties.
6
7
  */
7
- export async function listSites() {
8
- const client = await getBingClient();
8
+ export async function listSites(accountId) {
9
+ const client = await getBingClient(undefined, accountId);
9
10
  return client.getSiteList();
10
11
  }
11
12
  /**
@@ -15,7 +16,7 @@ export async function listSites() {
15
16
  * @returns A success message.
16
17
  */
17
18
  export async function addSite(siteUrl) {
18
- const client = await getBingClient();
19
+ const client = await getBingClient(siteUrl);
19
20
  await client.addSite(siteUrl);
20
21
  return `Successfully added site: ${siteUrl}`;
21
22
  }
@@ -26,7 +27,7 @@ export async function addSite(siteUrl) {
26
27
  * @returns A success message.
27
28
  */
28
29
  export async function removeSite(siteUrl) {
29
- const client = await getBingClient();
30
+ const client = await getBingClient(siteUrl);
30
31
  await client.removeSite(siteUrl);
31
32
  return `Successfully removed site: ${siteUrl}`;
32
33
  }
@@ -3,14 +3,14 @@ import { getBingClient } from '../client.js';
3
3
  * Get remaining URL submission quota.
4
4
  */
5
5
  export async function getUrlSubmissionQuota(siteUrl) {
6
- const client = await getBingClient();
6
+ const client = await getBingClient(siteUrl);
7
7
  return client.getUrlSubmissionQuota(siteUrl);
8
8
  }
9
9
  /**
10
10
  * Submit a single URL for indexing.
11
11
  */
12
12
  export async function submitUrl(siteUrl, url) {
13
- const client = await getBingClient();
13
+ const client = await getBingClient(siteUrl);
14
14
  await client.submitUrl(siteUrl, url);
15
15
  return `Successfully submitted URL: ${url}`;
16
16
  }
@@ -18,7 +18,7 @@ export async function submitUrl(siteUrl, url) {
18
18
  * Submit a batch of URLs for indexing.
19
19
  */
20
20
  export async function submitUrlBatch(siteUrl, urlList) {
21
- const client = await getBingClient();
21
+ const client = await getBingClient(siteUrl);
22
22
  await client.submitUrlBatch(siteUrl, urlList);
23
23
  return `Successfully submitted ${urlList.length} URLs in batch.`;
24
24
  }