Metadata-Version: 2.4
Name: zenmap
Version: 0.1.1
Summary: Publish to a ZenMap app and query its public map: client, command line and MCP server
License-Expression: Apache-2.0
Project-URL: Homepage, https://zenmap.ai/developers
Keywords: zenmap,gis,maps,geospatial,spatial,developer-api,mcp
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: mcp
Requires-Dist: mcp>=1.10; extra == "mcp"
Dynamic: license-file

# ZenMap developer tools

A client for [ZenMap's developer API](https://zenmap.ai/developers), the `zenmap` command, and an MCP server
(`zenmap mcp`) for Claude Code and other local MCP clients. They publish data
to a ZenMap **app** (a public, published map with its own keys) and query
that map's published version.

The client and the command line use the Python standard library only
(Python 3.10+). Only `zenmap mcp` needs one more package, the MCP SDK.

```sh
pip install zenmap            # the client and the command line
pip install 'zenmap[mcp]'     # and the MCP server
zenmap --version              # zenmap 0.1.1
```

The Python import is `zenmap_dev`:

```python
import os
from zenmap_dev.client import Client

client = Client(os.environ["ZENMAP_SECRET_KEY"])
print(client.status())
```

## The key

Each app has a **secret key** (`zm_sk_…`), shown once when it is made in
ZenMap (Settings → Developer). It can publish, so keep it off web pages and
out of your code:

- `zenmap login` keeps it in the macOS Keychain or the Linux Secret Service
  keyring (`--profile NAME` for more than one app);
- or set `ZENMAP_SECRET_KEY` (CI, containers, the MCP server's environment).

The tools send it only in the `Authorization` header and require HTTPS.
HTTP is allowed for local development on `localhost`, `127.0.0.1` and `::1`.
Redirects are refused, so a redirected request cannot leak your key.
A secret key sent in a web address is turned off by ZenMap at once.

The default service address is `https://zenmap.ai`; `ZENMAP_URL` or `--url`
overrides it. The same transport rules apply to all configured addresses
and upload URLs.

## Your whole workspace, with a personal key

A **personal key** (`zm_ak_…`, Settings → Developer → Personal keys) reaches
every project, layer and data folder in your workspace, with what you ticked
when you made it: read and write, and separately share and delete. It lasts
90 days unless you choose up to a year, and stops when you sign out
everywhere. It is kept and sent exactly like a secret key.

```sh
zenmap push splices.geojson cabinets.geojson poles.geojson --project Dallas
zenmap push drops.gpkg --folder Fiber/2026        # a missing folder is made
zenmap push sites.shp --project Dallas --below splices
zenmap update sites new-sites.geojson --key site_id
zenmap update sites more.geojson --append
zenmap update sites all-sites.geojson --replace
zenmap undo sites
zenmap versions sites                    # kept 30 days, and the newest always
zenmap restore sites --version 3         # put version 3 back (the data now is kept first)
zenmap style parcels parcels.qml         # on the one map it is on (--project P on several)
zenmap style parcels parcels.qml --library       # what every map it is added to starts with
zenmap view Dallas --lng -96.80 --lat 32.78 --zoom 11 --basemap voyager
zenmap move sites --folder Fiber
zenmap find splices --project Dallas
```

A layer is named by its name or its id. A name that several layers share is
refused, and the ids of the ones that match are listed.

Each command prints a short summary of what happened. `--json` prints
ZenMap's whole answer instead:

```sh
$ zenmap update units units-october.geojson --key unit_id
Updated units: 1 added, 1 changed, 54 unchanged
Changed on 1 map: DFW expansion
Undo with: zenmap undo units
```

A QGIS style saved beside a file (`splices.qml` or `.sld`, "Save as
default" in QGIS) styles its layer on the project's map; so does a
GeoPackage's own saved style. The answer lists anything that did not carry
over. `--no-style` skips them. Uploading never shares anything.

## Publishing

```sh
zenmap status                         # the app, its map link and its layers
zenmap push data.gpkg --name "Sites"  # a new layer (a shapefile's sidecars go with it)
zenmap style Sites style.json         # style, labels, popup: zenmap.map/1
zenmap fields Sites name score        # the columns the query API may return
zenmap publish                        # a new public version
zenmap update Sites new-data.gpkg --replace   # new rows; the old rows kept as a version
zenmap undo Sites
```

The public map changes only on `publish`.

## Querying the published map

```sh
zenmap query nearest LAYER --lng LNG --lat LAT --k 5
zenmap query within LAYER --lng LNG --lat LAT --distance 2 --unit km --all
zenmap query contains LAYER --lng LNG --lat LAT
zenmap query filter LAYER --where "COLUMN > 10" --fields COLUMN OTHER
zenmap query summarize LAYER --group-by COLUMN --metric count --metric COLUMN:mean
```

Only the fields the app publishes are answered. Queries use the service
address followed by `/api/v1/maps` by default. `ZENMAP_QUERY_URL` overrides
that query base address.

## MCP

```sh
zenmap login                         # the key goes to the keychain
claude mcp add zenmap -- zenmap mcp  # the server reads it from there
```

(`--env ZENMAP_SECRET_KEY=…` works too, but writes the key into Claude
Code's own settings file.)

Tools with an app key: `status`, `push`, `replace`, `undo_replace`,
`set_style`, `set_published_fields`, `publish`, `job_status`, `query`.

Tools with a personal key: `workspace`, `find_layers`, `push_layers`,
`move_to_folder`, `update_layer`, `undo_update`, `layer_versions`,
`restore_version`, `style_layer`, `set_view`,
`set_map_order`, `job_status`, and only when the key has the scope,
`share_project` and `stop_sharing` (share) or `delete_layer` and
`delete_project` (delete). "Push these three layers into my Dallas project
with my QGIS styles" and "put this in my Fiber folder" are each one
`push_layers` call.

Uploads and style files come only from the folder the server was started in
(or `ZENMAP_MCP_ROOT`), and only files ZenMap reads.
