@ductape/mcp 0.2.22 → 0.2.23
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/dist/index.js +192 -17
- package/docs/TOOLS.md +36 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -90,12 +90,15 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
90
90
|
These require an access key and CANNOT be done via ductape_execute (publishable key only).
|
|
91
91
|
→ Use ductape_cli instead. Examples:
|
|
92
92
|
ductape_cli("products list")
|
|
93
|
-
ductape_cli("products create
|
|
93
|
+
ductape_cli("products create -f product.json") # body: { name, tag?, envs?: [...] } — no --name/--tag flags exist, only -f <file.json> or interactive TTY
|
|
94
94
|
ductape_cli("cloud connections list")
|
|
95
95
|
ductape_cli("link --product my-product --env dev")
|
|
96
96
|
If the CLI is not installed, ductape_cli will return install instructions automatically.
|
|
97
|
-
NOTE: App actions are configured in the Workbench UI — there is no CLI command for them.
|
|
98
97
|
Environments DO have CLI commands: ductape_cli("products environments list/get/create/update ...").
|
|
98
|
+
App actions (endpoints) also have CLI commands: ductape_cli("apps actions list/get/create/update/delete
|
|
99
|
+
--app <app_tag> ..."). When the user wants to integrate a new endpoint, call
|
|
100
|
+
ductape_integrate_endpoint first — it returns the full ordered flow (choose/create the app,
|
|
101
|
+
define the action, connect the app to the product) before any code should be written.
|
|
99
102
|
|
|
100
103
|
RESOLVING "No linked project" ERRORS:
|
|
101
104
|
Some commands (declarative sync below, db migrate/schema, products environments *) need a
|
|
@@ -194,13 +197,22 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
194
197
|
1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
|
|
195
198
|
ALL creation and update operations require an access key and CANNOT go through ductape_execute.
|
|
196
199
|
→ Use ductape_cli for every create/update operation. Examples:
|
|
197
|
-
ductape_cli("apps create
|
|
200
|
+
ductape_cli("apps create -f app.json") # no --name/--description flags exist, only -f <file.json> or interactive TTY. body: { app_name, description, tag, envs?: [{ env_name, slug, description, base_url, active }] } — tag is REQUIRED.
|
|
198
201
|
ductape_cli("apps list")
|
|
199
202
|
ductape_cli("resources storage list")
|
|
200
203
|
ductape_cli("resources database create -f db-config.json")
|
|
201
204
|
This applies to: products, apps, and resources (databases, storage, caches, etc.),
|
|
202
205
|
cloud connections, and secrets. Environments have their own CLI commands (see below);
|
|
203
|
-
|
|
206
|
+
app actions have their own CLI commands too — ductape_cli("apps actions create --app <tag>
|
|
207
|
+
--action-file action.json") (NOT -f — that flag is claimed by the parent "apps" command, so
|
|
208
|
+
Commander resolves it against the ancestor and this subcommand never sees it; same fix as
|
|
209
|
+
"products environments create/update" uses --env-file for the identical reason). Body: {
|
|
210
|
+
tag, name, resource, method, request_type, description?, params?, query?, headers?, body?,
|
|
211
|
+
response? }, where resource is a path RELATIVE to each of the app's environment base_urls
|
|
212
|
+
(e.g. "/v1/users/{id}"), not a full URL, and request_type is REQUIRED — one of
|
|
213
|
+
"application/json" | "application/x-www-form-urlencoded" | "multipart/form-data" | "SOAP" |
|
|
214
|
+
"html" (not the short names "json"/"xml"/"form"). Auths are still configured
|
|
215
|
+
in the Workbench UI. Quotas, fallbacks, jobs, and
|
|
204
216
|
healthchecks are administrative resources managed with ductape_cli("resources <type> ...").
|
|
205
217
|
Features have no CLI create command because their definitions are code-first through
|
|
206
218
|
features.define. They DO have a CLI persistence command — ductape_cli("features sync") —
|
|
@@ -470,8 +482,26 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
470
482
|
app.init [app_tag]
|
|
471
483
|
|
|
472
484
|
━━━ MODULE: actions (app actions) ━━━
|
|
473
|
-
|
|
474
|
-
|
|
485
|
+
These are admin ops — go through ductape_cli, not ductape_execute:
|
|
486
|
+
ductape_cli("apps actions create --app <app_tag> --action-file action.json")
|
|
487
|
+
ductape_cli("apps actions update --app <app_tag> --action <action_tag> --action-file action.json")
|
|
488
|
+
ductape_cli("apps actions list --app <app_tag> --json")
|
|
489
|
+
ductape_cli("apps actions get --app <app_tag> --action <action_tag> --json")
|
|
490
|
+
ductape_cli("apps actions delete --app <app_tag> --action <action_tag>")
|
|
491
|
+
NOTE: the flag is --action-file, NOT -f/--file — that flag belongs to the parent "apps" command
|
|
492
|
+
(used by "apps create"/"apps update"), and Commander resolves a flag shared by an ancestor and a
|
|
493
|
+
descendant against the ancestor, so -f here would silently never reach this subcommand. Verified
|
|
494
|
+
live: passing -f prints "required option '-f, --file <path>' not specified" even though it was
|
|
495
|
+
supplied; --action-file is the fix (same pattern already used by "products environments
|
|
496
|
+
create/update" with --env-file for the identical collision).
|
|
497
|
+
resource is a path RELATIVE to each of the app's environment base_urls (e.g. "/v1/users/{id}"),
|
|
498
|
+
never a full URL — Ductape joins base_url + resource per environment at call time.
|
|
499
|
+
request_type is REQUIRED (verified live) — one of "application/json" |
|
|
500
|
+
"application/x-www-form-urlencoded" | "multipart/form-data" | "SOAP" | "html". response.status_code
|
|
501
|
+
is a STRING enum of standard HTTP status codes (e.g. "200", not the number 200) and
|
|
502
|
+
response.response_format uses the SAME enum as request_type above (not "json"/"xml"/"form").
|
|
503
|
+
actions.create [app_tag, data: { tag: string, name: string, resource: string, method: "GET"|"POST"|"PUT"|"PATCH"|"DELETE", request_type: "application/json"|"application/x-www-form-urlencoded"|"multipart/form-data"|"SOAP"|"html", description?: string, body?: object, params?: object, query?: object, headers?: object, response?: { name?: string, status_code: string, success: boolean, body: object, response_format: "application/json"|"application/x-www-form-urlencoded"|"multipart/form-data"|"SOAP"|"html" } }]
|
|
504
|
+
actions.update [app_tag, action_tag, data: { resource?: string, method?: "GET"|"POST"|"PUT"|"PATCH"|"DELETE", description?: string, request_type?: "application/json"|"application/x-www-form-urlencoded"|"multipart/form-data"|"SOAP"|"html", body?: object, query?: object, params?: object, headers?: object, response?: { name?: string, success: boolean, body: object, response_format: "application/json"|"application/x-www-form-urlencoded"|"multipart/form-data"|"SOAP"|"html", status_code: string } }]
|
|
475
505
|
actions.fetch [app_tag, action_tag]
|
|
476
506
|
actions.list [app_tag]
|
|
477
507
|
actions.run [{ product, env, app, action, input: { "body:fieldName": value, ... } }] ← CALL ductape_generate_payload FIRST (operation_family="action", method="run", targets={app, action})
|
|
@@ -1112,6 +1142,11 @@ const marketplaceConnectInputSchema = z.object({
|
|
|
1112
1142
|
const marketplaceInspectInputSchema = z.object({
|
|
1113
1143
|
app_tag: z.string().min(1).describe('Exact public app tag returned by ductape_marketplace_discover.'),
|
|
1114
1144
|
});
|
|
1145
|
+
const integrateEndpointInputSchema = z.object({
|
|
1146
|
+
product_tag: z.string().min(1).optional().describe('Target product tag, if already known. Omit if the user has not said which product this is for yet.'),
|
|
1147
|
+
app_tag: z.string().min(1).optional().describe('Existing app tag, if the user already said which app this endpoint belongs to. Omit when creating ' +
|
|
1148
|
+
'a new app, or when the app has not been chosen yet — the returned flow starts by asking.'),
|
|
1149
|
+
});
|
|
1115
1150
|
function toPrettyJson(value) {
|
|
1116
1151
|
return JSON.stringify(value ?? {}, null, 2);
|
|
1117
1152
|
}
|
|
@@ -2750,8 +2785,12 @@ If a feature step needs to call an external service and no App is registered for
|
|
|
2750
2785
|
An app is a versioned API integration definition. It contains environments (base URLs), actions
|
|
2751
2786
|
(individual endpoint specs), auth schemes, webhooks, variables, and constants.
|
|
2752
2787
|
|
|
2753
|
-
|
|
2754
|
-
|
|
2788
|
+
Call ductape_integrate_endpoint for the full ordered flow (choose/create the app, define the
|
|
2789
|
+
action, connect it to the product) before writing the ctx.api.run call below.
|
|
2790
|
+
|
|
2791
|
+
Create an app (admin — ductape_cli; no --name/--description flags exist, only -f <file.json> or
|
|
2792
|
+
interactive TTY):
|
|
2793
|
+
ductape_cli("apps create -f app.json") # body: { app_name, description, tag, envs?: [...] } — tag is REQUIRED
|
|
2755
2794
|
ductape_cli("app.init", ["app_tag"]) → loads app into builder state
|
|
2756
2795
|
|
|
2757
2796
|
Import from a file:
|
|
@@ -2832,15 +2871,27 @@ Connecting an app to a product (after creation):
|
|
|
2832
2871
|
NOTE: All product.* module methods require the access key and CANNOT use ductape_execute.
|
|
2833
2872
|
Use ductape_cli for all product-level operations.
|
|
2834
2873
|
|
|
2835
|
-
FULL FLOW to make an app callable from a product
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2874
|
+
FULL FLOW to make an app callable from a product — call ductape_integrate_endpoint for the
|
|
2875
|
+
ordered version of this with exact ask-the-user points; summary (all verified live):
|
|
2876
|
+
1. Create the app: ductape_cli("apps create -f app.json")
|
|
2877
|
+
body: { app_name, description, tag, envs: [{ env_name, slug, description,
|
|
2878
|
+
base_url, active }, ...] } — tag is REQUIRED; every active env needs a base_url.
|
|
2879
|
+
No --name/--description flags exist, only -f <file.json> or interactive TTY.
|
|
2880
|
+
2. Add environments: Part of step 1's envs array, or later via ductape_cli("apps update --tag
|
|
2881
|
+
<app_tag> --proxy -f app.json") with an updated envs array.
|
|
2882
|
+
3. Configure auth: Workbench (no CLI command).
|
|
2883
|
+
4. Define actions: ductape_cli("apps actions create --app <app_tag> --action-file action.json")
|
|
2884
|
+
body: { tag, name, resource, method, request_type, description?, params?,
|
|
2885
|
+
query?, headers?, body?, response? } — request_type is REQUIRED, one of
|
|
2886
|
+
"application/json" | "application/x-www-form-urlencoded" |
|
|
2887
|
+
"multipart/form-data" | "SOAP" | "html" (not "json"/"xml"/"form"); resource
|
|
2888
|
+
is relative to each env's base_url, never a full URL.
|
|
2840
2889
|
OR import: ductape_cli("apps import <file.json> -t postman|openapi")
|
|
2841
|
-
5. Connect to product:
|
|
2842
|
-
|
|
2843
|
-
|
|
2890
|
+
5. Connect to product: ductape_cli("products apps connect --product <product_tag> --app <app_tag>
|
|
2891
|
+
--env-map <app_env>:<product_env> [repeat --env-map per environment] --json")
|
|
2892
|
+
Requires the app to be public in the marketplace OR owned by the same
|
|
2893
|
+
workspace as the product. Does the full connect + environment-mapping
|
|
2894
|
+
sequence in one call.
|
|
2844
2895
|
6. Verify: ductape_cli("products apps list --product <product_tag> --json")
|
|
2845
2896
|
ductape_cli("products apps actions list --product <product_tag> --app <app_tag> --json")
|
|
2846
2897
|
`.trim(),
|
|
@@ -5002,7 +5053,14 @@ EMBEDDING EXECUTION — CURRENT CODE-FIRST CONTRACT:
|
|
|
5002
5053
|
generated.embeddings.some(v => v.length !== EXPECTED_VECTOR_DIMENSIONS)) {
|
|
5003
5054
|
throw new Error('EMBEDDING_DIMENSION_MISMATCH');
|
|
5004
5055
|
}
|
|
5005
|
-
Register a real local handler at application bootstrap or configure
|
|
5056
|
+
Register a real local handler at application bootstrap or configure gRPC-mTLS, Events, or signed HTTPS.
|
|
5057
|
+
Remote resolution order is gRPC-mTLS, Events, then signed HTTPS. A gRPC transport is
|
|
5058
|
+
{ type:'grpc', endpoint:'host:443', service:'ductape.functions.v1.PortableFunctions',
|
|
5059
|
+
method:'Invoke', authentication:'mtls', tls:{ ca_env, cert_env, key_env } }.
|
|
5060
|
+
Never write PEM, certificates, private keys, tokens, or access keys into function JSON. Set
|
|
5061
|
+
DUCTAPE_FUNCTION_GRPC_ENDPOINT and referenced TLS variables in the application runtime. The gRPC
|
|
5062
|
+
adapter must pool channels. Only UNAVAILABLE/deadline failures may fall back; auth, correlation,
|
|
5063
|
+
schema, and application failures must not execute again over another transport.
|
|
5006
5064
|
Input and output JSON schemas are validated at runtime. Missing implementation fails with
|
|
5007
5065
|
FUNCTION_UNAVAILABLE; schema mismatch fails with FUNCTION_SCHEMA_VALIDATION_FAILED; timeout fails
|
|
5008
5066
|
with FUNCTION_TIMEOUT. Never substitute sample/random/recording-time embeddings. The Feature's
|
|
@@ -5156,6 +5214,113 @@ const portableFunctionSetupHandler = async (args) => {
|
|
|
5156
5214
|
],
|
|
5157
5215
|
}, null, 2) }] };
|
|
5158
5216
|
};
|
|
5217
|
+
// This tool only derives a step-by-step plan from its arguments and read-only lookups the calling
|
|
5218
|
+
// agent performs itself via other tools. It runs no CLI command and writes nothing.
|
|
5219
|
+
const integrateEndpointHandler = async (args) => {
|
|
5220
|
+
const hasApp = Boolean(args.app_tag);
|
|
5221
|
+
const hasProduct = Boolean(args.product_tag);
|
|
5222
|
+
const stages = [];
|
|
5223
|
+
if (!hasApp) {
|
|
5224
|
+
stages.push({
|
|
5225
|
+
stage: 0,
|
|
5226
|
+
title: 'Choose the app',
|
|
5227
|
+
ask_user: 'Should this endpoint be added to an existing app, or do you want to create a new app for it?',
|
|
5228
|
+
if_existing_app: [
|
|
5229
|
+
'Get the exact app tag. If unsure, call ductape_cli("apps list --json"), or ' +
|
|
5230
|
+
'ductape_marketplace_discover/ductape_marketplace_inspect if it might be a public marketplace app.',
|
|
5231
|
+
'Re-call this tool with that app_tag once known — the returned flow will skip straight to stage 2.',
|
|
5232
|
+
],
|
|
5233
|
+
if_new_app: 'Continue to stage 1.',
|
|
5234
|
+
});
|
|
5235
|
+
}
|
|
5236
|
+
if (!hasApp) {
|
|
5237
|
+
stages.push({
|
|
5238
|
+
stage: 1,
|
|
5239
|
+
title: 'Create the app (new app path only — skip if using an existing app)',
|
|
5240
|
+
ask_user: [
|
|
5241
|
+
'App name and a short description.',
|
|
5242
|
+
'What environments does this app need (e.g. dev, staging, production), and what is the base ' +
|
|
5243
|
+
'URL for each? At least one environment is required. Every ACTIVE environment must have a ' +
|
|
5244
|
+
'base_url before the action in stage 2 can be called against it.',
|
|
5245
|
+
],
|
|
5246
|
+
then: [
|
|
5247
|
+
'Write a JSON body: { app_name, description, tag, envs: [{ env_name, slug, description, ' +
|
|
5248
|
+
'base_url, active: true }, ...] }. tag is REQUIRED — derive a slug from app_name if the ' +
|
|
5249
|
+
'user has not given one explicitly.',
|
|
5250
|
+
'Run: ductape_cli("apps create -f <path-to-body.json> --json")',
|
|
5251
|
+
'The app tag is what you supplied, not something the response generates — use it as app_tag ' +
|
|
5252
|
+
'in every later stage.',
|
|
5253
|
+
],
|
|
5254
|
+
});
|
|
5255
|
+
}
|
|
5256
|
+
stages.push({
|
|
5257
|
+
stage: 2,
|
|
5258
|
+
title: 'Define the action (the endpoint itself)',
|
|
5259
|
+
app_tag: args.app_tag ?? '<app tag from stage 1, or the existing app tag>',
|
|
5260
|
+
ask_user: [
|
|
5261
|
+
'The HTTP method (GET/POST/PUT/PATCH/DELETE).',
|
|
5262
|
+
'The endpoint PATH relative to the app\'s base_url — e.g. "/v1/users/{id}", never a full URL. ' +
|
|
5263
|
+
'Ductape joins base_url + resource per environment at call time, so the same action works ' +
|
|
5264
|
+
'across every environment automatically.',
|
|
5265
|
+
'Only what is relevant to this specific endpoint: query params, headers, request body shape ' +
|
|
5266
|
+
'(for POST/PUT/PATCH), and the expected response shape (status_code, success flag, a body ' +
|
|
5267
|
+
'sample). Do not invent fields the user has not described or that are not in the API docs.',
|
|
5268
|
+
],
|
|
5269
|
+
then: [
|
|
5270
|
+
'If the exact body shape is uncertain, call ductape_schema first.',
|
|
5271
|
+
'Write a JSON body: { tag, name, resource, method, request_type, description?, params?, ' +
|
|
5272
|
+
'query?, headers?, body?, response? }. request_type is REQUIRED — one of "application/json" ' +
|
|
5273
|
+
'| "application/x-www-form-urlencoded" | "multipart/form-data" | "SOAP" | "html" (not ' +
|
|
5274
|
+
'"json"/"xml"/"form"). If response is included, response.status_code and ' +
|
|
5275
|
+
'response.response_format use that same set of values, and status_code is a STRING (e.g. ' +
|
|
5276
|
+
'"200", not the number 200).',
|
|
5277
|
+
'Run: ductape_cli("apps actions create --app <app_tag> --action-file <path-to-action.json> ' +
|
|
5278
|
+
'--json") — the flag is --action-file, not -f (that flag belongs to the parent "apps" ' +
|
|
5279
|
+
'command and Commander resolves it there, so -f would silently be ignored here).',
|
|
5280
|
+
],
|
|
5281
|
+
});
|
|
5282
|
+
stages.push({
|
|
5283
|
+
stage: 3,
|
|
5284
|
+
title: 'Connect the app to the product',
|
|
5285
|
+
skip_if: 'The app is already connected to the target product — check with ' +
|
|
5286
|
+
'ductape_cli("products apps list --product <product_tag> --json") before asking the user anything here.',
|
|
5287
|
+
product_tag: args.product_tag ?? '<ask the user, if not already established from context>',
|
|
5288
|
+
then: [
|
|
5289
|
+
hasProduct
|
|
5290
|
+
? `Product already known: ${args.product_tag}.`
|
|
5291
|
+
: 'Ask the user which product this belongs to, unless already obvious from context.',
|
|
5292
|
+
'Get the product\'s environments: ductape_cli("products environments list <product_tag> --json")',
|
|
5293
|
+
'Map each product environment to one of the app\'s environments (usually 1:1 by name) — ask the ' +
|
|
5294
|
+
'user rather than guessing if it is not obvious.',
|
|
5295
|
+
'Run: ductape_cli("products apps connect --product <product_tag> --app <app_tag> --env-map ' +
|
|
5296
|
+
'<app_env>:<product_env> [repeat --env-map per environment] --json"). If the app is a public ' +
|
|
5297
|
+
'marketplace app, ductape_marketplace_connect does the same thing in one call.',
|
|
5298
|
+
],
|
|
5299
|
+
});
|
|
5300
|
+
stages.push({
|
|
5301
|
+
stage: 4,
|
|
5302
|
+
title: 'Only now write integration code',
|
|
5303
|
+
instructions: [
|
|
5304
|
+
'Do not write any code that calls this endpoint before stages 0-3 are complete — the action and ' +
|
|
5305
|
+
'the product connection must exist first, or the generated code will reference a tag that ' +
|
|
5306
|
+
'does not exist yet.',
|
|
5307
|
+
'Call ductape_generate_payload (operation_family="action", method="run" or "dispatch", ' +
|
|
5308
|
+
'targets={app, action}) first, then ductape_generate_snippet or ductape_execute to call it.',
|
|
5309
|
+
],
|
|
5310
|
+
});
|
|
5311
|
+
return {
|
|
5312
|
+
content: [{
|
|
5313
|
+
type: 'text',
|
|
5314
|
+
text: JSON.stringify({
|
|
5315
|
+
ok: true,
|
|
5316
|
+
flow: 'integrate_endpoint',
|
|
5317
|
+
note: 'Follow these stages in order. Each stage tells you what to ask the user and which ' +
|
|
5318
|
+
'ductape_cli command to run. Stages already satisfied by the arguments you passed are omitted.',
|
|
5319
|
+
stages,
|
|
5320
|
+
}, null, 2),
|
|
5321
|
+
}],
|
|
5322
|
+
};
|
|
5323
|
+
};
|
|
5159
5324
|
const eventsTopicSetupHandler = async (args) => {
|
|
5160
5325
|
const tag = `${args.broker_tag}:${args.topic_tag}`;
|
|
5161
5326
|
const relativePath = `ductape/events/${args.topic_tag}.topic.json`;
|
|
@@ -5840,6 +6005,15 @@ async function main() {
|
|
|
5840
6005
|
command: `products apps connect --product ${shellArgument(args.product_tag)} --app ${shellArgument(args.app_tag)}${mappings} --json`,
|
|
5841
6006
|
});
|
|
5842
6007
|
});
|
|
6008
|
+
server.registerTool('ductape_integrate_endpoint', {
|
|
6009
|
+
title: 'Integrate a New Endpoint',
|
|
6010
|
+
description: 'Call this FIRST whenever the user wants to integrate a new API endpoint — before writing any ' +
|
|
6011
|
+
'code. Returns the ordered flow: choose an existing app or create a new one, define the ' +
|
|
6012
|
+
'action (endpoint) on it, connect the app to the target product, and only then write code. ' +
|
|
6013
|
+
'Read-only — it runs no CLI command itself and performs the steps only when you follow up with ' +
|
|
6014
|
+
'the ductape_cli calls it names.',
|
|
6015
|
+
inputSchema: integrateEndpointInputSchema,
|
|
6016
|
+
}, integrateEndpointHandler);
|
|
5843
6017
|
server.registerTool('ductape_cli', {
|
|
5844
6018
|
title: 'Ductape CLI',
|
|
5845
6019
|
description: 'Run a Ductape CLI command for administrative operations.\n\n' +
|
|
@@ -5958,6 +6132,7 @@ async function main() {
|
|
|
5958
6132
|
server.tool('ductape_function_setup', portableFunctionSetupInputSchema.shape, portableFunctionSetupAnnotations, portableFunctionSetupHandler);
|
|
5959
6133
|
server.tool('ductape_redis_setup', redisSetupInputSchema.shape, redisSetupHandler);
|
|
5960
6134
|
server.tool('ductape_migration_plan', migrationInputSchema.shape, migrationHandler);
|
|
6135
|
+
server.tool('ductape_integrate_endpoint', integrateEndpointInputSchema.shape, integrateEndpointHandler);
|
|
5961
6136
|
server.tool('ductape_cli', cliInputSchema.shape, cliHandler);
|
|
5962
6137
|
}
|
|
5963
6138
|
else {
|
package/docs/TOOLS.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
The Ductape MCP server exposes proxy, CLI, discovery, documentation, migration, and portable-function setup tools.
|
|
4
4
|
|
|
5
|
+
## Tool: `ductape_integrate_endpoint`
|
|
6
|
+
|
|
7
|
+
Call this first whenever the user wants to integrate a new API endpoint, before writing any code.
|
|
8
|
+
It is read-only guidance — it runs no CLI command itself and returns the ordered flow to follow:
|
|
9
|
+
|
|
10
|
+
1. **Choose the app** — ask first: existing app, or create a new one? Do not assume "new" just
|
|
11
|
+
because the API seems unfamiliar — check `ductape_cli("apps list --json")` and let the user
|
|
12
|
+
decide. If new: `ductape_cli("apps create -f app.json")`, body `{ app_name, description, tag,
|
|
13
|
+
envs: [...] }` (`tag` is REQUIRED — no `--name`/`--description` flags exist, only `-f
|
|
14
|
+
<file.json>` or interactive TTY), collecting an environment + base URL for each environment the
|
|
15
|
+
app needs; every active environment needs a `base_url` before its action can be called.
|
|
16
|
+
2. **Define the action** — the HTTP method and the endpoint's path *relative to the app's base_url*
|
|
17
|
+
(e.g. `/v1/users/{id}`, never a full URL — Ductape joins `base_url + resource` per environment at
|
|
18
|
+
call time), plus only the params/headers/body/response fields relevant to that endpoint. Then
|
|
19
|
+
`ductape_cli("apps actions create --app <tag> --action-file action.json")` — the flag is
|
|
20
|
+
`--action-file`, **not** `-f`/`--file` (that flag belongs to the parent `apps` command; Commander
|
|
21
|
+
resolves a flag shared by an ancestor and a descendant against the ancestor, so `-f` here is
|
|
22
|
+
silently ignored — same fix `products environments create/update` uses `--env-file` for).
|
|
23
|
+
`request_type` is REQUIRED on the body — one of `"application/json"` |
|
|
24
|
+
`"application/x-www-form-urlencoded"` | `"multipart/form-data"` | `"SOAP"` | `"html"` (not the
|
|
25
|
+
short names `"json"`/`"xml"`/`"form"`); if `response` is included, `response.status_code` is a
|
|
26
|
+
**string** (e.g. `"200"`) and `response.response_format` uses that same value set.
|
|
27
|
+
3. **Connect the app to the product** — skip if already connected. Otherwise map each product
|
|
28
|
+
environment to an app environment and run
|
|
29
|
+
`ductape_cli("products apps connect --product <tag> --app <tag> --env-map <app_env>:<product_env> ...")`.
|
|
30
|
+
4. **Only then write integration code** — `ductape_generate_payload` → `ductape_generate_snippet` /
|
|
31
|
+
`ductape_execute`, calling `actions.run` or `actions.dispatch`.
|
|
32
|
+
|
|
33
|
+
Optional arguments `product_tag` and `app_tag` let the tool skip stages already answered (e.g. pass
|
|
34
|
+
`app_tag` once the user has said which existing app this belongs to, and the returned flow starts at
|
|
35
|
+
stage 2 instead of asking again) — but never infer `app_tag` on the tool's behalf without the user
|
|
36
|
+
having actually said so; when in doubt, omit it and let stage 1 ask.
|
|
37
|
+
|
|
38
|
+
All of the above was verified against a live workspace: created a `jsonplaceholder` app with `prd`/
|
|
39
|
+
`snd` environments, added a `GET /todos/1` action, and connected it to a real product end to end.
|
|
40
|
+
|
|
5
41
|
## Tools: `ductape_events_topic_setup` and `ductape_events_validate_project`
|
|
6
42
|
|
|
7
43
|
`ductape_events_topic_setup` is a read-only generator for one canonical topic asset. It returns the
|