@sync.so/mcp-server 0.1.3 → 0.2.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 (261) hide show
  1. package/README.md +261 -86
  2. package/deploy/generation-status/app.html.gz +0 -0
  3. package/deploy/generation-status/release.json +6 -0
  4. package/dist/abort-signals.d.ts +6 -0
  5. package/dist/abort-signals.d.ts.map +1 -0
  6. package/dist/abort-signals.js +23 -0
  7. package/dist/account-features.d.ts +7 -0
  8. package/dist/account-features.d.ts.map +1 -0
  9. package/dist/account-features.js +48 -0
  10. package/dist/app-upload-relay.d.ts +13 -0
  11. package/dist/app-upload-relay.d.ts.map +1 -0
  12. package/dist/app-upload-relay.js +136 -0
  13. package/dist/asset-thumbnail.d.ts +3 -0
  14. package/dist/asset-thumbnail.d.ts.map +1 -0
  15. package/dist/asset-thumbnail.js +2 -0
  16. package/dist/auth/async-context.d.ts +5 -0
  17. package/dist/auth/async-context.d.ts.map +1 -0
  18. package/dist/auth/async-context.js +14 -0
  19. package/dist/auth/bearer-auth.d.ts +16 -0
  20. package/dist/auth/bearer-auth.d.ts.map +1 -0
  21. package/dist/auth/bearer-auth.js +55 -0
  22. package/dist/auth/client-credentials.d.ts +13 -0
  23. package/dist/auth/client-credentials.d.ts.map +1 -0
  24. package/dist/auth/client-credentials.js +67 -0
  25. package/dist/auth/device-auth.d.ts.map +1 -1
  26. package/dist/auth/device-auth.js +3 -2
  27. package/dist/auth/fixture-cleanup.d.ts +4 -0
  28. package/dist/auth/fixture-cleanup.d.ts.map +1 -0
  29. package/dist/auth/fixture-cleanup.js +9 -0
  30. package/dist/auth/index.d.ts +2 -0
  31. package/dist/auth/index.d.ts.map +1 -1
  32. package/dist/auth/index.js +2 -0
  33. package/dist/auth/oauth-diagnostics.d.ts +11 -0
  34. package/dist/auth/oauth-diagnostics.d.ts.map +1 -0
  35. package/dist/auth/oauth-diagnostics.js +78 -0
  36. package/dist/auth/oauth-provider.d.ts +16 -0
  37. package/dist/auth/oauth-provider.d.ts.map +1 -0
  38. package/dist/auth/oauth-provider.js +107 -0
  39. package/dist/auth/resource-metadata.d.ts +2 -0
  40. package/dist/auth/resource-metadata.d.ts.map +1 -0
  41. package/dist/auth/resource-metadata.js +3 -0
  42. package/dist/auth/token-verification.d.ts +11 -0
  43. package/dist/auth/token-verification.d.ts.map +1 -0
  44. package/dist/auth/token-verification.js +99 -0
  45. package/dist/auth/verification-cache.d.ts +8 -0
  46. package/dist/auth/verification-cache.d.ts.map +1 -0
  47. package/dist/auth/verification-cache.js +111 -0
  48. package/dist/chatgpt-analytics.d.ts +22 -0
  49. package/dist/chatgpt-analytics.d.ts.map +1 -0
  50. package/dist/chatgpt-analytics.js +172 -0
  51. package/dist/chatgpt-app.d.ts +15 -0
  52. package/dist/chatgpt-app.d.ts.map +1 -0
  53. package/dist/chatgpt-app.js +133 -0
  54. package/dist/chatgpt-release.d.ts +12 -0
  55. package/dist/chatgpt-release.d.ts.map +1 -0
  56. package/dist/chatgpt-release.js +30 -0
  57. package/dist/cli.js +30 -16
  58. package/dist/client-profile.d.ts +10 -0
  59. package/dist/client-profile.d.ts.map +1 -0
  60. package/dist/client-profile.js +45 -0
  61. package/dist/config.d.ts +17 -0
  62. package/dist/config.d.ts.map +1 -1
  63. package/dist/config.js +52 -0
  64. package/dist/generation-status-html.d.ts +2 -0
  65. package/dist/generation-status-html.d.ts.map +1 -0
  66. package/dist/generation-status-html.js +26 -0
  67. package/dist/generation-status.d.ts +6 -0
  68. package/dist/generation-status.d.ts.map +1 -0
  69. package/dist/generation-status.js +37 -0
  70. package/dist/http-client.d.ts +45 -1
  71. package/dist/http-client.d.ts.map +1 -1
  72. package/dist/http-client.js +171 -34
  73. package/dist/http-server.d.ts +53 -0
  74. package/dist/http-server.d.ts.map +1 -0
  75. package/dist/http-server.js +753 -0
  76. package/dist/http-session.d.ts +18 -0
  77. package/dist/http-session.d.ts.map +1 -0
  78. package/dist/http-session.js +189 -0
  79. package/dist/index.d.ts +4 -3
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +3 -2
  82. package/dist/oauth-response.d.ts +3 -0
  83. package/dist/oauth-response.d.ts.map +1 -0
  84. package/dist/oauth-response.js +30 -0
  85. package/dist/openapi/index.d.ts +2 -2
  86. package/dist/openapi/index.d.ts.map +1 -1
  87. package/dist/openapi/index.js +1 -1
  88. package/dist/openapi/parser.d.ts +2 -0
  89. package/dist/openapi/parser.d.ts.map +1 -1
  90. package/dist/openapi/parser.js +41 -1
  91. package/dist/openapi/types.d.ts +3 -0
  92. package/dist/openapi/types.d.ts.map +1 -1
  93. package/dist/organization-selection.d.ts +61 -0
  94. package/dist/organization-selection.d.ts.map +1 -0
  95. package/dist/organization-selection.js +142 -0
  96. package/dist/rate-limits.d.ts +13 -0
  97. package/dist/rate-limits.d.ts.map +1 -0
  98. package/dist/rate-limits.js +67 -0
  99. package/dist/revision.d.ts +6 -0
  100. package/dist/revision.d.ts.map +1 -0
  101. package/dist/revision.js +17 -0
  102. package/dist/runtime-diagnostics.d.ts +32 -0
  103. package/dist/runtime-diagnostics.d.ts.map +1 -0
  104. package/dist/runtime-diagnostics.js +58 -0
  105. package/dist/server.d.ts +23 -2
  106. package/dist/server.d.ts.map +1 -1
  107. package/dist/server.js +893 -32
  108. package/dist/session-registry.d.ts +54 -0
  109. package/dist/session-registry.d.ts.map +1 -0
  110. package/dist/session-registry.js +202 -0
  111. package/dist/session-telemetry.d.ts +13 -0
  112. package/dist/session-telemetry.d.ts.map +1 -0
  113. package/dist/session-telemetry.js +87 -0
  114. package/dist/spend-confirmation.d.ts +21 -0
  115. package/dist/spend-confirmation.d.ts.map +1 -0
  116. package/dist/spend-confirmation.js +105 -0
  117. package/dist/tool-analytics-context.d.ts +11 -0
  118. package/dist/tool-analytics-context.d.ts.map +1 -0
  119. package/dist/tool-analytics-context.js +8 -0
  120. package/dist/tool-names.d.ts +35 -0
  121. package/dist/tool-names.d.ts.map +1 -0
  122. package/dist/tool-names.js +133 -0
  123. package/dist/tools/account-context.d.ts +5 -0
  124. package/dist/tools/account-context.d.ts.map +1 -0
  125. package/dist/tools/account-context.js +131 -0
  126. package/dist/tools/account-management.d.ts +4 -0
  127. package/dist/tools/account-management.d.ts.map +1 -0
  128. package/dist/tools/account-management.js +111 -0
  129. package/dist/tools/app-tools.d.ts +31 -0
  130. package/dist/tools/app-tools.d.ts.map +1 -0
  131. package/dist/tools/app-tools.js +350 -0
  132. package/dist/tools/asset-analysis.d.ts +4 -0
  133. package/dist/tools/asset-analysis.d.ts.map +1 -0
  134. package/dist/tools/asset-analysis.js +172 -0
  135. package/dist/tools/asset-creators.d.ts +4 -0
  136. package/dist/tools/asset-creators.d.ts.map +1 -0
  137. package/dist/tools/asset-creators.js +38 -0
  138. package/dist/tools/audio-trim.d.ts +6 -0
  139. package/dist/tools/audio-trim.d.ts.map +1 -0
  140. package/dist/tools/audio-trim.js +79 -0
  141. package/dist/tools/batch-results.d.ts +16 -0
  142. package/dist/tools/batch-results.d.ts.map +1 -0
  143. package/dist/tools/batch-results.js +103 -0
  144. package/dist/tools/batches.d.ts +28 -0
  145. package/dist/tools/batches.d.ts.map +1 -0
  146. package/dist/tools/batches.js +293 -0
  147. package/dist/tools/dialogue.d.ts +6 -0
  148. package/dist/tools/dialogue.d.ts.map +1 -0
  149. package/dist/tools/dialogue.js +309 -0
  150. package/dist/tools/estimate-duration.d.ts +35 -0
  151. package/dist/tools/estimate-duration.d.ts.map +1 -0
  152. package/dist/tools/estimate-duration.js +66 -0
  153. package/dist/tools/feedback.d.ts +14 -0
  154. package/dist/tools/feedback.d.ts.map +1 -0
  155. package/dist/tools/feedback.js +72 -0
  156. package/dist/tools/generated-adapters.d.ts +25 -0
  157. package/dist/tools/generated-adapters.d.ts.map +1 -0
  158. package/dist/tools/generated-adapters.js +143 -0
  159. package/dist/tools/generation-context.d.ts +14 -0
  160. package/dist/tools/generation-context.d.ts.map +1 -0
  161. package/dist/tools/generation-context.js +39 -0
  162. package/dist/tools/generation-continuation.d.ts +4 -0
  163. package/dist/tools/generation-continuation.d.ts.map +1 -0
  164. package/dist/tools/generation-continuation.js +124 -0
  165. package/dist/tools/generation-costs.d.ts +6 -0
  166. package/dist/tools/generation-costs.d.ts.map +1 -0
  167. package/dist/tools/generation-costs.js +67 -0
  168. package/dist/tools/generation-output.d.ts +3 -0
  169. package/dist/tools/generation-output.d.ts.map +1 -0
  170. package/dist/tools/generation-output.js +39 -0
  171. package/dist/tools/generator.d.ts +35 -2
  172. package/dist/tools/generator.d.ts.map +1 -1
  173. package/dist/tools/generator.js +127 -9
  174. package/dist/tools/job-status.d.ts +35 -0
  175. package/dist/tools/job-status.d.ts.map +1 -0
  176. package/dist/tools/job-status.js +92 -0
  177. package/dist/tools/job-wait.d.ts +41 -0
  178. package/dist/tools/job-wait.d.ts.map +1 -0
  179. package/dist/tools/job-wait.js +84 -0
  180. package/dist/tools/jobs.d.ts +3 -0
  181. package/dist/tools/jobs.d.ts.map +1 -0
  182. package/dist/tools/jobs.js +210 -0
  183. package/dist/tools/media-upload.d.ts +27 -0
  184. package/dist/tools/media-upload.d.ts.map +1 -0
  185. package/dist/tools/media-upload.js +247 -0
  186. package/dist/tools/model-options.d.ts +36 -0
  187. package/dist/tools/model-options.d.ts.map +1 -0
  188. package/dist/tools/model-options.js +48 -0
  189. package/dist/tools/organization-choice.d.ts +9 -0
  190. package/dist/tools/organization-choice.d.ts.map +1 -0
  191. package/dist/tools/organization-choice.js +136 -0
  192. package/dist/tools/organizations.d.ts +56 -0
  193. package/dist/tools/organizations.d.ts.map +1 -0
  194. package/dist/tools/organizations.js +235 -0
  195. package/dist/tools/output-schemas.d.ts +54 -0
  196. package/dist/tools/output-schemas.d.ts.map +1 -0
  197. package/dist/tools/output-schemas.js +58 -0
  198. package/dist/tools/overrides.d.ts +3 -0
  199. package/dist/tools/overrides.d.ts.map +1 -1
  200. package/dist/tools/overrides.js +113 -15
  201. package/dist/tools/playback-metadata.d.ts +4 -0
  202. package/dist/tools/playback-metadata.d.ts.map +1 -0
  203. package/dist/tools/playback-metadata.js +51 -0
  204. package/dist/tools/project-destination.d.ts +13 -0
  205. package/dist/tools/project-destination.d.ts.map +1 -0
  206. package/dist/tools/project-destination.js +45 -0
  207. package/dist/tools/project-history.d.ts +6 -0
  208. package/dist/tools/project-history.d.ts.map +1 -0
  209. package/dist/tools/project-history.js +45 -0
  210. package/dist/tools/project-intent.d.ts +8 -0
  211. package/dist/tools/project-intent.d.ts.map +1 -0
  212. package/dist/tools/project-intent.js +87 -0
  213. package/dist/tools/running-submissions.d.ts +7 -0
  214. package/dist/tools/running-submissions.d.ts.map +1 -0
  215. package/dist/tools/running-submissions.js +91 -0
  216. package/dist/tools/speech.d.ts +6 -0
  217. package/dist/tools/speech.d.ts.map +1 -0
  218. package/dist/tools/speech.js +116 -0
  219. package/dist/tools/studio-draft.d.ts +7 -0
  220. package/dist/tools/studio-draft.d.ts.map +1 -0
  221. package/dist/tools/studio-draft.js +195 -0
  222. package/dist/tools/studio-view-contract.d.ts +184 -0
  223. package/dist/tools/studio-view-contract.d.ts.map +1 -0
  224. package/dist/tools/studio-view-contract.js +67 -0
  225. package/dist/tools/studio-view.d.ts +6 -0
  226. package/dist/tools/studio-view.d.ts.map +1 -0
  227. package/dist/tools/studio-view.js +126 -0
  228. package/dist/tools/timeline-lipsync.d.ts +316 -0
  229. package/dist/tools/timeline-lipsync.d.ts.map +1 -0
  230. package/dist/tools/timeline-lipsync.js +304 -0
  231. package/dist/tools/translation-preview.d.ts +8 -0
  232. package/dist/tools/translation-preview.d.ts.map +1 -0
  233. package/dist/tools/translation-preview.js +146 -0
  234. package/dist/tools/translation.d.ts +6 -0
  235. package/dist/tools/translation.d.ts.map +1 -0
  236. package/dist/tools/translation.js +131 -0
  237. package/dist/tools/upload-widget.d.ts +13 -0
  238. package/dist/tools/upload-widget.d.ts.map +1 -0
  239. package/dist/tools/upload-widget.js +788 -0
  240. package/dist/toolsets.d.ts +18 -0
  241. package/dist/toolsets.d.ts.map +1 -0
  242. package/dist/toolsets.js +157 -0
  243. package/dist/update-check.d.ts +38 -0
  244. package/dist/update-check.d.ts.map +1 -0
  245. package/dist/update-check.js +150 -0
  246. package/dist/upload-runtime.d.ts +45 -0
  247. package/dist/upload-runtime.d.ts.map +1 -0
  248. package/dist/upload-runtime.js +156 -0
  249. package/dist/upload-source.d.ts +4 -0
  250. package/dist/upload-source.d.ts.map +1 -0
  251. package/dist/upload-source.js +46 -0
  252. package/dist/version.d.ts +2 -0
  253. package/dist/version.d.ts.map +1 -0
  254. package/dist/version.js +3 -0
  255. package/package.json +79 -33
  256. package/dist/openapi/parser.test.d.ts +0 -2
  257. package/dist/openapi/parser.test.d.ts.map +0 -1
  258. package/dist/openapi/parser.test.js +0 -188
  259. package/dist/tools/generator.test.d.ts +0 -2
  260. package/dist/tools/generator.test.d.ts.map +0 -1
  261. package/dist/tools/generator.test.js +0 -178
@@ -0,0 +1,18 @@
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
+ import type { Request, Response } from 'express';
3
+ import { OrganizationSession } from './organization-selection.js';
4
+ export declare function isSessionControlRequest(body: unknown): boolean;
5
+ /** Only handshake metadata survives between requests; SDK graphs never do. */
6
+ export declare class HttpSession {
7
+ #private;
8
+ readonly clientName: string;
9
+ private readonly createServer;
10
+ private readonly reportProtocol;
11
+ private readonly reportError;
12
+ /** The organization chosen in this MCP session; per-request servers read it from context. */
13
+ readonly organization: OrganizationSession;
14
+ constructor(clientName: string, createServer: (clientName?: string, handshakeClientName?: string) => McpServer, reportProtocol: (version: string) => void, reportError?: (details: Record<string, unknown>) => void);
15
+ handleRequest(req: Request, res: Response, initialize?: boolean): Promise<void>;
16
+ close(): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=http-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-session.d.ts","sourceRoot":"","sources":["../src/http-session.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAA4B,MAAM,6BAA6B,CAAC;AAE5F,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAW9D;AAED,8EAA8E;AAC9E,qBAAa,WAAW;;IAWpB,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAN9B,6FAA6F;IAC7F,QAAQ,CAAC,YAAY,sBAA6B;IAClD,YACW,UAAU,EAAE,MAAM,EACV,YAAY,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,SAAS,EAC9E,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACzC,WAAW,GAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAe,EACjF;IAEE,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,UAAU,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAgJlF;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ3B;CACF"}
@@ -0,0 +1,189 @@
1
+ import { CancelledNotificationSchema, InitializedNotificationSchema, JSONRPCMessageSchema, JSONRPCNotificationSchema, } from '@modelcontextprotocol/core';
2
+ import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
3
+ import { SUPPORTED_PROTOCOL_VERSIONS } from '@modelcontextprotocol/server';
4
+ import { OrganizationSession, runInOrganizationSession } from './organization-selection.js';
5
+ export function isSessionControlRequest(body) {
6
+ const messages = Array.isArray(body) ? body : [body];
7
+ return (messages.length > 0 &&
8
+ messages.every((message) => JSONRPCNotificationSchema.safeParse(message).success &&
9
+ (InitializedNotificationSchema.safeParse(message).success ||
10
+ CancelledNotificationSchema.safeParse(message).success)));
11
+ }
12
+ /** Only handshake metadata survives between requests; SDK graphs never do. */
13
+ export class HttpSession {
14
+ clientName;
15
+ createServer;
16
+ reportProtocol;
17
+ reportError;
18
+ #active = new Map();
19
+ #running = new Map();
20
+ #initialized = false;
21
+ #closed = false;
22
+ /** The organization chosen in this MCP session; per-request servers read it from context. */
23
+ organization = new OrganizationSession();
24
+ constructor(clientName, createServer, reportProtocol, reportError = () => { }) {
25
+ this.clientName = clientName;
26
+ this.createServer = createServer;
27
+ this.reportProtocol = reportProtocol;
28
+ this.reportError = reportError;
29
+ }
30
+ async handleRequest(req, res, initialize = false) {
31
+ if (this.#closed) {
32
+ res.status(404).json({ error: 'Session not found' });
33
+ return;
34
+ }
35
+ const body = req.body;
36
+ if (req.method !== 'POST') {
37
+ res.setHeader('Allow', 'POST, DELETE');
38
+ res.status(405).end();
39
+ return;
40
+ }
41
+ const protocol = req.headers['mcp-protocol-version'];
42
+ if (protocol !== undefined &&
43
+ (typeof protocol !== 'string' || !SUPPORTED_PROTOCOL_VERSIONS.includes(protocol))) {
44
+ res.status(400).json({ error: 'Unsupported protocol version' });
45
+ return;
46
+ }
47
+ const accept = req.headers.accept;
48
+ if (!accept?.includes('application/json') || !accept.includes('text/event-stream')) {
49
+ res.status(406).end();
50
+ return;
51
+ }
52
+ const notification = JSONRPCNotificationSchema.safeParse(body).success;
53
+ // No per-request server is needed for these lifecycle notifications.
54
+ if (notification && InitializedNotificationSchema.safeParse(body).success) {
55
+ this.#initialized = true;
56
+ res.status(202).end();
57
+ return;
58
+ }
59
+ const cancelled = CancelledNotificationSchema.safeParse(body);
60
+ if (notification && cancelled.success && cancelled.data.params.requestId !== undefined) {
61
+ const target = this.#active.get(cancelled.data.params.requestId);
62
+ await target?.cancel(body);
63
+ res.status(202).end();
64
+ return;
65
+ }
66
+ const messages = Array.isArray(body) ? body : [body];
67
+ const parsed = messages.map((value) => JSONRPCMessageSchema.safeParse(value));
68
+ if (parsed.some((result) => !result.success)) {
69
+ res.status(400).json({ error: 'Invalid JSON-RPC message' });
70
+ return;
71
+ }
72
+ for (const result of parsed) {
73
+ if (!result.success)
74
+ continue;
75
+ const batchNotification = JSONRPCNotificationSchema.safeParse(result.data);
76
+ if (!batchNotification.success)
77
+ continue;
78
+ if (InitializedNotificationSchema.safeParse(batchNotification.data).success)
79
+ this.#initialized = true;
80
+ const batchCancelled = CancelledNotificationSchema.safeParse(batchNotification.data);
81
+ if (batchCancelled.success && batchCancelled.data.params.requestId !== undefined)
82
+ await this.#active
83
+ .get(batchCancelled.data.params.requestId)
84
+ ?.cancel(batchNotification.data);
85
+ }
86
+ if (isSessionControlRequest(body)) {
87
+ res.status(202).end();
88
+ return;
89
+ }
90
+ if (!initialize && !this.#initialized) {
91
+ res.status(400).json({ error: 'Complete MCP initialization before sending requests' });
92
+ return;
93
+ }
94
+ const ids = [];
95
+ for (const result of parsed) {
96
+ if (!result.success || !('method' in result.data))
97
+ continue;
98
+ if (!initialize && result.data.method === 'initialize') {
99
+ res.status(400).json({ error: 'Session is already initialized' });
100
+ return;
101
+ }
102
+ if ('id' in result.data)
103
+ ids.push(result.data.id);
104
+ }
105
+ if (new Set(ids).size !== ids.length || ids.some((id) => this.#active.has(id))) {
106
+ res.status(400).json({ error: 'Request ID is already in flight in this session' });
107
+ return;
108
+ }
109
+ const server = this.createServer(initialize ? undefined : this.clientName, this.clientName);
110
+ const transport = new NodeStreamableHTTPServerTransport({
111
+ sessionIdGenerator: undefined,
112
+ enableJsonResponse: true,
113
+ });
114
+ const completed = new Set();
115
+ const send = transport.send.bind(transport);
116
+ transport.send = async (reply, options) => {
117
+ if ('id' in reply && reply.id !== undefined && ('result' in reply || 'error' in reply))
118
+ completed.add(reply.id);
119
+ if ('error' in reply || ('result' in reply && reply.result.isError === true)) {
120
+ this.reportError({
121
+ requestId: res.locals.requestId,
122
+ category: 'error' in reply ? 'protocol' : 'tool',
123
+ ...('error' in reply ? { code: reply.error.code } : {}),
124
+ });
125
+ }
126
+ if (initialize && 'result' in reply && typeof reply.result.protocolVersion === 'string')
127
+ this.reportProtocol(reply.result.protocolVersion);
128
+ return send(reply, options);
129
+ };
130
+ this.#running.set(server, res);
131
+ if (!initialize)
132
+ for (const id of ids)
133
+ this.#active.set(id, {
134
+ cancel: async (cancelNotification) => {
135
+ if (completed.has(id))
136
+ return;
137
+ const cancelMessage = JSONRPCNotificationSchema.parse(cancelNotification);
138
+ transport.onmessage?.(cancelMessage);
139
+ if (ids.length === 1) {
140
+ res.destroy();
141
+ await server.close();
142
+ }
143
+ else {
144
+ // Legacy JSON batches need a terminal slot to deliver unrelated results.
145
+ await transport.send({
146
+ jsonrpc: '2.0',
147
+ id,
148
+ error: { code: -32800, message: 'Request cancelled' },
149
+ });
150
+ }
151
+ },
152
+ });
153
+ let disconnected;
154
+ const closedResponse = new Promise((resolve) => {
155
+ disconnected = resolve;
156
+ });
157
+ const onClose = () => {
158
+ if (!res.writableFinished)
159
+ disconnected();
160
+ };
161
+ res.once('close', onClose);
162
+ try {
163
+ await server.connect(transport);
164
+ if (req.aborted || res.destroyed)
165
+ return;
166
+ // A disconnected client must release the SDK graph and abort its handlers,
167
+ // even when no JSON response can finish.
168
+ await Promise.race([
169
+ runInOrganizationSession(this.organization, () => transport.handleRequest(req, res, req.body)),
170
+ closedResponse,
171
+ ]);
172
+ }
173
+ finally {
174
+ res.off('close', onClose);
175
+ if (!initialize)
176
+ for (const id of ids)
177
+ this.#active.delete(id);
178
+ this.#running.delete(server);
179
+ await server.close();
180
+ }
181
+ }
182
+ async close() {
183
+ this.#closed = true;
184
+ await Promise.allSettled([...this.#running].map(async ([server, response]) => {
185
+ response.destroy();
186
+ await server.close();
187
+ }));
188
+ }
189
+ }
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  export type { ApiKeyAuth, DeviceAuthToken } from './auth/index.js';
2
- export { clearToken, createApiKeyAuth, loadToken, performDeviceAuth, saveToken, } from './auth/index.js';
2
+ export { clearToken, createApiKeyAuth, createOAuthProvider, getAuthToken, loadToken, performDeviceAuth, runWithAuth, saveToken, } from './auth/index.js';
3
3
  export type { SyncMcpConfig } from './config.js';
4
4
  export { resolveConfig } from './config.js';
5
5
  export type { HttpClient } from './http-client.js';
6
6
  export { createHttpClient } from './http-client.js';
7
- export type { JsonSchema, OpenApiSpec, ParsedOperation, ParsedParameter, } from './openapi/index.js';
7
+ export { startHttpServer } from './http-server.js';
8
+ export type { JsonSchema, OpenApiSpec, ParsedOperation, ParsedParameter } from './openapi/index.js';
8
9
  export { fetchSpec, loadSpecFromFile, parseSpec } from './openapi/index.js';
9
- export { createSyncMcpServer } from './server.js';
10
+ export { createMcpServerFactory, createSyncMcpServer } from './server.js';
10
11
  export type { McpToolDefinition, ToolRegistry } from './tools/index.js';
11
12
  export { createToolRegistry, generateTools, getOverride, operationIdToToolName, } from './tools/index.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EACV,UAAU,EACV,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEpG,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,qBAAqB,GACtB,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export { clearToken, createApiKeyAuth, loadToken, performDeviceAuth, saveToken, } from './auth/index.js';
1
+ export { clearToken, createApiKeyAuth, createOAuthProvider, getAuthToken, loadToken, performDeviceAuth, runWithAuth, saveToken, } from './auth/index.js';
2
2
  export { resolveConfig } from './config.js';
3
3
  export { createHttpClient } from './http-client.js';
4
+ export { startHttpServer } from './http-server.js';
4
5
  export { fetchSpec, loadSpecFromFile, parseSpec } from './openapi/index.js';
5
- export { createSyncMcpServer } from './server.js';
6
+ export { createMcpServerFactory, createSyncMcpServer } from './server.js';
6
7
  export { createToolRegistry, generateTools, getOverride, operationIdToToolName, } from './tools/index.js';
@@ -0,0 +1,3 @@
1
+ /** Cancel the body reader explicitly, including when fetch's abort does not settle it. */
2
+ export declare function readOAuthResponseText(response: Response, signal: AbortSignal): Promise<string>;
3
+ //# sourceMappingURL=oauth-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-response.d.ts","sourceRoot":"","sources":["../src/oauth-response.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -0,0 +1,30 @@
1
+ /** Cancel the body reader explicitly, including when fetch's abort does not settle it. */
2
+ export async function readOAuthResponseText(response, signal) {
3
+ if (!response.body) {
4
+ signal.throwIfAborted();
5
+ return '';
6
+ }
7
+ const reader = response.body.getReader();
8
+ const cancel = () => {
9
+ void reader.cancel(signal.reason).catch(() => { });
10
+ };
11
+ signal.addEventListener('abort', cancel, { once: true });
12
+ if (signal.aborted)
13
+ cancel();
14
+ const decoder = new TextDecoder();
15
+ let text = '';
16
+ try {
17
+ while (true) {
18
+ signal.throwIfAborted();
19
+ const { done, value } = await reader.read();
20
+ signal.throwIfAborted();
21
+ if (done)
22
+ return text + decoder.decode();
23
+ text += decoder.decode(value, { stream: true });
24
+ }
25
+ }
26
+ finally {
27
+ signal.removeEventListener('abort', cancel);
28
+ reader.releaseLock();
29
+ }
30
+ }
@@ -1,4 +1,4 @@
1
1
  export { fetchSpec, loadSpecFromFile } from './fetcher.js';
2
- export { parseSpec, resolveRef, resolveSchemaRefs } from './parser.js';
3
- export type { JsonSchema, OpenApiSpec, ParsedOperation, ParsedParameter, } from './types.js';
2
+ export { parseSpec } from './parser.js';
3
+ export type { JsonSchema, OpenApiSpec, ParsedOperation, ParsedParameter } from './types.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/openapi/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EACV,UAAU,EACV,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/openapi/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
@@ -1,2 +1,2 @@
1
1
  export { fetchSpec, loadSpecFromFile } from './fetcher.js';
2
- export { parseSpec, resolveRef, resolveSchemaRefs } from './parser.js';
2
+ export { parseSpec } from './parser.js';
@@ -2,4 +2,6 @@ import type { JsonSchema, OpenApiSpec, ParsedOperation } from './types.js';
2
2
  export declare function parseSpec(spec: OpenApiSpec): ParsedOperation[];
3
3
  export declare function resolveRef(spec: OpenApiSpec, ref: string): JsonSchema;
4
4
  export declare function resolveSchemaRefs(schema: JsonSchema, spec: OpenApiSpec): JsonSchema;
5
+ /** Read a property through the DTO wrappers emitted by the API. */
6
+ export declare function schemaProperty(schema: JsonSchema | undefined, name: string): JsonSchema | undefined;
5
7
  //# sourceMappingURL=parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/openapi/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAI5F,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,CAiC9D;AAgDD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAUrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CA2BnF"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/openapi/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAqB5F,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,CAiD9D;AAgDD,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAUrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CA2BnF;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,IAAI,EAAE,MAAM,GACX,UAAU,GAAG,SAAS,CAQxB"}
@@ -1,4 +1,21 @@
1
+ import { FEEDBACK_OPERATION_ID } from '../tools/feedback.js';
1
2
  const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete'];
3
+ /** Only these tags produce MCP tools — everything else is filtered out. */
4
+ const ALLOWED_TAGS = new Set([
5
+ 'generate',
6
+ 'generations',
7
+ 'models',
8
+ 'assets',
9
+ 'projects',
10
+ 'voices',
11
+ 'text-to-speech',
12
+ 'transcriptions',
13
+ 'dialogue edits',
14
+ 'dubbing',
15
+ 'audio trims',
16
+ // Read by the curated batches_* tools; createTools generates none from it.
17
+ 'batch',
18
+ ]);
2
19
  export function parseSpec(spec) {
3
20
  const operations = [];
4
21
  for (const [path, pathItem] of Object.entries(spec.paths)) {
@@ -7,7 +24,15 @@ export function parseSpec(spec) {
7
24
  if (!operation)
8
25
  continue;
9
26
  const tags = operation.tags ?? [];
10
- if (tags.includes('internal'))
27
+ const isChatgptCollector = path === '/v2/analytics/chatgpt-events' &&
28
+ method === 'post' &&
29
+ operation.operationId === 'analytics_captureChatgptEvent';
30
+ const isFeedbackCollector = path === '/v2/feedback' &&
31
+ method === 'post' &&
32
+ operation.operationId === FEEDBACK_OPERATION_ID;
33
+ if (!isChatgptCollector &&
34
+ !isFeedbackCollector &&
35
+ !tags.some((t) => ALLOWED_TAGS.has(t.toLowerCase())))
11
36
  continue;
12
37
  const operationId = operation.operationId;
13
38
  if (!operationId)
@@ -25,6 +50,9 @@ export function parseSpec(spec) {
25
50
  parameters,
26
51
  requestBody,
27
52
  isMultipart,
53
+ ...(operation['x-sync-studio-drafts'] === 1 ? { studioDraftVersion: 1 } : {}),
54
+ ...(operation['x-sync-studio-views'] === 1 ? { studioViewVersion: 1 } : {}),
55
+ ...(operation['x-sync-saved-asset-dubbing'] === 1 ? { savedAssetDubbingVersion: 1 } : {}),
28
56
  });
29
57
  }
30
58
  }
@@ -99,3 +127,15 @@ export function resolveSchemaRefs(schema, spec) {
99
127
  }
100
128
  return result;
101
129
  }
130
+ /** Read a property through the DTO wrappers emitted by the API. */
131
+ export function schemaProperty(schema, name) {
132
+ const properties = schema?.properties;
133
+ if (properties?.[name])
134
+ return properties[name];
135
+ for (const member of schema?.allOf ?? []) {
136
+ const found = schemaProperty(member, name);
137
+ if (found)
138
+ return found;
139
+ }
140
+ return undefined;
141
+ }
@@ -18,6 +18,9 @@ export type ParsedOperation = {
18
18
  contentType: string;
19
19
  };
20
20
  isMultipart: boolean;
21
+ savedAssetDubbingVersion?: number;
22
+ studioDraftVersion?: number;
23
+ studioViewVersion?: number;
21
24
  };
22
25
  export type JsonSchema = Record<string, unknown>;
23
26
  export type OpenApiSpec = {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/openapi/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACxD,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,UAAU,CAAA;SAAE,CAAC,CAAC;QAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/openapi/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,WAAW,EAAE,OAAO,CAAC;IACrB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACxD,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,UAAU,CAAA;SAAE,CAAC,CAAC;QAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Per-request organization choice for session and OAuth credentials (sync-api-v2,
3
+ * CRAFT-7789). API keys are bound to their organization and never send it.
4
+ */
5
+ export declare const ORGANIZATION_HEADER = "x-sync-organization-id";
6
+ export type OrganizationSummary = {
7
+ id: string;
8
+ name: string | null;
9
+ };
10
+ /** Where a call ran: a named organization, or null for the credential's own, sent without a header. */
11
+ export type RanIn = OrganizationSummary | null;
12
+ /** Taken before an account read starts; recordFeatures refuses its answer after a switch. */
13
+ export type FeatureStamp = Readonly<{
14
+ generation: number;
15
+ }>;
16
+ /**
17
+ * One MCP session's organization: the stdio process, or one hosted session. Nothing here
18
+ * grants access; the API checks membership on every request that names an organization.
19
+ */
20
+ export declare class OrganizationSession {
21
+ #private;
22
+ /** Chosen with organizations_select; every later call in this session sends it. */
23
+ get selected(): OrganizationSummary | undefined;
24
+ /** Switches the session, with the features read for `organization` (undefined: unknown). */
25
+ select(organization: OrganizationSummary, features: readonly string[] | undefined): void;
26
+ featureStamp(): FeatureStamp;
27
+ /** The organization calls in this session run in, when known: the selection, else the default. */
28
+ activeOrganizationId(): string | undefined;
29
+ /**
30
+ * Keeps features GET /v2/account answered for `organizationId`, unless the session switched
31
+ * since `stamp` was taken or the answer is for another organization than the active one.
32
+ * Undefined features clear what was kept. Returns whether the write was kept.
33
+ */
34
+ recordFeatures(features: readonly string[] | undefined, stamp: FeatureStamp, organizationId: string | undefined): boolean;
35
+ /**
36
+ * The active organization's features, when they were read for it in the current selection;
37
+ * undefined means unknown, so read them again.
38
+ */
39
+ activeFeatures(): readonly string[] | undefined;
40
+ /** The credential's own organization, as last read without a selection. */
41
+ defaultOrganization(): OrganizationSummary | undefined;
42
+ rememberDefault(organization: OrganizationSummary): void;
43
+ /** Whether calls in this session now run where `organization` is. */
44
+ isCurrent(organization: RanIn): boolean;
45
+ /** The credential's own organization id, if it was ever read. */
46
+ defaultId(): string | undefined;
47
+ /** Records ids returned by a call that ran in `organization`. */
48
+ rememberIds(ids: string[], organization: RanIn): void;
49
+ /** Where a remembered id in these arguments was created; undefined when none is. */
50
+ organizationFor(args: Record<string, unknown>): RanIn | undefined;
51
+ }
52
+ export declare function runInOrganizationSession<T>(session: OrganizationSession, fn: () => T): T;
53
+ export declare function currentOrganizationSession(): OrganizationSession | undefined;
54
+ /**
55
+ * Runs `fn` in `organization`, or in the credential's default when undefined, whatever the
56
+ * session selects meanwhile. The session's selection is not changed.
57
+ */
58
+ export declare function runInOrganization<T>(organization: OrganizationSummary | undefined, fn: () => T): T;
59
+ /** The organization this call runs in when one was chosen: the call's pin, else the session. */
60
+ export declare function chosenOrganization(): OrganizationSummary | undefined;
61
+ //# sourceMappingURL=organization-selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organization-selection.d.ts","sourceRoot":"","sources":["../src/organization-selection.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,2BAA2B,CAAC;AAE5D,MAAM,MAAM,mBAAmB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEtE,uGAAuG;AACvG,MAAM,MAAM,KAAK,GAAG,mBAAmB,GAAG,IAAI,CAAC;AAM/C,6FAA6F;AAC7F,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE5D;;;GAGG;AACH,qBAAa,mBAAmB;;IAW9B,mFAAmF;IACnF,IAAI,QAAQ,IAAI,mBAAmB,GAAG,SAAS,CAE9C;IAED,4FAA4F;IAC5F,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,CAMvF;IAED,YAAY,IAAI,YAAY,CAE3B;IAED,kGAAkG;IAClG,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAEzC;IAED;;;;OAIG;IACH,cAAc,CACZ,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EACvC,KAAK,EAAE,YAAY,EACnB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAYT;IAED;;;OAGG;IACH,cAAc,IAAI,SAAS,MAAM,EAAE,GAAG,SAAS,CAM9C;IAED,2EAA2E;IAC3E,mBAAmB,IAAI,mBAAmB,GAAG,SAAS,CAIrD;IAED,eAAe,CAAC,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAGvD;IAED,qEAAqE;IACrE,SAAS,CAAC,YAAY,EAAE,KAAK,GAAG,OAAO,CAMtC;IAED,iEAAiE;IACjE,SAAS,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,iEAAiE;IACjE,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,KAAK,GAAG,IAAI,CAUpD;IAED,oFAAoF;IACpF,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,GAAG,SAAS,CAOhE;CACF;AAMD,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAExF;AAED,wBAAgB,0BAA0B,IAAI,mBAAmB,GAAG,SAAS,CAE5E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,YAAY,EAAE,mBAAmB,GAAG,SAAS,EAC7C,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAEH;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,IAAI,mBAAmB,GAAG,SAAS,CAGpE"}
@@ -0,0 +1,142 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ /**
3
+ * Per-request organization choice for session and OAuth credentials (sync-api-v2,
4
+ * CRAFT-7789). API keys are bound to their organization and never send it.
5
+ */
6
+ export const ORGANIZATION_HEADER = 'x-sync-organization-id';
7
+ // Ids a call in another organization returned, so its follow-up reads stay there.
8
+ const REMEMBERED_IDS_LIMIT = 500;
9
+ const DEFAULT_ORGANIZATION_TTL_MS = 60_000;
10
+ /**
11
+ * One MCP session's organization: the stdio process, or one hosted session. Nothing here
12
+ * grants access; the API checks membership on every request that names an organization.
13
+ */
14
+ export class OrganizationSession {
15
+ #selected;
16
+ // Bumped by every select(), so an account read that began before a switch can't write after it.
17
+ #generation = 0;
18
+ // Feature flags from GET /v2/account, stamped with the organization and selection they belong to.
19
+ #features;
20
+ #default;
21
+ // Kept after the cache expires: tells the credential's own organization from others.
22
+ #defaultId;
23
+ #organizationById = new Map();
24
+ /** Chosen with organizations_select; every later call in this session sends it. */
25
+ get selected() {
26
+ return this.#selected;
27
+ }
28
+ /** Switches the session, with the features read for `organization` (undefined: unknown). */
29
+ select(organization, features) {
30
+ this.#selected = organization;
31
+ this.#generation += 1;
32
+ this.#features = features
33
+ ? { organizationId: organization.id, generation: this.#generation, names: [...features] }
34
+ : undefined;
35
+ }
36
+ featureStamp() {
37
+ return { generation: this.#generation };
38
+ }
39
+ /** The organization calls in this session run in, when known: the selection, else the default. */
40
+ activeOrganizationId() {
41
+ return this.#selected?.id ?? this.#defaultId;
42
+ }
43
+ /**
44
+ * Keeps features GET /v2/account answered for `organizationId`, unless the session switched
45
+ * since `stamp` was taken or the answer is for another organization than the active one.
46
+ * Undefined features clear what was kept. Returns whether the write was kept.
47
+ */
48
+ recordFeatures(features, stamp, organizationId) {
49
+ if (stamp.generation !== this.#generation)
50
+ return false;
51
+ const active = this.activeOrganizationId();
52
+ if (organizationId && active && organizationId !== active)
53
+ return false;
54
+ this.#features = features
55
+ ? {
56
+ organizationId: organizationId ?? active,
57
+ generation: this.#generation,
58
+ names: [...features],
59
+ }
60
+ : undefined;
61
+ return true;
62
+ }
63
+ /**
64
+ * The active organization's features, when they were read for it in the current selection;
65
+ * undefined means unknown, so read them again.
66
+ */
67
+ activeFeatures() {
68
+ const kept = this.#features;
69
+ if (!kept || kept.generation !== this.#generation)
70
+ return undefined;
71
+ const active = this.activeOrganizationId();
72
+ if (kept.organizationId && active && kept.organizationId !== active)
73
+ return undefined;
74
+ return kept.names;
75
+ }
76
+ /** The credential's own organization, as last read without a selection. */
77
+ defaultOrganization() {
78
+ return this.#default && this.#default.expiresAt > Date.now()
79
+ ? this.#default.organization
80
+ : undefined;
81
+ }
82
+ rememberDefault(organization) {
83
+ this.#default = { organization, expiresAt: Date.now() + DEFAULT_ORGANIZATION_TTL_MS };
84
+ this.#defaultId = organization.id;
85
+ }
86
+ /** Whether calls in this session now run where `organization` is. */
87
+ isCurrent(organization) {
88
+ // Compare by id where one is known: selecting the default organization is no switch.
89
+ const current = this.selected?.id ?? this.#defaultId;
90
+ if (organization === null)
91
+ return this.selected === undefined || this.selected.id === this.#defaultId;
92
+ return current === organization.id;
93
+ }
94
+ /** The credential's own organization id, if it was ever read. */
95
+ defaultId() {
96
+ return this.#defaultId;
97
+ }
98
+ /** Records ids returned by a call that ran in `organization`. */
99
+ rememberIds(ids, organization) {
100
+ for (const id of ids) {
101
+ this.#organizationById.delete(id);
102
+ this.#organizationById.set(id, organization);
103
+ }
104
+ while (this.#organizationById.size > REMEMBERED_IDS_LIMIT) {
105
+ const oldest = this.#organizationById.keys().next().value;
106
+ if (oldest === undefined)
107
+ break;
108
+ this.#organizationById.delete(oldest);
109
+ }
110
+ }
111
+ /** Where a remembered id in these arguments was created; undefined when none is. */
112
+ organizationFor(args) {
113
+ if (this.#organizationById.size === 0)
114
+ return undefined;
115
+ for (const value of Object.values(args)) {
116
+ if (typeof value === 'string' && this.#organizationById.has(value))
117
+ return this.#organizationById.get(value);
118
+ }
119
+ return undefined;
120
+ }
121
+ }
122
+ const sessionStore = new AsyncLocalStorage();
123
+ // A call's organization, fixed when it starts; undefined pins the credential's default.
124
+ const callStore = new AsyncLocalStorage();
125
+ export function runInOrganizationSession(session, fn) {
126
+ return sessionStore.run(session, fn);
127
+ }
128
+ export function currentOrganizationSession() {
129
+ return sessionStore.getStore();
130
+ }
131
+ /**
132
+ * Runs `fn` in `organization`, or in the credential's default when undefined, whatever the
133
+ * session selects meanwhile. The session's selection is not changed.
134
+ */
135
+ export function runInOrganization(organization, fn) {
136
+ return callStore.run({ organization }, fn);
137
+ }
138
+ /** The organization this call runs in when one was chosen: the call's pin, else the session. */
139
+ export function chosenOrganization() {
140
+ const pinned = callStore.getStore();
141
+ return pinned ? pinned.organization : sessionStore.getStore()?.selected;
142
+ }
@@ -0,0 +1,13 @@
1
+ /** Before bearer authentication: rejected credentials per address. */
2
+ export declare function createMcpRejectedCredentialLimit(): import("express-rate-limit").RateLimitRequestHandler;
3
+ /** After bearer authentication: requests per verified user, OAuth client and organization. */
4
+ export declare function createMcpUserLimit(): import("express-rate-limit").RateLimitRequestHandler;
5
+ /** Token and revocation requests the API rejected, per address. */
6
+ export declare function createOAuthRejectedRequestLimit(): import("express-rate-limit").RateLimitRequestHandler;
7
+ /**
8
+ * Accepted token and revocation requests per OAuth client. Client IDs are not secret,
9
+ * so requests the API rejects never spend a client's allowance; they count against
10
+ * the sender's address instead.
11
+ */
12
+ export declare function createOAuthClientLimit(): import("express-rate-limit").RateLimitRequestHandler;
13
+ //# sourceMappingURL=rate-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../src/rate-limits.ts"],"names":[],"mappings":"AA4BA,sEAAsE;AACtE,wBAAgB,gCAAgC,yDAE/C;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,yDAWjC;AAED,mEAAmE;AACnE,wBAAgB,+BAA+B,yDAE9C;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,yDAmBrC"}