@centia-io/mcp-server 1.0.15 → 1.0.17

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.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: centia-openapi-docs
3
+ description: OpenAPI and documentation fallback policy for Centia BaaS, including source priority, local caching workflow, and rules for docs-backed endpoint usage.
4
+ ---
5
+
6
+ # OpenAPI and Docs Policy
7
+
8
+ Use this skill when MCP/SDK are insufficient and HTTP contracts are needed.
9
+
10
+ ## Source priority
11
+
12
+ Use this order:
13
+
14
+ 1. Local `openapi/openapi.json`
15
+ 2. URL from `CENTIA_OPENAPI_URL`
16
+ 3. User-provided OpenAPI JSON
17
+
18
+ OpenAPI is the contract for described endpoints.
19
+
20
+ ## Fetch pattern
21
+
22
+ If allowed and needed, fetch into local cache:
23
+
24
+ ```sh
25
+ mkdir -p openapi
26
+ if [ -n "$CENTIA_OPENAPI_URL" ] && [ ! -f "openapi/openapi.fetched.json" ]; then
27
+ curl -fsSL -H "Authorization: Bearer $CENTIA_ACCESS_TOKEN" "$CENTIA_OPENAPI_URL" -o openapi/openapi.fetched.json
28
+ fi
29
+ ```
30
+
31
+ ## Documentation fallback
32
+
33
+ Primary docs:
34
+
35
+ - `https://centia.io/docs/intro`
36
+
37
+ Optional local vendor mirror:
38
+
39
+ ```sh
40
+ mkdir -p vendor
41
+ if [ ! -d "vendor/centia-docs/docs" ]; then
42
+ git clone --depth 1 --filter=blob:none --sparse https://github.com/centia-io/website.git vendor/centia-docs
43
+ cd vendor/centia-docs
44
+ git sparse-checkout set docs
45
+ cd ../..
46
+ fi
47
+ ```
48
+
49
+ If clone fails (auth/network), use web docs directly.
50
+
51
+ ## Rules
52
+
53
+ - Use docs-backed endpoints only when fully specified.
54
+ - Do not invent undocumented payloads.
55
+ - Document source URL for each HTTP fallback implementation.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: centia-privileges
3
+ description: Sub-user privileges and rights inheritance for Centia BaaS, including per-table privilege grants, group membership via user_group, transitive inheritance where the highest privilege wins, schema ownership, and layer authentication levels.
4
+ ---
5
+
6
+ # Privileges and Rights Inheritance
7
+
8
+ Use this skill when granting sub-users access to tables/layers, managing groups, or reasoning about which effective privilege a sub-user ends up with.
9
+
10
+ ## Model
11
+
12
+ - Privileges are **per relation** (per `schema.table`), not schema-wide grants.
13
+ - A grant maps a sub-user *or group* name to a level: `none` | `read` | `write` (API values; internally ranked none=0 < read=1 < read/write=2).
14
+ - **Groups are themselves sub-users.** There is no separate group entity or membership endpoint — any sub-user name can act as a group.
15
+
16
+ ## Endpoints and MCP tools
17
+
18
+ | Task | Route | MCP tool |
19
+ |---|---|---|
20
+ | Read grants for a table | `GET /api/v4/schemas/{s}/tables/{t}/privileges` | `getPrivileges` |
21
+ | Set grants | `PATCH .../privileges` with `{subuser, privilege}` (one or array) | `patchPrivileges` |
22
+ | Group membership | `user_group` field on `POST/PATCH/GET /api/v4/users` | `postUser` / `patchUser` / `getUser` |
23
+
24
+ `user_group` is a **JSON array of group names** (e.g. `["editors", "gis_admins"]`). A JSON-array *string* is accepted for back-compat; `null` clears membership; GET returns a decoded array (or null).
25
+
26
+ ## Inheritance — highest privilege wins
27
+
28
+ Evaluation is fully server-side:
29
+
30
+ 1. The full inheritance chain is resolved: the sub-user plus all ancestor groups, transitively (groups can belong to groups; breadth-first, diamond/cycle-safe).
31
+ 2. Effective privilege on a table = the **highest** level found among the sub-user's own grant and every group in the chain.
32
+
33
+ Example: `joe` has `read` directly, is member of `editors` which has `write` → joe's effective privilege is `write`.
34
+
35
+ This applies to token auth, Basic-auth WFS/OWS, and SQL authorization alike.
36
+
37
+ ## Schema ownership
38
+
39
+ A sub-user is **owner** of a schema if their own name — or any group in their transitive chain — equals the schema name. Owners bypass all privilege checks (full read/write on every layer in that schema). This is how schema-wide access is inherited: put the sub-user (directly or via intermediate groups) in a group named after the schema.
40
+
41
+ ## Layer authentication levels
42
+
43
+ Orthogonal to per-user grants, each layer has an `authentication` level (capitalized values) deciding *when* grants are enforced:
44
+
45
+ | Level | Anonymous | Authenticated sub-user |
46
+ |---|---|---|
47
+ | `Read/write` | No read, no write (403) | Read requires privilege ≠ `none`; write requires `write` (owner bypasses) |
48
+ | `Write` | Read allowed | Read allowed for all; write requires `write` (owner bypasses) |
49
+ | `Read` / `None` | Read open | No per-user enforcement on read |
50
+
51
+ OGC endpoints are merged, database-qualified routes (`/api/v4/ows/schema/{schema}/database/{database}`, `/api/v4/wfs/schema/{schema}/database/{database}/srs/...`; the old token-only variants are removed). Auth is decided per request: Bearer token (must match `{database}`, else 401 — never a silent anonymous downgrade), anonymous for publicly readable layers, or an HTTP Basic challenge for protected layers. WFS-T always requires credentials with `write`.
52
+
53
+ ## Common mistakes
54
+
55
+ | Mistake | Reality |
56
+ |---|---|
57
+ | Expecting a schema-wide grant endpoint | Grants are per table; schema-wide access only via ownership (group named = schema) |
58
+ | `PATCH /users/{name}` without `default_user` | The flag is **reset to false** when omitted — always send it explicitly |
59
+ | Promoting a new default user first | Only one default user per parent db (partial unique index) → 23505; demote the old one first |
60
+ | Treating groups as a separate concept | Groups are sub-users; membership lives on the member's `user_group` field |
61
+ | Assuming grants alone control access | The layer's `authentication` level decides whether grants are even consulted |
62
+ | Expecting `200` from `patchPrivileges`/`patchUser` | Provisioning PATCH returns `303 See Other` (see `centia-provisioning`) |
63
+
64
+ Spec quirk: the `Privilege` schema's `required` list names `privileges` (plural) but the actual property is singular `privilege` — use the singular key.
65
+
66
+ Auth context selection (tokens, OAuth, service vs browser) is covered in `centia-auth-model`.
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: centia-provisioning
3
+ description: Provisioning and schema lifecycle guidance for Centia BaaS, including schema/table/column/index/constraint operations, migration structure, SQL API limits, and destructive-change safety.
4
+ ---
5
+
6
+ # Provisioning Rules
7
+
8
+ Use this skill for schema changes, migrations, seed data, and platform setup.
9
+
10
+ ## Scope
11
+
12
+ Provisioning includes:
13
+
14
+ - schemas
15
+ - tables
16
+ - columns
17
+ - indexes
18
+ - constraints
19
+ - policies
20
+ - relations
21
+ - relation metadata (titles, descriptions, tags, field aliases)
22
+ - seed data
23
+ - migrations
24
+
25
+ Provisioning is not runtime logic.
26
+
27
+ ## Hard rules
28
+
29
+ - Preferred execution order: MCP tools, then OpenAPI, then docs-backed endpoints.
30
+ - Runtime app code must never call provisioning endpoints.
31
+ - Schema changes happen only in provisioning/codegen flows.
32
+
33
+ ## PATCH returns 303 See Other
34
+
35
+ All provisioning PATCH endpoints return `303 See Other` with a `Location` header pointing to the updated resource. HTTP clients that automatically follow redirects will receive an unexpected `200 OK` from the subsequent GET instead of the expected `303`.
36
+
37
+ External clients **must** disable automatic redirect-following:
38
+
39
+ - `fetch`: `{ redirect: 'manual' }` in `RequestInit`
40
+ - `axios`: `{ maxRedirects: 0 }` (catch the resulting error)
41
+ - `curl`: omit the `-L` flag
42
+ - Postman: disable "Automatically follow redirects" in Settings
43
+
44
+ The SDK already handles this via `redirect: 'manual'` in all fetch calls.
45
+
46
+ ## SQL dialect and API limits
47
+
48
+ - SQL dialect is PostgreSQL with PostGIS enabled.
49
+ - SQL API accepts only: `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `MERGE`.
50
+ - DDL and transaction control are not accepted by SQL API; use provisioning tools.
51
+
52
+ ## Provisioning output structure
53
+
54
+ Store artifacts in:
55
+
56
+ - `provision/`
57
+ - `migrations/`
58
+ - `schema/plan.json`
59
+ - `schema/plan.md`
60
+
61
+ Example runners:
62
+
63
+ ```sh
64
+ npx tsx provision/apply.ts
65
+ npx tsx migrations/001_create_tables.ts
66
+ ```
67
+
68
+ ## Safety for destructive operations
69
+
70
+ Before destructive operations, present:
71
+
72
+ - method
73
+ - MCP tool or endpoint
74
+ - purpose
75
+
76
+ Proceed only with explicit user confirmation.
77
+
78
+ Destructive includes:
79
+
80
+ - `DROP TABLE`
81
+ - `TRUNCATE`
82
+ - column deletion
83
+ - policy overwrite
84
+ - `DELETE` without safe filter
85
+ - `deleteTable`, `deleteColumn`, `deleteSchema`, `deleteConstraint`, `deleteIndex`
86
+
87
+ ## Project structure target
88
+
89
+ ```txt
90
+ src/
91
+ baas/
92
+ client.ts
93
+ http.ts
94
+ types.ts
95
+ features/
96
+
97
+ provision/
98
+ schema/
99
+ migrations/
100
+ docs/
101
+
102
+ .env.example
103
+ openapi/
104
+ vendor/
105
+ ```
@@ -0,0 +1,186 @@
1
+ ---
2
+ name: centia-realtime
3
+ description: Realtime events via WebSocket for Centia BaaS using @centia-io/sdk, including broadcast connections, subscriptions with shapes, event handling, and enabling table-level change events.
4
+ ---
5
+
6
+ # Realtime Events
7
+
8
+ Use this skill for WebSocket-based realtime database change events.
9
+
10
+ ## Hard rules
11
+
12
+ - Use `Ws` from `@centia-io/sdk` for all realtime connections.
13
+ - Do not implement raw WebSocket connections in runtime app code.
14
+ - Never hardcode tokens in connection URLs.
15
+ - Always use `encodeURIComponent` if building URLs manually (the SDK handles this).
16
+
17
+ ## Architecture
18
+
19
+ - WebSocket endpoint: `wss://event.centia.io/`
20
+ - Auth: JWT token passed as query parameter.
21
+ - Server validates the token and enforces per-relation access for sub-users.
22
+ - Events are batched (10 notifications or 2 seconds, whichever comes first).
23
+
24
+ ## SDK bootstrap
25
+
26
+ ```ts
27
+ import { Ws } from '@centia-io/sdk';
28
+
29
+ const ws = new Ws({
30
+ host: 'wss://event.centia.io',
31
+ rels: 'schema.table1,schema.table2', // optional: broadcast filter
32
+ reconnect: true, // default: true
33
+ reconnectInterval: 3000, // default: 3000ms
34
+ });
35
+
36
+ ws.connect();
37
+ ```
38
+
39
+ ### Options
40
+
41
+ | Option | Type | Required | Default | Description |
42
+ |---|---|---|---|---|
43
+ | `host` | string | yes | - | WebSocket endpoint URL |
44
+ | `rels` | string | no | - | Comma-separated relations for broadcast filtering |
45
+ | `wsClient` | unknown | no | `WebSocket` | Custom WebSocket implementation (e.g. `ws` for Node) |
46
+ | `reconnect` | boolean | no | `true` | Auto-reconnect on disconnect |
47
+ | `reconnectInterval` | number | no | `3000` | Reconnect delay in ms |
48
+
49
+ ## Event listeners
50
+
51
+ The `Ws` class uses a typed event system. `on()` returns an unsubscribe function.
52
+
53
+ ```ts
54
+ ws.on('open', () => { /* connected */ });
55
+
56
+ ws.on('batch', (msg) => {
57
+ // msg.db, msg.batch
58
+ for (const [rel, data] of Object.entries(msg.batch[msg.db])) {
59
+ if (data.INSERT) { /* handle inserts */ }
60
+ if (data.UPDATE) { /* handle updates */ }
61
+ if (data.DELETE) { /* handle deletes */ }
62
+ if (data.full_data) { /* full row data */ }
63
+ }
64
+ });
65
+
66
+ ws.on('subscription_ack', (msg) => {
67
+ // msg.id — matches the subscription id
68
+ });
69
+
70
+ ws.on('error', (msg) => {
71
+ // msg.error: 'missing_token' | 'invalid_token' | 'not_allowed'
72
+ // msg.message: human-readable
73
+ });
74
+
75
+ ws.on('close', ({ code, reason }) => { /* disconnected */ });
76
+
77
+ // Unsubscribe
78
+ const unsub = ws.on('batch', handler);
79
+ unsub(); // removes listener
80
+ ```
81
+
82
+ ## Subscriptions with shapes
83
+
84
+ Subscriptions provide server-side filtering so the client only receives matching events. Send after the connection is open.
85
+
86
+ ```ts
87
+ ws.on('open', () => {
88
+ ws.subscribe({
89
+ id: 'active-users',
90
+ schema: 'public',
91
+ rel: 'users',
92
+ where: "status = 'active' AND age >= 18",
93
+ columns: 'id,name,email',
94
+ op: 'UPDATE',
95
+ });
96
+ });
97
+ ```
98
+
99
+ ### Subscription fields
100
+
101
+ | Field | Type | Required | Description |
102
+ |---|---|---|---|
103
+ | `id` | string | yes | Client-chosen identifier |
104
+ | `schema` | string | yes | Database schema name |
105
+ | `rel` | string | yes | Table name |
106
+ | `where` | string | no | SQL-like filter on `full_data` rows |
107
+ | `columns` | string | no | Comma-separated column projection |
108
+ | `op` | string | no | Limit to `INSERT`, `UPDATE`, or `DELETE` |
109
+
110
+ ### Where syntax
111
+
112
+ Supports: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=`, `AND`, `OR`, parentheses, `IN`/`NOT IN`, `LIKE`/`ILIKE`.
113
+
114
+ ### Multiple subscriptions
115
+
116
+ A single client can register multiple subscriptions:
117
+
118
+ ```ts
119
+ ws.subscribe({ id: 'sub1', schema: 'public', rel: 'orders' });
120
+ ws.subscribe({ id: 'sub2', schema: 'public', rel: 'users', op: 'INSERT' });
121
+ ```
122
+
123
+ ## Enabling table events (provisioning)
124
+
125
+ To emit change events for a table, enable `emit_events` via the events endpoint. This is a provisioning operation.
126
+
127
+ Use MCP tool `postEvents` or HTTP:
128
+
129
+ ```http
130
+ PATCH https://api.centia.io/api/v4/schemas/{schema}/tables/{table}/events
131
+ Content-Type: application/json
132
+ Authorization: Bearer <token>
133
+
134
+ {
135
+ "emit_events": true
136
+ }
137
+ ```
138
+
139
+ ## Broadcast vs subscriptions
140
+
141
+ | Feature | Broadcast (legacy) | Subscriptions |
142
+ |---|---|---|
143
+ | Setup | Pass `rels` in constructor | Call `ws.subscribe()` after connect |
144
+ | Filtering | Client-side only | Server-side (where, columns, op) |
145
+ | Use case | Simple monitoring | Production apps needing efficiency |
146
+
147
+ Prefer subscriptions for new code. Broadcast is supported but sends all events for matched relations.
148
+
149
+ ## Node.js usage
150
+
151
+ Pass a WebSocket implementation for non-browser environments:
152
+
153
+ ```ts
154
+ import WebSocket from 'ws';
155
+ import { Ws } from '@centia-io/sdk';
156
+
157
+ const ws = new Ws({
158
+ host: 'wss://event.centia.io',
159
+ wsClient: WebSocket,
160
+ });
161
+ ```
162
+
163
+ ## Lifecycle
164
+
165
+ ```ts
166
+ ws.connect(); // start connection
167
+ ws.connected; // boolean — check state
168
+ ws.send('SELECT 1'); // send raw message
169
+ ws.disconnect(); // close and stop reconnecting
170
+ ```
171
+
172
+ ## Exported types
173
+
174
+ All types are available from `@centia-io/sdk`:
175
+
176
+ ```ts
177
+ import type {
178
+ WsOptions,
179
+ WsMessage,
180
+ BatchMessage,
181
+ TableBatch,
182
+ SubscriptionAckMessage,
183
+ WsErrorMessage,
184
+ SubscriptionRequest,
185
+ } from '@centia-io/sdk';
186
+ ```
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: centia-runtime-sdk
3
+ description: Runtime application guidance for Centia BaaS using @centia-io/sdk, including SDK client initialization, GraphQL/JSON-RPC/SQL method selection, HTTP fallback boundaries, and frontend tooling defaults.
4
+ ---
5
+
6
+ # Runtime SDK Rules
7
+
8
+ Use this skill for JS/TS runtime application work.
9
+
10
+ ## Hard rules
11
+
12
+ - Use `@centia-io/sdk` for runtime access.
13
+ - Do not use direct REST calls in runtime app code.
14
+ - If SDK lacks needed functionality, implement a documented fallback in `src/baas/http.ts`.
15
+
16
+ ## Query method selection
17
+
18
+ Choose in this order:
19
+
20
+ 1. GraphQL (`Gql`) for straightforward CRUD, filtering, pagination, nested relations.
21
+ 2. JSON-RPC (`createApi<T>()` or `Rpc`) for reusable complex SQL (joins, CTEs, aggregations).
22
+ 3. SQL (`Sql` and `createSqlBuilder`) as last resort for runtime-dynamic queries.
23
+
24
+ ## SDK bootstrap
25
+
26
+ Create shared clients in `src/baas/client.ts`.
27
+
28
+ Prefer exports:
29
+
30
+ - `Sql`
31
+ - `Rpc`
32
+ - `Gql`
33
+ - `createApi`
34
+ - `createSqlBuilder`
35
+ - `CodeFlow`, `PasswordFlow`, `SignUp` (by runtime context)
36
+
37
+ ## GraphQL notes
38
+
39
+ Auto-generated naming conventions by table:
40
+
41
+ - Select: `get[TableName]`
42
+ - Insert: `insert[TableName]`
43
+ - Update: `update[TableName]`
44
+ - Delete: `delete[TableName]`
45
+
46
+ Filtering operators:
47
+
48
+ - Comparison: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `like`, `ilike`
49
+ - Logical: `and`, `or`, `not`
50
+
51
+ ## SQL builder pattern
52
+
53
+ Prefer typed SQL builder for dynamic conditions:
54
+
55
+ ```ts
56
+ const b = createSqlBuilder(schema);
57
+ let req = b.table("logs").select(["id", "message"]);
58
+ if (level) req = req.andWhere({ level });
59
+ if (since) req = req.andWhere({ created_at: { gte: since } });
60
+ const rows = (await sql.exec(req.toSql())).data;
61
+ ```
62
+
63
+ ## HTTP fallback layer
64
+
65
+ File: `src/baas/http.ts`
66
+
67
+ Rules:
68
+
69
+ - Centralize headers.
70
+ - Use typed payloads.
71
+ - Prevent token leakage.
72
+ - Add source comments for each fallback endpoint.
73
+
74
+ Comment template:
75
+
76
+ ```ts
77
+ // Fallback to HTTP: not supported in @centia-io/sdk yet
78
+ // Source: https://centia.io/docs/...
79
+ ```
80
+
81
+ ## Web app defaults when scaffolding
82
+
83
+ Use these defaults only if project has no existing conventions:
84
+
85
+ - Package manager: `pnpm`
86
+ - Build tool: `Vite`
87
+ - Framework: React + TypeScript
88
+ - Styling: Tailwind CSS
89
+ - Routing: React Router
90
+
91
+ Do not mix package managers.
92
+
93
+ ## Code quality
94
+
95
+ - Prefer TypeScript strict mode.
96
+ - Centralize schema and table names.
97
+ - Avoid magic strings.
98
+ - Handle API errors explicitly.
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: centia-types-formats
3
+ description: PostgreSQL and PostGIS type guidance for Centia BaaS, including SQL parameter casts, type_hints, type_formats, and supported output formats for SQL and JSON-RPC responses.
4
+ ---
5
+
6
+ # Types and Formats
7
+
8
+ Use this skill for SQL typing, RPC type hints, and output format decisions.
9
+
10
+ ## SQL type system baseline
11
+
12
+ Use PostgreSQL types (including PostGIS-enabled environments).
13
+
14
+ Common groups:
15
+
16
+ - Numeric: `smallint`, `integer`, `bigint`, `numeric`, `real`, `double precision`
17
+ - Character: `varchar(n)`, `char(n)`, `text`
18
+ - Boolean: `boolean`
19
+ - JSON: `json`, `jsonb`
20
+ - Date/time: `date`, `time`, `timetz`, `timestamp`, `timestamptz`, `interval`
21
+ - Range: `int4range`, `int8range`, `numrange`, `daterange`, `tsrange`, `tstzrange`
22
+ - Arrays: append `[]` (for example `integer[]`, `text[]`)
23
+
24
+ ## SQL parameter syntax
25
+
26
+ Centia uses **PDO-style named parameters** (`:name`). Positional placeholders (`$1`, `$2`, …) are NOT supported and fail with `SQLSTATE[HY093]: Invalid parameter number`.
27
+
28
+ Pass a single-object `params` array where keys match the placeholder names:
29
+
30
+ ```json
31
+ {
32
+ "q": "SELECT * FROM schema.relation WHERE id = :id AND code = :code",
33
+ "params": [{ "id": 42, "code": "A1" }]
34
+ }
35
+ ```
36
+
37
+ Always cast parameters when types are ambiguous or for JSON-RPC type inference:
38
+
39
+ ```sql
40
+ SELECT :name::text, :age::integer, :joined::date
41
+ ```
42
+
43
+ ## type_hints
44
+
45
+ Use `type_hints` when server-side inference is ambiguous.
46
+
47
+ Example:
48
+
49
+ ```json
50
+ { "date": "timestamptz", "days": "integer" }
51
+ ```
52
+
53
+ ## type_formats
54
+
55
+ Use `type_formats` to format temporal output columns.
56
+
57
+ Example:
58
+
59
+ ```json
60
+ { "date": "D M d Y", "time": "H:i:s T" }
61
+ ```
62
+
63
+ ## Output formats (SQL and JSON-RPC)
64
+
65
+ Supported through `output_format`:
66
+
67
+ - Standard: `json`, `geojson`, `csv`, `excel`, `ogr/[format]`
68
+ - Streaming: `ndjson`, `ccsv`
69
+
70
+ Rules:
71
+
72
+ - Standard formats are single-response and capped at 100000 rows.
73
+ - Streaming formats have no row cap and are suitable for large exports/pipelines.
74
+ - Default format is `json` with `schema` and `data` fields.