@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 +79 -79
- package/dist/cli.js +30 -40
- package/dist/migrations/001_vnext_foundation.sql +628 -628
- package/package.json +52 -52
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
|
|
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
|
|
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
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
const id
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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
|
|
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);
|