@opengeni/capabilities 0.1.1 → 0.3.0-canary.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 +18 -2
- package/THIRD_PARTY_NOTICES +2 -2
- package/dist/graphql.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +374 -123
- package/dist/index.js.map +1 -1
- package/dist/integration-definitions.d.ts +57 -0
- package/dist/integration-presentations.d.ts +32 -0
- package/dist/mcp-bridge.d.ts +41 -0
- package/dist/openapi.d.ts +1 -1
- package/dist/types.d.ts +4 -2
- package/package.json +2 -2
- package/src/graphql.ts +24 -8
- package/src/index.ts +3 -1
- package/src/{providers.ts → integration-definitions.ts} +161 -137
- package/src/integration-presentations.ts +185 -0
- package/src/mcp-bridge.ts +117 -0
- package/src/openapi.ts +21 -5
- package/src/types.ts +4 -2
- package/dist/providers.d.ts +0 -44
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { MCPServer } from "@openai/agents";
|
|
2
|
+
|
|
3
|
+
export const LOCAL_MCP_BRIDGE_CONTRACT_VERSION = 1 as const;
|
|
4
|
+
|
|
5
|
+
export type LocalMcpBridgeAuthority = "connection" | "host" | "none";
|
|
6
|
+
export type LocalMcpBridgeToolSurface = "static_reviewed";
|
|
7
|
+
|
|
8
|
+
export type LocalMcpBridgeDestination = Readonly<{
|
|
9
|
+
origin: string;
|
|
10
|
+
pathPrefix: string;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Secret-free description of an in-process provider-to-MCP adapter.
|
|
15
|
+
*
|
|
16
|
+
* This is observability and registration metadata, not authorization. The
|
|
17
|
+
* adapter must still revalidate its named authority before each physical
|
|
18
|
+
* provider request and keep credentials outside tool results and schemas.
|
|
19
|
+
*/
|
|
20
|
+
export type LocalMcpBridgeDescriptor = Readonly<{
|
|
21
|
+
contractVersion: typeof LOCAL_MCP_BRIDGE_CONTRACT_VERSION;
|
|
22
|
+
adapterId: string;
|
|
23
|
+
providerId: string;
|
|
24
|
+
catalogIdentity: string;
|
|
25
|
+
transport: "in_process";
|
|
26
|
+
authority: LocalMcpBridgeAuthority;
|
|
27
|
+
toolSurface: LocalMcpBridgeToolSurface;
|
|
28
|
+
mutationReplay: "safe_reads_only";
|
|
29
|
+
destinations: readonly LocalMcpBridgeDestination[];
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
export interface LocalMcpBridgeServer extends MCPServer {
|
|
33
|
+
readonly bridge: LocalMcpBridgeDescriptor;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface LocalMcpBridgeAdapter<TConfig, TContext> {
|
|
37
|
+
readonly adapterId: string;
|
|
38
|
+
matches(config: TConfig): boolean;
|
|
39
|
+
create(config: TConfig, context: TContext): LocalMcpBridgeServer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function defineLocalMcpBridgeDescriptor(
|
|
43
|
+
input: Omit<LocalMcpBridgeDescriptor, "contractVersion" | "transport">,
|
|
44
|
+
): LocalMcpBridgeDescriptor {
|
|
45
|
+
const adapterId = boundedIdentity(input.adapterId, "adapterId");
|
|
46
|
+
const providerId = boundedIdentity(input.providerId, "providerId");
|
|
47
|
+
const catalogIdentity = boundedIdentity(input.catalogIdentity, "catalogIdentity", 512);
|
|
48
|
+
if (input.destinations.length === 0 || input.destinations.length > 32) {
|
|
49
|
+
throw new Error("Local MCP bridge must declare 1-32 provider destinations");
|
|
50
|
+
}
|
|
51
|
+
const destinations = input.destinations.map((destination) => {
|
|
52
|
+
const url = new URL(destination.origin);
|
|
53
|
+
if (url.protocol !== "https:" || url.origin !== destination.origin) {
|
|
54
|
+
throw new Error("Local MCP bridge destinations must be exact HTTPS origins");
|
|
55
|
+
}
|
|
56
|
+
if (
|
|
57
|
+
!destination.pathPrefix.startsWith("/") ||
|
|
58
|
+
destination.pathPrefix.includes("\\") ||
|
|
59
|
+
destination.pathPrefix.includes("?") ||
|
|
60
|
+
destination.pathPrefix.includes("#") ||
|
|
61
|
+
new URL(destination.pathPrefix, url.origin).pathname !== destination.pathPrefix
|
|
62
|
+
) {
|
|
63
|
+
throw new Error("Local MCP bridge destination pathPrefix must be an absolute URL path");
|
|
64
|
+
}
|
|
65
|
+
return Object.freeze({ origin: url.origin, pathPrefix: destination.pathPrefix });
|
|
66
|
+
});
|
|
67
|
+
return Object.freeze({
|
|
68
|
+
contractVersion: LOCAL_MCP_BRIDGE_CONTRACT_VERSION,
|
|
69
|
+
adapterId,
|
|
70
|
+
providerId,
|
|
71
|
+
catalogIdentity,
|
|
72
|
+
transport: "in_process",
|
|
73
|
+
authority: input.authority,
|
|
74
|
+
toolSurface: input.toolSurface,
|
|
75
|
+
mutationReplay: input.mutationReplay,
|
|
76
|
+
destinations: Object.freeze(destinations),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isLocalMcpBridgeServer(server: MCPServer): server is LocalMcpBridgeServer {
|
|
81
|
+
const bridge = (server as Partial<LocalMcpBridgeServer>).bridge;
|
|
82
|
+
return (
|
|
83
|
+
bridge?.contractVersion === LOCAL_MCP_BRIDGE_CONTRACT_VERSION &&
|
|
84
|
+
bridge.transport === "in_process"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Select exactly one adapter for a runtime catalog row. Ambiguous matches fail
|
|
90
|
+
* closed so adding a bridge cannot silently replace another provider route.
|
|
91
|
+
*/
|
|
92
|
+
export function createLocalMcpBridgeFromAdapters<TConfig, TContext>(
|
|
93
|
+
adapters: readonly LocalMcpBridgeAdapter<TConfig, TContext>[],
|
|
94
|
+
config: TConfig,
|
|
95
|
+
context: TContext,
|
|
96
|
+
): LocalMcpBridgeServer | null {
|
|
97
|
+
const matches = adapters.filter((adapter) => adapter.matches(config));
|
|
98
|
+
if (matches.length === 0) return null;
|
|
99
|
+
if (matches.length > 1) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Multiple local MCP bridge adapters matched: ${matches.map((entry) => entry.adapterId).join(", ")}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const adapter = matches[0]!;
|
|
105
|
+
const server = adapter.create(config, context);
|
|
106
|
+
if (server.bridge.adapterId !== adapter.adapterId) {
|
|
107
|
+
throw new Error(`Local MCP bridge adapter ${adapter.adapterId} returned mismatched metadata`);
|
|
108
|
+
}
|
|
109
|
+
return server;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function boundedIdentity(value: string, name: string, max = 128): string {
|
|
113
|
+
if (value.length === 0 || value.length > max || /[\u0000-\u001f\u007f]/u.test(value)) {
|
|
114
|
+
throw new Error(`Local MCP bridge ${name} is invalid`);
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
package/src/openapi.ts
CHANGED
|
@@ -55,7 +55,7 @@ export interface OpenApiOperationBinding {
|
|
|
55
55
|
export type OpenApiRevision = IntegrationRevision<OpenApiOperationBinding, "openapi">;
|
|
56
56
|
|
|
57
57
|
export interface CompileOpenApiOptions {
|
|
58
|
-
readonly
|
|
58
|
+
readonly definitionId: string;
|
|
59
59
|
readonly sourceUrl?: string;
|
|
60
60
|
readonly baseUrl?: string;
|
|
61
61
|
readonly provider?: string;
|
|
@@ -212,13 +212,13 @@ export function compileOpenApiRevision(
|
|
|
212
212
|
return {
|
|
213
213
|
id: revisionId,
|
|
214
214
|
protocol: "openapi",
|
|
215
|
-
|
|
215
|
+
definitionId: options.definitionId,
|
|
216
216
|
contentSha256,
|
|
217
217
|
source: {
|
|
218
218
|
...(options.sourceUrl ? { url: options.sourceUrl } : {}),
|
|
219
219
|
...(options.provider ? { provider: options.provider } : {}),
|
|
220
220
|
},
|
|
221
|
-
title: stringValue(info.title) ?? options.
|
|
221
|
+
title: stringValue(info.title) ?? options.definitionId,
|
|
222
222
|
...(stringValue(info.description) ? { description: stringValue(info.description)! } : {}),
|
|
223
223
|
...(stringValue(info.version) ? { version: stringValue(info.version)! } : {}),
|
|
224
224
|
tools,
|
|
@@ -261,7 +261,7 @@ export class OpenApiMcpServer implements MCPServer {
|
|
|
261
261
|
readonly name: string;
|
|
262
262
|
|
|
263
263
|
constructor(private readonly options: OpenApiServerOptions) {
|
|
264
|
-
this.name = `openapi:${stableToolId(options.revision.
|
|
264
|
+
this.name = `openapi:${stableToolId(options.revision.definitionId)}`;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
async connect(): Promise<void> {}
|
|
@@ -395,7 +395,7 @@ async function resolveOpenApiCredential(
|
|
|
395
395
|
const credential = await options.credentialResolver.resolve({
|
|
396
396
|
...options.authority,
|
|
397
397
|
protocol: "openapi",
|
|
398
|
-
|
|
398
|
+
definitionId: options.revision.definitionId,
|
|
399
399
|
revisionId: options.revision.id,
|
|
400
400
|
operationKey: toolId,
|
|
401
401
|
destinationUrl,
|
|
@@ -426,6 +426,22 @@ async function sendOpenApiRequest(
|
|
|
426
426
|
const headers = buildOperationHeaders(binding, args);
|
|
427
427
|
const body = buildOperationBody(binding, args, headers);
|
|
428
428
|
if (credential) applyCredentialPlacements(url, headers, credential);
|
|
429
|
+
if (credential?.authorizeProviderRequest) {
|
|
430
|
+
let authorized = false;
|
|
431
|
+
try {
|
|
432
|
+
authorized = await credential.authorizeProviderRequest();
|
|
433
|
+
} catch {
|
|
434
|
+
authorized = false;
|
|
435
|
+
}
|
|
436
|
+
if (!authorized) {
|
|
437
|
+
throw new IntegrationInvocationError(
|
|
438
|
+
"authorization_rejected",
|
|
439
|
+
"The connected account is no longer authorized for this operation",
|
|
440
|
+
"not_started",
|
|
441
|
+
false,
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
429
445
|
return await fetchWithDeadline(
|
|
430
446
|
options.transport,
|
|
431
447
|
url,
|
package/src/types.ts
CHANGED
|
@@ -37,6 +37,8 @@ export interface IntegrationCredentialAudience {
|
|
|
37
37
|
export interface ResolvedIntegrationCredential {
|
|
38
38
|
readonly audience: IntegrationCredentialAudience;
|
|
39
39
|
readonly placements: readonly IntegrationCredentialPlacement[];
|
|
40
|
+
/** Exact accepted-attempt fence invoked immediately before one HTTP request. */
|
|
41
|
+
readonly authorizeProviderRequest?: () => Promise<boolean>;
|
|
40
42
|
readonly expiresAt?: string;
|
|
41
43
|
readonly scope?: readonly string[];
|
|
42
44
|
}
|
|
@@ -54,7 +56,7 @@ export interface IntegrationInvocationAuthority {
|
|
|
54
56
|
|
|
55
57
|
export interface ResolveIntegrationCredentialRequest extends IntegrationInvocationAuthority {
|
|
56
58
|
readonly protocol: Exclude<IntegrationProtocol, "mcp">;
|
|
57
|
-
readonly
|
|
59
|
+
readonly definitionId: string;
|
|
58
60
|
readonly revisionId: string;
|
|
59
61
|
readonly operationKey: string;
|
|
60
62
|
readonly destinationUrl: string;
|
|
@@ -90,7 +92,7 @@ export interface IntegrationRevision<
|
|
|
90
92
|
> {
|
|
91
93
|
readonly id: string;
|
|
92
94
|
readonly protocol: TProtocol;
|
|
93
|
-
readonly
|
|
95
|
+
readonly definitionId: string;
|
|
94
96
|
readonly contentSha256: string;
|
|
95
97
|
readonly source: IntegrationRevisionSource;
|
|
96
98
|
readonly title: string;
|
package/dist/providers.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export interface OAuthProviderPreset {
|
|
2
|
-
readonly authorizationUrl: string;
|
|
3
|
-
readonly tokenUrl: string;
|
|
4
|
-
readonly scopes: readonly string[];
|
|
5
|
-
readonly tokenPlacement: {
|
|
6
|
-
readonly carrier: "header";
|
|
7
|
-
readonly name: "Authorization";
|
|
8
|
-
readonly prefix: "Bearer ";
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
export interface OpenApiProviderPreset {
|
|
12
|
-
readonly id: string;
|
|
13
|
-
readonly name: string;
|
|
14
|
-
readonly summary: string;
|
|
15
|
-
readonly family: "google" | "microsoft";
|
|
16
|
-
readonly sourceFormat: "google-discovery" | "openapi";
|
|
17
|
-
readonly sourceUrl: string;
|
|
18
|
-
readonly baseUrl: string;
|
|
19
|
-
readonly oauth: OAuthProviderPreset;
|
|
20
|
-
readonly pathPrefixes?: readonly string[];
|
|
21
|
-
readonly healthOperation?: string;
|
|
22
|
-
readonly healthArgs?: Readonly<Record<string, unknown>>;
|
|
23
|
-
readonly features: readonly IntegrationFeatureDefinition[];
|
|
24
|
-
}
|
|
25
|
-
export interface IntegrationFeatureDefinition {
|
|
26
|
-
readonly featureKey: string;
|
|
27
|
-
readonly kind: "knowledge_source" | "inbound_trigger" | "delivery_destination" | "identity_link";
|
|
28
|
-
readonly configSchema: Readonly<Record<string, unknown>>;
|
|
29
|
-
readonly capabilities: Readonly<Record<string, unknown>>;
|
|
30
|
-
}
|
|
31
|
-
export declare const GOOGLE_DRIVE_PRESET: OpenApiProviderPreset;
|
|
32
|
-
export declare const GOOGLE_GMAIL_PRESET: OpenApiProviderPreset;
|
|
33
|
-
export declare const MICROSOFT_GRAPH_OPENAPI_URL = "https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml";
|
|
34
|
-
export declare const MICROSOFT_GRAPH_BASE_URL = "https://graph.microsoft.com/v1.0";
|
|
35
|
-
export declare const MICROSOFT_OUTLOOK_MAIL_PRESET: OpenApiProviderPreset;
|
|
36
|
-
export declare const MICROSOFT_OUTLOOK_CALENDAR_PRESET: OpenApiProviderPreset;
|
|
37
|
-
export declare const MICROSOFT_OUTLOOK_CONTACTS_PRESET: OpenApiProviderPreset;
|
|
38
|
-
export declare const MICROSOFT_ONEDRIVE_PRESET: OpenApiProviderPreset;
|
|
39
|
-
export declare const CORE_PROVIDER_PRESETS: readonly OpenApiProviderPreset[];
|
|
40
|
-
export declare function providerPresetById(id: string): OpenApiProviderPreset | undefined;
|
|
41
|
-
export declare function providerDomainForPreset(preset: OpenApiProviderPreset): string;
|
|
42
|
-
export declare function integrationFeaturesForPreset(presetId: string | null | undefined): readonly IntegrationFeatureDefinition[];
|
|
43
|
-
export declare function filterOpenApiDocumentForPreset(document: Record<string, unknown>, preset: OpenApiProviderPreset): Record<string, unknown>;
|
|
44
|
-
export declare function googleDiscoveryToOpenApi(discovery: unknown): Record<string, unknown>;
|