@opengeni/core 4.0.3-canary.36079201993001 → 4.0.3
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/application/new-session-drafts.d.ts +3 -3
- package/dist/domain/capabilities.d.ts +1 -2
- package/dist/domain/knowledge-messages.d.ts +2 -2
- package/dist/domain/pr-review.d.ts +2 -2
- package/dist/index.js +22 -97
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/src/application/session-mcp-credential-rotation.ts +15 -57
- package/src/domain/capabilities.ts +1 -17
- package/dist/domain/session-native-connection-replacement.d.ts +0 -14
- package/src/domain/session-native-connection-replacement.ts +0 -64
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/core",
|
|
3
|
-
"version": "4.0.3
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "OpenGeni framework-agnostic core: the domain, access, and billing layers (neutral access, off-HTTP V2 surface). Behavior-preserving extraction from apps/api — keeps Hono's HTTPException for error throwing (typed-errors cleanup deferred).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -50,17 +50,17 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
53
|
-
"@opengeni/capabilities": "^0.3.4
|
|
54
|
-
"@opengeni/codex": "^0.2.26
|
|
55
|
-
"@opengeni/config": "^2.1.1
|
|
56
|
-
"@opengeni/contracts": "^5.1.1
|
|
57
|
-
"@opengeni/db": "^6.0.3
|
|
58
|
-
"@opengeni/documents": "^0.8.34
|
|
59
|
-
"@opengeni/events": "^0.4.32
|
|
60
|
-
"@opengeni/network": "^0.3.1
|
|
61
|
-
"@opengeni/observability": "^0.8.32
|
|
62
|
-
"@opengeni/runtime": "^4.0.3
|
|
63
|
-
"@opengeni/storage": "^0.2.133
|
|
53
|
+
"@opengeni/capabilities": "^0.3.4",
|
|
54
|
+
"@opengeni/codex": "^0.2.26",
|
|
55
|
+
"@opengeni/config": "^2.1.1",
|
|
56
|
+
"@opengeni/contracts": "^5.1.1",
|
|
57
|
+
"@opengeni/db": "^6.0.3",
|
|
58
|
+
"@opengeni/documents": "^0.8.34",
|
|
59
|
+
"@opengeni/events": "^0.4.32",
|
|
60
|
+
"@opengeni/network": "^0.3.1",
|
|
61
|
+
"@opengeni/observability": "^0.8.32",
|
|
62
|
+
"@opengeni/runtime": "^4.0.3",
|
|
63
|
+
"@opengeni/storage": "^0.2.133",
|
|
64
64
|
"hono": "^4.12.18",
|
|
65
65
|
"zod": "^4.2.1"
|
|
66
66
|
},
|
|
@@ -27,11 +27,6 @@ import {
|
|
|
27
27
|
import { requireVariableSetEncryption } from "../domain/environments";
|
|
28
28
|
import { normalizedSessionMcpCredentialHeaders } from "../domain/sessions";
|
|
29
29
|
import { externalContinuationCommitAuthorizer } from "./external-continuation";
|
|
30
|
-
import {
|
|
31
|
-
listOwnConnectionAccountsForGrant,
|
|
32
|
-
personalConnectionDelegationSourceForGrant,
|
|
33
|
-
} from "../domain/personal-connection-delegations";
|
|
34
|
-
import { nativeSessionConnectionReplacement } from "../domain/session-native-connection-replacement";
|
|
35
30
|
|
|
36
31
|
/** HMAC domains separate request identity from key availability. The key tag
|
|
37
32
|
* carries no request material and neither fingerprint is returned publicly. */
|
|
@@ -85,20 +80,11 @@ export async function rotateSessionMcpCredentialsForRequest(
|
|
|
85
80
|
id: update.id,
|
|
86
81
|
expectedCredentialVersion: update.expectedCredentialVersion,
|
|
87
82
|
expectedServerUrl: update.expectedServerUrl,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
: {}),
|
|
94
|
-
}
|
|
95
|
-
: {
|
|
96
|
-
headers: Object.fromEntries(
|
|
97
|
-
Object.entries(normalizedSessionMcpCredentialHeaders(update.headers))
|
|
98
|
-
.map(([name, value]) => [name.toLowerCase(), value] as const)
|
|
99
|
-
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0)),
|
|
100
|
-
),
|
|
101
|
-
}),
|
|
83
|
+
headers: Object.fromEntries(
|
|
84
|
+
Object.entries(normalizedSessionMcpCredentialHeaders(update.headers))
|
|
85
|
+
.map(([name, value]) => [name.toLowerCase(), value] as const)
|
|
86
|
+
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0)),
|
|
87
|
+
),
|
|
102
88
|
}))
|
|
103
89
|
.sort((left, right) => (left.id < right.id ? -1 : left.id > right.id ? 1 : 0)),
|
|
104
90
|
};
|
|
@@ -116,45 +102,20 @@ export async function rotateSessionMcpCredentialsForRequest(
|
|
|
116
102
|
actorType: grant.principalKind === "service" ? ("service" as const) : ("human" as const),
|
|
117
103
|
};
|
|
118
104
|
const externalAuthorize = externalContinuationCommitAuthorizer(authorization);
|
|
119
|
-
let authorizedGrant = grant;
|
|
120
105
|
try {
|
|
121
106
|
return await rotateSessionMcpCredentialsAtomically(deps.db, {
|
|
122
107
|
...scope,
|
|
123
108
|
operationKey: request.operationKey,
|
|
124
109
|
...sessionMcpRotationFingerprint(encryptionKey, scope, request),
|
|
125
|
-
updates: request.updates.map((update) =>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
name,
|
|
135
|
-
encryptVariableSetValue(encryptionKey, value),
|
|
136
|
-
]),
|
|
137
|
-
),
|
|
138
|
-
},
|
|
139
|
-
),
|
|
140
|
-
resolveNativeConnection: async (tx, server, nativeConnectionId, replacementServerUrl) => {
|
|
141
|
-
const connections = await listOwnConnectionAccountsForGrant(tx, authorizedGrant);
|
|
142
|
-
const source = personalConnectionDelegationSourceForGrant(authorizedGrant);
|
|
143
|
-
try {
|
|
144
|
-
return nativeSessionConnectionReplacement({
|
|
145
|
-
accountId: grant.accountId,
|
|
146
|
-
workspaceId: grant.workspaceId,
|
|
147
|
-
subjectId: source.kind === "subject" ? source.subjectId : null,
|
|
148
|
-
serverUrl: server.url,
|
|
149
|
-
...(replacementServerUrl ? { replacementServerUrl } : {}),
|
|
150
|
-
currentRef: server.connectionRef,
|
|
151
|
-
nativeConnectionId,
|
|
152
|
-
connections,
|
|
153
|
-
});
|
|
154
|
-
} catch {
|
|
155
|
-
throw new SessionMcpCredentialRotationError("connection_unavailable");
|
|
156
|
-
}
|
|
157
|
-
},
|
|
110
|
+
updates: request.updates.map(({ headers, ...update }) => ({
|
|
111
|
+
...update,
|
|
112
|
+
headersEncrypted: Object.fromEntries(
|
|
113
|
+
Object.entries(headers).map(([name, value]) => [
|
|
114
|
+
name,
|
|
115
|
+
encryptVariableSetValue(encryptionKey, value),
|
|
116
|
+
]),
|
|
117
|
+
),
|
|
118
|
+
})),
|
|
158
119
|
authorize: async (tx) => {
|
|
159
120
|
await externalAuthorize?.(tx);
|
|
160
121
|
const fresh = await reauthorize(tx);
|
|
@@ -184,7 +145,6 @@ export async function rotateSessionMcpCredentialsForRequest(
|
|
|
184
145
|
operation: "session.mcp.credentials.rotate",
|
|
185
146
|
surface: "http",
|
|
186
147
|
});
|
|
187
|
-
authorizedGrant = fresh;
|
|
188
148
|
},
|
|
189
149
|
});
|
|
190
150
|
} catch (error) {
|
|
@@ -202,9 +162,7 @@ export async function rotateSessionMcpCredentialsForRequest(
|
|
|
202
162
|
? 403
|
|
203
163
|
: error.code === "not_found"
|
|
204
164
|
? 404
|
|
205
|
-
: error.code === "invalid_request" ||
|
|
206
|
-
error.code === "brokered_server" ||
|
|
207
|
-
error.code === "connection_unavailable"
|
|
165
|
+
: error.code === "invalid_request" || error.code === "brokered_server"
|
|
208
166
|
? 422
|
|
209
167
|
: 409;
|
|
210
168
|
throw new HTTPException(status, { message: `credential_rotation_${error.code}` });
|
|
@@ -4,7 +4,6 @@ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
|
4
4
|
import { isDeepStrictEqual } from "node:util";
|
|
5
5
|
import { withLockedCapabilityInstallation } from "@opengeni/db/capability-reconciliation";
|
|
6
6
|
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
7
|
-
import { pinnedFetch } from "@opengeni/network";
|
|
8
7
|
import {
|
|
9
8
|
CapabilityCatalogItem,
|
|
10
9
|
capabilityCatalogItemIsTrustedForExposure,
|
|
@@ -345,12 +344,7 @@ export async function prepareCapabilityEnable(input: EnableCapabilityInput) {
|
|
|
345
344
|
...installationMetadata,
|
|
346
345
|
...(connectionRef && !headers
|
|
347
346
|
? authDeferredMcpConnectivity()
|
|
348
|
-
: await validateMcpCapabilityConnection(
|
|
349
|
-
item,
|
|
350
|
-
input.probeMcpServer,
|
|
351
|
-
headers ?? undefined,
|
|
352
|
-
input.settings,
|
|
353
|
-
)),
|
|
347
|
+
: await validateMcpCapabilityConnection(item, input.probeMcpServer, headers ?? undefined)),
|
|
354
348
|
};
|
|
355
349
|
if (connectionRef) {
|
|
356
350
|
installationConfig.connectionRef = connectionRef;
|
|
@@ -675,7 +669,6 @@ export type McpCapabilityProbeInput = {
|
|
|
675
669
|
url: string;
|
|
676
670
|
timeoutMs: number;
|
|
677
671
|
headers?: Record<string, string>;
|
|
678
|
-
settings?: Settings;
|
|
679
672
|
};
|
|
680
673
|
|
|
681
674
|
export type McpCapabilityProbeResult = {
|
|
@@ -690,7 +683,6 @@ export async function validateMcpCapabilityConnection(
|
|
|
690
683
|
item: CapabilityCatalogItem,
|
|
691
684
|
probe: McpCapabilityProbe = probeStreamableHttpMcpServer,
|
|
692
685
|
headers?: Record<string, string>,
|
|
693
|
-
settings?: Settings,
|
|
694
686
|
): Promise<Record<string, unknown>> {
|
|
695
687
|
if (item.kind !== "mcp") {
|
|
696
688
|
return {};
|
|
@@ -707,7 +699,6 @@ export async function validateMcpCapabilityConnection(
|
|
|
707
699
|
url: item.endpointUrl,
|
|
708
700
|
timeoutMs: mcpCapabilityProbeTimeoutMs,
|
|
709
701
|
...(headers ? { headers } : {}),
|
|
710
|
-
...(settings ? { settings } : {}),
|
|
711
702
|
});
|
|
712
703
|
return {
|
|
713
704
|
mcpConnectivity: {
|
|
@@ -726,8 +717,6 @@ export async function validateMcpCapabilityConnection(
|
|
|
726
717
|
async function probeStreamableHttpMcpServer(
|
|
727
718
|
input: McpCapabilityProbeInput,
|
|
728
719
|
): Promise<McpCapabilityProbeResult> {
|
|
729
|
-
const settings = input.settings;
|
|
730
|
-
if (!settings) throw new Error("Network policy is unavailable for MCP validation");
|
|
731
720
|
const controller = new AbortController();
|
|
732
721
|
const timeout = setTimeout(() => controller.abort(), input.timeoutMs);
|
|
733
722
|
const client = new Client(
|
|
@@ -740,11 +729,6 @@ async function probeStreamableHttpMcpServer(
|
|
|
740
729
|
signal: controller.signal,
|
|
741
730
|
...(input.headers ? { headers: input.headers } : {}),
|
|
742
731
|
},
|
|
743
|
-
fetch: (url, init) =>
|
|
744
|
-
pinnedFetch(url, init, settings, {
|
|
745
|
-
label: "MCP capability probe",
|
|
746
|
-
requireHttpsOutsideLocalTest: true,
|
|
747
|
-
}),
|
|
748
732
|
});
|
|
749
733
|
await client.connect(transport as unknown as Transport, {
|
|
750
734
|
timeout: input.timeoutMs,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ConnectionMetadata, McpServerConnectionRef } from "@opengeni/contracts";
|
|
2
|
-
/** Selection is restricted to the caller's already-authorized inventory. This
|
|
3
|
-
* projection grants no execution authority: subsequent admission and physical
|
|
4
|
-
* credential use still validate the exact native account normally. */
|
|
5
|
-
export declare function nativeSessionConnectionReplacement(input: {
|
|
6
|
-
accountId: string;
|
|
7
|
-
workspaceId: string;
|
|
8
|
-
subjectId: string | null;
|
|
9
|
-
serverUrl: string;
|
|
10
|
-
replacementServerUrl?: string;
|
|
11
|
-
currentRef: McpServerConnectionRef | null;
|
|
12
|
-
nativeConnectionId: string;
|
|
13
|
-
connections: ConnectionMetadata[];
|
|
14
|
-
}): McpServerConnectionRef;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { ConnectionMetadata, McpServerConnectionRef } from "@opengeni/contracts";
|
|
2
|
-
import { connectionMetadataMatchesBinding } from "@opengeni/db/session-mcp-credential-rotation";
|
|
3
|
-
|
|
4
|
-
/** Selection is restricted to the caller's already-authorized inventory. This
|
|
5
|
-
* projection grants no execution authority: subsequent admission and physical
|
|
6
|
-
* credential use still validate the exact native account normally. */
|
|
7
|
-
export function nativeSessionConnectionReplacement(input: {
|
|
8
|
-
accountId: string;
|
|
9
|
-
workspaceId: string;
|
|
10
|
-
subjectId: string | null;
|
|
11
|
-
serverUrl: string;
|
|
12
|
-
replacementServerUrl?: string;
|
|
13
|
-
currentRef: McpServerConnectionRef | null;
|
|
14
|
-
nativeConnectionId: string;
|
|
15
|
-
connections: ConnectionMetadata[];
|
|
16
|
-
}): McpServerConnectionRef {
|
|
17
|
-
const connection = input.connections.find(
|
|
18
|
-
(candidate) => candidate.id === input.nativeConnectionId,
|
|
19
|
-
);
|
|
20
|
-
if (
|
|
21
|
-
!connection ||
|
|
22
|
-
connection.accountId !== input.accountId ||
|
|
23
|
-
connection.status !== "active" ||
|
|
24
|
-
(connection.subjectId === null
|
|
25
|
-
? connection.workspaceId !== input.workspaceId
|
|
26
|
-
: !input.subjectId || connection.subjectId !== input.subjectId || !connection.authorityId)
|
|
27
|
-
) {
|
|
28
|
-
throw new Error("native connection unavailable");
|
|
29
|
-
}
|
|
30
|
-
const {
|
|
31
|
-
authoritySource: _authoritySource,
|
|
32
|
-
hostBinding: _hostBinding,
|
|
33
|
-
accountSelection: _accountSelection,
|
|
34
|
-
...retained
|
|
35
|
-
} = input.currentRef ?? {};
|
|
36
|
-
// Preserve provider, resource, scope and selected-resource restrictions.
|
|
37
|
-
// Retired host identity is never interpreted as a native connection ID.
|
|
38
|
-
if (
|
|
39
|
-
input.currentRef &&
|
|
40
|
-
input.currentRef.providerDomain.toLowerCase() !== connection.providerDomain.toLowerCase()
|
|
41
|
-
)
|
|
42
|
-
throw new Error("native connection provider mismatch");
|
|
43
|
-
const ref: McpServerConnectionRef = {
|
|
44
|
-
...retained,
|
|
45
|
-
...(!input.currentRef?.resource && typeof connection.metadata.resource === "string"
|
|
46
|
-
? { resource: connection.metadata.resource }
|
|
47
|
-
: {}),
|
|
48
|
-
connectionId: connection.id,
|
|
49
|
-
providerDomain: connection.providerDomain,
|
|
50
|
-
kind: connection.kind,
|
|
51
|
-
subjectScope: connection.subjectId === null ? "workspace" : "subject",
|
|
52
|
-
};
|
|
53
|
-
const destination = input.replacementServerUrl ?? input.serverUrl;
|
|
54
|
-
// A redirect must be explicitly requested and bound in the account metadata;
|
|
55
|
-
// the broker's legacy provider-host fallback is insufficient for rebinding.
|
|
56
|
-
if (
|
|
57
|
-
input.replacementServerUrl &&
|
|
58
|
-
(typeof connection.metadata.mcpUrl !== "string" || !connection.metadata.mcpUrl.trim())
|
|
59
|
-
)
|
|
60
|
-
throw new Error("native connection destination unavailable");
|
|
61
|
-
if (!connectionMetadataMatchesBinding(connection, ref, destination))
|
|
62
|
-
throw new Error("native connection binding mismatch");
|
|
63
|
-
return ref;
|
|
64
|
-
}
|