@agent-native/core 0.174.1-nightly-20260825193745 → 0.174.1

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.
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
- error?: undefined;
66
65
  states: {
67
66
  clientId: number;
68
67
  state: string;
69
68
  }[];
69
+ error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
- error?: undefined;
81
80
  users: {
82
81
  clientId: number;
83
82
  lastSeen: number;
84
83
  }[];
84
+ error?: undefined;
85
85
  }>>;
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- ok?: undefined;
17
16
  error: string;
17
+ ok?: undefined;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
- error?: undefined;
21
20
  configured?: undefined;
22
21
  connectPath?: undefined;
23
22
  url: string;
24
23
  id: string;
25
24
  provider: string;
25
+ error?: undefined;
26
26
  }>;
27
27
  export default _default;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { type AuthorizationServerMetadata, type OAuthClientInformationContext, type OAuthClientMetadata, type OAuthClientProvider, type OAuthDiscoveryState, OAuthProtectedResourceMetadata, type StoredOAuthClientInformation, type StoredOAuthTokens, OAuthTokens } from "@modelcontextprotocol/client";
10
10
  import { type OAuthCredential, type OAuthCredentialState, type OAuthRevocationResult } from "../oauth-tokens/lifecycle.js";
11
+ export declare function isGoogleWorkspaceMcpServer(value: string | URL): boolean;
11
12
  export type McpOAuthDiscoveryState = OAuthDiscoveryState;
12
13
  export interface McpOAuthCredentialBundle extends OAuthCredential {
13
14
  serverUrl: string;
@@ -6,13 +6,69 @@
6
6
  * protocol details; this module owns the framework-specific encrypted storage
7
7
  * and refresh boundary.
8
8
  */
9
+ import crypto from "node:crypto";
9
10
  import { auth, refreshAuthorization, validateAuthorizationResponseIssuer, } from "@modelcontextprotocol/client";
10
11
  import { ssrfSafeFetch } from "../extensions/url-safety.js";
11
12
  import { readOAuthCredentialState, resolveOAuthCredentialAccess, markOAuthReconnectRequired, revokeOAuthCredential, saveOAuthCredential, } from "../oauth-tokens/lifecycle.js";
12
13
  import { validateRemoteUrl } from "./remote-url.js";
13
14
  const TOKEN_EXPIRY_SKEW_MS = 60_000;
14
15
  const MAX_OAUTH_REDIRECTS = 5;
16
+ const MAX_OAUTH_RESPONSE_BYTES = 256 * 1024;
15
17
  const MCP_OAUTH_PRIVATE_ORIGINS_ENV = "AGENT_NATIVE_MCP_OAUTH_PRIVATE_ORIGINS";
18
+ const GOOGLE_MCP_ISSUER = "https://accounts.google.com";
19
+ const GOOGLE_MCP_AUTHORIZATION_ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth";
20
+ const GOOGLE_MCP_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
21
+ const GOOGLE_MCP_SCOPES_BY_ORIGIN = {
22
+ "https://workspacemcp.googleapis.com": [
23
+ "https://www.googleapis.com/auth/gmail.readonly",
24
+ "https://www.googleapis.com/auth/drive.readonly",
25
+ "https://www.googleapis.com/auth/calendar.readonly",
26
+ "https://www.googleapis.com/auth/chat.messages.readonly",
27
+ ],
28
+ "https://gmailmcp.googleapis.com": [
29
+ "https://www.googleapis.com/auth/gmail.readonly",
30
+ "https://www.googleapis.com/auth/gmail.compose",
31
+ ],
32
+ "https://drivemcp.googleapis.com": [
33
+ "https://www.googleapis.com/auth/drive.readonly",
34
+ "https://www.googleapis.com/auth/drive.file",
35
+ ],
36
+ "https://docsmcp.googleapis.com": [
37
+ "https://www.googleapis.com/auth/drive.readonly",
38
+ "https://www.googleapis.com/auth/drive.file",
39
+ "https://www.googleapis.com/auth/documents.readonly",
40
+ "https://www.googleapis.com/auth/documents",
41
+ ],
42
+ "https://sheetsmcp.googleapis.com": [
43
+ "https://www.googleapis.com/auth/drive.readonly",
44
+ "https://www.googleapis.com/auth/drive.file",
45
+ "https://www.googleapis.com/auth/spreadsheets.readonly",
46
+ "https://www.googleapis.com/auth/spreadsheets",
47
+ ],
48
+ "https://slidesmcp.googleapis.com": [
49
+ "https://www.googleapis.com/auth/drive.readonly",
50
+ "https://www.googleapis.com/auth/drive.file",
51
+ "https://www.googleapis.com/auth/presentations.readonly",
52
+ "https://www.googleapis.com/auth/presentations",
53
+ ],
54
+ "https://calendarmcp.googleapis.com": [
55
+ "https://www.googleapis.com/auth/calendar.calendarlist.readonly",
56
+ "https://www.googleapis.com/auth/calendar.events.freebusy",
57
+ "https://www.googleapis.com/auth/calendar.events.readonly",
58
+ ],
59
+ "https://chatmcp.googleapis.com": [
60
+ "https://www.googleapis.com/auth/chat.spaces.readonly",
61
+ "https://www.googleapis.com/auth/chat.memberships.readonly",
62
+ "https://www.googleapis.com/auth/chat.messages.readonly",
63
+ "https://www.googleapis.com/auth/chat.messages.create",
64
+ "https://www.googleapis.com/auth/chat.users.readstate.readonly",
65
+ ],
66
+ "https://people.googleapis.com": [
67
+ "https://www.googleapis.com/auth/directory.readonly",
68
+ "https://www.googleapis.com/auth/userinfo.profile",
69
+ "https://www.googleapis.com/auth/contacts.readonly",
70
+ ],
71
+ };
16
72
  function checkedRemoteUrl(value, label) {
17
73
  const result = validateRemoteUrl(String(value));
18
74
  if (!result.ok || !result.url) {
@@ -23,6 +79,142 @@ function checkedRemoteUrl(value, label) {
23
79
  function canonicalServerUrl(value) {
24
80
  return checkedRemoteUrl(value, "server").toString();
25
81
  }
82
+ function googleMcpScopes(serverUrl) {
83
+ try {
84
+ return GOOGLE_MCP_SCOPES_BY_ORIGIN[new URL(serverUrl).origin];
85
+ }
86
+ catch {
87
+ // coercion-ok: an invalid URL is absent from the managed-server allowlist.
88
+ return undefined;
89
+ }
90
+ }
91
+ export function isGoogleWorkspaceMcpServer(value) {
92
+ return Boolean(googleMcpScopes(typeof value === "string" ? value : value.toString()));
93
+ }
94
+ function googleMcpDiscoveryState(serverUrl, scopes) {
95
+ return {
96
+ authorizationServerUrl: GOOGLE_MCP_ISSUER,
97
+ authorizationServerMetadata: {
98
+ issuer: GOOGLE_MCP_ISSUER,
99
+ authorization_endpoint: GOOGLE_MCP_AUTHORIZATION_ENDPOINT,
100
+ token_endpoint: GOOGLE_MCP_TOKEN_ENDPOINT,
101
+ response_types_supported: ["code"],
102
+ grant_types_supported: ["authorization_code", "refresh_token"],
103
+ token_endpoint_auth_methods_supported: ["client_secret_post"],
104
+ code_challenge_methods_supported: ["S256"],
105
+ },
106
+ resourceMetadata: {
107
+ resource: serverUrl,
108
+ authorization_servers: [GOOGLE_MCP_ISSUER],
109
+ bearer_methods_supported: ["header"],
110
+ scopes_supported: [...scopes],
111
+ },
112
+ };
113
+ }
114
+ function googleMcpClientInformation(value) {
115
+ const clientId = value?.client_id;
116
+ const clientSecret = value?.client_secret;
117
+ if (!clientId || !clientSecret) {
118
+ throw new Error("Google Workspace MCP OAuth client credentials are not configured.");
119
+ }
120
+ if (value.issuer && value.issuer !== GOOGLE_MCP_ISSUER) {
121
+ throw new Error("Google Workspace MCP OAuth issuer is invalid.");
122
+ }
123
+ return {
124
+ ...value,
125
+ client_id: clientId,
126
+ client_secret: clientSecret,
127
+ issuer: GOOGLE_MCP_ISSUER,
128
+ };
129
+ }
130
+ function startGoogleMcpOAuthAuthorization(options, scopes) {
131
+ const clientInformation = googleMcpClientInformation(options.clientInformation);
132
+ const codeVerifier = options.codeVerifier ?? crypto.randomBytes(48).toString("base64url");
133
+ const codeChallenge = crypto
134
+ .createHash("sha256")
135
+ .update(codeVerifier)
136
+ .digest("base64url");
137
+ const authorizationUrl = new URL(GOOGLE_MCP_AUTHORIZATION_ENDPOINT);
138
+ authorizationUrl.searchParams.set("client_id", clientInformation.client_id);
139
+ authorizationUrl.searchParams.set("redirect_uri", options.redirectUrl);
140
+ authorizationUrl.searchParams.set("response_type", "code");
141
+ authorizationUrl.searchParams.set("scope", scopes.join(" "));
142
+ authorizationUrl.searchParams.set("state", options.state);
143
+ authorizationUrl.searchParams.set("access_type", "offline");
144
+ authorizationUrl.searchParams.set("prompt", "consent");
145
+ authorizationUrl.searchParams.set("code_challenge", codeChallenge);
146
+ authorizationUrl.searchParams.set("code_challenge_method", "S256");
147
+ return {
148
+ authorizationUrl: checkedRemoteUrl(authorizationUrl, "authorization redirect"),
149
+ codeVerifier,
150
+ state: options.state,
151
+ clientInformation,
152
+ discoveryState: googleMcpDiscoveryState(options.serverUrl, scopes),
153
+ };
154
+ }
155
+ async function readOAuthResponseJson(response) {
156
+ const text = await response.text();
157
+ if (Buffer.byteLength(text) > MAX_OAUTH_RESPONSE_BYTES) {
158
+ throw new Error("MCP OAuth response exceeded the size limit.");
159
+ }
160
+ try {
161
+ const parsed = JSON.parse(text);
162
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
163
+ throw new Error("MCP OAuth response was not an object.");
164
+ }
165
+ return parsed;
166
+ }
167
+ catch {
168
+ throw new Error("MCP OAuth response was not valid JSON.");
169
+ }
170
+ }
171
+ async function finishGoogleMcpOAuthAuthorization(options, scopes) {
172
+ const clientInformation = googleMcpClientInformation(options.clientInformation);
173
+ if (!options.codeVerifier) {
174
+ throw new Error("Google Workspace MCP OAuth code verifier is missing.");
175
+ }
176
+ const response = await guardedOAuthFetch()(GOOGLE_MCP_TOKEN_ENDPOINT, {
177
+ method: "POST",
178
+ headers: { "content-type": "application/x-www-form-urlencoded" },
179
+ body: new URLSearchParams({
180
+ code: options.authorizationCode,
181
+ client_id: clientInformation.client_id,
182
+ client_secret: clientInformation.client_secret,
183
+ redirect_uri: options.redirectUrl,
184
+ grant_type: "authorization_code",
185
+ code_verifier: options.codeVerifier,
186
+ }),
187
+ });
188
+ const body = await readOAuthResponseJson(response);
189
+ const accessToken = typeof body.access_token === "string" ? body.access_token : undefined;
190
+ const tokenType = typeof body.token_type === "string" ? body.token_type : undefined;
191
+ if (!response.ok || !accessToken || !tokenType) {
192
+ throw new Error("Google Workspace MCP OAuth token exchange failed.");
193
+ }
194
+ const expiresIn = Number(body.expires_in);
195
+ const tokens = {
196
+ access_token: accessToken,
197
+ token_type: tokenType,
198
+ ...(Number.isFinite(expiresIn) && expiresIn > 0
199
+ ? { expires_in: expiresIn }
200
+ : {}),
201
+ ...(typeof body.refresh_token === "string"
202
+ ? { refresh_token: body.refresh_token }
203
+ : {}),
204
+ ...(typeof body.scope === "string" ? { scope: body.scope } : {}),
205
+ ...(typeof body.id_token === "string" ? { id_token: body.id_token } : {}),
206
+ issuer: GOOGLE_MCP_ISSUER,
207
+ };
208
+ return {
209
+ credentials: {
210
+ serverUrl: options.serverUrl,
211
+ clientInformation,
212
+ discoveryState: googleMcpDiscoveryState(options.serverUrl, scopes),
213
+ tokens,
214
+ tokenExpiresAt: tokenExpiresAt(tokens),
215
+ },
216
+ };
217
+ }
26
218
  /**
27
219
  * RFC 8707 resource identifiers are exact strings. WHATWG `URL` origin-only
28
220
  * values stringify with a trailing slash (`https://api.builder.io/` vs
@@ -111,7 +303,7 @@ function guardedOAuthFetch() {
111
303
  nextHeaders.delete("cookie");
112
304
  nextHeaders.delete("proxy-authorization");
113
305
  if ((response.status === 307 || response.status === 308) &&
114
- currentInit.body != null) {
306
+ currentInit.body !== null) {
115
307
  throw new Error("MCP OAuth redirect cannot forward a request body across origins.");
116
308
  }
117
309
  }
@@ -307,7 +499,11 @@ export class McpOAuthClientProvider {
307
499
  }
308
500
  }
309
501
  export async function startMcpOAuthAuthorization(options) {
310
- checkedRemoteUrl(options.serverUrl, "server");
502
+ const serverUrl = checkedRemoteUrl(options.serverUrl, "server");
503
+ const googleScopes = googleMcpScopes(serverUrl.toString());
504
+ if (googleScopes) {
505
+ return startGoogleMcpOAuthAuthorization({ ...options, serverUrl: serverUrl.toString() }, googleScopes);
506
+ }
311
507
  const provider = new McpOAuthClientProvider(options);
312
508
  const result = await auth(provider, {
313
509
  serverUrl: options.serverUrl,
@@ -333,7 +529,11 @@ export async function startMcpOAuthAuthorization(options) {
333
529
  };
334
530
  }
335
531
  export async function finishMcpOAuthAuthorization(options) {
336
- checkedRemoteUrl(options.serverUrl, "server");
532
+ const serverUrl = checkedRemoteUrl(options.serverUrl, "server");
533
+ const googleScopes = googleMcpScopes(serverUrl.toString());
534
+ if (googleScopes) {
535
+ return finishGoogleMcpOAuthAuthorization({ ...options, serverUrl: serverUrl.toString() }, googleScopes);
536
+ }
337
537
  const provider = new McpOAuthClientProvider(options);
338
538
  const result = await auth(provider, {
339
539
  serverUrl: options.serverUrl,
@@ -36,6 +36,10 @@ const MANAGED_MCP_OAUTH_CLIENTS = [
36
36
  ],
37
37
  credentialPairs: [["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"]],
38
38
  },
39
+ {
40
+ serverOrigins: ["https://workspacemcp.googleapis.com"],
41
+ credentialPairs: [["GOOGLE_CLIENT_ID", "GOOGLE_CLIENT_SECRET"]],
42
+ },
39
43
  ];
40
44
  export function redirectWithStagedCookies(event, location) {
41
45
  const headers = new Headers({
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
44
  summary: import("./types.js").TraceSummary;
46
45
  spans: import("./types.js").TraceSpan[];
47
46
  id?: undefined;
47
+ error?: undefined;
48
48
  ok?: undefined;
49
49
  } | {
50
- error?: undefined;
51
50
  summary?: undefined;
52
51
  spans?: undefined;
53
52
  id: string;
53
+ error?: undefined;
54
54
  ok?: undefined;
55
55
  } | {
56
56
  summary?: undefined;
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
59
59
  error: any;
60
60
  ok?: undefined;
61
61
  } | {
62
- error?: undefined;
63
62
  summary?: undefined;
64
63
  spans?: undefined;
65
64
  id?: undefined;
66
65
  ok: boolean;
66
+ error?: undefined;
67
67
  }>>;
@@ -34,10 +34,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
34
34
  export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
35
35
  error: any;
36
36
  } | {
37
- error?: undefined;
38
37
  ok: boolean;
39
38
  key: string;
40
39
  baseUrlKey?: string;
41
40
  scope: AgentEngineApiKeyScope;
41
+ error?: undefined;
42
42
  }>>;
43
43
  export {};
@@ -26,8 +26,8 @@ export declare function createRealtimeTokenHandler(): import("h3").EventHandlerW
26
26
  expiresAt?: undefined;
27
27
  ttlSeconds?: undefined;
28
28
  } | {
29
- error?: undefined;
30
29
  token: string;
31
30
  expiresAt: string;
32
31
  ttlSeconds: number;
32
+ error?: undefined;
33
33
  }>>;
@@ -20,6 +20,6 @@ export declare function createTranscribeVoiceHandler(): import("h3").EventHandle
20
20
  error: string;
21
21
  text?: undefined;
22
22
  } | {
23
- error?: undefined;
24
23
  text: string;
24
+ error?: undefined;
25
25
  }>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.174.1-nightly-20260825193745",
3
+ "version": "0.174.1",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -429,8 +429,8 @@
429
429
  "y-protocols": "^1.0.7",
430
430
  "yjs": "^13.6.32",
431
431
  "zod": "^4.3.6",
432
- "@agent-native/recap-cli": "0.5.11",
433
- "@agent-native/toolkit": "^0.16.14"
432
+ "@agent-native/recap-cli": "0.5.12",
433
+ "@agent-native/toolkit": "^0.16.15"
434
434
  },
435
435
  "devDependencies": {
436
436
  "@ai-sdk/anthropic": "^3.0.71",