@ductape/mcp 0.1.23 → 0.1.24

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.
Files changed (3) hide show
  1. package/dist/index.js +162 -0
  2. package/package.json +1 -1
  3. package/src/index.ts +162 -0
package/dist/index.js CHANGED
@@ -54,6 +54,67 @@ There are THREE categories of operations. Use the right tool for each:
54
54
  ductape_cli("db migrate rollback") # roll back last migration
55
55
  ductape_cli("db migrate rollback -n 3") # roll back last 3 migrations
56
56
 
57
+ DATABASE SCHEMA FILE — ductape/database/schema.json
58
+ ─────────────────────────────────────────────────────────────────────────
59
+ IMPORTANT: "db schema generate" READS schema.json and emits migration files.
60
+ It does NOT create schema.json from scratch. You must write this file first.
61
+
62
+ If schema.json does not exist, "db schema generate" now auto-creates it and
63
+ attempts to hydrate it from the cloud (pass --db <tag> to enable cloud hydration):
64
+ ductape_cli("db schema generate --db statecraft-core")
65
+ If the cloud database is empty, schema.json will be created as [] and you must
66
+ populate it before running the command again.
67
+
68
+ FILE FORMAT — ductape/database/schema.json is a JSON array:
69
+ [
70
+ {
71
+ "db": "<database-component-tag>",
72
+ "tables": {
73
+ "<collection_name>": {
74
+ "<field_name>": {
75
+ "type": "string|number|boolean|date|datetime|timestamp|uuid|object|array|json|text|decimal|bigint|smallint|double|binary|blob|time",
76
+ "required": true, // false by default (nullable)
77
+ "unique": true, // unique constraint on this field
78
+ "index": true, // create an index on this field (use with unique for unique index)
79
+ "default": "value", // default value ("now"/"NOW" → CURRENT_TIMESTAMP)
80
+ "maxlength": 255, // max length for string fields
81
+ "enum": ["a", "b", "c"], // restrict to enum values (sets type to string automatically)
82
+ "primaryKey": true, // mark as primary key
83
+ "autoGenerate": true, // auto-generate value on insert
84
+ "float": true // treat number as float (use with type: "number")
85
+ }
86
+ }
87
+ }
88
+ }
89
+ ]
90
+
91
+ SINGLE-FIELD INDEX: set index: true (+ unique: true for unique constraint)
92
+ "matchId": { "type": "string", "required": true, "unique": true, "index": true }
93
+
94
+ COMPOSITE INDEX: schema.json does not support composite indexes.
95
+ After running db schema generate, add a manual migration file in
96
+ ductape/migrations/<db-tag>/ with type "createIndex" and a fields array:
97
+ {
98
+ "tag": "create_map_events_match_seq_idx",
99
+ "name": "Composite unique index on matchId+sequence",
100
+ "up": [{
101
+ "type": "createIndex",
102
+ "collection": "map_events",
103
+ "name": "map_events_matchId_sequence_idx",
104
+ "fields": [{ "name": "matchId" }, { "name": "sequence" }],
105
+ "unique": true,
106
+ "ifNotExists": true
107
+ }],
108
+ "down": [{ "type": "dropIndex", "collection": "map_events", "name": "map_events_matchId_sequence_idx", "ifExists": true }],
109
+ "createdAt": "<ISO timestamp>"
110
+ }
111
+
112
+ WORKFLOW:
113
+ 1. Create/edit ductape/database/schema.json with your collection definitions
114
+ 2. ductape_cli("db schema generate --db <tag>") → writes migration files
115
+ 3. ductape_cli("db migrate --db <tag>") → applies pending migrations
116
+ 4. Add manual migration files for composite/partial indexes not expressible in schema.json
117
+
57
118
  1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
58
119
  ALL creation and update operations require an access key and CANNOT go through ductape_execute.
59
120
  → Use ductape_cli for every create/update operation. Examples:
@@ -111,6 +172,107 @@ There are THREE categories of operations. Use the right tool for each:
111
172
 
112
173
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
113
174
 
175
+ ━━━ NESTJS INTEGRATION — use @ductape/nestjs ━━━
176
+
177
+ When the target application is a NestJS service or controller, always use @ductape/nestjs
178
+ instead of instantiating @ductape/sdk directly. It provides NestJS DI integration,
179
+ global interceptors, decorators, and type-safe resource handles.
180
+
181
+ SETUP — register once in AppModule:
182
+
183
+ import { DuctapeModule } from '@ductape/nestjs';
184
+
185
+ @Module({
186
+ imports: [
187
+ DuctapeModule.forIntegration({
188
+ accessKey: process.env.DUCTAPE_ACCESS_KEY,
189
+ product: 'my-product', // optional default — overridable per controller
190
+ env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
191
+ }),
192
+ ],
193
+ })
194
+ export class AppModule {}
195
+
196
+ // Async (e.g. pulling key from ConfigService):
197
+ DuctapeModule.forRootAsync({
198
+ imports: [ConfigModule],
199
+ inject: [ConfigService],
200
+ useFactory: (cfg: ConfigService) => ({
201
+ accessKey: cfg.get('DUCTAPE_ACCESS_KEY'),
202
+ product: cfg.get('DUCTAPE_PRODUCT'),
203
+ env: cfg.get('DUCTAPE_ENV'),
204
+ }),
205
+ })
206
+
207
+ INJECTING IN SERVICES AND CONTROLLERS — use @InjectContext():
208
+
209
+ import { InjectContext, DuctapeContext } from '@ductape/nestjs';
210
+
211
+ @Injectable()
212
+ export class MatchService {
213
+ constructor(@InjectContext() private readonly ductape: DuctapeContext) {}
214
+
215
+ async publishMatchState(matchId: string, state: object) {
216
+ await this.ductape.sdk.events.produce({
217
+ product: 'my-product',
218
+ env: 'prd',
219
+ event: 'statecraft-events:match-state',
220
+ message: { matchId, ...state },
221
+ });
222
+ }
223
+
224
+ async queryDatabase(env: string) {
225
+ const db = await this.ductape.database('core-db', { env });
226
+ return db.query({ table: 'matches', where: { active: true } });
227
+ }
228
+ }
229
+
230
+ AVAILABLE ON DuctapeContext:
231
+
232
+ ctx.sdk — full @ductape/sdk instance (events, sessions, etc.)
233
+ ctx.database(tag, overrides?) — connected DatabaseHandle (query, insert, update, delete)
234
+ ctx.storage(tag, overrides?) — StorageHandle (upload, download, remove)
235
+ ctx.cache(tag, overrides?) — CacheHandle (get, set, delete)
236
+ ctx.graph(tag, overrides?) — GraphHandle
237
+ ctx.vector(tag, overrides?) — VectorHandle
238
+ ctx.agent(tag, overrides?) — AgentHandle
239
+ ctx.warehouse(overrides?) — WarehouseHandle
240
+ ctx.cloudTiers(query?) — query available cloud resource tiers and pricing
241
+ ctx.runJob({ product, env, event, input? }) — trigger a product job
242
+
243
+ CONTROLLER DECORATORS:
244
+
245
+ @Product('my-product') — override default product at class or method level
246
+ @Env('prd') — override default env
247
+ @ApiRun({ app, action }) — bind a controller method to an app action (interceptor runs it automatically)
248
+ @ApiConfig({ app, ... }) — shared app credentials at class level
249
+ @Webhook.Register(...) — register a webhook consumer URL
250
+ @Webhook.Consumer(...) — mark inbound handler for forwarded webhook payloads
251
+
252
+ FOR MESSAGING (events.produce / events.consume):
253
+
254
+ Access via ctx.sdk.events — no dedicated NestJS handle, the SDK instance is sufficient:
255
+
256
+ await this.ductape.sdk.events.produce({
257
+ product: 'my-product', env: 'prd',
258
+ event: 'broker-tag:topic-tag',
259
+ message: { ... },
260
+ });
261
+
262
+ await this.ductape.sdk.events.consume({
263
+ product: 'my-product', env: 'prd',
264
+ event: 'broker-tag:topic-tag',
265
+ callback: async (message) => { /* handle */ },
266
+ });
267
+
268
+ SPECIALIZED MODULES (for injecting handles directly without @InjectContext):
269
+
270
+ DuctapeDatabaseModule.forTags(['core-db']) → inject with @Database('core-db')
271
+ DuctapeStorageModule.forTags(['assets']) → inject with @Storage('assets')
272
+ DuctapeCacheModule, DuctapeGraphModule, DuctapeVectorModule — same pattern
273
+
274
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
275
+
114
276
  ALL params are passed as a JSON array in positional order matching the SDK signature.
115
277
 
116
278
  ━━━ MODULE: product ━━━
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -65,6 +65,67 @@ There are THREE categories of operations. Use the right tool for each:
65
65
  ductape_cli("db migrate rollback") # roll back last migration
66
66
  ductape_cli("db migrate rollback -n 3") # roll back last 3 migrations
67
67
 
68
+ DATABASE SCHEMA FILE — ductape/database/schema.json
69
+ ─────────────────────────────────────────────────────────────────────────
70
+ IMPORTANT: "db schema generate" READS schema.json and emits migration files.
71
+ It does NOT create schema.json from scratch. You must write this file first.
72
+
73
+ If schema.json does not exist, "db schema generate" now auto-creates it and
74
+ attempts to hydrate it from the cloud (pass --db <tag> to enable cloud hydration):
75
+ ductape_cli("db schema generate --db statecraft-core")
76
+ If the cloud database is empty, schema.json will be created as [] and you must
77
+ populate it before running the command again.
78
+
79
+ FILE FORMAT — ductape/database/schema.json is a JSON array:
80
+ [
81
+ {
82
+ "db": "<database-component-tag>",
83
+ "tables": {
84
+ "<collection_name>": {
85
+ "<field_name>": {
86
+ "type": "string|number|boolean|date|datetime|timestamp|uuid|object|array|json|text|decimal|bigint|smallint|double|binary|blob|time",
87
+ "required": true, // false by default (nullable)
88
+ "unique": true, // unique constraint on this field
89
+ "index": true, // create an index on this field (use with unique for unique index)
90
+ "default": "value", // default value ("now"/"NOW" → CURRENT_TIMESTAMP)
91
+ "maxlength": 255, // max length for string fields
92
+ "enum": ["a", "b", "c"], // restrict to enum values (sets type to string automatically)
93
+ "primaryKey": true, // mark as primary key
94
+ "autoGenerate": true, // auto-generate value on insert
95
+ "float": true // treat number as float (use with type: "number")
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ]
101
+
102
+ SINGLE-FIELD INDEX: set index: true (+ unique: true for unique constraint)
103
+ "matchId": { "type": "string", "required": true, "unique": true, "index": true }
104
+
105
+ COMPOSITE INDEX: schema.json does not support composite indexes.
106
+ After running db schema generate, add a manual migration file in
107
+ ductape/migrations/<db-tag>/ with type "createIndex" and a fields array:
108
+ {
109
+ "tag": "create_map_events_match_seq_idx",
110
+ "name": "Composite unique index on matchId+sequence",
111
+ "up": [{
112
+ "type": "createIndex",
113
+ "collection": "map_events",
114
+ "name": "map_events_matchId_sequence_idx",
115
+ "fields": [{ "name": "matchId" }, { "name": "sequence" }],
116
+ "unique": true,
117
+ "ifNotExists": true
118
+ }],
119
+ "down": [{ "type": "dropIndex", "collection": "map_events", "name": "map_events_matchId_sequence_idx", "ifExists": true }],
120
+ "createdAt": "<ISO timestamp>"
121
+ }
122
+
123
+ WORKFLOW:
124
+ 1. Create/edit ductape/database/schema.json with your collection definitions
125
+ 2. ductape_cli("db schema generate --db <tag>") → writes migration files
126
+ 3. ductape_cli("db migrate --db <tag>") → applies pending migrations
127
+ 4. Add manual migration files for composite/partial indexes not expressible in schema.json
128
+
68
129
  1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
69
130
  ALL creation and update operations require an access key and CANNOT go through ductape_execute.
70
131
  → Use ductape_cli for every create/update operation. Examples:
@@ -122,6 +183,107 @@ There are THREE categories of operations. Use the right tool for each:
122
183
 
123
184
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
124
185
 
186
+ ━━━ NESTJS INTEGRATION — use @ductape/nestjs ━━━
187
+
188
+ When the target application is a NestJS service or controller, always use @ductape/nestjs
189
+ instead of instantiating @ductape/sdk directly. It provides NestJS DI integration,
190
+ global interceptors, decorators, and type-safe resource handles.
191
+
192
+ SETUP — register once in AppModule:
193
+
194
+ import { DuctapeModule } from '@ductape/nestjs';
195
+
196
+ @Module({
197
+ imports: [
198
+ DuctapeModule.forIntegration({
199
+ accessKey: process.env.DUCTAPE_ACCESS_KEY,
200
+ product: 'my-product', // optional default — overridable per controller
201
+ env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
202
+ }),
203
+ ],
204
+ })
205
+ export class AppModule {}
206
+
207
+ // Async (e.g. pulling key from ConfigService):
208
+ DuctapeModule.forRootAsync({
209
+ imports: [ConfigModule],
210
+ inject: [ConfigService],
211
+ useFactory: (cfg: ConfigService) => ({
212
+ accessKey: cfg.get('DUCTAPE_ACCESS_KEY'),
213
+ product: cfg.get('DUCTAPE_PRODUCT'),
214
+ env: cfg.get('DUCTAPE_ENV'),
215
+ }),
216
+ })
217
+
218
+ INJECTING IN SERVICES AND CONTROLLERS — use @InjectContext():
219
+
220
+ import { InjectContext, DuctapeContext } from '@ductape/nestjs';
221
+
222
+ @Injectable()
223
+ export class MatchService {
224
+ constructor(@InjectContext() private readonly ductape: DuctapeContext) {}
225
+
226
+ async publishMatchState(matchId: string, state: object) {
227
+ await this.ductape.sdk.events.produce({
228
+ product: 'my-product',
229
+ env: 'prd',
230
+ event: 'statecraft-events:match-state',
231
+ message: { matchId, ...state },
232
+ });
233
+ }
234
+
235
+ async queryDatabase(env: string) {
236
+ const db = await this.ductape.database('core-db', { env });
237
+ return db.query({ table: 'matches', where: { active: true } });
238
+ }
239
+ }
240
+
241
+ AVAILABLE ON DuctapeContext:
242
+
243
+ ctx.sdk — full @ductape/sdk instance (events, sessions, etc.)
244
+ ctx.database(tag, overrides?) — connected DatabaseHandle (query, insert, update, delete)
245
+ ctx.storage(tag, overrides?) — StorageHandle (upload, download, remove)
246
+ ctx.cache(tag, overrides?) — CacheHandle (get, set, delete)
247
+ ctx.graph(tag, overrides?) — GraphHandle
248
+ ctx.vector(tag, overrides?) — VectorHandle
249
+ ctx.agent(tag, overrides?) — AgentHandle
250
+ ctx.warehouse(overrides?) — WarehouseHandle
251
+ ctx.cloudTiers(query?) — query available cloud resource tiers and pricing
252
+ ctx.runJob({ product, env, event, input? }) — trigger a product job
253
+
254
+ CONTROLLER DECORATORS:
255
+
256
+ @Product('my-product') — override default product at class or method level
257
+ @Env('prd') — override default env
258
+ @ApiRun({ app, action }) — bind a controller method to an app action (interceptor runs it automatically)
259
+ @ApiConfig({ app, ... }) — shared app credentials at class level
260
+ @Webhook.Register(...) — register a webhook consumer URL
261
+ @Webhook.Consumer(...) — mark inbound handler for forwarded webhook payloads
262
+
263
+ FOR MESSAGING (events.produce / events.consume):
264
+
265
+ Access via ctx.sdk.events — no dedicated NestJS handle, the SDK instance is sufficient:
266
+
267
+ await this.ductape.sdk.events.produce({
268
+ product: 'my-product', env: 'prd',
269
+ event: 'broker-tag:topic-tag',
270
+ message: { ... },
271
+ });
272
+
273
+ await this.ductape.sdk.events.consume({
274
+ product: 'my-product', env: 'prd',
275
+ event: 'broker-tag:topic-tag',
276
+ callback: async (message) => { /* handle */ },
277
+ });
278
+
279
+ SPECIALIZED MODULES (for injecting handles directly without @InjectContext):
280
+
281
+ DuctapeDatabaseModule.forTags(['core-db']) → inject with @Database('core-db')
282
+ DuctapeStorageModule.forTags(['assets']) → inject with @Storage('assets')
283
+ DuctapeCacheModule, DuctapeGraphModule, DuctapeVectorModule — same pattern
284
+
285
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
286
+
125
287
  ALL params are passed as a JSON array in positional order matching the SDK signature.
126
288
 
127
289
  ━━━ MODULE: product ━━━