diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea43b519..d73c3900 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - run: npm ci - run: npm run check - run: npm run build + - run: npm run test:client-smoke - run: npm test publish: diff --git a/examples/clients/typescript/elicitation-defaults-test.ts b/examples/clients/typescript/elicitation-defaults-test.ts deleted file mode 100644 index 9a197e14..00000000 --- a/examples/clients/typescript/elicitation-defaults-test.ts +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env node - -/** - * Test client for SEP-1034 client-side elicitation defaults - * This client intentionally returns empty/partial content in elicitation responses - * to verify that the SDK applies defaults for omitted fields. - */ - -import { Client } from '@modelcontextprotocol/sdk/client/index.js'; -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; -import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js'; - -async function main(): Promise { - const serverUrl = process.argv[2]; - - if (!serverUrl) { - console.error('Usage: elicitation-defaults-test '); - process.exit(1); - } - - console.log(`Connecting to MCP server at: ${serverUrl}`); - - try { - const client = new Client( - { - name: 'elicitation-defaults-test-client', - version: '1.0.0' - }, - { - capabilities: { - elicitation: { - applyDefaults: true - } - } - } - ); - - // Register elicitation handler that returns empty content - // The SDK should fill in defaults for all omitted fields - client.setRequestHandler(ElicitRequestSchema, async (request) => { - console.log( - '📋 Received elicitation request:', - JSON.stringify(request.params, null, 2) - ); - console.log( - '✅ Accepting with empty content - SDK should apply defaults' - ); - - // Return empty content - SDK should merge in defaults - return { - action: 'accept' as const, - content: {} - }; - }); - - const transport = new StreamableHTTPClientTransport(new URL(serverUrl)); - - await client.connect(transport); - console.log('✅ Successfully connected to MCP server'); - - // List available tools - const tools = await client.listTools(); - console.log( - '📦 Available tools:', - tools.tools.map((t) => t.name) - ); - - // Call the test tool which will trigger elicitation - const testTool = tools.tools.find( - (t) => t.name === 'test_client_elicitation_defaults' - ); - if (!testTool) { - console.error('❌ Test tool not found: test_client_elicitation_defaults'); - process.exit(1); - } - - console.log('🔧 Calling test_client_elicitation_defaults tool...'); - const result = await client.callTool({ - name: 'test_client_elicitation_defaults', - arguments: {} - }); - - console.log('📄 Tool result:', JSON.stringify(result, null, 2)); - - await transport.close(); - console.log('✅ Connection closed successfully'); - - process.exit(0); - } catch (error) { - console.error('❌ Error:', error); - process.exit(1); - } -} - -main().catch((error) => { - console.error('Unhandled error:', error); - process.exit(1); -}); diff --git a/examples/clients/typescript/everything-client.ts b/examples/clients/typescript/everything-client.ts index ef272e4a..5d131710 100644 --- a/examples/clients/typescript/everything-client.ts +++ b/examples/clients/typescript/everything-client.ts @@ -481,6 +481,46 @@ registerScenarios( issValidationClient ); +// ============================================================================ +// SSE retry scenario +// ============================================================================ + +async function runSSERetryClient(serverUrl: string): Promise { + const client = new Client( + { name: 'sse-retry-test-client', version: '1.0.0' }, + { capabilities: {} } + ); + + const transport = new StreamableHTTPClientTransport(new URL(serverUrl)); + + await client.connect(transport); + logger.debug('Successfully connected to MCP server'); + + const tools = await client.listTools(); + logger.debug( + 'Available tools:', + tools.tools.map((t) => t.name) + ); + + const testTool = tools.tools.find((t) => t.name === 'test_reconnection'); + if (!testTool) { + throw new Error('Test tool not found: test_reconnection'); + } + + logger.debug('Calling test_reconnection tool...'); + const result = await client.callTool({ + name: 'test_reconnection', + arguments: {} + }); + + logger.debug('Tool result:', JSON.stringify(result, null, 2)); + + await transport.close(); + logger.debug('Connection closed successfully'); +} + +registerScenario('sse-retry', runSSERetryClient); + // ============================================================================ // Elicitation defaults scenario // ============================================================================ @@ -491,7 +531,9 @@ async function runElicitationDefaultsClient(serverUrl: string): Promise { { capabilities: { elicitation: { - applyDefaults: true + form: { + applyDefaults: true + } } } } @@ -545,7 +587,10 @@ async function runElicitationDefaultsClient(serverUrl: string): Promise { logger.debug('Connection closed successfully'); } -registerScenario('elicitation-defaults', runElicitationDefaultsClient); +registerScenario( + 'elicitation-sep1034-client-defaults', + runElicitationDefaultsClient +); // ============================================================================ // Client Credentials scenarios diff --git a/package.json b/package.json index 1d110048..72a79b4a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "scripts": { "start": "tsx src/index.ts", "test": "vitest run", + "test:client-smoke": "node dist/index.js client --command \"npx tsx examples/clients/typescript/everything-client.ts\" --suite core --timeout 60000", "test:watch": "vitest", "build": "tsdown src/index.ts --minify --clean --target node20 --no-fixed-extension", "lint": "eslint src/ examples/ && prettier --check .",