ng-postcode-mcp 0.2.3__tar.gz → 0.2.4__tar.gz
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.
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/PKG-INFO +2 -2
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/pyproject.toml +2 -2
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/server.json +2 -2
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/src/ng_postcode_mcp/server.py +14 -2
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/tests/test_resolve_tool.py +7 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/tests/test_tools.py +21 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/.gitignore +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/LICENSE +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/README.md +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/src/ng_postcode_mcp/__init__.py +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/src/ng_postcode_mcp/__main__.py +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/src/ng_postcode_mcp/py.typed +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/tests/test_metadata.py +0 -0
- {ng_postcode_mcp-0.2.3 → ng_postcode_mcp-0.2.4}/tests/test_stdio.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ng-postcode-mcp
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: MCP server for Nigeria's NIPOST digital postcode (NDAPS): validate, look up, reverse-geocode and resolve addresses to postcodes.
|
|
5
5
|
Project-URL: Repository, https://github.com/Adeniyikayodee/ng-postcode
|
|
6
6
|
Project-URL: Issues, https://github.com/Adeniyikayodee/ng-postcode/issues
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Scientific/Engineering :: GIS
|
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Requires-Dist: mcp<3,>=2.3
|
|
25
|
-
Requires-Dist: ng-address-resolver<0.2,>=0.1.
|
|
25
|
+
Requires-Dist: ng-address-resolver<0.2,>=0.1.4
|
|
26
26
|
Requires-Dist: ng-postcode[client]<0.3,>=0.2.1
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ng-postcode-mcp"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "MCP server for Nigeria's NIPOST digital postcode (NDAPS): validate, look up, reverse-geocode and resolve addresses to postcodes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -26,7 +26,7 @@ classifiers = [
|
|
|
26
26
|
"Topic :: Scientific/Engineering :: GIS",
|
|
27
27
|
"Typing :: Typed",
|
|
28
28
|
]
|
|
29
|
-
dependencies = ["mcp>=2.3,<3", "ng-address-resolver>=0.1.
|
|
29
|
+
dependencies = ["mcp>=2.3,<3", "ng-address-resolver>=0.1.4,<0.2", "ng-postcode[client]>=0.2.1,<0.3"]
|
|
30
30
|
|
|
31
31
|
[project.scripts]
|
|
32
32
|
ng-postcode-mcp = "ng_postcode_mcp.server:main"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.Adeniyikayodee/ng-postcode",
|
|
4
4
|
"title": "Nigeria Postcode",
|
|
5
5
|
"description": "Validate, look up and resolve addresses to Nigeria's NIPOST digital postcodes (NDAPS).",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.4",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/Adeniyikayodee/ng-postcode",
|
|
9
9
|
"source": "github",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "pypi",
|
|
15
15
|
"identifier": "ng-postcode-mcp",
|
|
16
|
-
"version": "0.2.
|
|
16
|
+
"version": "0.2.4",
|
|
17
17
|
"runtimeHint": "uvx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
@@ -65,6 +65,7 @@ HINTS = {
|
|
|
65
65
|
"level_not_granted": "The key is not granted this lookup level; use a lower level or "
|
|
66
66
|
f"request more access at {KEY_URL}.",
|
|
67
67
|
}
|
|
68
|
+
LEVEL_2_FIELDS = ("state_name", "lga_name", "locality_name", "address")
|
|
68
69
|
STATUS_HINTS = {
|
|
69
70
|
403: "The key lacks the scope or access level for this request.",
|
|
70
71
|
429: "NIPOST rate limit reached; wait before retrying.",
|
|
@@ -298,11 +299,14 @@ def create_server(
|
|
|
298
299
|
Field(ge=0, le=250, description="Search radius in metres. Defaults to 25."),
|
|
299
300
|
] = None,
|
|
300
301
|
) -> Location:
|
|
301
|
-
"""Return the postcode of the nearest building to a coordinate in Nigeria.
|
|
302
|
+
"""Return the postcode of the nearest building to a coordinate in Nigeria.
|
|
303
|
+
|
|
304
|
+
Names and the house address are withheld unless NG_POSTCODE_MAX_LEVEL is 2 or more.
|
|
305
|
+
"""
|
|
302
306
|
result = await api(ctx).send(
|
|
303
307
|
reverse(Coordinate(lat=latitude, lng=longitude), max_distance_m)
|
|
304
308
|
)
|
|
305
|
-
return Location.model_validate(unwrap(result))
|
|
309
|
+
return capped(Location.model_validate(unwrap(result)), settings.max_level)
|
|
306
310
|
|
|
307
311
|
@server.tool(
|
|
308
312
|
title="Resolve a Nigerian address to a postcode",
|
|
@@ -383,6 +387,14 @@ def geocoder_for(settings: Settings, http: httpx.AsyncClient | None) -> Nominati
|
|
|
383
387
|
)
|
|
384
388
|
|
|
385
389
|
|
|
390
|
+
def capped(location: Location, max_level: int) -> Location:
|
|
391
|
+
"""The location without the fields a level 2 key adds, unless the cap allows them."""
|
|
392
|
+
if max_level >= 2 or location.unit is None:
|
|
393
|
+
return location
|
|
394
|
+
unit = location.unit.model_copy(update=dict.fromkeys(LEVEL_2_FIELDS))
|
|
395
|
+
return location.model_copy(update={"unit": unit})
|
|
396
|
+
|
|
397
|
+
|
|
386
398
|
def completions(found: Autocomplete) -> Completions:
|
|
387
399
|
return Completions(
|
|
388
400
|
segment=None if found.segment is None else found.segment.value,
|
|
@@ -61,6 +61,13 @@ async def call(
|
|
|
61
61
|
return result
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
@pytest.mark.anyio
|
|
65
|
+
async def test_queries_without_landmarks_never_give_a_building() -> None:
|
|
66
|
+
queries = {k: v for k, v in BEHIND_FABIAN.items() if k != "landmarks"}
|
|
67
|
+
answer = (await call(queries)).structured_content
|
|
68
|
+
assert (answer["status"], answer["level"]) == ("partial", "area")
|
|
69
|
+
|
|
70
|
+
|
|
64
71
|
@pytest.mark.anyio
|
|
65
72
|
async def test_the_host_models_reading_drives_the_answer() -> None:
|
|
66
73
|
searched: list[httpx.Request] = []
|
|
@@ -22,6 +22,7 @@ from ng_postcode_mcp.server import (
|
|
|
22
22
|
Location,
|
|
23
23
|
NearestBuilding,
|
|
24
24
|
PostcodeDetails,
|
|
25
|
+
capped,
|
|
25
26
|
unwrap,
|
|
26
27
|
)
|
|
27
28
|
|
|
@@ -181,6 +182,26 @@ async def test_autocomplete_and_reverse() -> None:
|
|
|
181
182
|
assert (near.structured_content["found"], near.structured_content["radius_m"]) == (False, 25.0)
|
|
182
183
|
|
|
183
184
|
|
|
185
|
+
def test_the_level_cap_withholds_names_and_addresses() -> None:
|
|
186
|
+
unit = NearestBuilding(
|
|
187
|
+
postcode="EK-01-A03-FK-01",
|
|
188
|
+
display="EK 01 A03 FK 01",
|
|
189
|
+
distance_m=8.0,
|
|
190
|
+
confidence="high",
|
|
191
|
+
state_name="EKITI",
|
|
192
|
+
lga_name="ADO EKITI",
|
|
193
|
+
locality_name="ADO EKITI",
|
|
194
|
+
address="NTA ROAD",
|
|
195
|
+
)
|
|
196
|
+
found = Location(
|
|
197
|
+
found=True, unit=unit, area=None, district=None, state=None, message=None, radius_m=25.0
|
|
198
|
+
)
|
|
199
|
+
assert capped(found, 2) == found
|
|
200
|
+
held = capped(found, 1).unit
|
|
201
|
+
assert held is not None
|
|
202
|
+
assert (held.postcode, held.address, held.state_name) == ("EK-01-A03-FK-01", None, None)
|
|
203
|
+
|
|
204
|
+
|
|
184
205
|
@pytest.mark.anyio
|
|
185
206
|
async def test_rejects_out_of_range_arguments() -> None:
|
|
186
207
|
result = await call("find_postcode_at_location", {"latitude": 95, "longitude": 5.22})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|