@ductape/mcp 0.2.12 → 0.2.14
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 +2 -0
- package/README.md +6 -1
- package/dist/index.js +115 -9
- package/docs/TOOLS.md +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
- 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
|
+
- 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.
|
|
9
11
|
- Broadened Feature guidance from durable/event-driven workflows to synchronous or asynchronous named product capabilities.
|
|
10
12
|
- Added evidence-backed `FEATURE`, `FEATURE_STEP`, `DOMAIN_SERVICE`, `UTILITY`, and `INFRASTRUCTURE_ADAPTER` classification guidance.
|
|
11
13
|
- Added synchronous multi-step examples and repository discovery/grouping rules.
|
package/README.md
CHANGED
|
@@ -82,7 +82,12 @@ The server exposes runtime, schema, documentation, CLI, discovery, migration, an
|
|
|
82
82
|
- ready-to-copy SDK snippet in `typescript` or `python`
|
|
83
83
|
- Intended for engineers and copilots that need executable examples quickly.
|
|
84
84
|
|
|
85
|
-
4. **`
|
|
85
|
+
4. **`ductape_events_topic_setup` / `ductape_events_validate_project`**:
|
|
86
|
+
- Generate one canonical `ductape/events/<topic-tag>.topic.json` asset shape without writing it.
|
|
87
|
+
- Validate filenames, one-object-per-file schemas, unique qualified tags, supported fields, and publisher references before provisioning.
|
|
88
|
+
- Aggregate topic catalogues, manifests, envelope registries, and custom event registries are rejected.
|
|
89
|
+
|
|
90
|
+
5. **`ductape_function_setup`**:
|
|
86
91
|
- Produces the secure local and remote setup for application functions referenced by portable Features.
|
|
87
92
|
- Requires an externally reachable HTTPS base URL (HTTP only for localhost development).
|
|
88
93
|
- Returns deterministic well-known routes, framework raw-body requirements, HMAC-SHA256 headers,
|
package/dist/index.js
CHANGED
|
@@ -630,8 +630,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
630
630
|
events.list [product_tag]
|
|
631
631
|
events.delete [product_tag, broker_tag]
|
|
632
632
|
events.topics.create ← FORBIDDEN with publishable key. Use ductape_cli instead:
|
|
633
|
-
ductape_cli("events topics create -f topic.json")
|
|
634
|
-
topic
|
|
633
|
+
ductape_cli("events topics create -f ductape/events/<topic-tag>.topic.json")
|
|
634
|
+
One directly importable topic per file: { tag: "broker-tag:topic-tag", name, description?, sample?, idempotent?, queueUrls?: [{ env_slug, url }] }
|
|
635
635
|
← Always required before consuming. For SQS: must include queueUrls per env.
|
|
636
636
|
← For Pub/Sub, Kafka, RabbitMQ, Redis, NATS: the first produce call auto-registers the topic,
|
|
637
637
|
but you should still create it explicitly so consumers can subscribe before any produce occurs.
|
|
@@ -1468,6 +1468,33 @@ const portableFunctionSetupInputSchema = z.object({
|
|
|
1468
1468
|
version: z.string().min(1).describe('Contract version.'),
|
|
1469
1469
|
operations: z.array(z.string().min(1)).min(1).describe('Operation names that must be registered and exposed.'),
|
|
1470
1470
|
});
|
|
1471
|
+
// This tool only derives setup instructions from its arguments. It performs no network request,
|
|
1472
|
+
// filesystem write, process launch, registration, deployment, or infrastructure provisioning.
|
|
1473
|
+
// Declare that explicitly so MCP hosts do not send it through mutation approval workflows.
|
|
1474
|
+
const portableFunctionSetupAnnotations = {
|
|
1475
|
+
readOnlyHint: true,
|
|
1476
|
+
destructiveHint: false,
|
|
1477
|
+
idempotentHint: true,
|
|
1478
|
+
openWorldHint: false,
|
|
1479
|
+
};
|
|
1480
|
+
const eventsTopicSetupInputSchema = z.object({
|
|
1481
|
+
broker_tag: z.string().regex(/^[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*$/).describe('Existing Ductape Events broker tag.'),
|
|
1482
|
+
topic_tag: z.string().regex(/^[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)*$/).describe('Unqualified topic tag used as the canonical filename.'),
|
|
1483
|
+
name: z.string().min(1),
|
|
1484
|
+
description: z.string().optional(),
|
|
1485
|
+
sample: z.record(z.unknown()).optional(),
|
|
1486
|
+
idempotent: z.boolean().optional(),
|
|
1487
|
+
queueUrls: z.array(z.object({ env_slug: z.string().min(1), url: z.string().url() }).strict()).optional(),
|
|
1488
|
+
});
|
|
1489
|
+
const eventsProjectValidationInputSchema = z.object({
|
|
1490
|
+
dir: z.string().default('ductape/events').describe('Must be ductape/events relative to DUCTAPE_PROJECT_DIR.'),
|
|
1491
|
+
});
|
|
1492
|
+
const readOnlyLocalAnnotations = {
|
|
1493
|
+
readOnlyHint: true,
|
|
1494
|
+
destructiveHint: false,
|
|
1495
|
+
idempotentHint: true,
|
|
1496
|
+
openWorldHint: false,
|
|
1497
|
+
};
|
|
1471
1498
|
const migrationInputSchema = z.object({
|
|
1472
1499
|
source: z.string().describe('Absolute path to the existing codebase.'),
|
|
1473
1500
|
e2e_baseline: z.string().describe('Absolute path to a passing migration-e2e baseline manifest created before migration inspection.'),
|
|
@@ -3854,10 +3881,28 @@ Import (register an EXISTING cloud resource):
|
|
|
3854
3881
|
IMPORTANT: events.topics.create requires an access key (admin operation).
|
|
3855
3882
|
Use ductape_cli — NOT ductape_execute — to create topics.
|
|
3856
3883
|
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3884
|
+
CANONICAL ASSET CONTRACT — HARD REQUIREMENT:
|
|
3885
|
+
Ductape topic assets are individual, directly importable files at
|
|
3886
|
+
<project-root>/ductape/events/<topic-tag>.topic.json
|
|
3887
|
+
Never replace them with an aggregate catalog, manifest, envelope registry, custom event registry,
|
|
3888
|
+
or multi-topic JSON file. Exactly one topic definition is allowed per file. The filename uses the
|
|
3889
|
+
unqualified topic portion: tag "order-events:order-created" must be stored as
|
|
3890
|
+
ductape/events/order-created.topic.json.
|
|
3891
|
+
|
|
3892
|
+
Generate the exact path and body before writing a topic:
|
|
3893
|
+
ductape_events_topic_setup({ broker_tag: "order-events", topic_tag: "order-created", name: "Order Created" })
|
|
3894
|
+
Validate the complete repository before any remote mutation:
|
|
3895
|
+
ductape_events_validate_project({ dir: "ductape/events" })
|
|
3896
|
+
Or through the CLI:
|
|
3897
|
+
ductape_cli("events topics validate --dir ductape/events --json")
|
|
3898
|
+
ductape_cli("events topics create-all --dir ductape/events --json")
|
|
3899
|
+
|
|
3900
|
+
Create one topic directly from its canonical asset:
|
|
3901
|
+
ductape_cli("events topics create -f ductape/events/order-created.topic.json --json")
|
|
3902
|
+
|
|
3903
|
+
Each *.topic.json file may contain ONLY tag, name, description, sample, idempotent, and the
|
|
3904
|
+
currently supported provider-specific field queueUrls. Unknown custom fields are rejected.
|
|
3905
|
+
Canonical topic file schema:
|
|
3861
3906
|
{
|
|
3862
3907
|
"tag": "order-events:order-created", // ALWAYS "broker-tag:topic-tag" — full event string
|
|
3863
3908
|
"name": "Order Created",
|
|
@@ -4729,6 +4774,48 @@ const portableFunctionSetupHandler = async (args) => {
|
|
|
4729
4774
|
],
|
|
4730
4775
|
}, null, 2) }] };
|
|
4731
4776
|
};
|
|
4777
|
+
const eventsTopicSetupHandler = async (args) => {
|
|
4778
|
+
const tag = `${args.broker_tag}:${args.topic_tag}`;
|
|
4779
|
+
const relativePath = `ductape/events/${args.topic_tag}.topic.json`;
|
|
4780
|
+
const definition = {
|
|
4781
|
+
tag,
|
|
4782
|
+
name: args.name,
|
|
4783
|
+
...(args.description === undefined ? {} : { description: args.description }),
|
|
4784
|
+
...(args.sample === undefined ? {} : { sample: args.sample }),
|
|
4785
|
+
...(args.idempotent === undefined ? {} : { idempotent: args.idempotent }),
|
|
4786
|
+
...(args.queueUrls === undefined ? {} : { queueUrls: args.queueUrls }),
|
|
4787
|
+
};
|
|
4788
|
+
return {
|
|
4789
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
4790
|
+
ok: true,
|
|
4791
|
+
project_root: cliCwd(),
|
|
4792
|
+
path: join(cliCwd(), relativePath),
|
|
4793
|
+
relative_path: relativePath,
|
|
4794
|
+
definition,
|
|
4795
|
+
create_command: `ductape events topics create -f ${relativePath} --json`,
|
|
4796
|
+
validate_command: 'ductape events topics validate --dir ductape/events --json',
|
|
4797
|
+
rules: [
|
|
4798
|
+
'Write exactly this one JSON object to the returned path.',
|
|
4799
|
+
'Do not create an aggregate topic catalogue, manifest, envelope registry, or multi-topic JSON file.',
|
|
4800
|
+
'Run project validation before creating any remote topic.',
|
|
4801
|
+
],
|
|
4802
|
+
}, null, 2) }],
|
|
4803
|
+
};
|
|
4804
|
+
};
|
|
4805
|
+
const eventsProjectValidationHandler = async (args) => {
|
|
4806
|
+
const dir = args.dir ?? 'ductape/events';
|
|
4807
|
+
if (dir !== 'ductape/events') {
|
|
4808
|
+
return {
|
|
4809
|
+
content: [{ type: 'text', text: 'Events topic assets must use the canonical directory ductape/events; custom directories are forbidden.' }],
|
|
4810
|
+
isError: true,
|
|
4811
|
+
};
|
|
4812
|
+
}
|
|
4813
|
+
const result = runCli('events topics validate --dir ductape/events --json');
|
|
4814
|
+
return {
|
|
4815
|
+
content: [{ type: 'text', text: result.output || '(no output)' }],
|
|
4816
|
+
...(result.success ? {} : { isError: true }),
|
|
4817
|
+
};
|
|
4818
|
+
};
|
|
4732
4819
|
const cliInputSchema = z.object({
|
|
4733
4820
|
command: z.string().describe('The ductape CLI command to run, without the leading "ductape" word. ' +
|
|
4734
4821
|
'Examples: "products list", "products create --name \\"My Product\\" --tag my-product", ' +
|
|
@@ -5221,12 +5308,29 @@ async function main() {
|
|
|
5221
5308
|
'applications own transactional outboxes, domain rejection handling, and idempotent consumer mutations.',
|
|
5222
5309
|
inputSchema: eventsDiscoveryInputSchema,
|
|
5223
5310
|
}, eventsDiscoveryHandler);
|
|
5311
|
+
server.registerTool('ductape_events_topic_setup', {
|
|
5312
|
+
title: 'Ductape Events Topic Setup',
|
|
5313
|
+
description: 'Read-only generator for one canonical Ductape topic asset. Returns the required ' +
|
|
5314
|
+
'ductape/events/<topic-tag>.topic.json path, its directly importable JSON object, and validation/create commands. ' +
|
|
5315
|
+
'It does not write files or mutate remote resources.',
|
|
5316
|
+
inputSchema: eventsTopicSetupInputSchema,
|
|
5317
|
+
annotations: readOnlyLocalAnnotations,
|
|
5318
|
+
}, eventsTopicSetupHandler);
|
|
5319
|
+
server.registerTool('ductape_events_validate_project', {
|
|
5320
|
+
title: 'Validate Ductape Events Project',
|
|
5321
|
+
description: 'Read-only validation of canonical ductape/events/*.topic.json assets and statically discoverable publisher references. ' +
|
|
5322
|
+
'Rejects aggregate catalogues, invalid filenames/shapes/tags, duplicates, unknown fields, and undefined published topics.',
|
|
5323
|
+
inputSchema: eventsProjectValidationInputSchema,
|
|
5324
|
+
annotations: readOnlyLocalAnnotations,
|
|
5325
|
+
}, eventsProjectValidationHandler);
|
|
5224
5326
|
server.registerTool('ductape_function_setup', {
|
|
5225
5327
|
title: 'Ductape Portable Function Setup',
|
|
5226
|
-
description: '
|
|
5328
|
+
description: 'Read-only: generate (without applying) the mandatory secure local + remote runtime setup for application functions used by Features. ' +
|
|
5329
|
+
'This tool performs no network requests, writes, registrations, deployments, or infrastructure provisioning. ' +
|
|
5227
5330
|
'Use this only after a primitives-first decomposition proves residual application-owned logic remains. The result requires a registered local handler, ' +
|
|
5228
5331
|
'a deterministic HTTPS endpoint, raw-body HMAC verification, runtime checks, and fail-closed behavior.',
|
|
5229
5332
|
inputSchema: portableFunctionSetupInputSchema,
|
|
5333
|
+
annotations: portableFunctionSetupAnnotations,
|
|
5230
5334
|
}, portableFunctionSetupHandler);
|
|
5231
5335
|
server.registerTool('ductape_redis_setup', {
|
|
5232
5336
|
title: 'Ductape Local Redis Setup',
|
|
@@ -5334,7 +5438,7 @@ async function main() {
|
|
|
5334
5438
|
' GCP Pub/Sub service identifier is "pubsub". AWS SQS is "sqs". Azure Service Bus is "servicebus".\n' +
|
|
5335
5439
|
' Message brokers are import-only (no provision-persist). Import flow is the same as storage.\n' +
|
|
5336
5440
|
' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
|
|
5337
|
-
' After importing, create
|
|
5441
|
+
' After importing, create one topic per canonical ductape/events/<topic-tag>.topic.json file. First run "events topics validate --dir ductape/events --json", then use "events topics create-all --dir ductape/events --json" or create each file directly. SQS requires queueUrls. Aggregate topic catalogues are forbidden.\n' +
|
|
5338
5442
|
' - Listing workspaces, products, focused product components, secrets\n' +
|
|
5339
5443
|
' Prefer "products components list --product-tag <tag> --json" for compact inventory; use\n' +
|
|
5340
5444
|
' "products components get --product-tag <tag> --type notifications|events|healthchecks|features --json" for focused detail.\n' +
|
|
@@ -5361,7 +5465,9 @@ async function main() {
|
|
|
5361
5465
|
server.tool('ductape_schema', schemaInputSchema.shape, schemaHandler);
|
|
5362
5466
|
server.tool('ductape_docs', docsInputSchema.shape, docsHandler);
|
|
5363
5467
|
server.tool('ductape_events_discover', eventsDiscoveryInputSchema.shape, eventsDiscoveryHandler);
|
|
5364
|
-
server.tool('
|
|
5468
|
+
server.tool('ductape_events_topic_setup', eventsTopicSetupInputSchema.shape, readOnlyLocalAnnotations, eventsTopicSetupHandler);
|
|
5469
|
+
server.tool('ductape_events_validate_project', eventsProjectValidationInputSchema.shape, readOnlyLocalAnnotations, eventsProjectValidationHandler);
|
|
5470
|
+
server.tool('ductape_function_setup', portableFunctionSetupInputSchema.shape, portableFunctionSetupAnnotations, portableFunctionSetupHandler);
|
|
5365
5471
|
server.tool('ductape_redis_setup', redisSetupInputSchema.shape, redisSetupHandler);
|
|
5366
5472
|
server.tool('ductape_migration_plan', migrationInputSchema.shape, migrationHandler);
|
|
5367
5473
|
server.tool('ductape_cli', cliInputSchema.shape, cliHandler);
|
package/docs/TOOLS.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
The Ductape MCP server exposes proxy, CLI, discovery, documentation, migration, and portable-function setup tools.
|
|
4
4
|
|
|
5
|
+
## Tools: `ductape_events_topic_setup` and `ductape_events_validate_project`
|
|
6
|
+
|
|
7
|
+
`ductape_events_topic_setup` is a read-only generator for one canonical topic asset. It returns the
|
|
8
|
+
exact `<project-root>/ductape/events/<topic-tag>.topic.json` path, one directly importable JSON
|
|
9
|
+
object, and its validation/create commands. It never writes the file or creates a remote topic.
|
|
10
|
+
|
|
11
|
+
`ductape_events_validate_project` runs the CLI's repository validator. It rejects missing canonical
|
|
12
|
+
directories, non-`.topic.json` JSON files, aggregate arrays/catalogues, filename/tag disagreement,
|
|
13
|
+
unknown fields, duplicate tags, and statically discoverable publishers without a matching topic.
|
|
14
|
+
|
|
15
|
+
Ductape topic assets are always one topic per file. Never substitute an aggregate catalogue,
|
|
16
|
+
manifest, envelope registry, or custom event registry.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
5
20
|
## Tool: `ductape_function_setup`
|
|
6
21
|
|
|
7
22
|
Produces the mandatory local registry and signed HTTPS exposure plan for portable application
|