@digestseo/mcp-gsc 0.4.37 → 0.4.38
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 +9 -0
- package/README.md +4 -2
- package/SETUP.md +1 -1
- package/package.json +1 -1
- package/scripts/glama-start.mjs +8 -1
- package/src/index.ts +12 -9
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.38] - 2026-10-25
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Align Indexing API quotas, structured tool metadata and setup guidance with Google's October 16, 2026 documentation update: 200 default publish requests/day/project, removal of the outdated quota-approval form, and unchanged content eligibility, ownership, spam and quota restrictions.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Docker/Glama images now bind Wrangler to container interfaces when explicitly configured by the Docker image, restoring reachability through Docker-published ports while leaving direct npm launches loopback-only by default. Invalid custom bind addresses fail closed.
|
|
17
|
+
- Docker images now install system CA certificates so the Cloudflare workerd runtime can initialize TLS; CI builds and runs a real container, checks host-forwarded health, and confirms unauthenticated MCP still returns 401.
|
|
18
|
+
|
|
10
19
|
## [0.4.37] - 2026-10-24
|
|
11
20
|
|
|
12
21
|
### Security
|
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ npx -y @digestseo/mcp-gsc
|
|
|
24
24
|
|
|
25
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). The launcher exits before starting Wrangler if `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, or `TOKEN_ENCRYPTION_KEY` is missing or blank, and it also rejects a nonblank `TOKEN_ENCRYPTION_KEY` unless it is valid base64 that decodes to exactly 32 bytes. Errors identify only the invalid or missing setting, never its secret value, so a broken OAuth session is not started. It keeps secret values in the child process environment and Wrangler loads them through declared local secrets; their values are not copied into Wrangler `--var` command-line arguments. 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
|
+
**Docker/Glama network binding.** The ordinary `npx` launcher stays loopback-only by default (`MCP_GSC_BIND_IP=127.0.0.1`). The Docker image explicitly sets `MCP_GSC_BIND_IP=0.0.0.0` **inside the container**, because Docker port forwarding cannot reach a service bound only to the container's loopback interface. When publishing a Docker port on your computer, prefer `-p 127.0.0.1:8080:8080` to restrict host access; expose it beyond the host only with appropriate HTTPS/OAuth deployment configuration and an authorized Google redirect URI. `MCP_GSC_BIND_IP` accepts only `127.0.0.1` or `0.0.0.0`; do not use all-interface binding for ordinary local npm usage.
|
|
28
|
+
|
|
27
29
|
Local KV and Durable Object state, including the encrypted Google session, is stored in `~/.mcp-gsc/state` by default instead of inside the versioned npm cache. That keeps the local connection available across package upgrades and ordinary npm cache cleanup. Set `MCP_GSC_STATE_DIR` to use another directory; keep the same `TOKEN_ENCRYPTION_KEY` when reusing existing state because stored refresh tokens are encrypted with that key. The launcher prints the effective state directory at startup. The first release with this stable path may require one Google reconnect because older launcher releases stored local state under the npm package cache.
|
|
28
30
|
|
|
29
31
|
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 six mutation tools stay unregistered.
|
|
@@ -119,7 +121,7 @@ By default (`GSC_ACCESS_MODE=readwrite`), this server exposes 23 tools. Read-onl
|
|
|
119
121
|
| **`sites.add`** / **`sites.delete`** | Write | Add or remove a property from the connected account's Search Console site set. `sites.add` returns `ownership_verification_performed: false` because ownership verification is a separate Google Site Verification/Search Console workflow. `sites.delete` removes the property from that account's Search Console site set; it does not delete the website itself. |
|
|
120
122
|
| **`sitemaps.submit`** / **`sitemaps.delete`** | Write | Submit or remove a sitemap. |
|
|
121
123
|
| **`indexing.status`** | Read | Read Google's latest successful Indexing API `URL_UPDATED` / `URL_DELETED` notification receipts for a previously submitted URL. This is notification metadata only, not index coverage, crawl status, indexing completion, or removal completion. It requires `GSC_ACCESS_MODE=readwrite` because Google's metadata endpoint uses the Indexing API OAuth scope. |
|
|
122
|
-
| **`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. Google's default 200 publish requests/day/project
|
|
124
|
+
| **`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. Google's documented default is 200 publish requests/day/project shared by updates and removals. Eligible content types, API authorization, quota limits and spam policies still apply; this quota is not permission to submit other page types or exceed Google's limits. |
|
|
123
125
|
| **`indexing.remove`** | Write | Request a `URL_DELETED` notification for a previously eligible Indexing API page. Before removal, the URL must already return HTTP 404/410 or expose a robots `noindex` meta directive. Google's receipt does not prove removal completed; use URL Inspection for indexed-state evidence. |
|
|
124
126
|
|
|
125
127
|
Search Analytics calendar dates (`start_date`, `end_date`, and comparison-period dates) use Google's Pacific Time calendar (`America/Los_Angeles`, UTC-8/UTC-7 depending on daylight saving time), not the caller's local timezone or UTC. This also governs the server's generated/default Search Analytics date ranges.
|
|
@@ -130,7 +132,7 @@ Read-write mode requests the Google Search Console read-write and Indexing API s
|
|
|
130
132
|
|
|
131
133
|
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.
|
|
132
134
|
|
|
133
|
-
> **Indexing API eligibility and
|
|
135
|
+
> **Indexing API eligibility and quotas.** Google's Indexing API is not a general-purpose page submission tool — as of this writing, Google's own documentation limits it to two content types: pages with `JobPosting` structured data, and livestream pages with `BroadcastEvent` structured data nested inside `VideoObject`. Before `indexing.request`, the server fetches the target URL and checks static JSON-LD, Microdata, and RDFa markup for one of those two types; if neither is present (or the page can't be fetched), it returns an error explaining why the URL is ineligible instead of calling the Indexing API. Structured data injected only after client-side JavaScript runs cannot be confirmed by this bounded preflight and therefore fails closed. Before `indexing.remove`, the server instead confirms Google's removal precondition: the URL must already return HTTP 404/410 or expose a robots `noindex` meta directive. Update and removal notifications share the same publish quota. Google's current documentation lists a default 200 publish requests/day/project. Its October 16, 2026 update removed an outdated quota request form, but this does not authorize exceeding published quotas or submitting unsupported content. Check your project's API access and current quota in Google Cloud Console. Google also subjects every submission to spam detection and warns that abuse or attempts to exceed quotas through multiple accounts or other means can revoke access. A successful notification is only an acknowledgment that Google received it — it does not guarantee indexing or removal completed.
|
|
134
136
|
|
|
135
137
|
## What you can ask
|
|
136
138
|
|
package/SETUP.md
CHANGED
|
@@ -61,7 +61,7 @@ npx wrangler login
|
|
|
61
61
|
|
|
62
62
|
`readwrite` preserves the full tool surface: it grants Search Console read-write access and the Indexing API scope required to manage sites, sitemaps, and request URL crawling. `readonly` requests only the Search Console read-only scope and omits the mutation tools.
|
|
63
63
|
|
|
64
|
-
Note that the Indexing API itself is narrow: Google currently restricts it to pages containing `JobPosting` structured data or livestream pages containing `BroadcastEvent` inside `VideoObject`. It is not available for general webpage submission - the `indexing.request` tool checks a page's structured data before submitting and returns an error for ineligible URLs. `indexing.remove` is for previously eligible pages and requires the URL to already return HTTP 404/410 or expose a robots `noindex` meta directive before it sends Google's `URL_DELETED` notification. Google's default **200 publish requests/day/project** is shared by update and removal notifications
|
|
64
|
+
Note that the Indexing API itself is narrow: Google currently restricts it to pages containing `JobPosting` structured data or livestream pages containing `BroadcastEvent` inside `VideoObject`. It is not available for general webpage submission - the `indexing.request` tool checks a page's structured data before submitting and returns an error for ineligible URLs. `indexing.remove` is for previously eligible pages and requires the URL to already return HTTP 404/410 or expose a robots `noindex` meta directive before it sends Google's `URL_DELETED` notification. Google's documented default **200 publish requests/day/project** is shared by update and removal notifications. The October 16, 2026 Google documentation update removed an outdated quota request form; check project-specific API access and quotas in Google Cloud Console, and never exceed permitted limits. All submissions are subject to spam detection, and Google warns that abuse or attempts to exceed quotas through multiple accounts or other means can result in revoked access. The server's own lower safety limits do not increase Google quota or bypass provider restrictions.
|
|
65
65
|
5. Save the Data Access changes. In **Google Auth platform → Audience**, leave **Publishing status** as **Testing** for now — see [Step 7](#step-7--important-google-verification).
|
|
66
66
|
|
|
67
67
|
---
|
package/package.json
CHANGED
package/scripts/glama-start.mjs
CHANGED
|
@@ -39,6 +39,13 @@ if (
|
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
const bindIp = process.env.MCP_GSC_BIND_IP?.trim() || '127.0.0.1';
|
|
43
|
+
if (bindIp !== '127.0.0.1' && bindIp !== '0.0.0.0') {
|
|
44
|
+
console.error(
|
|
45
|
+
'[mcp-gsc] Invalid MCP_GSC_BIND_IP: expected 127.0.0.1 (safe local default) or 0.0.0.0 (explicit container/network binding).',
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
42
49
|
const port = process.env.PORT || '8080';
|
|
43
50
|
const localBaseUrl = `http://127.0.0.1:${port}`;
|
|
44
51
|
const mcpUrl = `${localBaseUrl}/mcp`;
|
|
@@ -62,7 +69,7 @@ const args = [
|
|
|
62
69
|
stateDir,
|
|
63
70
|
'--local',
|
|
64
71
|
'--ip',
|
|
65
|
-
|
|
72
|
+
bindIp,
|
|
66
73
|
'--port',
|
|
67
74
|
port,
|
|
68
75
|
];
|
package/src/index.ts
CHANGED
|
@@ -316,7 +316,7 @@ const SITE_ADD_VERIFICATION_NOTE =
|
|
|
316
316
|
"Google Search Console Sites.add only adds the property to the user's Search Console site set; it does not verify ownership. Ownership verification is a separate Google Site Verification/Search Console workflow.";
|
|
317
317
|
|
|
318
318
|
const INDEXING_PROVIDER_USAGE_NOTE =
|
|
319
|
-
"Google
|
|
319
|
+
"Google's current Indexing API quota documentation lists 200 publish requests per day per project, shared by update and removal notifications. Google's October 16, 2026 update removed the previous quota-approval request form; this does not authorize exceeding Google's quota, submitting ineligible content, or bypassing required project/API permissions. Google applies spam detection and may revoke access for abuse or attempts to circumvent limits.";
|
|
320
320
|
|
|
321
321
|
const URL_INSPECTION_SCOPE_NOTE =
|
|
322
322
|
"Google's URL Inspection API reports the version currently known in Google's index; it does not run a live URL test or prove current live-page indexability. The mobileUsabilityResult field is deprecated and may be absent.";
|
|
@@ -607,8 +607,9 @@ const CONTENT_DECAY_OUTPUT_SCHEMA = {
|
|
|
607
607
|
const INDEXING_OUTPUT_SCHEMA = {
|
|
608
608
|
result: z.unknown(),
|
|
609
609
|
note: z.string(),
|
|
610
|
-
|
|
611
|
-
|
|
610
|
+
provider_default_publish_requests_per_day: z.literal(200),
|
|
611
|
+
provider_default_quota_for_testing_only: z.literal(false),
|
|
612
|
+
provider_usage_approval_required: z.literal(false),
|
|
612
613
|
provider_spam_detection_applies: z.literal(true),
|
|
613
614
|
provider_usage_note: z.string(),
|
|
614
615
|
};
|
|
@@ -869,7 +870,7 @@ const TOOL_CATALOG = [
|
|
|
869
870
|
{
|
|
870
871
|
name: 'indexing.request',
|
|
871
872
|
description:
|
|
872
|
-
'Request an eligible JobPosting or livestream URL update through Google\'s restricted Indexing API; this is not a general webpage submission tool. Google
|
|
873
|
+
'Request an eligible JobPosting or livestream URL update through Google\'s restricted Indexing API; this is not a general webpage submission tool. Google documents a default quota of 200 publish requests/day/project; eligible content restrictions, project permissions, quota limits, and spam screening still apply.',
|
|
873
874
|
},
|
|
874
875
|
{
|
|
875
876
|
name: 'indexing.remove',
|
|
@@ -2033,7 +2034,7 @@ class GscMcpRuntime {
|
|
|
2033
2034
|
'indexing.request',
|
|
2034
2035
|
{
|
|
2035
2036
|
title: 'Request Indexing',
|
|
2036
|
-
description: "Requests indexing through Google's Indexing API for a URL within an owner-level Search Console property accessible to the connected Google account. 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. Google
|
|
2037
|
+
description: "Requests indexing through Google's Indexing API for a URL within an owner-level Search Console property accessible to the connected Google account. 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. Google documents a default of 200 publish requests/day/project, shared by update and removal notifications. Eligibility, project authorization, quota limits and spam detection still apply; the published default does not authorize higher-volume or ineligible submissions.",
|
|
2037
2038
|
inputSchema: {
|
|
2038
2039
|
site_url: SEARCH_CONSOLE_PROPERTY_SCHEMA.describe(`${SEARCH_CONSOLE_PROPERTY_DESCRIPTION} The connected Google account must be an owner of this property.`),
|
|
2039
2040
|
url: z.string().superRefine((value, ctx) => {
|
|
@@ -2062,8 +2063,9 @@ class GscMcpRuntime {
|
|
|
2062
2063
|
const payload = {
|
|
2063
2064
|
result,
|
|
2064
2065
|
note: "Google accepting this notification does not guarantee the URL will be indexed. Indexing remains at Google's discretion.",
|
|
2065
|
-
|
|
2066
|
-
|
|
2066
|
+
provider_default_publish_requests_per_day: 200 as const,
|
|
2067
|
+
provider_default_quota_for_testing_only: false as const,
|
|
2068
|
+
provider_usage_approval_required: false as const,
|
|
2067
2069
|
provider_spam_detection_applies: true as const,
|
|
2068
2070
|
provider_usage_note: INDEXING_PROVIDER_USAGE_NOTE,
|
|
2069
2071
|
};
|
|
@@ -2104,8 +2106,9 @@ class GscMcpRuntime {
|
|
|
2104
2106
|
const payload = {
|
|
2105
2107
|
result,
|
|
2106
2108
|
note: "Google accepting this URL_DELETED notification only confirms receipt. It does not prove the URL was removed from Google's index; use URL Inspection for indexed-state evidence.",
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
+
provider_default_publish_requests_per_day: 200 as const,
|
|
2110
|
+
provider_default_quota_for_testing_only: false as const,
|
|
2111
|
+
provider_usage_approval_required: false as const,
|
|
2109
2112
|
provider_spam_detection_applies: true as const,
|
|
2110
2113
|
provider_usage_note: INDEXING_PROVIDER_USAGE_NOTE,
|
|
2111
2114
|
removal_completion_proven: false as const,
|