From 4187b752a9c6ff60a49981de142cfa5014267ecd Mon Sep 17 00:00:00 2001 From: Anandaroop Roy Date: Wed, 8 Oct 2025 22:33:20 -0400 Subject: [PATCH 1/2] feat: add a nominatim search --- .env.example | 3 + .gitignore | 1 + app/controllers/mcp_controller.rb | 4 +- app/mcp/tools/nominatim/search.rb | 95 +++++++++++++++++++++++++++++++ lib/nominatim.rb | 45 +++++++++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 .env.example create mode 100644 app/mcp/tools/nominatim/search.rb create mode 100644 lib/nominatim.rb diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e165d3d --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +PORT=3000 +GEONAMES_USERNAME=REPLACE +NOMINATIM_USER_AGENT=https://github.com/anandaroop/geomcp diff --git a/.gitignore b/.gitignore index c3e206a..f43521f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ # Ignore all environment files. /.env* +!/.env.example # Ignore all logfiles and tempfiles. /log/* diff --git a/app/controllers/mcp_controller.rb b/app/controllers/mcp_controller.rb index d72ada1..a2a10f8 100644 --- a/app/controllers/mcp_controller.rb +++ b/app/controllers/mcp_controller.rb @@ -13,7 +13,9 @@ def index Tools::GeoNames::Search, Tools::GeoJson::Preview, Tools::Wikipedia::Search, - Tools::Wikipedia::GetPage + Tools::Wikipedia::GetPage, + Tools::Nominatim::Search + ], resources: [ MCP::Resource.new( diff --git a/app/mcp/tools/nominatim/search.rb b/app/mcp/tools/nominatim/search.rb new file mode 100644 index 0000000..29b8bf1 --- /dev/null +++ b/app/mcp/tools/nominatim/search.rb @@ -0,0 +1,95 @@ +class Tools::Nominatim::Search < MCP::Tool + tool_name "nominatim_search" + description "Search for toponyms using the Nominatim API" + input_schema( + properties: { + q: {type: "string", description: "Query terms to search for (may include a feature type, or a parent feature name)"}, + limit: {type: "number", description: "Maximum number of results to return (default 10, maximum 40)", default: 10, maximum: 40}, + namedetails: {type: "boolean", description: "Whether to include additional names for the toponym", default: false}, + countrycodes: {type: "string", description: "Comma-separated list of ISO 3166-1 alpha-2 country codes to restrict the search", default: nil}, + polygon_geojson: {type: "boolean", description: "Whether to return the polygon representation of the feature", default: false}, + feature_type: {type: "string", description: "Restrict results to country, state, city, settlement", default: nil, enum: ["country", "state", "city", "settlement"]} + }, + required: ["q"] + ) + output_schema( + type: "object", + properties: { + type: {type: "string"}, + licence: {type: "string"}, + features: { + type: "array", + items: { + type: "object", + properties: { + type: {type: "string", enum: ["Feature"]}, + properties: { + type: "object", + properties: { + osm_id: {type: "number", description: "OpenStreetMap unique identifier"}, + osm_type: {type: "string", description: "Type of OpenStreetMap element", enum: ["node", "way", "relation"]}, + name: {type: "string", description: "Name of the toponym"}, + display_name: {type: "string", description: "Comma-separated reverse-hierarchical string"}, + place_id: {type: "number"}, + place_rank: {type: "number"}, + importance: {type: "number"}, + addresstype: {type: "string"}, + category: {type: "string"}, + type: {type: "string"} + }, + required: ["osm_type", "osm_id", "name"] + }, + bbox: { + type: "array", items: {type: "number"}, description: "Array of bounding box coordinates [south, north, west, east]" + }, + geometry: { + type: "object", + properties: { + type: {type: "string", description: "GeoJSON geometry type", enum: ["Point", "LineString", "Polygon", "MultiPoint", "MultiLineString", "MultiPolygon"]}, + coordinates: { + type: "array", + items: {any_of: [{type: "number"}, {type: "array", items: {type: "number"}}]}, + description: "Array of coordinates [longitude, latitude] for a Point, or array of such arrays for other geometry types" + } + }, + required: ["type", "coordinates"] + } + }, + required: ["type", "properties", "geometry"] + } + } + }, + required: ["type", "features"] + ) + + class << self + def call( + q:, + limit: nil, + namedetails: false, + countrycodes: nil, + polygon_geojson: false, + feature_type: nil, + server_context: nil + ) + result = Nominatim.search( + q, + limit: limit, + namedetails: namedetails, + countrycodes: countrycodes, + polygon_geojson: polygon_geojson, + feature_type: feature_type + ) + + output_schema.validate_result(result) + + MCP::Tool::Response.new( + [{ + type: "text", + text: result.to_json + }], + structured_content: result + ) + end + end +end diff --git a/lib/nominatim.rb b/lib/nominatim.rb new file mode 100644 index 0000000..af818cc --- /dev/null +++ b/lib/nominatim.rb @@ -0,0 +1,45 @@ +class Nominatim + include HTTParty + + def self.search( + query, + limit: nil, + namedetails: false, + countrycodes: nil, + polygon_geojson: false, + feature_type: nil + ) + params = { + q: query, + format: "geojson" + } + params[:limit] = limit if limit + params[:namedetails] = 1 if namedetails + params[:countrycodes] = countrycodes if countrycodes + params[:polygon_geojson] = 1 if polygon_geojson + params[:featuretype] = feature_type if feature_type + + options = { + query: params, + headers: {"User-Agent" => ENV["NOMINATIM_USER_AGENT"]} + } + Rails.logger.info("Nominatim API request: #{params}") + response = get("https://nominatim.openstreetmap.org/search", options) + pp response + response.to_h + end + + # def self.lookup(osm_id) + # params = { + # osm_ids: "R" + osm_id.to_s, # R for relation may not always be right + # format: "geojson" + # } + # options = { + # query: params, + # headers: {"User-Agent" => ENV["NOMINATIM_USER_AGENT"]} + # } + # Rails.logger.info("Nominatim API request: #{params}") + # response = get("https://nominatim.openstreetmap.org/lookup", options) + # response.to_h + # end +end From bf5169078008e988e867e224e64b66880e3c9bfe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 21:24:08 +0000 Subject: [PATCH 2/2] build(deps): bump solid_cache from 1.0.7 to 1.0.8 Bumps [solid_cache](https://github.com/rails/solid_cache) from 1.0.7 to 1.0.8. - [Release notes](https://github.com/rails/solid_cache/releases) - [Commits](https://github.com/rails/solid_cache/compare/v1.0.7...v1.0.8) --- updated-dependencies: - dependency-name: solid_cache dependency-version: 1.0.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e4976f2..54093bc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -87,7 +87,7 @@ GEM base64 (0.3.0) bcrypt_pbkdf (1.1.1) benchmark (0.4.1) - bigdecimal (3.2.3) + bigdecimal (3.3.1) bindex (0.8.1) bootsnap (1.18.6) msgpack (~> 1.2) @@ -116,7 +116,7 @@ GEM dotenv (3.1.8) drb (2.2.3) ed25519 (1.4.0) - erb (5.0.2) + erb (5.0.3) erubi (1.13.1) et-orbi (1.3.0) tzinfo @@ -125,7 +125,7 @@ GEM raabro (~> 1.4) geo_names (1.0.2) rest-client (~> 2) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) http-accept (1.7.0) http-cookie (1.0.8) @@ -183,7 +183,7 @@ GEM mime-types-data (~> 3.2025, >= 3.2025.0507) mime-types-data (3.2025.0916) mini_mime (1.1.5) - minitest (5.25.5) + minitest (5.26.0) msgpack (1.8.0) multi_xml (0.7.2) bigdecimal (~> 3.1) @@ -222,7 +222,7 @@ GEM parser (3.3.9.0) ast (~> 2.4.1) racc - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) prism (1.5.1) @@ -241,7 +241,7 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) - rack (3.2.1) + rack (3.2.2) rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) @@ -280,9 +280,10 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.3.0) - rdoc (6.14.2) + rdoc (6.15.0) erb psych (>= 4.0.0) + tsort regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) @@ -334,7 +335,7 @@ GEM activejob (>= 7.2) activerecord (>= 7.2) railties (>= 7.2) - solid_cache (1.0.7) + solid_cache (1.0.8) activejob (>= 7.2) activerecord (>= 7.2) railties (>= 7.2) @@ -380,6 +381,7 @@ GEM thruster (0.1.15-arm64-darwin) thruster (0.1.15-x86_64-linux) timeout (0.4.3) + tsort (0.2.0) turbo-rails (2.0.16) actionpack (>= 7.1.0) railties (>= 7.1.0) @@ -388,7 +390,7 @@ GEM unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) - uri (1.0.3) + uri (1.0.4) useragent (0.16.11) web-console (4.2.1) actionview (>= 6.0.0)