@seekrit/mcp 0.7.0 → 0.8.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.
Files changed (2) hide show
  1. package/dist/index.js +364 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -45,6 +45,192 @@ z.object({
45
45
  path: z.string().trim().min(1).max(2048),
46
46
  secret: policySecretNameSchema.optional()
47
47
  });
48
+ /** One aggregated cell: a dimension tuple and how many times it happened. */
49
+ const activityEntrySchema = z.object({
50
+ host: policyHostSchema,
51
+ method: policyMethodSchema,
52
+ decision: z.enum([
53
+ "allow",
54
+ "no_rule",
55
+ "method_not_allowed",
56
+ "path_not_allowed",
57
+ "secret_not_allowed",
58
+ "unknown_secret",
59
+ "ratchet_withdrawn",
60
+ "policy_unavailable"
61
+ ]),
62
+ /**
63
+ * Which published rule decided, when one did. Null for refusals that never
64
+ * reached a rule (`no_rule`, `policy_unavailable`) — the distinction matters to
65
+ * a review, because "rule 3 refused this" and "nothing covered this" call for
66
+ * opposite changes.
67
+ */
68
+ ruleIndex: z.number().int().min(0).max(255).nullable(),
69
+ count: z.number().int().min(1).max(1e6),
70
+ /**
71
+ * Secret names actually injected, name → count. Only meaningful on `allow`.
72
+ * This is what lets a review say "rule 2 permits three secrets and the agent
73
+ * has only ever used one" — the most useful narrowing there is, and impossible
74
+ * to see from policy alone.
75
+ */
76
+ secrets: z.record(policySecretNameSchema, z.number().int().min(1)).optional()
77
+ });
78
+ z.object({
79
+ /** Start of the window these counts cover (ISO 8601). */
80
+ windowStart: z.string().trim().min(20).max(40),
81
+ /** Policy version in force while they were collected, for the ledger. */
82
+ policyVersion: z.number().int().min(0).optional(),
83
+ /**
84
+ * Capped so one report cannot be unbounded work. A proxy with more distinct
85
+ * cells than this in a window has a policy far broader than a review can help
86
+ * with, and truncating loudly beats accepting anything.
87
+ */
88
+ entries: z.array(activityEntrySchema).min(1).max(500)
89
+ });
90
+ /**
91
+ * Twelve hours, matching `[control] max_ttl` in a proxy config. A task is meant
92
+ * to bound one run; something that needs longer wants a policy change, not a
93
+ * longer ticket.
94
+ */
95
+ const TASK_MAX_TTL_SECONDS = 720 * 60;
96
+ /** An EC P-256 public JWK, for a sender-constraint proof key. */
97
+ const taskProofJwkSchema = z.object({
98
+ kty: z.literal("EC"),
99
+ crv: z.literal("P-256"),
100
+ x: z.string().min(1).max(128),
101
+ y: z.string().min(1).max(128)
102
+ });
103
+ z.object({
104
+ /**
105
+ * The public `skd_…` segment of the minted token. Sent because the API never
106
+ * sees the token at dispatch and still needs a readable handle for the audit
107
+ * row and for a revoke to name — the id half of a credential, without the
108
+ * secret half.
109
+ */
110
+ taskRef: z.string().trim().regex(/^skd_[0-9A-Za-z]+$/, "taskRef must be the skd_… segment of the minted token"),
111
+ /**
112
+ * SHA-256 (base64url) of the token the dispatcher minted. The token itself
113
+ * never reaches this API on the dispatch path — only on introspection, where
114
+ * it is hashed and discarded.
115
+ */
116
+ tokenHash: z.string().trim().min(16).max(128),
117
+ /**
118
+ * Secret names this run may use. Omit for "whatever the agent's policy
119
+ * allows" — mirroring `Session.scopes: Option<BTreeSet<String>>` in the proxy,
120
+ * so absent means unnarrowed in both places.
121
+ */
122
+ scopes: z.array(policySecretNameSchema).max(64).optional(),
123
+ ttlSeconds: z.number().int().min(60).max(TASK_MAX_TTL_SECONDS).optional(),
124
+ /**
125
+ * What this run is for, for the audit row and the operator's task list. Free
126
+ * text, and **not** a security input: never put a secret value in it.
127
+ */
128
+ label: z.string().trim().max(200).optional(),
129
+ /**
130
+ * Public half of a proof key the presenter holds, recorded as an RFC 7638
131
+ * thumbprint. See `AgentTaskSession.proofThumbprint` for what this does and —
132
+ * importantly — does not yet do.
133
+ */
134
+ proofJwk: taskProofJwkSchema.optional()
135
+ });
136
+ z.object({
137
+ /** The presented token. In the body, never a URL — it is a credential. */
138
+ token: z.string().trim().min(8).max(512) });
139
+ //#endregion
140
+ //#region ../../packages/core/src/archive.ts
141
+ /**
142
+ * The **break-glass archive** format: one signed JSON file holding everything
143
+ * seekrit stores for an org, in the form seekrit stores it — ciphertext stays
144
+ * ciphertext. Its whole purpose is to be openable on a machine that has never
145
+ * heard of seekrit, so the format is plain JSON, self-describing, and versioned
146
+ * by name (`seekrit-archive/v1`); a breaking change ships a new format string
147
+ * rather than mutating this one, exactly like the `sc1.`/`wd1.` blob prefixes.
148
+ *
149
+ * Three parts:
150
+ * - `manifest` — what this archive is, and a SHA-256 digest per section.
151
+ * - `signature` — Ed25519 over the canonical manifest, or null when the
152
+ * producing deployment has no signing key configured.
153
+ * - `data` — the sections themselves.
154
+ *
155
+ * Integrity fields (`digest`, `signature.value`, `publicKey`, `keyId`) are
156
+ * lowercase hex. Every blob *inside* `data` keeps its native base64url form, so
157
+ * the one encoding rule to remember is "the archive's own bookkeeping is hex,
158
+ * seekrit's blobs are unchanged".
159
+ *
160
+ * See docs/break-glass-export.md for what is deliberately excluded and why.
161
+ */
162
+ const ARCHIVE_FORMAT = "seekrit-archive/v1";
163
+ const sectionHeaderSchema = z.object({
164
+ name: z.enum([
165
+ "organization",
166
+ "users",
167
+ "memberships",
168
+ "invites",
169
+ "applications",
170
+ "groups",
171
+ "environments",
172
+ "environmentGroups",
173
+ "environmentKeys",
174
+ "secrets",
175
+ "secretVersions",
176
+ "serviceTokens",
177
+ "m2mClients",
178
+ "kmsKeys",
179
+ "kmsKeyVersions",
180
+ "kmsKeyGrants",
181
+ "recoveryConfig",
182
+ "recoveryShares",
183
+ "rotations",
184
+ "syncConnections",
185
+ "syncBindings",
186
+ "leaseTargets",
187
+ "agentIdentities",
188
+ "agentPolicies",
189
+ "auditLog",
190
+ "keyMaterial"
191
+ ]),
192
+ count: z.number().int().min(0),
193
+ truncated: z.boolean(),
194
+ digest: z.string().regex(/^sha256:[0-9a-f]{64}$/)
195
+ });
196
+ const manifestSchema = z.object({
197
+ archiveId: z.string().min(1),
198
+ createdAt: z.string().min(1),
199
+ org: z.object({
200
+ id: z.string(),
201
+ slug: z.string(),
202
+ name: z.string()
203
+ }),
204
+ producer: z.object({
205
+ service: z.string(),
206
+ environment: z.string(),
207
+ formatVersion: z.string()
208
+ }),
209
+ requestedBy: z.object({
210
+ actorType: z.string(),
211
+ actorId: z.string(),
212
+ label: z.string().nullable()
213
+ }),
214
+ options: z.object({
215
+ includeVersions: z.boolean(),
216
+ includeAudit: z.boolean(),
217
+ auditLimit: z.number().int().min(0)
218
+ }),
219
+ sections: z.array(sectionHeaderSchema),
220
+ digest: z.string().regex(/^sha256:[0-9a-f]{64}$/)
221
+ });
222
+ const signatureSchema = z.object({
223
+ algorithm: z.literal("ed25519"),
224
+ publicKey: z.string().regex(/^[0-9a-f]{64}$/),
225
+ keyId: z.string().regex(/^[0-9a-f]{16}$/),
226
+ value: z.string().regex(/^[0-9a-f]{128}$/)
227
+ });
228
+ z.object({
229
+ format: z.literal(ARCHIVE_FORMAT),
230
+ manifest: manifestSchema,
231
+ signature: signatureSchema.nullable(),
232
+ data: z.record(z.string(), z.unknown())
233
+ });
48
234
  /** All catalog keys as a runtime array (for iteration / zod enums). */
49
235
  const ENTITLEMENT_KEYS = Object.keys({
50
236
  "feature.kms": {
@@ -891,6 +1077,13 @@ const inviteRoleSchema = z.enum(["admin", "member"]);
891
1077
  const principalTypeSchema = z.enum(["user", "service_token"]);
892
1078
  /** Org-level capability a service token can hold (never `owner`). */
893
1079
  const serviceTokenRoleSchema = z.enum(["admin", "member"]);
1080
+ z.object({
1081
+ /** Include the full append-only ciphertext history of every secret. */
1082
+ includeVersions: z.boolean().optional(),
1083
+ includeAudit: z.boolean().optional(),
1084
+ /** Newest audit rows to keep. Exceeding the server cap is a 400, not a silent trim. */
1085
+ auditLimit: z.number().int().min(0).optional()
1086
+ });
894
1087
  z.object({
895
1088
  name: nameSchema,
896
1089
  slug: slugSchema
@@ -902,6 +1095,8 @@ z.object({
902
1095
  z.object({ name: nameSchema });
903
1096
  z.object({ name: nameSchema });
904
1097
  z.object({ name: nameSchema });
1098
+ z.object({ name: nameSchema });
1099
+ z.object({ name: nameSchema });
905
1100
  z.object({ required: z.boolean() });
906
1101
  z.object({
907
1102
  email: emailSchema,
@@ -1242,7 +1437,8 @@ z.enum([
1242
1437
  "netlify",
1243
1438
  "bunnyshell",
1244
1439
  "github-actions",
1245
- "gcp-secret-manager"
1440
+ "gcp-secret-manager",
1441
+ "langgraph-platform"
1246
1442
  ]);
1247
1443
  /**
1248
1444
  * Vercel account scope. The API token itself is never here — it is wrapped to
@@ -1507,6 +1703,56 @@ const gcpSecretManagerConnectionConfigSchema = z.object({
1507
1703
  /** Project ID (`acme-prod`) or project number. */
1508
1704
  projectId: gcpProjectSchema
1509
1705
  });
1706
+ /**
1707
+ * LangSmith workspace/tenant scope for LangGraph Platform.
1708
+ *
1709
+ * The API key is never here — it is wrapped to the connection's public key and
1710
+ * stored as ciphertext, exactly as Vercel's token is.
1711
+ *
1712
+ * Two optional fields, for two different situations, and setting both is
1713
+ * rejected rather than silently resolved:
1714
+ *
1715
+ * - `region` picks one of {@link LANGGRAPH_PLATFORM_HOSTS}. Omitted means
1716
+ * `us`, which is where an account created at `smith.langchain.com` lives.
1717
+ * - `baseUrl` points the connection at a **self-hosted** LangSmith install,
1718
+ * whose control plane is served from the customer's own host under
1719
+ * `/api-host` rather than from `*.api.host.langchain.com`.
1720
+ *
1721
+ * `tenantId` is the workspace a key was minted in. A workspace-scoped key names
1722
+ * its own tenant and does not need it; an organization-scoped key reaches
1723
+ * several workspaces and gets a bare 403 without it, which is the same trap
1724
+ * Vercel's `teamId` sets — so it is passed through as `X-Tenant-Id` whenever
1725
+ * it is present.
1726
+ */
1727
+ const langgraphPlatformConnectionConfigSchema = z.object({
1728
+ provider: z.literal("langgraph-platform"),
1729
+ /** Control-plane region. Omit for `us`. Mutually exclusive with `baseUrl`. */
1730
+ region: z.enum([
1731
+ "us",
1732
+ "eu",
1733
+ "apac",
1734
+ "aws-us"
1735
+ ]).optional(),
1736
+ /**
1737
+ * Self-hosted LangSmith control-plane root, e.g.
1738
+ * `https://langsmith.acme.com/api-host`. Omit for LangChain's own hosts.
1739
+ * Must be `https:` — this URL carries the API key.
1740
+ */
1741
+ baseUrl: z.string().trim().max(300).refine((value) => {
1742
+ let parsed;
1743
+ try {
1744
+ parsed = new URL(value);
1745
+ } catch {
1746
+ return false;
1747
+ }
1748
+ return parsed.protocol === "https:" && !parsed.username && !parsed.password;
1749
+ }, "must be an https:// URL — the self-hosted control-plane root, e.g. https://langsmith.acme.com/api-host").optional(),
1750
+ /** LangSmith workspace (tenant) UUID, sent as `X-Tenant-Id`. */
1751
+ tenantId: z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "must be a LangSmith workspace UUID").optional()
1752
+ }).refine((c) => !(c.baseUrl !== void 0 && c.region !== void 0), {
1753
+ message: "set region for a LangChain-hosted account or baseUrl for a self-hosted one, not both",
1754
+ path: ["baseUrl"]
1755
+ });
1510
1756
  const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
1511
1757
  vercelConnectionConfigSchema,
1512
1758
  cloudflareWorkersConnectionConfigSchema,
@@ -1523,7 +1769,8 @@ const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
1523
1769
  netlifyConnectionConfigSchema,
1524
1770
  bunnyshellConnectionConfigSchema,
1525
1771
  githubActionsConnectionConfigSchema,
1526
- gcpSecretManagerConnectionConfigSchema
1772
+ gcpSecretManagerConnectionConfigSchema,
1773
+ langgraphPlatformConnectionConfigSchema
1527
1774
  ]);
1528
1775
  const vercelDestinationSchema = z.object({
1529
1776
  provider: z.literal("vercel"),
@@ -2210,6 +2457,27 @@ const gcpSecretManagerDestinationSchema = z.object({
2210
2457
  message: "a customer-managed key covers one location — use automatic replication, or a single location",
2211
2458
  path: ["kmsKeyName"]
2212
2459
  });
2460
+ /**
2461
+ * One LangGraph Platform (Agent Server) **deployment**, addressed by its id.
2462
+ *
2463
+ * A deployment is the whole unit here: its secrets are a property of the
2464
+ * deployment, delivered to the agent container as environment variables, and
2465
+ * there is nothing finer to point at — no per-revision or per-graph scope, and
2466
+ * no equivalent of Vercel's `production`/`preview` split. A deployment that
2467
+ * needs different values is a different deployment, so it is a different
2468
+ * binding.
2469
+ *
2470
+ * Validated as a UUID because `PATCH /v2/deployments/{deployment_id}` declares
2471
+ * the path parameter as one: a name or a URL slug in the slot fails validation
2472
+ * at the control plane hours later inside an alarm, with nobody watching. It is
2473
+ * the `id` from `GET /v2/deployments`, and the UUID in the deployment's
2474
+ * dashboard URL.
2475
+ */
2476
+ const langgraphPlatformDestinationSchema = z.object({
2477
+ provider: z.literal("langgraph-platform"),
2478
+ /** Deployment UUID, from the dashboard URL or `GET /v2/deployments`. */
2479
+ deploymentId: z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "must be a LangGraph Platform deployment UUID")
2480
+ });
2213
2481
  const syncDestinationSchema = z.discriminatedUnion("provider", [
2214
2482
  vercelDestinationSchema,
2215
2483
  cloudflareWorkersDestinationSchema,
@@ -2226,7 +2494,8 @@ const syncDestinationSchema = z.discriminatedUnion("provider", [
2226
2494
  netlifyDestinationSchema,
2227
2495
  bunnyshellDestinationSchema,
2228
2496
  githubActionsDestinationSchema,
2229
- gcpSecretManagerDestinationSchema
2497
+ gcpSecretManagerDestinationSchema,
2498
+ langgraphPlatformDestinationSchema
2230
2499
  ]);
2231
2500
  /**
2232
2501
  * How seekrit secret names become destination key names. Applied in order:
@@ -2956,7 +3225,7 @@ function isServiceToken(value) {
2956
3225
  }
2957
3226
  //#endregion
2958
3227
  //#region ../cli/package.json
2959
- var version$1 = "0.42.0";
3228
+ var version$1 = "0.46.0";
2960
3229
  const PROJECT_FILE = "seekrit.json";
2961
3230
  function globalConfigPath() {
2962
3231
  return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "seekrit", "config.json");
@@ -3157,6 +3426,10 @@ var SeekritClient = class {
3157
3426
  getEnv(orgId, envId) {
3158
3427
  return this.request("GET", `/v1/orgs/${orgId}/envs/${envId}`);
3159
3428
  }
3429
+ /** Rename an environment (display name only — the slug is immutable). */
3430
+ updateEnv(orgId, envId, input) {
3431
+ return this.request("PATCH", `/v1/orgs/${orgId}/envs/${envId}`, input);
3432
+ }
3160
3433
  deleteEnv(orgId, envId) {
3161
3434
  return this.request("DELETE", `/v1/orgs/${orgId}/envs/${envId}`);
3162
3435
  }
@@ -3310,6 +3583,10 @@ var SeekritClient = class {
3310
3583
  createToken(orgId, input) {
3311
3584
  return this.request("POST", `/v1/orgs/${orgId}/tokens`, input);
3312
3585
  }
3586
+ /** Rename a token. Role, environment binding, and expiry are immutable. */
3587
+ updateToken(orgId, tokenId, input) {
3588
+ return this.request("PATCH", `/v1/orgs/${orgId}/tokens/${tokenId}`, input);
3589
+ }
3313
3590
  revokeToken(orgId, tokenId) {
3314
3591
  return this.request("DELETE", `/v1/orgs/${orgId}/tokens/${tokenId}`);
3315
3592
  }
@@ -3365,6 +3642,77 @@ var SeekritClient = class {
3365
3642
  getMyPolicySigner(orgId) {
3366
3643
  return this.request("GET", `/v1/orgs/${orgId}/agents/signers/me`);
3367
3644
  }
3645
+ /**
3646
+ * The bundle a proxy would see — `GET /v1/agents/:ref/policy`, the same route
3647
+ * `seekrit-proxy` polls, resolved by agent id or slug.
3648
+ *
3649
+ * Not org-scoped, because the caller is not: a proxy holds a service token that
3650
+ * knows an agent slug and nothing about org ids. Reachable with any service
3651
+ * token bound to the agent's org (or a user session), which is what lets
3652
+ * `seekrit proxy init` generate a config on the machine that holds the proxy's
3653
+ * own token rather than requiring an admin credential there.
3654
+ *
3655
+ * The `bundle` is signed and opaque to the API. Anything that *acts* on it must
3656
+ * verify the signature against locally pinned signers; decoding it for display
3657
+ * or to name a route is not acting on it.
3658
+ */
3659
+ getAgentPolicyBundle(agentRef) {
3660
+ return this.request("GET", `/v1/agents/${encodeURIComponent(agentRef)}/policy`);
3661
+ }
3662
+ /**
3663
+ * Dispatch a task for one agent run.
3664
+ *
3665
+ * The caller mints the token (`createAgentTaskToken` in `@seekrit/crypto`) and
3666
+ * sends only its hash plus the public `skd_…` segment, so no presentable
3667
+ * credential ever reaches this API — the same shape as service-token and CLI
3668
+ * session creation. `scopes` may only narrow what the agent's published policy
3669
+ * already permits; a name outside it is refused rather than dropped.
3670
+ *
3671
+ * Not org-scoped, because an orchestrator is not: it knows an agent slug.
3672
+ */
3673
+ dispatchAgentTask(agentRef, input) {
3674
+ return this.request("POST", `/v1/agents/${encodeURIComponent(agentRef)}/dispatch`, input);
3675
+ }
3676
+ /**
3677
+ * Exchange a presented token for the session it authorizes — what an
3678
+ * enforcement point calls once per task and caches until expiry.
3679
+ *
3680
+ * A POST because the token is a credential and must not land in a URL or an
3681
+ * access log. Fails closed and says which way: revoked, expired, or a disabled
3682
+ * identity are three different answers.
3683
+ */
3684
+ introspectAgentTask(token) {
3685
+ return this.request("POST", "/v1/tasks/introspect", { token });
3686
+ }
3687
+ /** End a run's authority now. Idempotent. */
3688
+ revokeAgentTask(taskId) {
3689
+ return this.request("POST", `/v1/tasks/${taskId}/revoke`);
3690
+ }
3691
+ getAgentTask(taskId) {
3692
+ return this.request("GET", `/v1/tasks/${taskId}`);
3693
+ }
3694
+ /** Runs dispatched for one identity, newest first (admin). */
3695
+ listAgentTasks(orgId, agentId) {
3696
+ return this.request("GET", `/v1/orgs/${orgId}/agents/${agentId}/tasks`);
3697
+ }
3698
+ /**
3699
+ * Report aggregate decisions. Called by an enforcement point, not a person.
3700
+ *
3701
+ * Counts only — hosts, methods, secret *names*, decisions, and rule indices.
3702
+ * Never a request path: see the module comment in `agent-activity.ts` for why
3703
+ * that line is drawn where it is.
3704
+ */
3705
+ reportAgentActivity(agentRef, input) {
3706
+ return this.request("POST", `/v1/agents/${encodeURIComponent(agentRef)}/activity`, input);
3707
+ }
3708
+ /**
3709
+ * What an agent actually did, collapsed onto its dimensions — the evidence a
3710
+ * grant review reasons over. The proposals themselves are computed client-side
3711
+ * (`reviewPolicy` in `@seekrit/core`), so the API never opines on policy.
3712
+ */
3713
+ getAgentActivity(orgId, agentId, days = 14) {
3714
+ return this.request("GET", `/v1/orgs/${orgId}/agents/${agentId}/activity?days=${encodeURIComponent(String(days))}`);
3715
+ }
3368
3716
  /** Keys the caller can see: all org keys for admins, granted keys otherwise. */
3369
3717
  listKmsKeys(orgId) {
3370
3718
  return this.request("GET", `/v1/orgs/${orgId}/kms/keys`);
@@ -3517,6 +3865,17 @@ var SeekritClient = class {
3517
3865
  const qs = params.size > 0 ? `?${params}` : "";
3518
3866
  return this.request("GET", `/v1/orgs/${orgId}/audit${qs}`);
3519
3867
  }
3868
+ /**
3869
+ * Export the org as one signed archive: every row seekrit holds for it, with
3870
+ * ciphertext still ciphertext (docs/break-glass-export.md).
3871
+ *
3872
+ * The archive comes back inline rather than as a job handle, and it can be
3873
+ * megabytes — buffer it to a file rather than holding several copies. Requires
3874
+ * admin; deliberately not entitlement-gated.
3875
+ */
3876
+ exportArchive(orgId, input = {}) {
3877
+ return this.request("POST", `/v1/orgs/${orgId}/export`, input);
3878
+ }
3520
3879
  getLogSink(orgId) {
3521
3880
  return this.request("GET", `/v1/orgs/${orgId}/log-sink`);
3522
3881
  }
@@ -5145,7 +5504,7 @@ async function runMcpServer(options = {}) {
5145
5504
  * `seekrit mcp`. tsdown bundles the shared server source in at build time, so the
5146
5505
  * published package is self-contained and needs no `@seekrit/cli` install.
5147
5506
  */
5148
- runMcpServer({ version: "0.7.0" }).catch((err) => {
5507
+ runMcpServer({ version: "0.8.0" }).catch((err) => {
5149
5508
  const message = err instanceof Error ? err.message : String(err);
5150
5509
  process.stderr.write(`seekrit-mcp: fatal: ${message}\n`);
5151
5510
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekrit/mcp",
3
- "version": "0.7.0",
3
+ "version": "0.8.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",
@@ -24,7 +24,7 @@
24
24
  "@types/node": "^26.1.0",
25
25
  "tsdown": "^0.22.3",
26
26
  "vitest": "^4.1.9",
27
- "@seekrit/cli": "0.42.0"
27
+ "@seekrit/cli": "0.46.0"
28
28
  },
29
29
  "scripts": {
30
30
  "build": "tsdown",