@ductape/mcp 0.1.52 → 0.1.55
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 +52 -9
- package/package.json +1 -1
- package/scripts/check-frontend-analytics-guidance.mjs +13 -0
- package/src/index.ts +52 -9
package/dist/index.js
CHANGED
|
@@ -1180,7 +1180,14 @@ function checkCli() {
|
|
|
1180
1180
|
}
|
|
1181
1181
|
function checkLoginState() {
|
|
1182
1182
|
try {
|
|
1183
|
-
|
|
1183
|
+
// `ductape whoami` only reports whether a local credentials file exists. It does not validate
|
|
1184
|
+
// the stored token, so an expired token would be cached as authenticated and fail later with 401.
|
|
1185
|
+
// Use a harmless authenticated read to validate the credential against the API.
|
|
1186
|
+
execSync('ductape workspaces list --json', {
|
|
1187
|
+
encoding: 'utf8',
|
|
1188
|
+
timeout: 10000,
|
|
1189
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1190
|
+
});
|
|
1184
1191
|
authState = 'ok';
|
|
1185
1192
|
return 'ok';
|
|
1186
1193
|
}
|
|
@@ -1225,6 +1232,26 @@ function runCli(command) {
|
|
|
1225
1232
|
}
|
|
1226
1233
|
catch (err) {
|
|
1227
1234
|
const msg = (err.stderr || err.stdout || err.message || String(err)).trim();
|
|
1235
|
+
if (/\bHTTP 401\b|unauthori[sz]ed|invalid token|token expired/i.test(msg)) {
|
|
1236
|
+
authState = 'none';
|
|
1237
|
+
workspaceSynced = false;
|
|
1238
|
+
return {
|
|
1239
|
+
success: false,
|
|
1240
|
+
output: [
|
|
1241
|
+
'Ductape CLI authentication expired or was revoked (HTTP 401).',
|
|
1242
|
+
'This administrative operation correctly used ductape_cli; do not retry it through ductape_execute.',
|
|
1243
|
+
'',
|
|
1244
|
+
'Re-authenticate locally in a trusted terminal:',
|
|
1245
|
+
' ductape login',
|
|
1246
|
+
'or use an OAuth flow:',
|
|
1247
|
+
' ductape login --browser google',
|
|
1248
|
+
' ductape login --browser github',
|
|
1249
|
+
'',
|
|
1250
|
+
'Do not paste a password, OAuth callback token, or stored CLI credential into an agent prompt.',
|
|
1251
|
+
'After login succeeds, retry the same ductape_cli command.',
|
|
1252
|
+
].join('\n'),
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1228
1255
|
return { success: false, output: msg };
|
|
1229
1256
|
}
|
|
1230
1257
|
}
|
|
@@ -2876,10 +2903,20 @@ Import (register an EXISTING cloud resource):
|
|
|
2876
2903
|
Only produce to topics whose schema is safe for client authorship.
|
|
2877
2904
|
NEVER allow clients to produce to topics that trigger privileged server-side operations
|
|
2878
2905
|
(payments, admin actions, state mutations) — those must go through a backend endpoint first.
|
|
2879
|
-
import
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2906
|
+
import { createClient } from '@ductape/client';
|
|
2907
|
+
// Equivalent supported import: import Ductape from '@ductape/client';
|
|
2908
|
+
const ductape = createClient({
|
|
2909
|
+
publishableKey: 'pk_...',
|
|
2910
|
+
env: 'prd',
|
|
2911
|
+
product: 'my-product',
|
|
2912
|
+
});
|
|
2913
|
+
await ductape.connect();
|
|
2914
|
+
await ductape.brokers.connect({
|
|
2915
|
+
broker: 'user-events',
|
|
2916
|
+
session: 'user-session:eyJ...',
|
|
2917
|
+
});
|
|
2918
|
+
await ductape.brokers.publish({
|
|
2919
|
+
topic: 'user-action',
|
|
2883
2920
|
message: { action: "button-click", screen: "dashboard" },
|
|
2884
2921
|
session: "user-session:eyJ...", // REQUIRED for client-side produce
|
|
2885
2922
|
});
|
|
@@ -3678,12 +3715,18 @@ async function main() {
|
|
|
3678
3715
|
content: [{
|
|
3679
3716
|
type: 'text',
|
|
3680
3717
|
text: [
|
|
3681
|
-
'
|
|
3718
|
+
'The Ductape CLI has no remotely valid authenticated session.',
|
|
3719
|
+
'',
|
|
3720
|
+
'Administrative reads and writes must remain on ductape_cli; do not fall back to ductape_execute.',
|
|
3682
3721
|
'',
|
|
3683
|
-
'Ask the user
|
|
3684
|
-
`
|
|
3722
|
+
'Ask the user to re-authenticate in a trusted local terminal:',
|
|
3723
|
+
` ductape login${wsFlag}`,
|
|
3724
|
+
'or:',
|
|
3725
|
+
` ductape login --browser google${wsFlag}`,
|
|
3726
|
+
` ductape login --browser github${wsFlag}`,
|
|
3685
3727
|
'',
|
|
3686
|
-
'
|
|
3728
|
+
'Never ask the user to paste their password, OAuth callback token, or stored CLI credential into the agent.',
|
|
3729
|
+
'After login succeeds, retry the original ductape_cli command.',
|
|
3687
3730
|
].join('\n'),
|
|
3688
3731
|
}],
|
|
3689
3732
|
isError: true,
|
package/package.json
CHANGED
|
@@ -42,9 +42,13 @@ const safetyChecks = [
|
|
|
42
42
|
['raw token logging prohibited', /Never log,[\s\S]*raw session\/refresh token/],
|
|
43
43
|
['durable token expiry semantics documented', /does not expose an immutable[\s\S]*expired-token attribution contract/],
|
|
44
44
|
['frontend durable work prohibits persisted JWTs', /For delayed\/durable work, do not persist raw JWTs indefinitely/],
|
|
45
|
+
['browser events use frontend client', /CLIENT-SIDE \(browser — publishable key\):[\s\S]*import \{ createClient \} from '@ductape\/client';[\s\S]*ductape\.brokers\.publish/],
|
|
45
46
|
['resilience decision matrix present', /DECISION MATRIX[\s\S]*Database atomicity/],
|
|
46
47
|
['payload session-awareness metadata present', /session_awareness:[\s\S]*accepts_session/],
|
|
47
48
|
['execute blocks administrative mutations', /Administrative operation.*is blocked in ductape_execute/],
|
|
49
|
+
['CLI auth is remotely validated', /ductape workspaces list --json/],
|
|
50
|
+
['CLI 401 triggers login-required state', /authentication expired or was revoked \(HTTP 401\)[\s\S]*ductape login/],
|
|
51
|
+
['CLI auth guidance does not request secrets', /Never ask the user to paste their password/],
|
|
48
52
|
];
|
|
49
53
|
|
|
50
54
|
for (const [name, pattern] of safetyChecks) {
|
|
@@ -55,4 +59,13 @@ for (const [name, pattern] of safetyChecks) {
|
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
const clientEventsSection = source.match(
|
|
63
|
+
/CLIENT-SIDE \(browser — publishable key\):[\s\S]*?SCHEDULED DISPATCH \(background job\):/,
|
|
64
|
+
)?.[0] ?? '';
|
|
65
|
+
assert.doesNotMatch(
|
|
66
|
+
clientEventsSection,
|
|
67
|
+
/from '@ductape\/sdk'/,
|
|
68
|
+
'Browser Events guidance must not import the server SDK',
|
|
69
|
+
);
|
|
70
|
+
|
|
58
71
|
console.log(`MCP guidance: ${checks.length + 1 + safetyChecks.length} acceptance checks passed`);
|
package/src/index.ts
CHANGED
|
@@ -1242,7 +1242,14 @@ function checkCli(): { available: boolean; version?: string } {
|
|
|
1242
1242
|
|
|
1243
1243
|
function checkLoginState(): 'ok' | 'none' {
|
|
1244
1244
|
try {
|
|
1245
|
-
|
|
1245
|
+
// `ductape whoami` only reports whether a local credentials file exists. It does not validate
|
|
1246
|
+
// the stored token, so an expired token would be cached as authenticated and fail later with 401.
|
|
1247
|
+
// Use a harmless authenticated read to validate the credential against the API.
|
|
1248
|
+
execSync('ductape workspaces list --json', {
|
|
1249
|
+
encoding: 'utf8',
|
|
1250
|
+
timeout: 10000,
|
|
1251
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1252
|
+
});
|
|
1246
1253
|
authState = 'ok';
|
|
1247
1254
|
return 'ok';
|
|
1248
1255
|
} catch {
|
|
@@ -1285,6 +1292,26 @@ function runCli(command: string): { success: boolean; output: string } {
|
|
|
1285
1292
|
return { success: true, output: output.trim() };
|
|
1286
1293
|
} catch (err: any) {
|
|
1287
1294
|
const msg = (err.stderr || err.stdout || err.message || String(err)).trim();
|
|
1295
|
+
if (/\bHTTP 401\b|unauthori[sz]ed|invalid token|token expired/i.test(msg)) {
|
|
1296
|
+
authState = 'none';
|
|
1297
|
+
workspaceSynced = false;
|
|
1298
|
+
return {
|
|
1299
|
+
success: false,
|
|
1300
|
+
output: [
|
|
1301
|
+
'Ductape CLI authentication expired or was revoked (HTTP 401).',
|
|
1302
|
+
'This administrative operation correctly used ductape_cli; do not retry it through ductape_execute.',
|
|
1303
|
+
'',
|
|
1304
|
+
'Re-authenticate locally in a trusted terminal:',
|
|
1305
|
+
' ductape login',
|
|
1306
|
+
'or use an OAuth flow:',
|
|
1307
|
+
' ductape login --browser google',
|
|
1308
|
+
' ductape login --browser github',
|
|
1309
|
+
'',
|
|
1310
|
+
'Do not paste a password, OAuth callback token, or stored CLI credential into an agent prompt.',
|
|
1311
|
+
'After login succeeds, retry the same ductape_cli command.',
|
|
1312
|
+
].join('\n'),
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1288
1315
|
return { success: false, output: msg };
|
|
1289
1316
|
}
|
|
1290
1317
|
}
|
|
@@ -2963,10 +2990,20 @@ Import (register an EXISTING cloud resource):
|
|
|
2963
2990
|
Only produce to topics whose schema is safe for client authorship.
|
|
2964
2991
|
NEVER allow clients to produce to topics that trigger privileged server-side operations
|
|
2965
2992
|
(payments, admin actions, state mutations) — those must go through a backend endpoint first.
|
|
2966
|
-
import
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2993
|
+
import { createClient } from '@ductape/client';
|
|
2994
|
+
// Equivalent supported import: import Ductape from '@ductape/client';
|
|
2995
|
+
const ductape = createClient({
|
|
2996
|
+
publishableKey: 'pk_...',
|
|
2997
|
+
env: 'prd',
|
|
2998
|
+
product: 'my-product',
|
|
2999
|
+
});
|
|
3000
|
+
await ductape.connect();
|
|
3001
|
+
await ductape.brokers.connect({
|
|
3002
|
+
broker: 'user-events',
|
|
3003
|
+
session: 'user-session:eyJ...',
|
|
3004
|
+
});
|
|
3005
|
+
await ductape.brokers.publish({
|
|
3006
|
+
topic: 'user-action',
|
|
2970
3007
|
message: { action: "button-click", screen: "dashboard" },
|
|
2971
3008
|
session: "user-session:eyJ...", // REQUIRED for client-side produce
|
|
2972
3009
|
});
|
|
@@ -3786,12 +3823,18 @@ async function main() {
|
|
|
3786
3823
|
content: [{
|
|
3787
3824
|
type: 'text',
|
|
3788
3825
|
text: [
|
|
3789
|
-
'
|
|
3826
|
+
'The Ductape CLI has no remotely valid authenticated session.',
|
|
3827
|
+
'',
|
|
3828
|
+
'Administrative reads and writes must remain on ductape_cli; do not fall back to ductape_execute.',
|
|
3790
3829
|
'',
|
|
3791
|
-
'Ask the user
|
|
3792
|
-
`
|
|
3830
|
+
'Ask the user to re-authenticate in a trusted local terminal:',
|
|
3831
|
+
` ductape login${wsFlag}`,
|
|
3832
|
+
'or:',
|
|
3833
|
+
` ductape login --browser google${wsFlag}`,
|
|
3834
|
+
` ductape login --browser github${wsFlag}`,
|
|
3793
3835
|
'',
|
|
3794
|
-
'
|
|
3836
|
+
'Never ask the user to paste their password, OAuth callback token, or stored CLI credential into the agent.',
|
|
3837
|
+
'After login succeeds, retry the original ductape_cli command.',
|
|
3795
3838
|
].join('\n'),
|
|
3796
3839
|
}],
|
|
3797
3840
|
isError: true,
|