@ductape/mcp 0.2.14 → 0.2.15
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/CHANGELOG.md +1 -0
- package/dist/index.js +132 -16
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
- Added source-verified graph/vector projection guidance to the `features`, `events`, `graphs`, and `vector` docs topics, including exact graph/vector payloads, NestJS consumer routing, replay/idempotency, projection-state ordering, rebuild/drift repair, and an explicit stop condition for the currently missing code-first `ctx.vector` API.
|
|
9
10
|
- Added read-only `ductape_events_topic_setup` and `ductape_events_validate_project` tools and made the canonical one-topic-per-file `ductape/events/<topic-tag>.topic.json` layout explicit and enforceable through the CLI.
|
|
10
11
|
- Mark `ductape_function_setup` as read-only, non-destructive, idempotent, and closed-world in both MCP registration APIs so hosts do not incorrectly require mutation approval for its pure setup-plan generation.
|
|
11
12
|
- Broadened Feature guidance from durable/event-driven workflows to synchronous or asynchronous named product capabilities.
|
package/dist/index.js
CHANGED
|
@@ -2396,8 +2396,9 @@ Schema management:
|
|
|
2396
2396
|
graph.dropIndex / dropConstraint / listIndexes / listConstraints
|
|
2397
2397
|
|
|
2398
2398
|
Saved actions (parameterized queries stored on the product):
|
|
2399
|
-
graph.createAction [{ graphTag,
|
|
2399
|
+
graph.createAction [{ graphTag?, name, description?, operation, query, parameters: [{ name, type, required?, defaultValue?, description? }] }, productTag?]
|
|
2400
2400
|
graph.listActions [graphTag?, productTag?]
|
|
2401
|
+
graph.execute [{ product, env, graph, action, input?, session?, cache? }]
|
|
2401
2402
|
graph.dispatch [data] ← call ductape_generate_payload FIRST
|
|
2402
2403
|
|
|
2403
2404
|
Supported index types: btree | fulltext | vector | range | point | text
|
|
@@ -2512,7 +2513,7 @@ Registration (admin — ductape_cli):
|
|
|
2512
2513
|
Runtime operations:
|
|
2513
2514
|
vector.upsert [{ product, env, tag, vectors: [{id, values: number[], metadata?}], namespace? }]
|
|
2514
2515
|
vector.upsertOne [{ product, env, tag, id, values: number[], metadata?, namespace? }]
|
|
2515
|
-
vector.query [{ product, env, tag, vector: number[], topK
|
|
2516
|
+
vector.query [{ product, env, tag, vector: number[], topK: number, filter?, namespace?,
|
|
2516
2517
|
includeValues?, includeMetadata? }]
|
|
2517
2518
|
vector.findSimilar [{ product, env, vector, values: number[], topK?, filter?, namespace? }]
|
|
2518
2519
|
vector.fetchOne [{ product, env, vector, id, namespace? }]
|
|
@@ -3615,14 +3616,15 @@ STEP 8 — SET rollbacks for reversible steps
|
|
|
3615
3616
|
async (result) => ctx.api.run({ app: 'stripe', event: 'refund', input: { chargeId: result.id } })
|
|
3616
3617
|
);
|
|
3617
3618
|
|
|
3618
|
-
|
|
3619
|
-
|
|
3619
|
+
Code-first ctx step types currently record: function | action | database | graph | vector |
|
|
3620
|
+
notification | storage | produce | quota | fallback | child_feature | sleep | wait_for_signal |
|
|
3621
|
+
checkpoint.
|
|
3620
3622
|
|
|
3621
3623
|
Valid synchronous Feature candidates include generate-world, resolve-nation-turn,
|
|
3622
3624
|
calculate-route-capacity, price-subscription, evaluate-entitlement, and build-replay.
|
|
3623
3625
|
|
|
3624
3626
|
Synchronous multi-step Feature (no Event, schedule, sleep, signal, or external system):
|
|
3625
|
-
await ductape.
|
|
3627
|
+
await ductape.feature.define({
|
|
3626
3628
|
product: 'example-product',
|
|
3627
3629
|
tag: 'resolve-nation-turn',
|
|
3628
3630
|
name: 'Resolve Nation Turn',
|
|
@@ -3635,15 +3637,10 @@ Synchronous multi-step Feature (no Event, schedule, sleep, signal, or external s
|
|
|
3635
3637
|
rejectedOrders: { type: 'number' },
|
|
3636
3638
|
},
|
|
3637
3639
|
handler: async (ctx) => {
|
|
3638
|
-
const validated = await ctx.step('validate-orders',
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
return resolveOrders(validated);
|
|
3643
|
-
});
|
|
3644
|
-
return ctx.step('build-result', async () => {
|
|
3645
|
-
return buildResult(resolved);
|
|
3646
|
-
});
|
|
3640
|
+
const validated = await ctx.step('validate-orders', () =>
|
|
3641
|
+
ctx.database.execute({ database: 'orders-db', event: 'validate-orders', input: ctx.input }));
|
|
3642
|
+
return ctx.step('resolve-orders', () =>
|
|
3643
|
+
ctx.database.execute({ database: 'orders-db', event: 'resolve-orders', input: validated }));
|
|
3647
3644
|
},
|
|
3648
3645
|
});
|
|
3649
3646
|
This is a valid Feature despite requiring no signal and producing no Event. Its qualification comes
|
|
@@ -3652,7 +3649,7 @@ step-level execution history.
|
|
|
3652
3649
|
|
|
3653
3650
|
Define a feature (write this into the project's source files — do NOT use features.create):
|
|
3654
3651
|
// src/features/onboard-user.ts (or the equivalent path/language for the project)
|
|
3655
|
-
await ductape.
|
|
3652
|
+
await ductape.feature.define({
|
|
3656
3653
|
tag: "onboard-user",
|
|
3657
3654
|
name: "Onboard User",
|
|
3658
3655
|
handler: async (ctx) => {
|
|
@@ -4699,6 +4696,122 @@ DEPRECATED ALIASES
|
|
|
4699
4696
|
useWorkflowCancel → useFeatureCancel
|
|
4700
4697
|
`.trim(),
|
|
4701
4698
|
};
|
|
4699
|
+
const GRAPH_VECTOR_PROJECTION_GUIDANCE = `
|
|
4700
|
+
|
|
4701
|
+
━━━ EVENT → GRAPH/VECTOR PROJECTION CONTRACT (CURRENT TYPESCRIPT/NESTJS SDK) ━━━
|
|
4702
|
+
|
|
4703
|
+
SOURCE-VERIFIED CODE-FIRST SUPPORT:
|
|
4704
|
+
IFeatureContext and RecordingContext expose both ctx.graph and ctx.vector. Their calls compile to
|
|
4705
|
+
portable JSON steps and the FeatureExecutor runs those steps against the configured local graph or
|
|
4706
|
+
vector service. Keep every primitive call inside ctx.step().
|
|
4707
|
+
|
|
4708
|
+
The TypeScript SDK facade is singular: ductape.feature.define/execute/dispatch. The backend proxy
|
|
4709
|
+
module is named "features", but application code must not infer a ductape.features property.
|
|
4710
|
+
|
|
4711
|
+
EXACT CODE-FIRST GRAPH CONTEXT:
|
|
4712
|
+
ctx.graph.createNode<T>({ graph, labels: string[], properties })
|
|
4713
|
+
ctx.graph.updateNode<T>({ graph, id: string|number, properties })
|
|
4714
|
+
ctx.graph.deleteNode({ graph, id })
|
|
4715
|
+
ctx.graph.createRelationship<T>({ graph, from, to, type, properties? })
|
|
4716
|
+
ctx.graph.deleteRelationship({ graph, id })
|
|
4717
|
+
ctx.graph.query<T>({ graph, action, params? })
|
|
4718
|
+
ctx.graph.execute<T>({ graph, action, input })
|
|
4719
|
+
Every call must be inside ctx.step(). createNode/createRelationship are not projection upserts.
|
|
4720
|
+
For replay-safe Neo4j projections, register a saved graph action whose Cypher uses MERGE and call
|
|
4721
|
+
that action through ctx.graph.execute. Do not describe CREATE as idempotent.
|
|
4722
|
+
|
|
4723
|
+
EXACT CODE-FIRST VECTOR CONTEXT:
|
|
4724
|
+
ctx.vector.upsert<T>({
|
|
4725
|
+
vector: "product-vectors",
|
|
4726
|
+
vectors: [{ id, values: number[], metadata? }], namespace?, wait?
|
|
4727
|
+
});
|
|
4728
|
+
ctx.vector.upsertOne<T>({
|
|
4729
|
+
vector: "product-vectors", id, values: number[], metadata?, namespace?
|
|
4730
|
+
});
|
|
4731
|
+
ctx.vector.query<T>({
|
|
4732
|
+
vector: "product-vectors", values: number[], topK: number,
|
|
4733
|
+
namespace?, filter?, includeValues?, includeMetadata?, minScore?
|
|
4734
|
+
});
|
|
4735
|
+
ctx.vector.deleteVectors<T>({ vector: "product-vectors", ids?, namespace?, deleteAll?, filter? });
|
|
4736
|
+
ctx.vector.execute<T>({ vector: "product-vectors", action: "saved-action", input });
|
|
4737
|
+
Product/env and the Feature's inherited session are supplied by the executor. Do not serialize
|
|
4738
|
+
them into the Feature definition. Note the deliberate naming: ctx.vector.query uses values for
|
|
4739
|
+
the embedding, while the lower-level ductape.vector.query runtime facade uses vector.
|
|
4740
|
+
Vertex AI Vector Search stores and searches supplied vectors. It does NOT generate embeddings.
|
|
4741
|
+
Generate embeddings through a separately registered/called model or App/function contract and
|
|
4742
|
+
verify that the returned number[] dimension exactly matches the vector component. Do not claim
|
|
4743
|
+
that a Vertex Vector Search index embeds text.
|
|
4744
|
+
|
|
4745
|
+
NESTJS CONSUMER ROUTING AND FEATURE INPUT:
|
|
4746
|
+
@Events.Consumer receives the published message object. Ductape may add
|
|
4747
|
+
__ductape_message_id; there is no automatic domain-event envelope and no automatic session token.
|
|
4748
|
+
DuctapeModule subscribes at startup using product/env defaults unless decorator overrides are set.
|
|
4749
|
+
The callback is acknowledged after it returns successfully; throwing propagates failure to the
|
|
4750
|
+
provider's retry/nack path. Do not catch and suppress a failed projection.
|
|
4751
|
+
|
|
4752
|
+
import { Injectable } from '@nestjs/common';
|
|
4753
|
+
import { DuctapeContextService, Events } from '@ductape/nestjs';
|
|
4754
|
+
|
|
4755
|
+
type ProductProjectionEvent = {
|
|
4756
|
+
eventId: string; // stable across retries and replay
|
|
4757
|
+
productId: string;
|
|
4758
|
+
version: number; // monotonic aggregate/source version
|
|
4759
|
+
name: string;
|
|
4760
|
+
embedding: number[]; // only if produced upstream; Vector Search does not generate it
|
|
4761
|
+
__ductape_message_id?: string;
|
|
4762
|
+
};
|
|
4763
|
+
|
|
4764
|
+
@Injectable()
|
|
4765
|
+
export class ProductProjectionConsumer {
|
|
4766
|
+
constructor(private readonly ductape: DuctapeContextService) {}
|
|
4767
|
+
|
|
4768
|
+
@Events.Consumer({ event: 'catalog-events:product-projection-requested' })
|
|
4769
|
+
async project(message: ProductProjectionEvent): Promise<void> {
|
|
4770
|
+
const result = await this.ductape.sdk.feature.execute({
|
|
4771
|
+
product: 'buydeck',
|
|
4772
|
+
env: 'snd',
|
|
4773
|
+
tag: 'project-product-graph',
|
|
4774
|
+
input: message,
|
|
4775
|
+
idempotency_key: 'product-projection:' + message.eventId,
|
|
4776
|
+
});
|
|
4777
|
+
if (result.status !== 'completed') {
|
|
4778
|
+
throw new Error(result.error ?? 'Product graph projection did not complete');
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4783
|
+
The consumer passes its message explicitly as Feature input. Use a stable domain eventId for the
|
|
4784
|
+
Feature idempotency key; __ductape_message_id is transport tracking metadata and must not replace a
|
|
4785
|
+
domain idempotency key. Broker replay/redelivery means consumers and every projection write must
|
|
4786
|
+
remain independently idempotent.
|
|
4787
|
+
|
|
4788
|
+
ORDERING, PROJECTION STATE, AND FAILURE RECOVERY:
|
|
4789
|
+
There is no atomic transaction spanning MongoDB, Neo4j, and Vertex Vector Search. Never claim that
|
|
4790
|
+
ctx.step ordering creates a distributed transaction. Treat MongoDB as the canonical source and
|
|
4791
|
+
graph/vector stores as rebuildable projections.
|
|
4792
|
+
|
|
4793
|
+
Recommended state machine, keyed by projection name + aggregate ID + source version/event ID:
|
|
4794
|
+
1. Atomically claim/read projection_state in MongoDB; completed means return success.
|
|
4795
|
+
2. Apply an idempotent Neo4j MERGE using stable domain IDs and sourceVersion.
|
|
4796
|
+
3. Upsert the vector using the same stable ID and sourceVersion metadata.
|
|
4797
|
+
4. Mark projection_state completed LAST, with graph/vector versions, checksum, and timestamps.
|
|
4798
|
+
On failure, retain pending/failed state and throw so the broker retries. A retry repeats graph/vector
|
|
4799
|
+
upserts safely and only then marks completion. Do not mark Mongo completion before derived writes.
|
|
4800
|
+
If strict atomic visibility across all three stores is required, the platform does not provide it.
|
|
4801
|
+
|
|
4802
|
+
REBUILD AND DRIFT REPAIR:
|
|
4803
|
+
Rebuild from canonical MongoDB records, not from broker retention or processor logs. Use a named,
|
|
4804
|
+
versioned rebuild job/Feature that pages deterministically, performs the same idempotent graph/vector
|
|
4805
|
+
upserts, checkpoints the last canonical cursor, and records projection version/checksum. Drift repair
|
|
4806
|
+
compares canonical IDs/sourceVersion/checksum with both projections, repairs missing/stale entries,
|
|
4807
|
+
and removes extras only after a complete successful scan. Keep live consumer and rebuild writes
|
|
4808
|
+
monotonic so an older rebuild item cannot overwrite a newer projection.
|
|
4809
|
+
|
|
4810
|
+
IMPLEMENTATION GATE:
|
|
4811
|
+
Combined database + graph + vector code-first projection Features are supported. Still stop when
|
|
4812
|
+
an embedding source, vector dimension, saved graph/vector action, or idempotency strategy is not
|
|
4813
|
+
specified; do not invent those application-level contracts.
|
|
4814
|
+
`.trim();
|
|
4702
4815
|
const docsHandler = async (args) => {
|
|
4703
4816
|
const requested = args.topic.toLowerCase().trim();
|
|
4704
4817
|
const recoveryAliases = /^(dlq|dead[- ]?letters?|failed messages?|retry|retries|poison messages?|replay|reprocess|consumer failures?)$/;
|
|
@@ -4719,7 +4832,10 @@ const docsHandler = async (args) => {
|
|
|
4719
4832
|
canonicalDocumentationTopic: 'events',
|
|
4720
4833
|
}, null, 2)}`
|
|
4721
4834
|
: '';
|
|
4722
|
-
|
|
4835
|
+
const projectionGuidance = ['features', 'events', 'graphs', 'vector'].includes(key)
|
|
4836
|
+
? `\n\n${GRAPH_VECTOR_PROJECTION_GUIDANCE}`
|
|
4837
|
+
: '';
|
|
4838
|
+
return { content: [{ type: 'text', text: doc + structured + projectionGuidance }] };
|
|
4723
4839
|
};
|
|
4724
4840
|
const portableFunctionSetupHandler = async (args) => {
|
|
4725
4841
|
const parsed = new URL(args.base_url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ductape/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "MCP server that exposes Ductape SDK operations via the backend proxy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc",
|
|
18
|
-
"test": "npm run build && node scripts/check-cli-command-security.mjs && node scripts/check-frontend-analytics-guidance.mjs && node scripts/check-events-discovery.mjs && node scripts/check-schema-fallback.mjs && node scripts/check-portable-functions.mjs && node scripts/check-project-link-guidance.mjs",
|
|
18
|
+
"test": "npm run build && node scripts/check-cli-command-security.mjs && node scripts/check-frontend-analytics-guidance.mjs && node scripts/check-events-discovery.mjs && node scripts/check-schema-fallback.mjs && node scripts/check-portable-functions.mjs && node scripts/check-project-link-guidance.mjs && node scripts/check-graph-vector-projection-guidance.mjs",
|
|
19
19
|
"start": "node dist/index.js",
|
|
20
20
|
"dev": "tsx src/index.ts"
|
|
21
21
|
},
|