@tryvira/mcp 0.1.11 → 0.1.13

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/README.md CHANGED
@@ -1,79 +1,79 @@
1
- # @tryvira/mcp
2
-
3
- MIT-licensed MCP client/server package. The Vira service, application, and
4
- backend remain proprietary and are governed separately.
5
-
6
- An npx-ready MCP server for Vira's existing scoped authority boundary.
7
-
8
- ## Run it
9
-
10
- ```bash
11
- npx @tryvira/mcp
12
- ```
13
-
14
- The default transport is the official MCP transport. The legacy custom transport is an
15
- explicit migration option only: `VIRA_MCP_PROTOCOL_MODE=legacy npx @tryvira/mcp`.
16
-
17
- The launcher reads environment variables from its parent process. It does not automatically load a
18
- `.env` file. For a local shell, export the variables first; for an MCP client, put them in the
19
- client's `env` configuration:
20
-
21
- ```dotenv
22
- VIRA_API_URL=http://127.0.0.1:4185
23
- VIRA_API_KEY=vira_dev_local.local-development-only-32chars
24
- VIRA_MCP_AUTH_TOKEN=replace-with-a-long-random-mcp-token
25
- VIRA_ORGANIZATION_ID=local-org
26
- VIRA_PROJECT_ID=local-project
27
- VIRA_ENVIRONMENT_ID=local
28
- ```
29
-
30
- For example, an MCP client configuration uses the same names:
31
-
32
- ```json
33
- {"env":{"VIRA_API_URL":"https://api.example","VIRA_API_KEY":"<customer-api-key>","VIRA_MCP_AUTH_TOKEN":"<mcp-token>","VIRA_ORGANIZATION_ID":"<org>","VIRA_PROJECT_ID":"<project>","VIRA_ENVIRONMENT_ID":"<environment>"}}
34
- ```
35
-
36
- The default launcher uses MCP stdio. To run the optional HTTP transport explicitly, use
37
- `npx @tryvira/mcp --http`; it binds to `127.0.0.1` by default and requires `Authorization: Bearer
38
- <VIRA_MCP_AUTH_TOKEN>`. Effectful MCP tools are denied by default. Local-only effectful
39
- execution requires the explicit `VIRA_MCP_ALLOW_EFFECTFUL=true` opt-in; production never
40
- accepts that shortcut. Set `VIRA_MCP_BIND_PUBLIC=true` only when an authenticated reverse
41
- proxy or equivalent network boundary is present.
42
-
43
- Effectful MCP tools remain authority-gated; the launcher does not create a second execution or telemetry path.
44
-
45
- ## What a client can discover
46
-
47
- After `initialize`, call `tools/list`. Read-only tools such as `resolve_semantic` may return
48
- semantic candidates. Effectful tools such as `resolve_runtime`, pack installation/release
49
- and Strategy execution call the configured authority endpoint and can be denied by scope,
50
- role, release state or policy.
51
-
52
- ## Minimal MCP client configuration
53
-
54
- ```json
55
- {
56
- "mcpServers": {
57
- "vira": {
58
- "command": "npx",
59
- "args": ["-y", "@tryvira/mcp"],
60
- "env": {
61
- "VIRA_API_URL": "https://api.example.invalid",
62
- "VIRA_API_KEY": "<customer-api-key>",
63
- "VIRA_MCP_AUTH_TOKEN": "<mcp-auth-token>",
64
- "VIRA_MCP_AUTHORITY_URL": "https://api.example.invalid/v1/mcp/authorize",
65
- "VIRA_ORGANIZATION_ID": "<organization>",
66
- "VIRA_PROJECT_ID": "<project>",
67
- "VIRA_ENVIRONMENT_ID": "<environment>"
68
- }
69
- }
70
- }
71
- }
72
- ```
73
-
74
- Never paste real credentials into a checked-in client configuration. For local-only effectful
75
- testing, use the local stack and its documented development credentials; the production
76
- authority path does not accept the local bypass.
77
-
78
- See [`docs/MCP_GUIDE.md`](../../docs/MCP_GUIDE.md) for JSON-RPC discovery, tool examples and
79
- authority/error behavior.
1
+ # @tryvira/mcp
2
+
3
+ MIT-licensed MCP client/server package. The Vira service, application, and
4
+ backend remain proprietary and are governed separately.
5
+
6
+ An npx-ready MCP server for Vira's existing scoped authority boundary.
7
+
8
+ ## Run it
9
+
10
+ ```bash
11
+ npx @tryvira/mcp
12
+ ```
13
+
14
+ The default transport is the official MCP transport. The legacy custom transport is an
15
+ explicit migration option only: `VIRA_MCP_PROTOCOL_MODE=legacy npx @tryvira/mcp`.
16
+
17
+ The launcher reads environment variables from its parent process. It does not automatically load a
18
+ `.env` file. For a local shell, export the variables first; for an MCP client, put them in the
19
+ client's `env` configuration:
20
+
21
+ ```dotenv
22
+ VIRA_API_URL=http://127.0.0.1:4185
23
+ VIRA_API_KEY=vira_dev_local.local-development-only-32chars
24
+ VIRA_MCP_AUTH_TOKEN=replace-with-a-long-random-mcp-token
25
+ VIRA_ORGANIZATION_ID=local-org
26
+ VIRA_PROJECT_ID=local-project
27
+ VIRA_ENVIRONMENT_ID=local
28
+ ```
29
+
30
+ For example, an MCP client configuration uses the same names:
31
+
32
+ ```json
33
+ {"env":{"VIRA_API_URL":"https://api.example","VIRA_API_KEY":"<customer-api-key>","VIRA_MCP_AUTH_TOKEN":"<mcp-token>","VIRA_ORGANIZATION_ID":"<org>","VIRA_PROJECT_ID":"<project>","VIRA_ENVIRONMENT_ID":"<environment>"}}
34
+ ```
35
+
36
+ The default launcher uses MCP stdio. To run the optional HTTP transport explicitly, use
37
+ `npx @tryvira/mcp --http`; it binds to `127.0.0.1` by default and requires `Authorization: Bearer
38
+ <VIRA_MCP_AUTH_TOKEN>`. Effectful MCP tools are denied by default. Local-only effectful
39
+ execution requires the explicit `VIRA_MCP_ALLOW_EFFECTFUL=true` opt-in; production never
40
+ accepts that shortcut. Set `VIRA_MCP_BIND_PUBLIC=true` only when an authenticated reverse
41
+ proxy or equivalent network boundary is present.
42
+
43
+ Effectful MCP tools remain authority-gated; the launcher does not create a second execution or telemetry path.
44
+
45
+ ## What a client can discover
46
+
47
+ After `initialize`, call `tools/list`. Read-only tools such as `resolve_semantic` may return
48
+ semantic candidates. Effectful tools such as `resolve_runtime`, pack installation/release
49
+ and Strategy execution call the configured authority endpoint and can be denied by scope,
50
+ role, release state or policy.
51
+
52
+ ## Minimal MCP client configuration
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "vira": {
58
+ "command": "npx",
59
+ "args": ["-y", "@tryvira/mcp"],
60
+ "env": {
61
+ "VIRA_API_URL": "https://api.example.invalid",
62
+ "VIRA_API_KEY": "<customer-api-key>",
63
+ "VIRA_MCP_AUTH_TOKEN": "<mcp-auth-token>",
64
+ "VIRA_MCP_AUTHORITY_URL": "https://api.example.invalid/v1/mcp/authorize",
65
+ "VIRA_ORGANIZATION_ID": "<organization>",
66
+ "VIRA_PROJECT_ID": "<project>",
67
+ "VIRA_ENVIRONMENT_ID": "<environment>"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ Never paste real credentials into a checked-in client configuration. For local-only effectful
75
+ testing, use the local stack and its documented development credentials; the production
76
+ authority path does not accept the local bypass.
77
+
78
+ See [`docs/MCP_GUIDE.md`](../../docs/MCP_GUIDE.md) for JSON-RPC discovery, tool examples and
79
+ authority/error behavior.
package/dist/cli.js CHANGED
@@ -969,7 +969,7 @@ var init_vira_server = __esm({
969
969
  { name: "submit_teach_evidence", description: "Submit scoped evidence to the Teach lifecycle.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, id: { type: "string" }, source: { type: "string" }, content: { type: "string" }, consent: { type: "boolean" } }, required: ["organizationId", "projectId", "environmentId", "id", "content", "consent"] } },
970
970
  { name: "create_teach_proposal", description: "Create a reviewed Teach proposal from admitted evidence.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, evidenceId: { type: "string" }, controlPointId: { type: "string" }, summary: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "evidenceId", "controlPointId", "summary"] } },
971
971
  { name: "transition_teach_proposal", description: "Review, evaluate, or promote a Teach proposal.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, proposalId: { type: "string" }, transition: { type: "string" }, decision: { type: "string" }, score: { type: "number" }, evaluationId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "proposalId", "transition"] } },
972
- { name: "create_release", description: "Create a scoped release after evaluation and approval.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, releaseId: { type: "string" }, artifactId: { type: "string" }, evaluationId: { type: "string" } }, required: ["organizationId", "projectId", "environmentId", "artifactId"] } },
972
+ { name: "create_release", description: "Create a scoped release using either provider-backed evaluation or explicit manual approval with step-up evidence.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, releaseId: { type: "string" }, artifactId: { type: "string" }, evaluationId: { type: "string" }, manualApproval: { type: "object", properties: { reason: { type: "string" } }, required: ["reason"] }, stepUp: { type: "object", properties: { id: { type: "string" }, assertion: { type: "string" }, purpose: { type: "string" } }, required: ["id", "assertion", "purpose"] } }, required: ["organizationId", "projectId", "environmentId", "artifactId"] } },
973
973
  { name: "resolve_semantic", description: "Resolve input through the governed semantic runtime.", inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, input: { type: "string" }, sessionId: { type: "string" }, profile: { type: "object" }, selection: { type: "object" }, candidates: { type: "array" }, timeoutMs: { type: "number" } }, required: ["organizationId", "projectId", "environmentId", "input"] } },
974
974
  { name: "resolve_runtime", description: "Resolve a persisted BehaviorPoint through the governed runtime boundary. behaviorPoint may be omitted when semanticContext supplies the selected canonical route.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, behaviorPoint: { type: "string" }, executionBoundary: { type: "object" }, semanticContext: { type: "object" } }, required: ["organizationId", "projectId", "environmentId"] } },
975
975
  { name: "create_signal_discovery_proposal", description: "Propose a discovered signal for scoped human review.", effectful: true, inputSchema: { type: "object", properties: { organizationId: { type: "string" }, projectId: { type: "string" }, environmentId: { type: "string" }, signalStableKey: { type: "string" }, evidenceEventIds: { type: "array", items: { type: "string" } }, suggestedProducerType: { type: "string" }, suggestedProducerKey: { type: "string" }, confidence: { type: "number" } }, required: ["organizationId", "projectId", "environmentId", "signalStableKey", "evidenceEventIds"] } },
@@ -1092,8 +1092,10 @@ function createPostgresDatabase(input) {
1092
1092
  }, transaction: async (work) => {
1093
1093
  if (typeof work !== "function") throw new Error("DATABASE_WORK_REQUIRED");
1094
1094
  const client = await input.pool.connect();
1095
+ const timeoutMs = Math.max(1e3, Number(process.env.VIRA_DB_STATEMENT_TIMEOUT_MS ?? 6e5));
1095
1096
  try {
1096
1097
  await client.query("BEGIN");
1098
+ await client.query(`SET LOCAL statement_timeout = ${timeoutMs}`);
1097
1099
  const result = await work(client);
1098
1100
  await client.query("COMMIT");
1099
1101
  return result;
@@ -1111,7 +1113,8 @@ function createPostgresDatabase(input) {
1111
1113
  function createPostgresPool(input = {}) {
1112
1114
  const connectionString = input.databaseUrl ?? process.env.DATABASE_URL;
1113
1115
  if (!connectionString?.startsWith("postgres")) throw new Error("POSTGRES_DATABASE_URL_REQUIRED");
1114
- const config = { connectionString, max: input.max ?? 10, idleTimeoutMillis: 3e4, connectionTimeoutMillis: 1e4, ...input.ssl ? { ssl: { rejectUnauthorized: false } } : {} };
1116
+ const statementTimeoutMs = Math.max(1e3, Number(process.env.VIRA_DB_STATEMENT_TIMEOUT_MS ?? 6e5));
1117
+ const config = { connectionString, max: input.max ?? 10, idleTimeoutMillis: 3e4, connectionTimeoutMillis: 1e4, statement_timeout: statementTimeoutMs, ...input.ssl ? { ssl: { rejectUnauthorized: false } } : {} };
1115
1118
  const pool = new Pool(config);
1116
1119
  return { pool, close: () => pool.end() };
1117
1120
  }
@@ -1277,25 +1280,28 @@ var init_postgres_migrations = __esm({
1277
1280
  });
1278
1281
 
1279
1282
  // ../database/src/postgres-runtime.ts
1280
- import { readFile } from "node:fs/promises";
1283
+ import { readFile, readdir } from "node:fs/promises";
1281
1284
  import { dirname, resolve } from "node:path";
1282
1285
  import { fileURLToPath } from "node:url";
1286
+ async function defaultMigrationPaths() {
1287
+ const migrationDirectory = resolve(here, "../migrations");
1288
+ const entries = await readdir(migrationDirectory, { withFileTypes: true });
1289
+ return entries.filter((entry) => entry.isFile() && /^\d+_.+\.sql$/i.test(entry.name)).map((entry) => resolve(migrationDirectory, entry.name)).sort((left, right) => left.localeCompare(right, "en"));
1290
+ }
1283
1291
  async function openPostgresRuntime(options = {}) {
1284
1292
  const { pool, close } = createPostgresPool({ databaseUrl: options.databaseUrl, max: options.maxConnections, ssl: options.ssl });
1285
1293
  const database = createPostgresDatabase({ pool, databaseUrl: options.databaseUrl });
1286
1294
  try {
1287
- const migrations = [];
1288
- const migrationPaths = options.migrationPaths ?? [...defaultMigrationPaths, resolve(here, "../migrations/006_postgres_control_plane_repositories.sql")];
1289
- for (const path of migrationPaths) {
1290
- const id = path.split(/[\\/]/).pop().replace(/\.sql$/i, "");
1291
- const legacyChecksums = id === "001_vnext_foundation" ? ["d1a175e1b8af20bcf9bc650fb299626513532e4d18da429569251eff69475427"] : void 0;
1292
- migrations.push({ id, sql: await readFile(path, "utf8"), ...legacyChecksums ? { legacyChecksums } : {} });
1293
- }
1294
- const client = await pool.connect();
1295
- try {
1296
- await applyPostgresMigrations(client, migrations);
1297
- } finally {
1298
- client.release();
1295
+ if (!options.skipMigrations && process.env.VIRA_WORKER_SKIP_MIGRATIONS !== "true") {
1296
+ const migrations = [];
1297
+ const migrationPaths = options.migrationPaths ?? await defaultMigrationPaths();
1298
+ for (const path of migrationPaths) migrations.push({ id: path.split(/[\\/]/).pop().replace(/\.sql$/i, ""), sql: await readFile(path, "utf8") });
1299
+ const client = await pool.connect();
1300
+ try {
1301
+ await applyPostgresMigrations(client, migrations);
1302
+ } finally {
1303
+ client.release();
1304
+ }
1299
1305
  }
1300
1306
  return { ...database, close };
1301
1307
  } catch (error) {
@@ -1303,35 +1309,13 @@ async function openPostgresRuntime(options = {}) {
1303
1309
  throw error;
1304
1310
  }
1305
1311
  }
1306
- var here, defaultMigrationPaths;
1312
+ var here;
1307
1313
  var init_postgres_runtime = __esm({
1308
1314
  "../database/src/postgres-runtime.ts"() {
1309
1315
  "use strict";
1310
1316
  init_postgres_adapter();
1311
1317
  init_postgres_migrations();
1312
1318
  here = dirname(fileURLToPath(import.meta.url));
1313
- defaultMigrationPaths = [
1314
- resolve(here, "../migrations/001_vnext_foundation.sql"),
1315
- resolve(here, "../migrations/002_session_handoff_approvals.sql"),
1316
- resolve(here, "../migrations/003_mfa_totp.sql"),
1317
- resolve(here, "../migrations/004_behavior_strategy_execution.sql"),
1318
- resolve(here, "../migrations/005_postgres_runtime_completeness.sql"),
1319
- resolve(here, "../migrations/007_postgres_native_support_repositories.sql"),
1320
- resolve(here, "../migrations/008_postgres_identity_session_repositories.sql"),
1321
- resolve(here, "../migrations/009_postgres_runtime_repair.sql"),
1322
- resolve(here, "../migrations/010_postgres_receipt_lineage_repair.sql"),
1323
- resolve(here, "../migrations/011_postgres_semantic_activation_receipts.sql"),
1324
- resolve(here, "../migrations/012_strategy_release_lineage.sql"),
1325
- resolve(here, "../migrations/013_strategy_variant_definition.sql"),
1326
- resolve(here, "../migrations/014_strategy_step_recovery_action.sql"),
1327
- resolve(here, "../migrations/015_strategy_total_recoveries.sql"),
1328
- resolve(here, "../migrations/016_postgres_studio_tenant_auth.sql"),
1329
- resolve(here, "../migrations/017_postgres_security_advisor_hardening.sql"),
1330
- resolve(here, "../migrations/018_postgres_foreign_key_indexes.sql"),
1331
- resolve(here, "../migrations/019_postgres_explicit_public_deny_policies.sql"),
1332
- resolve(here, "../migrations/020_semantic_registry_task_index.sql"),
1333
- resolve(here, "../migrations/021_unique_ai_system_names.sql")
1334
- ];
1335
1319
  }
1336
1320
  });
1337
1321
 
@@ -1542,7 +1526,10 @@ async function openProductionSqlite({ databaseUrl = process.env.DATABASE_URL } =
1542
1526
  environment_id TEXT,
1543
1527
  authority_digest TEXT,
1544
1528
  artifact_id TEXT,
1545
- promotion_version INTEGER
1529
+ promotion_version INTEGER,
1530
+ approval_mode TEXT NOT NULL DEFAULT 'EVALUATED',
1531
+ approval_reason TEXT,
1532
+ approval_receipt_digest TEXT
1546
1533
  )`);
1547
1534
  for (const statement of [
1548
1535
  "ALTER TABLE release_manifests ADD COLUMN authority_digest TEXT",
@@ -1553,7 +1540,10 @@ async function openProductionSqlite({ databaseUrl = process.env.DATABASE_URL } =
1553
1540
  "ALTER TABLE release_manifests ADD COLUMN strategy_digest TEXT",
1554
1541
  "ALTER TABLE release_manifests ADD COLUMN strategy_artifact_digest TEXT",
1555
1542
  "ALTER TABLE release_manifests ADD COLUMN program_digest TEXT",
1556
- "ALTER TABLE release_manifests ADD COLUMN evaluation_digest TEXT"
1543
+ "ALTER TABLE release_manifests ADD COLUMN evaluation_digest TEXT",
1544
+ "ALTER TABLE release_manifests ADD COLUMN approval_mode TEXT NOT NULL DEFAULT 'EVALUATED'",
1545
+ "ALTER TABLE release_manifests ADD COLUMN approval_reason TEXT",
1546
+ "ALTER TABLE release_manifests ADD COLUMN approval_receipt_digest TEXT"
1557
1547
  ]) {
1558
1548
  try {
1559
1549
  database.run(statement);