@digestseo/mcp-gsc 0.4.18 → 0.4.20
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 +14 -0
- package/README.md +3 -5
- package/SETUP.md +0 -3
- package/package.json +1 -1
- package/scripts/glama-start.mjs +3 -2
- package/src/google.ts +18 -2
- package/src/index.ts +41 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.20] - 2026-09-22
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Modern MCP requests that use the 2026-07-28 `Mcp-Method` header now fail with the required HTTP 400 `HeaderMismatch` error when `MCP-Protocol-Version` is missing, while legacy initialization without the modern header remains compatible.
|
|
14
|
+
- Repo-owned onboarding and support documentation no longer advertises the intentionally unavailable owner-hosted/automatic-weekly-email service, and Cursor guidance now identifies the one-click path as the local npm launcher.
|
|
15
|
+
- `GET /` now returns deployment-neutral discovery text with the derived MCP endpoint and canonical setup link instead of calling every deployment hosted or telling loopback users to connect it directly from Claude.ai.
|
|
16
|
+
|
|
17
|
+
## [0.4.19] - 2026-09-22
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- Search Analytics freshness metadata now normalizes Google's camelCase `firstIncompleteDate` / `firstIncompleteHour` wire fields into the stable MCP `first_incomplete_date` / `first_incomplete_hour` response contract.
|
|
21
|
+
- The npm launcher now exits deterministically after its Wrangler child terminates by signal instead of re-signalling itself while its own `SIGINT`/`SIGTERM` listeners are installed, preventing wrapper processes from lingering after Ctrl+C or supervisor shutdown on POSIX systems.
|
|
22
|
+
- ChatGPT local-launcher guidance now explicitly requires Secure MCP Tunnel for the loopback `127.0.0.1` endpoint, or a separately deployed remote Worker, instead of implying that ChatGPT can connect directly to localhost.
|
|
23
|
+
|
|
10
24
|
## [0.4.18] - 2026-09-22
|
|
11
25
|
|
|
12
26
|
### Fixed
|
package/README.md
CHANGED
|
@@ -62,14 +62,12 @@ claude mcp add --transport http gsc http://127.0.0.1:8080/mcp
|
|
|
62
62
|
|
|
63
63
|
Start the npm launcher before using either install button. Complete its Google OAuth flow on first use.
|
|
64
64
|
|
|
65
|
-
**ChatGPT** — add
|
|
65
|
+
**ChatGPT** — ChatGPT cannot connect directly to this loopback `127.0.0.1` endpoint. Use OpenAI's **Secure MCP Tunnel** for the local launcher, or deploy your own remote Cloudflare Worker and add that `/mcp` URL as a custom MCP app in Developer mode; see [Connect in your AI client](#connect-in-your-ai-client) below.
|
|
66
66
|
|
|
67
67
|
A self-hostable [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for **Google Search Console**. Connect it to Claude.ai, Cursor, ChatGPT, or any MCP-compatible client and ask your AI assistant about your site's organic search performance — impressions, clicks, top queries, index status, and sitemap health — straight from your own Google account.
|
|
68
68
|
|
|
69
69
|
It runs on [Cloudflare Workers](https://workers.cloudflare.com/) and ships with one-click Google OAuth onboarding: connect the server in your client, sign in with Google once, grant the requested Google Search Console permissions, and you're done. No API keys to copy around and no service-account JSON to manage.
|
|
70
70
|
|
|
71
|
-
> **Prefer zero setup?** The hosted version — with automatic weekly email digests delivered to your inbox — is at **[digestseo.com](https://digestseo.com)**. This repository is the open-source core you can run yourself.
|
|
72
|
-
|
|
73
71
|
## Tools
|
|
74
72
|
|
|
75
73
|
By default (`GSC_ACCESS_MODE=readwrite`), this server exposes 22 tools. Read-only analytics and reporting tools are marked with MCP's `readOnlyHint`; the write tools below can change Search Console properties, sitemaps, or indexing state. Set `GSC_ACCESS_MODE=readonly` to request only the Search Console read-only scope and expose the 16 tools that do not require either write access or the separate Indexing API scope.
|
|
@@ -126,7 +124,7 @@ https://<your-worker>.workers.dev/mcp
|
|
|
126
124
|
```
|
|
127
125
|
|
|
128
126
|
- **Claude.ai / Claude Desktop** — go to **Customize → Connectors**, click **+ → Add custom connector**, enter a name and paste the `/mcp` URL. Leave the optional advanced OAuth Client ID/Secret fields blank. On Team/Enterprise, an Owner or Primary Owner must first add the custom Web connector from **Organization settings → Connectors**; members then connect it from Customize → Connectors. On first connection, Claude opens the Google sign-in flow.
|
|
129
|
-
- **Cursor** — add a remote **Streamable HTTP** MCP server pointing at the same `/mcp` URL; Cursor supports OAuth for remote HTTP MCP servers. The
|
|
127
|
+
- **Cursor** — add a remote **Streamable HTTP** MCP server pointing at the same `/mcp` URL; Cursor supports OAuth for remote HTTP MCP servers. The Add to Cursor button above targets the local npm launcher's loopback endpoint instead.
|
|
130
128
|
- **ChatGPT** — enable **Developer mode**, then create a custom MCP **app** from **Settings → Apps → Create** (admins/owners can also use **Workspace settings → Apps → Create**). Provide the `/mcp` endpoint, select the applicable authentication option, **Scan Tools**, complete OAuth, then create the app. Full MCP including write/modify tools is currently available to Business and Enterprise/Edu; Pro custom MCP access is read/fetch-only, so use `GSC_ACCESS_MODE=readonly` for that path.
|
|
131
129
|
|
|
132
130
|
The `/mcp` endpoint is the same across clients, but each host has its own setup and permission flow.
|
|
@@ -166,4 +164,4 @@ Contributions welcome — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
|
166
164
|
|
|
167
165
|
---
|
|
168
166
|
|
|
169
|
-
*Built and maintained by [Tomi Šeregi](https://tomiseregi.si), the builder behind [
|
|
167
|
+
*Built and maintained by [Tomi Šeregi](https://tomiseregi.si), the builder behind [DigestSEO](https://digestseo.com) - open-source SEO tooling and search visibility software.*
|
package/SETUP.md
CHANGED
|
@@ -84,7 +84,6 @@ npx wrangler login
|
|
|
84
84
|
- The `npx` launcher persists its local KV and Durable Object state in `~/.mcp-gsc/state` by default so npm package upgrades or cache cleanup do not discard the encrypted Google session. Set `MCP_GSC_STATE_DIR` to use another directory, and keep the same `TOKEN_ENCRYPTION_KEY` when reusing existing state. The first release with this stable path may require one Google reconnect because older launcher releases stored local state under the npm package cache.
|
|
85
85
|
- The Worker derives Google's redirect URI from the MCP request URL. Google requires an exact authorized-URI match, so keep the same scheme, host, port, and `/google/callback` path. `localhost` and `127.0.0.1` are different hosts for this check.
|
|
86
86
|
- The path must be exactly `/google/callback` — that's the route this server handles.
|
|
87
|
-
- The DigestSEO-hosted instance also requires this exact callback URI in the OAuth client configured on its Worker: `https://mcp-gsc.digestseo.com/google/callback`.
|
|
88
87
|
4. Click **Create**. Copy the **Client ID** and **Client secret** — you'll set them as secrets in the next step.
|
|
89
88
|
|
|
90
89
|
---
|
|
@@ -208,8 +207,6 @@ To remove all three limits you must move the app to **Publishing status: In prod
|
|
|
208
207
|
|
|
209
208
|
**Bottom line:** for personal use with one or two Google accounts, Testing mode is fine as long as you don't mind reconnecting roughly every 7 days. For anything shared or automated, you'll want to complete Google's verification — and that, not the code, is the heaviest part of self-hosting a Google Search Console MCP.
|
|
210
209
|
|
|
211
|
-
> Don't want to deal with verification at all? The hosted version at **[digestseo.com](https://digestseo.com)** has already been through it (and adds automatic weekly email digests on top).
|
|
212
|
-
|
|
213
210
|
---
|
|
214
211
|
|
|
215
212
|
## Troubleshooting
|
package/package.json
CHANGED
package/scripts/glama-start.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
3
|
+
import { constants, homedir } from 'node:os';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
|
|
@@ -51,7 +51,8 @@ process.on('SIGTERM', () => forwardSignal('SIGTERM'));
|
|
|
51
51
|
|
|
52
52
|
child.on('exit', (code, signal) => {
|
|
53
53
|
if (signal) {
|
|
54
|
-
|
|
54
|
+
const signalNumber = constants.signals[signal];
|
|
55
|
+
process.exit(signalNumber === undefined ? 1 : 128 + signalNumber);
|
|
55
56
|
} else {
|
|
56
57
|
process.exit(code ?? 1);
|
|
57
58
|
}
|
package/src/google.ts
CHANGED
|
@@ -407,6 +407,11 @@ export interface SearchAnalyticsResponseMetadata {
|
|
|
407
407
|
first_incomplete_hour?: string;
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
interface GoogleSearchAnalyticsResponseMetadata {
|
|
411
|
+
firstIncompleteDate?: string;
|
|
412
|
+
firstIncompleteHour?: string;
|
|
413
|
+
}
|
|
414
|
+
|
|
410
415
|
export interface SearchAnalyticsResponse {
|
|
411
416
|
rows: SearchAnalyticsRow[];
|
|
412
417
|
responseAggregationType?: string;
|
|
@@ -610,14 +615,25 @@ export async function querySearchAnalytics(
|
|
|
610
615
|
const data = (await resp.json()) as {
|
|
611
616
|
rows?: SearchAnalyticsRow[];
|
|
612
617
|
responseAggregationType?: string;
|
|
613
|
-
metadata?:
|
|
618
|
+
metadata?: GoogleSearchAnalyticsResponseMetadata;
|
|
614
619
|
};
|
|
620
|
+
const metadata: SearchAnalyticsResponseMetadata | undefined =
|
|
621
|
+
data.metadata === undefined
|
|
622
|
+
? undefined
|
|
623
|
+
: {
|
|
624
|
+
...(data.metadata.firstIncompleteDate !== undefined
|
|
625
|
+
? { first_incomplete_date: data.metadata.firstIncompleteDate }
|
|
626
|
+
: {}),
|
|
627
|
+
...(data.metadata.firstIncompleteHour !== undefined
|
|
628
|
+
? { first_incomplete_hour: data.metadata.firstIncompleteHour }
|
|
629
|
+
: {}),
|
|
630
|
+
};
|
|
615
631
|
return {
|
|
616
632
|
rows: data.rows ?? [],
|
|
617
633
|
...(data.responseAggregationType !== undefined
|
|
618
634
|
? { responseAggregationType: data.responseAggregationType }
|
|
619
635
|
: {}),
|
|
620
|
-
...(
|
|
636
|
+
...(metadata !== undefined ? { metadata } : {}),
|
|
621
637
|
};
|
|
622
638
|
}
|
|
623
639
|
|
package/src/index.ts
CHANGED
|
@@ -2055,7 +2055,44 @@ export async function createGscMcpServer(
|
|
|
2055
2055
|
}
|
|
2056
2056
|
|
|
2057
2057
|
export const mcpApiHandler = {
|
|
2058
|
-
fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
2058
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
2059
|
+
if (
|
|
2060
|
+
request.method === 'POST' &&
|
|
2061
|
+
request.headers.has('Mcp-Method') &&
|
|
2062
|
+
!request.headers.has('MCP-Protocol-Version')
|
|
2063
|
+
) {
|
|
2064
|
+
let id: string | number | null = null;
|
|
2065
|
+
let isRequest = false;
|
|
2066
|
+
try {
|
|
2067
|
+
const body = (await request.clone().json()) as { id?: unknown };
|
|
2068
|
+
if (Object.prototype.hasOwnProperty.call(body, 'id')) {
|
|
2069
|
+
isRequest = true;
|
|
2070
|
+
if (typeof body.id === 'string' || typeof body.id === 'number') {
|
|
2071
|
+
id = body.id;
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
} catch {
|
|
2075
|
+
// Let the SDK handle malformed bodies; this guard targets valid modern requests.
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
if (isRequest) {
|
|
2079
|
+
return new Response(
|
|
2080
|
+
JSON.stringify({
|
|
2081
|
+
jsonrpc: '2.0',
|
|
2082
|
+
id,
|
|
2083
|
+
error: {
|
|
2084
|
+
code: -32020,
|
|
2085
|
+
message: 'HeaderMismatch: MCP-Protocol-Version header is required for modern MCP requests.',
|
|
2086
|
+
},
|
|
2087
|
+
}),
|
|
2088
|
+
{
|
|
2089
|
+
status: 400,
|
|
2090
|
+
headers: { 'content-type': 'application/json; charset=utf-8' },
|
|
2091
|
+
},
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2059
2096
|
const handler = createMcpHandler(() => createGscMcpServer(env), {
|
|
2060
2097
|
route: '/mcp',
|
|
2061
2098
|
});
|
|
@@ -2119,9 +2156,9 @@ export const defaultHandler = {
|
|
|
2119
2156
|
});
|
|
2120
2157
|
}
|
|
2121
2158
|
return new Response(
|
|
2122
|
-
'mcp-gsc —
|
|
2123
|
-
|
|
2124
|
-
|
|
2159
|
+
'mcp-gsc — Google Search Console MCP server.\n' +
|
|
2160
|
+
`MCP endpoint: ${url.origin}/mcp\n` +
|
|
2161
|
+
'Setup: https://github.com/AKzar1el/mcp-gsc#readme\n',
|
|
2125
2162
|
{ headers: { 'content-type': 'text/plain; charset=utf-8' } },
|
|
2126
2163
|
);
|
|
2127
2164
|
}
|