Skip to content

๐Ÿงช Tutorial: Urban Mapper + Jupyter Pipeline

๐Ÿงช Tutorial: Urban Mapper + Jupyter Pipeline

This tutorial shows how to stack two MCPs:

  • Urban Mapper (urban computing analysis utilising the Urban Mapper official library)
  • Jupyter (reproducible notebook analysis)

About Urban Mapper:

Youโ€™ll learn how to:

  1. Build a pipeline with both tools
  2. Ask in a natural language to build a reproducible urban analysis workflow utilising Urban Mapper
  3. Export code and results into a Jupyter Notebook for reproducible Python analysis

๐ŸŽฅ Video Walkthrough


Prerequisites

# If you prefer other Python package managers, feel free to adapt `pip install X`.

uv init --python 3.10
uv add mcpstack
uv add mcpstack-jupyter
uv add mcpstack-urbanmapper

# To see if the tools are all connected
uv run mcpstack list-tools

๐Ÿ”ง Step 1 โ€” Build with Pipeline W/ Urban Mapper Default

Urban Mapper Default is basically using HuggingFace's datasets to load datasets for urban pipeline analysis. You can control otherwise, but it would be preferable to start with the default.

uv run mcpstack pipeline urbanmapper --new-pipeline my_pipeline.json

๐Ÿ”ง Step 2 โ€” Create a Jupyter ToolConfig

Basically, Jupyter MCP works with some sort of connections between the LLM and the Jupyter instance. This is via a URL and a TOKEN. Hence, the need for a ToolConfig.

uv run mcpstack tools jupyter configure \
    --token YOUR_JUPYTER_TOKEN

# This create a `jupyter_config.json` file
# Ex of a token: 1117bf468693444a5608e882ab3b55d511f354a175a0df02

๐Ÿ”ง Step 3 โ€” Add To The Tool To The Pipeline

uv run mcpstack pipeline jupyter --to-pipeline my_pipeline.json --tool-config jupyter_config.json

๐Ÿ”ง Step 4 โ€” Compose & Run the Pipeline On Claude Desktop

uv run mcpstack build --pipeline my_pipeline.json --config-type claude

Now you can ask the LLM to operate an Urban Mapper's pipeline analysis and export results into Jupyter.

๐Ÿ“ฃ Prompt Used During The Demo Video

Initial Prompt

Hey there! May we build a `UrbanMapper`'s analysis so that we may have the count of complaints per streets in the Downtown Brooklyn of New York City, please?

I believe that the data of interest on huggingface datasets is called `oscur/NYC_311`

We would like to visualise the output of the `UrbanMapper`'s pipeline analysis interactively with their library. Nothing too fancy simply use the library capability nothing more for the time being.

Note: In case you may need to DL some packages / libraries, run `!uv add <package_name>`

Follow-up Prompt

Okay let's now compute the most common type of complaints per drive street in the same location please.  Final pipeline version looks like:


```
# --- Auto-generated by MCP UrbanMapper (YAML-driven defaults) ---
import urban_mapper as um
from urban_mapper.pipeline import UrbanPipeline
from IPython.display import display as _display

# # HFโ†’CSV pre-step
# mapper = um.UrbanMapper()
# data = (
#     mapper.loader
#     .from_huggingface("oscur/NYC_311")
#     .with_columns(longitude_column="Longitude", latitude_column="Latitude")
#     .load()
# )
# data['Longitude'] = data['Longitude'].astype(float)
# data['Latitude'] = data['Latitude'].astype(float)
# data.to_csv("./oscur_NYC_311.csv", index=False)

# 1) Define the pipeline
pipeline = UrbanPipeline([
    ("urban_layer", (
        mapper.urban_layer
        .with_type("streets_roads")
        .from_place("Downtown Brooklyn, New York City", network_type="drive")
        .with_mapping(longitude_column="Longitude", latitude_column="Latitude", output_column="Street Name")
        .build()
    )),
    ("loader", (
        mapper.loader
        .from_file("./oscur_NYC_311.csv")
        .with_columns(longitude_column="Longitude", latitude_column="Latitude")
        .build()
    )),
    ("imputer", (
        mapper.imputer
        .with_type("SimpleGeoImputer")
        .on_columns("Longitude", "Latitude")
        .build()
    )),
    ("filter", mapper.filter.with_type("BoundingBoxFilter").build()),
    ("enricher", (
        mapper.enricher
        .with_data(group_by="Street Name")
        .count_by(output_column="complaint_count")
        .build()
    )),
    ("visualiser", (
        mapper.visual.with_type("Interactive").with_style({"tiles": "CartoDB positron", "legend": True}).build()
    )),
])

# Optional: preview the pipeline structure
pipeline.preview()

# 2) Compose & immediately visualise
_ = pipeline.compose_transform()
_viz = pipeline.visualise(["complaint_count"])
try:
    _display(_viz)
except Exception:
    pass
```

Tip

Try chaining additional tools to build research-ready urban analysis (e.g. ML) workflows.