@decionis/mcp 0.3.0 → 0.4.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.
@@ -3,7 +3,7 @@ import {
3
3
  LocalDecionisGateway,
4
4
  PolicyFileRepository,
5
5
  normalizeVerdict
6
- } from "./chunk-LIVLO4MV.js";
6
+ } from "./chunk-5Q6BCK32.js";
7
7
 
8
8
  // src/agent/AgentContracts.ts
9
9
  var AgentHookInputError = class extends Error {
@@ -498,4 +498,4 @@ export {
498
498
  AgentHostRegistry,
499
499
  AgentHookCommand
500
500
  };
501
- //# sourceMappingURL=chunk-ABHNQFWI.js.map
501
+ //# sourceMappingURL=chunk-GCE6PUYD.js.map
package/dist/index.js CHANGED
@@ -5,12 +5,12 @@ import {
5
5
  external_exports,
6
6
  normalizeVerdict,
7
7
  sha256Hex
8
- } from "./chunk-LIVLO4MV.js";
8
+ } from "./chunk-5Q6BCK32.js";
9
9
 
10
10
  // src/Server.ts
11
11
  import { createInterface } from "readline";
12
12
  var SUPPORTED_PROTOCOL_VERSIONS = ["2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"];
13
- var MCP_SERVER_VERSION = "0.3.0";
13
+ var MCP_SERVER_VERSION = "0.4.0";
14
14
  var DEFAULT_PROTOCOL_VERSION = "2025-11-25";
15
15
  var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
16
16
  function rpcResult(id, result) {
@@ -606,7 +606,7 @@ function createDecionisTools(gateway2, additionalTools = [], options = {}) {
606
606
  // src/kubernetes/KubernetesActionPlanner.ts
607
607
  import { randomUUID } from "crypto";
608
608
 
609
- // ../../packages/sdk-node/dist/index.js
609
+ // ../../../Decionis/packages/sdk-node/dist/index.js
610
610
  import { createHash as createHash2 } from "crypto";
611
611
  import { createHash as createHash3 } from "crypto";
612
612
  var DEFAULT_SKEW_MS = 6 * 60 * 60 * 1e3;
@@ -733,16 +733,10 @@ var authorityVerificationRequirementsSchema = external_exports.object({
733
733
  });
734
734
  }
735
735
  });
736
- var authorityApprovalTierSchema = external_exports.object({
737
- priority: external_exports.number().int().min(1).max(5),
738
- principal_ids: external_exports.array(external_exports.string().min(1).max(200)).min(1).max(10)
739
- }).strict();
740
736
  var authorityResolutionPolicySchema = external_exports.object({
741
737
  quorum: external_exports.literal(1),
742
738
  delegation: external_exports.enum(["DISALLOWED", "REQUIREMENT_REFRESH"]),
743
- fallback_role_ids: external_exports.array(external_exports.string().regex(/^[A-Z][A-Z0-9_]{1,63}$/)).max(10).default([]),
744
- /** An approval chain: one valid proof from every tier satisfies the requirement. */
745
- tiers: external_exports.array(authorityApprovalTierSchema).min(1).max(5).optional()
739
+ fallback_role_ids: external_exports.array(external_exports.string().regex(/^[A-Z][A-Z0-9_]{1,63}$/)).max(10).default([])
746
740
  }).strict().superRefine((policy, context) => {
747
741
  if (new Set(policy.fallback_role_ids).size !== policy.fallback_role_ids.length) {
748
742
  context.addIssue({
@@ -751,24 +745,6 @@ var authorityResolutionPolicySchema = external_exports.object({
751
745
  message: "fallback role ids must be unique"
752
746
  });
753
747
  }
754
- if (policy.tiers) {
755
- const priorities = policy.tiers.map((tier) => tier.priority);
756
- if (priorities.some((priority, index) => index > 0 && priority <= priorities[index - 1])) {
757
- context.addIssue({
758
- code: external_exports.ZodIssueCode.custom,
759
- path: ["tiers"],
760
- message: "tier priorities must be unique and ascending"
761
- });
762
- }
763
- const principals = policy.tiers.flatMap((tier) => tier.principal_ids);
764
- if (new Set(principals).size !== principals.length) {
765
- context.addIssue({
766
- code: external_exports.ZodIssueCode.custom,
767
- path: ["tiers"],
768
- message: "a principal belongs to one tier only"
769
- });
770
- }
771
- }
772
748
  });
773
749
  var originatorSchema = external_exports.object({
774
750
  organization_id: external_exports.string().min(1).max(200),
@@ -834,13 +810,6 @@ var decionisAuthorityRequirementSchema = external_exports.object({
834
810
  message: "fallback roles must not repeat the primary role"
835
811
  });
836
812
  }
837
- if (requirement.resolution.tiers && requirement.selector.kind !== "ROLE") {
838
- context.addIssue({
839
- code: external_exports.ZodIssueCode.custom,
840
- path: ["resolution", "tiers"],
841
- message: "an approval chain names its members' trusted role through a ROLE selector"
842
- });
843
- }
844
813
  if (requirement.orchestration?.mode === "MANAGED" && requirement.selector.kind === "PRINCIPAL" && requirement.selector.role_id === void 0) {
845
814
  context.addIssue({
846
815
  code: external_exports.ZodIssueCode.custom,
@@ -1023,25 +992,13 @@ function expectedManagedEscalationOutcome(status) {
1023
992
  if (["EXPIRED", "REJECTED", "BLOCKED", "CANCELLED"].includes(status)) return "BLOCK";
1024
993
  return "ESCALATE_PENDING";
1025
994
  }
1026
- var managedApprovalTierSchema = external_exports.object({
1027
- tier: external_exports.number().int().min(1).max(5),
1028
- tier_count: external_exports.number().int().min(1).max(5),
1029
- principal_id: external_exports.string().min(1).max(200)
1030
- }).strict();
1031
- var managedApprovalChainSchema = external_exports.object({
1032
- chain_id: external_exports.string().max(200),
1033
- tier_count: external_exports.number().int().min(1).max(5),
1034
- tiers_approved: external_exports.number().int().min(0).max(5)
1035
- }).strict();
1036
995
  var managedEscalationReferenceSchema = external_exports.object({
1037
996
  outcome: managedEscalationOutcomeSchema,
1038
997
  escalation_id: external_exports.string().min(8).max(200),
1039
998
  intent_id: external_exports.string().min(1).max(200),
1040
999
  status: managedEscalationStatusSchema,
1041
1000
  expires_at: external_exports.string().datetime(),
1042
- reason_codes: external_exports.array(external_exports.string().min(1).max(200)).max(50),
1043
- approval_tier: managedApprovalTierSchema.nullable().optional().default(null),
1044
- approval_chain: managedApprovalChainSchema.nullable().optional().default(null)
1001
+ reason_codes: external_exports.array(external_exports.string().min(1).max(200)).max(50)
1045
1002
  }).strict().superRefine((reference, context) => {
1046
1003
  if (reference.outcome !== expectedManagedEscalationOutcome(reference.status)) {
1047
1004
  context.addIssue({
@@ -1092,8 +1049,6 @@ var managedEscalationViewSchema = external_exports.object({
1092
1049
  outcome: managedEscalationOutcomeSchema,
1093
1050
  expires_at: external_exports.string().datetime(),
1094
1051
  reason_codes: external_exports.array(external_exports.string().min(1).max(200)).max(50),
1095
- approval_tier: managedApprovalTierSchema.nullable().optional().default(null),
1096
- approval_chain: managedApprovalChainSchema.nullable().optional().default(null),
1097
1052
  decision: authorityDecisionSchema.nullable()
1098
1053
  }).strict().superRefine((view, context) => {
1099
1054
  if (view.outcome !== expectedManagedEscalationOutcome(view.status)) {
@@ -1379,7 +1334,6 @@ var DecionisExecutionClient = class {
1379
1334
  claim_token: claimToken,
1380
1335
  claim_validated_at: claimValidatedAt,
1381
1336
  claim_lease_expires_at: claimLeaseExpiresAt,
1382
- ...typeof body.claim_attestation === "string" && body.claim_attestation.length > 0 ? { claim_attestation: body.claim_attestation } : {},
1383
1337
  ...body.evidence && typeof body.evidence === "object" ? { evidence: body.evidence } : {}
1384
1338
  };
1385
1339
  if (executionAuthorityProtocolSemantics(redemption) === "UNSUPPORTED") {
@@ -2356,7 +2310,7 @@ var KubectlCommandRunner = class {
2356
2310
  // src/kubernetes/KubernetesOperations.ts
2357
2311
  import { createHash as createHash4 } from "crypto";
2358
2312
 
2359
- // ../../packages/dossier-verify/dist/chunk-WNX2CYWD.js
2313
+ // ../../../Decionis/packages/dossier-verify/dist/chunk-WNX2CYWD.js
2360
2314
  import { createHash, createPublicKey, verify as verifyEd25519 } from "crypto";
2361
2315
  import { lookup } from "dns/promises";
2362
2316
  import { request as httpsRequest } from "https";
@@ -8465,16 +8419,90 @@ var networkRead = {
8465
8419
  function createShieldPairTool(options = {}) {
8466
8420
  const store = options.store ?? new ShieldCredentialStore();
8467
8421
  const fetchImpl = options.fetch ?? fetch;
8468
- const baseUrl = () => (options.baseUrl?.() ?? process.env.DECIONIS_SHIELD_API_URL?.trim() ?? "").replace(/\/+$/, "") || "https://api.shield.decionis.com";
8422
+ const baseUrl = () => (options.baseUrl?.() ?? process.env.DECIONIS_SHIELD_API_URL?.trim() ?? "").replace(
8423
+ /\/+$/,
8424
+ ""
8425
+ ) || "https://api.shield.decionis.com";
8426
+ const pendingEmail = /* @__PURE__ */ new Map();
8427
+ const post = async (path, body) => fetchImpl(`${baseUrl()}${path}`, {
8428
+ method: "POST",
8429
+ headers: {
8430
+ accept: "application/json",
8431
+ "content-type": "application/json",
8432
+ "x-shield-surface": "mcp"
8433
+ },
8434
+ body: JSON.stringify(body)
8435
+ });
8436
+ const unreachable = (error) => ({
8437
+ content: text4({
8438
+ error: "SHIELD_PAIRING_UNREACHABLE",
8439
+ message: `Could not reach Shield: ${error instanceof Error ? error.message : String(error)}. Nothing was paired.`
8440
+ }),
8441
+ isError: true
8442
+ });
8443
+ const keep = async (response, displayName, agentId, hints) => {
8444
+ const payload = await response.json().catch(() => null);
8445
+ if (response.ok && payload?.upgrade_required === true) {
8446
+ return {
8447
+ content: text4({
8448
+ error: "SHIELD_SOURCE_LIMIT",
8449
+ message: "The user's free Shield already covers its limit of spending sources. They can remove one in the Shield app or upgrade; nothing was paired."
8450
+ }),
8451
+ isError: true
8452
+ };
8453
+ }
8454
+ if (!response.ok || !payload || typeof payload.api_key !== "string" || typeof payload.app_id !== "string") {
8455
+ const code_ = payload && typeof payload.error === "string" ? payload.error : `HTTP ${response.status}`;
8456
+ return {
8457
+ content: text4({ error: code_, message: hints[code_] ?? "Nothing was paired." }),
8458
+ isError: true
8459
+ };
8460
+ }
8461
+ const integration = payload.integration ?? {};
8462
+ const credential = {
8463
+ apiKey: payload.api_key,
8464
+ appId: payload.app_id,
8465
+ baseUrl: baseUrl(),
8466
+ integrationId: typeof integration.integrationId === "string" ? integration.integrationId : void 0,
8467
+ displayName,
8468
+ agentId,
8469
+ pairedAt: (/* @__PURE__ */ new Date()).toISOString()
8470
+ };
8471
+ store.write(credential);
8472
+ const limit = typeof payload.weekly_limit === "number" ? payload.weekly_limit : null;
8473
+ return {
8474
+ content: text4({
8475
+ paired: true,
8476
+ rotated: payload.rotated === true,
8477
+ displayName,
8478
+ weeklyLimit: limit,
8479
+ storedAt: store.location(),
8480
+ next: "The shield.* tools now ask this user's own Shield. Before any purchase, call shield.request_purchase; execute only on ALLOW."
8481
+ })
8482
+ };
8483
+ };
8469
8484
  return {
8470
8485
  name: "shield.pair",
8471
8486
  title: "Pair with the user's Shield",
8472
- description: "Bind this assistant to the user's Decionis Shield with the six-character pairing code shown in the Shield app (AI spending allowance \u2192 Bind your own agent). Ask the user for the code; never guess one. After pairing, shield.request_purchase and the other shield.* tools ask the user's own spending rules.",
8487
+ description: "Bind this assistant to the user's Decionis Shield, one of two ways. (1) The six-character pairing code shown in the Shield app (AI spending allowance \u2192 Bind your own agent): pass it as code. (2) The user's email: pass email, and Shield emails them a 6-digit code; ask the user for it, then call again with email and emailCode. Ask the user; never guess a code. After pairing, shield.request_purchase and the other shield.* tools ask the user's own spending rules.",
8473
8488
  inputSchema: {
8474
8489
  type: "object",
8475
- required: ["code"],
8476
8490
  properties: {
8477
- code: { type: "string", pattern: "^[A-Za-z0-9]{6,8}$", description: "The code from the Shield app." },
8491
+ code: {
8492
+ type: "string",
8493
+ pattern: "^[A-Za-z0-9]{6,8}$",
8494
+ description: "The pairing code from the Shield app."
8495
+ },
8496
+ email: {
8497
+ type: "string",
8498
+ maxLength: 254,
8499
+ description: "The user's Shield email, to be sent a sign-in code."
8500
+ },
8501
+ emailCode: {
8502
+ type: "string",
8503
+ pattern: "^[0-9]{6}$",
8504
+ description: "The 6-digit code Shield emailed to the user."
8505
+ },
8478
8506
  displayName: {
8479
8507
  type: "string",
8480
8508
  maxLength: 120,
@@ -8485,63 +8513,94 @@ function createShieldPairTool(options = {}) {
8485
8513
  },
8486
8514
  annotations: networkMutation,
8487
8515
  handler: async (args) => {
8488
- const code = typeof args.code === "string" ? args.code.trim().toUpperCase() : "";
8489
- if (!/^[A-Z0-9]{6,8}$/.test(code)) {
8490
- return { content: text4("A pairing code is 6 to 8 letters and digits. Ask the user for the code shown in the Shield app."), isError: true };
8491
- }
8492
8516
  const displayName = typeof args.displayName === "string" && args.displayName.trim() ? args.displayName.trim().slice(0, 120) : defaultAgentDisplayName(options.clientName?.());
8493
- const agentId = store.agentId();
8494
- let response;
8495
- try {
8496
- response = await fetchImpl(`${baseUrl()}/v1/consumer/pairing/claim`, {
8497
- method: "POST",
8498
- headers: {
8499
- accept: "application/json",
8500
- "content-type": "application/json",
8501
- "x-shield-surface": "mcp"
8502
- },
8503
- body: JSON.stringify({
8504
- code,
8505
- agent: { app_id: SHIELD_MCP_APP_ID, agent_id: agentId, display_name: displayName }
8506
- })
8517
+ const agent = {
8518
+ app_id: SHIELD_MCP_APP_ID,
8519
+ agent_id: store.agentId(),
8520
+ display_name: displayName
8521
+ };
8522
+ const email = typeof args.email === "string" ? args.email.trim().toLowerCase() : "";
8523
+ const emailCode = typeof args.emailCode === "string" ? args.emailCode.trim() : "";
8524
+ if (email) {
8525
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
8526
+ return {
8527
+ content: text4(
8528
+ "That is not an email address. Ask the user for the email they use with Shield."
8529
+ ),
8530
+ isError: true
8531
+ };
8532
+ }
8533
+ if (!emailCode) {
8534
+ let response3;
8535
+ try {
8536
+ response3 = await post("/v1/consumer/auth/start", { email, scope: "agent" });
8537
+ } catch (error) {
8538
+ return unreachable(error);
8539
+ }
8540
+ const payload = await response3.json().catch(() => null);
8541
+ if (!response3.ok || !payload || typeof payload.challenge_id !== "string") {
8542
+ return {
8543
+ content: text4({
8544
+ error: response3.status === 429 ? "SHIELD_TOO_MANY_CODES" : `HTTP ${response3.status}`,
8545
+ message: response3.status === 429 ? "Shield has sent several codes already. Wait a minute, then try again." : "Shield could not send a code right now. Nothing was paired."
8546
+ }),
8547
+ isError: true
8548
+ };
8549
+ }
8550
+ pendingEmail.set(email, payload.challenge_id);
8551
+ return {
8552
+ content: text4({
8553
+ emailed: true,
8554
+ email,
8555
+ next: `Ask the user for the 6-digit code Shield just emailed to ${email}, then call shield.pair again with email and emailCode.`
8556
+ })
8557
+ };
8558
+ }
8559
+ if (!/^[0-9]{6}$/.test(emailCode)) {
8560
+ return {
8561
+ content: text4(
8562
+ "The emailed code is 6 digits. Ask the user to read it from the Shield email."
8563
+ ),
8564
+ isError: true
8565
+ };
8566
+ }
8567
+ let response2;
8568
+ try {
8569
+ response2 = await post("/v1/consumer/auth/verify", {
8570
+ email,
8571
+ code: emailCode,
8572
+ ...pendingEmail.has(email) ? { challenge_id: pendingEmail.get(email) } : {},
8573
+ scope: "agent",
8574
+ agent
8575
+ });
8576
+ } catch (error) {
8577
+ return unreachable(error);
8578
+ }
8579
+ if (response2.ok) pendingEmail.delete(email);
8580
+ return keep(response2, displayName, agent.agent_id, {
8581
+ INVALID_CODE: "The code is wrong or has expired (five minutes). Ask the user for the newest code, or call shield.pair with email again to send a fresh one.",
8582
+ OTP_TOO_MANY_ATTEMPTS: "Too many wrong codes for that email. Call shield.pair with email again to send a fresh one.",
8583
+ CHALLENGE_NOT_FOUND: "No code is waiting for that email. Call shield.pair with email first."
8507
8584
  });
8508
- } catch (error) {
8585
+ }
8586
+ const code = typeof args.code === "string" ? args.code.trim().toUpperCase() : "";
8587
+ if (!/^[A-Z0-9]{6,8}$/.test(code)) {
8509
8588
  return {
8510
- content: text4({
8511
- error: "SHIELD_PAIRING_UNREACHABLE",
8512
- message: `Could not reach Shield: ${error instanceof Error ? error.message : String(error)}. Nothing was paired.`
8513
- }),
8589
+ content: text4(
8590
+ "Give either the pairing code shown in the Shield app (6 to 8 letters and digits) or the user's email. Ask the user; never guess."
8591
+ ),
8514
8592
  isError: true
8515
8593
  };
8516
8594
  }
8517
- const payload = await response.json().catch(() => null);
8518
- if (!response.ok || !payload || typeof payload.api_key !== "string" || typeof payload.app_id !== "string") {
8519
- const code_ = payload && typeof payload.error === "string" ? payload.error : `HTTP ${response.status}`;
8520
- const hint = code_ === "PAIRING_CODE_INVALID" ? "The code is wrong, expired (ten minutes) or already used. Ask the user for a fresh one from the Shield app." : "Nothing was paired.";
8521
- return { content: text4({ error: code_, message: hint }), isError: true };
8595
+ let response;
8596
+ try {
8597
+ response = await post("/v1/consumer/pairing/claim", { code, agent });
8598
+ } catch (error) {
8599
+ return unreachable(error);
8522
8600
  }
8523
- const integration = payload.integration ?? {};
8524
- const credential = {
8525
- apiKey: payload.api_key,
8526
- appId: payload.app_id,
8527
- baseUrl: baseUrl(),
8528
- integrationId: typeof integration.integrationId === "string" ? integration.integrationId : void 0,
8529
- displayName,
8530
- agentId,
8531
- pairedAt: (/* @__PURE__ */ new Date()).toISOString()
8532
- };
8533
- store.write(credential);
8534
- const limit = typeof payload.weekly_limit === "number" ? payload.weekly_limit : null;
8535
- return {
8536
- content: text4({
8537
- paired: true,
8538
- rotated: payload.rotated === true,
8539
- displayName,
8540
- weeklyLimit: limit,
8541
- storedAt: store.location(),
8542
- next: "The shield.* tools now ask this user's own Shield. Before any purchase, call shield.request_purchase; execute only on ALLOW."
8543
- })
8544
- };
8601
+ return keep(response, displayName, agent.agent_id, {
8602
+ PAIRING_CODE_INVALID: "The code is wrong, expired (ten minutes) or already used. Ask the user for a fresh one from the Shield app."
8603
+ });
8545
8604
  }
8546
8605
  };
8547
8606
  }