@cenogram/mcp-server 0.1.0 → 0.1.1
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.
- package/README.md +10 -10
- package/dist/client-id.js +2 -2
- package/dist/index.js +7 -7
- package/dist/tools.js +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,12 +20,12 @@ Manage your keys at [cenogram.pl/ustawienia](https://cenogram.pl/ustawienia).
|
|
|
20
20
|
|
|
21
21
|
## Installation
|
|
22
22
|
|
|
23
|
-
Pick your client. All options below use the hosted server
|
|
23
|
+
Pick your client. All options below use the hosted server - no local install needed (except npx/stdio).
|
|
24
24
|
|
|
25
25
|
<details open>
|
|
26
26
|
<summary><strong>Claude Code</strong></summary>
|
|
27
27
|
|
|
28
|
-
One command
|
|
28
|
+
One command - zero config files:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
claude mcp add cenogram https://mcp.cenogram.pl/mcp \
|
|
@@ -77,7 +77,7 @@ Add to your config file:
|
|
|
77
77
|
}
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
**Stdio fallback** (older versions
|
|
80
|
+
**Stdio fallback** (older versions - requires Node.js >= 18):
|
|
81
81
|
```json
|
|
82
82
|
{
|
|
83
83
|
"mcpServers": {
|
|
@@ -159,7 +159,7 @@ In VS Code: Settings > Cline > MCP Servers. Add:
|
|
|
159
159
|
</details>
|
|
160
160
|
|
|
161
161
|
<details>
|
|
162
|
-
<summary><strong>npx (stdio)
|
|
162
|
+
<summary><strong>npx (stdio) - local/offline</strong></summary>
|
|
163
163
|
|
|
164
164
|
Requires **Node.js >= 18**. Use this if you want to run the server locally instead of connecting to the hosted one.
|
|
165
165
|
|
|
@@ -191,7 +191,7 @@ Requires **Node.js >= 18**. Use this if you want to run the server locally inste
|
|
|
191
191
|
|
|
192
192
|
| Env Variable | Required | Default | Description |
|
|
193
193
|
|---|---|---|---|
|
|
194
|
-
| `CENOGRAM_API_KEY` | **Yes** (stdio) |
|
|
194
|
+
| `CENOGRAM_API_KEY` | **Yes** (stdio) | - | API key from [cenogram.pl/api](https://cenogram.pl/api) |
|
|
195
195
|
| `CENOGRAM_API_URL` | No | `https://cenogram.pl` | API base URL |
|
|
196
196
|
| `MCP_TRANSPORT` | No | `stdio` | Set to `http` for Streamable HTTP mode |
|
|
197
197
|
| `MCP_PORT` | No | `3002` | HTTP server port (HTTP mode only) |
|
|
@@ -235,7 +235,7 @@ You can also use the `--http` CLI flag instead of `MCP_TRANSPORT=http`.
|
|
|
235
235
|
|
|
236
236
|
- Most cities: use the city name directly (e.g., "Gdansk", "Lublin")
|
|
237
237
|
- Warsaw: use district names ("Mokotow", "Srodmiescie", "Wola") -- "Warszawa" won't match
|
|
238
|
-
- Krakow: use sub-districts ("Krakow-Podgorze", "Krakow-Srodmiescie")
|
|
238
|
+
- Krakow: use sub-districts ("Krakow-Podgorze", "Krakow-Srodmiescie") - plain "Krakow" won't match
|
|
239
239
|
- Use `list_locations` to find valid names
|
|
240
240
|
|
|
241
241
|
### Property types
|
|
@@ -269,13 +269,13 @@ This mimics how a property appraiser finds comparable transactions for valuation
|
|
|
269
269
|
|
|
270
270
|
## Troubleshooting
|
|
271
271
|
|
|
272
|
-
**"Error: CENOGRAM_API_KEY is required"**
|
|
272
|
+
**"Error: CENOGRAM_API_KEY is required"** - This only applies to stdio mode. Make sure `CENOGRAM_API_KEY` is set in the `env` block of your MCP config. For HTTP remote, the key goes in the `Authorization` header instead.
|
|
273
273
|
|
|
274
|
-
**npx hangs or fails**
|
|
274
|
+
**npx hangs or fails** - Check your Node.js version with `node -v`. The stdio mode requires Node.js >= 18. If you're on an older version, use the HTTP remote option instead (no Node.js needed).
|
|
275
275
|
|
|
276
|
-
**"Warszawa" returns 0 results**
|
|
276
|
+
**"Warszawa" returns 0 results** - Warsaw uses district names (Mokotow, Wola, Srodmiescie, Bemowo, etc.). Use `list_locations(search="warsz")` to find valid names. Same applies to Krakow (use "Krakow-Podgorze", "Krakow-Srodmiescie", etc.).
|
|
277
277
|
|
|
278
|
-
**401 Unauthorized (HTTP mode)**
|
|
278
|
+
**401 Unauthorized (HTTP mode)** - The `Authorization` header must be `Bearer cngrm_...` (with the `Bearer` prefix). Double-check that the full API key is included, not just the prefix.
|
|
279
279
|
|
|
280
280
|
## Development
|
|
281
281
|
|
package/dist/client-id.js
CHANGED
|
@@ -22,7 +22,7 @@ export function getClientId() {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
catch {
|
|
25
|
-
// File doesn't exist or isn't readable
|
|
25
|
+
// File doesn't exist or isn't readable - generate new
|
|
26
26
|
}
|
|
27
27
|
// Generate and persist
|
|
28
28
|
const id = randomUUID();
|
|
@@ -31,7 +31,7 @@ export function getClientId() {
|
|
|
31
31
|
writeFileSync(CLIENT_ID_FILE, id + "\n", { mode: 0o600 });
|
|
32
32
|
}
|
|
33
33
|
catch {
|
|
34
|
-
// Read-only fs (Docker, sandbox)
|
|
34
|
+
// Read-only fs (Docker, sandbox) - use ephemeral ID
|
|
35
35
|
}
|
|
36
36
|
cachedId = id;
|
|
37
37
|
return id;
|
package/dist/index.js
CHANGED
|
@@ -16,11 +16,11 @@ catch { /* fallback to hardcoded if dist/ used standalone */ }
|
|
|
16
16
|
export function createMcpServer(apiKey) {
|
|
17
17
|
const server = new McpServer({ name: "cenogram-mcp-server", version: PKG_VERSION }, {
|
|
18
18
|
instructions: [
|
|
19
|
-
"Cenogram MCP Server
|
|
19
|
+
"Cenogram MCP Server - 7M+ verified real estate transactions from Poland's official RCN registry (Rejestr Cen Nieruchomości). Transaction prices from notarial deeds - NOT asking/listing prices. Data from 2003 to present, ~380 counties, refreshed every ~2 weeks.",
|
|
20
20
|
"",
|
|
21
|
-
"CRITICAL
|
|
21
|
+
"CRITICAL - District names (ALWAYS verify first):",
|
|
22
22
|
"- NEVER guess district names. Call list_locations(search=\"city\") first.",
|
|
23
|
-
"- Warsaw: use district names (Mokotów, Wola, Śródmieście)
|
|
23
|
+
"- Warsaw: use district names (Mokotów, Wola, Śródmieście) - \"Warszawa\" returns 0 results",
|
|
24
24
|
"- Kraków: Kraków-Śródmieście, Kraków-Podgórze, Kraków-Krowodrza, Kraków-Nowa Huta",
|
|
25
25
|
"- Most cities (Gdańsk, Gdynia, Sopot, Poznań): just the city name, no sub-districts",
|
|
26
26
|
"",
|
|
@@ -29,14 +29,14 @@ export function createMcpServer(apiKey) {
|
|
|
29
29
|
"- Compare locations: list_locations → compare_locations (2-5 districts, requires at least one filter e.g. propertyType)",
|
|
30
30
|
"- Parcel lookup: search_parcels(q, min 3 chars) → search_by_area (use returned lat/lng)",
|
|
31
31
|
"- Address search: search_transactions(location, street, buildingNumber)",
|
|
32
|
-
"- Radius search: search_by_area(lat, lng, radiusKm)
|
|
33
|
-
"- Polygon search: search_by_polygon
|
|
32
|
+
"- Radius search: search_by_area(lat, lng, radiusKm) - for geographic proximity",
|
|
33
|
+
"- Polygon search: search_by_polygon - coordinates are [longitude, latitude], first=last point, max 500 vertices",
|
|
34
34
|
"",
|
|
35
35
|
"Data notes:",
|
|
36
36
|
"- price_per_m2 only meaningful for apartments (propertyType=\"unit\")",
|
|
37
|
-
"- API has no rooms filter
|
|
37
|
+
"- API has no rooms filter - use area as proxy (1-room: 20-35m², 2: 35-55m², 3: 55-90m², 4+: 80-130m²), then post-filter by rooms field in results",
|
|
38
38
|
"- Results paginated (default 10-20). Use page parameter for more.",
|
|
39
|
-
"- For §79-compliant export table or interactive map
|
|
39
|
+
"- For §79-compliant export table or interactive map - direct user to cenogram.pl",
|
|
40
40
|
].join("\n"),
|
|
41
41
|
});
|
|
42
42
|
registerTools(server, apiKey);
|
package/dist/tools.js
CHANGED
|
@@ -27,7 +27,7 @@ export function registerTools(server, apiKey) {
|
|
|
27
27
|
Returns transaction details: address, date, price, area, price/m², property type.
|
|
28
28
|
Use list_locations first to find valid location names.
|
|
29
29
|
Example: search for apartments in Mokotów sold in 2024 above 500,000 PLN.`, {
|
|
30
|
-
location: z.string().optional().describe("Location name
|
|
30
|
+
location: z.string().optional().describe("Location name - city (e.g. 'Kraków', 'Gdańsk') or district (e.g. 'Mokotów', 'Śródmieście'). For Warsaw, use district names (Mokotów, Wola, etc.) - 'Warszawa' won't match. Use list_locations to find valid names."),
|
|
31
31
|
propertyType: z.enum(["land", "building", "developed_land", "unit"]).optional()
|
|
32
32
|
.describe("Property type filter"),
|
|
33
33
|
marketType: z.enum(["primary", "secondary"]).optional()
|
|
@@ -38,7 +38,7 @@ Example: search for apartments in Mokotów sold in 2024 above 500,000 PLN.`, {
|
|
|
38
38
|
dateTo: z.string().optional().describe("End date (YYYY-MM-DD)"),
|
|
39
39
|
street: z.string().optional().describe("Street name filter (partial match, e.g. 'Puławska', 'Trakt Lubelski')"),
|
|
40
40
|
buildingNumber: z.string().optional().describe("Building/house number (e.g. '251C', '12A'). Requires location or street to be set."),
|
|
41
|
-
parcelId: z.string().optional().describe("Exact parcel ID as returned in search results (e.g. '146518_8.0108.27'). Must match exactly
|
|
41
|
+
parcelId: z.string().optional().describe("Exact parcel ID as returned in search results (e.g. '146518_8.0108.27'). Must match exactly - copy from a previous search result's parcel_id field."),
|
|
42
42
|
minArea: z.number().optional().describe("Minimum area in m²"),
|
|
43
43
|
maxArea: z.number().optional().describe("Maximum area in m²"),
|
|
44
44
|
limit: z.number().min(1).max(50).default(10)
|
|
@@ -77,7 +77,7 @@ Example: search for apartments in Mokotów sold in 2024 above 500,000 PLN.`, {
|
|
|
77
77
|
// ── Tool 2: get_price_statistics ────────────────────────────────────
|
|
78
78
|
server.tool("get_price_statistics", `Get price per m² statistics by location for residential apartments in Poland.
|
|
79
79
|
Note: only covers residential units (lokale mieszkalne). For other property types, use search_transactions.
|
|
80
|
-
For Warsaw: use district names (Mokotów, Wola)
|
|
80
|
+
For Warsaw: use district names (Mokotów, Wola) - 'Warszawa' won't match any results.`, {
|
|
81
81
|
location: z.string().optional().describe("Filter by location name (case-insensitive partial match). E.g. 'Kraków' matches 'Kraków-Podgórze', 'Kraków-Śródmieście', etc. Omit for all Poland."),
|
|
82
82
|
}, { readOnlyHint: true }, async (params) => withErrorHandling(async () => {
|
|
83
83
|
const { data: allRows, creditInfo } = await getPricePerM2(apiKey);
|
|
@@ -146,7 +146,7 @@ Returns: total transaction count, date range, breakdown by property type and mar
|
|
|
146
146
|
}));
|
|
147
147
|
// ── Tool 6: list_locations ──────────────────────────────────────────
|
|
148
148
|
server.tool("list_locations", `List available locations (cities and districts) in the database.
|
|
149
|
-
Returns administrative districts
|
|
149
|
+
Returns administrative districts - for most cities, the district name equals the city name.
|
|
150
150
|
For Warsaw: returns district names (Mokotów, Śródmieście, Wola, etc.), not 'Warszawa'.
|
|
151
151
|
For Kraków: returns sub-districts (Kraków-Podgórze, Kraków-Śródmieście, etc.).
|
|
152
152
|
Use the search parameter to filter by name.`, {
|
|
@@ -236,7 +236,7 @@ Requires at least one filter besides districts (e.g., propertyType).
|
|
|
236
236
|
Example: compare Mokotów, Wola, Ursynów for apartments.`, {
|
|
237
237
|
districts: z.string().min(1).describe("Comma-separated district names to compare (2-5). E.g. 'Mokotów,Wola,Ursynów'"),
|
|
238
238
|
propertyType: z.enum(["land", "building", "developed_land", "unit"]).optional()
|
|
239
|
-
.describe("Property type filter (recommended
|
|
239
|
+
.describe("Property type filter (recommended - API requires at least one filter)"),
|
|
240
240
|
marketType: z.enum(["primary", "secondary"]).optional()
|
|
241
241
|
.describe("Market type filter"),
|
|
242
242
|
minPrice: z.number().optional().describe("Minimum price in PLN"),
|