mcp-google-api 0.21.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.
@@ -0,0 +1,276 @@
1
+ interface SearchResponse {
2
+ success: boolean;
3
+ error?: string;
4
+ data?: any[];
5
+ location?: any;
6
+ nextPageToken?: string;
7
+ fieldMask?: string;
8
+ raw_response?: any;
9
+ }
10
+ interface PlaceDetailsResponse {
11
+ success: boolean;
12
+ error?: string;
13
+ data?: any;
14
+ fieldMask?: string;
15
+ }
16
+ interface GeocodeResponse {
17
+ success: boolean;
18
+ error?: string;
19
+ data?: {
20
+ location: {
21
+ lat: number;
22
+ lng: number;
23
+ };
24
+ formatted_address: string;
25
+ place_id: string;
26
+ };
27
+ }
28
+ interface ReverseGeocodeResponse {
29
+ success: boolean;
30
+ error?: string;
31
+ data?: {
32
+ formatted_address: string;
33
+ place_id: string;
34
+ address_components: any[];
35
+ };
36
+ }
37
+ interface DistanceMatrixResponse {
38
+ success: boolean;
39
+ error?: string;
40
+ data?: {
41
+ distances: any[][];
42
+ durations: any[][];
43
+ origin_addresses: string[];
44
+ destination_addresses: string[];
45
+ };
46
+ }
47
+ interface DirectionsResponse {
48
+ success: boolean;
49
+ error?: string;
50
+ data?: {
51
+ routes: any[];
52
+ summary: string;
53
+ total_distance: {
54
+ value: number;
55
+ text: string;
56
+ };
57
+ total_duration: {
58
+ value: number;
59
+ text: string;
60
+ };
61
+ };
62
+ }
63
+ interface TimezoneResponse {
64
+ success: boolean;
65
+ error?: string;
66
+ data?: {
67
+ timeZoneId: string;
68
+ timeZoneName: string;
69
+ utcOffset: number;
70
+ dstOffset: number;
71
+ localTime: string;
72
+ };
73
+ }
74
+ interface WeatherResponse {
75
+ success: boolean;
76
+ error?: string;
77
+ data?: any;
78
+ }
79
+ interface StaticMapResponse {
80
+ success: boolean;
81
+ error?: string;
82
+ data?: {
83
+ base64: string;
84
+ size: number;
85
+ dimensions: string;
86
+ };
87
+ }
88
+ interface AirQualityResponse {
89
+ success: boolean;
90
+ error?: string;
91
+ data?: any;
92
+ }
93
+ interface ElevationResponse {
94
+ success: boolean;
95
+ error?: string;
96
+ data?: Array<{
97
+ elevation: number;
98
+ location: {
99
+ lat: number;
100
+ lng: number;
101
+ };
102
+ }>;
103
+ }
104
+ declare class PlacesSearcher {
105
+ private mapsTools;
106
+ private newPlacesService;
107
+ private routesService;
108
+ constructor(apiKey?: string);
109
+ searchNearby(params: {
110
+ center: {
111
+ value: string;
112
+ isCoordinates: boolean;
113
+ };
114
+ keyword?: string;
115
+ radius?: number;
116
+ openNow?: boolean;
117
+ minRating?: number;
118
+ }): Promise<SearchResponse>;
119
+ searchText(params: {
120
+ query: string;
121
+ locationBias?: {
122
+ latitude: number;
123
+ longitude: number;
124
+ radius?: number;
125
+ };
126
+ openNow?: boolean;
127
+ minRating?: number;
128
+ includedType?: string;
129
+ strictTypeFiltering?: boolean;
130
+ priceLevels?: string[];
131
+ pageSize?: number;
132
+ pageToken?: string;
133
+ languageCode?: string;
134
+ includeRaw?: boolean;
135
+ }): Promise<SearchResponse>;
136
+ getPlaceDetails(placeId: string, maxPhotos?: number, includeRaw?: boolean): Promise<PlaceDetailsResponse>;
137
+ geocode(address: string): Promise<GeocodeResponse>;
138
+ reverseGeocode(latitude: number, longitude: number): Promise<ReverseGeocodeResponse>;
139
+ calculateDistanceMatrix(origins: string[], destinations: string[], mode?: "driving" | "walking" | "bicycling" | "transit", departure_time?: string, avoid_tolls?: boolean, avoid_highways?: boolean): Promise<DistanceMatrixResponse>;
140
+ getDirections(origin: string, destination: string, mode?: "driving" | "walking" | "bicycling" | "transit", departure_time?: string, arrival_time?: string, avoid_tolls?: boolean, avoid_highways?: boolean): Promise<DirectionsResponse>;
141
+ getTimezone(latitude: number, longitude: number, timestamp?: number): Promise<TimezoneResponse>;
142
+ getWeather(latitude: number, longitude: number, type?: "current" | "forecast_daily" | "forecast_hourly", forecastDays?: number, forecastHours?: number): Promise<WeatherResponse>;
143
+ getAirQuality(latitude: number, longitude: number, includeHealthRecommendations?: boolean, includePollutants?: boolean): Promise<AirQualityResponse>;
144
+ getStaticMap(params: {
145
+ center?: string;
146
+ zoom?: number;
147
+ size?: string;
148
+ maptype?: string;
149
+ markers?: string[];
150
+ path?: string[];
151
+ }): Promise<StaticMapResponse>;
152
+ searchAlongRoute(params: {
153
+ textQuery: string;
154
+ origin: string;
155
+ destination: string;
156
+ mode?: string;
157
+ maxResults?: number;
158
+ }): Promise<{
159
+ success: boolean;
160
+ error?: string;
161
+ data?: any;
162
+ }>;
163
+ private resolveLocation;
164
+ exploreArea(params: {
165
+ location: string;
166
+ types?: string[];
167
+ radius?: number;
168
+ topN?: number;
169
+ }): Promise<any>;
170
+ planRoute(params: {
171
+ stops: string[];
172
+ mode?: "driving" | "walking" | "bicycling" | "transit";
173
+ optimize?: boolean;
174
+ departure_time?: string;
175
+ avoid_tolls?: boolean;
176
+ avoid_highways?: boolean;
177
+ }): Promise<any>;
178
+ comparePlaces(params: {
179
+ query: string;
180
+ userLocation?: {
181
+ latitude: number;
182
+ longitude: number;
183
+ };
184
+ limit?: number;
185
+ }): Promise<any>;
186
+ getElevation(locations: Array<{
187
+ latitude: number;
188
+ longitude: number;
189
+ }>): Promise<ElevationResponse>;
190
+ localRankTracker(params: {
191
+ keywords: string[];
192
+ placeId: string;
193
+ center: {
194
+ latitude: number;
195
+ longitude: number;
196
+ };
197
+ gridSize?: number;
198
+ gridSpacing?: number;
199
+ }): Promise<any>;
200
+ private scanKeywordGrid;
201
+ }
202
+
203
+ declare class NewPlacesService {
204
+ private client;
205
+ private readonly apiKey;
206
+ private readonly defaultLanguage;
207
+ private readonly placeFieldMask;
208
+ private readonly searchNearbyFieldMask;
209
+ private readonly searchTextFieldMask;
210
+ constructor(apiKey?: string);
211
+ searchNearby(params: {
212
+ location: {
213
+ lat: number;
214
+ lng: number;
215
+ };
216
+ keyword?: string;
217
+ radius?: number;
218
+ maxResultCount?: number;
219
+ }): Promise<any[]>;
220
+ searchText(params: {
221
+ textQuery: string;
222
+ locationBias?: {
223
+ lat: number;
224
+ lng: number;
225
+ radius?: number;
226
+ };
227
+ openNow?: boolean;
228
+ minRating?: number;
229
+ includedType?: string;
230
+ strictTypeFiltering?: boolean;
231
+ priceLevels?: string[];
232
+ pageSize?: number;
233
+ pageToken?: string;
234
+ languageCode?: string;
235
+ maxResultCount?: number;
236
+ }): Promise<any[]>;
237
+ searchTextFull(params: {
238
+ textQuery: string;
239
+ locationBias?: {
240
+ lat: number;
241
+ lng: number;
242
+ radius?: number;
243
+ };
244
+ openNow?: boolean;
245
+ minRating?: number;
246
+ includedType?: string;
247
+ strictTypeFiltering?: boolean;
248
+ priceLevels?: string[];
249
+ pageSize?: number;
250
+ pageToken?: string;
251
+ languageCode?: string;
252
+ maxResultCount?: number;
253
+ includeRaw?: boolean;
254
+ }): Promise<{
255
+ places: any[];
256
+ nextPageToken?: string;
257
+ fieldMask: string;
258
+ raw?: any;
259
+ }>;
260
+ getPhotoUri(photoName: string, maxWidthPx?: number): Promise<string>;
261
+ getPlaceDetails(placeId: string, includeRaw?: boolean): Promise<any>;
262
+ private transformSearchResult;
263
+ private transformPlaceResponse;
264
+ private extractLegacyPlaceId;
265
+ private isCurrentlyOpen;
266
+ private getLocalTimeComponents;
267
+ private formatOpeningHours;
268
+ private extractErrorMessage;
269
+ }
270
+
271
+ declare const Logger: {
272
+ log: (...args: any[]) => void;
273
+ error: (...args: any[]) => void;
274
+ };
275
+
276
+ export { Logger, NewPlacesService, PlacesSearcher };
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import {
2
+ Logger,
3
+ NewPlacesService,
4
+ PlacesSearcher
5
+ } from "./chunk-VCFXCLJF.js";
6
+ export {
7
+ Logger,
8
+ NewPlacesService,
9
+ PlacesSearcher
10
+ };
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,116 @@
1
+ # Configurazione
2
+
3
+ ## Google Maps API key
4
+
5
+ Ottieni una chiave da <https://console.cloud.google.com/apis/credentials> e abilita sulla chiave i servizi che usi:
6
+
7
+ | Servizio console (nome API) | Tool che lo richiedono | Note |
8
+ | --- | --- | --- |
9
+ | **Places API (New)** (`places-backend-new`) | `maps_search_places`, `maps_search_nearby`, `maps_place_details`, `maps_explore_area`, `maps_compare_places`, `maps_search_along_route`, `maps_local_rank_tracker` | Obbligatoria per la skill `eatspot-discovery`; unica API di ricerca locali usata dal server |
10
+ | **Geocoding API** (`geocoding-backend`) | `maps_geocode`, `maps_reverse_geocode`, `maps_batch_geocode` | Anche fallback interno di risoluzione indirizzi |
11
+ | **Routes API** (`routes`) | `maps_directions`, `maps_distance_matrix`, `maps_plan_route` | |
12
+ | **Time Zone API** (`timezone-backend`) | `maps_timezone` | |
13
+ | **Maps Elevation API** (`elevation-backend`) | `maps_elevation` | In console appare come "Maps Elevation API" |
14
+ | **Maps Static API** (`static-maps-backend`) | `maps_static_map` | Immagine PNG | |
15
+ | **Weather API** (`weather-backend`) | `maps_weather` | Copertura regionale limitata |
16
+ | **Air Quality API** (`airquality`) | `maps_air_quality` | Copertura regionale limitata |
17
+
18
+ Non richieste da questo server: **Maps Grounding Lite** (solo MCP hosted Google) e **Geolocation API** (nessun tool la usa). L'elenco completo con priorità è in [README.md](../README.md#api-google-cloud-richieste).
19
+
20
+ The API key can be supplied as follows (decreasing per-request priority on the HTTP transport):
21
+
22
+ 1. HTTP header `X-Google-Maps-API-Key` — per request, useful in multi-session setups
23
+ 2. HTTP header `Authorization: Bearer <key>` — per request
24
+ 3. CLI flag `--apikey "AIza..."` or environment variable `GOOGLE_MAPS_API_KEY` (or a line in `.env`) — process default
25
+
26
+ ## Variabili d'ambiente
27
+
28
+ Definite in `.env` (copia da `.env.example`):
29
+
30
+ | Variabile | Default | Descrizione |
31
+ | --- | --- | --- |
32
+ | `GOOGLE_MAPS_API_KEY` | — | **Obbligatoria.** Chiave Google Maps |
33
+ | `MCP_SERVER_PORT` | `3000` | Porta del trasporto HTTP |
34
+ | `MCP_SERVER_HOST` | `127.0.0.1` | HTTP bind address. Default migrated from `0.0.0.0` to loopback `127.0.0.1`; set `0.0.0.0` to expose on all interfaces |
35
+ | `GOOGLE_MAPS_ENABLED_TOOLS` | `*` | Allowlist separata da virgole dei tool esposti (riduce il contesto per l'LLM). Esempio: `maps_search_places,maps_place_details,maps_geocode` |
36
+ | `MCP_AUTO_UPDATE_DISABLED` | unset (check enabled by default) | Set `1`, `true`, or `yes` to disable the automatic update check performed at server startup; any other value keeps it enabled |
37
+
38
+ Any command also accepts `--env-file <path>`: it loads exactly that file (exit 1 with a JSON error when missing) and replaces the implicit `.env` discovery chain for that invocation.
39
+
40
+ ## Trasporti
41
+
42
+ ### stdio (predefinito per client locali)
43
+
44
+ ```bash
45
+ scripts/start-mcp.sh # node dist/cli.js start --stdio
46
+ ```
47
+
48
+ stdout è riservato ai messaggi JSON-RPC; tutti i log del server vanno su stderr.
49
+
50
+ ### Streamable HTTP
51
+
52
+ ```bash
53
+ scripts/start-http.sh # http://127.0.0.1:3000/mcp
54
+ MCP_SERVER_PORT=8123 scripts/start-http.sh # porta personalizzata
55
+ scripts/start-http.sh --port 8123 --host 0.0.0.0
56
+ ```
57
+
58
+ Each MCP session (`mcp-session-id`) can carry its own API key via the `X-Google-Maps-API-Key` or `Authorization: Bearer` headers: tool calls use the session key, not the global default. The server binds to `127.0.0.1` by default (migration from `0.0.0.0`); `SIGINT`/`SIGTERM` close all sessions and the listening socket before exit; startup exits non-zero when no instance can listen.
59
+
60
+ ## Registrazione nei client MCP
61
+
62
+ ### pi CLI
63
+
64
+ Voce stdio per `~/.pi/agent/mcp.json` (utente) o `.pi/mcp.json` (progetto, richiede trust):
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "maps": {
70
+ "command": "node",
71
+ "args": ["/percorso/assoluto/a/mcp-google-api/dist/cli.js", "start", "--stdio"],
72
+ "env": { "GOOGLE_MAPS_API_KEY": "${GOOGLE_MAPS_API_KEY}" }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ - `${VAR}` espande variabili d'ambiente: la chiave non resta cablata nel file di configurazione.
79
+ - `pi mcp add maps -- node /percorso/assoluto/a/mcp-google-api/dist/cli.js start --stdio` registra da terminale; `/reload` in sessione dopo modifiche esterne; `/mcp` ispeziona la connessione.
80
+
81
+ ### OpenClaw
82
+
83
+ ```bash
84
+ openclaw mcp set maps '{"command":"node","args":["/percorso/assoluto/a/mcp-google-api/dist/cli.js","start","--stdio"],"env":{"GOOGLE_MAPS_API_KEY":"AIza..."},"description":"Google Maps MCP"}'
85
+ ```
86
+
87
+ - Alternativa senza installazione: `"command":"npx","args":["-y","mcp-google-api","start","--stdio"]`.
88
+ - Ricaricare il gateway o riavviare la sessione agente dopo la modifica; `openclaw mcp list` / `openclaw mcp show maps` per verificare.
89
+
90
+ ### Prerequisiti chiave e API Google
91
+
92
+ - **Una sola chiave** `GOOGLE_MAPS_API_KEY` (priorità di risoluzione in [Google Maps API key](#google-maps-api-key)); servizi da abilitare e sottoinsieme minimo (Places API (New) + Geocoding API): tabella in [README.md](../README.md#api-google-cloud-richieste).
93
+ - Limitare i tool esposti con [`GOOGLE_MAPS_ENABLED_TOOLS`](#variabili-dambiente).
94
+ - Verifica post-installazione: `node dist/cli.js doctor` e `node dist/cli.js doctor --live --apikey "AIza..."`.
95
+
96
+ ## Comandi CLI
97
+
98
+ ```bash
99
+ node dist/cli.js start --stdio # stdio server
100
+ node dist/cli.js start --port 3000 --host 127.0.0.1 # HTTP server (loopback default)
101
+ node dist/cli.js setup --write-env # onboarding: checks + .env scaffold (no network)
102
+ node dist/cli.js exec search-places '{"query":"pizzeria Torino"}' # one tool call (params is a JSON string)
103
+ node dist/cli.js doctor # local diagnostics (no network)
104
+ node dist/cli.js doctor --live --apikey "AIza..." # + real probes (all three APIs)
105
+ node dist/cli.js doctor --live --probes geocoding,routes # + selected probes only
106
+ node dist/cli.js --env-file /path/to/.env doctor # explicit environment file
107
+ node dist/cli.js snippet --client pi # registration snippet (stdout JSON, no files written)
108
+ ```
109
+
110
+ ## Diagnostica
111
+
112
+ ```bash
113
+ node dist/cli.js doctor
114
+ ```
115
+
116
+ Verifica versione Node, presenza della API key e (con `--live`) effettua chiamate reali ai servizi Google. The Node floor is `>= 24`, aligned with `package.json` `engines`; the local report also prints the effective configuration (masked key, per-field sources) and the Google APIs required by each enabled tool.
package/docs/TOOLS.md ADDED
@@ -0,0 +1,75 @@
1
+ # Catalogo dei tool
2
+
3
+ 18 tool MCP, tutti prefissati `maps_`. L'elenco esposto può essere ridotto con `GOOGLE_MAPS_ENABLED_TOOLS` (vedi [CONFIGURATION.md](CONFIGURATION.md)).
4
+
5
+ ## Ricerca e dettaglio locali (Places API — New)
6
+
7
+ ### `maps_search_places`
8
+
9
+ Text Search (New) con **filtri server-side** — è il tool principale per ricerche strutturate.
10
+
11
+ | Parametro | Tipo | Note |
12
+ | --- | --- | --- |
13
+ | `query` | string (obbligatorio) | Query testuale libera |
14
+ | `priceLevels` | enum array | Filtro economico cumulativo lato API: `PRICE_LEVEL_FREE`, `PRICE_LEVEL_INEXPENSIVE`, `PRICE_LEVEL_MODERATE`, `PRICE_LEVEL_EXPENSIVE`, `PRICE_LEVEL_VERY_EXPENSIVE` |
15
+ | `minRating` | number 1.0–5.0 | Filtro rating applicato dall'API |
16
+ | `includedType` | string | **Un solo tipo per chiamata** (contratto Text Search), es. `restaurant`, `cafe`, `bakery` |
17
+ | `strictTypeFiltering` | boolean | Matching rigoroso di `includedType` |
18
+ | `openNow` | boolean | Solo locali attualmente aperti |
19
+ | `locationBias` | oggetto | `{ latitude, longitude, radius? }` (raggio in metri, default 5000) |
20
+ | `pageSize` | 1–20 | Tetto risultati per chiamata (tradotto in `maxResultCount`). Nota SDK: il proto di `@googlemaps/places` (fino a 3.1.0) non include `page_size`/`page_token` → **paginazione oltre 20 risultati/chiamata non disponibile**; per più risultati usare query diverse |
21
+ | `pageToken` | string | Token della pagina successiva |
22
+ | `languageCode` | string | Lingua risultati (`it`, `en`, …) |
23
+ | `includeRaw` | boolean | Allega il payload originale: `raw_place` per ogni risultato, `raw_response` dell'intera pagina e la `field_mask` realmente inviata |
24
+
25
+ Output: `{ results[], nextPageToken?, field_mask, raw_response? }`. Ogni risultato include `place_id`, `name`, `address`, `location`, `primary_type`, `types`, `business_status`, `google_maps_uri`, `price_level`, **`price_range` (struttura Money originale: `startPrice`/`endPrice` → `currencyCode`/`units`/`nanos`)**, `rating`, `total_ratings`, `regular_opening_hours`, `current_opening_hours`.
26
+
27
+ ### `maps_search_nearby`
28
+
29
+ Search Nearby (New) attorno a un centro (`center.value` indirizzo/landmark/coordinate, `radius` in metri), con filtro `keyword` (tipo), `openNow`, `minRating` (post-filtro).
30
+
31
+ ### `maps_place_details`
32
+
33
+ Place Details (New) per `placeId`: contatti, sito, orari (regular + current con `periods`), recensioni con attribuzione, foto (`maxPhotos` 0–10), `price_range`, `business_status`, `types`. Con `includeRaw: true` allega `raw_place` e `field_mask`.
34
+
35
+ ### `maps_explore_area`
36
+
37
+ Esplora una zona per categorie (default `restaurant`, `cafe`, `tourist_attraction`) con i top-N per tipo.
38
+
39
+ ### `maps_compare_places`
40
+
41
+ Confronta fino a N locali da una query: tipo, orari, contatti, prezzi, opzioni.
42
+
43
+ ### `maps_search_along_route`
44
+
45
+ Cerca locali lungo un percorso (origine → destinazione).
46
+
47
+ ### `maps_local_rank_tracker`
48
+
49
+ Traccia il rank di un locale su una griglia di punti (ARP/ATRP/Solv) per una keyword.
50
+
51
+ ## Geocoding
52
+
53
+ - `maps_geocode` — indirizzo/nome → coordinate + `place_id`
54
+ - `maps_reverse_geocode` — coordinate → indirizzo
55
+ - `maps_batch_geocode` — fino a 25 richieste batch
56
+
57
+ ## Navigazione
58
+
59
+ - `maps_directions` — percorso A→B con tappe, modalità, evitamenti
60
+ - `maps_distance_matrix` — matrice origini/destinazioni (Routes API)
61
+ - `maps_plan_route` — pianificazione multi-tappa con ottimizzazione tappe intermedie
62
+
63
+ ## Ambiente
64
+
65
+ - `maps_elevation` — quota di punti o profilo di un tracciato
66
+ - `maps_timezone` — fuso orario e ora locale
67
+ - `maps_weather` — meteo corrente/previsioni
68
+ - `maps_air_quality` — qualità dell'aria
69
+ - `maps_static_map` — mappa statica (immagine PNG)
70
+
71
+ ## Convenzioni comuni
72
+
73
+ - Coordinate in input: `{ latitude, longitude }` oppure stringa `"lat,lng"` dove indicato.
74
+ - Tutti i tool restituiscono JSON in `content[0].text`; errori con `isError: true`.
75
+ - I campi grezzi dell'API (es. `price_range` Money) sono **conservati verbatim**, mai convertiti in numeri semplici.
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "mcp-google-api",
3
+ "version": "0.21.0",
4
+ "description": "Google API MCP server — 18 Google Maps tools for AI agents: geocoding, Places API (New) search with server-side filters, directions, distance matrix and more, via stdio or Streamable HTTP",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "bin": {
12
+ "mcp-google-api": "dist/cli.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src/resources/skills",
17
+ "README.md",
18
+ "CHANGELOG.md",
19
+ "docs",
20
+ ".env.example"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "dev": "tsup --watch",
25
+ "start": "node dist/cli.js",
26
+ "typecheck": "tsc --noEmit",
27
+ "test": "tsx --test tests/*.unit.test.ts",
28
+ "test:e2e": "tsx tests/smoke.test.ts",
29
+ "check:publish-version": "node scripts/check-publish-version.mjs",
30
+ "verify": "npm run typecheck && npm test && npm run build",
31
+ "prepublishOnly": "npm run check:publish-version && npm run build"
32
+ },
33
+ "engines": {
34
+ "node": ">=24"
35
+ },
36
+ "keywords": [
37
+ "google",
38
+ "google-maps",
39
+ "maps",
40
+ "mcp",
41
+ "mcp-server",
42
+ "model-context-protocol",
43
+ "places",
44
+ "places-api-new",
45
+ "geocoding",
46
+ "directions"
47
+ ],
48
+ "license": "GPL-3.0-only",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/Ogekuri/mcp-google-api.git"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/Ogekuri/mcp-google-api/issues"
55
+ },
56
+ "homepage": "https://github.com/Ogekuri/mcp-google-api#readme",
57
+ "dependencies": {
58
+ "@googlemaps/google-maps-services-js": "^3.4.0",
59
+ "@googlemaps/places": "^3.1.0",
60
+ "@modelcontextprotocol/sdk": "^1.32.0",
61
+ "dotenv": "^16.4.7",
62
+ "express": "^4.21.2",
63
+ "yargs": "^17.7.2",
64
+ "zod": "^3.25.0"
65
+ },
66
+ "devDependencies": {
67
+ "@types/express": "^4.17.21",
68
+ "@types/node": "^24.0.0",
69
+ "@types/yargs": "^17.0.33",
70
+ "tsup": "^8.4.0",
71
+ "tsx": "^4.19.2",
72
+ "typescript": "^5.7.3",
73
+ "typescript-eslint": "^8.72.1"
74
+ }
75
+ }
@@ -0,0 +1,45 @@
1
+ # Skills Resources — Authoring Guide
2
+
3
+ This directory ships LLM agent skills for the `mcp-google-api` MCP server. Skills are inert instruction assets: no runtime module under `src/` imports them, so adding a skill never requires source-code changes, rebuilds, or test changes.
4
+
5
+ ## Layout contract
6
+
7
+ ```
8
+ src/resources/skills/
9
+ ├── manifest.json # machine-readable registry of all skills
10
+ ├── README.md # this authoring guide
11
+ └── <skill-id>/ # exactly one skill per directory
12
+ ├── SKILL.md # the skill (English, self-contained)
13
+ └── DEBUG.md # optional debug overlay (never part of SKILL.md)
14
+ ```
15
+
16
+ Debug instructions are never embedded in `SKILL.md`. A skill that needs a debug section ships it in a dedicated overlay file declared by the manifest `debug` field; `mcp-google-api install` copies content files only and appends the overlay to the installed `SKILL.md` solely when `--debug-skills` is passed. The default install therefore produces a skill with no debug instructions.
17
+
18
+ ## SKILL.md format (Agent Skills compatible: pi, OpenClaw, and similar hosts)
19
+
20
+ - UTF-8 Markdown file starting with YAML front matter delimited by `---` lines.
21
+ - Front matter fields:
22
+ - `name`: 1–64 characters, lowercase ASCII letters, digits, hyphens; no leading, trailing, or consecutive hyphens; MUST match the containing directory name.
23
+ - `description`: non-empty, ≤ 1024 characters; states the capability and the triggering tasks, with important routing cues first.
24
+ - Body: operative instructions in English, self-contained, ≤ ~500 lines. Support files (if ever needed) go under canonical subdirectories (`scripts/`, `references/`, `assets/`, `images/`); `SKILL.md` stays the only file at the skill root.
25
+ - Skills that operate on Google Maps data MUST obtain every data value through the mcp-google-api tools listed in `PRJ-001` of `usereq/docs/REQUIREMENTS.md` and MUST forbid fabricated or simulated data presented as API-verified.
26
+
27
+ ## Adding a new skill
28
+
29
+ 1. Create `src/resources/skills/<skill-id>/SKILL.md` following the format above.
30
+ 2. Optional: create `src/resources/skills/<skill-id>/DEBUG.md` holding every debug instruction, and declare it as `"debug": "DEBUG.md"` in the manifest entry. Keep `SKILL.md` free of debug instructions.
31
+ 3. Append one entry to `manifest.json` (`id`, `name`, `version`, `language`, `path`, `description`, optional `debug`, optional `adaptedFrom`). Keep `name` equal to `id`, `path` relative to this directory, and `debug` relative to the skill directory.
32
+ 4. Commit both changes together. No other project file needs to change.
33
+
34
+ ## Registry entry schema
35
+
36
+ | Field | Required | Description |
37
+ |---|---|---|
38
+ | `id` | yes | Stable skill identifier; equal to `name` and the directory name. |
39
+ | `name` | yes | Agent Skills front-matter name; must match `id`. |
40
+ | `version` | yes | Semantic version of the skill (skill-local, independent of package version). |
41
+ | `language` | yes | ISO language code of the skill body (`en` by policy). |
42
+ | `path` | yes | Path of `SKILL.md` relative to this directory. |
43
+ | `description` | yes | Short routing summary; consistent with the front-matter `description`. |
44
+ | `debug` | no | Debug overlay path relative to the skill directory (convention: `DEBUG.md`); appended to the installed `SKILL.md` only with `install --debug-skills`. |
45
+ | `adaptedFrom` | no | Provenance note when the skill derives from an external source document. |
@@ -0,0 +1,71 @@
1
+ # EatSpot Discovery — DEBUG Overlay
2
+
3
+ Appended to `SKILL.md` only when the skill is installed with `mcp-google-api install --debug-skills`.
4
+ Without that option this file is never installed, so the installed skill contains no debug instructions.
5
+ The overlay is additive: it never alters PART I — CORE of `SKILL.md`.
6
+
7
+ ## PART II — DEBUG (temporary, removable)
8
+
9
+ ### D1. Permanent activation
10
+
11
+ For every search set `DEBUG_ENABLED = TRUE`. The DEBUG section MUST always appear at the bottom of the response: zero results, one or more conformant results, interrupted searches, unavailable API. Part II Section D MUST be the last output section. The DEBUG MUST NOT modify filters or the search.
12
+
13
+ ### D2. Mandatory provenance
14
+
15
+ Every raw datum MUST be tied to an actual call. Calling `raw` an object you reconstructed is forbidden. Distinguish `API_VERIFIED` and `MCP_SERVER_UNAVAILABLE`. If the origin is not the mcp-google-api server, do not simulate Places API fields or structures.
16
+
17
+ ### D3. Call register
18
+
19
+ For every actually executed tool call record: progressive call id; tool name; Grade phase; real query and parameters; `field_mask` actually applied (read it from the tool's `field_mask` output); request status; original payload received; errors; validations performed. Do not invent calls or parameters. Do not add credentials or tokens. Do not reconstruct missing responses.
20
+
21
+ ### D4. JSON format
22
+
23
+ Section D contains exactly one valid JSON array; each element represents one authentic executed call. Diagnostic schema:
24
+
25
+ ```json
26
+ [
27
+ {
28
+ "call_id": 1,
29
+ "source_mode": "API_VERIFIED",
30
+ "tool": "maps_search_places",
31
+ "phase_grade": "A",
32
+ "request_sent": {},
33
+ "field_mask_sent": [],
34
+ "raw_response": null,
35
+ "raw_accessibility": "NOT_ACCESSIBLE",
36
+ "validations": [],
37
+ "anomalies": [],
38
+ "error": null
39
+ }
40
+ ]
41
+ ```
42
+
43
+ This is only the schema, never a real call. In an actual entry: `request_sent` contains the effective parameters; `field_mask_sent` matches the real applied mask; `raw_response` contains the authentic payload (from `raw`/`raw_place` with `includeRaw: true`) or stays `null` when inaccessible; `validations` lists agent interpretations; `anomalies` lists discrepancies. If no call was executed, the array MUST be `[]`; never fabricate a call to avoid an empty array; Section C explains the unavailability.
44
+
45
+ ### D5. Validations separate from raw data
46
+
47
+ For each actually returned candidate, record separately: `place_id`; `raw_place_index`; `rating_interpreted`; `user_ratings_total_interpreted`; `grade_assigned`; `primary_type_raw`; `category`; `price_level_raw`; `price_range_raw`; `max_price_computed`; `budget_max`; `economic_status`; `final_status`; `exclusion_reasons`. Never copy interpreted values into `raw_response`. Never alter original structures.
48
+
49
+ ### D6. Error codes to detect
50
+
51
+ `MCP_SERVER_UNAVAILABLE`, `TOOL_CALL_SIMULATED`, `RAW_RESPONSE_RECONSTRUCTED`, `RAW_SCHEMA_NON_CONFORMANT`, `PLACE_ID_MISMATCH`, `PRICE_LEVEL_DIVERGENT`, `PRICE_RANGE_DIVERGENT`, `USER_RATING_COUNT_DIVERGENT`, `PRIMARY_TYPE_DIVERGENT`, `RESPONSE_INCOMPATIBLE_WITH_PRICE_LEVELS`, `PRICE_LEVELS_FILTER_NOT_APPLIED`, `PRICE_EXCEEDS_BUDGET`, `RATING_INSUFFICIENT`, `REVIEWS_INSUFFICIENT`, `GRADE_WRONG`, `HOURS_INCOMPATIBLE`, `OUT_OF_RADIUS`, `FIELD_MASK_INCOMPLETE`, `API_RESPONSE_NOT_ACCESSIBLE`. Register only ascertained anomalies; never invent error codes to mask missing data.
52
+
53
+ ### D7. Critical test — Osteria Veglio
54
+
55
+ When a response for Place ID `ChIJY2oWYByu0hIRZXOOaToUTA8` arrives, verify value consistency without overwriting. If a response presents `price_level = PRICE_LEVEL_EXPENSIVE`, `price_range.endPrice.units = "70"`, `user_ratings_total = 910`, you MUST NOT publish `PRICE_LEVEL_MODERATE`, "maximum price 30 EUR", or "1703 reviews" as original values of that response. If two real calls conflict, register the conflict and re-verify with `maps_place_details`; never resolve by editing the payload.
56
+
57
+ ### D8. API filter verification
58
+
59
+ For every `maps_search_places` call record: `priceLevels` actually sent; `minRating` actually sent; `includedType` actually used; `strictTypeFiltering` when present. If `priceLevels` admits only INEXPENSIVE and MODERATE, register as an anomaly any EXPENSIVE candidate in the response; do not force-normalize it; do not declare a call validly filtered when it was only simulated.
60
+
61
+ ### D9. Completeness control and bounded capture
62
+
63
+ The debug MUST preserve all accessible payloads within response-size limits. Declare partial payloads and truncated records explicitly; never present a partial response as complete. Do not deduplicate raw records: deduplication concerns final selection, not the call register.
64
+
65
+ Bounded capture: before persisting any register entry, apply the strictest serialized-size limit in force in the execution environment (response limits, tool-result limits, intermediate-storage limits). When the serialized `raw_response` exceeds that limit, persist a declared truncation instead of the full payload: keep the validation-relevant segment — for `maps_search_places`, `raw_place` of ALL returned results (never a subset of results) plus each result's `name`, `place_id`, `rating`, and `user_ratings_total` — and add an explicit truncation declaration naming the omitted parts (reviews, photos, editorial summaries) and the kept ranges. The declaration is agent metadata inside the register entry (e.g. `"raw_response_note": "TRUNCATED: reviews/photos/editorial omitted; all N results kept"`); it MUST never be presented as API data.
66
+
67
+ Register-entry integrity: before emitting the final JSON array, verify that every referenced register entry exists and holds the recorded payload. When an entry is missing — interrupted capture, size-limit abort, or lost intermediate state — re-execute the tool call; never dereference fields of a missing entry and never reconstruct the payload. When re-execution is impossible, emit the entry with `raw_response: null`, `raw_accessibility: "NOT_ACCESSIBLE"`, anomaly `API_RESPONSE_NOT_ACCESSIBLE`, and the concrete interruption reason in `error`.
68
+
69
+ ### D10. Final output
70
+
71
+ Produce in order: A. Search summary; B. Validated venues; C. Notes and limitations; D. DEBUG — GOOGLE PLACES API VIA mcp-google-api. Section D MUST end with one valid JSON array and nothing may follow it. Excluded candidates may appear only as technical data in Section D. The DEBUG does not modify functional logic. Removing Part II entirely MUST leave PART I — CORE fully operational.