@mcp-ts/sdk 2.4.4 → 2.4.5
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.
- package/dist/client/react.js +2 -6
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +2 -6
- package/dist/client/react.mjs.map +1 -1
- package/dist/index.js +60 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -48
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +60 -48
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +60 -48
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.js +2 -6
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +2 -6
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/server/handlers/sse-handler.ts +2 -2
- package/src/server/mcp/oauth-client.ts +53 -45
- package/src/server/storage/index.ts +1 -1
- package/src/shared/utils.ts +16 -14
package/package.json
CHANGED
|
@@ -38,7 +38,7 @@ import { UnauthorizedError } from '../../shared/errors.js';
|
|
|
38
38
|
import { isConnectionEvent, isRpcResponseEvent } from '../../shared/event-routing.js';
|
|
39
39
|
import { parseOAuthState } from '../../shared/utils.js';
|
|
40
40
|
import { MCPClient } from '../mcp/oauth-client.js';
|
|
41
|
-
import { sessions } from '../storage/index.js';
|
|
41
|
+
import { sessions, generateServerId } from '../storage/index.js';
|
|
42
42
|
|
|
43
43
|
// ============================================
|
|
44
44
|
// Types & Interfaces
|
|
@@ -256,7 +256,7 @@ export class SSEConnectionManager {
|
|
|
256
256
|
// Tool name format: tool_<serverId>_<toolName> - with 12 char serverId leaves 46 chars for tool name
|
|
257
257
|
const serverId = params.serverId && params.serverId.length <= 12
|
|
258
258
|
? params.serverId
|
|
259
|
-
:
|
|
259
|
+
: generateServerId();
|
|
260
260
|
|
|
261
261
|
// Check for existing connections
|
|
262
262
|
const existingSessions = await sessions.list(this.userId);
|
|
@@ -360,7 +360,15 @@ export class MCPClient {
|
|
|
360
360
|
if (!existingSession && this.serverId && this.serverUrl && this.callbackUrl) {
|
|
361
361
|
this.createdAt = Date.now();
|
|
362
362
|
const updatedAt = this.createdAt;
|
|
363
|
-
|
|
363
|
+
this._onObservabilityEvent.fire({
|
|
364
|
+
type: 'mcp:client:session_created',
|
|
365
|
+
level: 'info',
|
|
366
|
+
message: `Creating pending session ${this.sessionId} for connection setup`,
|
|
367
|
+
sessionId: this.sessionId,
|
|
368
|
+
serverId: this.serverId,
|
|
369
|
+
timestamp: Date.now(),
|
|
370
|
+
id: nanoid(),
|
|
371
|
+
});
|
|
364
372
|
await sessions.create({
|
|
365
373
|
sessionId: this.sessionId,
|
|
366
374
|
userId: this.userId,
|
|
@@ -542,7 +550,15 @@ export class MCPClient {
|
|
|
542
550
|
|
|
543
551
|
// Refresh session metadata on every successful connect so active sessions
|
|
544
552
|
// record ongoing usage and don't look dormant to session cleanup jobs.
|
|
545
|
-
|
|
553
|
+
this._onObservabilityEvent.fire({
|
|
554
|
+
type: 'mcp:client:session_saved',
|
|
555
|
+
level: 'info',
|
|
556
|
+
message: `Saving active session ${this.sessionId} (connect success)`,
|
|
557
|
+
sessionId: this.sessionId,
|
|
558
|
+
serverId: this.serverId,
|
|
559
|
+
timestamp: Date.now(),
|
|
560
|
+
id: nanoid(),
|
|
561
|
+
});
|
|
546
562
|
await this.saveSession('active');
|
|
547
563
|
} catch (error) {
|
|
548
564
|
/** Handle Authentication Errors */
|
|
@@ -580,7 +596,15 @@ export class MCPClient {
|
|
|
580
596
|
}
|
|
581
597
|
|
|
582
598
|
this.emitStateChange('AUTHENTICATING');
|
|
583
|
-
|
|
599
|
+
this._onObservabilityEvent.fire({
|
|
600
|
+
type: 'mcp:client:session_saved',
|
|
601
|
+
level: 'info',
|
|
602
|
+
message: `Saving pending OAuth session ${this.sessionId}`,
|
|
603
|
+
sessionId: this.sessionId,
|
|
604
|
+
serverId: this.serverId,
|
|
605
|
+
timestamp: Date.now(),
|
|
606
|
+
id: nanoid(),
|
|
607
|
+
});
|
|
584
608
|
await this.saveSession('pending');
|
|
585
609
|
|
|
586
610
|
if (this.serverId) {
|
|
@@ -686,34 +710,30 @@ export class MCPClient {
|
|
|
686
710
|
authenticatedStateEmitted = true;
|
|
687
711
|
}
|
|
688
712
|
|
|
689
|
-
this.emitProgress('Creating authenticated client...');
|
|
690
|
-
|
|
691
|
-
this.client = new Client(
|
|
692
|
-
{
|
|
693
|
-
name: MCP_CLIENT_NAME,
|
|
694
|
-
version: MCP_CLIENT_VERSION,
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
capabilities: {
|
|
698
|
-
extensions: {
|
|
699
|
-
'io.modelcontextprotocol/ui': {
|
|
700
|
-
mimeTypes: ['text/html+mcp'],
|
|
701
|
-
},
|
|
702
|
-
},
|
|
703
|
-
} as McpAppClientCapabilities
|
|
704
|
-
}
|
|
705
|
-
);
|
|
706
|
-
|
|
707
713
|
this.emitStateChange('CONNECTING');
|
|
708
714
|
|
|
709
|
-
|
|
715
|
+
// The SDK Client may still have a transport attached from a prior
|
|
716
|
+
// connect() that failed with UnauthorizedError; close it first so
|
|
717
|
+
// we can negotiate a fresh session with the newly-exchanged tokens.
|
|
718
|
+
if (this.client.transport) {
|
|
719
|
+
try { await this.client.close(); } catch {}
|
|
720
|
+
}
|
|
721
|
+
|
|
710
722
|
await this.client.connect(this.transport);
|
|
711
723
|
|
|
712
724
|
/** Connection succeeded — lock in the transport type */
|
|
713
725
|
this.transportType = currentType;
|
|
714
726
|
|
|
715
727
|
this.emitStateChange('CONNECTED');
|
|
716
|
-
|
|
728
|
+
this._onObservabilityEvent.fire({
|
|
729
|
+
type: 'mcp:client:session_saved',
|
|
730
|
+
level: 'info',
|
|
731
|
+
message: `Saving active session ${this.sessionId} (OAuth complete)`,
|
|
732
|
+
sessionId: this.sessionId,
|
|
733
|
+
serverId: this.serverId,
|
|
734
|
+
timestamp: Date.now(),
|
|
735
|
+
id: nanoid(),
|
|
736
|
+
});
|
|
717
737
|
await this.saveSession('active');
|
|
718
738
|
|
|
719
739
|
return; // Success, exit function
|
|
@@ -767,10 +787,6 @@ export class MCPClient {
|
|
|
767
787
|
* @throws {Error} When client is not connected
|
|
768
788
|
*/
|
|
769
789
|
async listTools(): Promise<ListToolsResult> {
|
|
770
|
-
if (!this.client) {
|
|
771
|
-
throw new Error('Not connected to server');
|
|
772
|
-
}
|
|
773
|
-
|
|
774
790
|
this.emitStateChange('DISCOVERING');
|
|
775
791
|
|
|
776
792
|
try {
|
|
@@ -814,9 +830,6 @@ export class MCPClient {
|
|
|
814
830
|
* @throws {Error} When client is not connected
|
|
815
831
|
*/
|
|
816
832
|
async callTool(toolName: string, toolArgs: Record<string, unknown>): Promise<CallToolResult> {
|
|
817
|
-
if (!this.client) {
|
|
818
|
-
throw new Error('Not connected to server');
|
|
819
|
-
}
|
|
820
833
|
|
|
821
834
|
const request: CallToolRequest = {
|
|
822
835
|
method: 'tools/call',
|
|
@@ -877,10 +890,6 @@ export class MCPClient {
|
|
|
877
890
|
* @throws {Error} When client is not connected
|
|
878
891
|
*/
|
|
879
892
|
async listPrompts(): Promise<ListPromptsResult> {
|
|
880
|
-
if (!this.client) {
|
|
881
|
-
throw new Error('Not connected to server');
|
|
882
|
-
}
|
|
883
|
-
|
|
884
893
|
this.emitStateChange('DISCOVERING');
|
|
885
894
|
|
|
886
895
|
try {
|
|
@@ -913,9 +922,6 @@ export class MCPClient {
|
|
|
913
922
|
* @throws {Error} When client is not connected
|
|
914
923
|
*/
|
|
915
924
|
async getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult> {
|
|
916
|
-
if (!this.client) {
|
|
917
|
-
throw new Error('Not connected to server');
|
|
918
|
-
}
|
|
919
925
|
|
|
920
926
|
const request: GetPromptRequest = {
|
|
921
927
|
method: 'prompts/get',
|
|
@@ -936,10 +942,6 @@ export class MCPClient {
|
|
|
936
942
|
* @throws {Error} When client is not connected
|
|
937
943
|
*/
|
|
938
944
|
async listResources(): Promise<ListResourcesResult> {
|
|
939
|
-
if (!this.client) {
|
|
940
|
-
throw new Error('Not connected to server');
|
|
941
|
-
}
|
|
942
|
-
|
|
943
945
|
this.emitStateChange('DISCOVERING');
|
|
944
946
|
|
|
945
947
|
try {
|
|
@@ -971,9 +973,6 @@ export class MCPClient {
|
|
|
971
973
|
* @throws {Error} When client is not connected
|
|
972
974
|
*/
|
|
973
975
|
async readResource(uri: string): Promise<ReadResourceResult> {
|
|
974
|
-
if (!this.client) {
|
|
975
|
-
throw new Error('Not connected to server');
|
|
976
|
-
}
|
|
977
976
|
|
|
978
977
|
const request: ReadResourceRequest = {
|
|
979
978
|
method: 'resources/read',
|
|
@@ -1031,7 +1030,16 @@ export class MCPClient {
|
|
|
1031
1030
|
try {
|
|
1032
1031
|
await this.ensureSession();
|
|
1033
1032
|
} catch (error) {
|
|
1034
|
-
|
|
1033
|
+
this._onObservabilityEvent.fire({
|
|
1034
|
+
type: 'mcp:client:error',
|
|
1035
|
+
level: 'warn',
|
|
1036
|
+
message: 'Initialization failed during clearSession',
|
|
1037
|
+
sessionId: this.sessionId,
|
|
1038
|
+
serverId: this.serverId,
|
|
1039
|
+
payload: { error: String(error) },
|
|
1040
|
+
timestamp: Date.now(),
|
|
1041
|
+
id: nanoid(),
|
|
1042
|
+
});
|
|
1035
1043
|
}
|
|
1036
1044
|
|
|
1037
1045
|
if (this.oauthProvider) {
|
|
@@ -9,7 +9,7 @@ import type { SessionStore, Session, SessionMutationEvent, SessionMutationListen
|
|
|
9
9
|
|
|
10
10
|
// Re-export types
|
|
11
11
|
export * from './types.js';
|
|
12
|
-
export { generateSessionId } from '../../shared/utils.js';
|
|
12
|
+
export { generateSessionId, generateServerId } from '../../shared/utils.js';
|
|
13
13
|
export { RedisStorageBackend, MemoryStorageBackend, FileStorageBackend, SqliteStorage, SupabaseStorageBackend, NeonStorageBackend };
|
|
14
14
|
|
|
15
15
|
export function createSupabaseStorageBackend(client: any): SupabaseStorageBackend {
|
package/src/shared/utils.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import { customAlphabet } from 'nanoid';
|
|
1
|
+
import { customAlphabet, nanoid } from 'nanoid';
|
|
2
2
|
|
|
3
3
|
const OAUTH_STATE_SEPARATOR = '.';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
1
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
/** remaining chars: alphanumeric */
|
|
12
|
-
const rest = customAlphabet(
|
|
13
|
-
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
|
14
|
-
11
|
|
5
|
+
const serverIdAlphabet = customAlphabet(
|
|
6
|
+
'abcdefghijklmnopqrstuvwxyz0123456789',
|
|
7
|
+
12
|
|
15
8
|
);
|
|
16
9
|
|
|
17
10
|
/**
|
|
@@ -32,11 +25,20 @@ export function sanitizeServerLabel(name: string): string {
|
|
|
32
25
|
}
|
|
33
26
|
|
|
34
27
|
/**
|
|
35
|
-
* Generates a
|
|
36
|
-
*
|
|
28
|
+
* Generates a session ID with a human-readable prefix for easy identification.
|
|
29
|
+
* Format: sess_<21-char nanoid> (26 chars total).
|
|
30
|
+
* Not used in tool names, so length is not constrained.
|
|
37
31
|
*/
|
|
38
32
|
export function generateSessionId(): string {
|
|
39
|
-
return
|
|
33
|
+
return 'sess_' + nanoid(21);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Generates a short server ID suitable for use in tool names.
|
|
38
|
+
* 12-char alphanumeric string that keeps tool_<serverId>_<toolName> under 64 chars.
|
|
39
|
+
*/
|
|
40
|
+
export function generateServerId(): string {
|
|
41
|
+
return serverIdAlphabet();
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export interface ParsedOAuthState {
|