gsc-mcp-tools 0.4.2__tar.gz
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.
- gsc_mcp_tools-0.4.2/.claude/settings.local.json +53 -0
- gsc_mcp_tools-0.4.2/.env.example +19 -0
- gsc_mcp_tools-0.4.2/.gitignore +13 -0
- gsc_mcp_tools-0.4.2/.idea/.gitignore +10 -0
- gsc_mcp_tools-0.4.2/.idea/google-search-console-mcp.iml +8 -0
- gsc_mcp_tools-0.4.2/.idea/modules.xml +8 -0
- gsc_mcp_tools-0.4.2/.idea/vcs.xml +6 -0
- gsc_mcp_tools-0.4.2/.idea/workspace.xml +97 -0
- gsc_mcp_tools-0.4.2/.python-version +1 -0
- gsc_mcp_tools-0.4.2/.superpowers/sdd/progress.md +17 -0
- gsc_mcp_tools-0.4.2/CHANGELOG.md +101 -0
- gsc_mcp_tools-0.4.2/CLAUDE.md +62 -0
- gsc_mcp_tools-0.4.2/LICENSE +21 -0
- gsc_mcp_tools-0.4.2/PKG-INFO +226 -0
- gsc_mcp_tools-0.4.2/README.md +195 -0
- gsc_mcp_tools-0.4.2/docs/architecture.md +150 -0
- gsc_mcp_tools-0.4.2/docs/google-setup.md +174 -0
- gsc_mcp_tools-0.4.2/docs/starter-prompt.md +120 -0
- gsc_mcp_tools-0.4.2/pyproject.toml +54 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/__init__.py +0 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/auth.py +108 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/constants.py +24 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/meta.py +11 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/quota.py +20 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/retry.py +36 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/server.py +83 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/__init__.py +0 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/analytics.py +276 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/cross.py +218 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/crux.py +144 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/ga4.py +463 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/indexing.py +85 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/inspection.py +110 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/properties.py +80 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/seo.py +355 -0
- gsc_mcp_tools-0.4.2/src/gsc_mcp/tools/sitemaps.py +174 -0
- gsc_mcp_tools-0.4.2/tests/__init__.py +0 -0
- gsc_mcp_tools-0.4.2/tests/conftest.py +80 -0
- gsc_mcp_tools-0.4.2/tests/test_analytics.py +186 -0
- gsc_mcp_tools-0.4.2/tests/test_auth.py +70 -0
- gsc_mcp_tools-0.4.2/tests/test_cross.py +300 -0
- gsc_mcp_tools-0.4.2/tests/test_crux.py +171 -0
- gsc_mcp_tools-0.4.2/tests/test_ga4.py +474 -0
- gsc_mcp_tools-0.4.2/tests/test_indexing.py +108 -0
- gsc_mcp_tools-0.4.2/tests/test_inspection.py +81 -0
- gsc_mcp_tools-0.4.2/tests/test_properties.py +55 -0
- gsc_mcp_tools-0.4.2/tests/test_scaffold.py +23 -0
- gsc_mcp_tools-0.4.2/tests/test_seo.py +111 -0
- gsc_mcp_tools-0.4.2/tests/test_seo_v2.py +463 -0
- gsc_mcp_tools-0.4.2/tests/test_sitemap_audit.py +165 -0
- gsc_mcp_tools-0.4.2/tests/test_sitemaps.py +58 -0
- gsc_mcp_tools-0.4.2/tests/test_sitemaps_v2.py +156 -0
- gsc_mcp_tools-0.4.2/tests/test_utilities.py +114 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(rtk ls *)",
|
|
5
|
+
"Bash(rtk read *)",
|
|
6
|
+
"mcp__gsc-mcp__list_properties",
|
|
7
|
+
"mcp__gsc-mcp__list_sitemaps",
|
|
8
|
+
"mcp__gsc-mcp__get_performance_overview",
|
|
9
|
+
"mcp__gsc-mcp__check_alerts",
|
|
10
|
+
"mcp__gsc-mcp__batch_url_inspection",
|
|
11
|
+
"mcp__gsc-mcp__inspect_url",
|
|
12
|
+
"Bash(rtk pytest *)",
|
|
13
|
+
"Bash(echo \"---EXIT $?\")",
|
|
14
|
+
"Bash(rtk proxy *)",
|
|
15
|
+
"mcp__gsc-mcp__get_search_analytics",
|
|
16
|
+
"mcp__gsc-mcp__compare_search_periods",
|
|
17
|
+
"mcp__gsc-mcp__quick_wins",
|
|
18
|
+
"mcp__gsc-mcp__traffic_drops",
|
|
19
|
+
"mcp__gsc-mcp__submit_url",
|
|
20
|
+
"Bash(rtk git *)",
|
|
21
|
+
"Bash(git add *)",
|
|
22
|
+
"Bash(git commit -m ' *)",
|
|
23
|
+
"Bash(rtk pip *)",
|
|
24
|
+
"Bash(python -c \"from google.analytics.data_v1beta import BetaAnalyticsDataClient; print\\('GA4 lib OK'\\)\")",
|
|
25
|
+
"Bash(python -c \"from gsc_mcp.server import mcp; print\\('server import OK'\\)\")",
|
|
26
|
+
"Bash(rtk grep *)",
|
|
27
|
+
"Bash(python3 *)",
|
|
28
|
+
"Bash(printenv)",
|
|
29
|
+
"Bash(git remote *)",
|
|
30
|
+
"Bash(git config *)",
|
|
31
|
+
"Bash(hatch build *)",
|
|
32
|
+
"Bash(/tmp/test-gsc-mcp/bin/pip install *)",
|
|
33
|
+
"Bash(/tmp/test-gsc-mcp/bin/gsc-mcp --help)",
|
|
34
|
+
"Bash(/tmp/test-gsc-mcp/bin/python *)",
|
|
35
|
+
"Bash(rtk gh *)",
|
|
36
|
+
"Bash(gh repo *)",
|
|
37
|
+
"Bash(git tag *)",
|
|
38
|
+
"Bash(gh release *)",
|
|
39
|
+
"Bash(GSC_SERVICE_ACCOUNT_PATH=/Users/florianbruniaux/bel-etage-analytics-c4db99d1b930.json GSC_SKIP_OAUTH=true /Users/florianbruniaux/Sites/perso/google-search-console-mcp/.venv/bin/python -m gsc_mcp.server)",
|
|
40
|
+
"Bash(kill %1)",
|
|
41
|
+
"Bash(wait)",
|
|
42
|
+
"Bash(/Users/florianbruniaux/Sites/perso/google-search-console-mcp/.venv/bin/pip install *)",
|
|
43
|
+
"Bash(GSC_SERVICE_ACCOUNT_PATH=/Users/florianbruniaux/bel-etage-analytics-c4db99d1b930.json GSC_SKIP_OAUTH=true /Users/florianbruniaux/Sites/perso/google-search-console-mcp/.venv/bin/python *)",
|
|
44
|
+
"mcp__gsc-mcp__get_capabilities",
|
|
45
|
+
"mcp__gsc-mcp__traffic_health_check",
|
|
46
|
+
"Bash(.venv/bin/pytest tests/test_ga4.py tests/test_cross.py -v)",
|
|
47
|
+
"Bash(.venv/bin/pytest tests/ -q)",
|
|
48
|
+
"Bash(git commit *)",
|
|
49
|
+
"mcp__gsc-mcp__page_analysis",
|
|
50
|
+
"Bash(rtk find *)"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Google Search Console MCP — environment variables
|
|
2
|
+
# Copy to .env and fill in your values
|
|
3
|
+
|
|
4
|
+
# Path to your OAuth2 client credentials JSON (from Google Cloud Console)
|
|
5
|
+
# Required for OAuth flow (default auth method)
|
|
6
|
+
GSC_CREDENTIALS_PATH=/path/to/credentials.json
|
|
7
|
+
|
|
8
|
+
# Path to a Service Account JSON key file
|
|
9
|
+
# Alternative to OAuth — set this to skip the browser auth flow
|
|
10
|
+
# GSC_SERVICE_ACCOUNT_PATH=/path/to/service-account.json
|
|
11
|
+
|
|
12
|
+
# Skip OAuth flow entirely (requires GSC_SERVICE_ACCOUNT_PATH)
|
|
13
|
+
# GSC_SKIP_OAUTH=false
|
|
14
|
+
|
|
15
|
+
# Allow destructive operations (sitemap delete, etc.) — disabled by default
|
|
16
|
+
# GSC_ALLOW_DESTRUCTIVE=false
|
|
17
|
+
|
|
18
|
+
# Data state for search analytics: "final" or "all" (default: final)
|
|
19
|
+
# GSC_DATA_STATE=final
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="inheritedJdk" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/google-search-console-mcp.iml" filepath="$PROJECT_DIR$/.idea/google-search-console-mcp.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="AutoImportSettings">
|
|
4
|
+
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ChangeListManager">
|
|
7
|
+
<list default="true" id="463c5a88-8b49-4583-a93b-5bde7abb04de" name="Changes" comment="">
|
|
8
|
+
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
10
|
+
<change beforePath="$PROJECT_DIR$/docs/architecture.md" beforeDir="false" afterPath="$PROJECT_DIR$/docs/architecture.md" afterDir="false" />
|
|
11
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/auth.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/auth.py" afterDir="false" />
|
|
12
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/retry.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/retry.py" afterDir="false" />
|
|
13
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/analytics.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/analytics.py" afterDir="false" />
|
|
14
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/ga4.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/ga4.py" afterDir="false" />
|
|
15
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/indexing.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/indexing.py" afterDir="false" />
|
|
16
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/inspection.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/inspection.py" afterDir="false" />
|
|
17
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/properties.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/properties.py" afterDir="false" />
|
|
18
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/seo.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/seo.py" afterDir="false" />
|
|
19
|
+
<change beforePath="$PROJECT_DIR$/src/gsc_mcp/tools/sitemaps.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/gsc_mcp/tools/sitemaps.py" afterDir="false" />
|
|
20
|
+
</list>
|
|
21
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
22
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
23
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
24
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
25
|
+
</component>
|
|
26
|
+
<component name="CopilotPersistence">
|
|
27
|
+
<persistenceIdMap>
|
|
28
|
+
<entry key="_/Users/florianbruniaux/Sites/perso/google-search-console-mcp" value="3FP6uMgTYPCWF1rcWtp89wMwXqi" />
|
|
29
|
+
</persistenceIdMap>
|
|
30
|
+
</component>
|
|
31
|
+
<component name="EmbeddingIndexingInfo">
|
|
32
|
+
<option name="cachedIndexableFilesCount" value="3722" />
|
|
33
|
+
<option name="fileBasedEmbeddingIndicesEnabled" value="true" />
|
|
34
|
+
</component>
|
|
35
|
+
<component name="Git.Settings">
|
|
36
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
37
|
+
</component>
|
|
38
|
+
<component name="McpProjectServerCommands">
|
|
39
|
+
<commands />
|
|
40
|
+
<urls />
|
|
41
|
+
</component>
|
|
42
|
+
<component name="ProjectColorInfo">{
|
|
43
|
+
"associatedIndex": 7,
|
|
44
|
+
"fromUser": false
|
|
45
|
+
}</component>
|
|
46
|
+
<component name="ProjectId" id="3FP6uMgTYPCWF1rcWtp89wMwXqi" />
|
|
47
|
+
<component name="ProjectViewState">
|
|
48
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
49
|
+
<option name="showLibraryContents" value="true" />
|
|
50
|
+
</component>
|
|
51
|
+
<component name="PropertiesComponent">{
|
|
52
|
+
"keyToString": {
|
|
53
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
54
|
+
"RunOnceActivity.MCP Project settings loaded": "true",
|
|
55
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
56
|
+
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
57
|
+
"RunOnceActivity.git.unshallow": "true",
|
|
58
|
+
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
|
|
59
|
+
"codeWithMe.voiceChat.enabledByDefault": "false",
|
|
60
|
+
"com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1": "true",
|
|
61
|
+
"git-widget-placeholder": "main",
|
|
62
|
+
"javascript.preferred.runtime.type.id": "node",
|
|
63
|
+
"junie.onboarding.icon.badge.shown": "true",
|
|
64
|
+
"last_opened_file_path": "/Users/florianbruniaux/Sites/perso/google-search-console-mcp",
|
|
65
|
+
"node.js.detected.package.eslint": "true",
|
|
66
|
+
"node.js.detected.package.tslint": "true",
|
|
67
|
+
"node.js.selected.package.eslint": "(autodetect)",
|
|
68
|
+
"node.js.selected.package.tslint": "(autodetect)",
|
|
69
|
+
"nodejs_package_manager_path": "npm",
|
|
70
|
+
"to.speed.mode.migration.done": "true",
|
|
71
|
+
"vue.rearranger.settings.migration": "true"
|
|
72
|
+
}
|
|
73
|
+
}</component>
|
|
74
|
+
<component name="SharedIndexes">
|
|
75
|
+
<attachedChunks>
|
|
76
|
+
<set>
|
|
77
|
+
<option value="bundled-js-predefined-d6986cc7102b-31caf2ab9e3c-JavaScript-WS-261.25134.101" />
|
|
78
|
+
</set>
|
|
79
|
+
</attachedChunks>
|
|
80
|
+
</component>
|
|
81
|
+
<component name="TaskManager">
|
|
82
|
+
<task active="true" id="Default" summary="Default task">
|
|
83
|
+
<changelist id="463c5a88-8b49-4583-a93b-5bde7abb04de" name="Changes" comment="" />
|
|
84
|
+
<created>1781964578952</created>
|
|
85
|
+
<option name="number" value="Default" />
|
|
86
|
+
<option name="presentableId" value="Default" />
|
|
87
|
+
<updated>1781964578952</updated>
|
|
88
|
+
<workItem from="1781964579955" duration="2110000" />
|
|
89
|
+
<workItem from="1782057449022" duration="7333000" />
|
|
90
|
+
<workItem from="1782214079804" duration="3000" />
|
|
91
|
+
</task>
|
|
92
|
+
<servers />
|
|
93
|
+
</component>
|
|
94
|
+
<component name="TypeScriptGeneratedFilesManager">
|
|
95
|
+
<option name="version" value="3" />
|
|
96
|
+
</component>
|
|
97
|
+
</project>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13.0
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# gsc-mcp v0.5.0 SDD Progress
|
|
2
|
+
|
|
3
|
+
Branch: main
|
|
4
|
+
Started: 2026-06-23
|
|
5
|
+
MERGE_BASE: 587c08cee (to be confirmed at start of each review)
|
|
6
|
+
|
|
7
|
+
## Tasks
|
|
8
|
+
|
|
9
|
+
- [ ] Task 1: Phase 1 - GA4 hostname + country filters
|
|
10
|
+
- [ ] Task 2: Phase 2 - CrUX tools (crux_page_vitals, crux_history)
|
|
11
|
+
- [ ] Task 3: Phase 3 - sitemap_audit tool
|
|
12
|
+
- [ ] Task 4: Phase 4 - schema_validate tool
|
|
13
|
+
- [ ] Task 5: Phase 5 - version bump + capabilities + docs
|
|
14
|
+
|
|
15
|
+
## Log
|
|
16
|
+
|
|
17
|
+
(entries appended as tasks complete)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.4.2] - 2026-06-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Tous les tools GA4 (`ga4_organic_landing_pages`, `ga4_traffic_sources`, `ga4_page_performance`, `ga4_realtime`, `ga4_user_behavior`, `ga4_conversion_funnel`) et les tools cross (`traffic_health_check`, `page_analysis`) acceptent un paramètre optionnel `property_id: str = None`. Quand fourni, il override `GA4_PROPERTY_ID` sans modifier la config. Permet de requêter plusieurs properties GA4 depuis une seule instance MCP.
|
|
8
|
+
- `get_ga4_property_id(override=None)` dans `auth.py` : accepte un override direct, court-circuite la lecture de l'env var. Sans override, comportement identique à avant.
|
|
9
|
+
- 4 nouveaux tests : `test_get_ga4_property_id_override_takes_precedence`, `test_get_ga4_property_id_override_no_env_needed`, `test_thc_property_id_propagated`, `test_pa_property_id_propagated`
|
|
10
|
+
|
|
11
|
+
## [0.4.1] - 2026-06-22
|
|
12
|
+
|
|
13
|
+
Corrections de cohérence et consolidation interne : pas de nouveaux tools.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `get_capabilities` retournait 18 tools sur les 32 réellement disponibles. Les 14 manquants (`analytics_anomalies`, `seo_striking_distance`, `seo_cannibalization`, `seo_lost_queries`, `sitemaps_delete`, `sitemaps_get`, 6 tools GA4, `traffic_health_check`, `page_analysis`) sont maintenant listés
|
|
18
|
+
- `inspect_url`, `batch_url_inspection`, `check_indexing_issues` appelaient `webmasters/v3`, qui n'expose pas `urlInspection`. Ces trois tools levaient une `AttributeError` au runtime sur chaque appel. Corrigé en passant sur `searchconsole/v1` (API qui expose la ressource `urlInspection`)
|
|
19
|
+
- `traffic_health_check` et `page_analysis` incluent maintenant un champ `note` dans leur réponse JSON pour avertir que GSC a un décalage de 3 jours vs GA4, les ratios sont donc approximatifs
|
|
20
|
+
- `ga4_organic_landing_pages` ajoute un champ `note` quand le nombre de résultats atteint la limite, signalant une troncature potentielle
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Tous les tools GSC (analytics, SEO, sitemaps, properties) consolidés sur `searchconsole/v1`. Le client `webmasters/v3` (`get_gsc_service`) est supprimé de `auth.py`. `searchconsole/v1` expose les mêmes ressources `sites`, `searchanalytics`, `sitemaps` en plus de `urlInspection`
|
|
25
|
+
|
|
26
|
+
### Tests
|
|
27
|
+
|
|
28
|
+
- Couverture `quick_wins` améliorée : 4 nouveaux cas couvrant les pages à CTR zéro avec impressions suffisantes, l'exclusion sous le seuil d'impressions et l'exclusion des pages déjà au benchmark de CTR
|
|
29
|
+
|
|
30
|
+
## [0.4.0] - 2026-06-22
|
|
31
|
+
|
|
32
|
+
Phase 3: 2 tools cross-platform GSC+GA4, nouveau module `cross.py`.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- `traffic_health_check(site, days)`: compare les clics GSC agrégés avec les sessions organiques GA4 pour détecter les écarts de tracking. Retourne un statut parmi `no_gsc_data`, `tracking_gap` (ratio < 0.6), `filter_issue` (ratio > 1.3) ou `healthy`. GA4 interrogé avec `limit=10000` pour éviter les sous-comptages sur les gros sites
|
|
37
|
+
- `page_analysis(site, days, limit)`: jointure page par page entre GSC (dimensions=["page"]) et GA4 (landing pages organiques). Les pages présentes dans une seule source sont incluses avec les champs manquants à `None`. Chaque page reçoit un `opportunity_score = log10(impressions+1)*10 + engagement_rate*100 + log10(conversions+1)*20`, trié décroissant, tronqué à `limit`
|
|
38
|
+
- `_normalize_url(url)` helper interne: ramène URLs absolues (GSC) et paths GA4 au même chemin nu, sans scheme, host, query ni slash final, pour fiabiliser la jointure
|
|
39
|
+
- `engagement_rate` dérivé dans `cross.py` comme `engaged_sessions/sessions` (formule GA4 native), sans modifier la sortie de `ga4_organic_landing_pages`
|
|
40
|
+
- 24 nouveaux tests dans `tests/test_cross.py`, dont les boundaries 0.6 et 1.3 du ratio, les cas GSC-only, GA4-only, trailing slash et query string
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Tool count: 30 vers 32
|
|
45
|
+
|
|
46
|
+
## [0.3.0] - 2026-06-22
|
|
47
|
+
|
|
48
|
+
Phase 2: 6 new GA4 tools, new dependency, new environment variable.
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- `ga4_organic_landing_pages(start_date, end_date, limit)`: sessions, engaged sessions, bounce rate, average session duration, conversions and revenue for organic landing pages. Uses the `sessionMedium=organic` filter on `landingPagePlusQueryString`
|
|
53
|
+
- `ga4_traffic_sources(start_date, end_date)`: sessions and conversions broken down by channel group, source and medium
|
|
54
|
+
- `ga4_page_performance(start_date, end_date, page_path)`: 7 metrics per page path (page views, active users, average session duration, engagement rate, bounce rate, conversions, revenue). Optional `page_path` parameter adds a CONTAINS filter
|
|
55
|
+
- `ga4_realtime()`: active users right now, by screen name, country and device. No date range, uses `run_realtime_report` directly
|
|
56
|
+
- `ga4_user_behavior(start_date, end_date)`: single `batch_run_reports` call returning three breakdowns (by device, by country top 20, by user type new/returning)
|
|
57
|
+
- `ga4_conversion_funnel(start_date, end_date, event_name)`: two sequential `run_report` calls. First lists pages with conversions > 0; second lists events, optionally filtered by exact event name
|
|
58
|
+
- New dependency: `google-analytics-data>=0.18.0` (Google Analytics Data API v1beta, protobuf-based client)
|
|
59
|
+
- New environment variable: `GA4_PROPERTY_ID` (numeric property ID, e.g. `123456789`). Validated lazily on first GA4 tool call, never at startup. GSC-only users are not affected
|
|
60
|
+
- `get_ga4_service()` and `get_ga4_property_id()` in `auth.py`, reusing the same `_resolve_creds` path as GSC and Indexing clients
|
|
61
|
+
|
|
62
|
+
### Changed
|
|
63
|
+
|
|
64
|
+
- Tool count: 24 → 30
|
|
65
|
+
|
|
66
|
+
## [0.2.0] - 2026-06-22
|
|
67
|
+
|
|
68
|
+
Phase 1: 6 new tools, no new dependencies.
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- `seo_striking_distance(site, days, min_impressions)`: queries in positions 8-15 sorted by impressions desc. Separate band from `quick_wins` (4-15), intended for queries one push away from page 1
|
|
73
|
+
- `seo_cannibalization(site, days, min_impressions)`: detects queries split across multiple pages using an HHI conflict score (`1 - sum(share²)`). Zero-click groups use uniform `1/n` fallback to avoid division by zero. Filters on per-query total impressions, not per-page
|
|
74
|
+
- `seo_lost_queries(site, days)`: flags queries with a click drop ≥ 80% vs the previous period, requiring at least 5 previous clicks. Iterates over the previous period to catch fully-vanished queries. Same two-window no-lag pattern as `traffic_drops`
|
|
75
|
+
- `analytics_anomalies(site, days, threshold)`: Z-score anomaly detection on daily clicks via `statistics.pstdev`. Returns `anomalies = []` when std is zero (constant or all-zero series) to handle low-traffic sites safely
|
|
76
|
+
- `sitemaps_delete(site, sitemap_url)`: deletes a sitemap with a safety check before any API call (URL must end with `.xml` or contain `/sitemap`, raises `ValueError` otherwise)
|
|
77
|
+
- `sitemaps_get(site, sitemap_url)`: fetches a single sitemap resource and normalises it to the same flat shape as `list_sitemaps` (warnings and errors coerced to int)
|
|
78
|
+
- 62 new tests (35 SEO, 15 sitemaps, 9 analytics anomalies): all mocked, no API calls
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- Tool count: 18 → 24
|
|
83
|
+
|
|
84
|
+
## [0.1.0] - 2026-06-20
|
|
85
|
+
|
|
86
|
+
Initial release.
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- 18 MCP tools across 6 categories: meta, properties, analytics, SEO, inspection, indexing, sitemaps
|
|
91
|
+
- `submit_batch` using true HTTP multipart batch via `new_batch_http_request()`, chunked at 100 URLs per request. Avoids the late-binding closure bug with a `_make_callback(url)` factory pattern
|
|
92
|
+
- Dual OAuth scope architecture: separate clients for GSC (`auth/webmasters`) and Indexing API (`auth/indexing`), because the Indexing API rejects webmasters tokens
|
|
93
|
+
- OAuth flow with token stored as JSON (`google.oauth2.credentials.Credentials.to_json()`) instead of pickle
|
|
94
|
+
- Service Account support as an alternative to OAuth (set `GSC_SERVICE_ACCOUNT_PATH`)
|
|
95
|
+
- Exponential backoff retry on HTTP 429/500/502/503/504 via `with_retry()` decorator, no retry on 404
|
|
96
|
+
- In-memory quota tracker (`QuotaTracker`) with configurable limit and warn threshold (warns at 180/200 by default)
|
|
97
|
+
- `with_meta()` wrapper on all tool outputs: every response includes a `_meta` block with tool name and call parameters, so Claude has full context on what was fetched
|
|
98
|
+
- `quick_wins` tool scoring CTR opportunity vs benchmark by SERP position
|
|
99
|
+
- `check_indexing_issues` categorizing URLs into `not_indexed`, `robots_blocked`, `fetch_error`, `canonical_issue`, `indexed`
|
|
100
|
+
- `traffic_drops` diagnosing drops as `ranking_loss`, `ctr_collapse`, or `demand_decline`
|
|
101
|
+
- Full test suite: 52 tests, fully mocked, no Google API calls required
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Requires Python 3.11+
|
|
9
|
+
python3 --version # must be 3.11 or higher
|
|
10
|
+
|
|
11
|
+
# Install (dev mode with test deps)
|
|
12
|
+
pip install -e ".[dev]"
|
|
13
|
+
|
|
14
|
+
# Run the MCP server
|
|
15
|
+
gsc-mcp
|
|
16
|
+
# or
|
|
17
|
+
python -m gsc_mcp.server
|
|
18
|
+
|
|
19
|
+
# Run all tests
|
|
20
|
+
pytest tests/ -v
|
|
21
|
+
|
|
22
|
+
# Run a single test file
|
|
23
|
+
pytest tests/test_analytics.py -v
|
|
24
|
+
|
|
25
|
+
# Run a specific test by name
|
|
26
|
+
pytest tests/ -k "test_submit_batch" -v
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
**Entry point**: `src/gsc_mcp/server.py` creates a `FastMCP("gsc-mcp")` instance and registers all 32 tools via `mcp.tool()()`. No dynamic discovery; every tool is explicitly imported and registered here. Adding a new tool means: implement it in the relevant `tools/` module, import it in `server.py`, and register it.
|
|
32
|
+
|
|
33
|
+
**Auth layer** (`auth.py`): Three separate credential pairs: GSC API (`searchconsole/v1`), Indexing API (`indexing/v3`), and GA4 (`analytics.readonly`). Resolution order: if `GSC_SERVICE_ACCOUNT_PATH` is set, use service account credentials. Otherwise, fall through to OAuth with token cached as JSON (not pickle) at the OS user data dir (`~/Library/Application Support/gsc-mcp/` on macOS). Token files: `token_gsc.json`, `token_indexing.json`, `token_ga4.json`. `get_ga4_property_id(override=None)` accepts an optional override string that bypasses `GA4_PROPERTY_ID`, enabling per-call multi-property support.
|
|
34
|
+
|
|
35
|
+
**Tools** (`src/gsc_mcp/tools/`): Eight modules, each owns a logical domain (analytics, cross, ga4, indexing, inspection, properties, seo, sitemaps). Every tool function is a plain Python function (no class, no decorator) registered by `server.py`. All return JSON strings via `json.dumps(with_meta(...))`.
|
|
36
|
+
|
|
37
|
+
**Cross-platform pattern** (`cross.py`): `traffic_health_check` and `page_analysis` compose the high-level functions from `analytics.py` and `ga4.py`. They call those functions, parse their JSON strings with `json.loads`, then join on `_normalize_url` (strips scheme/host/query/trailing-slash so GSC absolute URLs and GA4 paths match). `engagement_rate` is derived as `engaged_sessions/sessions` because `ga4_organic_landing_pages` does not expose it directly.
|
|
38
|
+
|
|
39
|
+
**Output contract** (`meta.py`): Every tool wraps its response dict with `with_meta(data, tool=..., params=...)`, which appends a `_meta` block (`{"tool": "<name>", "params": {...}}`). Any new tool must follow this pattern.
|
|
40
|
+
|
|
41
|
+
**Retry** (`retry.py`): `@with_retry(max_retries=3, base_delay=1.0)` wraps Google API calls. Retries on 429/500/502/503/504 with exponential backoff (`base_delay * 2^attempt`). Other HTTP errors propagate immediately.
|
|
42
|
+
|
|
43
|
+
**Quota tracking** (`quota.py` + `indexing.py`): `QuotaTracker` is a module-level singleton in `indexing.py` that tracks Indexing API usage within a single process lifetime (200 req/day limit, warns at 180). It does not persist across restarts; restarting the server resets the counter.
|
|
44
|
+
|
|
45
|
+
**Batching** (`indexing.py`): `submit_batch` uses `svc.new_batch_http_request()` chunked at 100 URLs per HTTP request. This is a true multipart batch, not a sequential loop. Callbacks collect per-URL results.
|
|
46
|
+
|
|
47
|
+
## Test conventions
|
|
48
|
+
|
|
49
|
+
All tests are fully mocked (no real Google API calls). `tests/conftest.py` defines two shared fixtures:
|
|
50
|
+
- `mock_gsc_service`: MagicMock wired for `sites`, `searchanalytics`, `sitemaps`, `urlInspection`
|
|
51
|
+
- `mock_indexing_service`: MagicMock with a working `new_batch_http_request()` implementation that fires callbacks synchronously
|
|
52
|
+
|
|
53
|
+
Tests patch `get_searchconsole_service` / `get_indexing_service` / `get_ga4_service` at the call site (e.g., `gsc_mcp.tools.analytics.get_searchconsole_service`) and inject the fixture as the return value. GA4 tests also use an `autouse` fixture to set `GA4_PROPERTY_ID`, since `get_ga4_property_id()` raises `RuntimeError` if the env var is absent and no `override` is passed.
|
|
54
|
+
|
|
55
|
+
## Environment variables
|
|
56
|
+
|
|
57
|
+
| Variable | Purpose |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `GSC_SERVICE_ACCOUNT_PATH` | Path to service account JSON (preferred for automation) |
|
|
60
|
+
| `GSC_CREDENTIALS_PATH` | Path to OAuth Desktop client JSON (interactive OAuth flow) |
|
|
61
|
+
| `GSC_SKIP_OAUTH` | Set to `true` to disable OAuth fallback (requires SA path) |
|
|
62
|
+
| `GA4_PROPERTY_ID` | Numeric GA4 property ID (e.g. `123456789`). Required for GA4 tools only, validated lazily |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Florian Bruniaux
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|