@seekrit/mcp 0.5.0 → 0.6.0
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 +75 -8
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -664,7 +664,7 @@ function isServiceToken(value) {
|
|
|
664
664
|
}
|
|
665
665
|
//#endregion
|
|
666
666
|
//#region ../cli/package.json
|
|
667
|
-
var version$1 = "0.
|
|
667
|
+
var version$1 = "0.24.0";
|
|
668
668
|
const PROJECT_FILE = "seekrit.json";
|
|
669
669
|
function globalConfigPath() {
|
|
670
670
|
return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "seekrit", "config.json");
|
|
@@ -886,6 +886,19 @@ var SeekritClient = class {
|
|
|
886
886
|
deleteSecret(orgId, envId, name) {
|
|
887
887
|
return this.request("DELETE", `/v1/orgs/${orgId}/envs/${envId}/secrets/${name}`);
|
|
888
888
|
}
|
|
889
|
+
/** A secret's append-only history, newest version first. */
|
|
890
|
+
listSecretVersions(orgId, envId, name, query = {}) {
|
|
891
|
+
const qs = query.limit === void 0 ? "" : `?limit=${query.limit}`;
|
|
892
|
+
return this.request("GET", `/v1/orgs/${orgId}/envs/${envId}/secrets/${name}/versions${qs}`);
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Roll a secret back to an earlier version. Keyless — the server copies the
|
|
896
|
+
* ciphertext it already stores, so this appends a new version rather than
|
|
897
|
+
* rewinding, and needs no DEK on the caller's side.
|
|
898
|
+
*/
|
|
899
|
+
restoreSecret(orgId, envId, name, version) {
|
|
900
|
+
return this.request("POST", `/v1/orgs/${orgId}/envs/${envId}/secrets/${name}/restore`, { version });
|
|
901
|
+
}
|
|
889
902
|
/** The calling principal's wrapped DEK for this environment. */
|
|
890
903
|
getMyEnvKey(orgId, envId) {
|
|
891
904
|
return this.request("GET", `/v1/orgs/${orgId}/envs/${envId}/key`);
|
|
@@ -1429,14 +1442,23 @@ function parseDotenv(content) {
|
|
|
1429
1442
|
}
|
|
1430
1443
|
return out;
|
|
1431
1444
|
}
|
|
1432
|
-
//#endregion
|
|
1433
|
-
//#region ../cli/src/secrets.ts
|
|
1434
1445
|
/** Fetch + decrypt every secret in a single environment. */
|
|
1435
1446
|
async function fetchDecryptedSecrets(ctx, orgId, envId) {
|
|
1436
1447
|
const [dek, { secrets }] = await Promise.all([getDek(ctx, orgId, envId), ctx.client.listSecrets(orgId, envId)]);
|
|
1437
1448
|
const entries = await Promise.all(secrets.map(async (secret) => [secret.name, await decryptSecret(dek, secret.ciphertext, secretAad(envId, secret.name))]));
|
|
1438
1449
|
return Object.fromEntries(entries);
|
|
1439
1450
|
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Decrypt one historical version of a secret. Ciphertext is bound to
|
|
1453
|
+
* `(envId, name)` as AAD and neither changes across versions, so an old blob
|
|
1454
|
+
* opens with the environment's current data key — no special handling needed.
|
|
1455
|
+
*/
|
|
1456
|
+
async function fetchDecryptedVersion(ctx, orgId, envId, name, version) {
|
|
1457
|
+
const [dek, { versions }] = await Promise.all([getDek(ctx, orgId, envId), ctx.client.listSecretVersions(orgId, envId, name, { limit: 200 })]);
|
|
1458
|
+
const row = versions.find((v) => v.version === version);
|
|
1459
|
+
if (!row) fail(`${name} has no version ${version} in its ${versions.length} newest versions`);
|
|
1460
|
+
return decryptSecret(dek, row.ciphertext, secretAad(envId, name));
|
|
1461
|
+
}
|
|
1440
1462
|
async function encryptAndSetSecret(ctx, orgId, envId, name, value) {
|
|
1441
1463
|
const ciphertext = await encryptSecret(await getDek(ctx, orgId, envId), value, secretAad(envId, name));
|
|
1442
1464
|
await ctx.client.setSecret(orgId, envId, name, ciphertext);
|
|
@@ -1563,6 +1585,8 @@ function getStartedText() {
|
|
|
1563
1585
|
"## 3. Store secrets",
|
|
1564
1586
|
"- `set_secret` — encrypts a value locally and stores the ciphertext.",
|
|
1565
1587
|
"- `list_secrets` — confirm names + versions (never returns values).",
|
|
1588
|
+
"- `list_secret_versions` + `restore_secret` — undo a bad write by rolling",
|
|
1589
|
+
" back to an earlier version (keyless, and history is append-only).",
|
|
1566
1590
|
"",
|
|
1567
1591
|
"## 4. Use secrets without exposing them",
|
|
1568
1592
|
"- `run_command -- <cmd>` — inject secrets into a subprocess; prefer this.",
|
|
@@ -2075,10 +2099,11 @@ async function runMcpServer(options = {}) {
|
|
|
2075
2099
|
name: o.name
|
|
2076
2100
|
};
|
|
2077
2101
|
});
|
|
2078
|
-
tool("get_secret", "Return one secret. By default only reports presence + version; pass reveal:true to decrypt the plaintext into this response (avoid unless the value is actually needed — prefer run_command).", {
|
|
2102
|
+
tool("get_secret", "Return one secret. By default only reports presence + version; pass reveal:true to decrypt the plaintext into this response (avoid unless the value is actually needed — prefer run_command). Pass `version` to read an earlier version instead of the current one.", {
|
|
2079
2103
|
...targetShape,
|
|
2080
2104
|
name: z.string(),
|
|
2081
|
-
reveal: z.boolean().optional()
|
|
2105
|
+
reveal: z.boolean().optional(),
|
|
2106
|
+
version: z.number().int().positive().optional().describe("an earlier version from list_secret_versions (default: current)")
|
|
2082
2107
|
}, async (o) => {
|
|
2083
2108
|
const ctx = getCtx();
|
|
2084
2109
|
const { orgId, envId } = await resolveTargetEnv(ctx, o);
|
|
@@ -2088,11 +2113,20 @@ async function runMcpServer(options = {}) {
|
|
|
2088
2113
|
if (!row) throw new Error(`no secret named ${o.name}`);
|
|
2089
2114
|
return {
|
|
2090
2115
|
name: row.name,
|
|
2091
|
-
version: row.version,
|
|
2116
|
+
version: o.version ?? row.version,
|
|
2092
2117
|
revealed: false
|
|
2093
2118
|
};
|
|
2094
2119
|
}
|
|
2095
2120
|
ensureDecryptable(ctx);
|
|
2121
|
+
if (o.version !== void 0) {
|
|
2122
|
+
const value = await fetchDecryptedVersion(ctx, orgId, envId, o.name, o.version);
|
|
2123
|
+
return {
|
|
2124
|
+
name: o.name,
|
|
2125
|
+
version: o.version,
|
|
2126
|
+
value,
|
|
2127
|
+
revealed: true
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2096
2130
|
const values = await fetchDecryptedSecrets(ctx, orgId, envId);
|
|
2097
2131
|
if (!(o.name in values)) throw new Error(`no secret named ${o.name}`);
|
|
2098
2132
|
return {
|
|
@@ -2101,6 +2135,39 @@ async function runMcpServer(options = {}) {
|
|
|
2101
2135
|
revealed: true
|
|
2102
2136
|
};
|
|
2103
2137
|
});
|
|
2138
|
+
tool("list_secret_versions", "List a secret's version history: who wrote each version, when, and which ones were restores. Never returns values — pair it with restore_secret to roll back, or get_secret(version, reveal:true) to inspect one.", {
|
|
2139
|
+
...targetShape,
|
|
2140
|
+
name: z.string(),
|
|
2141
|
+
limit: z.number().int().min(1).max(200).optional().describe("default 20")
|
|
2142
|
+
}, async (o) => {
|
|
2143
|
+
const ctx = getCtx();
|
|
2144
|
+
const { orgId, envId } = await resolveTargetEnv(ctx, o);
|
|
2145
|
+
const { versions, currentVersion } = await ctx.client.listSecretVersions(orgId, envId, o.name, { limit: o.limit ?? 20 });
|
|
2146
|
+
return {
|
|
2147
|
+
currentVersion,
|
|
2148
|
+
versions: versions.map((v) => ({
|
|
2149
|
+
version: v.version,
|
|
2150
|
+
createdAt: v.createdAt,
|
|
2151
|
+
createdBy: `${v.createdByType}:${v.createdById}`,
|
|
2152
|
+
restoredFromVersion: v.restoredFromVersion
|
|
2153
|
+
}))
|
|
2154
|
+
};
|
|
2155
|
+
});
|
|
2156
|
+
tool("restore_secret", "Roll a secret back to an earlier version. The stored ciphertext is replayed as a NEW version (history is append-only, nothing is overwritten). Keyless — no decryption happens, so this works even without a key.", {
|
|
2157
|
+
...targetShape,
|
|
2158
|
+
name: z.string(),
|
|
2159
|
+
version: z.number().int().positive()
|
|
2160
|
+
}, async (o) => {
|
|
2161
|
+
const ctx = getCtx();
|
|
2162
|
+
const { orgId, envId } = await resolveTargetEnv(ctx, o);
|
|
2163
|
+
const { secret, restoredFrom } = await ctx.client.restoreSecret(orgId, envId, o.name, o.version);
|
|
2164
|
+
return {
|
|
2165
|
+
ok: true,
|
|
2166
|
+
name: o.name,
|
|
2167
|
+
restoredFrom,
|
|
2168
|
+
version: secret.version
|
|
2169
|
+
};
|
|
2170
|
+
});
|
|
2104
2171
|
tool("delete_secret", "Delete a secret from an environment.", {
|
|
2105
2172
|
...targetShape,
|
|
2106
2173
|
name: z.string()
|
|
@@ -2389,7 +2456,7 @@ async function runMcpServer(options = {}) {
|
|
|
2389
2456
|
/**
|
|
2390
2457
|
* `@seekrit/mcp` — a standalone, `npx`-able entrypoint for seekrit's MCP server.
|
|
2391
2458
|
*
|
|
2392
|
-
* This is a thin wrapper: the server itself (
|
|
2459
|
+
* This is a thin wrapper: the server itself (every tool, the crypto plane of
|
|
2393
2460
|
* the two-server design) lives in `@seekrit/cli` and is shared with the
|
|
2394
2461
|
* `seekrit mcp` subcommand — this package just publishes it as its own binary so
|
|
2395
2462
|
* an agent can run it with zero prior install:
|
|
@@ -2402,7 +2469,7 @@ async function runMcpServer(options = {}) {
|
|
|
2402
2469
|
* `seekrit mcp`. tsdown bundles the shared server source in at build time, so the
|
|
2403
2470
|
* published package is self-contained and needs no `@seekrit/cli` install.
|
|
2404
2471
|
*/
|
|
2405
|
-
runMcpServer({ version: "0.
|
|
2472
|
+
runMcpServer({ version: "0.6.0" }).catch((err) => {
|
|
2406
2473
|
const message = err instanceof Error ? err.message : String(err);
|
|
2407
2474
|
process.stderr.write(`seekrit-mcp: fatal: ${message}\n`);
|
|
2408
2475
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seekrit/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "npx-able MCP server for seekrit — let Claude Code and other MCP clients provision, manage, and inject end-to-end encrypted secrets.",
|
|
5
5
|
"mcpName": "dev.seekrit/mcp",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^26.1.0",
|
|
25
25
|
"tsdown": "^0.22.3",
|
|
26
|
-
"@seekrit/cli": "0.
|
|
26
|
+
"@seekrit/cli": "0.24.0"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "tsdown",
|