@aipermission/mcp 0.2.54 → 0.2.55
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 +6 -3
- package/dist/config.js +1 -1
- package/dist/generated-connector-contract.js +27 -0
- package/dist/init.js +25 -14
- package/dist/response-contracts.js +24 -11
- package/dist/results.js +13 -2
- package/dist/server.js +5 -5
- package/package.json +2 -2
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ does this automatically.
|
|
|
86
86
|
## Tools
|
|
87
87
|
|
|
88
88
|
`AIPERMISSION_HTTP_TIMEOUT_MS` sets the gateway request deadline (default:
|
|
89
|
-
|
|
89
|
+
95000 milliseconds; maximum: 600000). The value must be a positive base-10
|
|
90
90
|
integer; invalid configuration stops the bridge instead of silently changing
|
|
91
91
|
the deadline. It covers both response headers and the complete response body,
|
|
92
92
|
including streamed bodies. A timeout does not prove that a submitted
|
|
@@ -228,8 +228,11 @@ disclose secrets. Never automatically retry `submission_unknown`.
|
|
|
228
228
|
|
|
229
229
|
Connector responses can include `approval_pending` or `running`. Poll
|
|
230
230
|
`get_connector_action_request(request_id)` until the request reaches a terminal
|
|
231
|
-
status.
|
|
232
|
-
|
|
231
|
+
status. Failed action calls carry the MCP `isError` flag; reading a recorded
|
|
232
|
+
failed request is a successful read and does not. A `stopped` call response is a
|
|
233
|
+
gateway control result, not a recorded request. `outcome_unknown` is terminal
|
|
234
|
+
and means the gateway could not prove the remote outcome after interruption;
|
|
235
|
+
inspect target state or ask the operator
|
|
233
236
|
before retrying. Gateway API errors with that status retain their request id,
|
|
234
237
|
assistant hint, and bounded retry delay in the MCP error envelope. MCP tool
|
|
235
238
|
responses never include file contents, gateway
|
package/dist/config.js
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Code generated by backend/cmd/openapi. DO NOT EDIT.
|
|
2
|
+
|
|
3
|
+
export const connectorActionStatuses = Object.freeze([
|
|
4
|
+
"completed",
|
|
5
|
+
"failed",
|
|
6
|
+
"canceled",
|
|
7
|
+
"running",
|
|
8
|
+
"approval_pending",
|
|
9
|
+
"blocked",
|
|
10
|
+
"stale",
|
|
11
|
+
"declined",
|
|
12
|
+
"error",
|
|
13
|
+
"outcome_unknown",
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const connectorRetryClasses = Object.freeze(["read_only", "idempotent", "conditional", "non_idempotent"]);
|
|
17
|
+
|
|
18
|
+
export const executionRules = Object.freeze(["always_run", "approval_required", "blocked"]);
|
|
19
|
+
|
|
20
|
+
export const connectorActionResponseRequiredFields = Object.freeze([
|
|
21
|
+
"status",
|
|
22
|
+
"request_id",
|
|
23
|
+
"target_ref",
|
|
24
|
+
"connector_kind",
|
|
25
|
+
"action_name",
|
|
26
|
+
"retry_policy",
|
|
27
|
+
]);
|
package/dist/init.js
CHANGED
|
@@ -609,29 +609,40 @@ function removeTOMLServer(source, name) {
|
|
|
609
609
|
}
|
|
610
610
|
|
|
611
611
|
function scanTOMLHeader(line, state) {
|
|
612
|
-
if (state.multiline) {
|
|
613
|
-
if (hasMultilineDelimiter(line, state.multiline)) state.multiline = "";
|
|
614
|
-
return null;
|
|
615
|
-
}
|
|
616
612
|
const trimmed = line.trimStart();
|
|
617
|
-
if (trimmed.startsWith("[")) {
|
|
613
|
+
if (!state.multiline && trimmed.startsWith("[")) {
|
|
618
614
|
try {
|
|
619
615
|
return findMarkerPath(parseTOML(`${line}\n__aipermission_header_marker = true\n`));
|
|
620
616
|
} catch {
|
|
621
|
-
|
|
617
|
+
// A string can start with a bracket; scan it before the next line.
|
|
622
618
|
}
|
|
623
619
|
}
|
|
624
|
-
|
|
625
|
-
if (!hasMultilineDelimiter(line, delimiter)) continue;
|
|
626
|
-
if ((line.split(delimiter).length - 1) % 2 === 1) state.multiline = delimiter;
|
|
627
|
-
break;
|
|
628
|
-
}
|
|
620
|
+
scanTOMLStrings(line, state);
|
|
629
621
|
return null;
|
|
630
622
|
}
|
|
631
623
|
|
|
632
|
-
function
|
|
633
|
-
|
|
634
|
-
|
|
624
|
+
function scanTOMLStrings(line, state) {
|
|
625
|
+
let quote = state.multiline;
|
|
626
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
627
|
+
const character = line[index];
|
|
628
|
+
if (!quote && character === "#") break;
|
|
629
|
+
if (quote.startsWith('"') && character === "\\") {
|
|
630
|
+
index += 1;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (quote) {
|
|
634
|
+
if (line.startsWith(quote, index)) {
|
|
635
|
+
index += quote.length - 1;
|
|
636
|
+
quote = "";
|
|
637
|
+
}
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
if (character !== '"' && character !== "'") continue;
|
|
641
|
+
const triple = character.repeat(3);
|
|
642
|
+
quote = line.startsWith(triple, index) ? triple : character;
|
|
643
|
+
index += quote.length - 1;
|
|
644
|
+
}
|
|
645
|
+
state.multiline = quote.length === 3 ? quote : "";
|
|
635
646
|
}
|
|
636
647
|
|
|
637
648
|
function findMarkerPath(value, pathParts = []) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { connectorActionStatuses, connectorRetryClasses } from "./generated-connector-contract.js";
|
|
2
3
|
|
|
3
4
|
const positiveID = z.number().int().positive();
|
|
4
5
|
const nonNegativeInteger = z.number().int().nonnegative();
|
|
@@ -97,7 +98,7 @@ const outputHintSchema = z
|
|
|
97
98
|
.strict();
|
|
98
99
|
const retryPolicySchema = z
|
|
99
100
|
.object({
|
|
100
|
-
class: z.
|
|
101
|
+
class: z.enum([...connectorRetryClasses]),
|
|
101
102
|
precondition_fields: z.array(z.string()).optional(),
|
|
102
103
|
guidance: z.string(),
|
|
103
104
|
})
|
|
@@ -119,28 +120,39 @@ const actionDefinitionSchema = z
|
|
|
119
120
|
|
|
120
121
|
const connectorActionsSchema = z.object({ items: z.array(actionDefinitionSchema) }).strict();
|
|
121
122
|
|
|
122
|
-
const
|
|
123
|
+
const connectorActionRequestSchema = z
|
|
123
124
|
.object({
|
|
124
|
-
status: z.
|
|
125
|
-
request_id: positiveID
|
|
126
|
-
target_ref: z.string()
|
|
125
|
+
status: z.enum([...connectorActionStatuses]),
|
|
126
|
+
request_id: positiveID,
|
|
127
|
+
target_ref: z.string(),
|
|
127
128
|
target_name: z.string().optional(),
|
|
128
|
-
connector_kind: z.string()
|
|
129
|
+
connector_kind: z.string(),
|
|
129
130
|
profile_label: z.string().optional(),
|
|
130
|
-
action_name: z.string()
|
|
131
|
+
action_name: z.string(),
|
|
131
132
|
input: z.record(z.unknown()).optional(),
|
|
132
133
|
// Connector-owned output is intentionally opaque. The gateway credential
|
|
133
134
|
// boundary redacts values; this schema owns only the shared MCP envelope.
|
|
134
135
|
output: z.unknown().optional(),
|
|
135
136
|
display_text: z.string().optional(),
|
|
136
137
|
error: z.string().optional(),
|
|
137
|
-
retry_policy: retryPolicySchema
|
|
138
|
-
retry_after_seconds: nonNegativeInteger.optional(),
|
|
138
|
+
retry_policy: retryPolicySchema,
|
|
139
|
+
retry_after_seconds: nonNegativeInteger.max(3600).optional(),
|
|
139
140
|
assistant_hint: z.string().optional(),
|
|
140
141
|
output_withheld: z.boolean().optional(),
|
|
141
142
|
replayed: z.boolean().optional(),
|
|
142
143
|
})
|
|
143
|
-
.strict()
|
|
144
|
+
.strict()
|
|
145
|
+
.superRefine((value, context) => {
|
|
146
|
+
if (value.output_withheld) {
|
|
147
|
+
if (value.input !== undefined || value.output !== undefined || value.display_text !== undefined || value.error !== undefined) {
|
|
148
|
+
context.addIssue({ code: z.ZodIssueCode.custom, message: "withheld action output must not contain request or result content" });
|
|
149
|
+
}
|
|
150
|
+
} else if (!value.target_ref || !value.connector_kind || !value.action_name) {
|
|
151
|
+
context.addIssue({ code: z.ZodIssueCode.custom, message: "action response identity is required" });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const connectorActionStoppedSchema = z.object({ status: z.literal("stopped"), error: z.string().min(1) }).strict();
|
|
155
|
+
const connectorActionCallSchema = z.union([connectorActionRequestSchema, connectorActionStoppedSchema]);
|
|
144
156
|
|
|
145
157
|
const vaultItemSchema = z
|
|
146
158
|
.object({
|
|
@@ -248,7 +260,8 @@ export const responseContracts = Object.freeze({
|
|
|
248
260
|
connectorTargets: z.array(connectorTargetSchema),
|
|
249
261
|
connectorHelp: connectorHelpSchema,
|
|
250
262
|
connectorActions: connectorActionsSchema,
|
|
251
|
-
|
|
263
|
+
connectorActionCall: connectorActionCallSchema,
|
|
264
|
+
connectorActionRequest: connectorActionRequestSchema,
|
|
252
265
|
vaultItems: vaultItemsSchema,
|
|
253
266
|
vaultAction: vaultActionResponseSchema,
|
|
254
267
|
});
|
package/dist/results.js
CHANGED
|
@@ -45,7 +45,7 @@ export function errorResult(error) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export async function jsonToolResult(callback, project, mutationContext = null) {
|
|
48
|
+
export async function jsonToolResult(callback, project, mutationContext = null, isFailure = () => false) {
|
|
49
49
|
if (typeof project !== "function") {
|
|
50
50
|
return errorResult(new Error("MCP tool result projector is required."));
|
|
51
51
|
}
|
|
@@ -56,12 +56,23 @@ export async function jsonToolResult(callback, project, mutationContext = null)
|
|
|
56
56
|
return errorResult(error);
|
|
57
57
|
}
|
|
58
58
|
try {
|
|
59
|
-
|
|
59
|
+
const projected = project(value);
|
|
60
|
+
const result = textResult(projected);
|
|
61
|
+
return isFailure(projected) ? { ...result, isError: true } : result;
|
|
60
62
|
} catch (error) {
|
|
61
63
|
return errorResult(mutationContext ? projectionOutcomeUnknown(error, mutationContext) : error);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
export function jsonActionToolResult(callback, project, mutationContext) {
|
|
68
|
+
return jsonToolResult(
|
|
69
|
+
callback,
|
|
70
|
+
project,
|
|
71
|
+
mutationContext,
|
|
72
|
+
(value) => !["completed", "approval_pending", "running"].includes(value.status),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
65
76
|
function projectionOutcomeUnknown(cause, context) {
|
|
66
77
|
const error = new Error(
|
|
67
78
|
"The gateway accepted the mutation, but its response failed MCP contract validation. The operation may have completed.",
|
package/dist/server.js
CHANGED
|
@@ -17,7 +17,7 @@ import { parseHTTPTimeout } from "./config.js";
|
|
|
17
17
|
import { idempotencyKeySchema } from "./idempotency-key.js";
|
|
18
18
|
import { normalizeLocalAPIURL } from "./local-url.js";
|
|
19
19
|
import { projectGatewaySuccess, responseContracts } from "./response-contracts.js";
|
|
20
|
-
import { jsonToolResult } from "./results.js";
|
|
20
|
+
import { jsonActionToolResult, jsonToolResult } from "./results.js";
|
|
21
21
|
import { externalActionAnnotations, localMutationAnnotations, localReadAnnotations } from "./tool-annotations.js";
|
|
22
22
|
|
|
23
23
|
const packageMetadata = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
@@ -100,7 +100,7 @@ server.tool(
|
|
|
100
100
|
},
|
|
101
101
|
externalActionAnnotations,
|
|
102
102
|
async ({ target_ref, action_name, input, reason, idempotency_key }, { signal }) => {
|
|
103
|
-
return
|
|
103
|
+
return jsonActionToolResult(
|
|
104
104
|
() =>
|
|
105
105
|
apiPost(
|
|
106
106
|
"/api/mcp/connector-actions/call",
|
|
@@ -113,7 +113,7 @@ server.tool(
|
|
|
113
113
|
},
|
|
114
114
|
{ signal },
|
|
115
115
|
),
|
|
116
|
-
(value) => projectGatewaySuccess(responseContracts.
|
|
116
|
+
(value) => projectGatewaySuccess(responseContracts.connectorActionCall, value),
|
|
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.
|
|
132
|
+
(value) => projectGatewaySuccess(responseContracts.connectorActionRequest, value),
|
|
133
133
|
);
|
|
134
134
|
},
|
|
135
135
|
);
|
|
@@ -158,7 +158,7 @@ server.tool(
|
|
|
158
158
|
callVaultActionSchema,
|
|
159
159
|
externalActionAnnotations,
|
|
160
160
|
async ({ project_ref, action_name, input, reason, idempotency_key }, { signal }) => {
|
|
161
|
-
return
|
|
161
|
+
return jsonActionToolResult(
|
|
162
162
|
() =>
|
|
163
163
|
apiPost(
|
|
164
164
|
"/api/mcp/vault-actions/call",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aipermission/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.55",
|
|
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",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
53
53
|
"smol-toml": "1.8.0",
|
|
54
|
-
"yaml": "2.9.
|
|
54
|
+
"yaml": "2.9.1",
|
|
55
55
|
"zod": "3.25.76"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
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.
|
|
6
|
+
"version": "0.2.55",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "@aipermission/mcp",
|
|
11
|
-
"version": "0.2.
|
|
11
|
+
"version": "0.2.55",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|