@mcp-ts/sdk 1.3.6 → 1.3.9

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 (103) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +398 -404
  3. package/dist/adapters/agui-adapter.d.mts +1 -1
  4. package/dist/adapters/agui-adapter.d.ts +1 -1
  5. package/dist/adapters/agui-adapter.js +2 -2
  6. package/dist/adapters/agui-adapter.js.map +1 -1
  7. package/dist/adapters/agui-adapter.mjs +2 -2
  8. package/dist/adapters/agui-adapter.mjs.map +1 -1
  9. package/dist/adapters/agui-middleware.d.mts +1 -1
  10. package/dist/adapters/agui-middleware.d.ts +1 -1
  11. package/dist/adapters/agui-middleware.js.map +1 -1
  12. package/dist/adapters/agui-middleware.mjs.map +1 -1
  13. package/dist/adapters/ai-adapter.d.mts +1 -1
  14. package/dist/adapters/ai-adapter.d.ts +1 -1
  15. package/dist/adapters/ai-adapter.js +1 -1
  16. package/dist/adapters/ai-adapter.js.map +1 -1
  17. package/dist/adapters/ai-adapter.mjs +1 -1
  18. package/dist/adapters/ai-adapter.mjs.map +1 -1
  19. package/dist/adapters/langchain-adapter.d.mts +1 -1
  20. package/dist/adapters/langchain-adapter.d.ts +1 -1
  21. package/dist/adapters/langchain-adapter.js +1 -1
  22. package/dist/adapters/langchain-adapter.js.map +1 -1
  23. package/dist/adapters/langchain-adapter.mjs +1 -1
  24. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  25. package/dist/adapters/mastra-adapter.d.mts +1 -1
  26. package/dist/adapters/mastra-adapter.d.ts +1 -1
  27. package/dist/adapters/mastra-adapter.js +1 -1
  28. package/dist/adapters/mastra-adapter.js.map +1 -1
  29. package/dist/adapters/mastra-adapter.mjs +1 -1
  30. package/dist/adapters/mastra-adapter.mjs.map +1 -1
  31. package/dist/bin/mcp-ts.js +0 -0
  32. package/dist/bin/mcp-ts.js.map +1 -1
  33. package/dist/bin/mcp-ts.mjs +0 -0
  34. package/dist/bin/mcp-ts.mjs.map +1 -1
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/client/index.mjs.map +1 -1
  37. package/dist/client/react.d.mts +2 -2
  38. package/dist/client/react.d.ts +2 -2
  39. package/dist/client/react.js +25 -2
  40. package/dist/client/react.js.map +1 -1
  41. package/dist/client/react.mjs +26 -3
  42. package/dist/client/react.mjs.map +1 -1
  43. package/dist/client/vue.js.map +1 -1
  44. package/dist/client/vue.mjs.map +1 -1
  45. package/dist/index.d.mts +1 -1
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +134 -71
  48. package/dist/index.js.map +1 -1
  49. package/dist/index.mjs +134 -71
  50. package/dist/index.mjs.map +1 -1
  51. package/dist/{multi-session-client-BYLarghq.d.ts → multi-session-client-CHE8QpVE.d.ts} +75 -5
  52. package/dist/{multi-session-client-CzhMkE0k.d.mts → multi-session-client-CQsRbxYI.d.mts} +75 -5
  53. package/dist/server/index.d.mts +1 -1
  54. package/dist/server/index.d.ts +1 -1
  55. package/dist/server/index.js +134 -71
  56. package/dist/server/index.js.map +1 -1
  57. package/dist/server/index.mjs +134 -71
  58. package/dist/server/index.mjs.map +1 -1
  59. package/dist/shared/index.js +10 -2
  60. package/dist/shared/index.js.map +1 -1
  61. package/dist/shared/index.mjs +10 -2
  62. package/dist/shared/index.mjs.map +1 -1
  63. package/package.json +185 -185
  64. package/src/adapters/agui-adapter.ts +222 -222
  65. package/src/adapters/agui-middleware.ts +382 -382
  66. package/src/adapters/ai-adapter.ts +115 -115
  67. package/src/adapters/langchain-adapter.ts +127 -127
  68. package/src/adapters/mastra-adapter.ts +126 -126
  69. package/src/bin/mcp-ts.ts +102 -102
  70. package/src/client/core/app-host.ts +417 -417
  71. package/src/client/core/sse-client.ts +371 -371
  72. package/src/client/core/types.ts +31 -31
  73. package/src/client/index.ts +27 -27
  74. package/src/client/react/index.ts +16 -16
  75. package/src/client/react/use-app-host.ts +73 -73
  76. package/src/client/react/use-mcp-apps.tsx +247 -214
  77. package/src/client/react/use-mcp.ts +641 -641
  78. package/src/client/vue/index.ts +10 -10
  79. package/src/client/vue/use-mcp.ts +617 -617
  80. package/src/index.ts +11 -11
  81. package/src/server/handlers/nextjs-handler.ts +204 -204
  82. package/src/server/handlers/sse-handler.ts +631 -631
  83. package/src/server/index.ts +57 -57
  84. package/src/server/mcp/multi-session-client.ts +228 -132
  85. package/src/server/mcp/oauth-client.ts +1188 -1188
  86. package/src/server/mcp/storage-oauth-provider.ts +272 -272
  87. package/src/server/storage/file-backend.ts +157 -170
  88. package/src/server/storage/index.ts +176 -175
  89. package/src/server/storage/memory-backend.ts +123 -136
  90. package/src/server/storage/redis-backend.ts +276 -289
  91. package/src/server/storage/redis.ts +160 -160
  92. package/src/server/storage/sqlite-backend.ts +182 -186
  93. package/src/server/storage/supabase-backend.ts +228 -227
  94. package/src/server/storage/types.ts +116 -116
  95. package/src/shared/constants.ts +29 -29
  96. package/src/shared/errors.ts +133 -133
  97. package/src/shared/event-routing.ts +28 -28
  98. package/src/shared/events.ts +180 -180
  99. package/src/shared/index.ts +75 -75
  100. package/src/shared/tool-utils.ts +61 -61
  101. package/src/shared/types.ts +282 -282
  102. package/src/shared/utils.ts +38 -16
  103. package/supabase/migrations/20260330195700_install_mcp_sessions.sql +84 -84
@@ -1,227 +1,228 @@
1
- import type { SupabaseClient } from '@supabase/supabase-js';
2
- import { StorageBackend, SessionData } from './types.js';
3
- import { SESSION_TTL_SECONDS } from '../../shared/constants.js';
4
-
5
- export class SupabaseStorageBackend implements StorageBackend {
6
- private readonly DEFAULT_TTL = SESSION_TTL_SECONDS;
7
-
8
- constructor(private supabase: SupabaseClient) {}
9
-
10
- async init(): Promise<void> {
11
- // Validate that the table exists
12
- const { error } = await this.supabase
13
- .from('mcp_sessions')
14
- .select('session_id')
15
- .limit(0);
16
-
17
- if (error) {
18
- // Postgres error code 42P01 is "relation does not exist"
19
- if (error.code === '42P01') {
20
- throw new Error(
21
- '[SupabaseStorage] Table "mcp_sessions" not found in your database. ' +
22
- 'Please run "npx mcp-ts supabase-init" in your project to set up the required table and RLS policies.'
23
- );
24
- }
25
- throw new Error(`[SupabaseStorage] Initialization check failed: ${error.message}`);
26
- }
27
-
28
- console.log('[mcp-ts][Storage] Supabase: ✓ "mcp_sessions" table verified.');
29
- }
30
-
31
- generateSessionId(): string {
32
- return crypto.randomUUID();
33
- }
34
-
35
- private mapRowToSessionData(row: any): SessionData {
36
- return {
37
- sessionId: row.session_id,
38
- serverId: row.server_id,
39
- serverName: row.server_name,
40
- serverUrl: row.server_url,
41
- transportType: row.transport_type,
42
- callbackUrl: row.callback_url,
43
- createdAt: new Date(row.created_at).getTime(),
44
- identity: row.identity,
45
- headers: row.headers,
46
- active: row.active,
47
- clientInformation: row.client_information,
48
- tokens: row.tokens,
49
- codeVerifier: row.code_verifier,
50
- clientId: row.client_id,
51
- };
52
- }
53
-
54
- async createSession(session: SessionData, ttl?: number): Promise<void> {
55
- const { sessionId, identity } = session;
56
- if (!sessionId || !identity) throw new Error('identity and sessionId required');
57
-
58
- const effectiveTtl = ttl ?? this.DEFAULT_TTL;
59
- const expiresAt = new Date(Date.now() + effectiveTtl * 1000).toISOString();
60
-
61
- const { error } = await this.supabase
62
- .from('mcp_sessions')
63
- .insert({
64
- session_id: sessionId,
65
- user_id: identity, // Maps user_id to identity to support RLS using auth.uid()
66
- server_id: session.serverId,
67
- server_name: session.serverName,
68
- server_url: session.serverUrl,
69
- transport_type: session.transportType,
70
- callback_url: session.callbackUrl,
71
- created_at: new Date(session.createdAt || Date.now()).toISOString(),
72
- identity: identity,
73
- headers: session.headers,
74
- active: session.active ?? false,
75
- client_information: session.clientInformation,
76
- tokens: session.tokens,
77
- code_verifier: session.codeVerifier,
78
- client_id: session.clientId,
79
- expires_at: expiresAt
80
- });
81
-
82
- if (error) {
83
- // Postgres error code 23505 is unique violation
84
- if (error.code === '23505') {
85
- throw new Error(`Session ${sessionId} already exists`);
86
- }
87
- throw new Error(`Failed to create session in Supabase: ${error.message}`);
88
- }
89
- }
90
-
91
- async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
92
- const effectiveTtl = ttl ?? this.DEFAULT_TTL;
93
- const expiresAt = new Date(Date.now() + effectiveTtl * 1000).toISOString();
94
-
95
- // Convert the camelCase keys to snake_case for Supabase
96
- const updateData: any = {
97
- expires_at: expiresAt,
98
- updated_at: new Date().toISOString()
99
- };
100
-
101
- if ('serverId' in data) updateData.server_id = data.serverId;
102
- if ('serverName' in data) updateData.server_name = data.serverName;
103
- if ('serverUrl' in data) updateData.server_url = data.serverUrl;
104
- if ('transportType' in data) updateData.transport_type = data.transportType;
105
- if ('callbackUrl' in data) updateData.callback_url = data.callbackUrl;
106
- if ('active' in data) updateData.active = data.active;
107
- if ('headers' in data) updateData.headers = data.headers;
108
- if ('clientInformation' in data) updateData.client_information = data.clientInformation;
109
- if ('tokens' in data) updateData.tokens = data.tokens;
110
- if ('codeVerifier' in data) updateData.code_verifier = data.codeVerifier;
111
- if ('clientId' in data) updateData.client_id = data.clientId;
112
-
113
- const { data: updatedRows, error } = await this.supabase
114
- .from('mcp_sessions')
115
- .update(updateData)
116
- .eq('identity', identity)
117
- .eq('session_id', sessionId)
118
- .select('id');
119
-
120
- if (error) {
121
- throw new Error(`Failed to update session: ${error.message}`);
122
- }
123
-
124
- if (!updatedRows || updatedRows.length === 0) {
125
- throw new Error(`Session ${sessionId} not found for identity ${identity}`);
126
- }
127
- }
128
-
129
- async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
130
- const { data, error } = await this.supabase
131
- .from('mcp_sessions')
132
- .select('*')
133
- .eq('identity', identity)
134
- .eq('session_id', sessionId)
135
- .maybeSingle();
136
-
137
- if (error) {
138
- console.error('[SupabaseStorage] Failed to get session:', error);
139
- return null;
140
- }
141
-
142
- if (!data) return null;
143
-
144
- return this.mapRowToSessionData(data);
145
- }
146
-
147
- async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
148
- const { data, error } = await this.supabase
149
- .from('mcp_sessions')
150
- .select('*')
151
- .eq('identity', identity);
152
-
153
- if (error) {
154
- console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
155
- return [];
156
- }
157
-
158
- return data.map(row => this.mapRowToSessionData(row));
159
- }
160
-
161
- async removeSession(identity: string, sessionId: string): Promise<void> {
162
- const { error } = await this.supabase
163
- .from('mcp_sessions')
164
- .delete()
165
- .eq('identity', identity)
166
- .eq('session_id', sessionId);
167
-
168
- if (error) {
169
- console.error('[SupabaseStorage] Failed to remove session:', error);
170
- }
171
- }
172
-
173
- async getIdentityMcpSessions(identity: string): Promise<string[]> {
174
- const { data, error } = await this.supabase
175
- .from('mcp_sessions')
176
- .select('session_id')
177
- .eq('identity', identity);
178
-
179
- if (error) {
180
- console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
181
- return [];
182
- }
183
-
184
- return data.map(row => row.session_id);
185
- }
186
-
187
- async getAllSessionIds(): Promise<string[]> {
188
- const { data, error } = await this.supabase
189
- .from('mcp_sessions')
190
- .select('session_id');
191
-
192
- if (error) {
193
- console.error('[SupabaseStorage] Failed to get all sessions:', error);
194
- return [];
195
- }
196
-
197
- return data.map(row => row.session_id);
198
- }
199
-
200
- async clearAll(): Promise<void> {
201
- // Warning: This deletes everything. Typically only used in testing.
202
- const { error } = await this.supabase
203
- .from('mcp_sessions')
204
- .delete()
205
- .neq('session_id', ''); // Delete all rows trick
206
-
207
- if (error) {
208
- console.error('[SupabaseStorage] Failed to clear sessions:', error);
209
- }
210
- }
211
-
212
- async cleanupExpiredSessions(): Promise<void> {
213
- const { error } = await this.supabase
214
- .from('mcp_sessions')
215
- .delete()
216
- .lt('expires_at', new Date().toISOString());
217
-
218
- if (error) {
219
- console.error('[SupabaseStorage] Failed to cleanup expired sessions:', error);
220
- }
221
- }
222
-
223
- async disconnect(): Promise<void> {
224
- // Supabase client handles its own connection pooling over HTTP,
225
- // there is no explicit disconnect method.
226
- }
227
- }
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+ import { StorageBackend, SessionData } from './types.js';
3
+ import { SESSION_TTL_SECONDS } from '../../shared/constants.js';
4
+ import { generateSessionId } from '../../shared/utils.js';
5
+
6
+ export class SupabaseStorageBackend implements StorageBackend {
7
+ private readonly DEFAULT_TTL = SESSION_TTL_SECONDS;
8
+
9
+ constructor(private supabase: SupabaseClient) {}
10
+
11
+ async init(): Promise<void> {
12
+ // Validate that the table exists
13
+ const { error } = await this.supabase
14
+ .from('mcp_sessions')
15
+ .select('session_id')
16
+ .limit(0);
17
+
18
+ if (error) {
19
+ // Postgres error code 42P01 is "relation does not exist"
20
+ if (error.code === '42P01') {
21
+ throw new Error(
22
+ '[SupabaseStorage] Table "mcp_sessions" not found in your database. ' +
23
+ 'Please run "npx mcp-ts supabase-init" in your project to set up the required table and RLS policies.'
24
+ );
25
+ }
26
+ throw new Error(`[SupabaseStorage] Initialization check failed: ${error.message}`);
27
+ }
28
+
29
+ console.log('[mcp-ts][Storage] Supabase: ✓ "mcp_sessions" table verified.');
30
+ }
31
+
32
+ generateSessionId(): string {
33
+ return generateSessionId();
34
+ }
35
+
36
+ private mapRowToSessionData(row: any): SessionData {
37
+ return {
38
+ sessionId: row.session_id,
39
+ serverId: row.server_id,
40
+ serverName: row.server_name,
41
+ serverUrl: row.server_url,
42
+ transportType: row.transport_type,
43
+ callbackUrl: row.callback_url,
44
+ createdAt: new Date(row.created_at).getTime(),
45
+ identity: row.identity,
46
+ headers: row.headers,
47
+ active: row.active,
48
+ clientInformation: row.client_information,
49
+ tokens: row.tokens,
50
+ codeVerifier: row.code_verifier,
51
+ clientId: row.client_id,
52
+ };
53
+ }
54
+
55
+ async createSession(session: SessionData, ttl?: number): Promise<void> {
56
+ const { sessionId, identity } = session;
57
+ if (!sessionId || !identity) throw new Error('identity and sessionId required');
58
+
59
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
60
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1000).toISOString();
61
+
62
+ const { error } = await this.supabase
63
+ .from('mcp_sessions')
64
+ .insert({
65
+ session_id: sessionId,
66
+ user_id: identity, // Maps user_id to identity to support RLS using auth.uid()
67
+ server_id: session.serverId,
68
+ server_name: session.serverName,
69
+ server_url: session.serverUrl,
70
+ transport_type: session.transportType,
71
+ callback_url: session.callbackUrl,
72
+ created_at: new Date(session.createdAt || Date.now()).toISOString(),
73
+ identity: identity,
74
+ headers: session.headers,
75
+ active: session.active ?? false,
76
+ client_information: session.clientInformation,
77
+ tokens: session.tokens,
78
+ code_verifier: session.codeVerifier,
79
+ client_id: session.clientId,
80
+ expires_at: expiresAt
81
+ });
82
+
83
+ if (error) {
84
+ // Postgres error code 23505 is unique violation
85
+ if (error.code === '23505') {
86
+ throw new Error(`Session ${sessionId} already exists`);
87
+ }
88
+ throw new Error(`Failed to create session in Supabase: ${error.message}`);
89
+ }
90
+ }
91
+
92
+ async updateSession(identity: string, sessionId: string, data: Partial<SessionData>, ttl?: number): Promise<void> {
93
+ const effectiveTtl = ttl ?? this.DEFAULT_TTL;
94
+ const expiresAt = new Date(Date.now() + effectiveTtl * 1000).toISOString();
95
+
96
+ // Convert the camelCase keys to snake_case for Supabase
97
+ const updateData: any = {
98
+ expires_at: expiresAt,
99
+ updated_at: new Date().toISOString()
100
+ };
101
+
102
+ if ('serverId' in data) updateData.server_id = data.serverId;
103
+ if ('serverName' in data) updateData.server_name = data.serverName;
104
+ if ('serverUrl' in data) updateData.server_url = data.serverUrl;
105
+ if ('transportType' in data) updateData.transport_type = data.transportType;
106
+ if ('callbackUrl' in data) updateData.callback_url = data.callbackUrl;
107
+ if ('active' in data) updateData.active = data.active;
108
+ if ('headers' in data) updateData.headers = data.headers;
109
+ if ('clientInformation' in data) updateData.client_information = data.clientInformation;
110
+ if ('tokens' in data) updateData.tokens = data.tokens;
111
+ if ('codeVerifier' in data) updateData.code_verifier = data.codeVerifier;
112
+ if ('clientId' in data) updateData.client_id = data.clientId;
113
+
114
+ const { data: updatedRows, error } = await this.supabase
115
+ .from('mcp_sessions')
116
+ .update(updateData)
117
+ .eq('identity', identity)
118
+ .eq('session_id', sessionId)
119
+ .select('id');
120
+
121
+ if (error) {
122
+ throw new Error(`Failed to update session: ${error.message}`);
123
+ }
124
+
125
+ if (!updatedRows || updatedRows.length === 0) {
126
+ throw new Error(`Session ${sessionId} not found for identity ${identity}`);
127
+ }
128
+ }
129
+
130
+ async getSession(identity: string, sessionId: string): Promise<SessionData | null> {
131
+ const { data, error } = await this.supabase
132
+ .from('mcp_sessions')
133
+ .select('*')
134
+ .eq('identity', identity)
135
+ .eq('session_id', sessionId)
136
+ .maybeSingle();
137
+
138
+ if (error) {
139
+ console.error('[SupabaseStorage] Failed to get session:', error);
140
+ return null;
141
+ }
142
+
143
+ if (!data) return null;
144
+
145
+ return this.mapRowToSessionData(data);
146
+ }
147
+
148
+ async getIdentitySessionsData(identity: string): Promise<SessionData[]> {
149
+ const { data, error } = await this.supabase
150
+ .from('mcp_sessions')
151
+ .select('*')
152
+ .eq('identity', identity);
153
+
154
+ if (error) {
155
+ console.error(`[SupabaseStorage] Failed to get session data for ${identity}:`, error);
156
+ return [];
157
+ }
158
+
159
+ return data.map(row => this.mapRowToSessionData(row));
160
+ }
161
+
162
+ async removeSession(identity: string, sessionId: string): Promise<void> {
163
+ const { error } = await this.supabase
164
+ .from('mcp_sessions')
165
+ .delete()
166
+ .eq('identity', identity)
167
+ .eq('session_id', sessionId);
168
+
169
+ if (error) {
170
+ console.error('[SupabaseStorage] Failed to remove session:', error);
171
+ }
172
+ }
173
+
174
+ async getIdentityMcpSessions(identity: string): Promise<string[]> {
175
+ const { data, error } = await this.supabase
176
+ .from('mcp_sessions')
177
+ .select('session_id')
178
+ .eq('identity', identity);
179
+
180
+ if (error) {
181
+ console.error(`[SupabaseStorage] Failed to get sessions for ${identity}:`, error);
182
+ return [];
183
+ }
184
+
185
+ return data.map(row => row.session_id);
186
+ }
187
+
188
+ async getAllSessionIds(): Promise<string[]> {
189
+ const { data, error } = await this.supabase
190
+ .from('mcp_sessions')
191
+ .select('session_id');
192
+
193
+ if (error) {
194
+ console.error('[SupabaseStorage] Failed to get all sessions:', error);
195
+ return [];
196
+ }
197
+
198
+ return data.map(row => row.session_id);
199
+ }
200
+
201
+ async clearAll(): Promise<void> {
202
+ // Warning: This deletes everything. Typically only used in testing.
203
+ const { error } = await this.supabase
204
+ .from('mcp_sessions')
205
+ .delete()
206
+ .neq('session_id', ''); // Delete all rows trick
207
+
208
+ if (error) {
209
+ console.error('[SupabaseStorage] Failed to clear sessions:', error);
210
+ }
211
+ }
212
+
213
+ async cleanupExpiredSessions(): Promise<void> {
214
+ const { error } = await this.supabase
215
+ .from('mcp_sessions')
216
+ .delete()
217
+ .lt('expires_at', new Date().toISOString());
218
+
219
+ if (error) {
220
+ console.error('[SupabaseStorage] Failed to cleanup expired sessions:', error);
221
+ }
222
+ }
223
+
224
+ async disconnect(): Promise<void> {
225
+ // Supabase client handles its own connection pooling over HTTP,
226
+ // there is no explicit disconnect method.
227
+ }
228
+ }