@mcp-z/oauth 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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/cjs/account-utils.d.cts +107 -0
  4. package/dist/cjs/account-utils.d.ts +107 -0
  5. package/dist/cjs/account-utils.js +481 -0
  6. package/dist/cjs/account-utils.js.map +1 -0
  7. package/dist/cjs/index.d.cts +19 -0
  8. package/dist/cjs/index.d.ts +19 -0
  9. package/dist/cjs/index.js +149 -0
  10. package/dist/cjs/index.js.map +1 -0
  11. package/dist/cjs/jwt-auth.d.cts +53 -0
  12. package/dist/cjs/jwt-auth.d.ts +53 -0
  13. package/dist/cjs/jwt-auth.js +417 -0
  14. package/dist/cjs/jwt-auth.js.map +1 -0
  15. package/dist/cjs/key-utils.d.cts +131 -0
  16. package/dist/cjs/key-utils.d.ts +131 -0
  17. package/dist/cjs/key-utils.js +421 -0
  18. package/dist/cjs/key-utils.js.map +1 -0
  19. package/dist/cjs/lib/account-server/index.d.cts +45 -0
  20. package/dist/cjs/lib/account-server/index.d.ts +45 -0
  21. package/dist/cjs/lib/account-server/index.js +67 -0
  22. package/dist/cjs/lib/account-server/index.js.map +1 -0
  23. package/dist/cjs/lib/account-server/loopback.d.cts +22 -0
  24. package/dist/cjs/lib/account-server/loopback.d.ts +22 -0
  25. package/dist/cjs/lib/account-server/loopback.js +778 -0
  26. package/dist/cjs/lib/account-server/loopback.js.map +1 -0
  27. package/dist/cjs/lib/account-server/me.d.cts +23 -0
  28. package/dist/cjs/lib/account-server/me.d.ts +23 -0
  29. package/dist/cjs/lib/account-server/me.js +412 -0
  30. package/dist/cjs/lib/account-server/me.js.map +1 -0
  31. package/dist/cjs/lib/account-server/shared-utils.d.cts +6 -0
  32. package/dist/cjs/lib/account-server/shared-utils.d.ts +6 -0
  33. package/dist/cjs/lib/account-server/shared-utils.js +235 -0
  34. package/dist/cjs/lib/account-server/shared-utils.js.map +1 -0
  35. package/dist/cjs/lib/account-server/stateless.d.cts +20 -0
  36. package/dist/cjs/lib/account-server/stateless.d.ts +20 -0
  37. package/dist/cjs/lib/account-server/stateless.js +32 -0
  38. package/dist/cjs/lib/account-server/stateless.js.map +1 -0
  39. package/dist/cjs/lib/account-server/types.d.cts +32 -0
  40. package/dist/cjs/lib/account-server/types.d.ts +32 -0
  41. package/dist/cjs/lib/account-server/types.js +7 -0
  42. package/dist/cjs/lib/account-server/types.js.map +1 -0
  43. package/dist/cjs/lib/dcr-types.d.cts +126 -0
  44. package/dist/cjs/lib/dcr-types.d.ts +126 -0
  45. package/dist/cjs/lib/dcr-types.js +12 -0
  46. package/dist/cjs/lib/dcr-types.js.map +1 -0
  47. package/dist/cjs/lib/rfc-metadata-types.d.cts +46 -0
  48. package/dist/cjs/lib/rfc-metadata-types.d.ts +46 -0
  49. package/dist/cjs/lib/rfc-metadata-types.js +8 -0
  50. package/dist/cjs/lib/rfc-metadata-types.js.map +1 -0
  51. package/dist/cjs/package.json +1 -0
  52. package/dist/cjs/pkce.d.cts +36 -0
  53. package/dist/cjs/pkce.d.ts +36 -0
  54. package/dist/cjs/pkce.js +25 -0
  55. package/dist/cjs/pkce.js.map +1 -0
  56. package/dist/cjs/sanitizer.d.cts +37 -0
  57. package/dist/cjs/sanitizer.d.ts +37 -0
  58. package/dist/cjs/sanitizer.js +407 -0
  59. package/dist/cjs/sanitizer.js.map +1 -0
  60. package/dist/cjs/schemas/index.d.cts +36 -0
  61. package/dist/cjs/schemas/index.d.ts +36 -0
  62. package/dist/cjs/schemas/index.js +28 -0
  63. package/dist/cjs/schemas/index.js.map +1 -0
  64. package/dist/cjs/session-auth.d.cts +79 -0
  65. package/dist/cjs/session-auth.d.ts +79 -0
  66. package/dist/cjs/session-auth.js +354 -0
  67. package/dist/cjs/session-auth.js.map +1 -0
  68. package/dist/cjs/templates.d.cts +18 -0
  69. package/dist/cjs/templates.d.ts +18 -0
  70. package/dist/cjs/templates.js +38 -0
  71. package/dist/cjs/templates.js.map +1 -0
  72. package/dist/cjs/types.d.cts +343 -0
  73. package/dist/cjs/types.d.ts +343 -0
  74. package/dist/cjs/types.js +210 -0
  75. package/dist/cjs/types.js.map +1 -0
  76. package/dist/esm/account-utils.d.ts +107 -0
  77. package/dist/esm/account-utils.js +179 -0
  78. package/dist/esm/account-utils.js.map +1 -0
  79. package/dist/esm/index.d.ts +19 -0
  80. package/dist/esm/index.js +23 -0
  81. package/dist/esm/index.js.map +1 -0
  82. package/dist/esm/jwt-auth.d.ts +53 -0
  83. package/dist/esm/jwt-auth.js +164 -0
  84. package/dist/esm/jwt-auth.js.map +1 -0
  85. package/dist/esm/key-utils.d.ts +131 -0
  86. package/dist/esm/key-utils.js +143 -0
  87. package/dist/esm/key-utils.js.map +1 -0
  88. package/dist/esm/lib/account-server/index.d.ts +45 -0
  89. package/dist/esm/lib/account-server/index.js +41 -0
  90. package/dist/esm/lib/account-server/index.js.map +1 -0
  91. package/dist/esm/lib/account-server/loopback.d.ts +22 -0
  92. package/dist/esm/lib/account-server/loopback.js +372 -0
  93. package/dist/esm/lib/account-server/loopback.js.map +1 -0
  94. package/dist/esm/lib/account-server/me.d.ts +23 -0
  95. package/dist/esm/lib/account-server/me.js +170 -0
  96. package/dist/esm/lib/account-server/me.js.map +1 -0
  97. package/dist/esm/lib/account-server/shared-utils.d.ts +6 -0
  98. package/dist/esm/lib/account-server/shared-utils.js +24 -0
  99. package/dist/esm/lib/account-server/shared-utils.js.map +1 -0
  100. package/dist/esm/lib/account-server/stateless.d.ts +20 -0
  101. package/dist/esm/lib/account-server/stateless.js +25 -0
  102. package/dist/esm/lib/account-server/stateless.js.map +1 -0
  103. package/dist/esm/lib/account-server/types.d.ts +32 -0
  104. package/dist/esm/lib/account-server/types.js +6 -0
  105. package/dist/esm/lib/account-server/types.js.map +1 -0
  106. package/dist/esm/lib/dcr-types.d.ts +126 -0
  107. package/dist/esm/lib/dcr-types.js +13 -0
  108. package/dist/esm/lib/dcr-types.js.map +1 -0
  109. package/dist/esm/lib/rfc-metadata-types.d.ts +46 -0
  110. package/dist/esm/lib/rfc-metadata-types.js +7 -0
  111. package/dist/esm/lib/rfc-metadata-types.js.map +1 -0
  112. package/dist/esm/package.json +1 -0
  113. package/dist/esm/pkce.d.ts +36 -0
  114. package/dist/esm/pkce.js +33 -0
  115. package/dist/esm/pkce.js.map +1 -0
  116. package/dist/esm/sanitizer.d.ts +37 -0
  117. package/dist/esm/sanitizer.js +256 -0
  118. package/dist/esm/sanitizer.js.map +1 -0
  119. package/dist/esm/schemas/index.d.ts +36 -0
  120. package/dist/esm/schemas/index.js +19 -0
  121. package/dist/esm/schemas/index.js.map +1 -0
  122. package/dist/esm/session-auth.d.ts +79 -0
  123. package/dist/esm/session-auth.js +141 -0
  124. package/dist/esm/session-auth.js.map +1 -0
  125. package/dist/esm/templates.d.ts +18 -0
  126. package/dist/esm/templates.js +132 -0
  127. package/dist/esm/templates.js.map +1 -0
  128. package/dist/esm/types.d.ts +343 -0
  129. package/dist/esm/types.js +34 -0
  130. package/dist/esm/types.js.map +1 -0
  131. package/package.json +82 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * JWT-based user authentication for multi-tenant deployments
3
+ *
4
+ * Extracts user ID from JWT tokens with signature and claims verification.
5
+ * Supports HS256, RS256, ES256 algorithms via JOSE library.
6
+ */ import { createRemoteJWKSet, importSPKI, jwtVerify } from 'jose';
7
+ /**
8
+ * JWT-based user authentication provider
9
+ *
10
+ * Verifies JWT tokens and extracts user IDs from claims.
11
+ * Use for multi-tenant deployments where users authenticate via JWT.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // HS256 with shared secret
16
+ * const userAuth = new JWTUserAuth({
17
+ * secret: process.env.JWT_SECRET!,
18
+ * issuer: 'https://auth.example.com',
19
+ * audience: 'api.example.com',
20
+ * });
21
+ *
22
+ * // RS256 with public key
23
+ * const userAuth = new JWTUserAuth({
24
+ * publicKey: process.env.JWT_PUBLIC_KEY!,
25
+ * issuer: 'https://auth.example.com',
26
+ * });
27
+ *
28
+ * // RS256 with JWKS URL (dynamic key rotation)
29
+ * const userAuth = new JWTUserAuth({
30
+ * jwksUrl: 'https://auth.example.com/.well-known/jwks.json',
31
+ * issuer: 'https://auth.example.com',
32
+ * audience: 'api.example.com',
33
+ * });
34
+ * ```
35
+ */ export class JWTUserAuth {
36
+ /**
37
+ * Extract and verify user ID from JWT token
38
+ *
39
+ * @param req - HTTP request object with Authorization header
40
+ * @returns User ID from verified JWT claims
41
+ * @throws Error if token missing, invalid, expired, or claims invalid
42
+ */ async getUserId(req) {
43
+ var _httpReq_headers;
44
+ const httpReq = req;
45
+ // Extract Authorization header
46
+ const authHeader = (_httpReq_headers = httpReq.headers) === null || _httpReq_headers === void 0 ? void 0 : _httpReq_headers.authorization;
47
+ if (!authHeader) {
48
+ throw new Error('JWTUserAuth: No Authorization header found');
49
+ }
50
+ // Parse Bearer token
51
+ const match = /^Bearer\s+(.+)$/i.exec(authHeader.trim());
52
+ if (!match) {
53
+ throw new Error('JWTUserAuth: Invalid Authorization header format (expected "Bearer <token>")');
54
+ }
55
+ const token = match[1];
56
+ if (!token) {
57
+ throw new Error('JWTUserAuth: Empty JWT token');
58
+ }
59
+ // Verify JWT and extract payload
60
+ const payload = await this.verifyToken(token);
61
+ // Extract user ID from configured claim
62
+ const userId = payload[this.config.userIdClaim];
63
+ if (!userId || typeof userId !== 'string') {
64
+ throw new Error(`JWTUserAuth: JWT missing or invalid '${this.config.userIdClaim}' claim`);
65
+ }
66
+ return userId;
67
+ }
68
+ /**
69
+ * Verify JWT signature and claims
70
+ */ async verifyToken(token) {
71
+ try {
72
+ // Build verification options
73
+ const options = {
74
+ ...this.config.issuer && {
75
+ issuer: this.config.issuer
76
+ },
77
+ ...this.config.audience && {
78
+ audience: this.config.audience
79
+ },
80
+ ...this.config.clockTolerance && {
81
+ clockTolerance: this.config.clockTolerance
82
+ }
83
+ };
84
+ // Verify with appropriate key type
85
+ let result;
86
+ if (this.config.secret) {
87
+ // HS256 verification with shared secret
88
+ const secret = new TextEncoder().encode(this.config.secret);
89
+ result = await jwtVerify(token, secret, {
90
+ ...options,
91
+ algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : [
92
+ 'HS256'
93
+ ]
94
+ });
95
+ } else if (this.remoteJWKSet) {
96
+ // RS256/ES256 verification with remote JWKS
97
+ result = await jwtVerify(token, this.remoteJWKSet, {
98
+ ...options,
99
+ algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : [
100
+ 'RS256',
101
+ 'ES256'
102
+ ]
103
+ });
104
+ } else if (this.config.publicKey) {
105
+ // RS256/ES256 verification with provided public key
106
+ // If string (PEM), import it first; if JWK, use directly
107
+ const key = typeof this.config.publicKey === 'string' ? await importSPKI(this.config.publicKey, 'RS256') : this.config.publicKey;
108
+ result = await jwtVerify(token, key, {
109
+ ...options,
110
+ algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : [
111
+ 'RS256',
112
+ 'ES256'
113
+ ]
114
+ });
115
+ } else {
116
+ throw new Error('JWTUserAuth: No verification key configured');
117
+ }
118
+ return result.payload;
119
+ } catch (error) {
120
+ if (error instanceof Error) {
121
+ throw new Error(`JWTUserAuth: JWT verification failed: ${error.message}`);
122
+ }
123
+ throw new Error('JWTUserAuth: JWT verification failed');
124
+ }
125
+ }
126
+ constructor(config){
127
+ var _config_userIdClaim, _config_algorithms, _config_clockTolerance;
128
+ // Validate configuration
129
+ if (!config.secret && !config.publicKey && !config.jwksUrl) {
130
+ throw new Error('JWTUserAuth: Must provide one of: secret (HS256), publicKey (RS256/ES256), or jwksUrl');
131
+ }
132
+ if (config.secret && config.secret.length < 32) {
133
+ throw new Error('JWTUserAuth: secret must be at least 32 characters for HS256');
134
+ }
135
+ if ((config.secret ? 1 : 0) + (config.publicKey ? 1 : 0) + (config.jwksUrl ? 1 : 0) > 1) {
136
+ throw new Error('JWTUserAuth: Provide only one of: secret, publicKey, or jwksUrl');
137
+ }
138
+ // Store configuration with defaults
139
+ this.config = {
140
+ ...config.secret !== undefined && {
141
+ secret: config.secret
142
+ },
143
+ ...config.publicKey !== undefined && {
144
+ publicKey: config.publicKey
145
+ },
146
+ ...config.jwksUrl !== undefined && {
147
+ jwksUrl: config.jwksUrl
148
+ },
149
+ ...config.issuer !== undefined && {
150
+ issuer: config.issuer
151
+ },
152
+ ...config.audience !== undefined && {
153
+ audience: config.audience
154
+ },
155
+ userIdClaim: (_config_userIdClaim = config.userIdClaim) !== null && _config_userIdClaim !== void 0 ? _config_userIdClaim : 'sub',
156
+ algorithms: (_config_algorithms = config.algorithms) !== null && _config_algorithms !== void 0 ? _config_algorithms : [],
157
+ clockTolerance: (_config_clockTolerance = config.clockTolerance) !== null && _config_clockTolerance !== void 0 ? _config_clockTolerance : 0
158
+ };
159
+ // Create remote JWK set if using JWKS URL
160
+ if (config.jwksUrl) {
161
+ this.remoteJWKSet = createRemoteJWKSet(new URL(config.jwksUrl));
162
+ }
163
+ }
164
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/jwt-auth.ts"],"sourcesContent":["/**\n * JWT-based user authentication for multi-tenant deployments\n *\n * Extracts user ID from JWT tokens with signature and claims verification.\n * Supports HS256, RS256, ES256 algorithms via JOSE library.\n */\n\nimport { createRemoteJWKSet, importSPKI, type JWK, type JWTPayload, type JWTVerifyOptions, type JWTVerifyResult, jwtVerify } from 'jose';\nimport type { JWTUserAuthConfig, UserAuthProvider } from './types.ts';\n\n/**\n * HTTP request interface (subset needed for JWT auth)\n */\ninterface HttpRequest {\n headers?: {\n authorization?: string;\n };\n}\n\n/**\n * JWT-based user authentication provider\n *\n * Verifies JWT tokens and extracts user IDs from claims.\n * Use for multi-tenant deployments where users authenticate via JWT.\n *\n * @example\n * ```typescript\n * // HS256 with shared secret\n * const userAuth = new JWTUserAuth({\n * secret: process.env.JWT_SECRET!,\n * issuer: 'https://auth.example.com',\n * audience: 'api.example.com',\n * });\n *\n * // RS256 with public key\n * const userAuth = new JWTUserAuth({\n * publicKey: process.env.JWT_PUBLIC_KEY!,\n * issuer: 'https://auth.example.com',\n * });\n *\n * // RS256 with JWKS URL (dynamic key rotation)\n * const userAuth = new JWTUserAuth({\n * jwksUrl: 'https://auth.example.com/.well-known/jwks.json',\n * issuer: 'https://auth.example.com',\n * audience: 'api.example.com',\n * });\n * ```\n */\nexport class JWTUserAuth implements UserAuthProvider {\n private readonly config: {\n secret?: string;\n publicKey?: string | JWK;\n jwksUrl?: string;\n issuer?: string | string[];\n audience?: string | string[];\n userIdClaim: string;\n algorithms: string[];\n clockTolerance: number;\n };\n private readonly remoteJWKSet?: ReturnType<typeof createRemoteJWKSet>;\n\n constructor(config: JWTUserAuthConfig) {\n // Validate configuration\n if (!config.secret && !config.publicKey && !config.jwksUrl) {\n throw new Error('JWTUserAuth: Must provide one of: secret (HS256), publicKey (RS256/ES256), or jwksUrl');\n }\n\n if (config.secret && config.secret.length < 32) {\n throw new Error('JWTUserAuth: secret must be at least 32 characters for HS256');\n }\n\n if ((config.secret ? 1 : 0) + (config.publicKey ? 1 : 0) + (config.jwksUrl ? 1 : 0) > 1) {\n throw new Error('JWTUserAuth: Provide only one of: secret, publicKey, or jwksUrl');\n }\n\n // Store configuration with defaults\n this.config = {\n ...(config.secret !== undefined && { secret: config.secret }),\n ...(config.publicKey !== undefined && { publicKey: config.publicKey }),\n ...(config.jwksUrl !== undefined && { jwksUrl: config.jwksUrl }),\n ...(config.issuer !== undefined && { issuer: config.issuer }),\n ...(config.audience !== undefined && { audience: config.audience }),\n userIdClaim: config.userIdClaim ?? 'sub',\n algorithms: config.algorithms ?? [],\n clockTolerance: config.clockTolerance ?? 0,\n };\n\n // Create remote JWK set if using JWKS URL\n if (config.jwksUrl) {\n this.remoteJWKSet = createRemoteJWKSet(new URL(config.jwksUrl));\n }\n }\n\n /**\n * Extract and verify user ID from JWT token\n *\n * @param req - HTTP request object with Authorization header\n * @returns User ID from verified JWT claims\n * @throws Error if token missing, invalid, expired, or claims invalid\n */\n async getUserId(req: unknown): Promise<string> {\n const httpReq = req as HttpRequest;\n\n // Extract Authorization header\n const authHeader = httpReq.headers?.authorization;\n if (!authHeader) {\n throw new Error('JWTUserAuth: No Authorization header found');\n }\n\n // Parse Bearer token\n const match = /^Bearer\\s+(.+)$/i.exec(authHeader.trim());\n if (!match) {\n throw new Error('JWTUserAuth: Invalid Authorization header format (expected \"Bearer <token>\")');\n }\n\n const token = match[1];\n if (!token) {\n throw new Error('JWTUserAuth: Empty JWT token');\n }\n\n // Verify JWT and extract payload\n const payload = await this.verifyToken(token);\n\n // Extract user ID from configured claim\n const userId = payload[this.config.userIdClaim];\n if (!userId || typeof userId !== 'string') {\n throw new Error(`JWTUserAuth: JWT missing or invalid '${this.config.userIdClaim}' claim`);\n }\n\n return userId;\n }\n\n /**\n * Verify JWT signature and claims\n */\n private async verifyToken(token: string): Promise<JWTPayload> {\n try {\n // Build verification options\n const options: JWTVerifyOptions = {\n ...(this.config.issuer && { issuer: this.config.issuer }),\n ...(this.config.audience && { audience: this.config.audience }),\n ...(this.config.clockTolerance && { clockTolerance: this.config.clockTolerance }),\n };\n\n // Verify with appropriate key type\n let result: JWTVerifyResult;\n\n if (this.config.secret) {\n // HS256 verification with shared secret\n const secret = new TextEncoder().encode(this.config.secret);\n result = await jwtVerify(token, secret, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['HS256'],\n });\n } else if (this.remoteJWKSet) {\n // RS256/ES256 verification with remote JWKS\n result = await jwtVerify(token, this.remoteJWKSet, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['RS256', 'ES256'],\n });\n } else if (this.config.publicKey) {\n // RS256/ES256 verification with provided public key\n // If string (PEM), import it first; if JWK, use directly\n const key = typeof this.config.publicKey === 'string' ? await importSPKI(this.config.publicKey, 'RS256') : this.config.publicKey;\n\n result = await jwtVerify(token, key, {\n ...options,\n algorithms: this.config.algorithms.length > 0 ? this.config.algorithms : ['RS256', 'ES256'],\n });\n } else {\n throw new Error('JWTUserAuth: No verification key configured');\n }\n\n return result.payload;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`JWTUserAuth: JWT verification failed: ${error.message}`);\n }\n throw new Error('JWTUserAuth: JWT verification failed');\n }\n }\n}\n"],"names":["createRemoteJWKSet","importSPKI","jwtVerify","JWTUserAuth","getUserId","req","httpReq","authHeader","headers","authorization","Error","match","exec","trim","token","payload","verifyToken","userId","config","userIdClaim","options","issuer","audience","clockTolerance","result","secret","TextEncoder","encode","algorithms","length","remoteJWKSet","publicKey","key","error","message","jwksUrl","undefined","URL"],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Key generation utilities for consistent storage key format
3
+ *
4
+ * Key format: {accountId}:{service}:{type}
5
+ * Example: work@gmail.com:gmail:token
6
+ */
7
+ import type { Keyv } from 'keyv';
8
+ /**
9
+ * Key types for account-scoped data (requires accountId)
10
+ */
11
+ export type AccountKeyType = 'token' | 'metadata' | 'dcr-client';
12
+ /**
13
+ * Key types for service-scoped data (no accountId)
14
+ */
15
+ export type ServiceKeyType = 'active' | 'linked';
16
+ /**
17
+ * Parameters for account-scoped keys.
18
+ * All fields are required - no silent defaults.
19
+ */
20
+ export interface AccountKeyParams {
21
+ /** Account identifier - typically an email address */
22
+ accountId: string;
23
+ /** Service name (e.g., 'gmail', 'sheets', 'drive', 'outlook') */
24
+ service: string;
25
+ }
26
+ /**
27
+ * Parameters for service-scoped keys.
28
+ * These keys don't include accountId.
29
+ */
30
+ export interface ServiceKeyParams {
31
+ /** Service name */
32
+ service: string;
33
+ }
34
+ /**
35
+ * Create account-scoped storage key.
36
+ *
37
+ * These keys are scoped to a specific account (email address) and store
38
+ * account-specific data like OAuth tokens and account metadata.
39
+ *
40
+ * @param type - Key type ('token' for OAuth tokens, 'metadata' for account details, 'dcr-client' for DCR registration)
41
+ * @param params - Account key parameters with explicit field names
42
+ * @returns Storage key in format: "{accountId}:{service}:{type}"
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // Store OAuth token
47
+ * const tokenKey = createAccountKey('token', {
48
+ * accountId: 'alice@gmail.com',
49
+ * service: 'gmail'
50
+ * });
51
+ * // Returns: "alice@gmail.com:gmail:token"
52
+ *
53
+ * // Store account metadata (alias, timestamps, profile)
54
+ * const metadataKey = createAccountKey('metadata', {
55
+ * accountId: 'alice@gmail.com',
56
+ * service: 'gmail'
57
+ * });
58
+ * // Returns: "alice@gmail.com:gmail:metadata"
59
+ *
60
+ * // Store DCR client registration info
61
+ * const dcrKey = createAccountKey('dcr-client', {
62
+ * accountId: 'alice@outlook.com',
63
+ * service: 'outlook'
64
+ * });
65
+ * // Returns: "alice@outlook.com:outlook:dcr-client"
66
+ * ```
67
+ */
68
+ export declare function createAccountKey(type: AccountKeyType, params: AccountKeyParams): string;
69
+ /**
70
+ * Create service-scoped storage key.
71
+ *
72
+ * These keys are scoped to a service (not a specific account) and store
73
+ * service-level data like which account is active or the list of linked accounts.
74
+ *
75
+ * @param type - Key type ('active' for active account, 'linked' for account list)
76
+ * @param params - Service key parameters
77
+ * @returns Storage key in format: "{service}:{type}"
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Track active account
82
+ * const activeKey = createServiceKey('active', {
83
+ * service: 'gmail'
84
+ * });
85
+ * // Returns: "gmail:active"
86
+ *
87
+ * // Store list of linked accounts
88
+ * const linkedKey = createServiceKey('linked', {
89
+ * service: 'gmail'
90
+ * });
91
+ * // Returns: "gmail:linked"
92
+ * ```
93
+ */
94
+ export declare function createServiceKey(type: ServiceKeyType, params: ServiceKeyParams): string;
95
+ /**
96
+ * Parse token key to extract components
97
+ *
98
+ * @param key - Storage key to parse
99
+ * @returns Object with accountId and service, or undefined if invalid format
100
+ *
101
+ * @example
102
+ * const parsed = parseTokenKey('user@gmail.com:gmail:token');
103
+ * // Returns: { accountId: 'user@gmail.com', service: 'gmail' }
104
+ *
105
+ * const invalid = parseTokenKey('invalid-key');
106
+ * // Returns: undefined
107
+ */
108
+ export declare function parseTokenKey(key: string): {
109
+ accountId: string;
110
+ service: string;
111
+ } | undefined;
112
+ /**
113
+ * List all account IDs for a service
114
+ *
115
+ * Iterates token keys and returns all accountIds that match the service.
116
+ * Encapsulates key format details for forward compatibility.
117
+ *
118
+ * @param store - Keyv store to iterate
119
+ * @param service - Service name
120
+ * @returns Array of account IDs (e.g., email addresses)
121
+ *
122
+ * @example
123
+ * const accounts = await listAccountIds(store, 'gmail');
124
+ * // Returns: ['alice@gmail.com', 'bob@gmail.com']
125
+ *
126
+ * @example
127
+ * // Empty array if no accounts found
128
+ * const empty = await listAccountIds(store, 'unknown-service');
129
+ * // Returns: []
130
+ */
131
+ export declare function listAccountIds(store: Keyv, service: string): Promise<string[]>;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Key generation utilities for consistent storage key format
3
+ *
4
+ * Key format: {accountId}:{service}:{type}
5
+ * Example: work@gmail.com:gmail:token
6
+ */ /**
7
+ * Validate key parameters don't contain colon delimiter
8
+ */ function validateKeyParams(params) {
9
+ for (const [key, value] of Object.entries(params)){
10
+ if (typeof value !== 'string') {
11
+ throw new Error(`Key parameter '${key}' must be a string, got: ${typeof value}`);
12
+ }
13
+ if (value.includes(':')) {
14
+ throw new Error(`Key parameter '${key}' cannot contain colon character: ${value}`);
15
+ }
16
+ }
17
+ }
18
+ /**
19
+ * Create account-scoped storage key.
20
+ *
21
+ * These keys are scoped to a specific account (email address) and store
22
+ * account-specific data like OAuth tokens and account metadata.
23
+ *
24
+ * @param type - Key type ('token' for OAuth tokens, 'metadata' for account details, 'dcr-client' for DCR registration)
25
+ * @param params - Account key parameters with explicit field names
26
+ * @returns Storage key in format: "{accountId}:{service}:{type}"
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // Store OAuth token
31
+ * const tokenKey = createAccountKey('token', {
32
+ * accountId: 'alice@gmail.com',
33
+ * service: 'gmail'
34
+ * });
35
+ * // Returns: "alice@gmail.com:gmail:token"
36
+ *
37
+ * // Store account metadata (alias, timestamps, profile)
38
+ * const metadataKey = createAccountKey('metadata', {
39
+ * accountId: 'alice@gmail.com',
40
+ * service: 'gmail'
41
+ * });
42
+ * // Returns: "alice@gmail.com:gmail:metadata"
43
+ *
44
+ * // Store DCR client registration info
45
+ * const dcrKey = createAccountKey('dcr-client', {
46
+ * accountId: 'alice@outlook.com',
47
+ * service: 'outlook'
48
+ * });
49
+ * // Returns: "alice@outlook.com:outlook:dcr-client"
50
+ * ```
51
+ */ export function createAccountKey(type, params) {
52
+ validateKeyParams(params);
53
+ return `${params.accountId}:${params.service}:${type}`;
54
+ }
55
+ /**
56
+ * Create service-scoped storage key.
57
+ *
58
+ * These keys are scoped to a service (not a specific account) and store
59
+ * service-level data like which account is active or the list of linked accounts.
60
+ *
61
+ * @param type - Key type ('active' for active account, 'linked' for account list)
62
+ * @param params - Service key parameters
63
+ * @returns Storage key in format: "{service}:{type}"
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Track active account
68
+ * const activeKey = createServiceKey('active', {
69
+ * service: 'gmail'
70
+ * });
71
+ * // Returns: "gmail:active"
72
+ *
73
+ * // Store list of linked accounts
74
+ * const linkedKey = createServiceKey('linked', {
75
+ * service: 'gmail'
76
+ * });
77
+ * // Returns: "gmail:linked"
78
+ * ```
79
+ */ export function createServiceKey(type, params) {
80
+ validateKeyParams(params);
81
+ return `${params.service}:${type}`;
82
+ }
83
+ /**
84
+ * Parse token key to extract components
85
+ *
86
+ * @param key - Storage key to parse
87
+ * @returns Object with accountId and service, or undefined if invalid format
88
+ *
89
+ * @example
90
+ * const parsed = parseTokenKey('user@gmail.com:gmail:token');
91
+ * // Returns: { accountId: 'user@gmail.com', service: 'gmail' }
92
+ *
93
+ * const invalid = parseTokenKey('invalid-key');
94
+ * // Returns: undefined
95
+ */ export function parseTokenKey(key) {
96
+ const parts = key.split(':');
97
+ if (parts.length !== 3 || parts[2] !== 'token' || !parts[0] || !parts[1]) {
98
+ return undefined;
99
+ }
100
+ return {
101
+ accountId: parts[0],
102
+ service: parts[1]
103
+ };
104
+ }
105
+ /**
106
+ * List all account IDs for a service
107
+ *
108
+ * Iterates token keys and returns all accountIds that match the service.
109
+ * Encapsulates key format details for forward compatibility.
110
+ *
111
+ * @param store - Keyv store to iterate
112
+ * @param service - Service name
113
+ * @returns Array of account IDs (e.g., email addresses)
114
+ *
115
+ * @example
116
+ * const accounts = await listAccountIds(store, 'gmail');
117
+ * // Returns: ['alice@gmail.com', 'bob@gmail.com']
118
+ *
119
+ * @example
120
+ * // Empty array if no accounts found
121
+ * const empty = await listAccountIds(store, 'unknown-service');
122
+ * // Returns: []
123
+ */ export async function listAccountIds(store, service) {
124
+ const accountIds = [];
125
+ try {
126
+ var _store_iterator;
127
+ const iterator = (_store_iterator = store.iterator) === null || _store_iterator === void 0 ? void 0 : _store_iterator.call(store, undefined);
128
+ if (!iterator) {
129
+ return accountIds;
130
+ }
131
+ for await (const [key] of iterator){
132
+ const parsed = parseTokenKey(key);
133
+ if (parsed && parsed.service === service) {
134
+ accountIds.push(parsed.accountId);
135
+ }
136
+ }
137
+ } catch (_error) {
138
+ // If iteration fails, return empty array (fail gracefully)
139
+ // This handles stores that don't support iteration
140
+ return accountIds;
141
+ }
142
+ return accountIds;
143
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/key-utils.ts"],"sourcesContent":["/**\n * Key generation utilities for consistent storage key format\n *\n * Key format: {accountId}:{service}:{type}\n * Example: work@gmail.com:gmail:token\n */\n\nimport type { Keyv } from 'keyv';\n\n/**\n * Key types for account-scoped data (requires accountId)\n */\nexport type AccountKeyType = 'token' | 'metadata' | 'dcr-client';\n\n/**\n * Key types for service-scoped data (no accountId)\n */\nexport type ServiceKeyType = 'active' | 'linked';\n\n/**\n * Parameters for account-scoped keys.\n * All fields are required - no silent defaults.\n */\nexport interface AccountKeyParams {\n /** Account identifier - typically an email address */\n accountId: string;\n\n /** Service name (e.g., 'gmail', 'sheets', 'drive', 'outlook') */\n service: string;\n}\n\n/**\n * Parameters for service-scoped keys.\n * These keys don't include accountId.\n */\nexport interface ServiceKeyParams {\n /** Service name */\n service: string;\n}\n\n/**\n * Validate key parameters don't contain colon delimiter\n */\nfunction validateKeyParams(params: AccountKeyParams | ServiceKeyParams): void {\n for (const [key, value] of Object.entries(params)) {\n if (typeof value !== 'string') {\n throw new Error(`Key parameter '${key}' must be a string, got: ${typeof value}`);\n }\n if (value.includes(':')) {\n throw new Error(`Key parameter '${key}' cannot contain colon character: ${value}`);\n }\n }\n}\n\n/**\n * Create account-scoped storage key.\n *\n * These keys are scoped to a specific account (email address) and store\n * account-specific data like OAuth tokens and account metadata.\n *\n * @param type - Key type ('token' for OAuth tokens, 'metadata' for account details, 'dcr-client' for DCR registration)\n * @param params - Account key parameters with explicit field names\n * @returns Storage key in format: \"{accountId}:{service}:{type}\"\n *\n * @example\n * ```typescript\n * // Store OAuth token\n * const tokenKey = createAccountKey('token', {\n * accountId: 'alice@gmail.com',\n * service: 'gmail'\n * });\n * // Returns: \"alice@gmail.com:gmail:token\"\n *\n * // Store account metadata (alias, timestamps, profile)\n * const metadataKey = createAccountKey('metadata', {\n * accountId: 'alice@gmail.com',\n * service: 'gmail'\n * });\n * // Returns: \"alice@gmail.com:gmail:metadata\"\n *\n * // Store DCR client registration info\n * const dcrKey = createAccountKey('dcr-client', {\n * accountId: 'alice@outlook.com',\n * service: 'outlook'\n * });\n * // Returns: \"alice@outlook.com:outlook:dcr-client\"\n * ```\n */\nexport function createAccountKey(type: AccountKeyType, params: AccountKeyParams): string {\n validateKeyParams(params);\n return `${params.accountId}:${params.service}:${type}`;\n}\n\n/**\n * Create service-scoped storage key.\n *\n * These keys are scoped to a service (not a specific account) and store\n * service-level data like which account is active or the list of linked accounts.\n *\n * @param type - Key type ('active' for active account, 'linked' for account list)\n * @param params - Service key parameters\n * @returns Storage key in format: \"{service}:{type}\"\n *\n * @example\n * ```typescript\n * // Track active account\n * const activeKey = createServiceKey('active', {\n * service: 'gmail'\n * });\n * // Returns: \"gmail:active\"\n *\n * // Store list of linked accounts\n * const linkedKey = createServiceKey('linked', {\n * service: 'gmail'\n * });\n * // Returns: \"gmail:linked\"\n * ```\n */\nexport function createServiceKey(type: ServiceKeyType, params: ServiceKeyParams): string {\n validateKeyParams(params);\n return `${params.service}:${type}`;\n}\n\n/**\n * Parse token key to extract components\n *\n * @param key - Storage key to parse\n * @returns Object with accountId and service, or undefined if invalid format\n *\n * @example\n * const parsed = parseTokenKey('user@gmail.com:gmail:token');\n * // Returns: { accountId: 'user@gmail.com', service: 'gmail' }\n *\n * const invalid = parseTokenKey('invalid-key');\n * // Returns: undefined\n */\nexport function parseTokenKey(key: string): { accountId: string; service: string } | undefined {\n const parts = key.split(':');\n if (parts.length !== 3 || parts[2] !== 'token' || !parts[0] || !parts[1]) {\n return undefined;\n }\n return {\n accountId: parts[0],\n service: parts[1],\n };\n}\n\n/**\n * List all account IDs for a service\n *\n * Iterates token keys and returns all accountIds that match the service.\n * Encapsulates key format details for forward compatibility.\n *\n * @param store - Keyv store to iterate\n * @param service - Service name\n * @returns Array of account IDs (e.g., email addresses)\n *\n * @example\n * const accounts = await listAccountIds(store, 'gmail');\n * // Returns: ['alice@gmail.com', 'bob@gmail.com']\n *\n * @example\n * // Empty array if no accounts found\n * const empty = await listAccountIds(store, 'unknown-service');\n * // Returns: []\n */\nexport async function listAccountIds(store: Keyv, service: string): Promise<string[]> {\n const accountIds: string[] = [];\n\n try {\n const iterator = store.iterator?.(undefined);\n if (!iterator) {\n return accountIds;\n }\n\n for await (const [key] of iterator) {\n const parsed = parseTokenKey(key);\n if (parsed && parsed.service === service) {\n accountIds.push(parsed.accountId);\n }\n }\n } catch (_error) {\n // If iteration fails, return empty array (fail gracefully)\n // This handles stores that don't support iteration\n return accountIds;\n }\n\n return accountIds;\n}\n"],"names":["validateKeyParams","params","key","value","Object","entries","Error","includes","createAccountKey","type","accountId","service","createServiceKey","parseTokenKey","parts","split","length","undefined","listAccountIds","store","accountIds","iterator","parsed","push","_error"],"mappings":"AAAA;;;;;CAKC,GAmCD;;CAEC,GACD,SAASA,kBAAkBC,MAA2C;IACpE,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACJ,QAAS;QACjD,IAAI,OAAOE,UAAU,UAAU;YAC7B,MAAM,IAAIG,MAAM,CAAC,eAAe,EAAEJ,IAAI,yBAAyB,EAAE,OAAOC,OAAO;QACjF;QACA,IAAIA,MAAMI,QAAQ,CAAC,MAAM;YACvB,MAAM,IAAID,MAAM,CAAC,eAAe,EAAEJ,IAAI,kCAAkC,EAAEC,OAAO;QACnF;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCC,GACD,OAAO,SAASK,iBAAiBC,IAAoB,EAAER,MAAwB;IAC7ED,kBAAkBC;IAClB,OAAO,GAAGA,OAAOS,SAAS,CAAC,CAAC,EAAET,OAAOU,OAAO,CAAC,CAAC,EAAEF,MAAM;AACxD;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASG,iBAAiBH,IAAoB,EAAER,MAAwB;IAC7ED,kBAAkBC;IAClB,OAAO,GAAGA,OAAOU,OAAO,CAAC,CAAC,EAAEF,MAAM;AACpC;AAEA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASI,cAAcX,GAAW;IACvC,MAAMY,QAAQZ,IAAIa,KAAK,CAAC;IACxB,IAAID,MAAME,MAAM,KAAK,KAAKF,KAAK,CAAC,EAAE,KAAK,WAAW,CAACA,KAAK,CAAC,EAAE,IAAI,CAACA,KAAK,CAAC,EAAE,EAAE;QACxE,OAAOG;IACT;IACA,OAAO;QACLP,WAAWI,KAAK,CAAC,EAAE;QACnBH,SAASG,KAAK,CAAC,EAAE;IACnB;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,eAAeI,eAAeC,KAAW,EAAER,OAAe;IAC/D,MAAMS,aAAuB,EAAE;IAE/B,IAAI;YACeD;QAAjB,MAAME,YAAWF,kBAAAA,MAAME,QAAQ,cAAdF,sCAAAA,qBAAAA,OAAiBF;QAClC,IAAI,CAACI,UAAU;YACb,OAAOD;QACT;QAEA,WAAW,MAAM,CAAClB,IAAI,IAAImB,SAAU;YAClC,MAAMC,SAAST,cAAcX;YAC7B,IAAIoB,UAAUA,OAAOX,OAAO,KAAKA,SAAS;gBACxCS,WAAWG,IAAI,CAACD,OAAOZ,SAAS;YAClC;QACF;IACF,EAAE,OAAOc,QAAQ;QACf,2DAA2D;QAC3D,mDAAmD;QACnD,OAAOJ;IACT;IAEA,OAAOA;AACT"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Unified account management API for MCP servers.
3
+ *
4
+ * Provides two account management modes:
5
+ * - Loopback: Server-managed multi-account OAuth (LoopbackOAuthProvider)
6
+ * - Stateless: MCP client-managed OAuth (read-only status)
7
+ *
8
+ * @example
9
+ * // Loopback OAuth account management
10
+ * const {tools, prompts} = AccountServer.createLoopback({
11
+ * service: 'gmail',
12
+ * store: tokenStore,
13
+ * logger,
14
+ * auth: authProvider
15
+ * });
16
+ *
17
+ * @example
18
+ * // Stateless mode (MCP OAuth)
19
+ * const {tools, prompts} = AccountServer.createStateless({
20
+ * service: 'gmail'
21
+ * });
22
+ */
23
+ import { createLoopback } from './loopback.js';
24
+ import { createStateless } from './stateless.js';
25
+ export declare const AccountServer: {
26
+ /**
27
+ * Create loopback OAuth account management tools.
28
+ * Server manages multiple accounts with stored tokens (LoopbackOAuthProvider).
29
+ * Returns 4 tools: account-me, account-switch, account-remove, account-list.
30
+ * No prompts.
31
+ */
32
+ createLoopback: typeof createLoopback;
33
+ /**
34
+ * Create stateless mode tools.
35
+ * MCP client manages authentication. Server extracts user identity from bearer token.
36
+ * Returns 1 tool: account-me.
37
+ * No prompts.
38
+ */
39
+ createStateless: typeof createStateless;
40
+ };
41
+ export { createLoopback } from './loopback.js';
42
+ export { createAccountMe } from './me.js';
43
+ export { findAccountByEmailOrAlias } from './shared-utils.js';
44
+ export { createStateless } from './stateless.js';
45
+ export type { AccountLoopbackConfig, AccountMeConfig, AccountStatelessConfig } from './types.js';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Unified account management API for MCP servers.
3
+ *
4
+ * Provides two account management modes:
5
+ * - Loopback: Server-managed multi-account OAuth (LoopbackOAuthProvider)
6
+ * - Stateless: MCP client-managed OAuth (read-only status)
7
+ *
8
+ * @example
9
+ * // Loopback OAuth account management
10
+ * const {tools, prompts} = AccountServer.createLoopback({
11
+ * service: 'gmail',
12
+ * store: tokenStore,
13
+ * logger,
14
+ * auth: authProvider
15
+ * });
16
+ *
17
+ * @example
18
+ * // Stateless mode (MCP OAuth)
19
+ * const {tools, prompts} = AccountServer.createStateless({
20
+ * service: 'gmail'
21
+ * });
22
+ */ import { createLoopback } from './loopback.js';
23
+ import { createStateless } from './stateless.js';
24
+ export const AccountServer = {
25
+ /**
26
+ * Create loopback OAuth account management tools.
27
+ * Server manages multiple accounts with stored tokens (LoopbackOAuthProvider).
28
+ * Returns 4 tools: account-me, account-switch, account-remove, account-list.
29
+ * No prompts.
30
+ */ createLoopback,
31
+ /**
32
+ * Create stateless mode tools.
33
+ * MCP client manages authentication. Server extracts user identity from bearer token.
34
+ * Returns 1 tool: account-me.
35
+ * No prompts.
36
+ */ createStateless
37
+ };
38
+ export { createLoopback } from './loopback.js';
39
+ export { createAccountMe } from './me.js';
40
+ export { findAccountByEmailOrAlias } from './shared-utils.js';
41
+ export { createStateless } from './stateless.js';
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth/src/lib/account-server/index.ts"],"sourcesContent":["/**\n * Unified account management API for MCP servers.\n *\n * Provides two account management modes:\n * - Loopback: Server-managed multi-account OAuth (LoopbackOAuthProvider)\n * - Stateless: MCP client-managed OAuth (read-only status)\n *\n * @example\n * // Loopback OAuth account management\n * const {tools, prompts} = AccountServer.createLoopback({\n * service: 'gmail',\n * store: tokenStore,\n * logger,\n * auth: authProvider\n * });\n *\n * @example\n * // Stateless mode (MCP OAuth)\n * const {tools, prompts} = AccountServer.createStateless({\n * service: 'gmail'\n * });\n */\n\nimport { createLoopback } from './loopback.ts';\nimport { createStateless } from './stateless.ts';\n\nexport const AccountServer = {\n /**\n * Create loopback OAuth account management tools.\n * Server manages multiple accounts with stored tokens (LoopbackOAuthProvider).\n * Returns 4 tools: account-me, account-switch, account-remove, account-list.\n * No prompts.\n */\n createLoopback,\n\n /**\n * Create stateless mode tools.\n * MCP client manages authentication. Server extracts user identity from bearer token.\n * Returns 1 tool: account-me.\n * No prompts.\n */\n createStateless,\n};\n\nexport { createLoopback } from './loopback.ts';\nexport { createAccountMe } from './me.ts';\nexport { findAccountByEmailOrAlias } from './shared-utils.ts';\nexport { createStateless } from './stateless.ts';\nexport type { AccountLoopbackConfig, AccountMeConfig, AccountStatelessConfig } from './types.ts';\n"],"names":["createLoopback","createStateless","AccountServer","createAccountMe","findAccountByEmailOrAlias"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;CAqBC,GAED,SAASA,cAAc,QAAQ,gBAAgB;AAC/C,SAASC,eAAe,QAAQ,iBAAiB;AAEjD,OAAO,MAAMC,gBAAgB;IAC3B;;;;;GAKC,GACDF;IAEA;;;;;GAKC,GACDC;AACF,EAAE;AAEF,SAASD,cAAc,QAAQ,gBAAgB;AAC/C,SAASG,eAAe,QAAQ,UAAU;AAC1C,SAASC,yBAAyB,QAAQ,oBAAoB;AAC9D,SAASH,eAAe,QAAQ,iBAAiB"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Loopback OAuth account management tools.
3
+ *
4
+ * Provides account management for LoopbackOAuthProvider (server-managed multi-account).
5
+ * Users can add multiple accounts, switch between them, and manage identities.
6
+ *
7
+ * Tools:
8
+ * - account-me: Show current user identity (email, alias, session expiry)
9
+ * - account-switch: Use account (add if needed, switch if already linked)
10
+ * - account-remove: Remove account and delete tokens
11
+ * - account-list: Show all linked accounts (returns empty array if none)
12
+ */
13
+ import type { McpPrompt, McpTool } from '../../types.js';
14
+ import type { AccountLoopbackConfig } from './types.js';
15
+ /**
16
+ * Create loopback OAuth account management tools.
17
+ * Returns 4 tools: account-me, account-switch, account-remove, account-list.
18
+ */
19
+ export declare function createLoopback(config: AccountLoopbackConfig): {
20
+ tools: McpTool[];
21
+ prompts: McpPrompt[];
22
+ };