@zivis/mcp 0.1.0-alpha.2 → 0.1.0-alpha.20

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.
Files changed (126) hide show
  1. package/dist/api-client.d.ts +11 -11
  2. package/dist/api-client.js +41 -36
  3. package/dist/api-client.js.map +1 -1
  4. package/dist/auth/index.d.ts +9 -5
  5. package/dist/auth/index.js +33 -17
  6. package/dist/auth/index.js.map +1 -1
  7. package/dist/auth/keychain.d.ts +6 -5
  8. package/dist/auth/keychain.js +82 -69
  9. package/dist/auth/keychain.js.map +1 -1
  10. package/dist/auth/oauth.d.ts +18 -0
  11. package/dist/auth/oauth.js +55 -6
  12. package/dist/auth/oauth.js.map +1 -1
  13. package/dist/auth/sessions.d.ts +5 -4
  14. package/dist/auth/sessions.js +20 -14
  15. package/dist/auth/sessions.js.map +1 -1
  16. package/dist/auth/token-refresh.d.ts +2 -2
  17. package/dist/auth/token-refresh.js +42 -9
  18. package/dist/auth/token-refresh.js.map +1 -1
  19. package/dist/cli.d.ts +7 -15
  20. package/dist/cli.js +36 -904
  21. package/dist/cli.js.map +1 -1
  22. package/dist/lib/next-steps.d.ts +57 -0
  23. package/dist/lib/next-steps.js +45 -0
  24. package/dist/lib/next-steps.js.map +1 -0
  25. package/dist/project-binding.d.ts +19 -2
  26. package/dist/project-binding.js +57 -3
  27. package/dist/project-binding.js.map +1 -1
  28. package/dist/prompts/audit-dependencies.d.ts +10 -0
  29. package/dist/prompts/audit-dependencies.js +56 -0
  30. package/dist/prompts/audit-dependencies.js.map +1 -0
  31. package/dist/prompts/getting-started.d.ts +12 -0
  32. package/dist/prompts/getting-started.js +48 -0
  33. package/dist/prompts/getting-started.js.map +1 -0
  34. package/dist/prompts/index.d.ts +14 -0
  35. package/dist/prompts/index.js +25 -0
  36. package/dist/prompts/index.js.map +1 -0
  37. package/dist/prompts/prep-for-launch.d.ts +9 -0
  38. package/dist/prompts/prep-for-launch.js +62 -0
  39. package/dist/prompts/prep-for-launch.js.map +1 -0
  40. package/dist/prompts/secure-my-app.d.ts +9 -0
  41. package/dist/prompts/secure-my-app.js +70 -0
  42. package/dist/prompts/secure-my-app.js.map +1 -0
  43. package/dist/prompts/weekly-check.d.ts +9 -0
  44. package/dist/prompts/weekly-check.js +48 -0
  45. package/dist/prompts/weekly-check.js.map +1 -0
  46. package/dist/server.d.ts +1 -1
  47. package/dist/server.js +114 -43
  48. package/dist/server.js.map +1 -1
  49. package/dist/tools/check-project.d.ts +7 -7
  50. package/dist/tools/check-project.js +127 -95
  51. package/dist/tools/check-project.js.map +1 -1
  52. package/dist/tools/check-repo-trust.d.ts +4 -10
  53. package/dist/tools/check-repo-trust.js +56 -27
  54. package/dist/tools/check-repo-trust.js.map +1 -1
  55. package/dist/tools/enterprise/get-campaign.d.ts +27 -0
  56. package/dist/tools/enterprise/get-campaign.js +31 -0
  57. package/dist/tools/enterprise/get-campaign.js.map +1 -0
  58. package/dist/tools/enterprise/list-agents.d.ts +1 -1
  59. package/dist/tools/enterprise/list-attack-scenarios.d.ts +29 -0
  60. package/dist/tools/enterprise/list-attack-scenarios.js +69 -0
  61. package/dist/tools/enterprise/list-attack-scenarios.js.map +1 -0
  62. package/dist/tools/enterprise/list-attack-step-library-tests.d.ts +29 -0
  63. package/dist/tools/enterprise/list-attack-step-library-tests.js +61 -0
  64. package/dist/tools/enterprise/list-attack-step-library-tests.js.map +1 -0
  65. package/dist/tools/enterprise/list-campaigns.d.ts +37 -0
  66. package/dist/tools/enterprise/list-campaigns.js +60 -0
  67. package/dist/tools/enterprise/list-campaigns.js.map +1 -0
  68. package/dist/tools/enterprise/list-threat-model-review-items.d.ts +35 -0
  69. package/dist/tools/enterprise/list-threat-model-review-items.js +60 -0
  70. package/dist/tools/enterprise/list-threat-model-review-items.js.map +1 -0
  71. package/dist/tools/enterprise/list-threat-model-versions.d.ts +29 -0
  72. package/dist/tools/enterprise/list-threat-model-versions.js +60 -0
  73. package/dist/tools/enterprise/list-threat-model-versions.js.map +1 -0
  74. package/dist/tools/enterprise/manage-campaign-agent-instance.d.ts +42 -0
  75. package/dist/tools/enterprise/manage-campaign-agent-instance.js +68 -0
  76. package/dist/tools/enterprise/manage-campaign-agent-instance.js.map +1 -0
  77. package/dist/tools/enterprise/manage-campaign-target.d.ts +44 -0
  78. package/dist/tools/enterprise/manage-campaign-target.js +70 -0
  79. package/dist/tools/enterprise/manage-campaign-target.js.map +1 -0
  80. package/dist/tools/enterprise/manage-campaign.d.ts +62 -0
  81. package/dist/tools/enterprise/manage-campaign.js +114 -0
  82. package/dist/tools/enterprise/manage-campaign.js.map +1 -0
  83. package/dist/tools/enterprise/manage-finding.d.ts +1 -1
  84. package/dist/tools/enterprise/manage-threat-model-analysis.d.ts +52 -0
  85. package/dist/tools/enterprise/manage-threat-model-analysis.js +92 -0
  86. package/dist/tools/enterprise/manage-threat-model-analysis.js.map +1 -0
  87. package/dist/tools/enterprise/manage-threat-model-review.d.ts +47 -0
  88. package/dist/tools/enterprise/manage-threat-model-review.js +82 -0
  89. package/dist/tools/enterprise/manage-threat-model-review.js.map +1 -0
  90. package/dist/tools/get-application-auth-config.d.ts +27 -0
  91. package/dist/tools/get-application-auth-config.js +44 -0
  92. package/dist/tools/get-application-auth-config.js.map +1 -0
  93. package/dist/tools/get-finding-details.d.ts +4 -10
  94. package/dist/tools/get-finding-details.js +28 -10
  95. package/dist/tools/get-finding-details.js.map +1 -1
  96. package/dist/tools/get-findings.d.ts +4 -10
  97. package/dist/tools/get-findings.js +44 -23
  98. package/dist/tools/get-findings.js.map +1 -1
  99. package/dist/tools/get-started.d.ts +28 -0
  100. package/dist/tools/get-started.js +364 -0
  101. package/dist/tools/get-started.js.map +1 -0
  102. package/dist/tools/list-applications.d.ts +1 -1
  103. package/dist/tools/list-scans.d.ts +4 -10
  104. package/dist/tools/list-scans.js +55 -21
  105. package/dist/tools/list-scans.js.map +1 -1
  106. package/dist/tools/list-test-scenarios.d.ts +4 -10
  107. package/dist/tools/list-test-scenarios.js +41 -13
  108. package/dist/tools/list-test-scenarios.js.map +1 -1
  109. package/dist/tools/local-scan.d.ts +1 -1
  110. package/dist/tools/manage-application.d.ts +72 -0
  111. package/dist/tools/manage-application.js +138 -0
  112. package/dist/tools/manage-application.js.map +1 -0
  113. package/dist/tools/manage-endpoint-lifecycle.d.ts +90 -0
  114. package/dist/tools/manage-endpoint-lifecycle.js +191 -0
  115. package/dist/tools/manage-endpoint-lifecycle.js.map +1 -0
  116. package/dist/tools/open-in-ide.d.ts +1 -1
  117. package/dist/tools/setup-red-team-target.d.ts +6 -12
  118. package/dist/tools/setup-red-team-target.js +51 -20
  119. package/dist/tools/setup-red-team-target.js.map +1 -1
  120. package/dist/tools/triage-finding.d.ts +4 -10
  121. package/dist/tools/triage-finding.js +30 -21
  122. package/dist/tools/triage-finding.js.map +1 -1
  123. package/dist/types.d.ts +42 -2
  124. package/dist/types.js +29 -23
  125. package/dist/types.js.map +1 -1
  126. package/package.json +1 -1
@@ -1,27 +1,27 @@
1
1
  /**
2
2
  * ZIVIS API Client
3
3
  *
4
- * HTTPS client that attaches the appropriate auth header
5
- * based on the active auth method:
6
- * - OAuth: Authorization: Bearer <token>
7
- * - API Key: X-API-Key: <key>
8
- *
9
- * Handles 401 responses by attempting token refresh (OAuth)
10
- * before returning an auth error.
4
+ * Uses API keys for all requests (X-API-Key). OAuth in the CLI exists to mint
5
+ * org-scoped keys via `zivis auth bind`; Bearer access tokens are not used here.
11
6
  */
12
7
  import type { ZivisConfig } from "./types.js";
13
8
  export declare class ApiClient {
14
9
  private config;
15
10
  constructor(config?: ZivisConfig);
11
+ /**
12
+ * Return a config with the session resolved from the project binding when
13
+ * no explicit session was set at server startup. Called on every request so
14
+ * that a globally-installed MCP server automatically uses the right session
15
+ * for whichever workspace it's called from, without needing a per-project
16
+ * `zivis mcp install`.
17
+ */
18
+ resolveEffectiveConfig(cwd?: string): ZivisConfig;
16
19
  /**
17
20
  * Build auth headers from current credentials
18
21
  */
19
22
  private buildAuthHeaders;
20
23
  /**
21
- * Make an authenticated request to the ZIVIS API.
22
- *
23
- * Automatically resolves credentials and attaches auth headers.
24
- * On 401 with OAuth, attempts one token refresh and retries.
24
+ * Make an authenticated request to the ZIVIS API using an MCP API key (X-API-Key).
25
25
  */
26
26
  request<T>(method: string, path: string, body?: unknown): Promise<T>;
27
27
  private doRequest;
@@ -1,21 +1,44 @@
1
1
  /**
2
2
  * ZIVIS API Client
3
3
  *
4
- * HTTPS client that attaches the appropriate auth header
5
- * based on the active auth method:
6
- * - OAuth: Authorization: Bearer <token>
7
- * - API Key: X-API-Key: <key>
8
- *
9
- * Handles 401 responses by attempting token refresh (OAuth)
10
- * before returning an auth error.
4
+ * Uses API keys for all requests (X-API-Key). OAuth in the CLI exists to mint
5
+ * org-scoped keys via `zivis auth bind`; Bearer access tokens are not used here.
11
6
  */
12
7
  import { resolveAuth } from "./auth/index.js";
8
+ import { detectProjectBinding, resolveConfigFromBinding, resolveMcpSessionForWorkspace, } from "./project-binding.js";
13
9
  import { DEFAULT_CONFIG } from "./types.js";
14
10
  export class ApiClient {
15
11
  config;
16
12
  constructor(config = DEFAULT_CONFIG) {
17
13
  this.config = config;
18
14
  }
15
+ /**
16
+ * Return a config with the session resolved from the project binding when
17
+ * no explicit session was set at server startup. Called on every request so
18
+ * that a globally-installed MCP server automatically uses the right session
19
+ * for whichever workspace it's called from, without needing a per-project
20
+ * `zivis mcp install`.
21
+ */
22
+ resolveEffectiveConfig(cwd) {
23
+ const ws = this.config.workspacePath ?? cwd ?? process.cwd();
24
+ const detected = detectProjectBinding(ws);
25
+ if (!detected) {
26
+ return this.config;
27
+ }
28
+ const { binding } = detected;
29
+ const fromBinding = resolveConfigFromBinding(ws);
30
+ // Same session precedence as server startup (see resolveMcpSessionForWorkspace).
31
+ // Always overlay binding for this workspace (OAuth URL, API host, keychain bucket).
32
+ return {
33
+ ...this.config,
34
+ session: resolveMcpSessionForWorkspace(this.config, ws, detected, fromBinding),
35
+ ...(binding.apiBaseUrl ? { apiBaseUrl: binding.apiBaseUrl } : {}),
36
+ ...(binding.oauthApiUrl ? { oauthApiUrl: binding.oauthApiUrl } : {}),
37
+ ...(binding.oauthClientId ? { oauthClientId: binding.oauthClientId } : {}),
38
+ ...(binding.keychainPrefix ? { keychainPrefix: binding.keychainPrefix } : {}),
39
+ ...(binding.configDir ? { configDir: binding.configDir } : {}),
40
+ };
41
+ }
19
42
  /**
20
43
  * Build auth headers from current credentials
21
44
  */
@@ -30,41 +53,22 @@ export class ApiClient {
30
53
  }
31
54
  }
32
55
  /**
33
- * Make an authenticated request to the ZIVIS API.
34
- *
35
- * Automatically resolves credentials and attaches auth headers.
36
- * On 401 with OAuth, attempts one token refresh and retries.
56
+ * Make an authenticated request to the ZIVIS API using an MCP API key (X-API-Key).
37
57
  */
38
58
  async request(method, path, body) {
39
- const creds = await resolveAuth(this.config);
59
+ const effectiveConfig = this.resolveEffectiveConfig();
60
+ const creds = await resolveAuth(effectiveConfig, "apikey");
40
61
  if (creds.method === "none") {
41
- throw new ApiError("Not authenticated. Run: zivis auth login", 401, "AUTH_REQUIRED");
42
- }
43
- const result = await this.doRequest(method, path, body, creds);
44
- // If 401 and using OAuth, try refreshing
45
- if (result.status === 401 && creds.method === "oauth") {
46
- const freshCreds = await resolveAuth(this.config);
47
- if (freshCreds.method === "oauth" &&
48
- freshCreds.accessToken !== creds.accessToken) {
49
- // Token was refreshed, retry
50
- const retryResult = await this.doRequest(method, path, body, freshCreds);
51
- if (retryResult.status === 401) {
52
- throw new ApiError("Session expired. Run: zivis auth login", 401, "SESSION_EXPIRED");
53
- }
54
- if (!retryResult.ok) {
55
- throw new ApiError(retryResult.errorMessage || "API request failed", retryResult.status, "API_ERROR");
56
- }
57
- return retryResult.data;
58
- }
59
- throw new ApiError("Session expired. Run: zivis auth login", 401, "SESSION_EXPIRED");
62
+ throw new ApiError("Not authenticated. MCP uses API keys only — run: zivis auth bind (or zivis auth token sk_... --session <name>)", 401, "AUTH_REQUIRED");
60
63
  }
64
+ const result = await this.doRequest(method, path, body, creds, effectiveConfig.apiBaseUrl);
61
65
  if (!result.ok) {
62
66
  throw new ApiError(result.errorMessage || `API request failed: ${method} ${path}`, result.status, "API_ERROR");
63
67
  }
64
68
  return result.data;
65
69
  }
66
- async doRequest(method, path, body, creds) {
67
- const url = `${this.config.apiBaseUrl}${path}`;
70
+ async doRequest(method, path, body, creds, apiBaseUrl) {
71
+ const url = `${apiBaseUrl}${path}`;
68
72
  const headers = {
69
73
  "Content-Type": "application/json",
70
74
  "User-Agent": "@zivis-ai/cli",
@@ -104,11 +108,12 @@ export class ApiClient {
104
108
  * Used for artifact content streaming where the response is not JSON.
105
109
  */
106
110
  async getText(path) {
107
- const creds = await resolveAuth(this.config);
111
+ const effectiveConfig = this.resolveEffectiveConfig();
112
+ const creds = await resolveAuth(effectiveConfig, "apikey");
108
113
  if (creds.method === "none") {
109
- throw new ApiError("Not authenticated. Run: zivis auth login", 401, "AUTH_REQUIRED");
114
+ throw new ApiError("Not authenticated. MCP uses API keys only — run: zivis auth bind", 401, "AUTH_REQUIRED");
110
115
  }
111
- const url = `${this.config.apiBaseUrl}${path}`;
116
+ const url = `${effectiveConfig.apiBaseUrl}${path}`;
112
117
  const headers = {
113
118
  "User-Agent": "@zivis-ai/cli",
114
119
  ...this.buildAuthHeaders(creds),
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,OAAO,SAAS;IACZ,MAAM,CAAc;IAE5B,YAAY,SAAsB,cAAc;QAC9C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACK,gBAAgB,CACtB,KAAsB;QAEtB,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;YACrB,KAAK,OAAO;gBACV,OAAO,EAAE,aAAa,EAAE,UAAU,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YAC1D,KAAK,QAAQ;gBACX,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,MAAO,EAAE,CAAC;YACxC,KAAK,MAAM;gBACT,OAAO,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CACX,MAAc,EACd,IAAY,EACZ,IAAc;QAEd,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAChB,0CAA0C,EAC1C,GAAG,EACH,eAAe,CAChB,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAElE,yCAAyC;QACzC,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YACtD,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClD,IACE,UAAU,CAAC,MAAM,KAAK,OAAO;gBAC7B,UAAU,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,EAC5C,CAAC;gBACD,6BAA6B;gBAC7B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CACtC,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,UAAU,CACX,CAAC;gBACF,IAAI,WAAW,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBAC/B,MAAM,IAAI,QAAQ,CAChB,wCAAwC,EACxC,GAAG,EACH,iBAAiB,CAClB,CAAC;gBACJ,CAAC;gBACD,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC;oBACpB,MAAM,IAAI,QAAQ,CAChB,WAAW,CAAC,YAAY,IAAI,oBAAoB,EAChD,WAAW,CAAC,MAAM,EAClB,WAAW,CACZ,CAAC;gBACJ,CAAC;gBACD,OAAO,WAAW,CAAC,IAAK,CAAC;YAC3B,CAAC;YACD,MAAM,IAAI,QAAQ,CAChB,wCAAwC,EACxC,GAAG,EACH,iBAAiB,CAClB,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,QAAQ,CAChB,MAAM,CAAC,YAAY,IAAI,uBAAuB,MAAM,IAAI,IAAI,EAAE,EAC9D,MAAM,CAAC,MAAM,EACb,WAAW,CACZ,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,IAAK,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,SAAS,CACrB,MAAc,EACd,IAAY,EACZ,IAAa,EACb,KAAsB;QAOtB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC;QAC/C,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,YAAY,EAAE,eAAe;YAC7B,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;SAChC,CAAC;QAEF,MAAM,YAAY,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QACtD,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YAC7B,YAAY,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;YAEhD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC1D,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,QAAQ,CAAC,MAAM;oBACvB,YAAY,EACT,SAAoC,CAAC,OAAO;wBAC5C,SAAoC,CAAC,KAAK;wBAC3C,QAAQ,QAAQ,CAAC,MAAM,EAAE;iBAC5B,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;YAC1C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACrD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,gBAAgB;YAChB,MAAM,OAAO,GACX,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;YAChE,MAAM,IAAI,QAAQ,CAChB,mCAAmC,GAAG,MAAM,OAAO,EAAE,EACrD,CAAC,EACD,eAAe,CAChB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,4BAA4B;IAE5B,KAAK,CAAC,GAAG,CAAI,IAAY;QACvB,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE7C,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAChB,0CAA0C,EAC1C,GAAG,EACH,eAAe,CAChB,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,EAAE,CAAC;QAC/C,MAAM,OAAO,GAA2B;YACtC,YAAY,EAAE,eAAe;YAC7B,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC;SAChC,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAE9D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1D,MAAM,IAAI,QAAQ,CACf,SAAoC,CAAC,OAAO;gBAC1C,SAAoC,CAAC,KAAK;gBAC3C,QAAQ,QAAQ,CAAC,MAAM,EAAE,EAC3B,QAAQ,CAAC,MAAM,EACf,WAAW,CACZ,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,KAAK,CAAI,IAAY,EAAE,IAAa;QACxC,OAAO,IAAI,CAAC,OAAO,CAAI,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,IAAY,EAAE,IAAa;QACvC,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,IAAY;QACvB,OAAO,IAAI,CAAC,OAAO,CAAI,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;CACF;AAED,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IACA;IAHlB,YACE,OAAe,EACC,MAAc,EACd,IAAY;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF"}
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -2,15 +2,19 @@
2
2
  * Auth Orchestrator
3
3
  *
4
4
  * Resolves credentials using the defined precedence:
5
- * 1. OS Keychain — OAuth tokens (preferred, user-scoped, auto-refresh)
6
- * 2. OS KeychainAPI key (fallback, org-scoped)
7
- * 3. ZIVIS_API_KEY env var (CI environments)
8
- * 4. Not authenticated (tools return auth error with setup instructions)
5
+ * 1. OS Keychain — Local Project Binding Token / API key (apikey.<session>)
6
+ * Minted by `zivis auth bind` org-scoped, never expires, no refresh drift.
7
+ * The MCP server resolves with forceMethod=`"apikey"` only: OAuth is for
8
+ * browser login that *mints* this key, not for Bearer access to tenant APIs.
9
+ * 2. ZIVIS_API_KEY env var (CI environments)
10
+ * 3. OS Keychain — OAuth tokens (CLI `zivis auth status` / token refresh only — not MCP)
11
+ * 4. OS Keychain — default API key (no named session)
12
+ * 5. Not authenticated
9
13
  */
10
14
  import type { AuthCredentials, AuthMethod, ZivisConfig } from "../types.js";
11
15
  export { login } from "./oauth.js";
12
16
  export { keychainClearAll, keychainClearSession, getStorageName, getSessionAccount } from "./keychain.js";
13
- export { keychainStore, keychainRead } from "./keychain.js";
17
+ export { keychainStore, keychainRead, keychainDelete } from "./keychain.js";
14
18
  export { loadTokens } from "./token-refresh.js";
15
19
  export { loadSessionIndex, saveSessionEntry, removeSessionEntry, listSessions } from "./sessions.js";
16
20
  /**
@@ -2,17 +2,21 @@
2
2
  * Auth Orchestrator
3
3
  *
4
4
  * Resolves credentials using the defined precedence:
5
- * 1. OS Keychain — OAuth tokens (preferred, user-scoped, auto-refresh)
6
- * 2. OS KeychainAPI key (fallback, org-scoped)
7
- * 3. ZIVIS_API_KEY env var (CI environments)
8
- * 4. Not authenticated (tools return auth error with setup instructions)
5
+ * 1. OS Keychain — Local Project Binding Token / API key (apikey.<session>)
6
+ * Minted by `zivis auth bind` org-scoped, never expires, no refresh drift.
7
+ * The MCP server resolves with forceMethod=`"apikey"` only: OAuth is for
8
+ * browser login that *mints* this key, not for Bearer access to tenant APIs.
9
+ * 2. ZIVIS_API_KEY env var (CI environments)
10
+ * 3. OS Keychain — OAuth tokens (CLI `zivis auth status` / token refresh only — not MCP)
11
+ * 4. OS Keychain — default API key (no named session)
12
+ * 5. Not authenticated
9
13
  */
10
14
  import { keychainRead } from "./keychain.js";
11
15
  import { getValidAccessToken } from "./token-refresh.js";
12
- import { DEFAULT_CONFIG } from "../types.js";
16
+ import { DEFAULT_CONFIG, credentialStorageFromConfig } from "../types.js";
13
17
  export { login } from "./oauth.js";
14
18
  export { keychainClearAll, keychainClearSession, getStorageName, getSessionAccount } from "./keychain.js";
15
- export { keychainStore, keychainRead } from "./keychain.js";
19
+ export { keychainStore, keychainRead, keychainDelete } from "./keychain.js";
16
20
  export { loadTokens } from "./token-refresh.js";
17
21
  export { loadSessionIndex, saveSessionEntry, removeSessionEntry, listSessions } from "./sessions.js";
18
22
  /**
@@ -26,34 +30,46 @@ export { loadSessionIndex, saveSessionEntry, removeSessionEntry, listSessions }
26
30
  */
27
31
  export async function resolveAuth(config = DEFAULT_CONFIG, forceMethod) {
28
32
  const session = config.session;
29
- // 1. OAuth tokens (unless forced to apikey)
30
- if (forceMethod !== "apikey") {
31
- const accessToken = await getValidAccessToken(config);
32
- if (accessToken) {
33
- return { method: "oauth", accessToken };
33
+ const st = credentialStorageFromConfig(config);
34
+ // 1. Per-session keychain API key — Local Project Binding Token (preferred).
35
+ // Minted by `zivis auth bind` and stored under `apikey.<session>`.
36
+ // Org-scoped at the WorkOS level, never refreshes, no drift risk.
37
+ // Also covers `zivis auth token sk_xxx --session <name>` for CI injection.
38
+ if (forceMethod !== "oauth") {
39
+ const apiKeyAccount = session ? `apikey.${session}` : null;
40
+ if (apiKeyAccount) {
41
+ const sessionApiKey = await keychainRead(apiKeyAccount, st);
42
+ if (sessionApiKey) {
43
+ return { method: "apikey", apiKey: sessionApiKey };
44
+ }
34
45
  }
35
46
  }
36
- // 2. ZIVIS_API_KEY env var — always checked, even for named sessions.
37
- // This is an explicit user action (they set the env var in their MCP config),
38
- // so it's safe to use regardless of session scope.
47
+ // 2. ZIVIS_API_KEY env var (CI environments explicit, no session ambiguity).
39
48
  const envKey = process.env.ZIVIS_API_KEY;
40
49
  if (envKey) {
41
50
  return { method: "apikey", apiKey: envKey };
42
51
  }
52
+ // 3. OAuth tokens (fallback for `zivis auth login` users who haven't run bind).
53
+ if (forceMethod !== "apikey") {
54
+ const accessToken = await getValidAccessToken(config);
55
+ if (accessToken) {
56
+ return { method: "oauth", accessToken };
57
+ }
58
+ }
43
59
  // CROSS-TENANT SAFETY: If a named session is active, do NOT fall through
44
60
  // to default keychain credentials. This prevents accidentally using admin tokens
45
61
  // when working in a customer's project.
46
62
  if (session) {
47
63
  return { method: "none" };
48
64
  }
49
- // 3. API key from keychain (only for default/no-session)
65
+ // 4. Default API key from keychain (no named session).
50
66
  if (forceMethod !== "oauth") {
51
- const apiKey = await keychainRead("apikey");
67
+ const apiKey = await keychainRead("apikey", st);
52
68
  if (apiKey) {
53
69
  return { method: "apikey", apiKey };
54
70
  }
55
71
  }
56
- // 4. Not authenticated
72
+ // 5. Not authenticated
57
73
  return { method: "none" };
58
74
  }
59
75
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC1G,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAsB,cAAc,EACpC,WAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAE/B,4CAA4C;IAC5C,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,8EAA8E;IAC9E,mDAAmD;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACzC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC;IAED,yEAAyE;IACzE,iFAAiF;IACjF,wCAAwC;IACxC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED,yDAAyD;IACzD,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAsB,cAAc;IAEpC,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;QACpG,KAAK,MAAM;YACT,OAAO,mBAAmB,CAAC;IAC/B,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC1G,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErG;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAsB,cAAc,EACpC,WAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAE/B,MAAM,EAAE,GAAG,2BAA2B,CAAC,MAAM,CAAC,CAAC;IAE/C,6EAA6E;IAC7E,sEAAsE;IACtE,qEAAqE;IACrE,8EAA8E;IAC9E,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3D,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,aAAa,GAAG,MAAM,YAAY,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;YAC5D,IAAI,aAAa,EAAE,CAAC;gBAClB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACzC,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC9C,CAAC;IAED,gFAAgF;IAChF,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,iFAAiF;IACjF,wCAAwC;IACxC,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED,uDAAuD;IACvD,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAChD,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QACtC,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAsB,cAAc;IAEpC,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,OAAO;YACV,OAAO,sBAAsB,CAAC;QAChC,KAAK,QAAQ;YACX,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;QACpG,KAAK,MAAM;YACT,OAAO,mBAAmB,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -10,24 +10,25 @@
10
10
  *
11
11
  * Falls back to encrypted file if keychain CLI is unavailable.
12
12
  */
13
+ import { type CredentialStorage } from "../types.js";
13
14
  /**
14
15
  * Returns the keychain account key for a given session.
15
16
  * - No session or undefined → "oauth" (backward compatible)
16
17
  * - Named session → "oauth.{session}"
17
18
  */
18
19
  export declare function getSessionAccount(session?: string): string;
19
- export declare function keychainStore(account: string, value: string): Promise<void>;
20
- export declare function keychainRead(account: string): Promise<string | null>;
21
- export declare function keychainDelete(account: string): Promise<void>;
20
+ export declare function keychainStore(account: string, value: string, storage?: CredentialStorage): Promise<void>;
21
+ export declare function keychainRead(account: string, storage?: CredentialStorage): Promise<string | null>;
22
+ export declare function keychainDelete(account: string, storage?: CredentialStorage): Promise<void>;
22
23
  /**
23
24
  * Delete credentials for a specific session
24
25
  */
25
- export declare function keychainClearSession(session?: string): Promise<void>;
26
+ export declare function keychainClearSession(session?: string, storage?: CredentialStorage): Promise<void>;
26
27
  /**
27
28
  * Delete all stored credentials (default OAuth + API key).
28
29
  * Does NOT clear named sessions — use keychainClearSession() for those.
29
30
  */
30
- export declare function keychainClearAll(): Promise<void>;
31
+ export declare function keychainClearAll(storage?: CredentialStorage): Promise<void>;
31
32
  /**
32
33
  * Returns the storage mechanism name for display purposes
33
34
  */
@@ -18,8 +18,12 @@ import * as os from "node:os";
18
18
  import * as crypto from "node:crypto";
19
19
  import { ENV_CONFIG } from "../types.js";
20
20
  const execFileAsync = promisify(execFile);
21
- // Use environment-aware prefix so staging and production credentials are isolated
22
- const SERVICE_PREFIX = ENV_CONFIG.keychainPrefix;
21
+ function resolvedStorage(storage) {
22
+ return (storage ?? {
23
+ keychainPrefix: ENV_CONFIG.keychainPrefix,
24
+ configDir: ENV_CONFIG.configDir,
25
+ });
26
+ }
23
27
  /**
24
28
  * Returns the keychain account key for a given session.
25
29
  * - No session or undefined → "oauth" (backward compatible)
@@ -28,38 +32,52 @@ const SERVICE_PREFIX = ENV_CONFIG.keychainPrefix;
28
32
  export function getSessionAccount(session) {
29
33
  return session ? `oauth.${session}` : "oauth";
30
34
  }
31
- function getServiceName(account) {
32
- return `${SERVICE_PREFIX}.${account}`;
35
+ function getServiceName(account, keychainPrefix) {
36
+ return `${keychainPrefix}.${account}`;
33
37
  }
34
- function getFallbackDir() {
35
- // Use environment-aware config dir so staging and production are isolated
36
- const configDir = ENV_CONFIG.configDir;
38
+ function getFallbackDir(configDir) {
37
39
  if (process.platform === "win32") {
38
40
  return path.join(process.env.APPDATA || os.homedir(), configDir);
39
41
  }
40
42
  return path.join(os.homedir(), ".config", configDir);
41
43
  }
42
- function getFallbackPath(account) {
43
- return path.join(getFallbackDir(), `${account}.enc`);
44
+ function getFallbackPath(account, storage) {
45
+ return path.join(getFallbackDir(storage.configDir), `${account}.enc`);
44
46
  }
45
47
  // ── macOS Keychain ──
46
- async function macosStore(account, value) {
47
- const service = getServiceName(account);
48
- // Delete existing entry first (ignore errors if not found)
49
- try {
50
- await execFileAsync("security", [
51
- "delete-generic-password",
52
- "-a",
53
- "zivis-mcp",
54
- "-s",
55
- service,
56
- ]);
57
- }
58
- catch {
59
- // Entry didn't exist, that's fine
48
+ /**
49
+ * Drains every entry that matches `-a zivis-mcp -s <service>`. The macOS
50
+ * keychain happily keeps duplicate entries for the same service+account pair
51
+ * (different creation timestamps), so a single delete leaves stragglers behind.
52
+ * Loop until the command errors (no entries left).
53
+ */
54
+ async function macosDeleteAll(service) {
55
+ for (let i = 0; i < 100; i++) {
56
+ try {
57
+ await execFileAsync("security", [
58
+ "delete-generic-password",
59
+ "-a",
60
+ "zivis-mcp",
61
+ "-s",
62
+ service,
63
+ ]);
64
+ }
65
+ catch {
66
+ return; // No more entries.
67
+ }
60
68
  }
69
+ }
70
+ async function macosStore(account, value, keychainPrefix) {
71
+ const service = getServiceName(account, keychainPrefix);
72
+ // Drain stale duplicates before writing — guards against history left by
73
+ // older builds that didn't clean up after themselves.
74
+ await macosDeleteAll(service);
75
+ // -U updates in place if a matching entry exists, otherwise creates one.
76
+ // Belt-and-braces with the drain above; together they keep the keychain
77
+ // from accumulating duplicate generic-password entries.
61
78
  await execFileAsync("security", [
62
79
  "add-generic-password",
80
+ "-U",
63
81
  "-a",
64
82
  "zivis-mcp",
65
83
  "-s",
@@ -68,8 +86,8 @@ async function macosStore(account, value) {
68
86
  value,
69
87
  ]);
70
88
  }
71
- async function macosRead(account) {
72
- const service = getServiceName(account);
89
+ async function macosRead(account, keychainPrefix) {
90
+ const service = getServiceName(account, keychainPrefix);
73
91
  try {
74
92
  const { stdout } = await execFileAsync("security", [
75
93
  "find-generic-password",
@@ -85,29 +103,17 @@ async function macosRead(account) {
85
103
  return null;
86
104
  }
87
105
  }
88
- async function macosDelete(account) {
89
- const service = getServiceName(account);
90
- try {
91
- await execFileAsync("security", [
92
- "delete-generic-password",
93
- "-a",
94
- "zivis-mcp",
95
- "-s",
96
- service,
97
- ]);
98
- }
99
- catch {
100
- // Entry didn't exist
101
- }
106
+ async function macosDelete(account, keychainPrefix) {
107
+ await macosDeleteAll(getServiceName(account, keychainPrefix));
102
108
  }
103
109
  // ── Linux Secret Service ──
104
- async function linuxStore(account, value) {
110
+ async function linuxStore(account, value, keychainPrefix) {
105
111
  const child = execFileAsync("secret-tool", [
106
112
  "store",
107
113
  "--label",
108
114
  `ZIVIS MCP ${account}`,
109
115
  "service",
110
- SERVICE_PREFIX,
116
+ keychainPrefix,
111
117
  "account",
112
118
  account,
113
119
  ]);
@@ -116,12 +122,12 @@ async function linuxStore(account, value) {
116
122
  child.child.stdin?.end();
117
123
  await child;
118
124
  }
119
- async function linuxRead(account) {
125
+ async function linuxRead(account, keychainPrefix) {
120
126
  try {
121
127
  const { stdout } = await execFileAsync("secret-tool", [
122
128
  "lookup",
123
129
  "service",
124
- SERVICE_PREFIX,
130
+ keychainPrefix,
125
131
  "account",
126
132
  account,
127
133
  ]);
@@ -131,12 +137,12 @@ async function linuxRead(account) {
131
137
  return null;
132
138
  }
133
139
  }
134
- async function linuxDelete(account) {
140
+ async function linuxDelete(account, keychainPrefix) {
135
141
  try {
136
142
  await execFileAsync("secret-tool", [
137
143
  "clear",
138
144
  "service",
139
- SERVICE_PREFIX,
145
+ keychainPrefix,
140
146
  "account",
141
147
  account,
142
148
  ]);
@@ -151,7 +157,7 @@ function deriveKey() {
151
157
  const seed = `zivis-mcp:${os.hostname()}:${os.userInfo().username}`;
152
158
  return crypto.createHash("sha256").update(seed).digest();
153
159
  }
154
- async function fileStore(account, value) {
160
+ async function fileStore(account, value, storage) {
155
161
  const key = deriveKey();
156
162
  const iv = crypto.randomBytes(16);
157
163
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -159,13 +165,13 @@ async function fileStore(account, value) {
159
165
  let encrypted = cipher.update(value, "utf8", "hex");
160
166
  encrypted += cipher.final("hex");
161
167
  const data = JSON.stringify({ iv: iv.toString("hex"), data: encrypted });
162
- const dir = getFallbackDir();
168
+ const dir = getFallbackDir(storage.configDir);
163
169
  await fs.mkdir(dir, { recursive: true });
164
- await fs.writeFile(getFallbackPath(account), data, { mode: 0o600 });
170
+ await fs.writeFile(getFallbackPath(account, storage), data, { mode: 0o600 });
165
171
  }
166
- async function fileRead(account) {
172
+ async function fileRead(account, storage) {
167
173
  try {
168
- const raw = await fs.readFile(getFallbackPath(account), "utf8");
174
+ const raw = await fs.readFile(getFallbackPath(account, storage), "utf8");
169
175
  const { iv, data } = JSON.parse(raw);
170
176
  const key = deriveKey();
171
177
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -178,9 +184,9 @@ async function fileRead(account) {
178
184
  return null;
179
185
  }
180
186
  }
181
- async function fileDelete(account) {
187
+ async function fileDelete(account, storage) {
182
188
  try {
183
- await fs.unlink(getFallbackPath(account));
189
+ await fs.unlink(getFallbackPath(account, storage));
184
190
  }
185
191
  catch {
186
192
  // File didn't exist
@@ -211,51 +217,58 @@ async function useKeychain() {
211
217
  return keychainAvailable;
212
218
  }
213
219
  // ── Public API ──
214
- export async function keychainStore(account, value) {
220
+ export async function keychainStore(account, value, storage) {
221
+ const st = resolvedStorage(storage);
215
222
  // Always store to encrypted file as fallback (for environments where keychain CLI
216
223
  // may not be available, e.g., MCP servers spawned by IDEs with different $PATH)
217
- await fileStore(account, value);
224
+ await fileStore(account, value, st);
218
225
  // Also store to native keychain if available
219
226
  if (await useKeychain()) {
220
227
  if (process.platform === "darwin") {
221
- await macosStore(account, value);
228
+ await macosStore(account, value, st.keychainPrefix);
222
229
  }
223
230
  else if (process.platform === "linux") {
224
- await linuxStore(account, value);
231
+ await linuxStore(account, value, st.keychainPrefix);
225
232
  }
226
233
  }
227
234
  }
228
- export async function keychainRead(account) {
235
+ export async function keychainRead(account, storage) {
236
+ const st = resolvedStorage(storage);
229
237
  if (await useKeychain()) {
230
238
  if (process.platform === "darwin")
231
- return macosRead(account);
239
+ return macosRead(account, st.keychainPrefix);
232
240
  if (process.platform === "linux")
233
- return linuxRead(account);
241
+ return linuxRead(account, st.keychainPrefix);
234
242
  }
235
- return fileRead(account);
243
+ return fileRead(account, st);
236
244
  }
237
- export async function keychainDelete(account) {
245
+ export async function keychainDelete(account, storage) {
246
+ const st = resolvedStorage(storage);
247
+ // keychainStore writes to BOTH the native keychain AND the encrypted file
248
+ // fallback (so MCP servers spawned by IDEs can still read credentials when
249
+ // the `security` CLI is unreachable on $PATH). Delete must wipe both, or
250
+ // a stale OAuth/api-key file silently outlives a logout.
251
+ await fileDelete(account, st);
238
252
  if (await useKeychain()) {
239
253
  if (process.platform === "darwin")
240
- return macosDelete(account);
254
+ return macosDelete(account, st.keychainPrefix);
241
255
  if (process.platform === "linux")
242
- return linuxDelete(account);
256
+ return linuxDelete(account, st.keychainPrefix);
243
257
  }
244
- return fileDelete(account);
245
258
  }
246
259
  /**
247
260
  * Delete credentials for a specific session
248
261
  */
249
- export async function keychainClearSession(session) {
250
- await keychainDelete(getSessionAccount(session));
262
+ export async function keychainClearSession(session, storage) {
263
+ await keychainDelete(getSessionAccount(session), storage);
251
264
  }
252
265
  /**
253
266
  * Delete all stored credentials (default OAuth + API key).
254
267
  * Does NOT clear named sessions — use keychainClearSession() for those.
255
268
  */
256
- export async function keychainClearAll() {
257
- await keychainDelete("oauth");
258
- await keychainDelete("apikey");
269
+ export async function keychainClearAll(storage) {
270
+ await keychainDelete("oauth", storage);
271
+ await keychainDelete("apikey", storage);
259
272
  }
260
273
  /**
261
274
  * Returns the storage mechanism name for display purposes