Inspiration

CityScope started from a very simple travel problem: when I arrive in a new city, one of the best ways to understand it is to move through it.

I often want to go for a run or a cycle, see interesting parts of the city, and maybe stop somewhere good for coffee or food. The problem is that doing that usually means stitching together several separate tools: search for a known route, check maps, look up cafés or landmarks, work out whether the route is actually practical, and then somehow get everything onto a map I can use.

CityScope is an attempt to turn that fragmented process into one agentic workflow.

The idea is simple:

Tell CityScope where you are, how you want to explore, and roughly what kind of experience you want. It should turn that into a real route through interesting places, grounded in real city data and Google Maps, and make it useful enough to actually take with you.

For the hackathon, I focused on cycling and running because they are a natural fit for a map-first agent that has to reason across intent, location, route geometry, places, and constraints.

What it does

CityScope is an agentic city exploration and journey-planning application.

A user can choose a city, choose whether they want to run or cycle, provide a starting point, and ask for an experience such as:

a scenic ride a park-heavy run a route through landmarks somewhere good for coffee somewhere to eat during or after the route

CityScope then coordinates several steps:

Gemini interprets the user's intent and converts it into a bounded route-planning decision. Google ADK runs the agent workflow. CityScope selects from curated route concepts for supported cities. City Data MCP can contribute historical mobility evidence where useful. Google Maps Grounding Lite is used to resolve current places such as landmarks, cafés, restaurants, and other useful stops. Deterministic backend logic converts those results into a small, validated set of waypoints. Google Routes generates the actual walking or bicycle route and returns the route geometry. The frontend renders the route, distance, duration, stops, provenance, and map links.

The result is intended to feel less like an analytics dashboard and more like:

“I’ve just arrived in London, I have an hour, I want to explore by bike and get a coffee. Plan it for me.”

How we built it

CityScope uses a deliberately mixed agentic and deterministic architecture.

The core principle is:

AI decides what to do; deterministic software owns computation, validation, routing, and safety boundaries.

The frontend is built with Next.js and React and is deployed to Firebase Hosting.

The backend is a FastAPI application running on Google Cloud Run. It acts as the public trust boundary and coordinates the agent, MCP services, Google Maps, Google Routes, validation, and persistence.

For agent orchestration, CityScope uses Google ADK with a root LlmAgent powered by Gemini 3.5 Flash. Gemini is used for structured planning decisions rather than direct unrestricted tool execution.

The agent can make bounded decisions such as:

identifying the city identifying whether the user wants to run or cycle selecting route intent extracting origin and destination selecting preferences deciding whether current place context is required

The actual route execution is not exposed directly to Gemini.

Instead, a private backend route service calls the Google Routes API using:

WALK for running journeys BICYCLE for cycling journeys

This was an important architectural decision because Google Maps Grounding Lite's MCP routing surface did not provide the bicycle routing and route geometry required for the product.

Google Maps Grounding Lite is used separately for current place context through bounded search_places calls.

CityScope also has a custom City Data MCP service that exposes deterministic mobility analytics over H3-indexed historical city data. The MCP layer is intentionally thin: it exposes approved tools rather than arbitrary database access.

Historical data is processed into canonical models using Python, DuckDB, Parquet, and H3 indexing. The project began with Transport for London cycling data and later expanded its architecture to support multiple cities and providers.

Supporting Google infrastructure includes:

Google Cloud Run Firestore Secret Manager Artifact Registry Firebase Hosting Firebase Authentication Google Maps JavaScript API Google Maps Grounding Lite Google Routes API

Server-side credentials are isolated from the browser and managed separately by purpose.

Challenges

One of the biggest challenges was deciding where the agent should have autonomy and where it should not.

It would have been easy to give the model broad control over every tool and let it construct route requests directly. Instead, I chose to make the agent responsible for interpretation and planning while keeping routing, geospatial computation, validation, and API boundaries deterministic.

That made the system more reliable, but it also required much more explicit contracts between components.

Another challenge was working across different kinds of data.

CityScope combines:

historical mobility data current Google Maps place data route execution data user intent

Those sources have different meanings and different levels of freshness, so the application has to keep them visibly separate rather than blending them into one unsupported claim.

Routing also created a significant architectural challenge.

The original plan was to stay entirely within Google Maps Grounding Lite MCP, but its documented route tool did not support the bicycle mode and polyline geometry required by CityScope. Rather than weaken the product to walking or driving, I introduced a separate private Google Routes API boundary for deterministic bicycle routing.

That ended up being one of the most useful design decisions in the project.

A final challenge was product scope.

CityScope began as a broader urban intelligence system with historical comparisons, H3 visualisation, live mobility data, and investigation workflows. During the final phase of the hackathon, I had to simplify the user experience around the thing that made the project most understandable and useful: helping someone actually explore a city.

That meant keeping the deeper city intelligence as supporting infrastructure while moving the route-planning journey to the centre of the product.

What we learned

The biggest lesson was that useful agentic systems need strong boundaries.

The most effective architecture was not “give the model every tool.” It was to let the model make a small number of high-value decisions, then let deterministic services execute those decisions safely.

Structured outputs, typed contracts, bounded tool calls, validated waypoints, and explicit fallback behaviour made the system easier to debug and easier to trust.

I also learned that provenance is not just an analytics concern.

When an agent combines historical mobility data with current Maps results and route geometry, the user needs to know which source supports which part of the answer. Keeping those boundaries visible improved both the technical architecture and the user experience.

Another important lesson was that production infrastructure is part of agent quality.

Cloud Run deployment, IAM, secret handling, CORS, Firebase integration, MCP service boundaries, provider failures, and fallback behaviour all directly affect whether the agent feels reliable.

Finally, I learned that product clarity matters as much as technical ambition.

The project became stronger when I stopped thinking of CityScope as “an agent that can analyse a city” and started thinking of it as:

“An agent that helps you experience a city.”

What I'm proud of

I am particularly proud that CityScope is not just a chat interface wrapped around a model.

It coordinates multiple systems, makes bounded decisions, retrieves grounded place information, uses real city data, selects and validates route waypoints, executes real walking and cycling routes, and presents the result as something a person can actually use.

The agent is not the whole application. It is the orchestration layer that connects the pieces.

That is the part of the project that best represents what I wanted to explore in this hackathon: how agentic AI can move beyond answering questions and begin coordinating real, useful actions.

What's next

The next step for CityScope is to make route generation richer and more adaptive.

That includes:

better loop and distance-aware route construction more route templates across additional cities richer landmark and food recommendations improved live mobility context multi-turn clarification saved and shareable journeys sending routes and stops more directly to mobile devices deeper personalisation around route style, pace, distance, food preferences, and accessibility

Longer term, the goal is for CityScope to become a lightweight city companion:

arrive somewhere new, tell it how you want to explore, and let the agent turn the city into a journey.

Built With

Share this project:

Updates