@aipermission/mcp 0.2.55 → 0.2.56

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
@@ -234,9 +234,12 @@ gateway control result, not a recorded request. `outcome_unknown` is terminal
234
234
  and means the gateway could not prove the remote outcome after interruption;
235
235
  inspect target state or ask the operator
236
236
  before retrying. Gateway API errors with that status retain their request id,
237
- assistant hint, and bounded retry delay in the MCP error envelope. MCP tool
238
- responses never include file contents, gateway
239
- temporary paths, archive staging paths, or local upload contents.
237
+ assistant hint, and bounded retry delay in the MCP error envelope. File-transfer
238
+ queue and status responses do not include transferred file bytes, gateway
239
+ temporary paths, archive staging paths, or local upload contents. Explicitly
240
+ authorized connector read actions may return bounded content, such as S3
241
+ `download_object` `content_base64` or SSH command output; treat it as sensitive
242
+ target data.
240
243
 
241
244
  ## Operator Skill
242
245
 
@@ -113,6 +113,7 @@ Terminal statuses:
113
113
  ```text
114
114
  completed
115
115
  failed
116
+ canceled
116
117
  declined
117
118
  blocked
118
119
  error
@@ -193,8 +194,11 @@ files or inspect remote paths. Prefer the smallest explicit path set. Do not use
193
194
  globs, recursive copy, or directory transfer unless a connector action
194
195
  explicitly supports that behavior.
195
196
 
196
- MCP connector responses never include file contents, gateway temp paths, archive
197
- staging paths, or local upload contents.
197
+ File-transfer queue and status responses do not include transferred file bytes,
198
+ gateway temp paths, archive staging paths, or local upload contents. Explicitly
199
+ authorized connector read actions may return bounded content, such as S3
200
+ `download_object` `content_base64` or SSH command output; treat it as sensitive
201
+ target data.
198
202
 
199
203
  ## S3/Object Storage Practice
200
204
 
@@ -259,7 +263,8 @@ the operator how to rotate or redact it.
259
263
  Use Project Vault only through its dedicated tools:
260
264
 
261
265
  1. Call `list_vault_items(project_ref)` to discover names and non-secret
262
- metadata. Never ask the gateway to reveal values.
266
+ metadata. Prefer an explicit `id:<id>` or `slug:<slug>` reference. Never ask
267
+ the gateway to reveal values.
263
268
  2. Use `call_vault_action` with `generate_item` only when the operator asked for
264
269
  a new secret. Use a stable, unique `idempotency_key`. Send `tags` as a string
265
270
  array, `shared_project_ids` as an integer array, and `usage_notes` as an
@@ -237,24 +237,46 @@ const vaultSessionOutputSchema = z
237
237
  })
238
238
  .strict();
239
239
 
240
- const vaultActionResponseSchema = z
241
- .object({
242
- status: z.string(),
243
- request_id: positiveID.optional(),
244
- project_ref: z.string().optional(),
245
- action_name: z.string().optional(),
246
- input: z.union([vaultGenerateInputSchema, vaultSessionInputSchema]).optional(),
247
- reason: z.string().optional(),
248
- created_at: z.string().optional(),
249
- expires_at: z.string().optional(),
250
- secret_values_returned: z.literal(false).optional(),
251
- output: z.union([generatedVaultOutputSchema, vaultSessionOutputSchema]).optional(),
252
- output_withheld: z.boolean().optional(),
253
- retry_after_seconds: nonNegativeInteger.optional(),
254
- assistant_hint: z.string().optional(),
255
- error: z.string().optional(),
256
- })
257
- .strict();
240
+ const vaultActionStatuses = ["approval_pending", "running", "completed", "failed", "declined", "stale", "canceled", "expired"];
241
+ const vaultActionRecordedShape = {
242
+ status: z.enum(vaultActionStatuses),
243
+ request_id: positiveID,
244
+ project_ref: z.string().min(1),
245
+ reason: z.string().optional(),
246
+ created_at: z.string().optional(),
247
+ expires_at: z.string().optional(),
248
+ secret_values_returned: z.literal(false),
249
+ output_withheld: z.literal(true).optional(),
250
+ retry_after_seconds: nonNegativeInteger.max(3600).optional(),
251
+ assistant_hint: z.string().optional(),
252
+ error: z.string().optional(),
253
+ };
254
+ const vaultActionRecordedSchema = z
255
+ .discriminatedUnion("action_name", [
256
+ z
257
+ .object({
258
+ ...vaultActionRecordedShape,
259
+ action_name: z.literal("generate_item"),
260
+ input: vaultGenerateInputSchema.optional(),
261
+ output: generatedVaultOutputSchema.optional(),
262
+ })
263
+ .strict(),
264
+ z
265
+ .object({
266
+ ...vaultActionRecordedShape,
267
+ action_name: z.literal("restart_session_with_environment"),
268
+ input: vaultSessionInputSchema.optional(),
269
+ output: vaultSessionOutputSchema.optional(),
270
+ })
271
+ .strict(),
272
+ ])
273
+ .superRefine((value, context) => {
274
+ if (value.output_withheld && value.output !== undefined) {
275
+ context.addIssue({ code: z.ZodIssueCode.custom, message: "withheld Vault output must not contain result content" });
276
+ }
277
+ });
278
+ const vaultActionStoppedSchema = z.object({ status: z.literal("stopped"), error: z.string().min(1) }).strict();
279
+ const vaultActionResponseSchema = z.union([vaultActionRecordedSchema, vaultActionStoppedSchema]);
258
280
 
259
281
  export const responseContracts = Object.freeze({
260
282
  connectorTargets: z.array(connectorTargetSchema),
@@ -266,12 +288,25 @@ export const responseContracts = Object.freeze({
266
288
  vaultAction: vaultActionResponseSchema,
267
289
  });
268
290
 
269
- export function projectGatewaySuccess(schema, value) {
291
+ export function projectGatewaySuccess(schema, value, expected = undefined) {
270
292
  const result = schema.safeParse(value);
271
293
  if (!result.success) {
272
- const error = new Error("Gateway success response failed MCP contract validation.");
273
- error.code = "gateway_response_contract_invalid";
274
- throw error;
294
+ throw gatewayContractError();
295
+ }
296
+ if (expected && result.data?.status !== "stopped") {
297
+ for (const [field, expectedValue] of Object.entries(expected)) {
298
+ if (expectedValue !== undefined && result.data?.[field] !== expectedValue) {
299
+ throw gatewayContractError();
300
+ }
301
+ }
302
+ } else if (expected && (expected.request_id !== undefined || expected.status !== undefined)) {
303
+ throw gatewayContractError();
275
304
  }
276
305
  return result.data;
277
306
  }
307
+
308
+ function gatewayContractError() {
309
+ const error = new Error("Gateway success response failed MCP contract validation.");
310
+ error.code = "gateway_response_contract_invalid";
311
+ return error;
312
+ }
package/dist/server.js CHANGED
@@ -113,7 +113,7 @@ server.tool(
113
113
  },
114
114
  { signal },
115
115
  ),
116
- (value) => projectGatewaySuccess(responseContracts.connectorActionCall, value),
116
+ (value) => projectGatewaySuccess(responseContracts.connectorActionCall, value, { target_ref, action_name }),
117
117
  { idempotencyKey: idempotency_key },
118
118
  );
119
119
  },
@@ -129,7 +129,7 @@ server.tool(
129
129
  async ({ request_id }, { signal }) => {
130
130
  return jsonToolResult(
131
131
  () => apiGet(`/api/mcp/connector-action-requests/${request_id}`, { signal }),
132
- (value) => projectGatewaySuccess(responseContracts.connectorActionRequest, value),
132
+ (value) => projectGatewaySuccess(responseContracts.connectorActionRequest, value, { request_id }),
133
133
  );
134
134
  },
135
135
  );
@@ -154,7 +154,7 @@ server.tool(
154
154
 
155
155
  server.tool(
156
156
  "call_vault_action",
157
- "Run a Vault action under the configured project capability. Prompt waits for local approval; Always executes immediately through the same tracked request path. generate_item input accepts name, secret_type, generator_kind, provider, environment, description, expires_at, expiry_warning_days, tags (string array), usage_notes (array of {location, notes}), and shared_project_ids (integer array). restart_session_with_environment input requires target_ref and items with item_id, source_project_id, and optional replace_existing. Never include raw secret values.",
157
+ "Run a Vault action under the configured project capability. Prompt waits for local approval; Always executes immediately through the same tracked request path. generate_item input accepts name, optional secret_type (defaults to generic_secret), generator_kind, provider, environment, description, expires_at, expiry_warning_days, tags (string array), usage_notes (array of {location, notes}), and shared_project_ids (integer array). restart_session_with_environment input requires target_ref and items with item_id, source_project_id, and optional replace_existing. Never include raw secret values.",
158
158
  callVaultActionSchema,
159
159
  externalActionAnnotations,
160
160
  async ({ project_ref, action_name, input, reason, idempotency_key }, { signal }) => {
@@ -171,7 +171,7 @@ server.tool(
171
171
  },
172
172
  { signal },
173
173
  ),
174
- (value) => projectGatewaySuccess(responseContracts.vaultAction, value),
174
+ (value) => projectGatewaySuccess(responseContracts.vaultAction, value, { project_ref, action_name }),
175
175
  { idempotencyKey: idempotency_key },
176
176
  );
177
177
  },
@@ -185,7 +185,7 @@ server.tool(
185
185
  async ({ request_id }, { signal }) => {
186
186
  return jsonToolResult(
187
187
  () => apiGet(`/api/mcp/vault-action-requests/${request_id}`, { signal }),
188
- (value) => projectGatewaySuccess(responseContracts.vaultAction, value),
188
+ (value) => projectGatewaySuccess(responseContracts.vaultAction, value, { request_id }),
189
189
  );
190
190
  },
191
191
  );
@@ -198,7 +198,7 @@ server.tool(
198
198
  async ({ request_id }, { signal }) => {
199
199
  return jsonToolResult(
200
200
  () => apiPost(`/api/mcp/vault-action-requests/${request_id}/cancel`, {}, { requestID: request_id, signal }),
201
- (value) => projectGatewaySuccess(responseContracts.vaultAction, value),
201
+ (value) => projectGatewaySuccess(responseContracts.vaultAction, value, { request_id, status: "canceled" }),
202
202
  { requestID: request_id },
203
203
  );
204
204
  },
@@ -2,11 +2,16 @@ import { z } from "zod";
2
2
  import { idempotencyKeySchema } from "./idempotency-key.js";
3
3
 
4
4
  export const listVaultItemsSchema = {
5
- project_ref: z.string().min(1).optional().describe("Optional project id or slug. Omit to list every readable project."),
5
+ project_ref: z
6
+ .string()
7
+ .trim()
8
+ .min(1)
9
+ .optional()
10
+ .describe("Optional project reference. Prefer id:<id> or slug:<slug>; omit to list every readable project."),
6
11
  };
7
12
 
8
13
  export const callVaultActionSchema = {
9
- project_ref: z.string().min(1).describe("Owning project id or slug."),
14
+ project_ref: z.string().trim().min(1).describe("Owning project reference. Prefer id:<id> or slug:<slug>."),
10
15
  action_name: z.enum(["generate_item", "restart_session_with_environment"]),
11
16
  input: z.record(z.unknown()).describe("Action input. Never include raw secret values."),
12
17
  reason: z.string().min(1).describe("Why this Vault action is needed."),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipermission/mcp",
3
- "version": "0.2.55",
3
+ "version": "0.2.56",
4
4
  "mcpName": "io.github.aipermission/aipermission-mcp",
5
5
  "description": "Local-only MCP bridge for the aipermission gateway.",
6
6
  "license": "AGPL-3.0-only",
@@ -58,6 +58,6 @@
58
58
  "@eslint/js": "^10.0.1",
59
59
  "eslint": "^10.10.0",
60
60
  "globals": "^17.11.0",
61
- "prettier": "^3.9.6"
61
+ "prettier": "^3.9.8"
62
62
  }
63
63
  }
package/server.json CHANGED
@@ -3,12 +3,12 @@
3
3
  "name": "io.github.aipermission/aipermission-mcp",
4
4
  "title": "AIPermission",
5
5
  "description": "Local-only MCP bridge for the AIPermission gateway.",
6
- "version": "0.2.55",
6
+ "version": "0.2.56",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "@aipermission/mcp",
11
- "version": "0.2.55",
11
+ "version": "0.2.56",
12
12
  "transport": {
13
13
  "type": "stdio"
14
14
  }