chore: refresh CLAUDE.md (#1217)
Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com>
This commit is contained in:
parent
4b5c25d592
commit
401a189fb8
1 changed files with 208 additions and 10 deletions
218
CLAUDE.md
218
CLAUDE.md
|
|
@ -1,13 +1,18 @@
|
|||
# MCP TypeScript SDK Guide
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Build & Test Commands
|
||||
|
||||
```sh
|
||||
npm run build # Build ESM and CJS versions
|
||||
npm run lint # Run ESLint
|
||||
npm test # Run all tests
|
||||
npx jest path/to/file.test.ts # Run specific test file
|
||||
npx jest -t "test name" # Run tests matching pattern
|
||||
npm run lint # Run ESLint and Prettier check
|
||||
npm run lint:fix # Auto-fix lint and formatting issues
|
||||
npm test # Run all tests (vitest)
|
||||
npm run test:watch # Run tests in watch mode
|
||||
npx vitest path/to/file.test.ts # Run specific test file
|
||||
npx vitest -t "test name" # Run tests matching pattern
|
||||
npm run typecheck # Type-check without emitting
|
||||
```
|
||||
|
||||
## Code Style Guidelines
|
||||
|
|
@ -16,13 +21,206 @@ npx jest -t "test name" # Run tests matching pattern
|
|||
- **Naming**: PascalCase for classes/types, camelCase for functions/variables
|
||||
- **Files**: Lowercase with hyphens, test files with `.test.ts` suffix
|
||||
- **Imports**: ES module style, include `.js` extension, group imports logically
|
||||
- **Error Handling**: Use TypeScript's strict mode, explicit error checking in tests
|
||||
- **Formatting**: 2-space indentation, semicolons required, single quotes preferred
|
||||
- **Testing**: Co-locate tests with source files, use descriptive test names
|
||||
- **Comments**: JSDoc for public APIs, inline comments for complex logic
|
||||
|
||||
## Project Structure
|
||||
## Architecture Overview
|
||||
|
||||
- `/src`: Source code with client, server, and shared modules
|
||||
- Tests alongside source files with `.test.ts` suffix
|
||||
- Node.js >= 18 required
|
||||
### Core Layers
|
||||
|
||||
The SDK is organized into three main layers:
|
||||
|
||||
1. **Types Layer** (`src/types.ts`) - Protocol types generated from the MCP specification. All JSON-RPC message types, schemas, and protocol constants are defined here using Zod v4.
|
||||
|
||||
2. **Protocol Layer** (`src/shared/protocol.ts`) - The abstract `Protocol` class that handles JSON-RPC message routing, request/response correlation, capability negotiation, and transport management. Both `Client` and `Server` extend this class.
|
||||
|
||||
3. **High-Level APIs**:
|
||||
- `Client` (`src/client/index.ts`) - Low-level client extending Protocol with typed methods for all MCP operations
|
||||
- `Server` (`src/server/index.ts`) - Low-level server extending Protocol with request handler registration
|
||||
- `McpServer` (`src/server/mcp.ts`) - High-level server API with simplified resource/tool/prompt registration
|
||||
|
||||
### Transport System
|
||||
|
||||
Transports (`src/shared/transport.ts`) provide the communication layer:
|
||||
|
||||
- **Streamable HTTP** (`src/server/streamableHttp.ts`, `src/client/streamableHttp.ts`) - Recommended transport for remote servers, supports SSE for streaming
|
||||
- **SSE** (`src/server/sse.ts`, `src/client/sse.ts`) - Legacy HTTP+SSE transport for backwards compatibility
|
||||
- **stdio** (`src/server/stdio.ts`, `src/client/stdio.ts`) - For local process-spawned integrations
|
||||
|
||||
### Server-Side Features
|
||||
|
||||
- **Tools/Resources/Prompts**: Registered via `McpServer.tool()`, `.resource()`, `.prompt()` methods
|
||||
- **OAuth/Auth**: Full OAuth 2.0 server implementation in `src/server/auth/`
|
||||
- **Completions**: Auto-completion support via `src/server/completable.ts`
|
||||
|
||||
### Client-Side Features
|
||||
|
||||
- **Auth**: OAuth client support in `src/client/auth.ts` and `src/client/auth-extensions.ts`
|
||||
- **Middleware**: Request middleware in `src/client/middleware.ts`
|
||||
- **Sampling**: Clients can handle `sampling/createMessage` requests from servers (LLM completions)
|
||||
- **Elicitation**: Clients can handle `elicitation/create` requests for user input (form or URL mode)
|
||||
- **Roots**: Clients can expose filesystem roots to servers via `roots/list`
|
||||
|
||||
### Experimental Features
|
||||
|
||||
Located in `src/experimental/`:
|
||||
|
||||
- **Tasks**: Long-running task support with polling/resumption (`src/experimental/tasks/`)
|
||||
|
||||
### Zod Compatibility
|
||||
|
||||
The SDK uses `zod/v4` internally but supports both v3 and v4 APIs. Compatibility utilities:
|
||||
|
||||
- `src/server/zod-compat.ts` - Schema parsing helpers that work across versions
|
||||
- `src/server/zod-json-schema-compat.ts` - Converts Zod schemas to JSON Schema
|
||||
|
||||
### Validation
|
||||
|
||||
Pluggable JSON Schema validation (`src/validation/`):
|
||||
|
||||
- `ajv-provider.ts` - Default Ajv-based validator
|
||||
- `cfworker-provider.ts` - Cloudflare Workers-compatible alternative
|
||||
|
||||
### Examples
|
||||
|
||||
Runnable examples in `src/examples/`:
|
||||
|
||||
- `server/` - Various server configurations (stateful, stateless, OAuth, etc.)
|
||||
- `client/` - Client examples (basic, OAuth, parallel calls, etc.)
|
||||
- `shared/` - Shared utilities like in-memory event store
|
||||
|
||||
## Message Flow (Bidirectional Protocol)
|
||||
|
||||
MCP is bidirectional: both client and server can send requests. Understanding this flow is essential when implementing new request types.
|
||||
|
||||
### Class Hierarchy
|
||||
|
||||
```
|
||||
Protocol (abstract base)
|
||||
├── Client (src/client/index.ts) - can send requests TO server, handle requests FROM server
|
||||
└── Server (src/server/index.ts) - can send requests TO client, handle requests FROM client
|
||||
└── McpServer (src/server/mcp.ts) - high-level wrapper around Server
|
||||
```
|
||||
|
||||
### Outbound Flow: Sending Requests
|
||||
|
||||
When code calls `client.callTool()` or `server.createMessage()`:
|
||||
|
||||
1. **High-level method** (e.g., `Client.callTool()`) calls `this.request()`
|
||||
2. **`Protocol.request()`**:
|
||||
- Assigns unique message ID
|
||||
- Checks capabilities via `assertCapabilityForMethod()` (abstract, implemented by Client/Server)
|
||||
- Creates response handler promise
|
||||
- Calls `transport.send()` with JSON-RPC request
|
||||
- Waits for response handler to resolve
|
||||
3. **Transport** serializes and sends over wire (HTTP, stdio, etc.)
|
||||
4. **`Protocol._onresponse()`** resolves the promise when response arrives
|
||||
|
||||
### Inbound Flow: Handling Requests
|
||||
|
||||
When a request arrives from the remote side:
|
||||
|
||||
1. **Transport** receives message, calls `transport.onmessage()`
|
||||
2. **`Protocol.connect()`** routes to `_onrequest()`, `_onresponse()`, or `_onnotification()`
|
||||
3. **`Protocol._onrequest()`**:
|
||||
- Looks up handler in `_requestHandlers` map (keyed by method name)
|
||||
- Creates `RequestHandlerExtra` with `signal`, `sessionId`, `sendNotification`, `sendRequest`
|
||||
- Invokes handler, sends JSON-RPC response back via transport
|
||||
4. **Handler** was registered via `setRequestHandler(Schema, handler)`
|
||||
|
||||
### Handler Registration
|
||||
|
||||
```typescript
|
||||
// In Client (for server→client requests like sampling, elicitation)
|
||||
client.setRequestHandler(CreateMessageRequestSchema, async (request, extra) => {
|
||||
// Handle sampling request from server
|
||||
return { role: "assistant", content: {...}, model: "..." };
|
||||
});
|
||||
|
||||
// In Server (for client→server requests like tools/call)
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
||||
// Handle tool call from client
|
||||
return { content: [...] };
|
||||
});
|
||||
```
|
||||
|
||||
### Request Handler Extra
|
||||
|
||||
The `extra` parameter in handlers (`RequestHandlerExtra`) provides:
|
||||
|
||||
- `signal`: AbortSignal for cancellation
|
||||
- `sessionId`: Transport session identifier
|
||||
- `authInfo`: Validated auth token info (if authenticated)
|
||||
- `requestId`: JSON-RPC message ID
|
||||
- `sendNotification(notification)`: Send related notification back
|
||||
- `sendRequest(request, schema)`: Send related request (for bidirectional flows)
|
||||
- `taskStore`: Task storage interface (if tasks enabled)
|
||||
|
||||
### Capability Checking
|
||||
|
||||
Both sides declare capabilities during initialization. The SDK enforces these:
|
||||
|
||||
- **Client→Server**: `Client.assertCapabilityForMethod()` checks `_serverCapabilities`
|
||||
- **Server→Client**: `Server.assertCapabilityForMethod()` checks `_clientCapabilities`
|
||||
- **Handler registration**: `assertRequestHandlerCapability()` validates local capabilities
|
||||
|
||||
### Adding a New Request Type
|
||||
|
||||
1. **Define schema** in `src/types.ts` (request params, result schema)
|
||||
2. **Add capability** to `ClientCapabilities` or `ServerCapabilities` in types
|
||||
3. **Implement sender** method in Client or Server class
|
||||
4. **Add capability check** in the appropriate `assertCapabilityForMethod()`
|
||||
5. **Register handler** on the receiving side with `setRequestHandler()`
|
||||
6. **For McpServer**: Add high-level wrapper method if needed
|
||||
|
||||
### Server-Initiated Requests (Sampling, Elicitation)
|
||||
|
||||
Server can request actions from client (requires client capability):
|
||||
|
||||
```typescript
|
||||
// Server sends sampling request to client
|
||||
const result = await server.createMessage({
|
||||
messages: [...],
|
||||
maxTokens: 100
|
||||
});
|
||||
|
||||
// Client must have registered handler:
|
||||
client.setRequestHandler(CreateMessageRequestSchema, async (request, extra) => {
|
||||
// Client-side LLM call
|
||||
return { role: "assistant", content: {...} };
|
||||
});
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### Request Handler Registration (Low-Level Server)
|
||||
|
||||
```typescript
|
||||
server.setRequestHandler(SomeRequestSchema, async (request, extra) => {
|
||||
// extra contains sessionId, authInfo, sendNotification, etc.
|
||||
return {
|
||||
/* result */
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
### Tool Registration (High-Level McpServer)
|
||||
|
||||
```typescript
|
||||
mcpServer.tool('tool-name', { param: z.string() }, async ({ param }, extra) => {
|
||||
return { content: [{ type: 'text', text: 'result' }] };
|
||||
});
|
||||
```
|
||||
|
||||
### Transport Connection
|
||||
|
||||
```typescript
|
||||
// Server
|
||||
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
|
||||
await server.connect(transport);
|
||||
|
||||
// Client
|
||||
const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp'));
|
||||
await client.connect(transport);
|
||||
```
|
||||
|
|
|
|||
Loading…
Reference in a new issue