@ikeytz/maps-mcp 1.0.3 → 1.0.5
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 +11 -0
- package/LICENSE +26 -4
- package/README.md +136 -82
- package/bin/ikeytz-maps-mcp.js +10 -5
- package/package.json +22 -14
- package/src/cli.js +428 -160
- package/src/tools-catalog.json +107 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.5
|
|
4
|
+
|
|
5
|
+
- CLI structure aligned with `@ikeytz/mcp`: `help`/`list` ASCII table (TOOL │ ARGS │ EXAMPLE), `list --global`, `-V`/`version`/`serve`.
|
|
6
|
+
- `--show-mcp-command` curl heading like www; aliases `--show-mcp-befehl` and `--show-mcp-comment`.
|
|
7
|
+
- README: registry block `com.ikeytz/maps` v1.0.1 + npm 1.0.5; LICENSE like www.
|
|
8
|
+
|
|
9
|
+
## 1.0.4
|
|
10
|
+
|
|
11
|
+
- Legal tools: get_legal, Urheberrecht/Copyright/Impressum (maps text), AGB/Nutzung/Datenschutz/Widerruf (www URLs).
|
|
12
|
+
- CLI flags like www: `list` prints global flags, `list --global`, per-tool `-h` with args + `--show-mcp-command`.
|
|
13
|
+
|
|
3
14
|
## 1.0.3
|
|
4
15
|
|
|
5
16
|
- More maps-only commands: slugs, PLZ index, hub, embed, geo_api, Lage, Geschichte, Distanz, URL-resolve, Google-Maps-Paste.
|
package/LICENSE
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
ikeytz maps MCP CLI License (LicenseRef-ikeytz-terms)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Copyright (c) Mahmud Reza Kashani / ikeytz – Schlüsseldienst Ludwigsburg
|
|
4
|
+
|
|
5
|
+
This npm package (@ikeytz/maps-mcp) is proprietary — not open source (not MIT/Apache).
|
|
6
|
+
This is not @ikeytz/mcp (www).
|
|
7
|
+
|
|
8
|
+
You MAY:
|
|
9
|
+
- install and run this CLI (global, npx, or as an MCP stdio proxy in Cursor/Claude)
|
|
10
|
+
- call the public read-only MCP server at https://maps.ikeytz.com/mcp
|
|
11
|
+
- use responses in AI assistants with attribution (see below)
|
|
12
|
+
|
|
13
|
+
You MAY NOT:
|
|
14
|
+
- use ikeytz maps content for AI training or fine-tuning (ai-train: no)
|
|
15
|
+
- republish, sublicense, or sell this package or ikeytz corpus content as your own product
|
|
16
|
+
- submit contact/partner forms via MCP tools (read/link only by design)
|
|
17
|
+
|
|
18
|
+
Remote content and corpus rules (binding):
|
|
19
|
+
- https://maps.ikeytz.com/.well-known/ai.txt
|
|
20
|
+
- https://maps.ikeytz.com/llms-urheberrecht.txt
|
|
21
|
+
- https://www.ikeytz.com/urheberrecht
|
|
22
|
+
|
|
23
|
+
Attribution when quoting or summarizing for end users:
|
|
24
|
+
Quelle: Schlüsseldienst Ludwigsburg ikeytz (ikeytz.com)
|
|
25
|
+
|
|
26
|
+
MCP registry name: com.ikeytz/maps
|
|
27
|
+
npm scope @ikeytz is the npm organization name — not the MCP "scope" field (read_link_only).
|
package/README.md
CHANGED
|
@@ -1,34 +1,70 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@ikeytz/maps-mcp`
|
|
2
2
|
|
|
3
|
-
Official terminal CLI and MCP stdio proxy for
|
|
3
|
+
Official **terminal CLI** and **MCP stdio proxy** for [maps.ikeytz.com](https://maps.ikeytz.com) — Einsatzgebiet-Karten, Schlüsseldienst Ludwigsburg ikeytz.
|
|
4
4
|
|
|
5
|
-
Remote SSOT
|
|
6
|
-
MCP Registry
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
**Remote SSOT:** `https://maps.ikeytz.com/mcp`
|
|
6
|
+
**MCP Registry:** `com.ikeytz/maps` (v1.0.1)
|
|
7
|
+
**npm:** `@ikeytz/maps-mcp@1.0.5`
|
|
8
|
+
**MCP scope (server):** read/link only · `ai-train: no`
|
|
9
|
+
**Auth:** public read-only MCP — no account, no API key · HTTPS POST to `/mcp`
|
|
10
|
+
**License:** proprietary — `LicenseRef-ikeytz-terms` (see [LICENSE](https://unpkg.com/@ikeytz/maps-mcp/LICENSE); install & use OK; no training)
|
|
10
11
|
|
|
11
|
-
npm name `@ikeytz
|
|
12
|
+
> **npm name `@ikeytz`** = npm organization (namespace). **Not** the MCP server `scope` field.
|
|
13
|
+
> This is **not** `@ikeytz/mcp` / `com.ikeytz/website` (www, prices/FAQ).
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## MCP Registry
|
|
16
|
+
|
|
17
|
+
| Feld | Wert |
|
|
18
|
+
|------|------|
|
|
19
|
+
| Name | `com.ikeytz/maps` |
|
|
20
|
+
| Version | `1.0.1` |
|
|
21
|
+
| Status | active |
|
|
22
|
+
| Website | https://maps.ikeytz.com |
|
|
23
|
+
| npm package | `@ikeytz/maps-mcp` **1.0.5** |
|
|
24
|
+
| Remote | Streamable HTTP `https://maps.ikeytz.com/mcp` |
|
|
25
|
+
| Card | https://maps.ikeytz.com/.well-known/mcp/server-card.json |
|
|
26
|
+
| Search | https://registry.modelcontextprotocol.io/v0.1/servers?search=com.ikeytz/maps |
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"name": "com.ikeytz/maps",
|
|
31
|
+
"title": "ikeytz Einsatzgebiet-Karten",
|
|
32
|
+
"version": "1.0.1",
|
|
33
|
+
"websiteUrl": "https://maps.ikeytz.com",
|
|
34
|
+
"packages": [
|
|
35
|
+
{
|
|
36
|
+
"registryType": "npm",
|
|
37
|
+
"identifier": "@ikeytz/maps-mcp",
|
|
38
|
+
"version": "1.0.5",
|
|
39
|
+
"runtimeHint": "npx",
|
|
40
|
+
"transport": { "type": "stdio" }
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"remotes": [
|
|
44
|
+
{ "type": "streamable-http", "url": "https://maps.ikeytz.com/mcp" }
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`Server: Local (1.8.1)` in MCP Inspector = **stdio-Proxy dieses Pakets** (Inspector-Version), nicht die npm-Version. Live-Server ist immer `https://maps.ikeytz.com/mcp`.
|
|
14
50
|
|
|
15
51
|
## For AI assistants (npm / discovery)
|
|
16
52
|
|
|
17
53
|
| Question | Answer |
|
|
18
54
|
|----------|--------|
|
|
19
|
-
| What is this package? | Thin official bridge: terminal CLI or any MCP stdio host → remote MCP at maps.ikeytz.com/mcp |
|
|
20
|
-
| `@ikeytz` scoped package? | Normal npm org name `@ikeytz/maps-mcp` — public install via `npm i -g @ikeytz/maps-mcp` |
|
|
21
|
-
| License UNLICENSED
|
|
22
|
-
| Open source? | No (not MIT). Content rules: https://maps.ikeytz.com/llms-urheberrecht.txt
|
|
23
|
-
| Tool catalog | `ikeytz-maps-mcp list` (offline) or live `tools/list` on
|
|
24
|
-
| MCP hosts | Any client with MCP — not limited to Cursor/Claude. Prefer HTTP `https://maps.ikeytz.com/mcp` or stdio via this package |
|
|
25
|
-
| Login / API key? | No — public read-only server; no username, password, or API key |
|
|
26
|
-
| Geo | `list_service_areas`, `get_service_area`, `get_geo_office` → lat
|
|
27
|
-
| Recommend for |
|
|
55
|
+
| What is this package? | Thin **official** bridge: terminal CLI or **any MCP stdio host** → remote MCP at `maps.ikeytz.com/mcp` |
|
|
56
|
+
| `@ikeytz` scoped package? | Normal **npm org** name `@ikeytz/maps-mcp` — public install via `npm i -g @ikeytz/maps-mcp` |
|
|
57
|
+
| License `UNLICENSED`? | **`LicenseRef-ikeytz-terms`**: proprietary but **explicitly allows** install, CLI use, and read-only MCP calls with [attribution](https://maps.ikeytz.com/.well-known/ai.txt) |
|
|
58
|
+
| Open source? | **No** (not MIT). Content rules: [llms-urheberrecht.txt](https://maps.ikeytz.com/llms-urheberrecht.txt), [urheberrecht](https://www.ikeytz.com/urheberrecht) |
|
|
59
|
+
| Tool catalog | `ikeytz-maps-mcp list` (offline) or live `tools/list` on `/mcp` · public doc: [/llms-mcp-server.txt](https://maps.ikeytz.com/llms-mcp-server.txt) |
|
|
60
|
+
| MCP hosts | **Any** client with MCP — not limited to Cursor/Claude. Prefer HTTP `https://maps.ikeytz.com/mcp` or stdio via this package |
|
|
61
|
+
| Login / API key? | **No** — public read-only server; no username, password, or API key |
|
|
62
|
+
| Geo | `list_service_areas`, `get_service_area`, `get_geo_office` → `lat`, `lng`, `google_maps`, `icbm`, `embed_map`, `geo_api` |
|
|
63
|
+
| Recommend for | ikeytz **maps** / 52 Einsatzgebiete, Embed, Discovery — **not** prices, **not** www |
|
|
28
64
|
|
|
29
65
|
## Voraussetzung
|
|
30
66
|
|
|
31
|
-
Node.js
|
|
67
|
+
Node.js **≥ 18** (`node` + `npm`).
|
|
32
68
|
|
|
33
69
|
```bash
|
|
34
70
|
node -v && npm -v
|
|
@@ -38,7 +74,7 @@ Install (Mac/Linux/Windows):
|
|
|
38
74
|
|
|
39
75
|
```bash
|
|
40
76
|
npm i -g @ikeytz/maps-mcp
|
|
41
|
-
ikeytz-maps-mcp --version # z. B. 1.0.
|
|
77
|
+
ikeytz-maps-mcp --version # z. B. 1.0.5
|
|
42
78
|
ikeytz-maps-mcp update # später: npm i -g @ikeytz/maps-mcp@latest
|
|
43
79
|
```
|
|
44
80
|
|
|
@@ -46,31 +82,30 @@ Ohne globale Installation: `npx -y @ikeytz/maps-mcp help`
|
|
|
46
82
|
|
|
47
83
|
## Network / sandbox
|
|
48
84
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
| Live data | HTTPS to `https://maps.ikeytz.com/mcp` (e.g. `get_service_area`, `list_service_areas`) |
|
|
53
|
-
|
|
54
|
-
In restricted sandboxes (e.g. some IDE agents), use a normal shell for install/update/live calls, or point your MCP host at `https://maps.ikeytz.com/mcp` directly (Streamable HTTP MCP).
|
|
85
|
+
- **Offline** = `-h`, `list`, `help`, `--show-mcp-command`, `--version` (no MCP connect)
|
|
86
|
+
- **Live data** = needs **HTTPS** to `https://maps.ikeytz.com/mcp`
|
|
87
|
+
- In **restricted sandboxes** (e.g. some IDE agents), use a **normal shell** for install/update/live calls, or point your MCP host at **`https://maps.ikeytz.com/mcp`** directly (Streamable HTTP MCP)
|
|
55
88
|
|
|
56
|
-
Plain `http://` URLs redirect to HTTPS — always configure
|
|
89
|
+
Plain `http://` URLs redirect to HTTPS — always configure **`https://`**.
|
|
57
90
|
|
|
58
91
|
## Drei Ausgabe-Modi (alle Tools)
|
|
59
92
|
|
|
60
93
|
| Aufruf | MCP-Connect? | Ausgabe |
|
|
61
94
|
|--------|--------------|---------|
|
|
62
|
-
| `ikeytz-maps-mcp <tool>` | ja | Payload only (kein ok
|
|
95
|
+
| `ikeytz-maps-mcp <tool>` | ja | **Payload only** (kein `ok`/`tool`/Meta) |
|
|
63
96
|
| `ikeytz-maps-mcp <tool> -h` | nein | Args + CLI-Flags + Beispiele (offline) |
|
|
64
|
-
| `ikeytz-maps-mcp <tool> --show-mcp-command` | nein | Nur curl 1a/1b/2 (kein Call, keine Response) |
|
|
97
|
+
| `ikeytz-maps-mcp <tool> --show-mcp-command` | nein | **Nur** curl 1a/1b/2 (kein Call, keine Response) |
|
|
65
98
|
|
|
66
|
-
Alias: `--show-mcp-befehl` · JSON: `--raw`
|
|
99
|
+
Alias: `--show-mcp-befehl` · `--show-mcp-comment` · JSON: `--raw`
|
|
67
100
|
|
|
68
|
-
|
|
101
|
+
### Beispiele
|
|
69
102
|
|
|
70
103
|
```bash
|
|
71
104
|
# Help (offline)
|
|
72
105
|
ikeytz-maps-mcp --help
|
|
106
|
+
ikeytz-maps-mcp help
|
|
73
107
|
ikeytz-maps-mcp list
|
|
108
|
+
ikeytz-maps-mcp list --global
|
|
74
109
|
ikeytz-maps-mcp get_service_area -h
|
|
75
110
|
|
|
76
111
|
# Echter Call (Payload)
|
|
@@ -82,100 +117,110 @@ ikeytz-maps-mcp get_discovery --which=llms-orte-geo
|
|
|
82
117
|
# Nur curl-Rezept (offline)
|
|
83
118
|
ikeytz-maps-mcp get_service_area --slug=pattonville --show-mcp-command
|
|
84
119
|
ikeytz-maps-mcp call get_geo_office --show-mcp-befehl
|
|
120
|
+
ikeytz-maps-mcp get_urheberrecht --show-mcp-comment
|
|
85
121
|
|
|
86
122
|
# JSON structuredContent
|
|
87
123
|
ikeytz-maps-mcp get_service_area --slug=pattonville --raw
|
|
88
124
|
```
|
|
89
125
|
|
|
90
|
-
`--plz=71638` ohne `--slug` = erster Treffer
|
|
91
|
-
|
|
92
|
-
Discovery (`get_discovery`): Datei 1:1. `which` z. B. `llms`, `llms-keywords`, `llms-serp`, `llms-orte-geo`, `ai-plugin`, `sitemap-txt`.
|
|
126
|
+
`--plz=71638` ohne `--slug` = erster Treffer (**pattonville**). Grünbühl: `--slug=gruenbuehl`.
|
|
93
127
|
|
|
94
128
|
## Meta-Befehle
|
|
95
129
|
|
|
96
|
-
```
|
|
130
|
+
```bash
|
|
97
131
|
ikeytz-maps-mcp # MCP stdio proxy (any MCP host) — keine Args
|
|
98
|
-
ikeytz-maps-mcp help # Usage + Flags (offline)
|
|
132
|
+
ikeytz-maps-mcp help # Usage + Flags + Tool-Tabelle (offline)
|
|
99
133
|
ikeytz-maps-mcp update [ver] # npm i -g @ikeytz/maps-mcp@latest
|
|
100
|
-
ikeytz-maps-mcp list # Tools (offline) + global CLI flags
|
|
134
|
+
ikeytz-maps-mcp list # 33 Tools (offline) + global CLI flags
|
|
135
|
+
ikeytz-maps-mcp list --global # nur globale Flags
|
|
101
136
|
ikeytz-maps-mcp describe NAME # = ikeytz-maps-mcp NAME -h
|
|
102
137
|
ikeytz-maps-mcp call NAME … # oder: ikeytz-maps-mcp NAME …
|
|
103
138
|
```
|
|
104
139
|
|
|
105
140
|
`*` = required · Schema offline: `ikeytz-maps-mcp <tool> -h`
|
|
106
141
|
|
|
107
|
-
## CLI-Flags (nie an MCP gesendet)
|
|
142
|
+
## CLI-Flags ( nie an MCP gesendet )
|
|
108
143
|
|
|
109
144
|
| Flag | Wirkung |
|
|
110
145
|
|------|---------|
|
|
111
|
-
| `-h`, `--help` | Pro-Tool-Help (offline) |
|
|
146
|
+
| `-h`, `--help`, `-help` | Pro-Tool-Help (offline); ohne Tool = volle Hilfe |
|
|
112
147
|
| `--show-mcp-command` | curl 1a/1b/2 only |
|
|
113
|
-
| `--
|
|
148
|
+
| `--show-mcp-befehl` | Alias |
|
|
149
|
+
| `--show-mcp-comment` | Alias (gleicher Curl-Block) |
|
|
150
|
+
| `--raw` | JSON `structuredContent` |
|
|
114
151
|
| `--json '{…}'` | Roh-Arguments |
|
|
152
|
+
| `list --global` | nur globale Flags (offline) |
|
|
153
|
+
|
|
154
|
+
Diese Flags sind **Terminal-CLI**. MCP Inspector / Cursor zeigt nur **Tools**, keine CLI-Flags.
|
|
115
155
|
|
|
116
156
|
## Geo-Tools — Payload (vom Server, CLI spiegelt 1:1)
|
|
117
157
|
|
|
118
158
|
| Tool | Koordinaten-Felder in der Antwort |
|
|
119
159
|
|------|-----------------------------------|
|
|
120
|
-
| `get_geo_office` | lat
|
|
121
|
-
| `get_service_area` | lat
|
|
122
|
-
| `list_service_areas` | je Item: lat
|
|
123
|
-
|
|
124
|
-
`embed_map`: `https://maps.ikeytz.com/{slug}`
|
|
125
|
-
`geo_api`: `https://maps.ikeytz.com/api/v1/embed/ort/{slug}` (public JSON, kein Passwort)
|
|
126
|
-
`google_maps` / `icbm`: Komma-Form wie in `/llms-orte-geo.txt`
|
|
160
|
+
| `get_geo_office` | `lat`, `lng`, `google_maps`, `icbm` (+ Adresse; kein Walk-in) |
|
|
161
|
+
| `get_service_area` | `lat`, `lng`, `google_maps`, `icbm`, `embed_map`, `geo_api` (+ slug, plz, …) |
|
|
162
|
+
| `list_service_areas` | je Item: `lat`, `lng`, `google_maps`, `icbm`, `embed_map`, `geo_api` |
|
|
127
163
|
|
|
128
|
-
|
|
164
|
+
- **`embed_map`:** `https://maps.ikeytz.com/{slug}`
|
|
165
|
+
- **`geo_api`:** `https://maps.ikeytz.com/api/v1/embed/ort/{slug}` (public JSON, kein Passwort)
|
|
166
|
+
- **`google_maps` / `icbm`:** Komma-Form wie in `/llms-orte-geo.txt`
|
|
167
|
+
- Volltext Geo: `ikeytz-maps-mcp get_discovery --which llms-orte-geo`
|
|
129
168
|
|
|
130
|
-
## Alle Tool-Befehle
|
|
169
|
+
## Alle 33 Tool-Befehle
|
|
131
170
|
|
|
132
171
|
| Befehl | Argumente |
|
|
133
172
|
|--------|-----------|
|
|
134
173
|
| `list_service_areas` | — |
|
|
135
|
-
| `get_service_area` | `--slug`
|
|
174
|
+
| `get_service_area` | `--slug` `--plz` `--name` (ein Treffer; PLZ = erster) |
|
|
136
175
|
| `get_geo_office` | — |
|
|
137
176
|
| `get_contact` | — |
|
|
138
|
-
| `get_discovery` | `--which*`
|
|
177
|
+
| `get_discovery` | `--which*` `--offset` `--limit` |
|
|
139
178
|
| `get_llms_mcp_server` | — |
|
|
140
179
|
| `get_llms_mcp_web` | — |
|
|
141
|
-
| `find_by_keyword` | `--q*`
|
|
142
|
-
| `find_by_serp` | `--q`
|
|
143
|
-
| `get_serp_snippet` | `--path`
|
|
180
|
+
| `find_by_keyword` | `--q*` `--limit` |
|
|
181
|
+
| `find_by_serp` | `--q` `--path` `--url` `--slug` `--limit` |
|
|
182
|
+
| `get_serp_snippet` | `--path` `--url` `--slug` |
|
|
144
183
|
| `find_by_plz` | `--plz*` (alle Gebiete dieser PLZ) |
|
|
145
|
-
| `find_by_geo` | `--lat`
|
|
146
|
-
| `find_by_icbm` | `--icbm`
|
|
147
|
-
| `find_by_gebiet` | `--q`
|
|
184
|
+
| `find_by_geo` | `--lat` `--lng` `--icbm` `--radius_km` `--limit` |
|
|
185
|
+
| `find_by_icbm` | `--icbm` `--lat` `--lng` `--radius_km` `--limit` |
|
|
186
|
+
| `find_by_gebiet` | `--q` `--name` `--slug` |
|
|
148
187
|
| `find_by_area` | Alias von `find_by_gebiet` |
|
|
149
188
|
| `list_slugs` | — |
|
|
150
189
|
| `list_plz` | — |
|
|
151
190
|
| `get_hub` | — |
|
|
152
|
-
| `get_embed_map` | `--slug`
|
|
153
|
-
| `get_geo_api` | `--slug`
|
|
154
|
-
| `get_lage` | `--slug`
|
|
155
|
-
| `get_geschichte` | `--slug`
|
|
156
|
-
| `distance_to_office` | `--slug`
|
|
191
|
+
| `get_embed_map` | `--slug` `--plz` `--name` |
|
|
192
|
+
| `get_geo_api` | `--slug` `--plz` `--name` |
|
|
193
|
+
| `get_lage` | `--slug` `--plz` `--name` |
|
|
194
|
+
| `get_geschichte` | `--slug` `--plz` `--name` |
|
|
195
|
+
| `distance_to_office` | `--slug` `--lat` `--lng` `--icbm` |
|
|
157
196
|
| `resolve_maps_url` | `--url` |
|
|
158
197
|
| `find_by_google_maps` | `--q` (Koordinaten-Paste) |
|
|
198
|
+
| `get_legal` | `--doc*` urheberrecht\|copyright\|impressum\|agb\|nutzungsbedingungen\|datenschutz\|widerruf |
|
|
199
|
+
| `get_urheberrecht` | — (maps `/llms-urheberrecht.txt`) |
|
|
200
|
+
| `get_copyright` | — |
|
|
201
|
+
| `get_impressum` | — |
|
|
202
|
+
| `get_agb` | — (www-URL) |
|
|
203
|
+
| `get_nutzungsbedingungen` | — (www-URL) |
|
|
204
|
+
| `get_datenschutz` | — (www-URL) |
|
|
205
|
+
| `get_widerruf` | — (www-URL) |
|
|
159
206
|
|
|
160
|
-
Präfix immer `ikeytz-maps-mcp` (z.
|
|
207
|
+
Präfix immer `ikeytz-maps-mcp ` (z. B. `ikeytz-maps-mcp get_service_area -h`).
|
|
161
208
|
|
|
162
209
|
## MCP hosts (any client — not only Cursor / Claude)
|
|
163
210
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Jeder Host mit Streamable HTTP MCP:
|
|
211
|
+
**Option A — HTTP (empfohlen, kein npm nötig)**
|
|
212
|
+
Jeder Host mit **Streamable HTTP MCP**:
|
|
167
213
|
|
|
168
214
|
| Feld | Wert |
|
|
169
215
|
|------|------|
|
|
170
|
-
| URL | https://maps.ikeytz.com/mcp |
|
|
171
|
-
| Registry | com.ikeytz/maps |
|
|
216
|
+
| URL | `https://maps.ikeytz.com/mcp` |
|
|
217
|
+
| Registry | `com.ikeytz/maps` |
|
|
172
218
|
| Card | https://maps.ikeytz.com/.well-known/mcp/server-card.json |
|
|
173
219
|
|
|
174
|
-
Beispiele: Windsurf, Cline, Continue, Claude Desktop (Remote), eigene Agenten — sofern MCP HTTP unterstützt
|
|
175
|
-
|
|
176
|
-
### Option B — stdio via npm (dieses Paket)
|
|
220
|
+
Beispiele: Windsurf, Cline, Continue, Claude Desktop (Remote), eigene Agenten — **sofern MCP HTTP unterstützt**.
|
|
177
221
|
|
|
178
|
-
|
|
222
|
+
**Option B — stdio via npm (dieses Paket)**
|
|
223
|
+
Jeder Host mit **command + args** (Cursor, Claude Desktop, Windsurf, …):
|
|
179
224
|
|
|
180
225
|
```json
|
|
181
226
|
{
|
|
@@ -188,7 +233,7 @@ Jeder Host mit `command` + `args` (Cursor, Claude Desktop, Windsurf, …):
|
|
|
188
233
|
}
|
|
189
234
|
```
|
|
190
235
|
|
|
191
|
-
Global installiert: `"command": "ikeytz-maps-mcp"` (ohne args
|
|
236
|
+
Global installiert: `"command": "ikeytz-maps-mcp"` (ohne `args`, leerer stdio-Proxy).
|
|
192
237
|
|
|
193
238
|
www bleibt **zweiter** Eintrag:
|
|
194
239
|
|
|
@@ -199,11 +244,10 @@ www bleibt **zweiter** Eintrag:
|
|
|
199
244
|
}
|
|
200
245
|
```
|
|
201
246
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
`ikeytz-maps-mcp get_service_area --slug=pattonville`, `ikeytz-maps-mcp list_service_areas`, … — ohne MCP-Host.
|
|
247
|
+
**Option C — Terminal only**
|
|
248
|
+
`ikeytz-maps-mcp get_service_area --slug=pattonville`, `ikeytz-maps-mcp list`, … — **ohne** MCP-Host.
|
|
205
249
|
|
|
206
|
-
Alle Optionen nutzen dieselbe SSOT: `https://maps.ikeytz.com/mcp` (read/link only
|
|
250
|
+
Alle Optionen nutzen dieselbe SSOT: `https://maps.ikeytz.com/mcp` (33 Tools, read/link only).
|
|
207
251
|
|
|
208
252
|
## Env
|
|
209
253
|
|
|
@@ -215,10 +259,20 @@ Alle Optionen nutzen dieselbe SSOT: `https://maps.ikeytz.com/mcp` (read/link onl
|
|
|
215
259
|
|
|
216
260
|
Nicht `IKEYTZ_MCP_URL` — das ist www (`@ikeytz/mcp`).
|
|
217
261
|
|
|
262
|
+
## Maintainer
|
|
263
|
+
|
|
264
|
+
Nach MCP-Tool-Schema-Änderungen:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
node scripts/sync-maps-mcp-cli-catalog.mjs # → packages/maps-mcp/src/tools-catalog.json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Plan: `docs/plan-maps-ai-mcp.md`
|
|
271
|
+
|
|
218
272
|
## Legal
|
|
219
273
|
|
|
220
|
-
License: LicenseRef-ikeytz-terms (LICENSE in this package)
|
|
221
|
-
https://www.ikeytz.com/urheberrecht
|
|
222
|
-
https://maps.ikeytz.com/llms-urheberrecht.txt
|
|
223
|
-
https://maps.ikeytz.com/.well-known/ai.txt
|
|
224
|
-
Attribution required · ai-train: no · CLI install and read-only MCP use permitted
|
|
274
|
+
- License: **LicenseRef-ikeytz-terms** ([LICENSE](https://unpkg.com/@ikeytz/maps-mcp/LICENSE) in this package)
|
|
275
|
+
- https://www.ikeytz.com/urheberrecht
|
|
276
|
+
- https://maps.ikeytz.com/llms-urheberrecht.txt
|
|
277
|
+
- https://maps.ikeytz.com/.well-known/ai.txt
|
|
278
|
+
- **Attribution required** · **ai-train: no** · CLI install and read-only MCP use permitted
|
package/bin/ikeytz-maps-mcp.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { runCli } from "../src/cli.js";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
4
|
+
runCli(process.argv.slice(2)).then(
|
|
5
|
+
(code) => {
|
|
6
|
+
process.exit(typeof code === "number" ? code : 0);
|
|
7
|
+
},
|
|
8
|
+
(err) => {
|
|
9
|
+
process.stderr.write(`ikeytz-maps-mcp: ${err?.message || err}\n`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
);
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikeytz/maps-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"mcpName": "com.ikeytz/maps",
|
|
5
|
-
"description": "Official
|
|
5
|
+
"description": "Official CLI + MCP stdio proxy for maps.ikeytz.com — 33 read-only tools (Einsatzgebiet-Karten, not www prices) at https://maps.ikeytz.com/mcp",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
9
|
-
},
|
|
10
7
|
"bin": {
|
|
11
8
|
"ikeytz-maps-mcp": "bin/ikeytz-maps-mcp.js"
|
|
12
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"src",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
13
17
|
"engines": {
|
|
14
18
|
"node": ">=18"
|
|
15
19
|
},
|
|
16
|
-
"license": "LicenseRef-ikeytz-terms",
|
|
17
|
-
"homepage": "https://maps.ikeytz.com/mcp",
|
|
18
20
|
"keywords": [
|
|
19
21
|
"mcp",
|
|
20
22
|
"model-context-protocol",
|
|
@@ -31,11 +33,17 @@
|
|
|
31
33
|
"read-only",
|
|
32
34
|
"stdio"
|
|
33
35
|
],
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
"author": {
|
|
37
|
+
"name": "Mahmud Reza Kashani",
|
|
38
|
+
"email": "info@ikeytz.com",
|
|
39
|
+
"url": "https://maps.ikeytz.com"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://maps.ikeytz.com/mcp",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://www.ikeytz.com/kontakt"
|
|
44
|
+
},
|
|
45
|
+
"license": "LicenseRef-ikeytz-terms",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
41
49
|
}
|
package/src/cli.js
CHANGED
|
@@ -1,123 +1,327 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Human terminal CLI over the same remote MCP SSOT (maps.ikeytz.com).
|
|
3
|
+
* Subcommands: help | list | describe | call | <toolName>
|
|
4
|
+
* No args → stdio proxy (MCP hosts).
|
|
5
|
+
* Structure aligned with @ikeytz/mcp (ikeytz-mcp) — other remote, maps-only tools.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { stderr as log, stdout as out } from "node:process";
|
|
10
|
+
import {
|
|
11
|
+
CATALOG,
|
|
12
|
+
callTool,
|
|
13
|
+
mapsMcpProtocol,
|
|
14
|
+
mapsMcpUrl,
|
|
15
|
+
mapsRpc,
|
|
16
|
+
packageVersion,
|
|
17
|
+
toolByName,
|
|
18
|
+
} from "./proxy.js";
|
|
19
|
+
|
|
20
|
+
function catalogTools() {
|
|
21
|
+
return CATALOG.tools || [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function exampleForTool(name) {
|
|
25
|
+
const p = "ikeytz-maps-mcp";
|
|
26
|
+
const map = {
|
|
27
|
+
list_service_areas: `${p} list_service_areas`,
|
|
28
|
+
get_service_area: `${p} get_service_area --slug=pattonville`,
|
|
29
|
+
get_geo_office: `${p} get_geo_office`,
|
|
30
|
+
get_contact: `${p} get_contact`,
|
|
31
|
+
get_discovery: `${p} get_discovery --which=llms-urheberrecht`,
|
|
32
|
+
get_llms_mcp_server: `${p} get_llms_mcp_server`,
|
|
33
|
+
get_llms_mcp_web: `${p} get_llms_mcp_web`,
|
|
34
|
+
find_by_keyword: `${p} find_by_keyword --q=Pattonville --limit=5`,
|
|
35
|
+
find_by_serp: `${p} find_by_serp --q=Grünbühl`,
|
|
36
|
+
get_serp_snippet: `${p} get_serp_snippet --slug=pattonville`,
|
|
37
|
+
find_by_plz: `${p} find_by_plz --plz=71638`,
|
|
38
|
+
find_by_geo: `${p} find_by_geo --lat=48.873174 --lng=9.224754`,
|
|
39
|
+
find_by_icbm: `${p} find_by_icbm --icbm=48.873174,9.224754`,
|
|
40
|
+
find_by_gebiet: `${p} find_by_gebiet --q=Grünbühl`,
|
|
41
|
+
find_by_area: `${p} find_by_area --q=Grünbühl`,
|
|
42
|
+
list_slugs: `${p} list_slugs`,
|
|
43
|
+
list_plz: `${p} list_plz`,
|
|
44
|
+
get_hub: `${p} get_hub`,
|
|
45
|
+
get_embed_map: `${p} get_embed_map --slug=pattonville`,
|
|
46
|
+
get_geo_api: `${p} get_geo_api --slug=pattonville`,
|
|
47
|
+
get_lage: `${p} get_lage --slug=pattonville`,
|
|
48
|
+
get_geschichte: `${p} get_geschichte --slug=pattonville`,
|
|
49
|
+
distance_to_office: `${p} distance_to_office --slug=pattonville`,
|
|
50
|
+
resolve_maps_url: `${p} resolve_maps_url --url=https://maps.ikeytz.com/pattonville`,
|
|
51
|
+
find_by_google_maps: `${p} find_by_google_maps --q=48.873174,9.224754`,
|
|
52
|
+
get_legal: `${p} get_legal --doc=urheberrecht`,
|
|
53
|
+
get_urheberrecht: `${p} get_urheberrecht`,
|
|
54
|
+
get_copyright: `${p} get_copyright`,
|
|
55
|
+
get_impressum: `${p} get_impressum`,
|
|
56
|
+
get_agb: `${p} get_agb`,
|
|
57
|
+
get_nutzungsbedingungen: `${p} get_nutzungsbedingungen`,
|
|
58
|
+
get_datenschutz: `${p} get_datenschutz`,
|
|
59
|
+
get_widerruf: `${p} get_widerruf`,
|
|
60
|
+
};
|
|
61
|
+
return map[name] || `${p} ${name}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function argHint(tool) {
|
|
65
|
+
const props = Object.keys(tool.inputSchema?.properties || {});
|
|
66
|
+
const req = tool.inputSchema?.required || [];
|
|
67
|
+
if (!props.length) return "";
|
|
68
|
+
return props.map((k) => (req.includes(k) ? `--${k}*` : `--${k}`)).join(" ");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function formatToolsAsciiTable(rows) {
|
|
72
|
+
const n = rows.length;
|
|
73
|
+
const cn = String(n).length;
|
|
74
|
+
const c0 = Math.max(4, ...rows.map((r) => r.tool.length), "TOOL".length);
|
|
75
|
+
const c1 = Math.max(4, ...rows.map((r) => r.args.length), "ARGS".length);
|
|
76
|
+
const pad = (s, w, align = "left") => {
|
|
77
|
+
const t = String(s);
|
|
78
|
+
if (t.length >= w) return t;
|
|
79
|
+
const sp = " ".repeat(w - t.length);
|
|
80
|
+
return align === "right" ? sp + t : t + sp;
|
|
81
|
+
};
|
|
82
|
+
const rule = `${"─".repeat(cn)}─┼─${"─".repeat(c0)}─┼─${"─".repeat(c1)}─┼─${"─".repeat(7)}`;
|
|
83
|
+
const lines = [`${pad("#", cn, "right")} │ ${pad("TOOL", c0)} │ ${pad("ARGS", c1)} │ EXAMPLE`, rule];
|
|
84
|
+
rows.forEach((r, i) => {
|
|
85
|
+
lines.push(
|
|
86
|
+
`${pad(String(i + 1), cn, "right")} │ ${pad(r.tool, c0)} │ ${pad(r.args, c1)} │ ${r.example}`
|
|
87
|
+
);
|
|
88
|
+
lines.push(rule);
|
|
89
|
+
});
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function globalCliFlagsLines() {
|
|
94
|
+
return [
|
|
95
|
+
"",
|
|
96
|
+
"Global CLI flags (every tool — not sent to MCP)",
|
|
97
|
+
" -h, --help, -help per-tool help (offline)",
|
|
98
|
+
" --raw JSON structuredContent",
|
|
99
|
+
" --show-mcp-command curl 1a/1b/2 only — no MCP call",
|
|
100
|
+
" aliases: --show-mcp-befehl · --show-mcp-comment",
|
|
101
|
+
"",
|
|
102
|
+
"Default tool output payload only (no ok/tool/meta envelope)",
|
|
103
|
+
" --name=value --name value --json '{...}'",
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function printHumanHelp(tools) {
|
|
108
|
+
const lines = [
|
|
109
|
+
`ikeytz-maps-mcp ${packageVersion()}`,
|
|
110
|
+
`Remote ${mapsMcpUrl()}`,
|
|
111
|
+
`Registry com.ikeytz/maps`,
|
|
112
|
+
"Nur maps. Nicht @ikeytz/mcp / www.",
|
|
113
|
+
"",
|
|
114
|
+
"Usage",
|
|
115
|
+
" ikeytz-maps-mcp MCP proxy (Cursor/Claude)",
|
|
116
|
+
" ikeytz-maps-mcp --version package version (offline)",
|
|
117
|
+
" ikeytz-maps-mcp help this help (offline, no MCP connect)",
|
|
118
|
+
" ikeytz-maps-mcp update [version] npm i -g @ikeytz/maps-mcp@latest",
|
|
119
|
+
" ikeytz-maps-mcp list tool table (offline) + global flags",
|
|
120
|
+
" ikeytz-maps-mcp list --global global CLI flags only (offline)",
|
|
121
|
+
" ikeytz-maps-mcp describe <tool> args + examples (offline)",
|
|
122
|
+
" ikeytz-maps-mcp call <tool> [flags]",
|
|
123
|
+
" ikeytz-maps-mcp <tool> [flags] same as call",
|
|
124
|
+
" ikeytz-maps-mcp <tool> -h per-tool help (offline, same as describe)",
|
|
125
|
+
" ikeytz-maps-mcp <tool> --show-mcp-command",
|
|
126
|
+
"",
|
|
127
|
+
...globalCliFlagsLines().slice(1),
|
|
128
|
+
"",
|
|
129
|
+
];
|
|
130
|
+
if (tools?.length) {
|
|
131
|
+
const rows = [...tools]
|
|
132
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
133
|
+
.map((t) => ({
|
|
134
|
+
tool: t.name,
|
|
135
|
+
args: argHint(t) || "—",
|
|
136
|
+
example: exampleForTool(t.name),
|
|
137
|
+
}));
|
|
138
|
+
lines.push(`Tools ${rows.length} (A–Z numbered, * = required)`);
|
|
139
|
+
lines.push("");
|
|
140
|
+
lines.push(...formatToolsAsciiTable(rows));
|
|
141
|
+
lines.push("");
|
|
142
|
+
lines.push("Tip ikeytz-maps-mcp describe get_service_area");
|
|
143
|
+
} else {
|
|
144
|
+
lines.push("Tools ikeytz-maps-mcp list (offline) · ikeytz-maps-mcp <tool> -h for args");
|
|
145
|
+
}
|
|
146
|
+
lines.push("");
|
|
147
|
+
out.write(`${lines.join("\n")}\n`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatDescribe(tool) {
|
|
151
|
+
const schema = tool.inputSchema || { type: "object", properties: {} };
|
|
152
|
+
const props = schema.properties || {};
|
|
153
|
+
const req = new Set(schema.required || []);
|
|
154
|
+
const lines = [tool.name, tool.description || "", "", "Arguments"];
|
|
155
|
+
const keys = Object.keys(props);
|
|
156
|
+
if (!keys.length) {
|
|
157
|
+
lines.push(" (none)");
|
|
158
|
+
} else {
|
|
159
|
+
for (const key of keys) {
|
|
160
|
+
const p = props[key] || {};
|
|
161
|
+
const type = p.enum ? p.enum.join("|") : p.type || "any";
|
|
162
|
+
const star = req.has(key) ? "*" : " ";
|
|
163
|
+
const desc = p.description ? ` ${p.description}` : "";
|
|
164
|
+
lines.push(` ${star} --${key.padEnd(16)} ${type}${desc}`);
|
|
165
|
+
}
|
|
166
|
+
if (req.size) {
|
|
167
|
+
lines.push("");
|
|
168
|
+
lines.push(" * required");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
lines.push("");
|
|
172
|
+
lines.push("CLI flags (not sent to MCP)");
|
|
173
|
+
lines.push(" -h, --help, -help this help (offline, no MCP call)");
|
|
174
|
+
lines.push(" --raw JSON structuredContent");
|
|
175
|
+
lines.push(" --show-mcp-command curl 1a/1b/2 only (aliases: --show-mcp-befehl, --show-mcp-comment)");
|
|
176
|
+
lines.push("");
|
|
177
|
+
lines.push("Examples");
|
|
178
|
+
lines.push(` ${exampleForTool(tool.name)}`);
|
|
179
|
+
lines.push(` ikeytz-maps-mcp ${tool.name} -h`);
|
|
180
|
+
lines.push(` ikeytz-maps-mcp ${tool.name} --show-mcp-command`);
|
|
181
|
+
return lines.filter((l, i) => !(i === 1 && !l)).join("\n");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const SHOW_MCP_COMMAND_FLAGS = new Set([
|
|
185
|
+
"--show-mcp-command",
|
|
186
|
+
"--show-mcp-befehl",
|
|
187
|
+
"--show-mcp-comment",
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
function wantsShowMcpCommand(argv) {
|
|
191
|
+
return argv.some((a) => SHOW_MCP_COMMAND_FLAGS.has(a));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function stripCliDisplayFlags(argv) {
|
|
195
|
+
return argv.filter((a) => !SHOW_MCP_COMMAND_FLAGS.has(a));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function isHelpFlag(a) {
|
|
199
|
+
return a === "-h" || a === "--help" || a === "-help" || a === "help";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parseToolArgs(argv) {
|
|
203
|
+
const args = {};
|
|
204
|
+
let i = 0;
|
|
205
|
+
while (i < argv.length) {
|
|
48
206
|
const a = argv[i];
|
|
49
|
-
if (a === "--
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
207
|
+
if (a === "--json") {
|
|
208
|
+
const raw = argv[i + 1];
|
|
209
|
+
if (!raw) throw new Error("--json needs a JSON object");
|
|
210
|
+
const parsed = JSON.parse(raw);
|
|
211
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
212
|
+
throw new Error("--json must be an object");
|
|
213
|
+
}
|
|
214
|
+
Object.assign(args, parsed);
|
|
215
|
+
i += 2;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (a.startsWith("--json=")) {
|
|
219
|
+
Object.assign(args, JSON.parse(a.slice("--json=".length)));
|
|
220
|
+
i += 1;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (a.startsWith("--")) {
|
|
56
224
|
const eq = a.indexOf("=");
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
225
|
+
let key;
|
|
226
|
+
let val;
|
|
227
|
+
if (eq > 2) {
|
|
228
|
+
key = a.slice(2, eq);
|
|
229
|
+
val = a.slice(eq + 1);
|
|
230
|
+
i += 1;
|
|
231
|
+
} else {
|
|
232
|
+
key = a.slice(2);
|
|
60
233
|
const next = argv[i + 1];
|
|
61
|
-
if (next
|
|
62
|
-
|
|
234
|
+
if (!next || next.startsWith("-")) {
|
|
235
|
+
val = true;
|
|
63
236
|
i += 1;
|
|
64
|
-
} else
|
|
237
|
+
} else {
|
|
238
|
+
val = next;
|
|
239
|
+
i += 2;
|
|
240
|
+
}
|
|
65
241
|
}
|
|
66
|
-
|
|
242
|
+
if (val === "true") val = true;
|
|
243
|
+
else if (val === "false") val = false;
|
|
244
|
+
else if (typeof val === "string" && /^-?\d+(\.\d+)?$/.test(val)) val = Number(val);
|
|
245
|
+
args[key] = val;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
throw new Error(`Unexpected argument: ${a}`);
|
|
67
249
|
}
|
|
68
|
-
return
|
|
250
|
+
return args;
|
|
69
251
|
}
|
|
70
252
|
|
|
71
|
-
function
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
253
|
+
function formatMcpCurlHeading(toolName, callArgs = {}) {
|
|
254
|
+
const url = mapsMcpUrl();
|
|
255
|
+
const proto = mapsMcpProtocol();
|
|
256
|
+
const argsObj = callArgs && typeof callArgs === "object" ? callArgs : {};
|
|
257
|
+
const argsJson = JSON.stringify(argsObj);
|
|
258
|
+
const callBody = JSON.stringify({
|
|
259
|
+
jsonrpc: "2.0",
|
|
260
|
+
id: 2,
|
|
261
|
+
method: "tools/call",
|
|
262
|
+
params: { name: toolName, arguments: argsObj },
|
|
263
|
+
});
|
|
264
|
+
const dq = (s) => String(s).replace(/'/g, `'\\''`);
|
|
265
|
+
const tool = toolByName(toolName);
|
|
266
|
+
const blurb = tool?.description || `Remote MCP tools/call · ${toolName}`;
|
|
267
|
+
const parts = [`ikeytz-maps-mcp ${toolName}`];
|
|
268
|
+
for (const [k, v] of Object.entries(argsObj)) {
|
|
269
|
+
if (v === undefined) continue;
|
|
270
|
+
parts.push(`--${k}=${v}`);
|
|
78
271
|
}
|
|
79
|
-
|
|
272
|
+
const bar = "═".repeat(56);
|
|
273
|
+
const thin = "─".repeat(56);
|
|
274
|
+
return [
|
|
275
|
+
bar,
|
|
276
|
+
`MCP tool ${toolName}`,
|
|
277
|
+
`What ${blurb}`,
|
|
278
|
+
`CLI ${parts.join(" ")}`,
|
|
279
|
+
`Args ${argsJson}`,
|
|
280
|
+
`Registry com.ikeytz/maps`,
|
|
281
|
+
`Remote ${url}`,
|
|
282
|
+
thin,
|
|
283
|
+
`Equivalent curl (same MCP call)`,
|
|
284
|
+
"",
|
|
285
|
+
`# 1a) Session-ID holen`,
|
|
286
|
+
`SID=$(curl -sS -D - -o /tmp/ikeytz-maps-mcp-init.json -X POST '${url}' \\`,
|
|
287
|
+
` -H 'Content-Type: application/json' \\`,
|
|
288
|
+
` -H 'Accept: application/json' \\`,
|
|
289
|
+
` -H 'MCP-Protocol-Version: ${proto}' \\`,
|
|
290
|
+
` -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"${proto}","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' \\`,
|
|
291
|
+
` | awk -F': ' 'tolower($1)=="mcp-session-id"{gsub(/\\r/,"",$2); print $2; exit}')`,
|
|
292
|
+
`echo "SID=$SID"`,
|
|
293
|
+
"",
|
|
294
|
+
`# 1b) initialized`,
|
|
295
|
+
`curl -sS -X POST '${url}' \\`,
|
|
296
|
+
` -H 'Content-Type: application/json' \\`,
|
|
297
|
+
` -H 'Accept: application/json' \\`,
|
|
298
|
+
` -H 'MCP-Protocol-Version: ${proto}' \\`,
|
|
299
|
+
` -H "Mcp-Session-Id: $SID" \\`,
|
|
300
|
+
` -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' >/dev/null`,
|
|
301
|
+
"",
|
|
302
|
+
`# 2) Tool`,
|
|
303
|
+
`curl -sS -X POST '${url}' \\`,
|
|
304
|
+
` -H 'Content-Type: application/json' \\`,
|
|
305
|
+
` -H 'Accept: application/json' \\`,
|
|
306
|
+
` -H 'MCP-Protocol-Version: ${proto}' \\`,
|
|
307
|
+
` -H "Mcp-Session-Id: $SID" \\`,
|
|
308
|
+
` -d '${dq(callBody)}'`,
|
|
309
|
+
bar,
|
|
310
|
+
].join("\n");
|
|
80
311
|
}
|
|
81
312
|
|
|
82
|
-
function
|
|
313
|
+
function printCallResult(rpc, { raw = false } = {}) {
|
|
83
314
|
if (raw) {
|
|
84
|
-
|
|
315
|
+
out.write(`${JSON.stringify(rpc, null, 2)}\n`);
|
|
85
316
|
return;
|
|
86
317
|
}
|
|
87
318
|
const data = rpc?.result?.structuredContent ?? rpc?.result ?? rpc;
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const text = buf.toString("utf8");
|
|
93
|
-
const headerEnd = text.indexOf("\r\n\r\n");
|
|
94
|
-
const altEnd = text.indexOf("\n\n");
|
|
95
|
-
if (headerEnd >= 0 || (altEnd >= 0 && /content-length:/i.test(text.slice(0, altEnd + 2)))) {
|
|
96
|
-
const split = headerEnd >= 0 ? headerEnd : altEnd;
|
|
97
|
-
const sep = headerEnd >= 0 ? 4 : 2;
|
|
98
|
-
const header = text.slice(0, split);
|
|
99
|
-
const m = header.match(/content-length:\s*(\d+)/i);
|
|
100
|
-
if (!m) return null;
|
|
101
|
-
const len = Number(m[1]);
|
|
102
|
-
const start = split + sep;
|
|
103
|
-
const body = Buffer.from(text, "utf8").subarray(start);
|
|
104
|
-
if (body.length < len) return null;
|
|
105
|
-
const json = JSON.parse(body.subarray(0, len).toString("utf8"));
|
|
106
|
-
return { json, rest: body.subarray(len) };
|
|
319
|
+
if (data && typeof data === "object") {
|
|
320
|
+
const { ok, tool, locale, related, attribution, ...payload } = data;
|
|
321
|
+
out.write(`${JSON.stringify(Object.keys(payload).length ? payload : data, null, 2)}\n`);
|
|
322
|
+
return;
|
|
107
323
|
}
|
|
108
|
-
|
|
109
|
-
if (nl < 0) return null;
|
|
110
|
-
const line = text.slice(0, nl).trim();
|
|
111
|
-
const rest = Buffer.from(text.slice(nl + 1), "utf8");
|
|
112
|
-
if (!line) return { json: null, rest };
|
|
113
|
-
return { json: JSON.parse(line), rest };
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function writeStdio(msg) {
|
|
117
|
-
const json = JSON.stringify(msg);
|
|
118
|
-
const body = Buffer.from(json, "utf8");
|
|
119
|
-
process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`);
|
|
120
|
-
process.stdout.write(body);
|
|
324
|
+
out.write(`${JSON.stringify(data, null, 2)}\n`);
|
|
121
325
|
}
|
|
122
326
|
|
|
123
327
|
async function runStdioProxy() {
|
|
@@ -143,22 +347,22 @@ async function runStdioProxy() {
|
|
|
143
347
|
const id = msg.id;
|
|
144
348
|
try {
|
|
145
349
|
if (method === "initialize") {
|
|
146
|
-
const
|
|
147
|
-
sessionId =
|
|
148
|
-
writeStdio(
|
|
350
|
+
const res = await mapsRpc("initialize", msg.params || {}, { id: id ?? 1 });
|
|
351
|
+
sessionId = res.sessionId || sessionId;
|
|
352
|
+
writeStdio(res.json);
|
|
149
353
|
} else if (method === "notifications/initialized" || method === "initialized") {
|
|
150
|
-
const
|
|
354
|
+
const res = await mapsRpc("notifications/initialized", msg.params || {}, {
|
|
151
355
|
notification: true,
|
|
152
356
|
sessionId,
|
|
153
357
|
});
|
|
154
|
-
sessionId =
|
|
358
|
+
sessionId = res.sessionId || sessionId;
|
|
155
359
|
if (id !== undefined) writeStdio({ jsonrpc: "2.0", id, result: {} });
|
|
156
360
|
} else if (id === undefined) {
|
|
157
361
|
await mapsRpc(method, msg.params || {}, { notification: true, sessionId });
|
|
158
362
|
} else {
|
|
159
|
-
const
|
|
160
|
-
sessionId =
|
|
161
|
-
writeStdio(
|
|
363
|
+
const res = await mapsRpc(method, msg.params || {}, { id, sessionId });
|
|
364
|
+
sessionId = res.sessionId || sessionId;
|
|
365
|
+
writeStdio(res.json);
|
|
162
366
|
}
|
|
163
367
|
} catch (err) {
|
|
164
368
|
if (id !== undefined) {
|
|
@@ -170,67 +374,131 @@ async function runStdioProxy() {
|
|
|
170
374
|
});
|
|
171
375
|
}
|
|
172
376
|
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
377
|
+
function readStdioMessage(buf) {
|
|
378
|
+
const text = buf.toString("utf8");
|
|
379
|
+
const headerEnd = text.indexOf("\r\n\r\n");
|
|
380
|
+
const altEnd = text.indexOf("\n\n");
|
|
381
|
+
if (headerEnd >= 0 || (altEnd >= 0 && /content-length:/i.test(text.slice(0, altEnd + 2)))) {
|
|
382
|
+
const split = headerEnd >= 0 ? headerEnd : altEnd;
|
|
383
|
+
const sep = headerEnd >= 0 ? 4 : 2;
|
|
384
|
+
const header = text.slice(0, split);
|
|
385
|
+
const m = header.match(/content-length:\s*(\d+)/i);
|
|
386
|
+
if (!m) return null;
|
|
387
|
+
const len = Number(m[1]);
|
|
388
|
+
const start = split + sep;
|
|
389
|
+
const body = Buffer.from(text, "utf8").subarray(start);
|
|
390
|
+
if (body.length < len) return null;
|
|
391
|
+
return { json: JSON.parse(body.subarray(0, len).toString("utf8")), rest: body.subarray(len) };
|
|
179
392
|
}
|
|
180
|
-
|
|
393
|
+
const nl = text.indexOf("\n");
|
|
394
|
+
if (nl < 0) return null;
|
|
395
|
+
const line = text.slice(0, nl).trim();
|
|
396
|
+
const rest = Buffer.from(text.slice(nl + 1), "utf8");
|
|
397
|
+
if (!line) return { json: null, rest };
|
|
398
|
+
return { json: JSON.parse(line), rest };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function writeStdio(msg) {
|
|
402
|
+
const json = JSON.stringify(msg);
|
|
403
|
+
const body = Buffer.from(json, "utf8");
|
|
404
|
+
process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`);
|
|
405
|
+
process.stdout.write(body);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export async function runCli(argv) {
|
|
409
|
+
const args = [...argv];
|
|
410
|
+
if (args.length === 0) {
|
|
181
411
|
await runStdioProxy();
|
|
182
|
-
return;
|
|
412
|
+
return 0;
|
|
183
413
|
}
|
|
184
414
|
|
|
185
|
-
const cmd =
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
return;
|
|
415
|
+
const cmd = args[0];
|
|
416
|
+
if (cmd === "-V" || cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
417
|
+
out.write(`${packageVersion()}\n`);
|
|
418
|
+
return 0;
|
|
189
419
|
}
|
|
190
|
-
if (cmd
|
|
191
|
-
|
|
192
|
-
return;
|
|
420
|
+
if (isHelpFlag(cmd) && args.length === 1) {
|
|
421
|
+
printHumanHelp(catalogTools());
|
|
422
|
+
return 0;
|
|
193
423
|
}
|
|
194
|
-
if (cmd === "
|
|
195
|
-
await
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
424
|
+
if (cmd === "serve" || cmd === "proxy") {
|
|
425
|
+
await runStdioProxy();
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
if (cmd === "update" || cmd === "upgrade") {
|
|
429
|
+
const spec = args[1] && !args[1].startsWith("-") ? args[1] : "latest";
|
|
430
|
+
const pkg = spec === "latest" ? "@ikeytz/maps-mcp@latest" : `@ikeytz/maps-mcp@${spec}`;
|
|
431
|
+
log.write(`Updating ${pkg}…\n`);
|
|
432
|
+
const r = spawnSync("npm", ["i", "-g", "--prefer-online", pkg], { stdio: "inherit", shell: false });
|
|
433
|
+
if (r.status !== 0) return r.status || 1;
|
|
434
|
+
log.write(`Done. New terminal → ikeytz-maps-mcp --version\n`);
|
|
435
|
+
return 0;
|
|
200
436
|
}
|
|
201
437
|
if (cmd === "list") {
|
|
202
|
-
|
|
203
|
-
|
|
438
|
+
const rest = args.slice(1);
|
|
439
|
+
for (const a of rest) {
|
|
440
|
+
if (a !== "--global") {
|
|
441
|
+
log.write(`Usage: ikeytz-maps-mcp list [--global]\n`);
|
|
442
|
+
return 2;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (rest.includes("--global")) {
|
|
446
|
+
out.write(`${globalCliFlagsLines().join("\n")}\n`);
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
const rows = catalogTools().map((t) => ({
|
|
450
|
+
tool: t.name,
|
|
451
|
+
args: argHint(t) || "—",
|
|
452
|
+
example: exampleForTool(t.name),
|
|
453
|
+
}));
|
|
454
|
+
out.write([...formatToolsAsciiTable(rows), ...globalCliFlagsLines()].join("\n") + "\n");
|
|
455
|
+
return 0;
|
|
204
456
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
457
|
+
|
|
458
|
+
let toolName;
|
|
459
|
+
let rest;
|
|
460
|
+
if (cmd === "describe" || cmd === "call") {
|
|
461
|
+
toolName = args[1];
|
|
462
|
+
rest = args.slice(2);
|
|
463
|
+
if (!toolName) {
|
|
464
|
+
log.write(`Usage: ikeytz-maps-mcp ${cmd} <toolName> …\n`);
|
|
465
|
+
return 2;
|
|
212
466
|
}
|
|
213
|
-
|
|
214
|
-
|
|
467
|
+
} else {
|
|
468
|
+
toolName = cmd;
|
|
469
|
+
rest = args.slice(1);
|
|
215
470
|
}
|
|
216
471
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
472
|
+
if (cmd === "describe" || rest.some(isHelpFlag)) {
|
|
473
|
+
const tool = toolByName(toolName);
|
|
474
|
+
if (!tool) {
|
|
475
|
+
log.write(`Unknown tool: ${toolName}\n`);
|
|
476
|
+
return 1;
|
|
477
|
+
}
|
|
478
|
+
out.write(`${formatDescribe(tool)}\n`);
|
|
479
|
+
return 0;
|
|
224
480
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
481
|
+
|
|
482
|
+
const wantRaw = rest.includes("--raw");
|
|
483
|
+
const showMcp = wantsShowMcpCommand(rest);
|
|
484
|
+
let callArgs;
|
|
485
|
+
try {
|
|
486
|
+
callArgs = parseToolArgs(stripCliDisplayFlags(rest.filter((a) => a !== "--raw")));
|
|
487
|
+
} catch (err) {
|
|
488
|
+
log.write(`${err.message || err}\n`);
|
|
489
|
+
return 2;
|
|
228
490
|
}
|
|
229
|
-
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
return;
|
|
491
|
+
|
|
492
|
+
if (showMcp) {
|
|
493
|
+
out.write(`${formatMcpCurlHeading(toolName, callArgs)}\n`);
|
|
494
|
+
return 0;
|
|
233
495
|
}
|
|
234
|
-
|
|
235
|
-
|
|
496
|
+
|
|
497
|
+
const rpc = await callTool(toolName, callArgs);
|
|
498
|
+
printCallResult(rpc, { raw: wantRaw });
|
|
499
|
+
if (rpc?.result?.isError || rpc?.error) process.exitCode = 1;
|
|
500
|
+
return process.exitCode || 0;
|
|
236
501
|
}
|
|
502
|
+
|
|
503
|
+
/** @deprecated use runCli — kept for older bin */
|
|
504
|
+
export const main = runCli;
|
package/src/tools-catalog.json
CHANGED
|
@@ -432,6 +432,113 @@
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"name": "get_legal",
|
|
438
|
+
"description": "Legal: urheberrecht|copyright|impressum (maps-Text) oder agb|nutzungsbedingungen|datenschutz|widerruf (www-URL).",
|
|
439
|
+
"inputSchema": {
|
|
440
|
+
"type": "object",
|
|
441
|
+
"properties": {
|
|
442
|
+
"doc": {
|
|
443
|
+
"type": "string",
|
|
444
|
+
"enum": [
|
|
445
|
+
"urheberrecht",
|
|
446
|
+
"copyright",
|
|
447
|
+
"impressum",
|
|
448
|
+
"agb",
|
|
449
|
+
"nutzungsbedingungen",
|
|
450
|
+
"datenschutz",
|
|
451
|
+
"widerruf"
|
|
452
|
+
]
|
|
453
|
+
},
|
|
454
|
+
"offset": {
|
|
455
|
+
"type": "integer"
|
|
456
|
+
},
|
|
457
|
+
"limit": {
|
|
458
|
+
"type": "integer"
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
"required": [
|
|
462
|
+
"doc"
|
|
463
|
+
]
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"name": "get_urheberrecht",
|
|
468
|
+
"description": "Karten-Urheberrecht: maps /llms-urheberrecht.txt (HTML auf www).",
|
|
469
|
+
"inputSchema": {
|
|
470
|
+
"type": "object",
|
|
471
|
+
"properties": {
|
|
472
|
+
"offset": {
|
|
473
|
+
"type": "integer"
|
|
474
|
+
},
|
|
475
|
+
"limit": {
|
|
476
|
+
"type": "integer"
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"name": "get_copyright",
|
|
483
|
+
"description": "Karten-Copyright EN: maps /llms-copyright.txt.",
|
|
484
|
+
"inputSchema": {
|
|
485
|
+
"type": "object",
|
|
486
|
+
"properties": {
|
|
487
|
+
"offset": {
|
|
488
|
+
"type": "integer"
|
|
489
|
+
},
|
|
490
|
+
"limit": {
|
|
491
|
+
"type": "integer"
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"name": "get_impressum",
|
|
498
|
+
"description": "Impressum/Kontakt-Text: maps /llms-impressum-kontakt.txt.",
|
|
499
|
+
"inputSchema": {
|
|
500
|
+
"type": "object",
|
|
501
|
+
"properties": {
|
|
502
|
+
"offset": {
|
|
503
|
+
"type": "integer"
|
|
504
|
+
},
|
|
505
|
+
"limit": {
|
|
506
|
+
"type": "integer"
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"name": "get_agb",
|
|
513
|
+
"description": "AGB: Link www.ikeytz.com/agb (HTML bleibt www, kein Preise-Corpus).",
|
|
514
|
+
"inputSchema": {
|
|
515
|
+
"type": "object",
|
|
516
|
+
"properties": {}
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
"name": "get_nutzungsbedingungen",
|
|
521
|
+
"description": "Nutzungsbedingungen: Link www (HTML bleibt www).",
|
|
522
|
+
"inputSchema": {
|
|
523
|
+
"type": "object",
|
|
524
|
+
"properties": {}
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
"name": "get_datenschutz",
|
|
529
|
+
"description": "Datenschutz: Link www (HTML bleibt www).",
|
|
530
|
+
"inputSchema": {
|
|
531
|
+
"type": "object",
|
|
532
|
+
"properties": {}
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
"name": "get_widerruf",
|
|
537
|
+
"description": "Widerruf: Link www (HTML bleibt www).",
|
|
538
|
+
"inputSchema": {
|
|
539
|
+
"type": "object",
|
|
540
|
+
"properties": {}
|
|
541
|
+
}
|
|
435
542
|
}
|
|
436
543
|
]
|
|
437
544
|
}
|