Skip to content

Conversation

@laulauland
Copy link
Member

@laulauland laulauland commented Oct 14, 2025

  • Add McpClient, StreamableHttpClientTransport, and Connection (tools, prompts, resources, ping)
  • Support stateless and session modes (SSE stream, onElicit/onSample for server-initiated requests)
  • Implement ClientSessionAdapter with InMemoryClientSessionAdapter
  • Protocol: negotiate and send MCP-Protocol-Version (uses SUPPORTED_MCP_PROTOCOL_VERSIONS.V2025_06_18)
  • Docs: add README.client.md with examples (tool discovery before call, progress handling, connection pool + persistence)
  • Tests: stateless ops, server-initiated requests (elicitation), multi-server workflows, progress notifications, error recovery
  • Cleanup: remove client middleware/context; delete packages/core/src/client/context.ts; simplify types and exports; fix version constants

Why

  • Minimal, spec-aligned client; reflects typical MCP flows (discover tools then call)
  • Enables session-based features (SSE, elicitation/sampling) without forcing state in basic use

Notes

  • No breaking changes to server APIs
  • Client API is new; examples provided in README.client.md

Follow-ups

  • Tool list caching
  • Reconnect/backoff strategy in connection pool

@changeset-bot
Copy link

changeset-bot bot commented Oct 14, 2025

🦋 Changeset detected

Latest commit: 37b8c04

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
mcp-lite Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@laulauland laulauland force-pushed the mcp-client-implementation branch from 4792449 to f855e96 Compare October 14, 2025 14:11
@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 14, 2025

Open in StackBlitz

npm i https://pkg.pr.new/fiberplane/mcp-lite@133

commit: 37b8c04

@laulauland laulauland force-pushed the mcp-client-implementation branch from f8866c3 to 4626cb6 Compare October 16, 2025 12:06
@laulauland laulauland changed the title Implement MCP client with session and bidirectional support core/client: add MCP client with sessions, server requests, and docs Oct 16, 2025
laulauland and others added 10 commits October 28, 2025 14:47
Add complete MCP client implementation:

Core Foundation:
- McpClient class with fluent API (.use, .onError)  
- Connection class for server interactions (tools, prompts, resources)
- StreamableHttpClientTransport for HTTP communication
- ToolAdapter interface for SDK integration
- Full TypeScript support with proper type inference
- Zero runtime dependencies (built on Fetch API)

Session Management:
- ClientSessionAdapter interface for session persistence
- InMemoryClientSessionAdapter implementation
- openSessionStream() for GET SSE streams
- Session lifecycle management (create, reconnect, delete)
- Event replay with Last-Event-ID support

Server-Initiated Requests:
- onSample() and onElicit() handler registration
- Client _dispatch() method for processing server requests
- Middleware support for server-initiated requests
- Automatic SSE stream processing for incoming requests
- Response sending back to server via POST

Testing and Documentation:
- 182 integration tests (stateless, session, server requests, E2E)
- 624 expect() assertions validating all features
- Multi-server workflow tests
- Progress notification handling
- Error recovery patterns
- Complete README documentation with examples

The client mirrors McpServer patterns but inverted for sending
requests rather than handling them. Supports stateless operations
by default with opt-in session and bidirectional features.
Make openSessionStream() return Promise<void> instead of returning a stream.
This removes the complexity of tee'ing streams and race conditions entirely.

The connection processes SSE events internally for server-initiated requests
(elicitation/sampling). Tests that need to observe raw events use the
test-utils openSessionStream() helper directly.

This is cleaner architecture because:
- No stream tee complexity
- No race conditions  
- Aligns with actual usage (users just call openSessionStream(), don't need the stream)
- Server only allows one SSE stream per session anyway

Breaking change: openSessionStream() now returns void
Tests updated to use test-utils helper for event observation
CI shows timing issue where ping event not received before making fetch call.
Add small 50ms delay to ensure SSE stream is fully connected and ping
event has been delivered before making tool call.

Fixes intermittent CI failure: Expected length 4, Received length 3
The test was expecting 4 events (ping + 3 progress) but ping delivery
is inconsistent in CI. Changed to collect events with 1000ms timeout
and filter for progress notifications, making the test robust regardless
of whether ping arrives or not.

This fixes the intermittent CI failure where only 3 events were received.
Add 'headers' option to StreamableHttpClientTransportOptions to support
custom headers in all HTTP requests. This enables:
- Authorization tokens (Bearer, API keys, etc.)
- Custom authentication schemes
- Request tracking headers
- Any other custom HTTP headers

Changes:
- Add headers option to StreamableHttpClientTransportOptions
- Store custom headers in transport and pass to Connection
- Merge custom headers into all fetch calls:
  * Initialize request
  * Connection requests (tools, prompts, resources)
  * SSE stream opening
  * Response sending back to server
- Custom headers override protocol defaults if conflicts exist
- Add comprehensive tests for custom headers functionality

Usage example:
```typescript
const transport = new StreamableHttpClientTransport({
  headers: {
    'Authorization': 'Bearer my-token',
    'X-API-Key': 'my-key'
  }
});
```

Note: This complements the OAuth support. OAuth provides automatic
token management, while custom headers allow manual control and
support for non-OAuth auth schemes.
…construction

Headers are now passed per-connection rather than per-transport.
This allows using the same transport instance to connect to multiple
servers with different authentication headers.

Before:
  const transport = new StreamableHttpClientTransport({
    headers: { Authorization: 'Bearer token' }
  });
  const connection = await connect(serverUrl);

After:
  const transport = new StreamableHttpClientTransport();
  const connection = await connect(serverUrl, {
    headers: { Authorization: 'Bearer token' }
  });

This is more flexible for multi-server scenarios where each server
needs different headers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added comprehensive documentation for the custom headers feature:

- New section: 'Connection with Custom Headers'
- Updated 'Multiple Server Connections' section with header examples
- Added to features list
- Explained header behavior and OAuth interaction

Shows how to:
- Pass headers at connect() time
- Use different headers for different servers
- Understand header precedence with OAuth

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@laulauland laulauland force-pushed the mcp-client-implementation branch from d68a251 to 72b92c3 Compare October 28, 2025 13:47
Problem:
- mcp-lite was incorrectly appending /.well-known/oauth-protected-resource
  to the full MCP endpoint URL
- Example: https://example.com/mcphttps://example.com/mcp/.well-known/oauth-protected-resource (wrong)
- RFC 8707 Section 3 requires .well-known endpoints at the origin, not sub-paths

Solution:
- Extract origin from baseUrl for RFC 8707 compliant discovery
- Correct: https://example.com/mcphttps://example.com/.well-known/oauth-protected-resource
- Added WWW-Authenticate header fallback for servers that provide as_uri hint
- Added validateAndReturnEndpoints() helper to reduce code duplication

Testing:
- Added 3 new integration tests covering origin-based discovery, 
  WWW-Authenticate fallback, and graceful failure scenarios
- All 14 OAuth tests pass
- Full test suite passes (234 tests)
Adds automatic client registration for OAuth-protected MCP servers
that don't require pre-configured client credentials.

Changes:
- Add registerOAuthClient() function implementing RFC 7591
- Extend OAuthAdapter with client credential storage
- Update OAuthConfig to make clientId optional
- Automatically perform DCR when no clientId is provided
- Store and reuse registered client credentials per authorization server
- Add registrationEndpoint to OAuthEndpoints from discovery
- Remove dynamic imports from transport-http.ts

Implementation:
- Client credentials are stored per authorization server URL
- Reuses existing credentials if already registered
- Falls back to error if DCR not available and no clientId
- Stores clientId in PendingAuthState for token operations

Testing:
- All 234 tests pass including OAuth integration tests
- TypeScript compilation passes without errors
… README

Updates client documentation with:
- DCR support (clientId is now optional)
- Automatic client registration via RFC 7591
- Origin-based OAuth discovery per RFC 8707 Section 3
- Client credential storage methods in OAuthAdapter
- Examples of manual DCR usage
- Updated FileOAuthAdapter example with credentials storage
- Configuration summary with updated OAuthConfig interface
Adds missing exports to packages/core/src/index.ts:
- registerOAuthClient function
- ClientCredentials type
- ClientMetadata type

These were exported from client/index.ts but not re-exported
from the main package index, causing them to be unavailable
when importing from 'mcp-lite'.
@laulauland laulauland force-pushed the mcp-client-implementation branch from 50f3c36 to 992ae6c Compare October 28, 2025 14:44
CRITICAL: Fixed hardcoded protocol version in WWW-Authenticate 
fallback mechanism. Was using invalid '2024-11-05', now correctly 
uses SUPPORTED_MCP_PROTOCOL_VERSIONS.V2025_06_18.

This incorrect protocol version could have caused MCP connection 
failures during OAuth discovery fallback when the server checks 
protocol version in initialize requests.

Changes:
- Import SUPPORTED_MCP_PROTOCOL_VERSIONS constant
- Replace hardcoded '2024-11-05' with V2025_06_18 constant
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants