|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +mcp-framework is a TypeScript framework for building Model Context Protocol (MCP) servers. It provides an opinionated architecture with automatic directory-based discovery for tools, resources, and prompts. The framework is used as a dependency in other projects (similar to Express.js) and runs from node_modules. |
| 8 | + |
| 9 | +## Development Commands |
| 10 | + |
| 11 | +### Build and Watch |
| 12 | +```bash |
| 13 | +npm run build # Compile TypeScript to dist/ |
| 14 | +npm run watch # Watch mode for development |
| 15 | +``` |
| 16 | + |
| 17 | +### Testing |
| 18 | +```bash |
| 19 | +npm test # Run all tests |
| 20 | +npm run test:watch # Run tests in watch mode |
| 21 | +npm run test:coverage # Run tests with coverage report |
| 22 | +``` |
| 23 | + |
| 24 | +### Linting and Formatting |
| 25 | +```bash |
| 26 | +npm run lint # Run ESLint |
| 27 | +npm run lint:fix # Run ESLint with auto-fix |
| 28 | +npm run format # Format code with Prettier |
| 29 | +``` |
| 30 | + |
| 31 | +### Local Development with yalc |
| 32 | +```bash |
| 33 | +npm run dev:pub # Build and publish to yalc for local testing |
| 34 | +``` |
| 35 | + |
| 36 | +### CLI Commands (for projects using the framework) |
| 37 | +```bash |
| 38 | +mcp create <project-name> # Create new MCP server project |
| 39 | +mcp add tool <tool-name> # Add a new tool |
| 40 | +mcp add prompt <prompt-name> # Add a new prompt |
| 41 | +mcp add resource <resource-name> # Add a new resource |
| 42 | +mcp validate # Validate tool schemas |
| 43 | +mcp-build # Build project (used in build scripts) |
| 44 | +``` |
| 45 | + |
| 46 | +## Architecture |
| 47 | + |
| 48 | +### Core Components |
| 49 | + |
| 50 | +1. **MCPServer** ([src/core/MCPServer.ts](src/core/MCPServer.ts)) |
| 51 | + - Main server class that orchestrates everything |
| 52 | + - Handles capability detection (tools, prompts, resources) |
| 53 | + - Manages transport configuration (stdio, SSE, HTTP stream) |
| 54 | + - Loads and validates tools/prompts/resources on startup |
| 55 | + - Resolves basePath from config or process.argv[1] or process.cwd() |
| 56 | + |
| 57 | +2. **Loaders** ([src/loaders/](src/loaders/)) |
| 58 | + - ToolLoader, PromptLoader, ResourceLoader |
| 59 | + - Automatically discover and load implementations from directories |
| 60 | + - Look for files in `<basePath>/tools`, `<basePath>/prompts`, `<basePath>/resources` |
| 61 | + - Load from compiled JS in dist/ (not from src/) |
| 62 | + |
| 63 | +3. **Base Classes** |
| 64 | + - **MCPTool** ([src/tools/BaseTool.ts](src/tools/BaseTool.ts)) - Base for all tools |
| 65 | + - **BasePrompt** ([src/prompts/BasePrompt.ts](src/prompts/BasePrompt.ts)) - Base for prompts |
| 66 | + - **BaseResource** ([src/resources/BaseResource.ts](src/resources/BaseResource.ts)) - Base for resources |
| 67 | + |
| 68 | +4. **Transport Layer** ([src/transports/](src/transports/)) |
| 69 | + - stdio: Standard input/output (default) |
| 70 | + - SSE: Server-Sent Events transport |
| 71 | + - HTTP Stream: HTTP-based streaming with session management |
| 72 | + |
| 73 | +### Tool Schema System |
| 74 | + |
| 75 | +The framework uses Zod schemas with **mandatory descriptions** for all fields: |
| 76 | + |
| 77 | +```typescript |
| 78 | +const schema = z.object({ |
| 79 | + message: z.string().describe("Message to process"), // Description is required |
| 80 | + count: z.number().optional().describe("Optional count") |
| 81 | +}); |
| 82 | + |
| 83 | +class MyTool extends MCPTool { |
| 84 | + name = "my_tool"; |
| 85 | + description = "Tool description"; |
| 86 | + schema = schema; |
| 87 | + |
| 88 | + async execute(input: MCPInput<this>) { |
| 89 | + // input is fully typed from schema |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +**Validation occurs at multiple levels:** |
| 95 | +- Build-time: `npm run build` validates all schemas |
| 96 | +- Development: `defineSchema()` helper validates immediately |
| 97 | +- Standalone: `mcp validate` command |
| 98 | +- Runtime: Server validates on startup |
| 99 | + |
| 100 | +Missing descriptions will cause build failures. Skip with `MCP_SKIP_TOOL_VALIDATION=true` (not recommended). |
| 101 | + |
| 102 | +### Path Resolution |
| 103 | + |
| 104 | +Since mcp-framework runs from node_modules: |
| 105 | +- `basePath` is resolved from config, process.argv[1], or process.cwd() |
| 106 | +- Loaders search for tools/prompts/resources relative to basePath |
| 107 | +- Framework code uses `import.meta.url` for its own files |
| 108 | +- Projects using the framework have tools/prompts/resources in their own directory structure |
| 109 | + |
| 110 | +## Key Technical Details |
| 111 | + |
| 112 | +### Module System |
| 113 | +- ESM modules (type: "module" in package.json) |
| 114 | +- TypeScript config: module="Node16", moduleResolution="Node16" |
| 115 | +- All imports use .js extensions (even for .ts files) |
| 116 | +- Jest configured for ESM with ts-jest |
| 117 | + |
| 118 | +### Authentication |
| 119 | + |
| 120 | +The framework supports three authentication providers for SSE and HTTP Stream transports: |
| 121 | + |
| 122 | +#### OAuth 2.1 Authentication (Recommended for Production) |
| 123 | + |
| 124 | +OAuth 2.1 authentication per MCP specification (2025-06-18) with RFC compliance: |
| 125 | + |
| 126 | +**Components:** |
| 127 | +- **OAuthAuthProvider** ([src/auth/providers/oauth.ts](src/auth/providers/oauth.ts)): Main provider implementing AuthProvider interface |
| 128 | +- **JWTValidator** ([src/auth/validators/jwt-validator.ts](src/auth/validators/jwt-validator.ts)): Async JWT validation with JWKS support |
| 129 | +- **IntrospectionValidator** ([src/auth/validators/introspection-validator.ts](src/auth/validators/introspection-validator.ts)): OAuth token introspection (RFC 7662) |
| 130 | +- **ProtectedResourceMetadata** ([src/auth/metadata/protected-resource.ts](src/auth/metadata/protected-resource.ts)): RFC 9728 metadata generation |
| 131 | + |
| 132 | +**Metadata Endpoint:** |
| 133 | +- Path: `/.well-known/oauth-protected-resource` |
| 134 | +- Public (no auth required) |
| 135 | +- Returns authorization server URLs and resource identifier |
| 136 | +- Automatically served by SSE and HTTP Stream transports when OAuth is configured |
| 137 | + |
| 138 | +**Token Validation Strategies:** |
| 139 | + |
| 140 | +1. **JWT Validation** (recommended for performance): |
| 141 | + - Fetches public keys from JWKS endpoint |
| 142 | + - Validates: signature, expiration, audience, issuer, nbf |
| 143 | + - JWKS key caching for 15 minutes (configurable) |
| 144 | + - Supports RS256 and ES256 algorithms |
| 145 | + - Fast: ~5-10ms per request (cached keys) |
| 146 | + |
| 147 | +2. **Token Introspection** (recommended for real-time revocation): |
| 148 | + - Calls authorization server's introspection endpoint (RFC 7662) |
| 149 | + - Validates: active status, expiration, audience, issuer |
| 150 | + - Caches results for 5 minutes (configurable) |
| 151 | + - Allows immediate token revocation |
| 152 | + - Slower: ~20-50ms per request (cached) |
| 153 | + |
| 154 | +**Security Features:** |
| 155 | +- Tokens must be in Authorization header (Bearer scheme) |
| 156 | +- Tokens in query strings rejected automatically (security requirement) |
| 157 | +- Audience validation prevents token reuse across services |
| 158 | +- WWW-Authenticate challenges per RFC 6750 |
| 159 | +- Comprehensive logging of authentication events |
| 160 | + |
| 161 | +**Configuration Example:** |
| 162 | +```typescript |
| 163 | +import { OAuthAuthProvider } from 'mcp-framework'; |
| 164 | + |
| 165 | +// JWT validation |
| 166 | +const provider = new OAuthAuthProvider({ |
| 167 | + authorizationServers: ['https://auth.example.com'], |
| 168 | + resource: 'https://mcp.example.com', |
| 169 | + validation: { |
| 170 | + type: 'jwt', |
| 171 | + jwksUri: 'https://auth.example.com/.well-known/jwks.json', |
| 172 | + audience: 'https://mcp.example.com', |
| 173 | + issuer: 'https://auth.example.com' |
| 174 | + } |
| 175 | +}); |
| 176 | + |
| 177 | +// Token introspection |
| 178 | +const provider = new OAuthAuthProvider({ |
| 179 | + authorizationServers: ['https://auth.example.com'], |
| 180 | + resource: 'https://mcp.example.com', |
| 181 | + validation: { |
| 182 | + type: 'introspection', |
| 183 | + audience: 'https://mcp.example.com', |
| 184 | + issuer: 'https://auth.example.com', |
| 185 | + introspection: { |
| 186 | + endpoint: 'https://auth.example.com/oauth/introspect', |
| 187 | + clientId: 'mcp-server', |
| 188 | + clientSecret: process.env.CLIENT_SECRET |
| 189 | + } |
| 190 | + } |
| 191 | +}); |
| 192 | +``` |
| 193 | + |
| 194 | +**Integration:** Works with Auth0, Okta, AWS Cognito, Azure AD/Entra ID, and any RFC-compliant OAuth 2.1 server. See [docs/OAUTH.md](docs/OAUTH.md) for detailed setup guides. |
| 195 | + |
| 196 | +#### JWT Authentication (Simple Token-Based) |
| 197 | + |
| 198 | +- **JWTAuthProvider**: Token-based auth with configurable algorithms (HS256, RS256, etc.) |
| 199 | +- Simpler than OAuth, suitable for internal services |
| 200 | +- No automatic metadata endpoint |
| 201 | + |
| 202 | +#### API Key Authentication |
| 203 | + |
| 204 | +- **APIKeyAuthProvider**: Simple key-based auth |
| 205 | +- Good for development and testing |
| 206 | +- Not recommended for production |
| 207 | + |
| 208 | +#### Custom Authentication |
| 209 | + |
| 210 | +- **AuthProvider interface**: Implement custom authentication logic |
| 211 | +- Async authenticate method returns boolean or AuthResult with claims |
| 212 | +- getAuthError method provides error responses |
| 213 | + |
| 214 | +### Transport Features |
| 215 | +- **SSE**: CORS configuration, optional auth on endpoints |
| 216 | +- **HTTP Stream**: |
| 217 | + - Response modes: "batch" (default) or "stream" |
| 218 | + - Session management with configurable headers |
| 219 | + - Stream resumability for missed messages |
| 220 | + - Batch request/response support |
| 221 | + |
| 222 | +### CLI Templates |
| 223 | +The CLI uses templates ([src/cli/templates/](src/cli/templates/)) to scaffold new projects and components. These templates are used by the `mcp create` and `mcp add` commands. |
| 224 | + |
| 225 | +### Logging |
| 226 | +- Logger utility in [src/core/Logger.ts](src/core/Logger.ts) |
| 227 | +- Environment variables: |
| 228 | + - `MCP_ENABLE_FILE_LOGGING`: Enable file logging (default: false) |
| 229 | + - `MCP_LOG_DIRECTORY`: Log directory (default: "logs") |
| 230 | + - `MCP_DEBUG_CONSOLE`: Show debug messages in console (default: false) |
| 231 | + |
| 232 | +## Testing |
| 233 | + |
| 234 | +Tests are in the `tests/` directory with the pattern `*.test.ts`. The project uses Jest with ts-jest for ESM support. Run tests with: |
| 235 | +- `NODE_OPTIONS='--experimental-vm-modules' jest` |
| 236 | +- Or use npm scripts: `npm test`, `npm run test:watch`, `npm run test:coverage` |
| 237 | + |
| 238 | +## Important Notes |
| 239 | + |
| 240 | +- All tool schema fields must have descriptions (enforced at build time) |
| 241 | +- The framework is meant to be used as a dependency, not modified directly |
| 242 | +- When testing locally, use `yalc` for linking instead of npm link |
| 243 | +- Transport layer is pluggable - choose stdio (default), SSE, or HTTP stream based on use case |
| 244 | +- Server performs validation on startup - tools with invalid schemas will prevent server start |
0 commit comments