@ductape/mcp 0.1.53 → 0.1.56

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 CHANGED
@@ -1180,7 +1180,14 @@ function checkCli() {
1180
1180
  }
1181
1181
  function checkLoginState() {
1182
1182
  try {
1183
- execSync('ductape whoami --json', { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
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,41 @@ 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
+ // Distinguish a genuinely expired login from a command-specific endpoint/auth bug.
1237
+ // A valid workspace read proves the CLI session and selected workspace are authenticated.
1238
+ if (checkLoginState() === 'ok') {
1239
+ return {
1240
+ success: false,
1241
+ output: [
1242
+ 'The Ductape CLI session and active workspace are authenticated, but this command endpoint returned HTTP 401.',
1243
+ 'Do not ask the user to log in again and do not retry through ductape_execute.',
1244
+ 'This indicates a CLI command routing or endpoint authorization problem.',
1245
+ '',
1246
+ 'Update the Ductape CLI to the latest version and retry the same ductape_cli command.',
1247
+ `Original command: ductape ${command}`,
1248
+ ].join('\n'),
1249
+ };
1250
+ }
1251
+ authState = 'none';
1252
+ workspaceSynced = false;
1253
+ return {
1254
+ success: false,
1255
+ output: [
1256
+ 'Ductape CLI authentication expired or was revoked (HTTP 401).',
1257
+ 'This administrative operation correctly used ductape_cli; do not retry it through ductape_execute.',
1258
+ '',
1259
+ 'Re-authenticate locally in a trusted terminal:',
1260
+ ' ductape login',
1261
+ 'or use an OAuth flow:',
1262
+ ' ductape login --browser google',
1263
+ ' ductape login --browser github',
1264
+ '',
1265
+ 'Do not paste a password, OAuth callback token, or stored CLI credential into an agent prompt.',
1266
+ 'After login succeeds, retry the same ductape_cli command.',
1267
+ ].join('\n'),
1268
+ };
1269
+ }
1228
1270
  return { success: false, output: msg };
1229
1271
  }
1230
1272
  }
@@ -3688,12 +3730,18 @@ async function main() {
3688
3730
  content: [{
3689
3731
  type: 'text',
3690
3732
  text: [
3691
- 'Not logged in to the Ductape CLI.',
3733
+ 'The Ductape CLI has no remotely valid authenticated session.',
3734
+ '',
3735
+ 'Administrative reads and writes must remain on ductape_cli; do not fall back to ductape_execute.',
3692
3736
  '',
3693
- 'Ask the user for their Ductape email and password, then call:',
3694
- ` ductape_cli("login --email <email> --password <password>${wsFlag}")`,
3737
+ 'Ask the user to re-authenticate in a trusted local terminal:',
3738
+ ` ductape login${wsFlag}`,
3739
+ 'or:',
3740
+ ` ductape login --browser google${wsFlag}`,
3741
+ ` ductape login --browser github${wsFlag}`,
3695
3742
  '',
3696
- 'Or the user can run `ductape login` in their terminal and then retry.',
3743
+ 'Never ask the user to paste their password, OAuth callback token, or stored CLI credential into the agent.',
3744
+ 'After login succeeds, retry the original ductape_cli command.',
3697
3745
  ].join('\n'),
3698
3746
  }],
3699
3747
  isError: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.1.53",
3
+ "version": "0.1.56",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -46,6 +46,10 @@ const safetyChecks = [
46
46
  ['resilience decision matrix present', /DECISION MATRIX[\s\S]*Database atomicity/],
47
47
  ['payload session-awareness metadata present', /session_awareness:[\s\S]*accepts_session/],
48
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 distinguishes endpoint 401 from expired login', /session and active workspace are authenticated[\s\S]*Do not ask the user to log in again/],
52
+ ['CLI auth guidance does not request secrets', /Never ask the user to paste their password/],
49
53
  ];
50
54
 
51
55
  for (const [name, pattern] of safetyChecks) {
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
- execSync('ductape whoami --json', { encoding: 'utf8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
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,41 @@ 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
+ // Distinguish a genuinely expired login from a command-specific endpoint/auth bug.
1297
+ // A valid workspace read proves the CLI session and selected workspace are authenticated.
1298
+ if (checkLoginState() === 'ok') {
1299
+ return {
1300
+ success: false,
1301
+ output: [
1302
+ 'The Ductape CLI session and active workspace are authenticated, but this command endpoint returned HTTP 401.',
1303
+ 'Do not ask the user to log in again and do not retry through ductape_execute.',
1304
+ 'This indicates a CLI command routing or endpoint authorization problem.',
1305
+ '',
1306
+ 'Update the Ductape CLI to the latest version and retry the same ductape_cli command.',
1307
+ `Original command: ductape ${command}`,
1308
+ ].join('\n'),
1309
+ };
1310
+ }
1311
+ authState = 'none';
1312
+ workspaceSynced = false;
1313
+ return {
1314
+ success: false,
1315
+ output: [
1316
+ 'Ductape CLI authentication expired or was revoked (HTTP 401).',
1317
+ 'This administrative operation correctly used ductape_cli; do not retry it through ductape_execute.',
1318
+ '',
1319
+ 'Re-authenticate locally in a trusted terminal:',
1320
+ ' ductape login',
1321
+ 'or use an OAuth flow:',
1322
+ ' ductape login --browser google',
1323
+ ' ductape login --browser github',
1324
+ '',
1325
+ 'Do not paste a password, OAuth callback token, or stored CLI credential into an agent prompt.',
1326
+ 'After login succeeds, retry the same ductape_cli command.',
1327
+ ].join('\n'),
1328
+ };
1329
+ }
1288
1330
  return { success: false, output: msg };
1289
1331
  }
1290
1332
  }
@@ -3796,12 +3838,18 @@ async function main() {
3796
3838
  content: [{
3797
3839
  type: 'text',
3798
3840
  text: [
3799
- 'Not logged in to the Ductape CLI.',
3841
+ 'The Ductape CLI has no remotely valid authenticated session.',
3842
+ '',
3843
+ 'Administrative reads and writes must remain on ductape_cli; do not fall back to ductape_execute.',
3800
3844
  '',
3801
- 'Ask the user for their Ductape email and password, then call:',
3802
- ` ductape_cli("login --email <email> --password <password>${wsFlag}")`,
3845
+ 'Ask the user to re-authenticate in a trusted local terminal:',
3846
+ ` ductape login${wsFlag}`,
3847
+ 'or:',
3848
+ ` ductape login --browser google${wsFlag}`,
3849
+ ` ductape login --browser github${wsFlag}`,
3803
3850
  '',
3804
- 'Or the user can run `ductape login` in their terminal and then retry.',
3851
+ 'Never ask the user to paste their password, OAuth callback token, or stored CLI credential into the agent.',
3852
+ 'After login succeeds, retry the original ductape_cli command.',
3805
3853
  ].join('\n'),
3806
3854
  }],
3807
3855
  isError: true,