@opengeni/core 0.28.0 → 1.0.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.
- package/README.md +1 -1
- package/dist/access/external-actor-authority.d.ts +47 -0
- package/dist/access/index.d.ts +182 -3
- package/dist/access/service-initiator.d.ts +3 -0
- package/dist/application/api-integration-servers.d.ts +20 -0
- package/dist/application/archived-session-imports.d.ts +6 -0
- package/dist/application/composer-submit.d.ts +24 -0
- package/dist/application/connect-authority.d.ts +13 -0
- package/dist/application/connect-operation.d.ts +31 -0
- package/dist/application/external-continuation.d.ts +15 -0
- package/dist/application/external-identity-lifecycle.d.ts +20 -0
- package/dist/application/external-link-work-admission.d.ts +12 -0
- package/dist/application/external-workspace-members.d.ts +34 -0
- package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
- package/dist/application/new-session-drafts.d.ts +39 -2
- package/dist/application/sandbox-recovery.d.ts +58 -0
- package/dist/application/session-commands.d.ts +26 -13
- package/dist/application/session-mcp-credential-rotation.d.ts +29 -0
- package/dist/application/session-tenancy.d.ts +19 -0
- package/dist/application/user-resource-grants.d.ts +20 -0
- package/dist/billing/limits.d.ts +11 -0
- package/dist/canonical-human-identities.d.ts +7 -2
- package/dist/canonical-human-identities.js +17 -4
- package/dist/canonical-human-identities.js.map +1 -1
- package/dist/{chunk-6WKPWE5S.js → chunk-2MKMRGSY.js} +108 -24
- package/dist/chunk-2MKMRGSY.js.map +1 -0
- package/dist/{chunk-NYPMQ7NO.js → chunk-GVO3N37G.js} +205 -37
- package/dist/chunk-GVO3N37G.js.map +1 -0
- package/dist/{chunk-JJU6I5XD.js → chunk-LCQXNR62.js} +36 -10
- package/dist/chunk-LCQXNR62.js.map +1 -0
- package/dist/chunk-MM6N7CTO.js +387 -0
- package/dist/chunk-MM6N7CTO.js.map +1 -0
- package/dist/chunk-QO5GVFFO.js +235 -0
- package/dist/chunk-QO5GVFFO.js.map +1 -0
- package/dist/codex-model-availability.d.ts +14 -0
- package/dist/default-session-model.d.ts +139 -0
- package/dist/dependencies.d.ts +87 -4
- package/dist/developer-setup-credential-ceiling.d.ts +10 -0
- package/dist/domain/agent-config-resolution.d.ts +49 -0
- package/dist/domain/automations.d.ts +58 -0
- package/dist/domain/capabilities.d.ts +22 -4
- package/dist/domain/company-profile-agent-admin.d.ts +25 -0
- package/dist/domain/connection-account-selection-error.d.ts +8 -0
- package/dist/domain/connector-tool-permissions.d.ts +36 -0
- package/dist/domain/direct-model-provider.d.ts +3 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
- package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
- package/dist/domain/editable-artifacts/ports.d.ts +10 -0
- package/dist/domain/editable-artifacts/service.d.ts +4 -3
- package/dist/domain/environments.d.ts +5 -9
- package/dist/domain/external-creation-attribution.d.ts +6 -0
- package/dist/domain/file-owner.d.ts +8 -0
- package/dist/domain/github-action-policies.d.ts +36 -0
- package/dist/domain/github-repository-bindings.d.ts +117 -0
- package/dist/domain/github-skill-source.d.ts +5 -0
- package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
- package/dist/domain/insights-timing.d.ts +16 -0
- package/dist/domain/insights-usage.d.ts +27 -0
- package/dist/domain/insights.d.ts +28 -1
- package/dist/domain/knowledge-files.d.ts +20 -0
- package/dist/domain/knowledge-messages.d.ts +39 -0
- package/dist/domain/knowledge-messages.test.d.ts +1 -0
- package/dist/domain/knowledge-preparation.d.ts +10 -0
- package/dist/domain/knowledge-preparation.test.d.ts +1 -0
- package/dist/domain/knowledge-search.d.ts +15 -0
- package/dist/domain/knowledge-search.test.d.ts +1 -0
- package/dist/domain/knowledge.d.ts +9 -0
- package/dist/domain/mcp-account-bindings.d.ts +19 -0
- package/dist/domain/mcp-account-routes.d.ts +21 -0
- package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
- package/dist/domain/organization-integration-catalog.d.ts +15 -0
- package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
- package/dist/domain/personal-connection-delegations.d.ts +87 -4
- package/dist/domain/pr-review.d.ts +249 -0
- package/dist/domain/resources.d.ts +24 -3
- package/dist/domain/scheduled-task-access.d.ts +247 -0
- package/dist/domain/scheduled-tasks.d.ts +103 -6
- package/dist/domain/session-connection-accounts.d.ts +16 -0
- package/dist/domain/session-file-access.d.ts +4 -0
- package/dist/domain/session-native-connection-replacement.d.ts +14 -0
- package/dist/domain/session-start-timing.d.ts +7 -0
- package/dist/domain/session-tool-policy.d.ts +93 -4
- package/dist/domain/sessions.d.ts +275 -28
- package/dist/domain/skill-imports.d.ts +1 -0
- package/dist/domain/skill-search.d.ts +30 -0
- package/dist/domain/skills.d.ts +117 -0
- package/dist/domain/slack-bot.d.ts +46 -1
- package/dist/domain/workspace-members.d.ts +8 -1
- package/dist/editable-artifact-live/application.d.ts +12 -3
- package/dist/editable-artifact-live/ports.d.ts +0 -1
- package/dist/editable-artifact-live/server.d.ts +5 -2
- package/dist/editable-artifact-live/ticket.d.ts +2 -2
- package/dist/editable-artifact-live/types.d.ts +14 -2
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifacts.js +10 -4
- package/dist/index.d.ts +49 -3
- package/dist/index.js +18826 -6847
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.d.ts +131 -0
- package/dist/managed-auth-session-sets.js +51 -0
- package/dist/managed-auth-session-sets.js.map +1 -0
- package/dist/managed-session.d.ts +98 -11
- package/dist/model-catalog.d.ts +117 -0
- package/dist/product-usage-metrics.d.ts +30 -0
- package/dist/rigs/index.d.ts +22 -5
- package/dist/rigs/provider-images.d.ts +1 -1
- package/dist/sandbox/fleet.d.ts +59 -10
- package/dist/sandbox/routing.d.ts +22 -2
- package/dist/sandbox/runtime-settings.d.ts +56 -0
- package/dist/session-authorization.d.ts +38 -1
- package/dist/site-session-origin.d.ts +9 -0
- package/dist/transcription.d.ts +12 -1
- package/dist/turn-surface.d.ts +18 -0
- package/dist/user-presence.d.ts +32 -0
- package/package.json +18 -11
- package/src/access/external-actor-authority.ts +94 -0
- package/src/access/index.ts +1386 -44
- package/src/access/service-initiator.ts +50 -0
- package/src/application/api-integration-servers.ts +215 -0
- package/src/application/archived-session-imports.ts +161 -0
- package/src/application/composer-submit.ts +77 -0
- package/src/application/connect-authority.ts +77 -0
- package/src/application/connect-operation.ts +80 -0
- package/src/application/external-continuation.ts +129 -0
- package/src/application/external-identity-lifecycle.ts +48 -0
- package/src/application/external-link-work-admission.ts +90 -0
- package/src/application/external-workspace-members.ts +232 -0
- package/src/application/modal-native-worker-host-transport.ts +263 -0
- package/src/application/new-session-drafts.ts +222 -29
- package/src/application/sandbox-recovery.ts +78 -0
- package/src/application/session-commands.ts +430 -73
- package/src/application/session-mcp-credential-rotation.ts +217 -0
- package/src/application/session-tenancy.ts +445 -0
- package/src/application/user-resource-grants.ts +154 -0
- package/src/billing/limits.ts +103 -24
- package/src/canonical-human-identities.ts +22 -3
- package/src/codex-model-availability.ts +136 -0
- package/src/default-session-model.ts +656 -0
- package/src/dependencies.ts +97 -5
- package/src/developer-setup-credential-ceiling.ts +60 -0
- package/src/domain/agent-config-resolution.ts +148 -0
- package/src/domain/automations.ts +270 -0
- package/src/domain/capabilities.ts +244 -151
- package/src/domain/company-profile-agent-admin.ts +56 -0
- package/src/domain/connection-account-selection-error.ts +19 -0
- package/src/domain/connector-tool-permissions.ts +371 -0
- package/src/domain/direct-model-provider.ts +62 -0
- package/src/domain/editable-artifacts/agent-application.ts +61 -15
- package/src/domain/editable-artifacts/durable-export.ts +100 -5
- package/src/domain/editable-artifacts/in-memory.ts +6 -0
- package/src/domain/editable-artifacts/ports.ts +11 -0
- package/src/domain/editable-artifacts/service.ts +32 -1
- package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
- package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
- package/src/domain/environments.ts +25 -45
- package/src/domain/external-creation-attribution.ts +22 -0
- package/src/domain/file-owner.ts +67 -0
- package/src/domain/github-action-policies.ts +169 -0
- package/src/domain/github-repository-bindings.ts +295 -0
- package/src/domain/github-skill-source.ts +233 -0
- package/src/domain/governed-learning-slack-publication.ts +157 -0
- package/src/domain/insights-timing.ts +95 -0
- package/src/domain/insights-usage.ts +67 -0
- package/src/domain/insights.ts +270 -169
- package/src/domain/knowledge-files.ts +46 -0
- package/src/domain/knowledge-messages.test.ts +91 -0
- package/src/domain/knowledge-messages.ts +106 -0
- package/src/domain/knowledge-preparation.test.ts +276 -0
- package/src/domain/knowledge-preparation.ts +147 -0
- package/src/domain/knowledge-search.test.ts +561 -0
- package/src/domain/knowledge-search.ts +256 -0
- package/src/domain/knowledge.ts +133 -0
- package/src/domain/mcp-account-bindings.ts +171 -0
- package/src/domain/mcp-account-routes.ts +121 -0
- package/src/domain/native-mcp-connection-admission.ts +14 -0
- package/src/domain/organization-integration-catalog.ts +88 -0
- package/src/domain/organization-membership-lifecycle.ts +10 -0
- package/src/domain/personal-connection-delegations.ts +758 -115
- package/src/domain/pr-review.ts +687 -0
- package/src/domain/resources.ts +182 -25
- package/src/domain/scheduled-task-access.ts +1052 -0
- package/src/domain/scheduled-tasks.ts +1952 -215
- package/src/domain/session-connection-accounts.ts +67 -0
- package/src/domain/session-file-access.ts +47 -0
- package/src/domain/session-native-connection-replacement.ts +64 -0
- package/src/domain/session-start-timing.ts +55 -0
- package/src/domain/session-tool-policy.ts +443 -19
- package/src/domain/sessions.ts +3417 -619
- package/src/domain/skill-imports.ts +137 -17
- package/src/domain/skill-search.ts +221 -0
- package/src/domain/skills.ts +104 -0
- package/src/domain/slack-bot.ts +128 -3
- package/src/domain/workspace-members.ts +34 -2
- package/src/editable-artifact-live/application.ts +29 -4
- package/src/editable-artifact-live/ports.ts +0 -1
- package/src/editable-artifact-live/server.ts +167 -13
- package/src/editable-artifact-live/ticket.ts +87 -6
- package/src/editable-artifact-live/types.ts +16 -2
- package/src/editable-artifact-live/wire.ts +0 -10
- package/src/index.ts +69 -4
- package/src/managed-auth-session-sets.ts +371 -0
- package/src/managed-session.ts +520 -2
- package/src/model-catalog.ts +873 -0
- package/src/product-usage-metrics.ts +101 -0
- package/src/rigs/index.ts +179 -63
- package/src/rigs/provider-images.ts +2 -3
- package/src/sandbox/fleet.ts +471 -59
- package/src/sandbox/routing.ts +160 -33
- package/src/sandbox/runtime-settings.ts +281 -0
- package/src/session-authorization.ts +262 -78
- package/src/site-session-origin.ts +22 -0
- package/src/transcription.ts +13 -1
- package/src/turn-surface.ts +45 -0
- package/src/user-presence.ts +134 -0
- package/dist/chunk-6WKPWE5S.js.map +0 -1
- package/dist/chunk-IBOEYG6N.js +0 -34
- package/dist/chunk-IBOEYG6N.js.map +0 -1
- package/dist/chunk-JJU6I5XD.js.map +0 -1
- package/dist/chunk-NYPMQ7NO.js.map +0 -1
- package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
- package/dist/domain/packs.d.ts +0 -55
- package/src/domain/durable-learning-slack-publication.ts +0 -202
- package/src/domain/packs.ts +0 -556
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
2
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
3
3
|
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
import { withLockedCapabilityInstallation } from "@opengeni/db/capability-reconciliation";
|
|
4
6
|
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
7
|
+
import { pinnedFetch } from "@opengeni/network";
|
|
5
8
|
import {
|
|
6
9
|
CapabilityCatalogItem,
|
|
7
10
|
capabilityCatalogItemIsTrustedForExposure,
|
|
@@ -16,6 +19,7 @@ import {
|
|
|
16
19
|
type EnableCapabilityRequest,
|
|
17
20
|
type McpServerConnectionRef,
|
|
18
21
|
type McpPersonalConnectionDelegation,
|
|
22
|
+
assertOrganizationIntegrationAllowed,
|
|
19
23
|
type SocialConnection,
|
|
20
24
|
} from "@opengeni/contracts";
|
|
21
25
|
import {
|
|
@@ -42,7 +46,6 @@ import {
|
|
|
42
46
|
listEnabledMcpCapabilityServers,
|
|
43
47
|
listInstalledApiIntegrations,
|
|
44
48
|
listInstalledSkills,
|
|
45
|
-
listPackInstallations,
|
|
46
49
|
listSocialConnections,
|
|
47
50
|
mcpServerIdForCapability,
|
|
48
51
|
upsertCapabilityCatalogItem,
|
|
@@ -52,10 +55,14 @@ import {
|
|
|
52
55
|
type InstalledSkillSummary,
|
|
53
56
|
} from "@opengeni/db";
|
|
54
57
|
import { HTTPException } from "hono/http-exception";
|
|
58
|
+
import {
|
|
59
|
+
withOrganizationIntegrationAcquisition,
|
|
60
|
+
withOrganizationIntegrationPolicyFence,
|
|
61
|
+
} from "@opengeni/db/organization-integration-policy";
|
|
55
62
|
import { hasPermission } from "../access";
|
|
56
63
|
import { isFikenConnection, preferredFikenConnection } from "./fiken";
|
|
57
64
|
import { listSkillLibraryEntries, type SkillLibraryEntry } from "@opengeni/runtime/skill-library";
|
|
58
|
-
import {
|
|
65
|
+
import { assertNativeMcpConnectionRef } from "./native-mcp-connection-admission";
|
|
59
66
|
|
|
60
67
|
const officialMcpRegistryUrl = "https://registry.modelcontextprotocol.io";
|
|
61
68
|
const firstPartyMcpServerIds = new Set(["opengeni", "files", "docs"]);
|
|
@@ -64,7 +71,6 @@ const mcpRegistryMaxPages = 3;
|
|
|
64
71
|
const mcpCapabilityProbeTimeoutMs = 15000;
|
|
65
72
|
const maxMcpCredentialHeaders = 16;
|
|
66
73
|
const maxMcpCredentialHeaderValueLength = 4096;
|
|
67
|
-
const officialGmailMcpUrl = "https://gmailmcp.googleapis.com/mcp/v1";
|
|
68
74
|
// RFC 9110 field-name token characters.
|
|
69
75
|
const mcpCredentialHeaderName = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
|
|
70
76
|
|
|
@@ -77,18 +83,15 @@ export async function buildCapabilityCatalog(input: {
|
|
|
77
83
|
const [
|
|
78
84
|
persistedItems,
|
|
79
85
|
capabilityInstallations,
|
|
80
|
-
packInstallations,
|
|
81
|
-
workspacePacks,
|
|
82
86
|
socialConnections,
|
|
83
87
|
workspaceConnections,
|
|
84
88
|
curatedLibrarySkills,
|
|
85
89
|
installedSkills,
|
|
86
90
|
codexAppsCredentialId,
|
|
91
|
+
runnableMcpServers,
|
|
87
92
|
] = await Promise.all([
|
|
88
93
|
listCapabilityCatalogItems(input.db, input.workspaceId),
|
|
89
94
|
listCapabilityInstallations(input.db, input.workspaceId),
|
|
90
|
-
listPackInstallations(input.db, input.workspaceId),
|
|
91
|
-
listWorkspaceCapabilityPacks(input.db, input.workspaceId),
|
|
92
95
|
listSocialConnections(input.db, input.workspaceId, 500, input.subjectId),
|
|
93
96
|
listConnectionsMetadata(input.db, input.workspaceId, null),
|
|
94
97
|
discoverCuratedSkillLibraryItems(),
|
|
@@ -96,7 +99,9 @@ export async function buildCapabilityCatalog(input: {
|
|
|
96
99
|
input.settings.codexConnectedAppsEnabled
|
|
97
100
|
? resolveCodexAppsCredentialIdForRun(input.db, input.workspaceId)
|
|
98
101
|
: Promise.resolve(null),
|
|
102
|
+
listEnabledMcpCapabilityServers(input.db, input.workspaceId),
|
|
99
103
|
]);
|
|
104
|
+
const runnableCapabilityIds = new Set(runnableMcpServers.map((server) => server.capabilityId));
|
|
100
105
|
const catalogInstallations = capabilityInstallations.filter(
|
|
101
106
|
(installation) => installation.kind === "mcp",
|
|
102
107
|
);
|
|
@@ -108,20 +113,11 @@ export async function buildCapabilityCatalog(input: {
|
|
|
108
113
|
.filter((skill) => skill.owners.some((owner) => owner.kind === "direct"))
|
|
109
114
|
.map((skill) => [skill.capabilityId, skill]),
|
|
110
115
|
);
|
|
111
|
-
const activePackIds = new Set(
|
|
112
|
-
packInstallations
|
|
113
|
-
.filter((installation) => installation.status === "active")
|
|
114
|
-
.map((installation) => installation.packId),
|
|
115
|
-
);
|
|
116
|
-
const builtInPackIds = new Set(listCapabilityPacks().map((pack) => pack.id));
|
|
117
116
|
const builtIns = [
|
|
118
|
-
...workspacePacks.map((pack) =>
|
|
119
|
-
packCatalogItem(pack, builtInPackIds.has(pack.id) ? "built_in" : "manual"),
|
|
120
|
-
),
|
|
121
117
|
...configuredMcpCatalogItems(input.settings),
|
|
122
118
|
...providerIntegrationCatalogItems(socialConnections),
|
|
123
119
|
fikenCatalogItem(workspaceConnections.filter(isFikenConnection)),
|
|
124
|
-
...curatedLibrarySkills,
|
|
120
|
+
...curatedLibrarySkills.filter((item) => installedSkillById.has(item.id)),
|
|
125
121
|
...installedSkills
|
|
126
122
|
.filter(
|
|
127
123
|
(skill) =>
|
|
@@ -149,8 +145,26 @@ export async function buildCapabilityCatalog(input: {
|
|
|
149
145
|
const projected =
|
|
150
146
|
item.kind === "skill"
|
|
151
147
|
? applyInstalledSkillEnablement(item, installedSkillById.get(item.id))
|
|
152
|
-
: applyCapabilityEnablement(item, capabilityInstallationById.get(item.id)
|
|
153
|
-
|
|
148
|
+
: applyCapabilityEnablement(item, capabilityInstallationById.get(item.id));
|
|
149
|
+
// An installed connector can become unrunnable after a lifecycle or
|
|
150
|
+
// ownership change. Use the execution registry rather than presenting
|
|
151
|
+
// its stale catalog definition as a selectable tool.
|
|
152
|
+
const runtimeProjected =
|
|
153
|
+
projected.kind === "mcp" &&
|
|
154
|
+
projected.enabled &&
|
|
155
|
+
projected.source !== "configured" &&
|
|
156
|
+
projected.source !== "built_in" &&
|
|
157
|
+
!runnableCapabilityIds.has(projected.id)
|
|
158
|
+
? {
|
|
159
|
+
...projected,
|
|
160
|
+
runtime: {
|
|
161
|
+
...projected.runtime,
|
|
162
|
+
available: false,
|
|
163
|
+
notes: "This installed connection is unavailable. Review its connection settings.",
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
: projected;
|
|
167
|
+
return applyCapabilityLifecycle(runtimeProjected);
|
|
154
168
|
})
|
|
155
169
|
.sort(compareCatalogItems);
|
|
156
170
|
return {
|
|
@@ -166,11 +180,7 @@ export async function createCatalogItem(input: {
|
|
|
166
180
|
payload: CreateCapabilityCatalogItemRequest;
|
|
167
181
|
}): Promise<CapabilityCatalogItem> {
|
|
168
182
|
const id = input.payload.id?.trim() || generatedCapabilityId(input.payload);
|
|
169
|
-
|
|
170
|
-
throw new HTTPException(422, {
|
|
171
|
-
message: "packs are managed by OpenGeni and cannot be manually created",
|
|
172
|
-
});
|
|
173
|
-
}
|
|
183
|
+
|
|
174
184
|
if (id.startsWith("skill:")) {
|
|
175
185
|
throw new HTTPException(422, {
|
|
176
186
|
message: "Skills are installed through the Skill library or source import flow",
|
|
@@ -229,7 +239,7 @@ export async function createCatalogItem(input: {
|
|
|
229
239
|
});
|
|
230
240
|
}
|
|
231
241
|
|
|
232
|
-
|
|
242
|
+
type EnableCapabilityInput = {
|
|
233
243
|
db: Database;
|
|
234
244
|
grant: AccessGrant;
|
|
235
245
|
accountId: string;
|
|
@@ -238,13 +248,29 @@ export async function enableCapability(input: {
|
|
|
238
248
|
capabilityId: string;
|
|
239
249
|
payload: EnableCapabilityRequest;
|
|
240
250
|
probeMcpServer?: McpCapabilityProbe;
|
|
241
|
-
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export async function enableCapability(
|
|
254
|
+
input: EnableCapabilityInput,
|
|
255
|
+
): Promise<CapabilityInstallation> {
|
|
256
|
+
const prepared = await prepareCapabilityEnable(input);
|
|
257
|
+
return prepared.commit(input.db);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Probe outside a durable Connect commit; persist the exact prepared settings
|
|
261
|
+
* inside the caller's authorized receipt transaction. Native enable uses this too. */
|
|
262
|
+
export async function prepareCapabilityEnable(input: EnableCapabilityInput) {
|
|
263
|
+
input = { ...input, payload: structuredClone(input.payload) };
|
|
242
264
|
const item = await requireCatalogItem(
|
|
243
265
|
input.db,
|
|
244
266
|
input.workspaceId,
|
|
245
267
|
input.settings,
|
|
246
268
|
input.capabilityId,
|
|
247
269
|
);
|
|
270
|
+
if (isReservedCodexAppsCatalogItem(item))
|
|
271
|
+
throw new HTTPException(422, {
|
|
272
|
+
message: "Codex Apps use the dedicated account designation flow",
|
|
273
|
+
});
|
|
248
274
|
if (item.kind === "skill") {
|
|
249
275
|
throw new HTTPException(409, {
|
|
250
276
|
message: "Install Skills through the Skill library or source import flow",
|
|
@@ -260,11 +286,7 @@ export async function enableCapability(input: {
|
|
|
260
286
|
message: "Install Plugins through the Plugin Package flow",
|
|
261
287
|
});
|
|
262
288
|
}
|
|
263
|
-
|
|
264
|
-
throw new HTTPException(409, {
|
|
265
|
-
message: "Install Packs through the Pack installation preview flow",
|
|
266
|
-
});
|
|
267
|
-
}
|
|
289
|
+
|
|
268
290
|
if (item.kind === "mcp" && !item.runtime.available) {
|
|
269
291
|
throw new HTTPException(422, {
|
|
270
292
|
message: "MCP capabilities need a remote streamable HTTP endpoint before they can be enabled",
|
|
@@ -280,6 +302,49 @@ export async function enableCapability(input: {
|
|
|
280
302
|
delete installationConfig.headerNames;
|
|
281
303
|
delete installationConfig.connectionRef;
|
|
282
304
|
if (item.kind === "mcp") {
|
|
305
|
+
if (
|
|
306
|
+
item.endpointUrl?.replace(/\/+$/u, "") === "https://gmailmcp.googleapis.com/mcp/v1" &&
|
|
307
|
+
(input.payload.connectionRef?.subjectScope !== "subject" ||
|
|
308
|
+
Object.keys(input.payload.headers ?? {}).length > 0)
|
|
309
|
+
) {
|
|
310
|
+
throw new HTTPException(422, {
|
|
311
|
+
message: "Gmail requires a personal-owned connection reference",
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
// This generic, potentially workspace-authored catalog is not proof of a
|
|
315
|
+
// curated provider identity. Dedicated Connect adapters classify separately.
|
|
316
|
+
const unchanged = await withOrganizationIntegrationPolicyFence(
|
|
317
|
+
input.db,
|
|
318
|
+
input,
|
|
319
|
+
async (tx, policy) => {
|
|
320
|
+
const existing = await unchangedMcpInstallation(
|
|
321
|
+
{ ...input, db: tx },
|
|
322
|
+
item,
|
|
323
|
+
installationConfig,
|
|
324
|
+
);
|
|
325
|
+
if (existing) return existing;
|
|
326
|
+
assertOrganizationIntegrationAllowed(policy, "custom:mcp");
|
|
327
|
+
return null;
|
|
328
|
+
},
|
|
329
|
+
);
|
|
330
|
+
if (unchanged) {
|
|
331
|
+
return {
|
|
332
|
+
commit: (db: Database) =>
|
|
333
|
+
withOrganizationIntegrationPolicyFence(db, input, async (tx) => {
|
|
334
|
+
const current = await unchangedMcpInstallation(
|
|
335
|
+
{ ...input, db: tx },
|
|
336
|
+
item,
|
|
337
|
+
installationConfig,
|
|
338
|
+
);
|
|
339
|
+
if (!current || !isDeepStrictEqual(current, unchanged)) {
|
|
340
|
+
throw new HTTPException(409, {
|
|
341
|
+
message: "Capability installation changed; reload before reconciling",
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
return current;
|
|
345
|
+
}),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
283
348
|
const headers = await resolveMcpCredentialHeaders(input, item);
|
|
284
349
|
const connectionRef = input.payload.connectionRef
|
|
285
350
|
? await validateMcpCapabilityConnectionRef(input, item, input.payload.connectionRef)
|
|
@@ -289,7 +354,12 @@ export async function enableCapability(input: {
|
|
|
289
354
|
...installationMetadata,
|
|
290
355
|
...(connectionRef && !headers
|
|
291
356
|
? authDeferredMcpConnectivity()
|
|
292
|
-
: await validateMcpCapabilityConnection(
|
|
357
|
+
: await validateMcpCapabilityConnection(
|
|
358
|
+
item,
|
|
359
|
+
input.probeMcpServer,
|
|
360
|
+
headers ?? undefined,
|
|
361
|
+
input.settings,
|
|
362
|
+
)),
|
|
293
363
|
};
|
|
294
364
|
if (connectionRef) {
|
|
295
365
|
installationConfig.connectionRef = connectionRef;
|
|
@@ -301,14 +371,66 @@ export async function enableCapability(input: {
|
|
|
301
371
|
);
|
|
302
372
|
}
|
|
303
373
|
}
|
|
304
|
-
|
|
374
|
+
const installation = {
|
|
305
375
|
accountId: input.accountId,
|
|
306
376
|
workspaceId: input.workspaceId,
|
|
307
377
|
capabilityId: item.id,
|
|
308
378
|
kind: item.kind,
|
|
309
379
|
config: installationConfig,
|
|
310
380
|
metadata: installationMetadata,
|
|
311
|
-
|
|
381
|
+
...(input.payload.onlyIfUninstalled ? { onlyIfUninstalled: true } : {}),
|
|
382
|
+
};
|
|
383
|
+
return {
|
|
384
|
+
commit: (db: Database) =>
|
|
385
|
+
item.kind === "mcp"
|
|
386
|
+
? withOrganizationIntegrationAcquisition(db, installation, ["custom:mcp"], (tx) =>
|
|
387
|
+
enableCapabilityInstallation(tx, installation),
|
|
388
|
+
)
|
|
389
|
+
: enableCapabilityInstallation(db, installation),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** A no-effect reconciliation is not a new acquisition. Validate ordinary
|
|
394
|
+
* connection visibility before locking the installation, and compare actual
|
|
395
|
+
* stored credentials rather than trusting caller-supplied identity hints. */
|
|
396
|
+
async function unchangedMcpInstallation(
|
|
397
|
+
input: EnableCapabilityInput,
|
|
398
|
+
item: CapabilityCatalogItem,
|
|
399
|
+
requestedConfig: Record<string, unknown>,
|
|
400
|
+
): Promise<CapabilityInstallation | null> {
|
|
401
|
+
const connectionRef = input.payload.connectionRef
|
|
402
|
+
? await validateMcpCapabilityConnectionRef(input, item, input.payload.connectionRef)
|
|
403
|
+
: null;
|
|
404
|
+
const existing = await withLockedCapabilityInstallation(
|
|
405
|
+
input.db,
|
|
406
|
+
input.workspaceId,
|
|
407
|
+
item.id,
|
|
408
|
+
(tx) => getCapabilityInstallation(tx, input.workspaceId, item.id),
|
|
409
|
+
);
|
|
410
|
+
if (!existing || existing.kind !== "mcp" || existing.status !== "active") return null;
|
|
411
|
+
const {
|
|
412
|
+
headerNames: _names,
|
|
413
|
+
headersEncrypted: _encrypted,
|
|
414
|
+
headers: _headers,
|
|
415
|
+
...storedConfig
|
|
416
|
+
} = existing.config;
|
|
417
|
+
const config = { ...requestedConfig, ...(connectionRef ? { connectionRef } : {}) };
|
|
418
|
+
const { mcpConnectivity: _storedConnectivity, ...storedMetadata } = existing.metadata;
|
|
419
|
+
const { mcpConnectivity: _requestedConnectivity, ...requestedMetadata } = input.payload.metadata;
|
|
420
|
+
if (
|
|
421
|
+
!isDeepStrictEqual(config, storedConfig) ||
|
|
422
|
+
!isDeepStrictEqual(requestedMetadata, storedMetadata)
|
|
423
|
+
)
|
|
424
|
+
return null;
|
|
425
|
+
const provided = normalizedMcpCredentialHeaders(input.payload.headers);
|
|
426
|
+
if (provided) {
|
|
427
|
+
const stored = await resolveMcpCredentialHeaders(
|
|
428
|
+
{ ...input, payload: { ...input.payload, headers: {} } },
|
|
429
|
+
item,
|
|
430
|
+
);
|
|
431
|
+
if (!isDeepStrictEqual(provided, stored)) return null;
|
|
432
|
+
}
|
|
433
|
+
return existing;
|
|
312
434
|
}
|
|
313
435
|
|
|
314
436
|
/**
|
|
@@ -402,24 +524,17 @@ function normalizedMcpCredentialHeaders(
|
|
|
402
524
|
}
|
|
403
525
|
|
|
404
526
|
async function validateMcpCapabilityConnectionRef(
|
|
405
|
-
input: { db: Database; grant: AccessGrant; workspaceId: string },
|
|
527
|
+
input: { db: Database; grant: AccessGrant; workspaceId: string; settings: Settings },
|
|
406
528
|
item: CapabilityCatalogItem,
|
|
407
529
|
ref: McpServerConnectionRef,
|
|
408
530
|
): Promise<McpServerConnectionRef> {
|
|
409
531
|
const subjectScope = ref.subjectScope ?? "workspace";
|
|
410
|
-
const personalOnly =
|
|
411
|
-
item.metadata.connectionOwnership === "personal_only" ||
|
|
412
|
-
item.endpointUrl?.replace(/\/+$/, "") === officialGmailMcpUrl;
|
|
413
|
-
if (personalOnly && subjectScope !== "subject") {
|
|
414
|
-
throw new HTTPException(422, {
|
|
415
|
-
message:
|
|
416
|
-
"this capability requires a personal connection; each workspace member must connect their own account",
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
532
|
const normalized: McpServerConnectionRef = {
|
|
420
533
|
providerDomain: ref.providerDomain.trim(),
|
|
421
534
|
subjectScope,
|
|
422
535
|
...(ref.connectionId ? { connectionId: ref.connectionId } : {}),
|
|
536
|
+
...(ref.accountSelection ? { accountSelection: ref.accountSelection } : {}),
|
|
537
|
+
...(ref.authoritySource === "host" ? { authoritySource: "host" as const } : {}),
|
|
423
538
|
...(ref.provider ? { provider: ref.provider.trim() } : {}),
|
|
424
539
|
...(ref.kind ? { kind: ref.kind } : {}),
|
|
425
540
|
...(ref.scopes ? { scopes: uniqueStrings(ref.scopes) } : {}),
|
|
@@ -443,6 +558,10 @@ async function validateMcpCapabilityConnectionRef(
|
|
|
443
558
|
"MCP capabilities need a remote streamable HTTP endpoint before they can use a connectionRef",
|
|
444
559
|
});
|
|
445
560
|
}
|
|
561
|
+
if (normalized.authoritySource === "host") {
|
|
562
|
+
assertNativeMcpConnectionRef(normalized);
|
|
563
|
+
return normalized;
|
|
564
|
+
}
|
|
446
565
|
|
|
447
566
|
let connection = normalized.connectionId
|
|
448
567
|
? await getConnectionMetadata(
|
|
@@ -452,7 +571,11 @@ async function validateMcpCapabilityConnectionRef(
|
|
|
452
571
|
input.grant.subjectId,
|
|
453
572
|
)
|
|
454
573
|
: null;
|
|
455
|
-
if (
|
|
574
|
+
if (
|
|
575
|
+
!connection &&
|
|
576
|
+
!normalized.connectionId &&
|
|
577
|
+
(subjectScope === "subject" || normalized.accountSelection === "all_eligible")
|
|
578
|
+
) {
|
|
456
579
|
const visible = await listConnectionsMetadata(
|
|
457
580
|
input.db,
|
|
458
581
|
input.workspaceId,
|
|
@@ -461,7 +584,7 @@ async function validateMcpCapabilityConnectionRef(
|
|
|
461
584
|
connection =
|
|
462
585
|
visible.find(
|
|
463
586
|
(candidate) =>
|
|
464
|
-
candidate.subjectId === input.grant.subjectId &&
|
|
587
|
+
candidate.subjectId === (subjectScope === "subject" ? input.grant.subjectId : null) &&
|
|
465
588
|
candidate.providerDomain === normalized.providerDomain &&
|
|
466
589
|
(!normalized.kind || candidate.kind === normalized.kind) &&
|
|
467
590
|
candidate.status === "active",
|
|
@@ -561,6 +684,7 @@ export type McpCapabilityProbeInput = {
|
|
|
561
684
|
url: string;
|
|
562
685
|
timeoutMs: number;
|
|
563
686
|
headers?: Record<string, string>;
|
|
687
|
+
settings?: Settings;
|
|
564
688
|
};
|
|
565
689
|
|
|
566
690
|
export type McpCapabilityProbeResult = {
|
|
@@ -575,6 +699,7 @@ export async function validateMcpCapabilityConnection(
|
|
|
575
699
|
item: CapabilityCatalogItem,
|
|
576
700
|
probe: McpCapabilityProbe = probeStreamableHttpMcpServer,
|
|
577
701
|
headers?: Record<string, string>,
|
|
702
|
+
settings?: Settings,
|
|
578
703
|
): Promise<Record<string, unknown>> {
|
|
579
704
|
if (item.kind !== "mcp") {
|
|
580
705
|
return {};
|
|
@@ -591,6 +716,7 @@ export async function validateMcpCapabilityConnection(
|
|
|
591
716
|
url: item.endpointUrl,
|
|
592
717
|
timeoutMs: mcpCapabilityProbeTimeoutMs,
|
|
593
718
|
...(headers ? { headers } : {}),
|
|
719
|
+
...(settings ? { settings } : {}),
|
|
594
720
|
});
|
|
595
721
|
return {
|
|
596
722
|
mcpConnectivity: {
|
|
@@ -609,6 +735,8 @@ export async function validateMcpCapabilityConnection(
|
|
|
609
735
|
async function probeStreamableHttpMcpServer(
|
|
610
736
|
input: McpCapabilityProbeInput,
|
|
611
737
|
): Promise<McpCapabilityProbeResult> {
|
|
738
|
+
const settings = input.settings;
|
|
739
|
+
if (!settings) throw new Error("Network policy is unavailable for MCP validation");
|
|
612
740
|
const controller = new AbortController();
|
|
613
741
|
const timeout = setTimeout(() => controller.abort(), input.timeoutMs);
|
|
614
742
|
const client = new Client(
|
|
@@ -621,6 +749,11 @@ async function probeStreamableHttpMcpServer(
|
|
|
621
749
|
signal: controller.signal,
|
|
622
750
|
...(input.headers ? { headers: input.headers } : {}),
|
|
623
751
|
},
|
|
752
|
+
fetch: (url, init) =>
|
|
753
|
+
pinnedFetch(url, init, settings, {
|
|
754
|
+
label: "MCP capability probe",
|
|
755
|
+
requireHttpsOutsideLocalTest: true,
|
|
756
|
+
}),
|
|
624
757
|
});
|
|
625
758
|
await client.connect(transport as unknown as Transport, {
|
|
626
759
|
timeout: input.timeoutMs,
|
|
@@ -646,9 +779,9 @@ function mcpProbeErrorMessage(error: unknown, endpointUrl: string): string {
|
|
|
646
779
|
normalized,
|
|
647
780
|
)
|
|
648
781
|
) {
|
|
649
|
-
return `
|
|
782
|
+
return `Opengeni could not reach a valid Streamable HTTP MCP server at ${endpoint}. Check the endpoint URL or choose a different catalog entry.`;
|
|
650
783
|
}
|
|
651
|
-
return `
|
|
784
|
+
return `Opengeni could not initialize ${endpoint}. Check the endpoint configuration or try again.`;
|
|
652
785
|
}
|
|
653
786
|
|
|
654
787
|
function safeEndpointLabel(endpointUrl: string): string {
|
|
@@ -687,11 +820,7 @@ export async function disableCapability(input: {
|
|
|
687
820
|
message: "Remove Plugins through the Plugin Package flow",
|
|
688
821
|
});
|
|
689
822
|
}
|
|
690
|
-
|
|
691
|
-
throw new HTTPException(409, {
|
|
692
|
-
message: "Uninstall Packs through the Pack uninstall preview flow",
|
|
693
|
-
});
|
|
694
|
-
}
|
|
823
|
+
|
|
695
824
|
if (item.source === "built_in" || item.source === "configured") {
|
|
696
825
|
throw new HTTPException(409, {
|
|
697
826
|
message:
|
|
@@ -745,7 +874,7 @@ export function apiIntegrationsMatchingDelegations(
|
|
|
745
874
|
const exact = new Set(
|
|
746
875
|
delegations.map((delegation) =>
|
|
747
876
|
[
|
|
748
|
-
delegation.serverId,
|
|
877
|
+
delegation.canonicalServerId ?? delegation.serverId,
|
|
749
878
|
delegation.connectionId,
|
|
750
879
|
delegation.providerDomain.toLowerCase(),
|
|
751
880
|
delegation.kind ?? "",
|
|
@@ -952,9 +1081,6 @@ export async function discoverMcpRegistryCapabilities(input: {
|
|
|
952
1081
|
if (!item || seen.has(item.id)) {
|
|
953
1082
|
continue;
|
|
954
1083
|
}
|
|
955
|
-
if (query && !catalogSearchText(item).includes(query)) {
|
|
956
|
-
continue;
|
|
957
|
-
}
|
|
958
1084
|
seen.add(item.id);
|
|
959
1085
|
items.push(item);
|
|
960
1086
|
if (items.length >= limit) {
|
|
@@ -962,7 +1088,9 @@ export async function discoverMcpRegistryCapabilities(input: {
|
|
|
962
1088
|
}
|
|
963
1089
|
}
|
|
964
1090
|
cursor = typeof page.metadata?.nextCursor === "string" ? page.metadata.nextCursor : undefined;
|
|
965
|
-
|
|
1091
|
+
// Return usable results promptly rather than fetching more pages merely
|
|
1092
|
+
// to fill the requested limit. The registry already applies the search.
|
|
1093
|
+
if (items.length > 0 || !cursor) {
|
|
966
1094
|
break;
|
|
967
1095
|
}
|
|
968
1096
|
}
|
|
@@ -1028,52 +1156,6 @@ async function requireCatalogItem(
|
|
|
1028
1156
|
return item;
|
|
1029
1157
|
}
|
|
1030
1158
|
|
|
1031
|
-
function packCatalogItem(
|
|
1032
|
-
pack: ReturnType<typeof listCapabilityPacks>[number],
|
|
1033
|
-
source: "built_in" | "manual",
|
|
1034
|
-
): CapabilityCatalogItem {
|
|
1035
|
-
const customMetadata = { ...pack.metadata };
|
|
1036
|
-
for (const key of [
|
|
1037
|
-
"packId",
|
|
1038
|
-
"version",
|
|
1039
|
-
"connectors",
|
|
1040
|
-
"knowledge",
|
|
1041
|
-
"scheduledTaskTemplates",
|
|
1042
|
-
"sandboxImage",
|
|
1043
|
-
"sandboxProviderImages",
|
|
1044
|
-
"skills",
|
|
1045
|
-
]) {
|
|
1046
|
-
delete customMetadata[key];
|
|
1047
|
-
}
|
|
1048
|
-
return CapabilityCatalogItem.parse({
|
|
1049
|
-
id: `pack:${pack.id}`,
|
|
1050
|
-
kind: "pack",
|
|
1051
|
-
source,
|
|
1052
|
-
name: pack.name,
|
|
1053
|
-
description: pack.description,
|
|
1054
|
-
category: pack.category,
|
|
1055
|
-
tags: [pack.role, pack.category, "pack"],
|
|
1056
|
-
tools: pack.tools,
|
|
1057
|
-
runtime: {
|
|
1058
|
-
available: true,
|
|
1059
|
-
notes: "Enables role-scoped tools, connectors, knowledge, and scheduled-task templates.",
|
|
1060
|
-
},
|
|
1061
|
-
metadata: {
|
|
1062
|
-
...customMetadata,
|
|
1063
|
-
packId: pack.id,
|
|
1064
|
-
version: pack.version,
|
|
1065
|
-
connectors: pack.connectors,
|
|
1066
|
-
knowledge: pack.knowledge,
|
|
1067
|
-
scheduledTaskTemplates: pack.scheduledTaskTemplates,
|
|
1068
|
-
...(pack.variableSet ? { variableSet: pack.variableSet } : {}),
|
|
1069
|
-
// Runtime composition surface only: skill names, never file content.
|
|
1070
|
-
...(pack.sandboxImage ? { sandboxImage: pack.sandboxImage } : {}),
|
|
1071
|
-
...(pack.sandboxProviderImages ? { sandboxProviderImages: pack.sandboxProviderImages } : {}),
|
|
1072
|
-
...(pack.skills.length > 0 ? { skills: pack.skills.map((skill) => skill.name) } : {}),
|
|
1073
|
-
},
|
|
1074
|
-
});
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
1159
|
function configuredMcpCatalogItems(settings: Settings): CapabilityCatalogItem[] {
|
|
1078
1160
|
return (
|
|
1079
1161
|
settings.mcpServers
|
|
@@ -1094,12 +1176,23 @@ function configuredMcpCatalogItems(settings: Settings): CapabilityCatalogItem[]
|
|
|
1094
1176
|
category: "configured",
|
|
1095
1177
|
tags: ["mcp", ...(server.allowedTools?.length ? ["limited-tools"] : [])],
|
|
1096
1178
|
endpointUrl: server.url,
|
|
1179
|
+
authKind:
|
|
1180
|
+
server.connectionRef?.authoritySource !== "host" &&
|
|
1181
|
+
server.connectionRef?.kind === "oauth2"
|
|
1182
|
+
? "oauth2"
|
|
1183
|
+
: null,
|
|
1184
|
+
// Deployment-managed personal selectors need the same account picker
|
|
1185
|
+
// as installed connectors. Fixed bindings remain server-resolved.
|
|
1186
|
+
connectionRef:
|
|
1187
|
+
server.connectionRef?.subjectScope === "subject" && !server.connectionRef.connectionId
|
|
1188
|
+
? installationConnectionRef({ connectionRef: server.connectionRef })
|
|
1189
|
+
: null,
|
|
1097
1190
|
tools: [{ kind: "mcp", id: server.id }],
|
|
1098
1191
|
runtime: {
|
|
1099
1192
|
available: true,
|
|
1100
1193
|
mcpServerId: server.id,
|
|
1101
1194
|
transport: "streamable-http",
|
|
1102
|
-
notes: "Managed by this
|
|
1195
|
+
notes: "Managed by this Opengeni deployment through OPENGENI_MCP_SERVERS.",
|
|
1103
1196
|
},
|
|
1104
1197
|
metadata: {
|
|
1105
1198
|
mcpServerId: server.id,
|
|
@@ -1235,7 +1328,7 @@ function fikenCatalogItem(fikenConnections: ConnectionMetadata[]): CapabilityCat
|
|
|
1235
1328
|
runtime: {
|
|
1236
1329
|
available: true,
|
|
1237
1330
|
mcpServerId: "opengeni",
|
|
1238
|
-
notes: "Fiken access is provided through
|
|
1331
|
+
notes: "Fiken access is provided through Opengeni's first-party fiken tools.",
|
|
1239
1332
|
},
|
|
1240
1333
|
enabled: fikenEnabled,
|
|
1241
1334
|
enabledReason: fikenEnabled
|
|
@@ -1246,6 +1339,7 @@ function fikenCatalogItem(fikenConnections: ConnectionMetadata[]): CapabilityCat
|
|
|
1246
1339
|
metadata: {
|
|
1247
1340
|
connectorMode: "first_party_fiken",
|
|
1248
1341
|
ownership: "workspace",
|
|
1342
|
+
connectionStatus: fikenConnection?.status ?? null,
|
|
1249
1343
|
// Derived from the contracts catalog so a new fiken_* tool cannot be
|
|
1250
1344
|
// registered without also appearing on the capability tile.
|
|
1251
1345
|
firstPartyMcpTools: FIRST_PARTY_MCP_TOOL_NAMES.filter((name) => name.startsWith("fiken_")),
|
|
@@ -1277,11 +1371,11 @@ function providerIntegrationCatalogItems(
|
|
|
1277
1371
|
available: true,
|
|
1278
1372
|
mcpServerId: "opengeni",
|
|
1279
1373
|
notes:
|
|
1280
|
-
"
|
|
1374
|
+
"Opengeni's social provider adapter routes every call through an exact visible account Connection.",
|
|
1281
1375
|
},
|
|
1282
1376
|
enabled,
|
|
1283
1377
|
enabledReason: socialConnectionSummary(counts),
|
|
1284
|
-
provenance: "
|
|
1378
|
+
provenance: "Opengeni provider adapter",
|
|
1285
1379
|
metadata: {
|
|
1286
1380
|
providerAdapter: "social",
|
|
1287
1381
|
provider: definition.provider,
|
|
@@ -1306,7 +1400,7 @@ export function nativeConnectionCapabilityRecommendations(): CapabilityCatalogIt
|
|
|
1306
1400
|
kind: "api",
|
|
1307
1401
|
source: "built_in",
|
|
1308
1402
|
name: "GitHub App",
|
|
1309
|
-
description: "Connect repositories through
|
|
1403
|
+
description: "Connect repositories through Opengeni's GitHub resource picker.",
|
|
1310
1404
|
category: "source-control",
|
|
1311
1405
|
tags: ["github", "repositories", "source-control"],
|
|
1312
1406
|
homepageUrl: "https://github.com",
|
|
@@ -1494,31 +1588,21 @@ function socialProviderConnectionCounts(item: CapabilityCatalogItem): {
|
|
|
1494
1588
|
export function applyCapabilityEnablement(
|
|
1495
1589
|
item: CapabilityCatalogItem,
|
|
1496
1590
|
installation: CapabilityInstallation | undefined,
|
|
1497
|
-
activePackIds: Set<string>,
|
|
1498
1591
|
): CapabilityCatalogItem {
|
|
1592
|
+
if (item.surfaceType === "first_party_fiken") {
|
|
1593
|
+
// Fiken is an API item with connection-derived state, not a generic API
|
|
1594
|
+
// installation. Preserve that state before the non-MCP fallback below.
|
|
1595
|
+
return { ...item, connectionRef: null };
|
|
1596
|
+
}
|
|
1499
1597
|
if (item.kind === "skill" || item.kind === "api" || item.kind === "plugin") {
|
|
1500
1598
|
return { ...item, enabled: false, enabledReason: null, connectionRef: null };
|
|
1501
1599
|
}
|
|
1502
|
-
if (item.kind === "pack") {
|
|
1503
|
-
const enabled = activePackIds.has(packIdFromCapabilityId(item.id));
|
|
1504
|
-
return {
|
|
1505
|
-
...item,
|
|
1506
|
-
enabled,
|
|
1507
|
-
enabledReason: enabled ? "enabled" : null,
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
1600
|
if (isSocialProviderIntegration(item)) {
|
|
1511
1601
|
// Provider-integration state is derived from every authoritative visible
|
|
1512
1602
|
// social Connection while the catalog is built. The catalog summary never
|
|
1513
1603
|
// publishes a personal connection UUID or collapses many accounts to one.
|
|
1514
1604
|
return { ...item, connectionRef: null };
|
|
1515
1605
|
}
|
|
1516
|
-
if (item.surfaceType === "first_party_fiken") {
|
|
1517
|
-
// Fiken connector state is derived from the authoritative workspace
|
|
1518
|
-
// connection row while the catalog is built; browseable never means an
|
|
1519
|
-
// account is already connected.
|
|
1520
|
-
return { ...item, connectionRef: null };
|
|
1521
|
-
}
|
|
1522
1606
|
if (item.surfaceType === "codex_apps") {
|
|
1523
1607
|
// Unlike ordinary built-ins, Apps availability is derived from the exact
|
|
1524
1608
|
// active workspace designation above. Do not overwrite it with the
|
|
@@ -1530,7 +1614,7 @@ export function applyCapabilityEnablement(
|
|
|
1530
1614
|
...item,
|
|
1531
1615
|
enabled: true,
|
|
1532
1616
|
enabledReason: "managed by deployment",
|
|
1533
|
-
connectionRef:
|
|
1617
|
+
connectionRef: item.connectionRef,
|
|
1534
1618
|
};
|
|
1535
1619
|
}
|
|
1536
1620
|
if (item.source === "built_in") {
|
|
@@ -1541,11 +1625,17 @@ export function applyCapabilityEnablement(
|
|
|
1541
1625
|
}
|
|
1542
1626
|
const activeInstallation = installation?.status === "active";
|
|
1543
1627
|
const enabled = !!activeInstallation && capabilityInstallationRuntimeReady(item, installation);
|
|
1628
|
+
const connectionRef =
|
|
1629
|
+
enabled && installation ? installationConnectionRef(installation.config) : null;
|
|
1544
1630
|
return {
|
|
1545
1631
|
...item,
|
|
1632
|
+
// Older custom entries recorded the native credential kind on the
|
|
1633
|
+
// installation only. Project it without guessing from a URL or overwriting
|
|
1634
|
+
// an explicit catalog authentication contract.
|
|
1635
|
+
authKind: item.authKind ?? (connectionRef?.kind === "oauth2" ? "oauth2" : null),
|
|
1546
1636
|
enabled,
|
|
1547
1637
|
enabledReason: enabled ? "enabled" : null,
|
|
1548
|
-
connectionRef
|
|
1638
|
+
connectionRef,
|
|
1549
1639
|
};
|
|
1550
1640
|
}
|
|
1551
1641
|
|
|
@@ -1572,7 +1662,10 @@ function applyCapabilityLifecycle(item: CapabilityCatalogItem): CapabilityCatalo
|
|
|
1572
1662
|
detail: item.runtime.notes,
|
|
1573
1663
|
managedBy: item.source === "built_in" ? "platform" : null,
|
|
1574
1664
|
},
|
|
1575
|
-
actions:
|
|
1665
|
+
actions:
|
|
1666
|
+
item.enabled && item.kind === "mcp" && item.source !== "built_in"
|
|
1667
|
+
? ["disconnect", "inspect"]
|
|
1668
|
+
: ["inspect"],
|
|
1576
1669
|
};
|
|
1577
1670
|
}
|
|
1578
1671
|
|
|
@@ -1611,7 +1704,7 @@ function applyCapabilityLifecycle(item: CapabilityCatalogItem): CapabilityCatalo
|
|
|
1611
1704
|
|
|
1612
1705
|
const installed = item.enabled;
|
|
1613
1706
|
const actions: CapabilityAction[] = installed
|
|
1614
|
-
? item.kind === "
|
|
1707
|
+
? item.kind === "skill" || item.kind === "plugin"
|
|
1615
1708
|
? ["configure", "update", "uninstall", "inspect"]
|
|
1616
1709
|
: ["configure", "disconnect", "inspect"]
|
|
1617
1710
|
: item.kind === "mcp" || item.kind === "api"
|
|
@@ -1674,14 +1767,34 @@ function installationConnectionRef(
|
|
|
1674
1767
|
if (!ref || typeof ref !== "object") {
|
|
1675
1768
|
return null;
|
|
1676
1769
|
}
|
|
1677
|
-
const { connectionId, providerDomain, kind, subjectScope } =
|
|
1770
|
+
const { authoritySource, connectionId, providerDomain, kind, subjectScope, accountSelection } =
|
|
1771
|
+
ref as Record<string, unknown>;
|
|
1678
1772
|
if (typeof providerDomain !== "string" || typeof kind !== "string") {
|
|
1679
1773
|
return null;
|
|
1680
1774
|
}
|
|
1775
|
+
if (authoritySource === "host") {
|
|
1776
|
+
// The internal installation/runtime ref retains the exact host binding.
|
|
1777
|
+
// Public capability catalogs use the existing null representation for an
|
|
1778
|
+
// enabled capability without a native OpenGeni connection, so indefinitely
|
|
1779
|
+
// open old browser bundles cannot treat a host UUID as native OAuth state.
|
|
1780
|
+
return null;
|
|
1781
|
+
}
|
|
1782
|
+
if (accountSelection === "all_eligible" && connectionId === undefined) {
|
|
1783
|
+
return {
|
|
1784
|
+
providerDomain,
|
|
1785
|
+
kind,
|
|
1786
|
+
accountSelection,
|
|
1787
|
+
...(subjectScope === "subject" ? { subjectScope } : {}),
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1681
1790
|
if (subjectScope === "subject") {
|
|
1682
|
-
// Never project a personal connection UUID through workspace-visible
|
|
1791
|
+
// Never project a native personal connection UUID through workspace-visible
|
|
1683
1792
|
// capability configuration, including legacy rows that still contain one.
|
|
1684
|
-
return {
|
|
1793
|
+
return {
|
|
1794
|
+
providerDomain,
|
|
1795
|
+
kind,
|
|
1796
|
+
subjectScope: "subject",
|
|
1797
|
+
};
|
|
1685
1798
|
}
|
|
1686
1799
|
if (typeof connectionId !== "string") {
|
|
1687
1800
|
return null;
|
|
@@ -1807,10 +1920,6 @@ function publicRegistryCapabilityId(name: string, version: string, endpointUrl:
|
|
|
1807
1920
|
return `mcp-registry:${slugify(name)}-${shortHash(`${name}:${version}:${endpointUrl}`)}`;
|
|
1808
1921
|
}
|
|
1809
1922
|
|
|
1810
|
-
function packIdFromCapabilityId(capabilityId: string): string {
|
|
1811
|
-
return capabilityId.replace(/^pack:/, "");
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
1923
|
function uniqueTags(tags: string[]): string[] {
|
|
1815
1924
|
return [...new Set(tags.map((tag) => tag.trim()).filter(Boolean))];
|
|
1816
1925
|
}
|
|
@@ -1958,22 +2067,6 @@ function validUrl(value: string | undefined): string | null {
|
|
|
1958
2067
|
}
|
|
1959
2068
|
}
|
|
1960
2069
|
|
|
1961
|
-
function catalogSearchText(item: CapabilityCatalogItem): string {
|
|
1962
|
-
return [
|
|
1963
|
-
item.name,
|
|
1964
|
-
item.description,
|
|
1965
|
-
item.category,
|
|
1966
|
-
...item.tags,
|
|
1967
|
-
item.endpointUrl,
|
|
1968
|
-
item.homepageUrl,
|
|
1969
|
-
item.installUrl,
|
|
1970
|
-
JSON.stringify(item.metadata),
|
|
1971
|
-
]
|
|
1972
|
-
.filter(Boolean)
|
|
1973
|
-
.join(" ")
|
|
1974
|
-
.toLowerCase();
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
2070
|
function capabilityInstallationRuntimeReady(
|
|
1978
2071
|
item: CapabilityCatalogItem,
|
|
1979
2072
|
installation: CapabilityInstallation | undefined,
|