@digestseo/mcp-gsc 0.4.8 → 0.4.10
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/CHANGELOG.md +15 -0
- package/README.md +4 -2
- package/package.json +1 -1
- package/scripts/glama-start.mjs +5 -1
- package/src/index.ts +37 -4
- package/src/search-console-property-schema.ts +15 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.4.10] - 2026-09-21
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- The published `npx` launcher now resolves `wrangler.example.jsonc` from the installed package instead of the caller's working directory, so `npx -y @digestseo/mcp-gsc` works outside a cloned repository.
|
|
14
|
+
|
|
15
|
+
## [0.4.9] - 2026-09-21
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- `sites.list` now marks whether each returned property identifier matches the URL-prefix / `sc-domain:` forms currently documented by the Search Console API, and the docs explicitly avoid guessing an undocumented API identifier for Search Console platform properties.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- `analytics.query` now exposes `position_supported: false` plus an explicit note for Google Discover responses so agents do not interpret the generic row `position` field as a supported Discover metric.
|
|
22
|
+
|
|
8
23
|
## [0.4.8] - 2026-09-21
|
|
9
24
|
|
|
10
25
|
### Fixed
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ The self-hosting launcher is available as [`@digestseo/mcp-gsc`](https://www.npm
|
|
|
22
22
|
npx -y @digestseo/mcp-gsc
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
The npm launcher starts a loopback-only **Streamable HTTP** Worker on `127.0.0.1` (port `8080` by default); it is not a stdio MCP process. Clients that launch the package should connect to `http://127.0.0.1:8080/mcp` after supplying the Google OAuth and token-encryption environment variables described in [SETUP.md](SETUP.md). Before the first OAuth sign-in, authorize the exact callback `http://127.0.0.1:8080/google/callback` in Google Cloud (or use the same custom `PORT` you launch with); the launcher prints both URLs at startup.
|
|
25
|
+
The npm launcher starts a loopback-only **Streamable HTTP** Worker on `127.0.0.1` (port `8080` by default); it is not a stdio MCP process. You can run the `npx` command from any directory because the launcher resolves its bundled Worker configuration from the installed package. Clients that launch the package should connect to `http://127.0.0.1:8080/mcp` after supplying the Google OAuth and token-encryption environment variables described in [SETUP.md](SETUP.md). Before the first OAuth sign-in, authorize the exact callback `http://127.0.0.1:8080/google/callback` in Google Cloud (or use the same custom `PORT` you launch with); the launcher prints both URLs at startup.
|
|
26
26
|
|
|
27
27
|
The local launcher defaults to `GSC_ACCESS_MODE=readwrite`. For least-privilege analytics/reporting-only use, set `GSC_ACCESS_MODE=readonly` in the environment before running `npx`; the launcher forwards that value into the local Worker so OAuth requests only the read-only Search Console scope and the five mutation tools stay unregistered.
|
|
28
28
|
|
|
@@ -75,7 +75,7 @@ By default (`GSC_ACCESS_MODE=readwrite`), this server exposes 21 tools. Read-onl
|
|
|
75
75
|
| **`server.capabilities`** | Read | List every tool this server exposes and report whether your Google connection is currently authenticated (`connected` / `not_connected`). Takes no arguments — a good first call for discovery. |
|
|
76
76
|
| **`sites.list`** | Read | List the Search Console properties the connected Google account can access (`siteUrl`, `permissionLevel`). |
|
|
77
77
|
| **`sites.get`** | Read | Retrieve one exact Search Console property and the connected account's permission level for it. |
|
|
78
|
-
| **`analytics.query`** | Read | Impressions, clicks, CTR, and average position over a date range, with dimensions, filters, safe pagination, and selectable search type. Pass `dimensions: []` for true site totals; Google's aggregate row may omit `keys`. Search Analytics does not guarantee every data row and can return only top rows, so exhausting local pagination is not proof that the provider dataset is exhaustive. |
|
|
78
|
+
| **`analytics.query`** | Read | Impressions, clicks, CTR, and average position over a date range, with dimensions, filters, safe pagination, and selectable search type. Pass `dimensions: []` for true site totals; Google's aggregate row may omit `keys`. Search Analytics does not guarantee every data row and can return only top rows, so exhausting local pagination is not proof that the provider dataset is exhaustive. For `search_type: "discover"`, `position_supported` is `false` because Google Discover does not support average position. |
|
|
79
79
|
| **`insights.page_queries`** / **`insights.query_pages`** | Read | Drill from one exact page to its Search Console queries, or from one exact query to the pages Google surfaced for it, with `row_limit` / `start_row` pagination. |
|
|
80
80
|
| **`urls.inspect`** | Read | Google's URL Inspection report for the version of one page currently known in Google's index. It is not a live URL test; mobile-usability output is deprecated. |
|
|
81
81
|
| **`urls.inspect_many`** | Read | Inspect Google's indexed versions of up to 10 URLs sequentially in one call. Each URL still consumes one Google URL Inspection request and one unit of the server's shared inspection safety budget; this does not run live URL tests. |
|
|
@@ -87,6 +87,8 @@ By default (`GSC_ACCESS_MODE=readwrite`), this server exposes 21 tools. Read-onl
|
|
|
87
87
|
| **`sitemaps.submit`** / **`sitemaps.delete`** | Write | Submit or remove a sitemap. |
|
|
88
88
|
| **`indexing.request`** | Write | Requests indexing through Google's Indexing API. Google currently restricts this API to pages containing `JobPosting` structured data or livestream pages containing `BroadcastEvent` inside `VideoObject`. It is not available for general webpage submission. |
|
|
89
89
|
|
|
90
|
+
> **Platform properties.** Search Console now supports platform properties for social/video accounts such as Instagram, TikTok, X, and YouTube in its UI. Google's current Search Console API documentation still defines `siteUrl` using URL-prefix and `sc-domain:` website-property forms and does not publish a platform-property API identifier contract. `sites.list` therefore preserves every identifier Google returns and marks whether it matches the documented API grammar; the other tools deliberately reject undocumented forms instead of inventing an identifier. Use the Search Console UI/export for a platform property until Google documents API support.
|
|
91
|
+
|
|
90
92
|
Read-write mode requests the Google Search Console read-write and Indexing API scopes. Read-only mode requests only `https://www.googleapis.com/auth/webmasters.readonly` (plus `openid` and `email`) and does not register the five write tools. Read-write remains the default so existing deployments retain their current behavior; see [SETUP.md](SETUP.md) to select a mode before connecting users.
|
|
91
93
|
|
|
92
94
|
Large analytics responses are bounded at the MCP boundary instead of being generated and then discarded by clients with structured-content limits. List-style tools expose `start_row` / `next_start_row` where applicable; ranked analytical tools expose `limit`, `start_row`, and `result_page` metadata including `has_more`, `truncated`, and `byte_limit_reached`. Continue paging while `has_more` is true rather than assuming one response is complete.
|
package/package.json
CHANGED
package/scripts/glama-start.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
3
4
|
|
|
4
5
|
const port = process.env.PORT || '8080';
|
|
5
6
|
const localBaseUrl = `http://127.0.0.1:${port}`;
|
|
6
7
|
const mcpUrl = `${localBaseUrl}/mcp`;
|
|
7
8
|
const googleRedirectUri = `${localBaseUrl}/google/callback`;
|
|
9
|
+
const wranglerConfig = fileURLToPath(
|
|
10
|
+
new URL('../wrangler.example.jsonc', import.meta.url),
|
|
11
|
+
);
|
|
8
12
|
const forwardedVariables = [
|
|
9
13
|
'GOOGLE_CLIENT_ID',
|
|
10
14
|
'GOOGLE_CLIENT_SECRET',
|
|
@@ -15,7 +19,7 @@ const forwardedVariables = [
|
|
|
15
19
|
const args = [
|
|
16
20
|
'dev',
|
|
17
21
|
'--config',
|
|
18
|
-
|
|
22
|
+
wranglerConfig,
|
|
19
23
|
'--local',
|
|
20
24
|
'--ip',
|
|
21
25
|
'127.0.0.1',
|
package/src/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ import { resolveIndexedPagesDateRange } from './indexed-pages-range';
|
|
|
58
58
|
import { createQuickWinsInputSchema } from './quick-wins-schema';
|
|
59
59
|
import { SITEMAP_URL_SCHEMA } from './sitemap-url-schema';
|
|
60
60
|
import {
|
|
61
|
+
classifySearchConsolePropertyIdentifier,
|
|
61
62
|
SEARCH_CONSOLE_PROPERTY_DESCRIPTION,
|
|
62
63
|
SEARCH_CONSOLE_PROPERTY_SCHEMA,
|
|
63
64
|
} from './search-console-property-schema';
|
|
@@ -132,6 +133,16 @@ const SITE_OUTPUT_SCHEMA = {
|
|
|
132
133
|
permissionLevel: z.string(),
|
|
133
134
|
};
|
|
134
135
|
|
|
136
|
+
const LISTED_SITE_OUTPUT_SCHEMA = {
|
|
137
|
+
...SITE_OUTPUT_SCHEMA,
|
|
138
|
+
api_identifier_kind: z.enum(['domain', 'url_prefix', 'undocumented']),
|
|
139
|
+
mcp_site_url_accepted: z.boolean(),
|
|
140
|
+
unsupported_reason: z.string().optional(),
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const PLATFORM_PROPERTY_API_NOTE =
|
|
144
|
+
"Google Search Console now has platform properties for social/video accounts in its UI, but the current Search Console API documentation still defines siteUrl using URL-prefix and sc-domain website-property forms. mcp-gsc preserves any other identifier returned by sites.list but will not pass an undocumented property identifier to API tools until Google publishes that contract.";
|
|
145
|
+
|
|
135
146
|
const SITEMAP_OUTPUT_SCHEMA = z
|
|
136
147
|
.object({
|
|
137
148
|
path: z.string(),
|
|
@@ -168,7 +179,7 @@ const CAPABILITIES_OUTPUT_SCHEMA = {
|
|
|
168
179
|
};
|
|
169
180
|
|
|
170
181
|
const SITES_OUTPUT_SCHEMA = {
|
|
171
|
-
sites: z.array(z.object(
|
|
182
|
+
sites: z.array(z.object(LISTED_SITE_OUTPUT_SCHEMA)),
|
|
172
183
|
};
|
|
173
184
|
|
|
174
185
|
const SITE_DETAIL_OUTPUT_SCHEMA = {
|
|
@@ -205,6 +216,10 @@ const SEARCH_ANALYTICS_OUTPUT_SCHEMA = {
|
|
|
205
216
|
row_count: z.number().int().nonnegative(),
|
|
206
217
|
start_row: z.number().int().nonnegative(),
|
|
207
218
|
rows: z.array(z.object(SEARCH_ROW_OUTPUT_SCHEMA)),
|
|
219
|
+
position_supported: z.boolean().describe(
|
|
220
|
+
'Whether average position is supported for the selected Search Analytics search type. False for Google Discover.',
|
|
221
|
+
),
|
|
222
|
+
position_note: z.string().optional(),
|
|
208
223
|
provider_exhaustiveness_guaranteed: z.literal(false).describe(
|
|
209
224
|
'False because the Search Analytics API does not guarantee every data row; Google can return only top rows even after local pagination is exhausted.',
|
|
210
225
|
),
|
|
@@ -516,7 +531,7 @@ const TOOL_CATALOG = [
|
|
|
516
531
|
{
|
|
517
532
|
name: 'sites.list',
|
|
518
533
|
description:
|
|
519
|
-
'List the Google Search Console properties (sites) the connected Google account can access.',
|
|
534
|
+
'List the Google Search Console properties (sites) the connected Google account can access, including whether each returned identifier matches the website-property siteUrl forms currently documented by the Search Console API.',
|
|
520
535
|
},
|
|
521
536
|
{
|
|
522
537
|
name: 'sites.get',
|
|
@@ -710,7 +725,7 @@ class GscMcpRuntime {
|
|
|
710
725
|
{
|
|
711
726
|
title: 'List Search Console properties',
|
|
712
727
|
description:
|
|
713
|
-
"List the Google Search Console properties (sites) the connected Google account has access to.
|
|
728
|
+
"List the Google Search Console properties (sites) the connected Google account has access to. Each entry preserves Google's siteUrl and permissionLevel and adds api_identifier_kind plus mcp_site_url_accepted. Google now exposes platform properties for social/video accounts in the Search Console UI, but its current API documentation still defines siteUrl using URL-prefix and sc-domain website-property forms; identifiers outside those documented forms are preserved here but marked unsupported rather than guessed. Call this when the user asks 'what sites do I have?' or 'what properties are connected?', or before another tool that requires site_url.",
|
|
714
729
|
inputSchema: {},
|
|
715
730
|
outputSchema: SITES_OUTPUT_SCHEMA,
|
|
716
731
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
@@ -718,7 +733,18 @@ class GscMcpRuntime {
|
|
|
718
733
|
async () => {
|
|
719
734
|
const googleId = this.requireGoogleId();
|
|
720
735
|
const accessToken = await this.getAccessToken(googleId);
|
|
721
|
-
const sites = await listSites(accessToken)
|
|
736
|
+
const sites = (await listSites(accessToken)).map((site) => {
|
|
737
|
+
const apiIdentifierKind = classifySearchConsolePropertyIdentifier(site.siteUrl);
|
|
738
|
+
const mcpSiteUrlAccepted = apiIdentifierKind !== 'undocumented';
|
|
739
|
+
return {
|
|
740
|
+
...site,
|
|
741
|
+
api_identifier_kind: apiIdentifierKind,
|
|
742
|
+
mcp_site_url_accepted: mcpSiteUrlAccepted,
|
|
743
|
+
...(!mcpSiteUrlAccepted
|
|
744
|
+
? { unsupported_reason: PLATFORM_PROPERTY_API_NOTE }
|
|
745
|
+
: {}),
|
|
746
|
+
};
|
|
747
|
+
});
|
|
722
748
|
return toolResponse(JSON.stringify(sites, null, 2), { sites });
|
|
723
749
|
},
|
|
724
750
|
);
|
|
@@ -1079,6 +1105,13 @@ class GscMcpRuntime {
|
|
|
1079
1105
|
row_count: bounded.items.length,
|
|
1080
1106
|
start_row,
|
|
1081
1107
|
rows: bounded.items,
|
|
1108
|
+
position_supported: search_type !== 'discover',
|
|
1109
|
+
...(search_type === 'discover'
|
|
1110
|
+
? {
|
|
1111
|
+
position_note:
|
|
1112
|
+
"Google Discover does not support average position. Do not interpret rows[].position for search_type='discover'.",
|
|
1113
|
+
}
|
|
1114
|
+
: {}),
|
|
1082
1115
|
provider_exhaustiveness_guaranteed: false,
|
|
1083
1116
|
provider_note: SEARCH_ANALYTICS_PROVIDER_NOTE,
|
|
1084
1117
|
has_more: bounded.resultPage.has_more,
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
export const SEARCH_CONSOLE_PROPERTY_DESCRIPTION =
|
|
4
|
-
"The Search Console property identifier, exactly as returned by sites.list. Domain properties
|
|
4
|
+
"The Search Console property identifier, exactly as returned by sites.list. The current Search Console API documents Domain properties as 'sc-domain:example.com' and URL-prefix properties as an HTTP or HTTPS URL, typically including the trailing slash, e.g. 'https://www.example.com/'. Search Console platform properties for social/video accounts are available in the Search Console UI, but Google has not documented an API siteUrl identifier contract for them; mcp-gsc therefore does not guess one. Call sites.list first if unsure.";
|
|
5
|
+
|
|
6
|
+
export type SearchConsolePropertyIdentifierKind =
|
|
7
|
+
| 'domain'
|
|
8
|
+
| 'url_prefix'
|
|
9
|
+
| 'undocumented';
|
|
5
10
|
|
|
6
11
|
function isDomainPropertyIdentifier(value: string): boolean {
|
|
7
12
|
if (!value.startsWith('sc-domain:')) return false;
|
|
@@ -36,10 +41,18 @@ export function isSearchConsolePropertyIdentifier(value: string): boolean {
|
|
|
36
41
|
return isDomainPropertyIdentifier(value) || isUrlPrefixPropertyIdentifier(value);
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
export function classifySearchConsolePropertyIdentifier(
|
|
45
|
+
value: string,
|
|
46
|
+
): SearchConsolePropertyIdentifierKind {
|
|
47
|
+
if (isDomainPropertyIdentifier(value)) return 'domain';
|
|
48
|
+
if (isUrlPrefixPropertyIdentifier(value)) return 'url_prefix';
|
|
49
|
+
return 'undocumented';
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
export const SEARCH_CONSOLE_PROPERTY_SCHEMA = z
|
|
40
53
|
.string()
|
|
41
54
|
.refine(isSearchConsolePropertyIdentifier, {
|
|
42
55
|
message:
|
|
43
|
-
"Expected a Search Console property identifier such as 'sc-domain:example.com' or an HTTP/HTTPS URL-prefix property such as 'https://www.example.com/'.",
|
|
56
|
+
"Expected a Search Console API property identifier such as 'sc-domain:example.com' or an HTTP/HTTPS URL-prefix property such as 'https://www.example.com/'. Platform-property identifiers are not accepted until Google documents their API siteUrl contract.",
|
|
44
57
|
})
|
|
45
58
|
.describe(SEARCH_CONSOLE_PROPERTY_DESCRIPTION);
|