@ductape/mcp 0.2.29 → 0.2.31
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 +131 -31
- package/docs/TOOLS.md +23 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
- Added `cli-authentication` guidance for automatic Google/GitHub browser OAuth, validated loopback callbacks, trusted-terminal handoff, credential non-disclosure, and external session refresh.
|
|
9
10
|
- 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.
|
|
10
11
|
- 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.
|
|
11
12
|
- 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.
|
package/dist/index.js
CHANGED
|
@@ -197,14 +197,14 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
197
197
|
1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
|
|
198
198
|
ALL creation and update operations require an access key and CANNOT go through ductape_execute.
|
|
199
199
|
→ Use ductape_cli for every create/update operation. Examples:
|
|
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.
|
|
200
|
+
ductape_cli("apps create -f ductape/apps/<app-tag>.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.
|
|
201
201
|
ductape_cli("apps list")
|
|
202
202
|
ductape_cli("resources storage list")
|
|
203
|
-
ductape_cli("resources database create -f
|
|
203
|
+
ductape_cli("resources database create -f ductape/databases/<database-tag>.database.json")
|
|
204
204
|
This applies to: products, apps, and resources (databases, storage, caches, etc.),
|
|
205
205
|
cloud connections, and secrets. Environments have their own CLI commands (see below);
|
|
206
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
|
|
207
|
+
--action-file ductape/actions/<app-tag>/<action-tag>.action.json") (NOT -f — that flag is claimed by the parent "apps" command, so
|
|
208
208
|
Commander resolves it against the ancestor and this subcommand never sees it; same fix as
|
|
209
209
|
"products environments create/update" uses --env-file for the identical reason). Body: {
|
|
210
210
|
tag, name, resource, method, request_type, description?, params?, query?, headers?, body?,
|
|
@@ -290,6 +290,16 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
290
290
|
→ Applies to: actions, features, sessions, notifications, databases, storage, graphs, vectors,
|
|
291
291
|
quotas, fallbacks, jobs, and any other operation that executes against a pre-configured schema.
|
|
292
292
|
|
|
293
|
+
SAVED ACTION REUSE IS MANDATORY:
|
|
294
|
+
→ Before creating a database, graph, or vector action, list the existing actions on that exact
|
|
295
|
+
component and inspect plausible matches with get/fetch.
|
|
296
|
+
→ Reuse an existing action when its operation, target, template/query, parameters, and output
|
|
297
|
+
semantics satisfy the requested behavior. Invoke it by tag; do not create a duplicate merely
|
|
298
|
+
because the requested name differs.
|
|
299
|
+
→ Create a new action only when no existing action provides the required behavior. Update an
|
|
300
|
+
existing action only for a compatible correction; do not mutate a shared action into different
|
|
301
|
+
semantics. State the evidence used to decide reuse versus creation.
|
|
302
|
+
|
|
293
303
|
Skipping ductape_generate_payload for applicable runtime operations will produce incorrect or empty input payloads.
|
|
294
304
|
Exception: messaging produce/consume/dispatch — the producer defines the schema, so infer from context instead.
|
|
295
305
|
|
|
@@ -483,8 +493,8 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
483
493
|
|
|
484
494
|
━━━ MODULE: actions (app actions) ━━━
|
|
485
495
|
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")
|
|
496
|
+
ductape_cli("apps actions create --app <app_tag> --action-file ductape/actions/<app_tag>/<action_tag>.action.json")
|
|
497
|
+
ductape_cli("apps actions update --app <app_tag> --action <action_tag> --action-file ductape/actions/<app_tag>/<action_tag>.action.json")
|
|
488
498
|
ductape_cli("apps actions list --app <app_tag> --json")
|
|
489
499
|
ductape_cli("apps actions get --app <app_tag> --action <action_tag> --json")
|
|
490
500
|
ductape_cli("apps actions delete --app <app_tag> --action <action_tag>")
|
|
@@ -782,7 +792,7 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
782
792
|
databases.migration.status [migrations]
|
|
783
793
|
databases.action.create [{ tag: "product:database:action", name, description, tableName, operation: "query"|"insert"|"update"|"delete"|"aggregate"|"count", template, filterTemplate? }]
|
|
784
794
|
← ADMINISTRATIVE (access key required). FORBIDDEN via ductape_execute (publishable key) —
|
|
785
|
-
use ductape_cli("db actions create --action-file action.json") instead. tag MUST be fully qualified
|
|
795
|
+
use ductape_cli("db actions create --action-file ductape/database/actions/<action-tag>.action.json") instead. tag MUST be fully qualified
|
|
786
796
|
"product_tag:database_tag:action_tag" — a 2-part "database:action" shorthand only resolves
|
|
787
797
|
inside a long-lived process that already has that product's builder cached, never in a
|
|
788
798
|
fresh CLI/MCP call. This is pure metadata registration; it never connects to the live
|
|
@@ -899,12 +909,13 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
899
909
|
vector.deleteIndex [{ product, env, vector, name }]
|
|
900
910
|
vector.listIndexes [{ product, env, vector }]
|
|
901
911
|
vector.count [{ product, env, vector, namespace? }]
|
|
902
|
-
vector.
|
|
903
|
-
vector.
|
|
904
|
-
vector.
|
|
905
|
-
vector.
|
|
906
|
-
vector.
|
|
907
|
-
vector.
|
|
912
|
+
vector.action.create [{ product, vector, actionTag, name, operation, template, description?, parameters? }]
|
|
913
|
+
vector.action.update [{ product, vector, actionTag, name?, description?, template?, parameters? }]
|
|
914
|
+
vector.action.fetch [{ product, vector, actionTag }]
|
|
915
|
+
vector.action.fetchAll [{ product, vector }]
|
|
916
|
+
vector.action.delete [{ product, vector, actionTag }]
|
|
917
|
+
vector.action.execute [{ product, env, vector, action, input, session? }]
|
|
918
|
+
← synchronous saved-action execution. Do not use vector.query for a saved action.
|
|
908
919
|
|
|
909
920
|
━━━ MODULE: features ━━━
|
|
910
921
|
Feature definitions are code-first. Use features.define in application source; do not call
|
|
@@ -1490,6 +1501,7 @@ function runCli(command) {
|
|
|
1490
1501
|
'or use an OAuth flow:',
|
|
1491
1502
|
' ductape login --browser google',
|
|
1492
1503
|
' ductape login --browser github',
|
|
1504
|
+
'Browser OAuth returns to the CLI automatically through a validated 127.0.0.1 callback.',
|
|
1493
1505
|
'',
|
|
1494
1506
|
'Do not paste a password, OAuth callback token, or stored CLI credential into an agent prompt.',
|
|
1495
1507
|
'After login succeeds, retry the same ductape_cli command.',
|
|
@@ -1541,7 +1553,7 @@ const docsInputSchema = z.object({
|
|
|
1541
1553
|
topic: z.string().describe('Feature topic to look up. Supported: ' +
|
|
1542
1554
|
'transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
|
|
1543
1555
|
'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
|
|
1544
|
-
'notifications, resilience, features, portable-functions, events, logs, migration, frontend, frontend-analytics, client, react, vue'),
|
|
1556
|
+
'notifications, resilience, features, portable-functions, events, logs, migration, cli-authentication, frontend, frontend-analytics, client, react, vue'),
|
|
1545
1557
|
});
|
|
1546
1558
|
const eventsDiscoveryInputSchema = z.object({
|
|
1547
1559
|
query: z.string().optional().describe('Capability or recovery search, including DLQ, dead letter, failed messages, retry, poison message, replay, or consumer failure.'),
|
|
@@ -1597,6 +1609,34 @@ const migrationInputSchema = z.object({
|
|
|
1597
1609
|
write: z.boolean().optional().default(false).describe('Write redacted advisory artifacts only. This never writes application code or executable Ductape assets.'),
|
|
1598
1610
|
});
|
|
1599
1611
|
const DOCS = {
|
|
1612
|
+
'cli-authentication': `
|
|
1613
|
+
DUCTAPE CLI AUTHENTICATION
|
|
1614
|
+
|
|
1615
|
+
For an interactive local login, prefer browser OAuth:
|
|
1616
|
+
ductape login --browser google
|
|
1617
|
+
ductape login --browser github
|
|
1618
|
+
|
|
1619
|
+
The CLI opens the provider in the user's browser, starts a temporary loopback listener on
|
|
1620
|
+
127.0.0.1, validates the returned OAuth state, exchanges the callback token, stores the resulting
|
|
1621
|
+
session in the local Ductape credential store, and then continues workspace selection. The user
|
|
1622
|
+
does not copy a callback token or share credentials with an agent.
|
|
1623
|
+
|
|
1624
|
+
Run browser login only in the user's trusted local terminal. An MCP tool, agent subprocess, CI
|
|
1625
|
+
runner, container, SSH session, or remote development host may not own the browser's loopback
|
|
1626
|
+
interface. Do not invoke interactive login through ductape_cli and do not ask the user to paste a
|
|
1627
|
+
password, OAuth callback token, auth token, or stored credential into chat.
|
|
1628
|
+
|
|
1629
|
+
Use email/password login only when the user deliberately chooses it in their own terminal:
|
|
1630
|
+
ductape login
|
|
1631
|
+
|
|
1632
|
+
Use --token only as a compatibility or automation fallback when the user already possesses a
|
|
1633
|
+
valid OAuth callback token through a trusted flow. Never solicit that token in an agent prompt.
|
|
1634
|
+
|
|
1635
|
+
After login succeeds, retry the original ductape_cli operation. The MCP re-reads a previously
|
|
1636
|
+
missing or expired external CLI session without requiring an MCP restart. If a command returns
|
|
1637
|
+
HTTP 401 while workspace reads still succeed, treat it as endpoint authorization or command
|
|
1638
|
+
routing failure, not as a reason to re-authenticate.
|
|
1639
|
+
`.trim(),
|
|
1600
1640
|
'portable-functions': `
|
|
1601
1641
|
PORTABLE APPLICATION FUNCTIONS
|
|
1602
1642
|
|
|
@@ -2432,9 +2472,15 @@ ctx.database.execute inside a Feature step invokes a saved action through its \`
|
|
|
2432
2472
|
Direct ctx.database.query/insert/update/delete calls retain their normal {table, where, data, ...}
|
|
2433
2473
|
shape and do not resolve a saved action.
|
|
2434
2474
|
|
|
2475
|
+
REUSE BEFORE CREATE:
|
|
2476
|
+
1. ductape_cli("db actions list --database product_tag:database_tag --json")
|
|
2477
|
+
2. Inspect likely matches with ductape_cli("db actions get product_tag:database_tag:action_tag --json").
|
|
2478
|
+
3. Reuse the matching tag with database.execute/ctx.database.execute when operation, table,
|
|
2479
|
+
template, parameters, and result semantics match. Create only when no exact functional match exists.
|
|
2480
|
+
|
|
2435
2481
|
Create/update/delete are ADMINISTRATIVE (access key) — FORBIDDEN through ductape_execute
|
|
2436
2482
|
(publishable key). Use ductape_cli, same as every other admin resource:
|
|
2437
|
-
ductape_cli("db actions create --action-file action.json")
|
|
2483
|
+
ductape_cli("db actions create --action-file ductape/database/actions/<action-tag>.action.json")
|
|
2438
2484
|
File: { tag: "product:database:action", name, description, tableName,
|
|
2439
2485
|
operation: "query"|"insert"|"update"|"delete"|"aggregate"|"count",
|
|
2440
2486
|
template, filterTemplate? }
|
|
@@ -2478,6 +2524,10 @@ scheduled, dispatched with retries, and audited via logs.
|
|
|
2478
2524
|
graphs: `
|
|
2479
2525
|
DUCTAPE GRAPH DATABASES
|
|
2480
2526
|
|
|
2527
|
+
Before creating a graph action, list existing actions for the graph and inspect likely matches.
|
|
2528
|
+
Reuse an action whose query/operation, parameters, and result semantics already satisfy the task;
|
|
2529
|
+
create a new action only when no functional match exists.
|
|
2530
|
+
|
|
2481
2531
|
Supported engines: neo4j | neptune | cosmos-gremlin | spanner-graph | arangodb | memgraph
|
|
2482
2532
|
|
|
2483
2533
|
Registration (admin — ductape_cli):
|
|
@@ -2531,10 +2581,12 @@ Schema management:
|
|
|
2531
2581
|
graph.dropIndex / dropConstraint / listIndexes / listConstraints
|
|
2532
2582
|
|
|
2533
2583
|
Saved actions (parameterized queries stored on the product):
|
|
2534
|
-
graph.
|
|
2535
|
-
graph.
|
|
2536
|
-
graph.
|
|
2537
|
-
graph.
|
|
2584
|
+
graph.action.create [{ product, graph|graphTag, name, description?, operation, query, parameters? }]
|
|
2585
|
+
graph.action.fetchAll [{ product, graph|graphTag }]
|
|
2586
|
+
graph.action.fetch [{ product, graph|graphTag, action|actionTag }]
|
|
2587
|
+
graph.action.execute [{ product, env, graph, action, input, session? }]
|
|
2588
|
+
graph.action.dispatch [{ product, env, graph, event, input, schedule? }] ← call ductape_generate_payload FIRST
|
|
2589
|
+
Use graph.action.execute for a saved action. graph.query is only for a direct provider-native query.
|
|
2538
2590
|
|
|
2539
2591
|
Supported index types: btree | fulltext | vector | range | point | text
|
|
2540
2592
|
Supported constraint types: UNIQUE | EXISTS | NODE_KEY
|
|
@@ -2841,7 +2893,7 @@ action, connect it to the product) before writing the ctx.api.run call below.
|
|
|
2841
2893
|
|
|
2842
2894
|
Create an app (admin — ductape_cli; no --name/--description flags exist, only -f <file.json> or
|
|
2843
2895
|
interactive TTY):
|
|
2844
|
-
ductape_cli("apps create -f app.json") # body: { app_name, description, tag, envs?: [...] } — tag is REQUIRED
|
|
2896
|
+
ductape_cli("apps create -f ductape/apps/<app-tag>.app.json") # body: { app_name, description, tag, envs?: [...] } — tag is REQUIRED
|
|
2845
2897
|
ductape_cli("app.init", ["app_tag"]) → loads app into builder state
|
|
2846
2898
|
|
|
2847
2899
|
Import from a file:
|
|
@@ -2924,14 +2976,14 @@ Connecting an app to a product (after creation):
|
|
|
2924
2976
|
|
|
2925
2977
|
FULL FLOW to make an app callable from a product — call ductape_integrate_endpoint for the
|
|
2926
2978
|
ordered version of this with exact ask-the-user points; summary (all verified live):
|
|
2927
|
-
1. Create the app: ductape_cli("apps create -f app.json")
|
|
2979
|
+
1. Create the app: ductape_cli("apps create -f ductape/apps/<app-tag>.app.json")
|
|
2928
2980
|
body: { app_name, description, tag, envs: [{ env_name, slug, description,
|
|
2929
2981
|
base_url, active }, ...] } — tag is REQUIRED; every active env needs a base_url.
|
|
2930
2982
|
No --name/--description flags exist, only -f <file.json> or interactive TTY.
|
|
2931
2983
|
2. Add environments: Part of step 1's envs array, or later via ductape_cli("apps update --tag
|
|
2932
|
-
<app_tag> --proxy -f app.json") with an updated envs array.
|
|
2984
|
+
<app_tag> --proxy -f ductape/apps/<app-tag>.app.json") with an updated envs array.
|
|
2933
2985
|
3. Configure auth: Workbench (no CLI command).
|
|
2934
|
-
4. Define actions: ductape_cli("apps actions create --app <app_tag> --action-file action.json")
|
|
2986
|
+
4. Define actions: ductape_cli("apps actions create --app <app_tag> --action-file ductape/actions/<app_tag>/<action-tag>.action.json")
|
|
2935
2987
|
body: { tag, name, resource, method, request_type, description?, params?,
|
|
2936
2988
|
query?, headers?, body?, response? } — request_type is REQUIRED, one of
|
|
2937
2989
|
"application/json" | "application/x-www-form-urlencoded" |
|
|
@@ -4005,6 +4057,14 @@ When you call features.define({ handler }), the handler runs TWICE:
|
|
|
4005
4057
|
For loops: supply recordInput and iterate ctx.sampleInput so all iterations are recorded.
|
|
4006
4058
|
ctx.input is always the runtime operator surface and must never expose recordInput literals.
|
|
4007
4059
|
For branches: use branchOverrides so each path is captured.
|
|
4060
|
+
Never make an authorization, validation, tenancy, or other security decision by branching on
|
|
4061
|
+
ctx.input during recording. Use ctx.sampleInput only to discover graph shape; enforce security
|
|
4062
|
+
invariants inside a runtime portable Function or recorded step.
|
|
4063
|
+
|
|
4064
|
+
ductape features sync [filter] sets DUCTAPE_SYNC_MODE=1 and, when filtered,
|
|
4065
|
+
DUCTAPE_FEATURE_FILTER=<filter>. This is an administrative catalogue-only lifecycle: framework
|
|
4066
|
+
integrations and project readiness hooks must not start HTTP listeners, provider probes,
|
|
4067
|
+
schedulers, or Event consumers. Apply the filter before booting unrelated service modules.
|
|
4008
4068
|
|
|
4009
4069
|
2. EXECUTION PHASE (at runtime) — handler is called with a real ExecutionContext.
|
|
4010
4070
|
ctx.step() actually executes. All real Ductape component calls happen.
|
|
@@ -5075,10 +5135,15 @@ EXACT CODE-FIRST VECTOR CONTEXT:
|
|
|
5075
5135
|
them into the Feature definition. Note the deliberate naming: ctx.vector.query uses values for
|
|
5076
5136
|
the embedding, while the lower-level ductape.vector.query runtime facade uses vector.
|
|
5077
5137
|
Register reusable vector actions administratively, then read them back before compiling Features:
|
|
5138
|
+
ductape vector actions list --vector <vector-tag> --json
|
|
5078
5139
|
ductape vector actions create --action-file vector-action.json --json
|
|
5079
5140
|
ductape vector actions get <action-tag> --vector <vector-tag> --json
|
|
5080
|
-
|
|
5081
|
-
|
|
5141
|
+
Always list and inspect first. Reuse an existing action when operation, namespace/filter/template,
|
|
5142
|
+
parameters, and output semantics match; do not create a duplicate action under a new name.
|
|
5143
|
+
Update/delete/list use the same plural \`ductape vector actions\` CLI surface. Runtime SDK/MCP calls
|
|
5144
|
+
use the singular \`ductape.vector.action.*\` namespace; execute saved actions with
|
|
5145
|
+
\`ductape.vector.action.execute({ product, env, vector, action, input })\`. Feature handlers instead
|
|
5146
|
+
use \`ctx.vector.execute({ vector, action, input })\` because product/env/session are inherited.
|
|
5082
5147
|
Vertex AI Vector Search stores and searches supplied vectors. It does NOT generate embeddings.
|
|
5083
5148
|
Do not claim that a Vertex Vector Search index embeds text.
|
|
5084
5149
|
|
|
@@ -5303,7 +5368,8 @@ const integrateEndpointHandler = async (args) => {
|
|
|
5303
5368
|
'Write a JSON body: { app_name, description, tag, envs: [{ env_name, slug, description, ' +
|
|
5304
5369
|
'base_url, active: true }, ...] }. tag is REQUIRED — derive a slug from app_name if the ' +
|
|
5305
5370
|
'user has not given one explicitly.',
|
|
5306
|
-
'
|
|
5371
|
+
'Persist it at ductape/apps/<app-tag>.app.json. Do not use a scratchpad, temporary file, or repository-root JSON.',
|
|
5372
|
+
'Run: ductape_cli("apps create -f ductape/apps/<app-tag>.app.json --json")',
|
|
5307
5373
|
'The app tag is what you supplied, not something the response generates — use it as app_tag ' +
|
|
5308
5374
|
'in every later stage.',
|
|
5309
5375
|
],
|
|
@@ -5330,7 +5396,8 @@ const integrateEndpointHandler = async (args) => {
|
|
|
5330
5396
|
'"json"/"xml"/"form"). If response is included, response.status_code and ' +
|
|
5331
5397
|
'response.response_format use that same set of values, and status_code is a STRING (e.g. ' +
|
|
5332
5398
|
'"200", not the number 200).',
|
|
5333
|
-
'
|
|
5399
|
+
'Persist it at ductape/actions/<app_tag>/<action-tag>.action.json. This canonical file must remain in the project after creation.',
|
|
5400
|
+
'Run: ductape_cli("apps actions create --app <app_tag> --action-file ductape/actions/<app_tag>/<action-tag>.action.json ' +
|
|
5334
5401
|
'--json") — the flag is --action-file, not -f (that flag belongs to the parent "apps" ' +
|
|
5335
5402
|
'command and Commander resolves it there, so -f would silently be ignored here).',
|
|
5336
5403
|
],
|
|
@@ -5422,7 +5489,7 @@ const eventsProjectValidationHandler = async (args) => {
|
|
|
5422
5489
|
const cliInputSchema = z.object({
|
|
5423
5490
|
command: z.string().describe('The ductape CLI command to run, without the leading "ductape" word. ' +
|
|
5424
5491
|
'Examples: "products list", "products create --name \\"My Product\\" --tag my-product", ' +
|
|
5425
|
-
'"apps list", "apps create -f app.json", "resources storage list", ' +
|
|
5492
|
+
'"apps list", "apps create -f ductape/apps/<app-tag>.app.json", "resources storage list", ' +
|
|
5426
5493
|
'"events topics create -f topic.json", "events topics list --tag broker-tag", ' +
|
|
5427
5494
|
'"cloud connections list", "link --product my-product --env dev".\n\n' +
|
|
5428
5495
|
'Use this tool for administrative operations: creating or updating products, apps, ' +
|
|
@@ -5436,7 +5503,9 @@ const cliInputSchema = z.object({
|
|
|
5436
5503
|
'"features sync" (runs the project\'s own "features:sync" npm script) — never call ' +
|
|
5437
5504
|
'features.define from the app\'s normal startup path, since that blocks every boot on ' +
|
|
5438
5505
|
'Ductape API reachability. See ductape_docs for the full convention.\n\n' +
|
|
5439
|
-
'The CLI uses the user\'s local logged-in session
|
|
5506
|
+
'The CLI uses the user\'s local logged-in session. Prefer browser OAuth in a trusted local terminal: ' +
|
|
5507
|
+
'ductape login --browser google (or github). It returns automatically through a validated loopback callback. ' +
|
|
5508
|
+
'Never invoke interactive login through MCP or ask the user for credentials.'),
|
|
5440
5509
|
});
|
|
5441
5510
|
async function loadMcpSdk() {
|
|
5442
5511
|
try {
|
|
@@ -5492,6 +5561,16 @@ function handleCliFlags() {
|
|
|
5492
5561
|
// Surfaced to every connected agent at initialize time, before any tool is called.
|
|
5493
5562
|
// Keep this short — it is ambient, unconditional overhead on every session.
|
|
5494
5563
|
// The full doctrine with concrete API shapes lives in the "ductape-first-approach" prompt below.
|
|
5564
|
+
const DUCTAPE_ASSET_FILE_POLICY = 'Canonical asset-file policy: every JSON file used by the CLI to create, update, import, ' +
|
|
5565
|
+
'provision, or apply a Ductape asset must be kept inside the target project\'s ductape/ folder, ' +
|
|
5566
|
+
'under the matching asset-type directory (for example ductape/apps/, ductape/actions/, ' +
|
|
5567
|
+
'ductape/databases/, ductape/quotas/, ductape/fallbacks/, or ductape/healthchecks/). Use a ' +
|
|
5568
|
+
'stable tag-based filename such as <tag>.<asset-type>.json. Never create authoritative asset ' +
|
|
5569
|
+
'JSON in /tmp, a scratchpad, the repository root, or another disposable location and then pass ' +
|
|
5570
|
+
'that path to the CLI. Preserve established specialized layouts such as ' +
|
|
5571
|
+
'ductape/database/schema.json, ductape/database/migrations/, ductape/graphs/<graph-tag>/actions/, ' +
|
|
5572
|
+
'and ductape/events/<topic-tag>.topic.json. The ductape/ copy is the source-controlled source of ' +
|
|
5573
|
+
'truth and must remain after the CLI command completes.';
|
|
5495
5574
|
const DUCTAPE_FIRST_INSTRUCTIONS = 'Ductape-first: before writing custom code for a capability, check whether Ductape already ' +
|
|
5496
5575
|
'provides it (databases, storage, notifications, events, sessions, caches, actions, quotas, ' +
|
|
5497
5576
|
'fallback, health). Model any multi-step or externally-risky operation as a Feature ' +
|
|
@@ -5504,7 +5583,7 @@ const DUCTAPE_FIRST_INSTRUCTIONS = 'Ductape-first: before writing custom code fo
|
|
|
5504
5583
|
'to be defined once and reused across products and environments — check what already exists ' +
|
|
5505
5584
|
'before creating something new. Treat the ductape/ folder (features/, database/schema.json, ' +
|
|
5506
5585
|
'notifications.json, events/) as the source of truth: define there first, sync/apply, then ' +
|
|
5507
|
-
'write application code against it — never the reverse. Invoke the "ductape-first-approach" ' +
|
|
5586
|
+
'write application code against it — never the reverse. ' + DUCTAPE_ASSET_FILE_POLICY + ' Invoke the "ductape-first-approach" ' +
|
|
5508
5587
|
'prompt for the full doctrine with concrete API shapes before scaffolding a new backend.';
|
|
5509
5588
|
// The detailed doctrine, invoked on demand via the "ductape-first-approach" prompt rather than
|
|
5510
5589
|
// forced onto every session. Grounded in the actual module contracts this server exposes
|
|
@@ -5525,7 +5604,7 @@ const DUCTAPE_FIRST_DOCTRINE = 'Follow this checklist before defaulting to plain
|
|
|
5525
5604
|
'"ductape features sync", ductape/database/schema.json + "ductape db schema generate/migrate", ' +
|
|
5526
5605
|
'ductape/notifications.json + "ductape apply notifications", ductape/events/*.topic.json + ' +
|
|
5527
5606
|
'"ductape apply events". Define the capability there first, then write application code that ' +
|
|
5528
|
-
'calls into it — never duplicate the logic inline
|
|
5607
|
+
'calls into it — never duplicate the logic inline. ' + DUCTAPE_ASSET_FILE_POLICY + '\n\n' +
|
|
5529
5608
|
'5. Secrets never live in code or plain config. Store every credential with secrets.create ' +
|
|
5530
5609
|
'(scoped and env-aware), then reference it as $Secret{KEY} wherever a credential is needed — ' +
|
|
5531
5610
|
'action auth headers, database/storage connection config, notification provider config. ' +
|
|
@@ -5591,6 +5670,25 @@ async function main() {
|
|
|
5591
5670
|
const isLocalMigrationGuidance = (firstWord === 'migrate-codebase' && !args.command.includes('--ensure-product')) ||
|
|
5592
5671
|
firstWord.startsWith('migration-');
|
|
5593
5672
|
const isDiagnostic = firstWord === 'doctor';
|
|
5673
|
+
if (firstWord === 'login') {
|
|
5674
|
+
return {
|
|
5675
|
+
content: [{
|
|
5676
|
+
type: 'text',
|
|
5677
|
+
text: [
|
|
5678
|
+
'Ductape login must be completed by the user in a trusted local terminal, not through an MCP tool process.',
|
|
5679
|
+
'',
|
|
5680
|
+
'Preferred browser OAuth:',
|
|
5681
|
+
' ductape login --browser google',
|
|
5682
|
+
' ductape login --browser github',
|
|
5683
|
+
'',
|
|
5684
|
+
'The browser returns to the CLI automatically through a validated 127.0.0.1 callback; no token copying is required.',
|
|
5685
|
+
'Never ask the user to paste a password, OAuth callback token, auth token, or stored CLI credential into the agent.',
|
|
5686
|
+
'After login succeeds, retry the original ductape_cli operation. The MCP will detect the new session.',
|
|
5687
|
+
].join('\n'),
|
|
5688
|
+
}],
|
|
5689
|
+
isError: true,
|
|
5690
|
+
};
|
|
5691
|
+
}
|
|
5594
5692
|
if (!isAuthCommand && !isLocalMigrationGuidance && !isDiagnostic) {
|
|
5595
5693
|
// Cache successful authentication, but re-check a missing/expired session on every call.
|
|
5596
5694
|
// The user may complete `ductape login` in another terminal while this MCP process remains
|
|
@@ -5613,6 +5711,7 @@ async function main() {
|
|
|
5613
5711
|
'or:',
|
|
5614
5712
|
` ductape login --browser google${wsFlag}`,
|
|
5615
5713
|
` ductape login --browser github${wsFlag}`,
|
|
5714
|
+
'Browser OAuth returns to the CLI automatically through a validated 127.0.0.1 callback.',
|
|
5616
5715
|
'',
|
|
5617
5716
|
'Never ask the user to paste their password, OAuth callback token, or stored CLI credential into the agent.',
|
|
5618
5717
|
'After login succeeds, retry the original ductape_cli command.',
|
|
@@ -6047,7 +6146,7 @@ async function main() {
|
|
|
6047
6146
|
'index strategy, operation types) that should be confirmed with the user first.\n\n' +
|
|
6048
6147
|
'Available topics: transactions, presave, triggers, aggregations, migrations, indexes, performance, actions, ' +
|
|
6049
6148
|
'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
|
|
6050
|
-
'notifications, resilience, features, portable-functions, events, logs, migration, frontend, frontend-analytics, client, react, vue',
|
|
6149
|
+
'notifications, resilience, features, portable-functions, events, logs, migration, cli-authentication, frontend, frontend-analytics, client, react, vue',
|
|
6051
6150
|
inputSchema: docsInputSchema,
|
|
6052
6151
|
}, docsHandler);
|
|
6053
6152
|
server.registerTool('ductape_events_discover', {
|
|
@@ -6144,6 +6243,7 @@ async function main() {
|
|
|
6144
6243
|
server.registerTool('ductape_cli', {
|
|
6145
6244
|
title: 'Ductape CLI',
|
|
6146
6245
|
description: 'Run a Ductape CLI command for administrative operations.\n\n' +
|
|
6246
|
+
DUCTAPE_ASSET_FILE_POLICY + '\n\n' +
|
|
6147
6247
|
'USE THIS TOOL for any operation that creates or modifies platform configuration:\n' +
|
|
6148
6248
|
' - Creating or updating products (products create/update) and apps (apps create/update)\n' +
|
|
6149
6249
|
' - Importing an app from a Postman v2.1 or OpenAPI 3.0 file: "apps import <file> -t postman|openapi"\n' +
|
package/docs/TOOLS.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# MCP Tools Reference
|
|
2
2
|
|
|
3
|
+
## Canonical asset files
|
|
4
|
+
|
|
5
|
+
Every JSON document used by the CLI to create, update, import, provision, or apply an asset is a
|
|
6
|
+
project asset, not a temporary command payload. Store it beneath `<project-root>/ductape/` in the
|
|
7
|
+
matching asset-type directory and keep it after the command completes. For example:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
ductape/apps/paystack.app.json
|
|
11
|
+
ductape/actions/paystack/initialize-transaction.action.json
|
|
12
|
+
ductape/databases/orders.database.json
|
|
13
|
+
ductape/quotas/payment-gateway.quota.json
|
|
14
|
+
ductape/fallbacks/payment-gateway.fallback.json
|
|
15
|
+
ductape/healthchecks/paystack.healthcheck.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Never place authoritative asset JSON in `/tmp`, a scratchpad, the repository root, or another
|
|
19
|
+
disposable directory. Established specialized layouts remain authoritative, including
|
|
20
|
+
`ductape/database/schema.json`, `ductape/database/migrations/`,
|
|
21
|
+
`ductape/graphs/<graph-tag>/actions/`, and `ductape/events/<topic-tag>.topic.json`.
|
|
22
|
+
|
|
3
23
|
The Ductape MCP server exposes proxy, CLI, discovery, documentation, migration, and portable-function setup tools.
|
|
4
24
|
|
|
5
25
|
## Tool: `ductape_integrate_endpoint`
|
|
@@ -12,11 +32,12 @@ It is read-only guidance — it runs no CLI command itself and returns the order
|
|
|
12
32
|
decide. If new: `ductape_cli("apps create -f app.json")`, body `{ app_name, description, tag,
|
|
13
33
|
envs: [...] }` (`tag` is REQUIRED — no `--name`/`--description` flags exist, only `-f
|
|
14
34
|
<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.
|
|
35
|
+
app needs; every active environment needs a `base_url` before its action can be called. Persist
|
|
36
|
+
the body at `ductape/apps/<app-tag>.app.json`.
|
|
16
37
|
2. **Define the action** — the HTTP method and the endpoint's path *relative to the app's base_url*
|
|
17
38
|
(e.g. `/v1/users/{id}`, never a full URL — Ductape joins `base_url + resource` per environment at
|
|
18
39
|
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
|
|
40
|
+
`ductape_cli("apps actions create --app <tag> --action-file ductape/actions/<app-tag>/<action-tag>.action.json")` — the flag is
|
|
20
41
|
`--action-file`, **not** `-f`/`--file` (that flag belongs to the parent `apps` command; Commander
|
|
21
42
|
resolves a flag shared by an ancestor and a descendant against the ancestor, so `-f` here is
|
|
22
43
|
silently ignored — same fix `products environments create/update` uses `--env-file` for).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ductape/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
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-paystack-action-schema.mjs && node scripts/check-portable-functions.mjs && node scripts/check-project-link-guidance.mjs && node scripts/check-graph-vector-projection-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-paystack-action-schema.mjs && node scripts/check-portable-functions.mjs && node scripts/check-project-link-guidance.mjs && node scripts/check-graph-vector-projection-guidance.mjs && node scripts/check-asset-file-guidance.mjs",
|
|
19
19
|
"start": "node dist/index.js",
|
|
20
20
|
"dev": "tsx src/index.ts"
|
|
21
21
|
},
|