@centia-io/mcp-server 1.0.17 → 1.0.19
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/AGENTS.md +2 -0
- package/README.md +2 -0
- package/centia-api.json +721 -0
- package/package.json +2 -2
- package/skills/README.md +2 -0
- package/skills/centia-auth-model/SKILL.md +20 -0
- package/skills/centia-delivery/SKILL.md +6 -1
- package/skills/centia-ogc/SKILL.md +70 -0
- package/skills/centia-ows/SKILL.md +54 -0
- package/skills/centia-runtime-sdk/SKILL.md +1 -1
package/AGENTS.md
CHANGED
|
@@ -61,6 +61,8 @@ Use the minimal matching skill(s) below for each task:
|
|
|
61
61
|
- Feature CRUD as GeoJSON by primary key: `skills/centia-feature/SKILL.md`
|
|
62
62
|
- Realtime WebSocket events: `skills/centia-realtime/SKILL.md`
|
|
63
63
|
- File import workflow: `skills/centia-file-import/SKILL.md`
|
|
64
|
+
- OGC web services (WMS/WFS/WFS-T/MapCache): `skills/centia-ows/SKILL.md`
|
|
65
|
+
- OGC API Features and Maps (GeoJSON items, map images): `skills/centia-ogc/SKILL.md`
|
|
64
66
|
- OpenAPI and docs fallback: `skills/centia-openapi-docs/SKILL.md`
|
|
65
67
|
- Delivery checklist and MCP reference: `skills/centia-delivery/SKILL.md`
|
|
66
68
|
- Danish address/matrikel search component: `skills/danish-search/SKILL.md`
|
package/README.md
CHANGED
|
@@ -135,6 +135,8 @@ To ensure your AI agent follows these rules while developing your application:
|
|
|
135
135
|
- `centia-auth-model` — Auth across browser, backend, CLI, and provisioning
|
|
136
136
|
- `centia-types-formats` — PostgreSQL types, casts, type hints, output formats
|
|
137
137
|
- `centia-file-import` — File import workflow
|
|
138
|
+
- `centia-ows` — WMS/WFS/WFS-T and MapCache proxy endpoints and their access model
|
|
139
|
+
- `centia-ogc` — OGC API Features and Maps (GeoJSON items, map images)
|
|
138
140
|
- `centia-openapi-docs` — OpenAPI and docs fallback policy
|
|
139
141
|
- `centia-delivery` — Delivery checklist and quality gate
|
|
140
142
|
|
package/centia-api.json
CHANGED
|
@@ -3013,6 +3013,611 @@
|
|
|
3013
3013
|
}
|
|
3014
3014
|
}
|
|
3015
3015
|
},
|
|
3016
|
+
"/api/v4/oauth/guest": {
|
|
3017
|
+
"post": {
|
|
3018
|
+
"tags": [
|
|
3019
|
+
"OAuth"
|
|
3020
|
+
],
|
|
3021
|
+
"description": "Get a guest token.",
|
|
3022
|
+
"operationId": "postGuest",
|
|
3023
|
+
"requestBody": {
|
|
3024
|
+
"description": "Get a guest token. A default user must be present.",
|
|
3025
|
+
"required": true,
|
|
3026
|
+
"content": {
|
|
3027
|
+
"application/json": {
|
|
3028
|
+
"schema": {
|
|
3029
|
+
"$ref": "#/components/schemas/OAuthGuest"
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
},
|
|
3034
|
+
"responses": {
|
|
3035
|
+
"201": {
|
|
3036
|
+
"description": "Created",
|
|
3037
|
+
"content": {
|
|
3038
|
+
"application/json": {
|
|
3039
|
+
"schema": {
|
|
3040
|
+
"$ref": "#/components/schemas/OAuthResponse"
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
},
|
|
3045
|
+
"400": {
|
|
3046
|
+
"description": "Bad request"
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
},
|
|
3051
|
+
"/api/v4/ogc/database/{database}": {
|
|
3052
|
+
"get": {
|
|
3053
|
+
"tags": [
|
|
3054
|
+
"Ogc"
|
|
3055
|
+
],
|
|
3056
|
+
"description": "OGC API landing page for a database (Bearer, HTTP Basic or anonymous).",
|
|
3057
|
+
"operationId": "getOgcLandingPage",
|
|
3058
|
+
"parameters": [
|
|
3059
|
+
{
|
|
3060
|
+
"name": "database",
|
|
3061
|
+
"in": "path",
|
|
3062
|
+
"description": "Database name",
|
|
3063
|
+
"required": true,
|
|
3064
|
+
"schema": {
|
|
3065
|
+
"type": "string"
|
|
3066
|
+
},
|
|
3067
|
+
"example": "my_database"
|
|
3068
|
+
}
|
|
3069
|
+
],
|
|
3070
|
+
"responses": {
|
|
3071
|
+
"200": {
|
|
3072
|
+
"description": "Landing page with links to conformance, collections and the OpenAPI document"
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
},
|
|
3077
|
+
"/api/v4/ogc/database/{database}/conformance": {
|
|
3078
|
+
"get": {
|
|
3079
|
+
"tags": [
|
|
3080
|
+
"Ogc"
|
|
3081
|
+
],
|
|
3082
|
+
"description": "Conformance classes implemented by this OGC API.",
|
|
3083
|
+
"operationId": "getOgcConformance",
|
|
3084
|
+
"parameters": [
|
|
3085
|
+
{
|
|
3086
|
+
"name": "database",
|
|
3087
|
+
"in": "path",
|
|
3088
|
+
"description": "Database name",
|
|
3089
|
+
"required": true,
|
|
3090
|
+
"schema": {
|
|
3091
|
+
"type": "string"
|
|
3092
|
+
},
|
|
3093
|
+
"example": "my_database"
|
|
3094
|
+
}
|
|
3095
|
+
],
|
|
3096
|
+
"responses": {
|
|
3097
|
+
"200": {
|
|
3098
|
+
"description": "conformsTo list"
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
},
|
|
3103
|
+
"/api/v4/ogc/database/{database}/collections": {
|
|
3104
|
+
"get": {
|
|
3105
|
+
"tags": [
|
|
3106
|
+
"Ogc"
|
|
3107
|
+
],
|
|
3108
|
+
"description": "OWS-enabled layers as OGC API collections (id = schema.table), filtered by the caller's access.",
|
|
3109
|
+
"operationId": "getOgcCollections",
|
|
3110
|
+
"parameters": [
|
|
3111
|
+
{
|
|
3112
|
+
"name": "database",
|
|
3113
|
+
"in": "path",
|
|
3114
|
+
"description": "Database name",
|
|
3115
|
+
"required": true,
|
|
3116
|
+
"schema": {
|
|
3117
|
+
"type": "string"
|
|
3118
|
+
},
|
|
3119
|
+
"example": "my_database"
|
|
3120
|
+
},
|
|
3121
|
+
{
|
|
3122
|
+
"name": "limit",
|
|
3123
|
+
"in": "query",
|
|
3124
|
+
"description": "Page size (default 100, max 1000)",
|
|
3125
|
+
"required": false,
|
|
3126
|
+
"schema": {
|
|
3127
|
+
"type": "integer",
|
|
3128
|
+
"default": 100
|
|
3129
|
+
}
|
|
3130
|
+
},
|
|
3131
|
+
{
|
|
3132
|
+
"name": "offset",
|
|
3133
|
+
"in": "query",
|
|
3134
|
+
"description": "Collections to skip",
|
|
3135
|
+
"required": false,
|
|
3136
|
+
"schema": {
|
|
3137
|
+
"type": "integer",
|
|
3138
|
+
"default": 0
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
],
|
|
3142
|
+
"responses": {
|
|
3143
|
+
"200": {
|
|
3144
|
+
"description": "Collections with extent, crs list and links"
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
},
|
|
3149
|
+
"/api/v4/ogc/database/{database}/collections/{collectionId}": {
|
|
3150
|
+
"get": {
|
|
3151
|
+
"tags": [
|
|
3152
|
+
"Ogc"
|
|
3153
|
+
],
|
|
3154
|
+
"description": "One collection.",
|
|
3155
|
+
"operationId": "getOgcCollection",
|
|
3156
|
+
"parameters": [
|
|
3157
|
+
{
|
|
3158
|
+
"name": "database",
|
|
3159
|
+
"in": "path",
|
|
3160
|
+
"description": "Database name",
|
|
3161
|
+
"required": true,
|
|
3162
|
+
"schema": {
|
|
3163
|
+
"type": "string"
|
|
3164
|
+
},
|
|
3165
|
+
"example": "my_database"
|
|
3166
|
+
},
|
|
3167
|
+
{
|
|
3168
|
+
"name": "collectionId",
|
|
3169
|
+
"in": "path",
|
|
3170
|
+
"description": "Collection id (schema.table)",
|
|
3171
|
+
"required": true,
|
|
3172
|
+
"schema": {
|
|
3173
|
+
"type": "string"
|
|
3174
|
+
},
|
|
3175
|
+
"example": "my_schema.my_table"
|
|
3176
|
+
}
|
|
3177
|
+
],
|
|
3178
|
+
"responses": {
|
|
3179
|
+
"200": {
|
|
3180
|
+
"description": "Collection"
|
|
3181
|
+
},
|
|
3182
|
+
"401": {
|
|
3183
|
+
"description": "Read/write collection: credentials required"
|
|
3184
|
+
},
|
|
3185
|
+
"403": {
|
|
3186
|
+
"description": "Read/write collection: insufficient privileges"
|
|
3187
|
+
},
|
|
3188
|
+
"404": {
|
|
3189
|
+
"description": "Unknown collection"
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
},
|
|
3194
|
+
"/api/v4/ogc/database/{database}/map": {
|
|
3195
|
+
"get": {
|
|
3196
|
+
"tags": [
|
|
3197
|
+
"Ogc"
|
|
3198
|
+
],
|
|
3199
|
+
"description": "Map of several collections (same schema) rendered through the WMS backend with geofence rules and versioning applied.",
|
|
3200
|
+
"operationId": "getOgcDatasetMap",
|
|
3201
|
+
"parameters": [
|
|
3202
|
+
{
|
|
3203
|
+
"name": "database",
|
|
3204
|
+
"in": "path",
|
|
3205
|
+
"description": "Database name",
|
|
3206
|
+
"required": true,
|
|
3207
|
+
"schema": {
|
|
3208
|
+
"type": "string"
|
|
3209
|
+
},
|
|
3210
|
+
"example": "my_database"
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
"name": "collections",
|
|
3214
|
+
"in": "query",
|
|
3215
|
+
"description": "Comma-separated collection ids",
|
|
3216
|
+
"required": true,
|
|
3217
|
+
"schema": {
|
|
3218
|
+
"type": "string"
|
|
3219
|
+
},
|
|
3220
|
+
"example": "my_schema.roads,my_schema.buildings"
|
|
3221
|
+
},
|
|
3222
|
+
{
|
|
3223
|
+
"name": "bbox",
|
|
3224
|
+
"in": "query",
|
|
3225
|
+
"description": "minx,miny,maxx,maxy in bbox-crs",
|
|
3226
|
+
"required": false,
|
|
3227
|
+
"schema": {
|
|
3228
|
+
"type": "string"
|
|
3229
|
+
}
|
|
3230
|
+
},
|
|
3231
|
+
{
|
|
3232
|
+
"name": "bbox-crs",
|
|
3233
|
+
"in": "query",
|
|
3234
|
+
"description": "CRS URI of bbox (default CRS84)",
|
|
3235
|
+
"required": false,
|
|
3236
|
+
"schema": {
|
|
3237
|
+
"type": "string"
|
|
3238
|
+
}
|
|
3239
|
+
},
|
|
3240
|
+
{
|
|
3241
|
+
"name": "crs",
|
|
3242
|
+
"in": "query",
|
|
3243
|
+
"description": "Output CRS URI (default CRS84)",
|
|
3244
|
+
"required": false,
|
|
3245
|
+
"schema": {
|
|
3246
|
+
"type": "string"
|
|
3247
|
+
}
|
|
3248
|
+
},
|
|
3249
|
+
{
|
|
3250
|
+
"name": "width",
|
|
3251
|
+
"in": "query",
|
|
3252
|
+
"description": "Image width in pixels (max 16384)",
|
|
3253
|
+
"required": false,
|
|
3254
|
+
"schema": {
|
|
3255
|
+
"type": "integer"
|
|
3256
|
+
}
|
|
3257
|
+
},
|
|
3258
|
+
{
|
|
3259
|
+
"name": "height",
|
|
3260
|
+
"in": "query",
|
|
3261
|
+
"description": "Image height in pixels (max 16384)",
|
|
3262
|
+
"required": false,
|
|
3263
|
+
"schema": {
|
|
3264
|
+
"type": "integer"
|
|
3265
|
+
}
|
|
3266
|
+
},
|
|
3267
|
+
{
|
|
3268
|
+
"name": "f",
|
|
3269
|
+
"in": "query",
|
|
3270
|
+
"description": "png (default) or jpeg",
|
|
3271
|
+
"required": false,
|
|
3272
|
+
"schema": {
|
|
3273
|
+
"type": "string",
|
|
3274
|
+
"enum": [
|
|
3275
|
+
"png",
|
|
3276
|
+
"jpeg"
|
|
3277
|
+
]
|
|
3278
|
+
}
|
|
3279
|
+
},
|
|
3280
|
+
{
|
|
3281
|
+
"name": "transparent",
|
|
3282
|
+
"in": "query",
|
|
3283
|
+
"description": "Transparent background (default true for png)",
|
|
3284
|
+
"required": false,
|
|
3285
|
+
"schema": {
|
|
3286
|
+
"type": "boolean"
|
|
3287
|
+
}
|
|
3288
|
+
},
|
|
3289
|
+
{
|
|
3290
|
+
"name": "bgcolor",
|
|
3291
|
+
"in": "query",
|
|
3292
|
+
"description": "Background colour 0xRRGGBB",
|
|
3293
|
+
"required": false,
|
|
3294
|
+
"schema": {
|
|
3295
|
+
"type": "string"
|
|
3296
|
+
}
|
|
3297
|
+
},
|
|
3298
|
+
{
|
|
3299
|
+
"name": "datetime",
|
|
3300
|
+
"in": "query",
|
|
3301
|
+
"description": "ISO 8601 instant: render the version valid at that time (versioned layers)",
|
|
3302
|
+
"required": false,
|
|
3303
|
+
"schema": {
|
|
3304
|
+
"type": "string"
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
],
|
|
3308
|
+
"responses": {
|
|
3309
|
+
"200": {
|
|
3310
|
+
"description": "image/png or image/jpeg"
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
},
|
|
3315
|
+
"/api/v4/ogc/database/{database}/collections/{collectionId}/items": {
|
|
3316
|
+
"get": {
|
|
3317
|
+
"tags": [
|
|
3318
|
+
"Ogc"
|
|
3319
|
+
],
|
|
3320
|
+
"description": "Features of a collection as a GeoJSON FeatureCollection. Geofence rules, versioning and workflow are applied by the WFS engine.",
|
|
3321
|
+
"operationId": "getOgcItems",
|
|
3322
|
+
"parameters": [
|
|
3323
|
+
{
|
|
3324
|
+
"name": "database",
|
|
3325
|
+
"in": "path",
|
|
3326
|
+
"description": "Database name",
|
|
3327
|
+
"required": true,
|
|
3328
|
+
"schema": {
|
|
3329
|
+
"type": "string"
|
|
3330
|
+
},
|
|
3331
|
+
"example": "my_database"
|
|
3332
|
+
},
|
|
3333
|
+
{
|
|
3334
|
+
"name": "collectionId",
|
|
3335
|
+
"in": "path",
|
|
3336
|
+
"description": "Collection id (schema.table)",
|
|
3337
|
+
"required": true,
|
|
3338
|
+
"schema": {
|
|
3339
|
+
"type": "string"
|
|
3340
|
+
},
|
|
3341
|
+
"example": "my_schema.my_table"
|
|
3342
|
+
},
|
|
3343
|
+
{
|
|
3344
|
+
"name": "limit",
|
|
3345
|
+
"in": "query",
|
|
3346
|
+
"description": "Page size (default 10, max 10000)",
|
|
3347
|
+
"required": false,
|
|
3348
|
+
"schema": {
|
|
3349
|
+
"type": "integer",
|
|
3350
|
+
"default": 10
|
|
3351
|
+
}
|
|
3352
|
+
},
|
|
3353
|
+
{
|
|
3354
|
+
"name": "offset",
|
|
3355
|
+
"in": "query",
|
|
3356
|
+
"description": "Features to skip",
|
|
3357
|
+
"required": false,
|
|
3358
|
+
"schema": {
|
|
3359
|
+
"type": "integer",
|
|
3360
|
+
"default": 0
|
|
3361
|
+
}
|
|
3362
|
+
},
|
|
3363
|
+
{
|
|
3364
|
+
"name": "bbox",
|
|
3365
|
+
"in": "query",
|
|
3366
|
+
"description": "minx,miny,maxx,maxy in bbox-crs",
|
|
3367
|
+
"required": false,
|
|
3368
|
+
"schema": {
|
|
3369
|
+
"type": "string"
|
|
3370
|
+
},
|
|
3371
|
+
"example": "9,55,10,56"
|
|
3372
|
+
},
|
|
3373
|
+
{
|
|
3374
|
+
"name": "bbox-crs",
|
|
3375
|
+
"in": "query",
|
|
3376
|
+
"description": "CRS URI of bbox (default CRS84)",
|
|
3377
|
+
"required": false,
|
|
3378
|
+
"schema": {
|
|
3379
|
+
"type": "string"
|
|
3380
|
+
}
|
|
3381
|
+
},
|
|
3382
|
+
{
|
|
3383
|
+
"name": "crs",
|
|
3384
|
+
"in": "query",
|
|
3385
|
+
"description": "Output CRS URI from the collection crs list (default CRS84)",
|
|
3386
|
+
"required": false,
|
|
3387
|
+
"schema": {
|
|
3388
|
+
"type": "string"
|
|
3389
|
+
}
|
|
3390
|
+
},
|
|
3391
|
+
{
|
|
3392
|
+
"name": "datetime",
|
|
3393
|
+
"in": "query",
|
|
3394
|
+
"description": "ISO 8601 instant: the version valid at that time (versioned layers only)",
|
|
3395
|
+
"required": false,
|
|
3396
|
+
"schema": {
|
|
3397
|
+
"type": "string"
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
],
|
|
3401
|
+
"responses": {
|
|
3402
|
+
"200": {
|
|
3403
|
+
"description": "GeoJSON FeatureCollection (application/geo+json) with Content-Crs header"
|
|
3404
|
+
},
|
|
3405
|
+
"400": {
|
|
3406
|
+
"description": "Invalid or unknown parameter"
|
|
3407
|
+
},
|
|
3408
|
+
"401": {
|
|
3409
|
+
"description": "Credentials required for a Read/write layer"
|
|
3410
|
+
},
|
|
3411
|
+
"403": {
|
|
3412
|
+
"description": "Insufficient privileges, or denied by a geofence rule"
|
|
3413
|
+
},
|
|
3414
|
+
"404": {
|
|
3415
|
+
"description": "Unknown collection"
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
},
|
|
3420
|
+
"/api/v4/ogc/database/{database}/collections/{collectionId}/items/{featureId}": {
|
|
3421
|
+
"get": {
|
|
3422
|
+
"tags": [
|
|
3423
|
+
"Ogc"
|
|
3424
|
+
],
|
|
3425
|
+
"description": "One feature by primary key as a GeoJSON Feature.",
|
|
3426
|
+
"operationId": "getOgcItem",
|
|
3427
|
+
"parameters": [
|
|
3428
|
+
{
|
|
3429
|
+
"name": "database",
|
|
3430
|
+
"in": "path",
|
|
3431
|
+
"description": "Database name",
|
|
3432
|
+
"required": true,
|
|
3433
|
+
"schema": {
|
|
3434
|
+
"type": "string"
|
|
3435
|
+
},
|
|
3436
|
+
"example": "my_database"
|
|
3437
|
+
},
|
|
3438
|
+
{
|
|
3439
|
+
"name": "collectionId",
|
|
3440
|
+
"in": "path",
|
|
3441
|
+
"description": "Collection id (schema.table)",
|
|
3442
|
+
"required": true,
|
|
3443
|
+
"schema": {
|
|
3444
|
+
"type": "string"
|
|
3445
|
+
},
|
|
3446
|
+
"example": "my_schema.my_table"
|
|
3447
|
+
},
|
|
3448
|
+
{
|
|
3449
|
+
"name": "featureId",
|
|
3450
|
+
"in": "path",
|
|
3451
|
+
"description": "Primary key value",
|
|
3452
|
+
"required": true,
|
|
3453
|
+
"schema": {
|
|
3454
|
+
"type": "string"
|
|
3455
|
+
},
|
|
3456
|
+
"example": "1"
|
|
3457
|
+
},
|
|
3458
|
+
{
|
|
3459
|
+
"name": "crs",
|
|
3460
|
+
"in": "query",
|
|
3461
|
+
"description": "Output CRS URI (default CRS84)",
|
|
3462
|
+
"required": false,
|
|
3463
|
+
"schema": {
|
|
3464
|
+
"type": "string"
|
|
3465
|
+
}
|
|
3466
|
+
},
|
|
3467
|
+
{
|
|
3468
|
+
"name": "datetime",
|
|
3469
|
+
"in": "query",
|
|
3470
|
+
"description": "ISO 8601 instant (versioned layers only)",
|
|
3471
|
+
"required": false,
|
|
3472
|
+
"schema": {
|
|
3473
|
+
"type": "string"
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
],
|
|
3477
|
+
"responses": {
|
|
3478
|
+
"200": {
|
|
3479
|
+
"description": "GeoJSON Feature (application/geo+json)"
|
|
3480
|
+
},
|
|
3481
|
+
"404": {
|
|
3482
|
+
"description": "Unknown feature or collection"
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
},
|
|
3487
|
+
"/api/v4/ogc/database/{database}/collections/{collectionId}/map": {
|
|
3488
|
+
"get": {
|
|
3489
|
+
"tags": [
|
|
3490
|
+
"Ogc"
|
|
3491
|
+
],
|
|
3492
|
+
"description": "Map of one collection rendered by the WMS backend; geofence rules and versioning (datetime) are applied.",
|
|
3493
|
+
"operationId": "getOgcCollectionMap",
|
|
3494
|
+
"parameters": [
|
|
3495
|
+
{
|
|
3496
|
+
"name": "database",
|
|
3497
|
+
"in": "path",
|
|
3498
|
+
"description": "Database name",
|
|
3499
|
+
"required": true,
|
|
3500
|
+
"schema": {
|
|
3501
|
+
"type": "string"
|
|
3502
|
+
},
|
|
3503
|
+
"example": "my_database"
|
|
3504
|
+
},
|
|
3505
|
+
{
|
|
3506
|
+
"name": "collectionId",
|
|
3507
|
+
"in": "path",
|
|
3508
|
+
"description": "Collection id (schema.table)",
|
|
3509
|
+
"required": true,
|
|
3510
|
+
"schema": {
|
|
3511
|
+
"type": "string"
|
|
3512
|
+
},
|
|
3513
|
+
"example": "my_schema.my_table"
|
|
3514
|
+
},
|
|
3515
|
+
{
|
|
3516
|
+
"name": "bbox",
|
|
3517
|
+
"in": "query",
|
|
3518
|
+
"description": "minx,miny,maxx,maxy in bbox-crs (default: the collection extent)",
|
|
3519
|
+
"required": false,
|
|
3520
|
+
"schema": {
|
|
3521
|
+
"type": "string"
|
|
3522
|
+
},
|
|
3523
|
+
"example": "9,55,10,56"
|
|
3524
|
+
},
|
|
3525
|
+
{
|
|
3526
|
+
"name": "bbox-crs",
|
|
3527
|
+
"in": "query",
|
|
3528
|
+
"description": "CRS URI of bbox (default CRS84)",
|
|
3529
|
+
"required": false,
|
|
3530
|
+
"schema": {
|
|
3531
|
+
"type": "string"
|
|
3532
|
+
}
|
|
3533
|
+
},
|
|
3534
|
+
{
|
|
3535
|
+
"name": "crs",
|
|
3536
|
+
"in": "query",
|
|
3537
|
+
"description": "Output CRS URI from the collection crs list (default CRS84)",
|
|
3538
|
+
"required": false,
|
|
3539
|
+
"schema": {
|
|
3540
|
+
"type": "string"
|
|
3541
|
+
}
|
|
3542
|
+
},
|
|
3543
|
+
{
|
|
3544
|
+
"name": "width",
|
|
3545
|
+
"in": "query",
|
|
3546
|
+
"description": "Image width in pixels (max 16384; default 1024 or from the aspect ratio)",
|
|
3547
|
+
"required": false,
|
|
3548
|
+
"schema": {
|
|
3549
|
+
"type": "integer"
|
|
3550
|
+
}
|
|
3551
|
+
},
|
|
3552
|
+
{
|
|
3553
|
+
"name": "height",
|
|
3554
|
+
"in": "query",
|
|
3555
|
+
"description": "Image height in pixels (max 16384; default from the aspect ratio)",
|
|
3556
|
+
"required": false,
|
|
3557
|
+
"schema": {
|
|
3558
|
+
"type": "integer"
|
|
3559
|
+
}
|
|
3560
|
+
},
|
|
3561
|
+
{
|
|
3562
|
+
"name": "f",
|
|
3563
|
+
"in": "query",
|
|
3564
|
+
"description": "png (default) or jpeg",
|
|
3565
|
+
"required": false,
|
|
3566
|
+
"schema": {
|
|
3567
|
+
"type": "string",
|
|
3568
|
+
"enum": [
|
|
3569
|
+
"png",
|
|
3570
|
+
"jpeg"
|
|
3571
|
+
]
|
|
3572
|
+
}
|
|
3573
|
+
},
|
|
3574
|
+
{
|
|
3575
|
+
"name": "transparent",
|
|
3576
|
+
"in": "query",
|
|
3577
|
+
"description": "Transparent background (default true for png, false for jpeg)",
|
|
3578
|
+
"required": false,
|
|
3579
|
+
"schema": {
|
|
3580
|
+
"type": "boolean"
|
|
3581
|
+
}
|
|
3582
|
+
},
|
|
3583
|
+
{
|
|
3584
|
+
"name": "bgcolor",
|
|
3585
|
+
"in": "query",
|
|
3586
|
+
"description": "Background colour 0xRRGGBB",
|
|
3587
|
+
"required": false,
|
|
3588
|
+
"schema": {
|
|
3589
|
+
"type": "string"
|
|
3590
|
+
}
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
"name": "datetime",
|
|
3594
|
+
"in": "query",
|
|
3595
|
+
"description": "ISO 8601 instant: render the version valid at that time (versioned layers only)",
|
|
3596
|
+
"required": false,
|
|
3597
|
+
"schema": {
|
|
3598
|
+
"type": "string"
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
],
|
|
3602
|
+
"responses": {
|
|
3603
|
+
"200": {
|
|
3604
|
+
"description": "image/png or image/jpeg"
|
|
3605
|
+
},
|
|
3606
|
+
"400": {
|
|
3607
|
+
"description": "Invalid or unknown parameter"
|
|
3608
|
+
},
|
|
3609
|
+
"401": {
|
|
3610
|
+
"description": "Credentials required for a Read/write layer"
|
|
3611
|
+
},
|
|
3612
|
+
"403": {
|
|
3613
|
+
"description": "Insufficient privileges, or denied by a geofence rule"
|
|
3614
|
+
},
|
|
3615
|
+
"404": {
|
|
3616
|
+
"description": "Unknown collection"
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
},
|
|
3016
3621
|
"/api/v4/ows/schema/{schema}/database/{database}": {
|
|
3017
3622
|
"get": {
|
|
3018
3623
|
"tags": [
|
|
@@ -3402,6 +4007,82 @@
|
|
|
3402
4007
|
}
|
|
3403
4008
|
}
|
|
3404
4009
|
},
|
|
4010
|
+
"/api/v4/schemas/{schema}/tables/{table}/search": {
|
|
4011
|
+
"get": {
|
|
4012
|
+
"tags": [
|
|
4013
|
+
"Search"
|
|
4014
|
+
],
|
|
4015
|
+
"description": "Search the table's OpenSearch index. Query string or body is passed to OpenSearch _search unaltered.",
|
|
4016
|
+
"operationId": "search",
|
|
4017
|
+
"responses": {
|
|
4018
|
+
"200": {
|
|
4019
|
+
"description": "OpenSearch response"
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
},
|
|
4023
|
+
"put": {
|
|
4024
|
+
"tags": [
|
|
4025
|
+
"Search"
|
|
4026
|
+
],
|
|
4027
|
+
"description": "(Re)build the OpenSearch index from the table/view. Owner/superuser only.",
|
|
4028
|
+
"operationId": "buildSearchIndex",
|
|
4029
|
+
"responses": {
|
|
4030
|
+
"200": {
|
|
4031
|
+
"description": "Index built"
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
},
|
|
4035
|
+
"post": {
|
|
4036
|
+
"tags": [
|
|
4037
|
+
"Search"
|
|
4038
|
+
],
|
|
4039
|
+
"description": "Search with an OpenSearch query DSL body.",
|
|
4040
|
+
"operationId": "searchPost",
|
|
4041
|
+
"responses": {
|
|
4042
|
+
"200": {
|
|
4043
|
+
"description": "OpenSearch response"
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
},
|
|
4047
|
+
"delete": {
|
|
4048
|
+
"tags": [
|
|
4049
|
+
"Search"
|
|
4050
|
+
],
|
|
4051
|
+
"description": "Drop the OpenSearch index. Owner/superuser only.",
|
|
4052
|
+
"operationId": "dropSearchIndex",
|
|
4053
|
+
"responses": {
|
|
4054
|
+
"200": {
|
|
4055
|
+
"description": "Index dropped"
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
},
|
|
4060
|
+
"/api/v4/search/settings": {
|
|
4061
|
+
"get": {
|
|
4062
|
+
"tags": [
|
|
4063
|
+
"Search"
|
|
4064
|
+
],
|
|
4065
|
+
"description": "Get the per-database OpenSearch analysis block (the default when none is set).",
|
|
4066
|
+
"operationId": "getSearchSettings",
|
|
4067
|
+
"responses": {
|
|
4068
|
+
"200": {
|
|
4069
|
+
"description": "The analysis block"
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
},
|
|
4073
|
+
"put": {
|
|
4074
|
+
"tags": [
|
|
4075
|
+
"Search"
|
|
4076
|
+
],
|
|
4077
|
+
"description": "Set the per-database OpenSearch analysis block. Applies to indexes built after this call.",
|
|
4078
|
+
"operationId": "putSearchSettings",
|
|
4079
|
+
"responses": {
|
|
4080
|
+
"200": {
|
|
4081
|
+
"description": "Saved"
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
},
|
|
3405
4086
|
"/api/v4/schemas/{schema}/sequences/{sequence}": {
|
|
3406
4087
|
"get": {
|
|
3407
4088
|
"tags": [
|
|
@@ -6010,6 +6691,10 @@
|
|
|
6010
6691
|
},
|
|
6011
6692
|
"type": "object"
|
|
6012
6693
|
}
|
|
6694
|
+
},
|
|
6695
|
+
"elasticsearch": {
|
|
6696
|
+
"description": "Per-column Elasticsearch/OpenSearch mapping config (elasticsearchtype/analyzer/search_analyzer per column). Consumed when the Search API (re)builds the index.",
|
|
6697
|
+
"type": "object"
|
|
6013
6698
|
}
|
|
6014
6699
|
},
|
|
6015
6700
|
"type": "object"
|
|
@@ -6244,6 +6929,34 @@
|
|
|
6244
6929
|
},
|
|
6245
6930
|
"type": "object"
|
|
6246
6931
|
},
|
|
6932
|
+
"OAuthGuest": {
|
|
6933
|
+
"description": "Guest token request payload.",
|
|
6934
|
+
"required": [
|
|
6935
|
+
"client_id",
|
|
6936
|
+
"database"
|
|
6937
|
+
],
|
|
6938
|
+
"properties": {
|
|
6939
|
+
"database": {
|
|
6940
|
+
"title": "Database",
|
|
6941
|
+
"description": "Database name the guest token will be created for.",
|
|
6942
|
+
"type": "string",
|
|
6943
|
+
"example": "my_database"
|
|
6944
|
+
},
|
|
6945
|
+
"client_id": {
|
|
6946
|
+
"title": "Client id",
|
|
6947
|
+
"description": "OAuth client id.",
|
|
6948
|
+
"type": "string",
|
|
6949
|
+
"example": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3"
|
|
6950
|
+
},
|
|
6951
|
+
"client_secret": {
|
|
6952
|
+
"title": "Client secret",
|
|
6953
|
+
"description": "OAuth client secret.",
|
|
6954
|
+
"type": "string",
|
|
6955
|
+
"example": "xxx"
|
|
6956
|
+
}
|
|
6957
|
+
},
|
|
6958
|
+
"type": "object"
|
|
6959
|
+
},
|
|
6247
6960
|
"Outbox": {
|
|
6248
6961
|
"description": "",
|
|
6249
6962
|
"required": [],
|
|
@@ -7065,6 +7778,10 @@
|
|
|
7065
7778
|
"name": "OAuth",
|
|
7066
7779
|
"description": "OAuth"
|
|
7067
7780
|
},
|
|
7781
|
+
{
|
|
7782
|
+
"name": "Ogc",
|
|
7783
|
+
"description": "Ogc"
|
|
7784
|
+
},
|
|
7068
7785
|
{
|
|
7069
7786
|
"name": "Ows",
|
|
7070
7787
|
"description": "Ows"
|
|
@@ -7073,6 +7790,10 @@
|
|
|
7073
7790
|
"name": "Privileges",
|
|
7074
7791
|
"description": "Privileges"
|
|
7075
7792
|
},
|
|
7793
|
+
{
|
|
7794
|
+
"name": "Search",
|
|
7795
|
+
"description": "Search"
|
|
7796
|
+
},
|
|
7076
7797
|
{
|
|
7077
7798
|
"name": "Sql",
|
|
7078
7799
|
"description": "Sql"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centia-io/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"author": "",
|
|
25
25
|
"license": "ISC",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@centia-io/sdk": "^0.2.
|
|
27
|
+
"@centia-io/sdk": "^0.2.13",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
29
29
|
"axios": "^1.13.5",
|
|
30
30
|
"zod": "^4.3.6"
|
package/skills/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Use the smallest matching skill set for the task.
|
|
|
20
20
|
| `centia-keyvalue` | Storing/reading JSON state (settings, flags, preferences) in the key/value store |
|
|
21
21
|
| `centia-feature` | Reading/creating/updating/deleting single features as GeoJSON by primary key |
|
|
22
22
|
| `centia-realtime` | Realtime WebSocket events, subscriptions with shapes, enabling table change events |
|
|
23
|
+
| `centia-ows` | Serving/querying layers via WMS, WFS, WFS-T and the MapCache (WMTS/TMS/XYZ) proxy; Basic/Bearer/anonymous access, FILTERS, srs/time-slice |
|
|
24
|
+
| `centia-ogc` | OGC API Features/Maps: collections, GeoJSON items with bbox/limit/offset/crs/datetime, map images by URL |
|
|
23
25
|
| `centia-delivery` | Final delivery checklist, MCP coverage check, and handoff quality gate |
|
|
24
26
|
| `danish-search` | Danish address/matrikel typeahead search component (`@centia-io/danish-search`) |
|
|
25
27
|
|
|
@@ -31,6 +31,25 @@ Forbidden:
|
|
|
31
31
|
- manual OAuth implementation
|
|
32
32
|
- provisioning API calls from browser runtime
|
|
33
33
|
|
|
34
|
+
## Public (anonymous) access without sign-in
|
|
35
|
+
|
|
36
|
+
When an app must call the API before any user has signed in, use the **guest token flow**: SDK class `GuestFlow` (`POST /api/v4/oauth/guest`; MCP tool `postGuest`).
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { GuestFlow } from '@centia-io/sdk'
|
|
40
|
+
const flow = new GuestFlow({ host, clientId, database, clientSecret? })
|
|
41
|
+
await flow.signIn() // stores access/refresh tokens; Sql/Rpc/Gql now run as the default user
|
|
42
|
+
flow.signOut() // clears tokens
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- Body: `database`, `client_id`, and `client_secret` only when the OAuth client is confidential (public clients omit it).
|
|
46
|
+
- No `Authorization` header. Returns the normal OAuth response (`access_token`, `refresh_token`, `token_type: bearer`, `expires_in`), refreshable with the standard refresh flow.
|
|
47
|
+
- The token is issued for the database's **default user** (`default_user=true`, see `centia-privileges`) and carries exactly that user's privileges — the same identity anonymous OGC/OWS requests get.
|
|
48
|
+
- Prerequisites: a default user must exist on the database (otherwise 404 `NO_DEFAULT_USER_FOUND`) and an OAuth client must be registered (`postClient`); unknown client → 401 `invalid_grant`, wrong secret → 401 `invalid_client`.
|
|
49
|
+
- Use it for read-only/public UIs and for token-only endpoints (Feature, SQL, GraphQL, RPC) that need a bearer even for public data. Never grant the default user write or admin privileges to make a guest app "work".
|
|
50
|
+
- In browsers use a **public** client so no secret ships in the bundle; switch to `CodeFlow` once the user signs in.
|
|
51
|
+
- Errors are thrown as plain `Error` with status and response body, like the other auth flows — not `CentiaApiError`. `GuestFlow` ships in `@centia-io/sdk` >= 0.2.14 (unreleased as of 2026-09-11; latest is 0.2.13).
|
|
52
|
+
|
|
34
53
|
## Server and CLI
|
|
35
54
|
|
|
36
55
|
Allowed methods:
|
|
@@ -63,6 +82,7 @@ Forbidden:
|
|
|
63
82
|
|
|
64
83
|
- Browser app: `CodeFlow`
|
|
65
84
|
- Browser signup: `SignUp`
|
|
85
|
+
- Browser/public access before sign-in: `GuestFlow`
|
|
66
86
|
- Backend app: `PasswordFlow`
|
|
67
87
|
- Provisioning: `PasswordFlow` or access token
|
|
68
88
|
- CLI: `PasswordFlow`
|
|
@@ -39,7 +39,7 @@ Query and API:
|
|
|
39
39
|
|
|
40
40
|
Auth and access:
|
|
41
41
|
|
|
42
|
-
- `postOauth`, `postDevice`
|
|
42
|
+
- `postOauth`, `postDevice`, `postGuest`
|
|
43
43
|
- `getUser`, `postUser`, `patchUser`, `deleteUsers`
|
|
44
44
|
- `getRule`, `postRule`, `patchRule`, `deleteRule`
|
|
45
45
|
- `getPrivileges`, `patchPrivileges`
|
|
@@ -49,6 +49,11 @@ Metadata:
|
|
|
49
49
|
|
|
50
50
|
- `getMetaData`, `patchMetaData`
|
|
51
51
|
|
|
52
|
+
OGC services:
|
|
53
|
+
|
|
54
|
+
- `getOws`, `postOws`, `getWfs`, `postWfs`, `getMapcache`, `deleteMapcacheTileset`
|
|
55
|
+
- `getOgcLandingPage`, `getOgcConformance`, `getOgcCollections`, `getOgcCollection`, `getOgcItems`, `getOgcItem`, `getOgcCollectionMap`, `getOgcDatasetMap`
|
|
56
|
+
|
|
52
57
|
Import and misc:
|
|
53
58
|
|
|
54
59
|
- `postFileUpload`, `postFileProcess`
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: centia-ogc
|
|
3
|
+
description: Use when reading features or rendering maps through the OGC API (OGC API Features Part 1/2, OGC API Maps Part 1) in Centia BaaS — /api/v4/ogc/database/{database}/... routes, collections, items as GeoJSON with bbox/limit/offset/crs/datetime, map images, the getOgc* tools, CRS URIs and axis order, or the 401/403/404 semantics of collections.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OGC API (Features + Maps)
|
|
7
|
+
|
|
8
|
+
Use this skill for the v4 OGC API under `/api/v4/ogc/database/{database}`: a standards-based, read-only view of OWS-enabled layers. Features are served by the in-process WFS engine (geofence rules with service `wfst`, versioning, workflow all apply), maps by the WMS backend through the OWS proxy (rules with service `ows`, versioning). For single-feature CRUD use `centia-feature`; for classic WMS/WFS/WMTS use `centia-ows`.
|
|
9
|
+
|
|
10
|
+
## Availability
|
|
11
|
+
|
|
12
|
+
Lives on the GeoCloud2 branch `feature/v4-ogc-api` (2026-09-10/11). **Not** on production `api.centia.io` yet — assume dev-only. SDK: `@centia-io/sdk` >= 0.2.13 ships the `Ogc` class (explicit-client pattern, `new Ogc(client)`) with `getLandingPage`, `getConformance`, `getCollections`, `getCollection`, `getItems<P>`, `getItem<P>`, and the URL builders `mapUrl`/`datasetMapUrl` for images, plus `OGC_CRS84` and `ogcEpsgCrs(code)` for CRS URIs. Otherwise use the MCP tools or plain HTTP. `f=jpeg` maps need the WMS mapfiles regenerated once after upgrade (save any layer via `postLayer`, or the legacy `/controllers/mapfile` endpoint).
|
|
13
|
+
|
|
14
|
+
## Routes and MCP tools
|
|
15
|
+
|
|
16
|
+
All routes are `GET`, public (`Scope::PUBLIC`): Bearer token, HTTP Basic or anonymous. A Bearer token must match `{database}` (401 otherwise). Collection id is always `schema.table`.
|
|
17
|
+
|
|
18
|
+
| Route (relative to `/api/v4/ogc/database/{database}`) | Tool | Returns |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `/` | `getOgcLandingPage` | links: `conformance`, `data` (collections), `service-desc` (OpenAPI), `service-doc` |
|
|
21
|
+
| `/conformance` | `getOgcConformance` | `conformsTo` — Features Core/GeoJSON/OAS30/CRS, Maps Core/bbox/crs/datetime/png/jpeg |
|
|
22
|
+
| `/collections?limit&offset` | `getOgcCollections` | `collections[]`, `numberMatched`, `numberReturned`, `next`/`prev` links (limit default 100, max 1000) |
|
|
23
|
+
| `/collections/{id}` | `getOgcCollection` | `id`, `title`, `description`, `extent.spatial.bbox` (CRS84), `extent.temporal` (versioned layers), `crs[]`, `storageCrs`, links `items` (vector only) and `map` |
|
|
24
|
+
| `/collections/{id}/items?limit&offset&bbox&bbox-crs&crs&datetime` | `getOgcItems` / `Ogc.getItems(db, id, {limit, offset, bbox, bboxCrs, crs, datetime})` | streamed `application/geo+json` FeatureCollection + `Content-Crs` header |
|
|
25
|
+
| `/collections/{id}/items/{fid}?crs&datetime` | `getOgcItem` / `Ogc.getItem` | bare GeoJSON `Feature` with `links` |
|
|
26
|
+
| `/collections/{id}/map?bbox&bbox-crs&crs&width&height&f&transparent&bgcolor&datetime` | `getOgcCollectionMap` / `Ogc.mapUrl` | `image/png` (default) or `image/jpeg` |
|
|
27
|
+
| `/map?collections=a,b&...` | `getOgcDatasetMap` / `Ogc.datasetMapUrl` | image with several collections **of the same schema** (400 otherwise) |
|
|
28
|
+
|
|
29
|
+
Only layers with WFS/OWS enabled (`enableows`) are collections. Raster layers appear as collections without `itemType`/`items` (map only). The MCP tools return the response as text: fine for JSON, useless for map images — fetch maps over HTTP.
|
|
30
|
+
|
|
31
|
+
## Items: parameters and defaults
|
|
32
|
+
|
|
33
|
+
- `limit` default **10**, max 10000 (values above are clamped); `offset` default 0. The response always carries `numberMatched` (exact count, saturating at 1,000,000) and `numberReturned`; follow the `next` link to page.
|
|
34
|
+
- `bbox` = `minx,miny,maxx,maxy` (6 values allowed, the vertical pair is dropped) in the axis order of `bbox-crs`; a lower corner above the upper corner is 400 (no antimeridian boxes).
|
|
35
|
+
- `crs` / `bbox-crs` must be a URI from the collection's `crs` list: `http://www.opengis.net/def/crs/OGC/1.3/CRS84` (default, lon/lat), `http://www.opengis.net/def/crs/EPSG/0/4326` (**lat/lon** axis order, both in bbox input and GeoJSON output), `http://www.opengis.net/def/crs/EPSG/0/<n>` for projected CRSs (x/y). Anything else is 400 `INVALID_CRS`.
|
|
36
|
+
- `datetime` = one ISO 8601 instant (`2024-01-01T00:00:00Z`); intervals are 400 `DATETIME_INTERVAL_UNSUPPORTED`. On a versioned layer it selects the version valid at that instant; on other layers it is ignored. Without it, versioned layers return the current version.
|
|
37
|
+
- `f=json` only; any unknown query parameter is 400 `UNKNOWN_PARAMETER` (OGC API Common). The single-item route accepts only `crs`, `datetime`, `f`.
|
|
38
|
+
- Properties keep the primary key as well as exposing it as the feature `id` (int when the key column is an integer type). Geometry is `null` for rows without geometry; `bytea` columns are omitted.
|
|
39
|
+
|
|
40
|
+
## Maps: parameters
|
|
41
|
+
|
|
42
|
+
`bbox` defaults to the collection extent; `width`/`height` 1..16384 — give one and the other follows the bbox aspect ratio, give none and width is 1024; `f` = `png` (default, transparent) or `jpeg` (opaque); `bgcolor` = `0xRRGGBB`; `datetime` as for items. Rendering goes through the same WMS backend as `centia-ows`, so styling comes from the layer classes (`centia-map-styling`).
|
|
43
|
+
|
|
44
|
+
## Access model and status codes
|
|
45
|
+
|
|
46
|
+
Same per-layer model as WMS/WFS (`centia-privileges`): anonymous callers read layers with `authentication` `None`/`Write`; `Read/write` layers need credentials and, for sub-users, a privilege ≠ `none` (group inheritance applies). Geofence rules (`getRule`/`postRule`) apply: service `wfst` for items, `ows` for maps; `deny` → 403 `FORBIDDEN`, `limit` filters the rows/pixels. Anonymous requests are geofenced as `*`, never as the database owner.
|
|
47
|
+
|
|
48
|
+
| Situation | Status |
|
|
49
|
+
|---|---|
|
|
50
|
+
| Unknown collection or feature | 404 `COLLECTION_NOT_FOUND` / `FEATURE_NOT_FOUND` |
|
|
51
|
+
| Existing `Read/write` collection, no credentials | 401 `UNAUTHORIZED` + `WWW-Authenticate: Basic` |
|
|
52
|
+
| Existing `Read/write` collection, identity without privilege | 403 `INSUFFICIENT_PRIVILEGES` |
|
|
53
|
+
| Geofence deny | 403 `FORBIDDEN` |
|
|
54
|
+
| Bad/unknown parameter, bad CRS, interval datetime, items on a raster | 400 |
|
|
55
|
+
| Map backend (MapServer) failure, e.g. missing mapfile | 502 `MAP_BACKEND_ERROR` (JSON, never an HTML page with 200) |
|
|
56
|
+
|
|
57
|
+
`/collections` simply omits what the caller may not read. Errors use the v4 JSON shape `{"success":false,"message":…,"code":…,"errorCode":…}`.
|
|
58
|
+
|
|
59
|
+
## Common mistakes
|
|
60
|
+
|
|
61
|
+
| Mistake | Reality |
|
|
62
|
+
|---|---|
|
|
63
|
+
| Expecting every feature from `/items` | default `limit` is 10 — pass `limit` or follow `next` |
|
|
64
|
+
| Passing `bbox` as lat/lon with the default CRS | CRS84 is lon/lat; only the EPSG:4326 URI is lat/lon |
|
|
65
|
+
| Passing `crs=EPSG:25832` | CRS must be the URI `http://www.opengis.net/def/crs/EPSG/0/25832` |
|
|
66
|
+
| Expecting 404 on a protected collection | it exists → 401 (anonymous) or 403 (no privilege); 404 means unknown |
|
|
67
|
+
| Adding custom query parameters | 400 `UNKNOWN_PARAMETER` |
|
|
68
|
+
| Reading a map image through the MCP tool | tools return text; use `Ogc.mapUrl()` and fetch the image over HTTP |
|
|
69
|
+
| Mixing schemas in `/map?collections=` | one WMS mapfile per schema → 400 |
|
|
70
|
+
| Using it for editing | read-only; write with `centia-feature` (WFS-T) or the SQL API |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: centia-ows
|
|
3
|
+
description: Use when serving or querying layers through the classic OGC web services in Centia BaaS — WMS/UTFGRID/MVT via /api/v4/ows, WFS and WFS-T via /api/v4/wfs, the authorizing MapCache (WMTS/TMS/XYZ) proxy via /api/v4/mapcache, the getOws/postOws/getWfs/postWfs/getMapcache/deleteMapcacheTileset tools, the FILTERS/labels parameters, srs and time-slice path segments, or Basic/Bearer/anonymous access to protected layers.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OWS: WMS, WFS, WFS-T and MapCache
|
|
7
|
+
|
|
8
|
+
Use this skill for the v4 OGC web service endpoints. They are the map/feature backends that GIS clients (QGIS, MapLibre/Leaflet WMS layers, OpenLayers WFS) consume directly. For the RESTful OGC API (GeoJSON items, map images by URL) use `centia-ogc`; for single-feature CRUD use `centia-feature`.
|
|
9
|
+
|
|
10
|
+
## Availability
|
|
11
|
+
|
|
12
|
+
Production-ready on GeoCloud2 `master` (2026-08). SDK: `@centia-io/sdk` >= 0.2.12 ships `Ows`, `Wfs` and `Mapcache` (explicit-client pattern: `new Wfs(client)`); they return text/JSON, and `Mapcache.mapcacheUrl()` builds tile URL templates for map libraries. The MCP tools wrap the same routes and return text (XML/JSON), so they suit GetCapabilities/DescribeFeatureType/GetFeature checks, not images or tiles.
|
|
13
|
+
|
|
14
|
+
## Endpoints, MCP tools and SDK
|
|
15
|
+
|
|
16
|
+
| Service | Route | Tools / SDK | Notes |
|
|
17
|
+
|---|---|---|---|
|
|
18
|
+
| WMS 1.1.1/1.3.0, UTFGRID, MVT | `GET /api/v4/ows/schema/{schema}/database/{database}?SERVICE=WMS&REQUEST=GetMap&LAYERS=schema.table&...` | `getOws` / `Ows.getOws` | MapServer (or QGIS Server / external WMS source per layer). Layer names are `schema.table`. `FORMAT=json` → UTFGRID, `FORMAT=mvt` → vector tiles |
|
|
19
|
+
| WFS proxy (MapServer) | `POST /api/v4/ows/...` with WFS XML | `postOws` / `Ows.postOws` | legacy path; prefer `/api/v4/wfs` |
|
|
20
|
+
| WFS 1.0.0/1.1.0 | `GET /api/v4/wfs/schema/{schema}/database/{database}/srs/{srs}/ts/{timeSlice}?SERVICE=WFS&REQUEST=GetFeature&TYPENAME=table&...` | `getWfs` / `Wfs.getWfs(schema, db, params, {srs, timeSlice})` | in-process engine; `srs` = output SRID, `timeSlice` = ISO date for versioned layers — both optional path segments (omit the whole `/srs/...` tail) |
|
|
21
|
+
| WFS-T | `POST /api/v4/wfs/...` with `<wfs:Transaction>` (Insert/Update/Delete) | `postWfs` / `Wfs.postWfs` | versioning, workflow, geofence rules, tile-cache busting, pre/post processors |
|
|
22
|
+
| MapCache (WMTS, TMS, XYZ, Google) | `GET /api/v4/mapcache/database/{database}/{path}` e.g. `wmts/1.0.0/WMTSCapabilities.xml`, `tms/1.0.0/schema.table@g/10/512/340.png` | `getMapcache` / `Mapcache.getMapcache`, `Mapcache.mapcacheUrl` | authorizing proxy in front of MapCache; a tile whose tileset cannot be resolved fails closed (403) |
|
|
23
|
+
| Tile cache wipe | `DELETE /api/v4/mapcache/database/{database}/tileset/{tileset}?bbox&zoom` | `deleteMapcacheTileset` / `Mapcache.deleteMapcacheTileset` | full wipe is synchronous (200), scoped delete is a background job (202) |
|
|
24
|
+
|
|
25
|
+
WMS GetMap parameters follow the standard (`BBOX`, `WIDTH`, `HEIGHT`, `CRS`/`SRS`, `FORMAT=image/png`, `STYLES=`, `TRANSPARENT`). Extra vendor parameters on `/ows`: `FILTERS` = base64url JSON `{ "schema.table": ["sql where", ...] }` applied as extra WHERE clauses; `LABELS=false` disables labels. Rules and `FILTERS` do not work together with several QGIS-backed layers in one request.
|
|
26
|
+
|
|
27
|
+
WFS `GetFeature` supports `TYPENAME`, `PROPERTYNAME`, `FEATUREID` (`table.key`), `BBOX`, `FILTER` (OGC Filter Encoding XML), `MAXFEATURES`, `SRSNAME`, `OUTPUTFORMAT` (`GML2`/`GML3`), `RESULTTYPE=hits`. There is no start index/offset — page with the OGC API instead.
|
|
28
|
+
|
|
29
|
+
## Access model
|
|
30
|
+
|
|
31
|
+
Every route is public (`Scope::PUBLIC`) and decides auth per request, per layer:
|
|
32
|
+
|
|
33
|
+
- **Bearer token**: validated, must be issued for `{database}` (401 otherwise, never a silent anonymous downgrade); sub-user privileges via `Authorization::check` with full group inheritance.
|
|
34
|
+
- **HTTP Basic**: user = database owner or a sub-user; the password is the login password, with fallback to the per-database "viewer" password. Credentials are verified even on layer-less requests (GetCapabilities), so a fabricated header is rejected.
|
|
35
|
+
- **Anonymous**: allowed for layers with `authentication` `None`/`Write`; `Read/write` layers answer `401` with a Basic challenge. WFS-T on a `Write` layer also needs credentials with `write` privilege.
|
|
36
|
+
- Allow decisions for token/Basic identities are cached 60 s (tiles); anonymous decisions never are.
|
|
37
|
+
|
|
38
|
+
Layer visibility in GetCapabilities follows the same rule (anonymous clients see `None`/`Write` layers). Geofence rules (`centia-privileges`, `getRule`/`postRule`): service `ows` for WMS/UTFGRID/MVT, `wfst` for WFS/WFS-T; `deny` renders an OWS `ServiceException` (HTTP 200 once streaming started), `limit` injects the rule filter. Anonymous traffic is geofenced as `*`, not as the database owner.
|
|
39
|
+
|
|
40
|
+
## Versioning and workflow
|
|
41
|
+
|
|
42
|
+
Versioned layers (`gc2_version_*` columns): WFS/WMS show the current version by default; `/srs/{srs}/ts/{timeSlice}` on WFS returns the version valid at that time. WFS-T updates close the old version and insert a new one. Workflow layers (`gc2_status`/`gc2_workflow`): a sub-user without a role only reads published rows (`gc2_status = 3`) and cannot transact.
|
|
43
|
+
|
|
44
|
+
## Common mistakes
|
|
45
|
+
|
|
46
|
+
| Mistake | Reality |
|
|
47
|
+
|---|---|
|
|
48
|
+
| Using unqualified layer names in WMS | `LAYERS=schema.table`; WFS `TYPENAME` may be unqualified because the schema is in the path |
|
|
49
|
+
| Sending a token for another database | 401 — the token must carry `database == {database}` |
|
|
50
|
+
| Expecting anonymous access to a `Read/write` layer | 401 + `WWW-Authenticate: Basic`; use Basic (viewer/login password) or a token |
|
|
51
|
+
| Reading tiles straight from `/mapcache/` | bypasses authorization; use `/api/v4/mapcache/database/{db}/...` |
|
|
52
|
+
| Paging WFS with `STARTINDEX` | unsupported; use OGC API `/items?limit&offset` |
|
|
53
|
+
| Fetching images through MCP tools or `Ows.getOws` | tools and wrappers return text; images/tiles must be fetched over HTTP (`Mapcache.mapcacheUrl()` for tile templates) |
|
|
54
|
+
| Expecting HTTP 4xx from a WMS/WFS error mid-stream | OGC `ServiceException`/`ExceptionReport` XML with HTTP 200 once headers are sent |
|