@cenogram/mcp-server 0.6.0 → 0.11.0

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.
@@ -26,7 +26,8 @@ function extractCreditInfo(res) {
26
26
  const cost = parseInt(res.headers.get("X-Credits-Cost") ?? "", 10);
27
27
  if (isNaN(balance) || isNaN(cost))
28
28
  return null;
29
- return { balance, cost };
29
+ const refunded = parseInt(res.headers.get("X-Credits-Refunded") ?? "", 10);
30
+ return isNaN(refunded) ? { balance, cost } : { balance, cost, refunded };
30
31
  }
31
32
  export const OAUTH_CTX_PREFIX = "\x01";
32
33
  export function encodeOAuthCtx(userId, grantId) {
@@ -94,15 +95,56 @@ export function formatRetryAfter(seconds) {
94
95
  return unit(Math.ceil(seconds / 3600), "hour");
95
96
  return unit(Math.ceil(seconds / 86400), "day");
96
97
  }
98
+ export class ApiHttpError extends Error {
99
+ status;
100
+ code;
101
+ authMode;
102
+ retryAfterSeconds;
103
+ constructor(message, opts) {
104
+ super(message);
105
+ this.name = "ApiHttpError";
106
+ this.status = opts.status;
107
+ this.code = opts.code;
108
+ this.authMode = opts.authMode;
109
+ this.retryAfterSeconds = opts.retryAfterSeconds;
110
+ }
111
+ }
112
+ export function isExpectedApiError(error) {
113
+ if (!(error instanceof ApiHttpError))
114
+ return false;
115
+ const { status, authMode, code } = error;
116
+ if (status >= 500)
117
+ return false;
118
+ switch (status) {
119
+ case 400:
120
+ case 402:
121
+ case 404:
122
+ case 409:
123
+ case 410:
124
+ case 422:
125
+ case 429:
126
+ return true;
127
+ case 401:
128
+ return authMode !== "oauth";
129
+ case 403:
130
+ return code === "email_not_verified";
131
+ default:
132
+ return false;
133
+ }
134
+ }
97
135
  async function handleErrorResponse(res, apiKey) {
136
+ const mode = getAuthMode(apiKey ?? process.env.CENOGRAM_API_KEY);
98
137
  if (res.status === 429) {
99
138
  const seconds = parseRetryAfterSeconds(res.headers?.get?.("Retry-After"));
100
139
  const wait = seconds !== null ? ` Retry in ${formatRetryAfter(seconds)}.` : " Retry shortly.";
101
- throw new Error(`Too many requests - this is a rate limit, not an exhausted allowance.${wait}`);
140
+ throw new ApiHttpError(`Too many requests - this is a rate limit, not an exhausted allowance.${wait}`, { status: 429, authMode: mode, retryAfterSeconds: seconds ?? undefined });
102
141
  }
103
142
  const body = (await res.json().catch(() => ({})));
104
- const mode = getAuthMode(apiKey ?? process.env.CENOGRAM_API_KEY);
105
- throw new Error(authErrorMessage(res.status, mode, body));
143
+ throw new ApiHttpError(authErrorMessage(res.status, mode, body), {
144
+ status: res.status,
145
+ code: typeof body.error === "string" ? body.error : undefined,
146
+ authMode: mode,
147
+ });
106
148
  }
107
149
  function toQueryParams(obj) {
108
150
  const params = {};
@@ -175,6 +217,10 @@ export function getTransactions(p, apiKey) {
175
217
  floodRisk: p.floodRisk,
176
218
  heritageStatus: p.heritageStatus,
177
219
  landslideRisk: p.landslideRisk,
220
+ landUse: p.landUse,
221
+ buildingStoreys: p.buildingStoreys,
222
+ minFootprintArea: p.minFootprintArea,
223
+ maxFootprintArea: p.maxFootprintArea,
178
224
  minPrice: p.minPrice,
179
225
  maxPrice: p.maxPrice,
180
226
  dateFrom: p.dateFrom,
@@ -207,6 +253,10 @@ export function getTransactionsSummary(p, apiKey) {
207
253
  floodRisk: p.floodRisk,
208
254
  heritageStatus: p.heritageStatus,
209
255
  landslideRisk: p.landslideRisk,
256
+ landUse: p.landUse,
257
+ buildingStoreys: p.buildingStoreys,
258
+ minFootprintArea: p.minFootprintArea,
259
+ maxFootprintArea: p.maxFootprintArea,
210
260
  minPrice: p.minPrice,
211
261
  maxPrice: p.maxPrice,
212
262
  dateFrom: p.dateFrom,
@@ -247,6 +297,17 @@ export function getPriceSpreadLocations(params, apiKey) {
247
297
  search: params.search,
248
298
  }), apiKey);
249
299
  }
300
+ export function getFloodRisk(params, apiKey) {
301
+ return fetchApi("/api/v1/flood-risk", toQueryParams({
302
+ location: params.location,
303
+ teryt: params.teryt,
304
+ }), apiKey);
305
+ }
306
+ export function getFloodRiskLocations(params, apiKey) {
307
+ return fetchApi("/api/v1/flood-risk/locations", toQueryParams({
308
+ search: params.search,
309
+ }), apiKey);
310
+ }
250
311
  export function getValuation(params, apiKey) {
251
312
  return fetchApi("/api/v1/valuations", toQueryParams({
252
313
  lat: params.lat,
@@ -261,12 +322,43 @@ export function getValuation(params, apiKey) {
261
322
  export function getLocations(parent, apiKey) {
262
323
  return fetchApi("/api/v1/locations", parent ? { parent } : undefined, apiKey);
263
324
  }
325
+ export function searchLocations(q, apiKey) {
326
+ return fetchApi("/api/v1/locations", { search: q }, apiKey);
327
+ }
264
328
  export function getPriceHistogram(bins = 20, max = 3_000_000, apiKey) {
265
329
  return fetchApi("/api/v1/stats/price-histogram", toQueryParams({ bins, max }), apiKey);
266
330
  }
267
331
  export function searchParcels(q, limit, apiKey) {
268
332
  return fetchApi("/api/v1/parcels/search", toQueryParams({ q, limit }), apiKey);
269
333
  }
334
+ export function listParcels(p, apiKey) {
335
+ return fetchApi("/api/v1/parcels", toQueryParams({
336
+ location: p.location,
337
+ teryt: p.teryt,
338
+ bbox: p.bbox,
339
+ lat: p.lat,
340
+ lng: p.lng,
341
+ radiusKm: p.radiusKm,
342
+ minArea: p.minArea,
343
+ maxArea: p.maxArea,
344
+ street: p.street,
345
+ buildingNumber: p.buildingNumber,
346
+ limit: p.limit,
347
+ cursor: p.cursor,
348
+ }), apiKey);
349
+ }
350
+ export function getStreets(scope, apiKey) {
351
+ return fetchApi("/api/v1/streets", toQueryParams({ ...scope }), apiKey);
352
+ }
353
+ export function getParcelsMap(bbox, limit, apiKey) {
354
+ return fetchApi("/api/v1/parcels/map", toQueryParams({ bbox, limit }), apiKey);
355
+ }
356
+ export function searchParcelsByPolygon(polygon, limit, apiKey) {
357
+ const body = { polygon };
358
+ if (limit != null)
359
+ body.limit = limit;
360
+ return fetchApiPost("/api/v1/parcels/spatial", body, apiKey);
361
+ }
270
362
  export function resolveParcel(params, apiKey) {
271
363
  return fetchApi("/api/v1/parcels/resolve", toQueryParams({
272
364
  q: params.q,
@@ -279,6 +371,10 @@ export function getParcelReport(parcelKey, apiKey) {
279
371
  const urlKey = parcelKey.trim().replace(/\//g, "-");
280
372
  return fetchApi(`/api/v1/parcels/${encodeURIComponent(urlKey)}/report`, undefined, apiKey);
281
373
  }
374
+ export function getParcelLandClass(parcelKey, apiKey) {
375
+ const urlKey = parcelKey.trim().replace(/\//g, "-");
376
+ return fetchApi(`/api/v1/parcels/${encodeURIComponent(urlKey)}/land-class`, undefined, apiKey);
377
+ }
282
378
  export function searchByPolygon(p, apiKey) {
283
379
  const body = { polygon: p.polygon };
284
380
  if (p.propertyType != null)
@@ -315,6 +411,14 @@ export function searchByPolygon(p, apiKey) {
315
411
  body.rooms = p.rooms;
316
412
  if (p.floor)
317
413
  body.floor = p.floor;
414
+ if (p.landUse)
415
+ body.landUse = p.landUse;
416
+ if (p.buildingStoreys)
417
+ body.buildingStoreys = p.buildingStoreys;
418
+ if (p.minFootprintArea != null)
419
+ body.minFootprintArea = p.minFootprintArea;
420
+ if (p.maxFootprintArea != null)
421
+ body.maxFootprintArea = p.maxFootprintArea;
318
422
  if (p.limit != null)
319
423
  body.limit = p.limit;
320
424
  return fetchApiPost("/api/v1/transactions/spatial", body, apiKey);
@@ -353,9 +457,18 @@ export function getTransactionHeritage(transactionId, apiKey) {
353
457
  export function getTransactionLandslide(transactionId, apiKey) {
354
458
  return fetchApi(`/api/v1/transactions/${transactionId}/landslide`, undefined, apiKey);
355
459
  }
460
+ export function getTransactionNature(transactionId, apiKey) {
461
+ return fetchApi(`/api/v1/transactions/${transactionId}/nature`, undefined, apiKey);
462
+ }
463
+ export function getTransactionSubsurface(transactionId, apiKey) {
464
+ return fetchApi(`/api/v1/transactions/${transactionId}/subsurface`, undefined, apiKey);
465
+ }
356
466
  export function getTransactionSurroundings(transactionId, apiKey) {
357
467
  return fetchApi(`/api/v1/transactions/${transactionId}/surroundings`, undefined, apiKey);
358
468
  }
469
+ export function getTransactionRoads(transactionId, apiKey) {
470
+ return fetchApi(`/api/v1/transactions/${transactionId}/roads`, undefined, apiKey);
471
+ }
359
472
  export function getTransactionTransit(transactionId, apiKey) {
360
473
  return fetchApi(`/api/v1/transactions/${transactionId}/transit`, undefined, apiKey);
361
474
  }
@@ -41,7 +41,7 @@ export function authErrorMessage(status, mode, body = {}) {
41
41
  return "API key rejected. Check https://cenogram.pl/ustawienia#api-keys if it's still active.";
42
42
  case 402: {
43
43
  const explanation = specificField(body.message);
44
- if (body.error === "trial_expired" && explanation) {
44
+ if (explanation) {
45
45
  const upgrade = specificField(body.upgrade);
46
46
  return upgrade && !explanation.includes(upgrade) ? `${sentence(explanation)} (${upgrade})` : explanation;
47
47
  }
@@ -1,4 +1,4 @@
1
- import type { Transaction, TransactionsResponse, TransactionsSummary, StatsResponse, PricePerM2Row, HistogramBin, ParcelSearchResponse, ParcelResolveResponse, SpatialSearchResponse, CompareResponse, LocationItem, RentalYieldResponse, RentalYieldLocationsResponse, PriceSpreadResponse, PriceSpreadLocationsResponse, ValuationResponse, BuildingBreakdownResponse, FloodBreakdownResponse, HeritageBreakdownResponse, LandslideBreakdownResponse, SurroundingsResponse, TransitBreakdownResponse, PermitsResponse, PlanningResponse, FarmlandResponse, DemographicsResponse, InfrastructureSignalsResponse, ParcelReportResponse } from "./api-client.js";
1
+ import type { Transaction, TransactionsResponse, TransactionsSummary, StatsResponse, PricePerM2Row, HistogramBin, ParcelSearchResponse, ParcelResolveResponse, ParcelListResponse, StreetListResponse, ParcelFeatureCollection, CorpusCoverage, SpatialSearchResponse, CompareResponse, LocationItem, LocationSearchItem, RentalYieldResponse, RentalYieldLocationsResponse, PriceSpreadResponse, PriceSpreadLocationsResponse, FloodRiskResponse, FloodRiskLocationsResponse, ValuationResponse, BuildingBreakdownResponse, BuildingAgeEstimate, FloodBreakdownResponse, HeritageBreakdownResponse, LandslideBreakdownResponse, NatureBreakdownResponse, SubsurfaceBreakdownResponse, SurroundingsResponse, RoadsBreakdownResponse, TransitBreakdownResponse, PermitsResponse, PlanningResponse, FarmlandResponse, DemographicsResponse, InfrastructureSignalsResponse, ParcelReportResponse, ParcelLandClassResponse } from "./api-client.js";
2
2
  export declare const MARKET_CAVEAT = "Note: median/average prices are market-based \u2014 fractional ownership shares and non-market deeds (public tenders, foreclosures, privileged/subsidized sales) are excluded from price aggregates. Transaction counts and coverage stay complete.";
3
3
  export declare function formatPLN(value: number | null | undefined): string;
4
4
  export declare function formatArea(m2: number | null | undefined): string;
@@ -9,18 +9,27 @@ export declare function formatMarketOverview(stats: StatsResponse): string;
9
9
  export declare function formatPriceStats(rows: PricePerM2Row[], location?: string): string;
10
10
  export declare function formatHistogram(bins: HistogramBin[]): string;
11
11
  export declare function formatParcelResults(res: ParcelSearchResponse, query: string): string;
12
+ export declare function formatCorpusCoverage(cov: CorpusCoverage | null | undefined): string;
13
+ export declare function formatParcelList(res: ParcelListResponse, scope: string, creditsRefunded?: boolean): string;
14
+ export declare function formatStreetList(res: StreetListResponse, q: string, scope: string): string;
15
+ export declare function formatParcelFeatures(res: ParcelFeatureCollection, scope: string): string;
12
16
  export declare function formatParcelResolve(res: ParcelResolveResponse): string;
13
17
  export declare function formatSpatialResults(res: SpatialSearchResponse): string;
18
+ export declare function formatBuildingAge(age: BuildingAgeEstimate | undefined): string;
14
19
  export declare function formatBuildingBreakdown(res: BuildingBreakdownResponse): string;
15
20
  export declare function formatFloodBreakdown(res: FloodBreakdownResponse): string;
16
21
  export declare function formatHeritageBreakdown(res: HeritageBreakdownResponse): string;
17
22
  export declare function formatLandslideBreakdown(res: LandslideBreakdownResponse): string;
23
+ export declare function formatNatureBreakdown(res: NatureBreakdownResponse): string;
24
+ export declare function formatSubsurfaceBreakdown(res: SubsurfaceBreakdownResponse): string;
18
25
  export declare function formatSurroundings(res: SurroundingsResponse): string;
26
+ export declare function formatRoads(res: RoadsBreakdownResponse): string;
19
27
  export declare function formatTransitBreakdown(res: TransitBreakdownResponse): string;
20
28
  export declare function formatPermitsBreakdown(res: PermitsResponse): string;
21
29
  export declare function formatPlanningBreakdown(res: PlanningResponse): string;
22
30
  export declare function formatFarmland(res: FarmlandResponse): string;
23
31
  export declare function formatLocationHierarchy(items: LocationItem[], parent?: string): string;
32
+ export declare function formatLocationSearch(items: LocationSearchItem[], rcnOnly: string[], query: string): string;
24
33
  export declare function formatCompareResults(res: CompareResponse): string;
25
34
  export declare function formatDemographics(r: DemographicsResponse): string;
26
35
  export declare function formatInfrastructureSignals(r: InfrastructureSignalsResponse): string;
@@ -28,5 +37,9 @@ export declare function formatRentalYield(r: RentalYieldResponse): string;
28
37
  export declare function formatRentalYieldLocations(r: RentalYieldLocationsResponse): string;
29
38
  export declare function formatPriceSpread(r: PriceSpreadResponse): string;
30
39
  export declare function formatPriceSpreadLocations(r: PriceSpreadLocationsResponse): string;
40
+ export declare function formatFloodRisk(r: FloodRiskResponse): string;
41
+ export declare function formatFloodRiskLocations(r: FloodRiskLocationsResponse): string;
31
42
  export declare function formatValuation(r: ValuationResponse): string;
43
+ export declare const REPORT_LAYER_ORDER: Array<[string, string]>;
32
44
  export declare function formatParcelReport(res: ParcelReportResponse): string;
45
+ export declare function formatParcelLandClass(res: ParcelLandClassResponse): string;