@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-ts/sdk",
3
- "version": "2.4.4",
3
+ "version": "2.4.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -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
- : await sessions.generateSessionId();
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
- console.log(`[MCPClient] Creating pending session ${this.sessionId} for connection setup`);
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
- console.log(`[MCPClient] Saving active session ${this.sessionId} (connect success)`);
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
- console.log(`[MCPClient] Saving pending OAuth session ${this.sessionId}`);
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
- /** We explicitly try to connect with the transport we just auth'd with first */
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
- console.log(`[MCPClient] Saving active session ${this.sessionId} (OAuth complete)`);
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
- console.warn('[MCPClient] Initialization failed during clearSession:', error);
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 {
@@ -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
- /** first char: letters only (required by OpenAI) */
6
- const firstChar = customAlphabet(
7
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
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 standard 12-character session ID compliant with external tool restrictions.
36
- * First character is always a letter.
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 firstChar() + rest();
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 {