@zivis/mcp 0.1.17 → 0.1.19

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.
@@ -5,4 +5,11 @@ export { keychainStore, keychainRead, keychainDelete } from "./keychain.js";
5
5
  export { loadTokens } from "./token-refresh.js";
6
6
  export { loadSessionIndex, saveSessionEntry, removeSessionEntry, listSessions } from "./sessions.js";
7
7
  export declare function resolveAuth(config?: ZivisConfig, forceMethod?: AuthMethod): Promise<AuthCredentials>;
8
+ export interface ApiKeyOrgProbe {
9
+ ok: boolean;
10
+ workosOrgId: string | null;
11
+ orgName?: string;
12
+ error?: string;
13
+ }
14
+ export declare function probeApiKeyOrg(config: ZivisConfig, apiKey: string): Promise<ApiKeyOrgProbe>;
8
15
  export declare function getAuthDescription(config?: ZivisConfig): Promise<string>;
@@ -39,6 +39,38 @@ export async function resolveAuth(config = DEFAULT_CONFIG, forceMethod) {
39
39
  }
40
40
  return { method: "none" };
41
41
  }
42
+ export async function probeApiKeyOrg(config, apiKey) {
43
+ let response;
44
+ try {
45
+ response = await fetch(`${config.apiBaseUrl}/api/mcp/session`, {
46
+ headers: {
47
+ "X-API-Key": apiKey,
48
+ "User-Agent": "@zivis/mcp",
49
+ },
50
+ });
51
+ }
52
+ catch (err) {
53
+ return {
54
+ ok: false,
55
+ workosOrgId: null,
56
+ error: err instanceof Error ? err.message : String(err),
57
+ };
58
+ }
59
+ if (!response.ok) {
60
+ const body = await response.text().catch(() => "");
61
+ return {
62
+ ok: false,
63
+ workosOrgId: null,
64
+ error: `HTTP ${response.status}: ${body.slice(0, 200)}`,
65
+ };
66
+ }
67
+ const payload = (await response.json());
68
+ return {
69
+ ok: true,
70
+ workosOrgId: payload.org?.workosOrgId ?? null,
71
+ orgName: payload.org?.name,
72
+ };
73
+ }
42
74
  export async function getAuthDescription(config = DEFAULT_CONFIG) {
43
75
  const creds = await resolveAuth(config);
44
76
  switch (creds.method) {
@@ -3,7 +3,7 @@
3
3
  "pack_id": "zivis-public",
4
4
  "pack_name": "ZIVIS Public Pattern Pack",
5
5
  "version": "0.2.0",
6
- "built_at": "2026-09-08T13:37:26.490Z",
6
+ "built_at": "2026-09-10T20:35:21.881Z",
7
7
  "tier": "customer_safe",
8
8
  "description": "ZIVIS-curated public pattern pack — capsules + inference prompts evaluated locally on the user's machine.",
9
9
  "capsules": [
@@ -3,7 +3,9 @@ export declare function detectProjectBinding(cwd: string): {
3
3
  binding: ProjectBinding;
4
4
  filePath: string;
5
5
  } | null;
6
- export declare function sanitizeBindingOverrides(binding: ProjectBinding, filePath: string): Partial<Pick<ZivisConfig, "apiBaseUrl" | "oauthApiUrl" | "oauthClientId">>;
6
+ type BindingOverrides = Partial<Pick<ZivisConfig, "apiBaseUrl" | "oauthApiUrl" | "oauthClientId" | "keychainPrefix" | "configDir">>;
7
+ export declare function sanitizeBindingOverrides(binding: ProjectBinding, filePath: string): BindingOverrides;
7
8
  export declare function resolveConfigFromBinding(cwd?: string): ZivisConfig;
8
9
  export declare function resolveMcpSessionForWorkspace(incoming: ZivisConfig, workspacePath: string, detected: ReturnType<typeof detectProjectBinding>, fromBinding: ZivisConfig): string;
9
10
  export declare function validateProjectBinding(binding: ProjectBinding, filePath: string, config: ZivisConfig): void;
11
+ export {};
@@ -27,10 +27,13 @@ export function detectProjectBinding(cwd) {
27
27
  }
28
28
  return null;
29
29
  }
30
- const TRUSTED_BINDING_HOST = "zivis.ai";
31
- const TRUSTED_BINDING_HOST_SUFFIX = ".zivis.ai";
30
+ const TRUSTED_API_HOST_SUFFIXES = [".zivis.ai"];
31
+ const TRUSTED_OAUTH_HOST_SUFFIXES = [".zivis.ai", ".authkit.app"];
32
+ const TRUSTED_APEX_HOSTS = new Set(["zivis.ai"]);
32
33
  const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
33
- function isTrustedBindingUrl(raw, envAnchor) {
34
+ const KEYCHAIN_PREFIX_RE = /^ai\.zivis(\.[a-z0-9-]{1,32})?$/;
35
+ const CONFIG_DIR_RE = /^zivis(-[a-z0-9]{1,32})?$/;
36
+ function isTrustedBindingUrl(raw, envAnchor, suffixes) {
34
37
  if (envAnchor && raw === envAnchor)
35
38
  return true;
36
39
  let parsed;
@@ -45,7 +48,9 @@ function isTrustedBindingUrl(raw, envAnchor) {
45
48
  return true;
46
49
  if (parsed.protocol !== "https:")
47
50
  return false;
48
- return host === TRUSTED_BINDING_HOST || host.endsWith(TRUSTED_BINDING_HOST_SUFFIX);
51
+ if (TRUSTED_APEX_HOSTS.has(host))
52
+ return true;
53
+ return suffixes.some((s) => host.endsWith(s));
49
54
  }
50
55
  function describeHost(raw) {
51
56
  try {
@@ -57,22 +62,34 @@ function describeHost(raw) {
57
62
  }
58
63
  export function sanitizeBindingOverrides(binding, filePath) {
59
64
  const out = {};
60
- const gate = (field, value, envName) => {
65
+ const warn = (field, shown, rule) => {
66
+ console.error(`[zivis] WARNING: ignoring untrusted "${field}" in ${filePath} (${shown}). ` +
67
+ `A repo binding may only set ${field} to ${rule}. Falling back to the configured/default value.`);
68
+ };
69
+ const gateUrl = (field, value, envName, suffixes) => {
70
+ if (!value)
71
+ return;
72
+ if (isTrustedBindingUrl(value, process.env[envName], suffixes)) {
73
+ out[field] = value;
74
+ return;
75
+ }
76
+ warn(field, `host: ${describeHost(value)}`, `https://zivis.ai, https://*${suffixes.join(" / https://*")}, localhost, or the exact value of ${envName}`);
77
+ };
78
+ const gateToken = (field, value, envName, re, ruleText) => {
61
79
  if (!value)
62
80
  return;
63
- if (isTrustedBindingUrl(value, process.env[envName])) {
81
+ if (value === process.env[envName] || re.test(value)) {
64
82
  out[field] = value;
65
83
  return;
66
84
  }
67
- console.error(`[zivis] WARNING: ignoring untrusted "${field}" in ${filePath} ` +
68
- `(host: ${describeHost(value)}). A repo binding may only point at ` +
69
- `https://*.zivis.ai, localhost, or the exact value of ${envName}. ` +
70
- `Falling back to the configured/default host.`);
85
+ warn(field, `value: ${JSON.stringify(value)}`, `${ruleText} or the exact value of ${envName}`);
71
86
  };
72
- gate("apiBaseUrl", binding.apiBaseUrl, "ZIVIS_API_URL");
73
- gate("oauthApiUrl", binding.oauthApiUrl, "ZIVIS_OAUTH_API_URL");
87
+ gateUrl("apiBaseUrl", binding.apiBaseUrl, "ZIVIS_API_URL", TRUSTED_API_HOST_SUFFIXES);
88
+ gateUrl("oauthApiUrl", binding.oauthApiUrl, "ZIVIS_OAUTH_API_URL", TRUSTED_OAUTH_HOST_SUFFIXES);
74
89
  if (binding.oauthClientId)
75
90
  out.oauthClientId = binding.oauthClientId;
91
+ gateToken("keychainPrefix", binding.keychainPrefix, "ZIVIS_KEYCHAIN_PREFIX", KEYCHAIN_PREFIX_RE, "a name inside the ai.zivis.* namespace (e.g. ai.zivis.staging)");
92
+ gateToken("configDir", binding.configDir, "ZIVIS_CONFIG_DIR", CONFIG_DIR_RE, "a single directory name of the form zivis or zivis-<env> (e.g. zivis-staging)");
76
93
  return out;
77
94
  }
78
95
  export function resolveConfigFromBinding(cwd) {
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { ApiClient } from "./api-client.js";
4
- import { resolveAuth } from "./auth/index.js";
4
+ import { probeApiKeyOrg, resolveAuth } from "./auth/index.js";
5
5
  import { detectProjectBinding, resolveConfigFromBinding, resolveMcpSessionForWorkspace, validateProjectBinding, } from "./project-binding.js";
6
6
  import { DEFAULT_CONFIG } from "./types.js";
7
7
  async function validateApiKeyMatchesBinding(binding, config) {
@@ -15,26 +15,12 @@ async function validateApiKeyMatchesBinding(binding, config) {
15
15
  console.error("[zivis] INTERNAL: org verification skipped — no API key (session gate should have prevented this)");
16
16
  return;
17
17
  }
18
- let response;
19
- try {
20
- response = await fetch(`${config.apiBaseUrl}/api/mcp/session`, {
21
- headers: {
22
- "X-API-Key": creds.apiKey,
23
- "User-Agent": "@zivis/mcp",
24
- },
25
- });
26
- }
27
- catch (err) {
28
- console.error(`[zivis] WARNING: Could not reach API to verify org (${err instanceof Error ? err.message : err})`);
29
- return;
30
- }
31
- if (!response.ok) {
32
- const body = await response.text().catch(() => "");
33
- console.error(`[zivis] WARNING: API key org probe failed (${response.status}): ${body.slice(0, 200)}`);
18
+ const probe = await probeApiKeyOrg(config, creds.apiKey);
19
+ if (!probe.ok) {
20
+ console.error(`[zivis] WARNING: API key org probe failed: ${probe.error}`);
34
21
  return;
35
22
  }
36
- const payload = (await response.json());
37
- const resolved = payload.org?.workosOrgId;
23
+ const resolved = probe.workosOrgId;
38
24
  if (resolved !== binding.workosOrgId) {
39
25
  const orgLabel = binding.orgName ? ` (${binding.orgName})` : "";
40
26
  console.error("[zivis] SECURITY ERROR: API key org does not match project binding!");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zivis/mcp",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "ZIVIS MCP server — threat modeling, security scans, and AI red team tools for IDE integration",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://zivis.ai",
@@ -43,7 +43,6 @@
43
43
  "build:pattern-pack": "tsx ../scripts/build-pattern-pack.ts",
44
44
  "build": "tsc -p tsconfig.build.json && pnpm run build:pattern-pack && node scripts/ensure-symlink.cjs",
45
45
  "prepublishOnly": "rm -rf dist && pnpm run build",
46
- "postpublish": "node -e \"const p=require('./package.json'); require('child_process').execSync('npm dist-tag add '+p.name+'@'+p.version+' latest',{stdio:'inherit'});\"",
47
46
  "dev": "tsc --watch",
48
47
  "start": "node dist/index.js",
49
48
  "test": "vitest run",