@ignfab/geocontext 0.9.3 → 0.9.5
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 +46 -28
- package/dist/gpf/adminexpress.js +7 -27
- package/dist/gpf/adminexpress.js.map +1 -1
- package/dist/gpf/parcellaire-express.js +13 -33
- package/dist/gpf/parcellaire-express.js.map +1 -1
- package/dist/gpf/urbanisme.d.ts +4 -2
- package/dist/gpf/urbanisme.js +20 -61
- package/dist/gpf/urbanisme.js.map +1 -1
- package/dist/gpf/wfs.d.ts +2 -2
- package/dist/gpf/wfs.js +17 -71
- package/dist/gpf/wfs.js.map +1 -1
- package/dist/helpers/distance.d.ts +4 -2
- package/dist/helpers/distance.js +20 -5
- package/dist/helpers/distance.js.map +1 -1
- package/dist/helpers/http.d.ts +4 -0
- package/dist/helpers/http.js +103 -1
- package/dist/helpers/http.js.map +1 -1
- package/dist/helpers/jsonSchema.d.ts +3 -0
- package/dist/helpers/jsonSchema.js +8 -0
- package/dist/helpers/jsonSchema.js.map +1 -0
- package/dist/helpers/schemas.d.ts +13 -0
- package/dist/helpers/schemas.js +18 -0
- package/dist/helpers/schemas.js.map +1 -0
- package/dist/helpers/wfs.d.ts +27 -0
- package/dist/helpers/wfs.js +55 -0
- package/dist/helpers/wfs.js.map +1 -0
- package/dist/helpers/wfs_internal/compile.d.ts +46 -0
- package/dist/helpers/wfs_internal/compile.js +595 -0
- package/dist/helpers/wfs_internal/compile.js.map +1 -0
- package/dist/helpers/wfs_internal/request.d.ts +38 -0
- package/dist/helpers/wfs_internal/request.js +92 -0
- package/dist/helpers/wfs_internal/request.js.map +1 -0
- package/dist/helpers/wfs_internal/response.d.ts +21 -0
- package/dist/helpers/wfs_internal/response.js +29 -0
- package/dist/helpers/wfs_internal/response.js.map +1 -0
- package/dist/helpers/wfs_internal/schema.d.ts +167 -0
- package/dist/helpers/wfs_internal/schema.js +81 -0
- package/dist/helpers/wfs_internal/schema.js.map +1 -0
- package/dist/index.js +47 -25
- package/dist/index.js.map +1 -1
- package/dist/tools/AdminexpressTool.d.ts +52 -2
- package/dist/tools/AdminexpressTool.js +11 -14
- package/dist/tools/AdminexpressTool.js.map +1 -1
- package/dist/tools/AltitudeTool.d.ts +2 -2
- package/dist/tools/AltitudeTool.js +4 -13
- package/dist/tools/AltitudeTool.js.map +1 -1
- package/dist/tools/AssietteSupTool.d.ts +55 -3
- package/dist/tools/AssietteSupTool.js +12 -15
- package/dist/tools/AssietteSupTool.js.map +1 -1
- package/dist/tools/CadastreTool.d.ts +52 -2
- package/dist/tools/CadastreTool.js +13 -15
- package/dist/tools/CadastreTool.js.map +1 -1
- package/dist/tools/GeocodeTool.d.ts +2 -2
- package/dist/tools/GeocodeTool.js +6 -4
- package/dist/tools/GeocodeTool.js.map +1 -1
- package/dist/tools/GpfWfsDescribeTypeTool.d.ts +16 -16
- package/dist/tools/GpfWfsDescribeTypeTool.js +4 -3
- package/dist/tools/GpfWfsDescribeTypeTool.js.map +1 -1
- package/dist/tools/GpfWfsGetFeaturesTool.d.ts +170 -44
- package/dist/tools/GpfWfsGetFeaturesTool.js +161 -114
- package/dist/tools/GpfWfsGetFeaturesTool.js.map +1 -1
- package/dist/tools/GpfWfsSearchTypesTool.d.ts +8 -2
- package/dist/tools/GpfWfsSearchTypesTool.js +12 -9
- package/dist/tools/GpfWfsSearchTypesTool.js.map +1 -1
- package/dist/tools/UrbanismeTool.d.ts +53 -3
- package/dist/tools/UrbanismeTool.js +9 -15
- package/dist/tools/UrbanismeTool.js.map +1 -1
- package/package.json +8 -7
- package/dist/resources/WfsCqlFilterResource.d.ts +0 -10
- package/dist/resources/WfsCqlFilterResource.js +0 -23
- package/dist/resources/WfsCqlFilterResource.js.map +0 -1
- package/dist/resources/content/wfs-cql-filter.md +0 -215
- package/dist/tools/GpfWfsListTypesTool.d.ts +0 -22
- package/dist/tools/GpfWfsListTypesTool.js +0 -26
- package/dist/tools/GpfWfsListTypesTool.js.map +0 -1
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
import { MCPTool } from "mcp-framework";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
property_names: z.ZodOptional<z.ZodString>;
|
|
6
|
-
sort_by: z.ZodOptional<z.ZodString>;
|
|
7
|
-
count: z.ZodOptional<z.ZodNumber>;
|
|
8
|
-
cql_filter: z.ZodOptional<z.ZodString>;
|
|
9
|
-
result_type: z.ZodOptional<z.ZodEnum<["results", "hits", "url"]>>;
|
|
10
|
-
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
cql_filter?: string;
|
|
12
|
-
typename?: string;
|
|
13
|
-
property_names?: string;
|
|
14
|
-
sort_by?: string;
|
|
15
|
-
count?: number;
|
|
16
|
-
result_type?: "results" | "hits" | "url";
|
|
17
|
-
}, {
|
|
18
|
-
cql_filter?: string;
|
|
19
|
-
typename?: string;
|
|
20
|
-
property_names?: string;
|
|
21
|
-
sort_by?: string;
|
|
22
|
-
count?: number;
|
|
23
|
-
result_type?: "results" | "hits" | "url";
|
|
24
|
-
}>;
|
|
25
|
-
type GpfWfsGetFeaturesInput = z.infer<typeof gpfWfsGetFeaturesInputSchema>;
|
|
2
|
+
import type { Collection } from "@ignfab/gpf-schema-store";
|
|
3
|
+
import { type CompiledRequest } from "../helpers/wfs_internal/request.js";
|
|
4
|
+
import { type GpfWfsGetFeaturesInput } from "../helpers/wfs_internal/schema.js";
|
|
26
5
|
declare class GpfWfsGetFeaturesTool extends MCPTool<GpfWfsGetFeaturesInput> {
|
|
27
6
|
name: string;
|
|
28
7
|
title: string;
|
|
@@ -33,28 +12,121 @@ declare class GpfWfsGetFeaturesTool extends MCPTool<GpfWfsGetFeaturesInput> {
|
|
|
33
12
|
openWorldHint: boolean;
|
|
34
13
|
};
|
|
35
14
|
description: string;
|
|
36
|
-
schema:
|
|
37
|
-
typename:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
15
|
+
schema: import("zod").ZodObject<{
|
|
16
|
+
typename: import("zod").ZodString;
|
|
17
|
+
limit: import("zod").ZodDefault<import("zod").ZodNumber>;
|
|
18
|
+
result_type: import("zod").ZodDefault<import("zod").ZodEnum<["results", "hits", "request"]>>;
|
|
19
|
+
select: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
20
|
+
order_by: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
|
|
21
|
+
property: import("zod").ZodString;
|
|
22
|
+
direction: import("zod").ZodDefault<import("zod").ZodEnum<["asc", "desc"]>>;
|
|
23
|
+
}, "strict", import("zod").ZodTypeAny, {
|
|
24
|
+
property?: string;
|
|
25
|
+
direction?: "asc" | "desc";
|
|
26
|
+
}, {
|
|
27
|
+
property?: string;
|
|
28
|
+
direction?: "asc" | "desc";
|
|
29
|
+
}>, "many">>;
|
|
30
|
+
where: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
|
|
31
|
+
property: import("zod").ZodString;
|
|
32
|
+
operator: import("zod").ZodEnum<["eq", "ne", "lt", "lte", "gt", "gte", "in", "is_null"]>;
|
|
33
|
+
value: import("zod").ZodOptional<import("zod").ZodString>;
|
|
34
|
+
values: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
35
|
+
}, "strict", import("zod").ZodTypeAny, {
|
|
36
|
+
values?: string[];
|
|
37
|
+
value?: string;
|
|
38
|
+
property?: string;
|
|
39
|
+
operator?: "gte" | "gt" | "lte" | "lt" | "eq" | "ne" | "in" | "is_null";
|
|
40
|
+
}, {
|
|
41
|
+
values?: string[];
|
|
42
|
+
value?: string;
|
|
43
|
+
property?: string;
|
|
44
|
+
operator?: "gte" | "gt" | "lte" | "lt" | "eq" | "ne" | "in" | "is_null";
|
|
45
|
+
}>, "many">>;
|
|
46
|
+
spatial_operator: import("zod").ZodOptional<import("zod").ZodEnum<["bbox", "intersects_point", "dwithin_point", "intersects_feature"]>>;
|
|
47
|
+
bbox_west: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
48
|
+
bbox_south: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
49
|
+
bbox_east: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
50
|
+
bbox_north: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
51
|
+
intersects_lon: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
52
|
+
intersects_lat: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
53
|
+
dwithin_lon: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
54
|
+
dwithin_lat: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
55
|
+
dwithin_distance_m: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
56
|
+
intersects_feature_typename: import("zod").ZodOptional<import("zod").ZodString>;
|
|
57
|
+
intersects_feature_id: import("zod").ZodOptional<import("zod").ZodString>;
|
|
58
|
+
}, "strict", import("zod").ZodTypeAny, {
|
|
45
59
|
typename?: string;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
limit?: number;
|
|
61
|
+
result_type?: "request" | "results" | "hits";
|
|
62
|
+
select?: string[];
|
|
63
|
+
order_by?: {
|
|
64
|
+
property?: string;
|
|
65
|
+
direction?: "asc" | "desc";
|
|
66
|
+
}[];
|
|
67
|
+
where?: {
|
|
68
|
+
values?: string[];
|
|
69
|
+
value?: string;
|
|
70
|
+
property?: string;
|
|
71
|
+
operator?: "gte" | "gt" | "lte" | "lt" | "eq" | "ne" | "in" | "is_null";
|
|
72
|
+
}[];
|
|
73
|
+
spatial_operator?: "bbox" | "intersects_point" | "dwithin_point" | "intersects_feature";
|
|
74
|
+
bbox_west?: number;
|
|
75
|
+
bbox_south?: number;
|
|
76
|
+
bbox_east?: number;
|
|
77
|
+
bbox_north?: number;
|
|
78
|
+
intersects_lon?: number;
|
|
79
|
+
intersects_lat?: number;
|
|
80
|
+
dwithin_lon?: number;
|
|
81
|
+
dwithin_lat?: number;
|
|
82
|
+
dwithin_distance_m?: number;
|
|
83
|
+
intersects_feature_typename?: string;
|
|
84
|
+
intersects_feature_id?: string;
|
|
50
85
|
}, {
|
|
51
|
-
cql_filter?: string;
|
|
52
86
|
typename?: string;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
limit?: number;
|
|
88
|
+
result_type?: "request" | "results" | "hits";
|
|
89
|
+
select?: string[];
|
|
90
|
+
order_by?: {
|
|
91
|
+
property?: string;
|
|
92
|
+
direction?: "asc" | "desc";
|
|
93
|
+
}[];
|
|
94
|
+
where?: {
|
|
95
|
+
values?: string[];
|
|
96
|
+
value?: string;
|
|
97
|
+
property?: string;
|
|
98
|
+
operator?: "gte" | "gt" | "lte" | "lt" | "eq" | "ne" | "in" | "is_null";
|
|
99
|
+
}[];
|
|
100
|
+
spatial_operator?: "bbox" | "intersects_point" | "dwithin_point" | "intersects_feature";
|
|
101
|
+
bbox_west?: number;
|
|
102
|
+
bbox_south?: number;
|
|
103
|
+
bbox_east?: number;
|
|
104
|
+
bbox_north?: number;
|
|
105
|
+
intersects_lon?: number;
|
|
106
|
+
intersects_lat?: number;
|
|
107
|
+
dwithin_lon?: number;
|
|
108
|
+
dwithin_lat?: number;
|
|
109
|
+
dwithin_distance_m?: number;
|
|
110
|
+
intersects_feature_typename?: string;
|
|
111
|
+
intersects_feature_id?: string;
|
|
57
112
|
}>;
|
|
113
|
+
/**
|
|
114
|
+
* Exposes an input schema variant that stays compatible with most MCP integrations.
|
|
115
|
+
*
|
|
116
|
+
* @returns The published input schema exposed through the MCP tool definition.
|
|
117
|
+
*/
|
|
118
|
+
get inputSchema(): {
|
|
119
|
+
type: "object";
|
|
120
|
+
properties?: Record<string, object>;
|
|
121
|
+
required?: string[];
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Formats compact responses (`hits`, `request`) into `structuredContent`.
|
|
125
|
+
* Full result sets are still delegated to the framework default behavior.
|
|
126
|
+
*
|
|
127
|
+
* @param data Raw execution result returned by the tool implementation.
|
|
128
|
+
* @returns An MCP success response, optionally enriched with structured content.
|
|
129
|
+
*/
|
|
58
130
|
protected createSuccessResponse(data: unknown): import("mcp-framework").ToolResponse | {
|
|
59
131
|
content: {
|
|
60
132
|
type: "text";
|
|
@@ -70,10 +142,64 @@ declare class GpfWfsGetFeaturesTool extends MCPTool<GpfWfsGetFeaturesInput> {
|
|
|
70
142
|
text: string;
|
|
71
143
|
}[];
|
|
72
144
|
structuredContent: {
|
|
145
|
+
body?: string;
|
|
146
|
+
result_type?: "request";
|
|
147
|
+
method?: "POST";
|
|
73
148
|
url?: string;
|
|
74
|
-
|
|
149
|
+
query?: Record<string, string>;
|
|
150
|
+
get_url?: string;
|
|
75
151
|
};
|
|
76
152
|
};
|
|
77
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Loads a WFS feature type description from the embedded catalog.
|
|
155
|
+
*
|
|
156
|
+
* @param typename Exact WFS typename to load from the embedded schema store.
|
|
157
|
+
* @returns The matching feature type description.
|
|
158
|
+
*/
|
|
159
|
+
protected getFeatureType(typename: string): Promise<Collection>;
|
|
160
|
+
/**
|
|
161
|
+
* Executes a compiled WFS request as POST and returns the JSON FeatureCollection.
|
|
162
|
+
*
|
|
163
|
+
* @param request Compiled request split into query-string parameters and POST body.
|
|
164
|
+
* @returns The parsed JSON response returned by the WFS endpoint.
|
|
165
|
+
*/
|
|
166
|
+
protected fetchFeatureCollection(request: CompiledRequest): Promise<any>;
|
|
167
|
+
/**
|
|
168
|
+
* Extracts a result count from a WFS response, preferring `numberMatched`.
|
|
169
|
+
* Explicitly rejects responses that do not provide a usable total.
|
|
170
|
+
*
|
|
171
|
+
* @param featureCollection Parsed WFS response object.
|
|
172
|
+
* @returns The total number of matching features.
|
|
173
|
+
*/
|
|
174
|
+
protected getMatchedFeatureCount(featureCollection: Record<string, unknown>): number;
|
|
175
|
+
/**
|
|
176
|
+
* Enriches transformed features with a complete `feature_ref`, reusable
|
|
177
|
+
* in particular by `intersects_feature`.
|
|
178
|
+
*
|
|
179
|
+
* @param featureCollection Raw WFS FeatureCollection response.
|
|
180
|
+
* @param typename Typename of the main queried layer.
|
|
181
|
+
* @returns The transformed FeatureCollection with fully populated feature references.
|
|
182
|
+
*/
|
|
183
|
+
protected attachFeatureRefs(featureCollection: Record<string, unknown>, typename: string): Record<string, unknown>;
|
|
184
|
+
/**
|
|
185
|
+
* Resolves the geometry of a reference feature when `intersects_feature` is used,
|
|
186
|
+
* then converts it to EWKT for CQL compilation.
|
|
187
|
+
*
|
|
188
|
+
* @param input Normalized tool input.
|
|
189
|
+
* @returns The resolved reference geometry, or `undefined` when no reference feature is needed.
|
|
190
|
+
*/
|
|
191
|
+
protected resolveIntersectsFeatureGeometry(input: GpfWfsGetFeaturesInput): Promise<{
|
|
192
|
+
typename: string;
|
|
193
|
+
feature_id: string;
|
|
194
|
+
geometry_ewkt: string;
|
|
195
|
+
}>;
|
|
196
|
+
/**
|
|
197
|
+
* Orchestrates the full tool execution flow:
|
|
198
|
+
* catalog lookup -> compilation -> WFS request -> response post-processing.
|
|
199
|
+
*
|
|
200
|
+
* @param input Normalized tool input.
|
|
201
|
+
* @returns Either a compiled request, a hit count, or a transformed FeatureCollection.
|
|
202
|
+
*/
|
|
203
|
+
execute(input: GpfWfsGetFeaturesInput): Promise<Record<string, unknown>>;
|
|
78
204
|
}
|
|
79
205
|
export default GpfWfsGetFeaturesTool;
|
|
@@ -1,76 +1,40 @@
|
|
|
1
1
|
import { MCPTool } from "mcp-framework";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { fetchJSON } from "../helpers/http.js";
|
|
2
|
+
import { wfsClient } from "../gpf/wfs.js";
|
|
3
|
+
import { fetchJSONPost } from "../helpers/http.js";
|
|
5
4
|
import logger from "../logger.js";
|
|
6
5
|
import { READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS } from "../helpers/toolAnnotations.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const gpfWfsGetFeaturesInputSchema = z.object({
|
|
12
|
-
typename: z
|
|
13
|
-
.string()
|
|
14
|
-
.trim()
|
|
15
|
-
.min(1, "le nom du type ne doit pas être vide")
|
|
16
|
-
.describe("L'identifiant exact du type WFS à interroger (ex : `BDTOPO_V3:batiment`). Ce paramètre détermine la collection interrogée et doit correspondre à un type valide. Utiliser `gpf_wfs_search_types` pour trouver un `typename` pertinent, puis `gpf_wfs_describe_type` pour inspecter ses propriétés avant la requête."),
|
|
17
|
-
property_names: z
|
|
18
|
-
.string()
|
|
19
|
-
.optional()
|
|
20
|
-
.describe("La liste des propriétés à inclure dans chaque objet renvoyé, séparées par des virgules (ex : \"code_insee,nom_officiel,geometrie\"). Ce paramètre limite les champs présents dans la réponse, sans filtrer les objets eux-mêmes. Les noms doivent correspondre exactement aux propriétés du type WFS ; utiliser `gpf_wfs_describe_type` pour les connaître."),
|
|
21
|
-
sort_by: z
|
|
22
|
-
.string()
|
|
23
|
-
.optional()
|
|
24
|
-
.describe("Les propriétés à utiliser pour trier les objets renvoyés, avec la syntaxe `field [A|D]` où `A` signifie tri ascendant et `D` tri descendant. Plusieurs critères peuvent être séparés par des virgules (ex : `nom_officiel A, population D`). Les noms doivent correspondre exactement aux propriétés du type WFS ; utiliser `gpf_wfs_describe_type` pour les connaître."),
|
|
25
|
-
count: z
|
|
26
|
-
.number()
|
|
27
|
-
.int()
|
|
28
|
-
.min(1)
|
|
29
|
-
.max(1000)
|
|
30
|
-
.optional()
|
|
31
|
-
.describe("Le nombre maximum d'objets à retourner dans la réponse (entre 1 et 1000). Ce paramètre limite les résultats renvoyés, sans modifier le nombre total d'objets correspondant à la requête. Il est surtout utile avec `result_type=\"results\"`."),
|
|
32
|
-
cql_filter: z
|
|
33
|
-
.string()
|
|
34
|
-
.optional()
|
|
35
|
-
.describe([
|
|
36
|
-
"Un filtre `cql_filter` GeoServer pour restreindre les objets renvoyés par la requête.",
|
|
37
|
-
"Il faut utiliser les noms exacts des propriétés du type WFS ; utiliser `gpf_wfs_describe_type` pour les connaître.",
|
|
38
|
-
"Attention : en `EPSG:4326`, les coordonnées des géométries doivent être exprimées en `lat lon` (y x), y compris pour les points, lignes et polygones.",
|
|
39
|
-
"Exemples :",
|
|
40
|
-
"- filtre attributaire : `code_insee = '75056'`",
|
|
41
|
-
"- filtre spatial point : `DWITHIN(geom,Point(48.8566 2.3522),100,meters)`",
|
|
42
|
-
"- filtre spatial polygone : `INTERSECTS(geom,POLYGON((48.85 2.34,48.86 2.34,48.86 2.36,48.85 2.36,48.85 2.34)))`",
|
|
43
|
-
].join("\r\n")),
|
|
44
|
-
result_type: z
|
|
45
|
-
.enum(["results", "hits", "url"])
|
|
46
|
-
.optional()
|
|
47
|
-
.describe([
|
|
48
|
-
"Choisit le type de résultat renvoyé par le tool :",
|
|
49
|
-
"- `results` : retourne les objets trouvés sous forme de `FeatureCollection` GeoJSON complète (défaut)",
|
|
50
|
-
"- `hits` : retourne uniquement le nombre total d'objets correspondant à la requête",
|
|
51
|
-
"- `url` : retourne uniquement l'URL WFS construite pour la requête, utile pour inspection, débogage ou réutilisation côté client",
|
|
52
|
-
].join("\r\n"))
|
|
53
|
-
});
|
|
54
|
-
const gpfWfsGetFeaturesHitsOutputSchema = z.object({
|
|
55
|
-
result_type: z.literal("hits").describe("Indique que la réponse contient uniquement un comptage."),
|
|
56
|
-
totalFeatures: z.number().describe("Le nombre total d'objets correspondant à la requête."),
|
|
57
|
-
});
|
|
58
|
-
const gpfWfsGetFeaturesUrlOutputSchema = z.object({
|
|
59
|
-
result_type: z.literal("url").describe("Indique que la réponse contient uniquement l'URL de la requête."),
|
|
60
|
-
url: z.string().describe("L'URL WFS générée pour la requête."),
|
|
61
|
-
});
|
|
6
|
+
import { compileQueryParts, geometryToEwkt, getGeometryProperty, getSpatialFilter } from "../helpers/wfs_internal/compile.js";
|
|
7
|
+
import { buildMainRequest, buildReferenceGeometryRequest } from "../helpers/wfs_internal/request.js";
|
|
8
|
+
import { transformFeatureCollectionResponse } from "../helpers/wfs_internal/response.js";
|
|
9
|
+
import { gpfWfsGetFeaturesHitsOutputSchema, gpfWfsGetFeaturesInputSchema, gpfWfsGetFeaturesPublishedInputSchema, gpfWfsGetFeaturesRequestOutputSchema, } from "../helpers/wfs_internal/schema.js";
|
|
62
10
|
class GpfWfsGetFeaturesTool extends MCPTool {
|
|
63
11
|
name = "gpf_wfs_get_features";
|
|
64
12
|
title = "Lecture d’objets WFS";
|
|
65
13
|
annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS;
|
|
66
14
|
description = [
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
|
|
15
|
+
"Interroge un type WFS et renvoie des résultats structurés sans demander au modèle d'écrire du CQL ou du WFS.",
|
|
16
|
+
"Utiliser `select` pour choisir les propriétés, `where` pour filtrer, `order_by` pour trier et `spatial_operator` avec ses paramètres dédiés pour le spatial. Avec `result_type=\"request\"`, la géométrie est automatiquement ajoutée aux propriétés sélectionnées pour garantir une requête cartographiable.",
|
|
17
|
+
"Exemple attributaire : `where=[{ property: \"code_insee\", operator: \"eq\", value: \"75056\" }]`.",
|
|
18
|
+
"Exemple bbox : `spatial_operator=\"bbox\"` avec `bbox_west`, `bbox_south`, `bbox_east`, `bbox_north` en `lon/lat`.",
|
|
19
|
+
"Exemple distance : `spatial_operator=\"dwithin_point\"` avec `dwithin_lon`, `dwithin_lat`, `dwithin_distance_m`.",
|
|
20
|
+
"Exemple réutilisation : `spatial_operator=\"intersects_feature\"` avec `intersects_feature_typename` et `intersects_feature_id` issus d'une `feature_ref`.",
|
|
21
|
+
].join("\n");
|
|
73
22
|
schema = gpfWfsGetFeaturesInputSchema;
|
|
23
|
+
/**
|
|
24
|
+
* Exposes an input schema variant that stays compatible with most MCP integrations.
|
|
25
|
+
*
|
|
26
|
+
* @returns The published input schema exposed through the MCP tool definition.
|
|
27
|
+
*/
|
|
28
|
+
get inputSchema() {
|
|
29
|
+
return gpfWfsGetFeaturesPublishedInputSchema;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Formats compact responses (`hits`, `request`) into `structuredContent`.
|
|
33
|
+
* Full result sets are still delegated to the framework default behavior.
|
|
34
|
+
*
|
|
35
|
+
* @param data Raw execution result returned by the tool implementation.
|
|
36
|
+
* @returns An MCP success response, optionally enriched with structured content.
|
|
37
|
+
*/
|
|
74
38
|
createSuccessResponse(data) {
|
|
75
39
|
if (typeof data === "object" &&
|
|
76
40
|
data !== null &&
|
|
@@ -79,80 +43,163 @@ class GpfWfsGetFeaturesTool extends MCPTool {
|
|
|
79
43
|
"totalFeatures" in data &&
|
|
80
44
|
typeof data.totalFeatures === "number") {
|
|
81
45
|
return {
|
|
82
|
-
content: [
|
|
83
|
-
{
|
|
84
|
-
type: "text",
|
|
85
|
-
text: JSON.stringify(data.totalFeatures),
|
|
86
|
-
},
|
|
87
|
-
],
|
|
46
|
+
content: [{ type: "text", text: JSON.stringify(data.totalFeatures) }],
|
|
88
47
|
structuredContent: gpfWfsGetFeaturesHitsOutputSchema.parse(data),
|
|
89
48
|
};
|
|
90
49
|
}
|
|
91
50
|
if (typeof data === "object" &&
|
|
92
51
|
data !== null &&
|
|
93
52
|
"result_type" in data &&
|
|
94
|
-
data.result_type === "
|
|
95
|
-
"url" in data &&
|
|
96
|
-
typeof data.url === "string") {
|
|
53
|
+
data.result_type === "request") {
|
|
97
54
|
return {
|
|
98
|
-
content: [
|
|
99
|
-
|
|
100
|
-
type: "text",
|
|
101
|
-
text: data.url,
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
structuredContent: gpfWfsGetFeaturesUrlOutputSchema.parse(data),
|
|
55
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
56
|
+
structuredContent: gpfWfsGetFeaturesRequestOutputSchema.parse(data),
|
|
105
57
|
};
|
|
106
58
|
}
|
|
107
59
|
return super.createSuccessResponse(data);
|
|
108
60
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Loads a WFS feature type description from the embedded catalog.
|
|
63
|
+
*
|
|
64
|
+
* @param typename Exact WFS typename to load from the embedded schema store.
|
|
65
|
+
* @returns The matching feature type description.
|
|
66
|
+
*/
|
|
67
|
+
async getFeatureType(typename) {
|
|
68
|
+
return wfsClient.getFeatureType(typename);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Executes a compiled WFS request as POST and returns the JSON FeatureCollection.
|
|
72
|
+
*
|
|
73
|
+
* @param request Compiled request split into query-string parameters and POST body.
|
|
74
|
+
* @returns The parsed JSON response returned by the WFS endpoint.
|
|
75
|
+
*/
|
|
76
|
+
async fetchFeatureCollection(request) {
|
|
77
|
+
const url = `${request.url}?${new URLSearchParams(request.query).toString()}`;
|
|
78
|
+
return fetchJSONPost(url, request.body, {
|
|
79
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
80
|
+
"Accept": "application/json",
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extracts a result count from a WFS response, preferring `numberMatched`.
|
|
85
|
+
* Explicitly rejects responses that do not provide a usable total.
|
|
86
|
+
*
|
|
87
|
+
* @param featureCollection Parsed WFS response object.
|
|
88
|
+
* @returns The total number of matching features.
|
|
89
|
+
*/
|
|
90
|
+
getMatchedFeatureCount(featureCollection) {
|
|
91
|
+
if (typeof featureCollection.numberMatched === "number") {
|
|
92
|
+
return featureCollection.numberMatched;
|
|
119
93
|
}
|
|
120
|
-
if (
|
|
121
|
-
|
|
94
|
+
if (featureCollection.numberMatched === "unknown") {
|
|
95
|
+
throw new Error("Le service WFS a renvoyé un comptage indéterminé (numberMatched=\"unknown\").");
|
|
122
96
|
}
|
|
123
|
-
if (
|
|
124
|
-
|
|
97
|
+
if (typeof featureCollection.totalFeatures === "number") {
|
|
98
|
+
return featureCollection.totalFeatures;
|
|
99
|
+
}
|
|
100
|
+
throw new Error("Le service WFS n'a pas retourné de comptage exploitable");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Enriches transformed features with a complete `feature_ref`, reusable
|
|
104
|
+
* in particular by `intersects_feature`.
|
|
105
|
+
*
|
|
106
|
+
* @param featureCollection Raw WFS FeatureCollection response.
|
|
107
|
+
* @param typename Typename of the main queried layer.
|
|
108
|
+
* @returns The transformed FeatureCollection with fully populated feature references.
|
|
109
|
+
*/
|
|
110
|
+
attachFeatureRefs(featureCollection, typename) {
|
|
111
|
+
const transformed = transformFeatureCollectionResponse(featureCollection);
|
|
112
|
+
if (!Array.isArray(transformed.features)) {
|
|
113
|
+
return transformed;
|
|
114
|
+
}
|
|
115
|
+
transformed.features = transformed.features.map((feature) => {
|
|
116
|
+
if (typeof feature !== "object" || feature === null || !("feature_ref" in feature)) {
|
|
117
|
+
return feature;
|
|
118
|
+
}
|
|
119
|
+
const featureRef = feature.feature_ref;
|
|
120
|
+
if (typeof featureRef !== "object" || featureRef === null) {
|
|
121
|
+
return feature;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
...feature,
|
|
125
|
+
feature_ref: {
|
|
126
|
+
...featureRef,
|
|
127
|
+
typename,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
return transformed;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Resolves the geometry of a reference feature when `intersects_feature` is used,
|
|
135
|
+
* then converts it to EWKT for CQL compilation.
|
|
136
|
+
*
|
|
137
|
+
* @param input Normalized tool input.
|
|
138
|
+
* @returns The resolved reference geometry, or `undefined` when no reference feature is needed.
|
|
139
|
+
*/
|
|
140
|
+
async resolveIntersectsFeatureGeometry(input) {
|
|
141
|
+
const spatialFilter = getSpatialFilter(input);
|
|
142
|
+
if (!spatialFilter || spatialFilter.operator !== "intersects_feature") {
|
|
143
|
+
return undefined;
|
|
125
144
|
}
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
const referenceFeatureType = await this.getFeatureType(spatialFilter.typename);
|
|
146
|
+
const referenceGeometryProperty = getGeometryProperty(referenceFeatureType);
|
|
147
|
+
const request = buildReferenceGeometryRequest(spatialFilter.typename, spatialFilter.feature_id, referenceGeometryProperty.name);
|
|
148
|
+
const featureCollection = await this.fetchFeatureCollection(request);
|
|
149
|
+
const referenceFeature = Array.isArray(featureCollection?.features) ? featureCollection.features[0] : undefined;
|
|
150
|
+
if (!referenceFeature) {
|
|
151
|
+
throw new Error(`Le feature de référence '${spatialFilter.feature_id}' est introuvable dans '${spatialFilter.typename}'.`);
|
|
128
152
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// On est obligé de faire une requete getfeature pour récupérer le totalFeatures...
|
|
132
|
-
if (input.result_type === 'hits') {
|
|
133
|
-
params.count = "1";
|
|
134
|
-
// On n'a pas besoin des propriétés détaillées pour un comptage
|
|
135
|
-
delete params.propertyName;
|
|
153
|
+
if (!referenceFeature?.geometry) {
|
|
154
|
+
throw new Error(`Le feature de référence '${spatialFilter.feature_id}' n'a pas de géométrie exploitable.`);
|
|
136
155
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
156
|
+
return {
|
|
157
|
+
typename: spatialFilter.typename,
|
|
158
|
+
feature_id: spatialFilter.feature_id,
|
|
159
|
+
geometry_ewkt: geometryToEwkt(referenceFeature.geometry),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Orchestrates the full tool execution flow:
|
|
164
|
+
* catalog lookup -> compilation -> WFS request -> response post-processing.
|
|
165
|
+
*
|
|
166
|
+
* @param input Normalized tool input.
|
|
167
|
+
* @returns Either a compiled request, a hit count, or a transformed FeatureCollection.
|
|
168
|
+
*/
|
|
169
|
+
async execute(input) {
|
|
170
|
+
const featureType = await this.getFeatureType(input.typename);
|
|
171
|
+
const resolvedGeometryRef = await this.resolveIntersectsFeatureGeometry(input);
|
|
172
|
+
const compiled = compileQueryParts(input, featureType, resolvedGeometryRef);
|
|
173
|
+
const request = buildMainRequest(input, compiled);
|
|
174
|
+
if (input.result_type === "request") {
|
|
140
175
|
return {
|
|
141
|
-
result_type: "
|
|
142
|
-
|
|
176
|
+
result_type: "request",
|
|
177
|
+
method: request.method,
|
|
178
|
+
url: request.url,
|
|
179
|
+
query: request.query,
|
|
180
|
+
body: request.body,
|
|
181
|
+
get_url: request.get_url ?? null,
|
|
143
182
|
};
|
|
144
183
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
184
|
+
let featureCollection;
|
|
185
|
+
try {
|
|
186
|
+
logger.info(`[gpf_wfs_get_features] POST ${request.url}?${new URLSearchParams(request.query).toString()}`);
|
|
187
|
+
featureCollection = await this.fetchFeatureCollection(request);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
191
|
+
if (message.includes(`Illegal property name: ${compiled.geometryProperty.name}`)) {
|
|
192
|
+
throw new Error(`Le champ géométrique '${compiled.geometryProperty.name}' issu du catalogue embarqué est rejeté par le WFS live pour '${input.typename}'. Le catalogue embarqué est probablement désynchronisé. Détail : ${message}`);
|
|
149
193
|
}
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
if (input.result_type === "hits") {
|
|
150
197
|
return {
|
|
151
198
|
result_type: "hits",
|
|
152
|
-
totalFeatures: featureCollection
|
|
199
|
+
totalFeatures: this.getMatchedFeatureCount(featureCollection),
|
|
153
200
|
};
|
|
154
201
|
}
|
|
155
|
-
return featureCollection;
|
|
202
|
+
return this.attachFeatureRefs(featureCollection, input.typename);
|
|
156
203
|
}
|
|
157
204
|
}
|
|
158
205
|
export default GpfWfsGetFeaturesTool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GpfWfsGetFeaturesTool.js","sourceRoot":"","sources":["../../src/tools/GpfWfsGetFeaturesTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"GpfWfsGetFeaturesTool.js","sourceRoot":"","sources":["../../src/tools/GpfWfsGetFeaturesTool.ts"],"names":[],"mappings":"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"}
|