@opendatalabs/personal-server-ts-core 1.29.1 → 1.30.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.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Where the owner answers an MCP client's scope request.
3
+ *
4
+ * `request_scope_access` only records a pending request; the owner decides it
5
+ * on Vana Web (the same approval surface as app data connection requests).
6
+ * The page loads the request from this server and posts the decision back to
7
+ * `POST /v1/mcp/connections/:id/scope-request/{approve,deny}` with a
8
+ * Web3Signed owner request, so the link has to name both the connection and
9
+ * the server's public origin:
10
+ *
11
+ * https://app.vana.org/mcp/requests/<connectionId>?ps_origin=<server origin>
12
+ *
13
+ * `ps_origin` is the same param the MCP OAuth approval link carries. The page
14
+ * treats it as a claim, never as proof: before it signs anything it checks
15
+ * that the Gateway lists an active server registered by the signed-in owner
16
+ * at exactly that origin.
17
+ */
18
+ /** Production Vana Web. */
19
+ export declare const VANA_WEB_ORIGIN = "https://app.vana.org";
20
+ /** Vana Web route that renders one MCP scope request. */
21
+ export declare const MCP_SCOPE_REQUEST_APPROVAL_PATH = "/mcp/requests";
22
+ /** Query param naming the server that holds the request. */
23
+ export declare const MCP_APPROVAL_PS_ORIGIN_PARAM = "ps_origin";
24
+ /** What a host knows when the agent asks for a link. */
25
+ export interface McpScopeRequestApprovalUrlContext {
26
+ /**
27
+ * This server's current public origin (the Web3Signed audience): the
28
+ * tunnel URL once it is up, otherwise the configured origin.
29
+ */
30
+ serverOrigin?: string;
31
+ }
32
+ /**
33
+ * Host hook: returns the page for one connection's pending request, or
34
+ * undefined to keep pointing the owner at Vana without a link.
35
+ */
36
+ export type McpScopeRequestApprovalUrlHook = (connectionId: string, context: McpScopeRequestApprovalUrlContext) => string | undefined;
37
+ /**
38
+ * The approval link for one connection, or undefined when this server cannot
39
+ * be reached from that web origin (no public origin yet, local-only http).
40
+ */
41
+ export declare function buildMcpScopeRequestApprovalUrl(input: {
42
+ connectionId: string;
43
+ serverOrigin: string | undefined;
44
+ webOrigin?: string;
45
+ }): string | undefined;
46
+ /**
47
+ * Default hook for hosts that let Vana Web answer scope requests: builds the
48
+ * link from the server's current public origin at the moment the agent asks.
49
+ * Pass `webOrigin` for app-dev or a local web dev server.
50
+ */
51
+ export declare function vanaWebMcpScopeRequestApprovalUrl(options?: {
52
+ webOrigin?: string;
53
+ }): McpScopeRequestApprovalUrlHook;
54
+ //# sourceMappingURL=approval-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-url.d.ts","sourceRoot":"","sources":["../../src/mcp/approval-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,2BAA2B;AAC3B,eAAO,MAAM,eAAe,yBAAyB,CAAC;AAEtD,yDAAyD;AACzD,eAAO,MAAM,+BAA+B,kBAAkB,CAAC;AAE/D,4DAA4D;AAC5D,eAAO,MAAM,4BAA4B,cAAc,CAAC;AAExD,wDAAwD;AACxD,MAAM,WAAW,iCAAiC;IAChD;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,8BAA8B,GAAG,CAC3C,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,iCAAiC,KACvC,MAAM,GAAG,SAAS,CAAC;AAkCxB;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,GAAG,SAAS,CAWrB;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,8BAA8B,CAOhC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Where the owner answers an MCP client's scope request.
3
+ *
4
+ * `request_scope_access` only records a pending request; the owner decides it
5
+ * on Vana Web (the same approval surface as app data connection requests).
6
+ * The page loads the request from this server and posts the decision back to
7
+ * `POST /v1/mcp/connections/:id/scope-request/{approve,deny}` with a
8
+ * Web3Signed owner request, so the link has to name both the connection and
9
+ * the server's public origin:
10
+ *
11
+ * https://app.vana.org/mcp/requests/<connectionId>?ps_origin=<server origin>
12
+ *
13
+ * `ps_origin` is the same param the MCP OAuth approval link carries. The page
14
+ * treats it as a claim, never as proof: before it signs anything it checks
15
+ * that the Gateway lists an active server registered by the signed-in owner
16
+ * at exactly that origin.
17
+ */
18
+ /** Production Vana Web. */
19
+ export const VANA_WEB_ORIGIN = "https://app.vana.org";
20
+ /** Vana Web route that renders one MCP scope request. */
21
+ export const MCP_SCOPE_REQUEST_APPROVAL_PATH = "/mcp/requests";
22
+ /** Query param naming the server that holds the request. */
23
+ export const MCP_APPROVAL_PS_ORIGIN_PARAM = "ps_origin";
24
+ function parseOrigin(value) {
25
+ if (!value)
26
+ return undefined;
27
+ try {
28
+ const url = new URL(value);
29
+ return url.protocol === "http:" || url.protocol === "https:"
30
+ ? url
31
+ : undefined;
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ function isLoopbackHost(hostname) {
38
+ return (hostname === "localhost" ||
39
+ hostname === "127.0.0.1" ||
40
+ hostname === "[::1]" ||
41
+ hostname.endsWith(".localhost"));
42
+ }
43
+ /**
44
+ * A page on `webOrigin` can only call a server it can reach: an https server
45
+ * from anywhere, a loopback http server only from a loopback dev web origin
46
+ * (a public https page cannot fetch http, and a phone cannot reach the
47
+ * laptop's loopback at all).
48
+ */
49
+ function isReachableFromWeb(web, server) {
50
+ if (server.protocol === "https:")
51
+ return true;
52
+ return isLoopbackHost(server.hostname) && isLoopbackHost(web.hostname);
53
+ }
54
+ /**
55
+ * The approval link for one connection, or undefined when this server cannot
56
+ * be reached from that web origin (no public origin yet, local-only http).
57
+ */
58
+ export function buildMcpScopeRequestApprovalUrl(input) {
59
+ const web = parseOrigin(input.webOrigin ?? VANA_WEB_ORIGIN);
60
+ const server = parseOrigin(input.serverOrigin);
61
+ if (!web || !server || !input.connectionId)
62
+ return undefined;
63
+ if (!isReachableFromWeb(web, server))
64
+ return undefined;
65
+ const url = new URL(`${MCP_SCOPE_REQUEST_APPROVAL_PATH}/${encodeURIComponent(input.connectionId)}`, web.origin);
66
+ url.searchParams.set(MCP_APPROVAL_PS_ORIGIN_PARAM, server.origin);
67
+ return url.toString();
68
+ }
69
+ /**
70
+ * Default hook for hosts that let Vana Web answer scope requests: builds the
71
+ * link from the server's current public origin at the moment the agent asks.
72
+ * Pass `webOrigin` for app-dev or a local web dev server.
73
+ */
74
+ export function vanaWebMcpScopeRequestApprovalUrl(options = {}) {
75
+ return (connectionId, context) => buildMcpScopeRequestApprovalUrl({
76
+ connectionId,
77
+ serverOrigin: context.serverOrigin,
78
+ webOrigin: options.webOrigin,
79
+ });
80
+ }
81
+ //# sourceMappingURL=approval-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-url.js","sourceRoot":"","sources":["../../src/mcp/approval-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAEtD,yDAAyD;AACzD,MAAM,CAAC,MAAM,+BAA+B,GAAG,eAAe,CAAC;AAE/D,4DAA4D;AAC5D,MAAM,CAAC,MAAM,4BAA4B,GAAG,WAAW,CAAC;AAoBxD,SAAS,WAAW,CAAC,KAAyB;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAC1D,CAAC,CAAC,GAAG;YACL,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB;IACtC,OAAO,CACL,QAAQ,KAAK,WAAW;QACxB,QAAQ,KAAK,WAAW;QACxB,QAAQ,KAAK,OAAO;QACpB,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAChC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,GAAQ,EAAE,MAAW;IAC/C,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,+BAA+B,CAAC,KAI/C;IACC,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,SAAS,IAAI,eAAe,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY;QAAE,OAAO,SAAS,CAAC;IAC7D,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IACvD,MAAM,GAAG,GAAG,IAAI,GAAG,CACjB,GAAG,+BAA+B,IAAI,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAC9E,GAAG,CAAC,MAAM,CACX,CAAC;IACF,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAClE,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iCAAiC,CAC/C,UAAkC,EAAE;IAEpC,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE,CAC/B,+BAA+B,CAAC;QAC9B,YAAY;QACZ,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAC;AACP,CAAC"}
@@ -7,13 +7,18 @@
7
7
  * POST /v1/mcp/connections/:id/approve
8
8
  * DELETE /v1/mcp/connections/:id
9
9
  *
10
+ * plus the server-signed answer to an MCP client's `request_scope_access`:
11
+ *
12
+ * POST /v1/mcp/connections/:id/scope-request/approve
13
+ * POST /v1/mcp/connections/:id/scope-request/deny
14
+ *
10
15
  * These endpoints DO NOT read user data. They only manage connection records
11
16
  * — create the per-connection grantee/token, store grant ids after the user
12
17
  * approves them, and mark connections revoked.
13
18
  */
14
- import type { DataPortabilityGatewayConfig, GatewayClient } from "@opendatalabs/vana-sdk/browser";
19
+ import { type DataPortabilityGatewayConfig, type GatewayClient } from "@opendatalabs/vana-sdk/browser";
15
20
  import type { ServerSigner } from "../signing/index.js";
16
- import type { McpConnectionRecord, McpConnectionStore, McpConnectionGrant, McpScopeAccessRequest, McpOAuthAuthorizationRecord, McpOAuthAuthorizationStore } from "./types.js";
21
+ import type { McpConnectionRecord, McpConnectionStore, McpConnectionGrant, McpScopeAccessDecision, McpScopeAccessRequest, McpOAuthAuthorizationRecord, McpOAuthAuthorizationStore } from "./types.js";
17
22
  export declare function hashConnectionToken(token: string): Promise<string>;
18
23
  export interface CreateMcpConnectionInput {
19
24
  displayName?: string;
@@ -49,6 +54,13 @@ export interface ApproveMcpConnectionOptions {
49
54
  store: McpConnectionStore;
50
55
  now?: () => Date;
51
56
  }
57
+ /**
58
+ * errorCode for an unknown connection id on the single-connection and
59
+ * scope-request routes. Distinct from the generic `NOT_FOUND` a server
60
+ * answers for a route it does not have, so a caller (Vana Web) can tell
61
+ * "this request is gone" from "this server predates the route".
62
+ */
63
+ export declare const MCP_CONNECTION_NOT_FOUND = "MCP_CONNECTION_NOT_FOUND";
52
64
  export declare class McpConnectionNotFoundError extends Error {
53
65
  connectionId: string;
54
66
  constructor(connectionId: string);
@@ -71,6 +83,51 @@ export declare function requestMcpScopeAccess(input: {
71
83
  connection: McpConnectionRecord;
72
84
  requestRecorded: boolean;
73
85
  }>;
86
+ /** A scope-request answer the owner cannot give in the connection's state. */
87
+ export declare class McpScopeRequestError extends Error {
88
+ code: string;
89
+ status: 400 | 409 | 500 | 502;
90
+ body?: unknown | undefined;
91
+ constructor(code: string, message: string, status?: 400 | 409 | 500 | 502, body?: unknown | undefined);
92
+ }
93
+ export interface ApproveMcpScopeAccessRequestInput {
94
+ connectionId: string;
95
+ /** Scopes the owner approves; must be a subset of the pending request. */
96
+ scopes: unknown;
97
+ }
98
+ export interface ApproveMcpScopeAccessRequestOptions {
99
+ store: McpConnectionStore;
100
+ gateway: Pick<GatewayClient, "getBuilder" | "getGrant" | "createGrant">;
101
+ serverOwner?: `0x${string}`;
102
+ serverSigner?: Pick<ServerSigner, "signGrantRegistration">;
103
+ now?: () => Date;
104
+ }
105
+ export interface ApproveMcpScopeAccessRequestOutput {
106
+ connection: McpConnectionRecord;
107
+ grantId: string;
108
+ /** Every scope the connection's grant now covers. */
109
+ grantedScopes: string[];
110
+ approvedScopes: string[];
111
+ deniedScopes: string[];
112
+ }
113
+ /**
114
+ * Owner approves (part of) a pending `request_scope_access` without an
115
+ * external signer: the server signs the grant itself, as the OAuth approve
116
+ * path does.
117
+ *
118
+ * The gateway keeps ONE grant per (owner, grantee) and a new registration
119
+ * replaces its scopes, so the grant is re-signed over the union of every
120
+ * scope the connection already holds plus the approved ones. Signing only
121
+ * the new scopes would silently revoke the old ones.
122
+ */
123
+ export declare function approveMcpScopeAccessRequest(input: ApproveMcpScopeAccessRequestInput, options: ApproveMcpScopeAccessRequestOptions): Promise<ApproveMcpScopeAccessRequestOutput>;
124
+ /** Owner declines the pending request; the decision stays for the client. */
125
+ export declare function denyMcpScopeAccessRequest(input: {
126
+ connectionId: string;
127
+ }, options: {
128
+ store: McpConnectionStore;
129
+ now?: () => Date;
130
+ }): Promise<McpConnectionRecord>;
74
131
  export interface RevokeMcpConnectionOptions {
75
132
  store: McpConnectionStore;
76
133
  now?: () => Date;
@@ -86,13 +143,20 @@ export interface McpConnectionView {
86
143
  granteeAddress: `0x${string}`;
87
144
  status: "pending" | "approved" | "revoked";
88
145
  grants: McpConnectionGrant[];
146
+ /** Every scope the connection's grants cover, deduplicated and sorted. */
147
+ grantedScopes: string[];
148
+ /** Scopes the MCP client is still asking for, if any. */
89
149
  scopeAccessRequest?: McpScopeAccessRequest;
150
+ /** Owner's answer to the most recent scope request. */
151
+ scopeAccessDecision?: McpScopeAccessDecision;
90
152
  createdAt: string;
91
153
  approvedAt?: string;
92
154
  revokedAt?: string;
93
155
  lastUsedAt?: string;
94
156
  }
95
157
  export declare function toMcpConnectionView(record: McpConnectionRecord): McpConnectionView;
158
+ /** One connection as the owner sees it; throws when the id is unknown. */
159
+ export declare function getMcpConnectionView(connectionId: string, store: McpConnectionStore): Promise<McpConnectionView>;
96
160
  export declare function listMcpConnectionViews(store: McpConnectionStore): Promise<McpConnectionView[]>;
97
161
  export interface CreateMcpOAuthAuthorizationInput {
98
162
  clientId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"connection-api.d.ts","sourceRoot":"","sources":["../../src/mcp/connection-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection-api.d.ts","sourceRoot":"","sources":["../../src/mcp/connection-api.ts"],"names":[],"mappings":"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"}
@@ -7,12 +7,18 @@
7
7
  * POST /v1/mcp/connections/:id/approve
8
8
  * DELETE /v1/mcp/connections/:id
9
9
  *
10
+ * plus the server-signed answer to an MCP client's `request_scope_access`:
11
+ *
12
+ * POST /v1/mcp/connections/:id/scope-request/approve
13
+ * POST /v1/mcp/connections/:id/scope-request/deny
14
+ *
10
15
  * These endpoints DO NOT read user data. They only manage connection records
11
16
  * — create the per-connection grantee/token, store grant ids after the user
12
17
  * approves them, and mark connections revoked.
13
18
  */
14
19
  import { generateMcpGrantee } from "./grantee.js";
15
20
  import { createGrantContract } from "../contracts/index.js";
21
+ import { ScopeSchema, } from "@opendatalabs/vana-sdk/browser";
16
22
  import { appUrlFromOAuthRedirectUri, ensureMcpGranteeRegistered, McpGranteeRegistrationError, } from "./builder-registration.js";
17
23
  import { MCP_SCOPE_ACCESS_REQUEST_LIMIT } from "./types.js";
18
24
  import { isMcpRefreshExpired, MCP_TOKEN_TTL_MS, mcpRefreshExpiry, mcpTokenExpiry, } from "./token-expiry.js";
@@ -103,6 +109,13 @@ export function buildMcpProtectedResourceMetadataUrl(publicOrigin) {
103
109
  : publicOrigin;
104
110
  return `${base}/.well-known/oauth-protected-resource/mcp`;
105
111
  }
112
+ /**
113
+ * errorCode for an unknown connection id on the single-connection and
114
+ * scope-request routes. Distinct from the generic `NOT_FOUND` a server
115
+ * answers for a route it does not have, so a caller (Vana Web) can tell
116
+ * "this request is gone" from "this server predates the route".
117
+ */
118
+ export const MCP_CONNECTION_NOT_FOUND = "MCP_CONNECTION_NOT_FOUND";
106
119
  export class McpConnectionNotFoundError extends Error {
107
120
  connectionId;
108
121
  constructor(connectionId) {
@@ -185,6 +198,212 @@ export async function requestMcpScopeAccess(input, options) {
185
198
  missingInputScopes.every((scope) => updated.scopeAccessRequest?.scopes.includes(scope)),
186
199
  };
187
200
  }
201
+ /** A scope-request answer the owner cannot give in the connection's state. */
202
+ export class McpScopeRequestError extends Error {
203
+ code;
204
+ status;
205
+ body;
206
+ constructor(code, message, status = 400, body) {
207
+ super(message);
208
+ this.code = code;
209
+ this.status = status;
210
+ this.body = body;
211
+ this.name = "McpScopeRequestError";
212
+ }
213
+ }
214
+ /**
215
+ * Owner approves (part of) a pending `request_scope_access` without an
216
+ * external signer: the server signs the grant itself, as the OAuth approve
217
+ * path does.
218
+ *
219
+ * The gateway keeps ONE grant per (owner, grantee) and a new registration
220
+ * replaces its scopes, so the grant is re-signed over the union of every
221
+ * scope the connection already holds plus the approved ones. Signing only
222
+ * the new scopes would silently revoke the old ones.
223
+ */
224
+ export async function approveMcpScopeAccessRequest(input, options) {
225
+ const connection = await options.store.getById(input.connectionId);
226
+ if (!connection)
227
+ throw new McpConnectionNotFoundError(input.connectionId);
228
+ const pending = requirePendingScopeRequest(connection);
229
+ const approvedScopes = parseApprovedScopes(input.scopes, pending);
230
+ const live = await readLiveGrantState(connection, options.gateway);
231
+ const grantedScopes = sortedUnique([...live.scopes, ...approvedScopes]);
232
+ let grantResult;
233
+ try {
234
+ grantResult = await createGrantContract({
235
+ gateway: options.gateway,
236
+ serverOwner: options.serverOwner,
237
+ serverSigner: options.serverSigner,
238
+ body: {
239
+ granteeAddress: connection.granteeAddress,
240
+ scopes: grantedScopes,
241
+ grantVersion: live.nextGrantVersion,
242
+ ...(live.expiresAt !== undefined ? { expiresAt: live.expiresAt } : {}),
243
+ },
244
+ });
245
+ }
246
+ catch (err) {
247
+ throw new McpScopeRequestError("GRANT_CREATION_FAILED", err instanceof Error ? err.message : String(err), 502);
248
+ }
249
+ if (!grantResult.ok) {
250
+ throw new McpScopeRequestError("GRANT_CREATION_FAILED", extractGrantErrorMessage(grantResult.body), grantResult.status >= 500 ? 502 : grantResult.status === 409 ? 409 : 400, grantResult.body);
251
+ }
252
+ const grantId = grantResult.body.grantId;
253
+ if (!grantId) {
254
+ throw new McpScopeRequestError("GRANT_CREATION_FAILED", "Gateway did not return a grant id.", 502, grantResult.body);
255
+ }
256
+ const decidedAt = nowIso(options.now);
257
+ const deniedScopes = pending.scopes.filter((scope) => !approvedScopes.includes(scope));
258
+ const updated = await options.store.mutate(input.connectionId, (latest) => {
259
+ // The grant above was signed from the state read before the gateway
260
+ // round trip; refuse to store it over a change made in between.
261
+ requirePendingScopeRequest(latest);
262
+ if (JSON.stringify(latest.grants) !== JSON.stringify(connection.grants)) {
263
+ throw new McpScopeRequestError("CONCURRENT_UPDATE", "The connection's grants changed during approval; retry.", 409);
264
+ }
265
+ const decision = {
266
+ decision: "approved",
267
+ approvedScopes,
268
+ deniedScopes,
269
+ requestedAt: pending.requestedAt,
270
+ decidedAt,
271
+ };
272
+ return {
273
+ ...latest,
274
+ grants: [{ grantId, scopes: grantedScopes }],
275
+ scopeAccessRequest: undefined,
276
+ scopeAccessDecision: decision,
277
+ };
278
+ });
279
+ if (!updated)
280
+ throw new McpConnectionNotFoundError(input.connectionId);
281
+ return {
282
+ connection: updated,
283
+ grantId,
284
+ grantedScopes,
285
+ approvedScopes,
286
+ deniedScopes,
287
+ };
288
+ }
289
+ /** Owner declines the pending request; the decision stays for the client. */
290
+ export async function denyMcpScopeAccessRequest(input, options) {
291
+ const decidedAt = nowIso(options.now);
292
+ const updated = await options.store.mutate(input.connectionId, (latest) => {
293
+ const pending = requirePendingScopeRequest(latest);
294
+ const decision = {
295
+ decision: "denied",
296
+ approvedScopes: [],
297
+ deniedScopes: pending.scopes,
298
+ requestedAt: pending.requestedAt,
299
+ decidedAt,
300
+ };
301
+ return {
302
+ ...latest,
303
+ scopeAccessRequest: undefined,
304
+ scopeAccessDecision: decision,
305
+ };
306
+ });
307
+ if (!updated)
308
+ throw new McpConnectionNotFoundError(input.connectionId);
309
+ return updated;
310
+ }
311
+ function requirePendingScopeRequest(connection) {
312
+ if (connection.status !== "approved") {
313
+ throw new McpConnectionStateError(connection.id, connection.status, "approved");
314
+ }
315
+ const pending = connection.scopeAccessRequest;
316
+ if (!pending || pending.scopes.length === 0) {
317
+ throw new McpScopeRequestError("NO_PENDING_REQUEST", `mcp connection ${connection.id} has no pending scope request`, 409);
318
+ }
319
+ return pending;
320
+ }
321
+ function parseApprovedScopes(value, pending) {
322
+ if (!Array.isArray(value) || value.length === 0) {
323
+ throw new McpScopeRequestError("SCOPES_REQUIRED", "Approve requires a non-empty scopes array; use deny to decline all.");
324
+ }
325
+ const scopes = [];
326
+ for (const raw of value) {
327
+ const scope = typeof raw === "string" ? raw.trim() : "";
328
+ if (!ScopeSchema.safeParse(scope).success) {
329
+ throw new McpScopeRequestError("INVALID_SCOPE", `Invalid scope: ${JSON.stringify(raw)}`);
330
+ }
331
+ if (!pending.scopes.includes(scope)) {
332
+ throw new McpScopeRequestError("SCOPE_NOT_REQUESTED", `Scope ${scope} is not part of the pending request`);
333
+ }
334
+ if (!scopes.includes(scope))
335
+ scopes.push(scope);
336
+ }
337
+ return scopes.sort();
338
+ }
339
+ /**
340
+ * Read what the gateway holds for this connection's grantee: the scopes the
341
+ * replacement grant must keep, the version counter it must advance past, and
342
+ * the expiry it must not outlive. A revoked grant contributes its version but
343
+ * no scopes, so approval never resurrects access the owner already pulled.
344
+ */
345
+ async function readLiveGrantState(connection, gateway) {
346
+ const grantIds = sortedUnique(connection.grants.map((grant) => grant.grantId));
347
+ const revoked = new Set();
348
+ const scopes = new Set();
349
+ let maxVersion = 0n;
350
+ let expiresAt;
351
+ for (const grantId of grantIds) {
352
+ let live;
353
+ try {
354
+ live = await gateway.getGrant(grantId);
355
+ }
356
+ catch (err) {
357
+ throw new McpScopeRequestError("GATEWAY_UNAVAILABLE", `Could not read grant ${grantId}: ${err instanceof Error ? err.message : String(err)}`, 502);
358
+ }
359
+ if (!live)
360
+ continue;
361
+ const version = parseUint(live.grantVersion);
362
+ if (version !== null && version > maxVersion)
363
+ maxVersion = version;
364
+ if (live.revokedAt) {
365
+ revoked.add(grantId);
366
+ continue;
367
+ }
368
+ for (const scope of live.scopes)
369
+ scopes.add(scope);
370
+ const liveExpiry = parseExpirySeconds(live.expiresAt);
371
+ if (liveExpiry !== undefined) {
372
+ expiresAt =
373
+ expiresAt === undefined ? liveExpiry : Math.max(expiresAt, liveExpiry);
374
+ }
375
+ }
376
+ for (const grant of connection.grants) {
377
+ if (revoked.has(grant.grantId))
378
+ continue;
379
+ for (const scope of grant.scopes)
380
+ scopes.add(scope);
381
+ }
382
+ return {
383
+ scopes: sortedUnique(scopes),
384
+ nextGrantVersion: (maxVersion + 1n).toString(),
385
+ ...(expiresAt !== undefined ? { expiresAt } : {}),
386
+ };
387
+ }
388
+ function parseUint(value) {
389
+ if (typeof value !== "string" || !/^\d+$/u.test(value))
390
+ return null;
391
+ return BigInt(value);
392
+ }
393
+ /** Gateway expiry may be unix seconds or an ISO date; 0/null means perpetual. */
394
+ function parseExpirySeconds(value) {
395
+ if (!value)
396
+ return undefined;
397
+ if (/^\d+$/u.test(value)) {
398
+ const seconds = Number(value);
399
+ return seconds > 0 ? seconds : undefined;
400
+ }
401
+ const ms = Date.parse(value);
402
+ return Number.isFinite(ms) ? Math.floor(ms / 1000) : undefined;
403
+ }
404
+ function sortedUnique(values) {
405
+ return Array.from(new Set(values)).sort();
406
+ }
188
407
  export async function revokeMcpConnection(connectionId, options) {
189
408
  const existing = await options.store.getById(connectionId);
190
409
  if (!existing)
@@ -210,13 +429,22 @@ export function toMcpConnectionView(record) {
210
429
  granteeAddress: record.granteeAddress,
211
430
  status: record.status,
212
431
  grants: record.grants,
432
+ grantedScopes: sortedUnique(record.grants.flatMap((grant) => grant.scopes)),
213
433
  scopeAccessRequest: record.scopeAccessRequest,
434
+ scopeAccessDecision: record.scopeAccessDecision,
214
435
  createdAt: record.createdAt,
215
436
  approvedAt: record.approvedAt,
216
437
  revokedAt: record.revokedAt,
217
438
  lastUsedAt: record.lastUsedAt,
218
439
  };
219
440
  }
441
+ /** One connection as the owner sees it; throws when the id is unknown. */
442
+ export async function getMcpConnectionView(connectionId, store) {
443
+ const record = await store.getById(connectionId);
444
+ if (!record)
445
+ throw new McpConnectionNotFoundError(connectionId);
446
+ return toMcpConnectionView(record);
447
+ }
220
448
  export async function listMcpConnectionViews(store) {
221
449
  const records = await store.list();
222
450
  return records.map(toMcpConnectionView);