Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
- **TypeScript**: Strict type checking, ES modules, explicit return types
- **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
- **Imports**: ES module style, no `.js` extension on relative imports (project uses `moduleResolution: bundler`), group imports logically

Check warning on line 39 in CLAUDE.md

View check run for this annotation

Claude / Claude Code Review

Stale eslint-config comment still references NodeNext-style .js imports

The rationale comment at `common/eslint-config/eslint.config.mjs:39` ("Let the TS resolver handle NodeNext-style imports like \"./foo.js\"") now contradicts the import convention this PR establishes — CLAUDE.md was updated to say "no `.js` extension on relative imports (project uses `moduleResolution: bundler`)". The `extensions` resolver setting itself is still useful; only the comment is stale and should be updated or removed so future contributors aren't told the project still writes `./foo.j
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The rationale comment at common/eslint-config/eslint.config.mjs:39 ("Let the TS resolver handle NodeNext-style imports like "./foo.js"") now contradicts the import convention this PR establishes — CLAUDE.md was updated to say "no .js extension on relative imports (project uses moduleResolution: bundler)". The extensions resolver setting itself is still useful; only the comment is stale and should be updated or removed so future contributors aren't told the project still writes ./foo.js imports.

Extended reasoning...

What the bug is

This PR flips the project's module resolution from NodeNext to bundler and strips .js extensions from every relative TypeScript import across packages/, examples/, scripts/, and test/. It also updates CLAUDE.md line 39 to document the new convention: "ES module style, no .js extension on relative imports (project uses moduleResolution: bundler)". However, common/eslint-config/eslint.config.mjs was not touched, and line 39 there still reads:

// Let the TS resolver handle NodeNext-style imports like "./foo.js"
extensions: ['.js', '.jsx', '.ts', '.tsx', '.d.ts'],

After this PR there are no NodeNext-style ./foo.js relative imports left in the repo (the PR's stated goal was to remove them all), so the comment now describes a convention the project explicitly migrated away from.

How it manifests

A contributor reading the eslint config — for example, while debugging an import-resolution lint error — would conclude from this comment that the project still writes relative imports with a .js extension and that the resolver setting exists to support that. That conclusion is the opposite of what CLAUDE.md now says and what every .ts file in the repo now does. Two pieces of in-repo documentation now actively contradict each other.

Why nothing catches it

The comment is plain prose inside a .mjs config file. pnpm lint:all and pnpm typecheck:all only verify code behavior, not whether comments accurately describe the surrounding configuration. There is no sync:snippets-style guard for config-file comments.

Step-by-step proof

  1. Before this PR, CLAUDE.md line 39 said: "ES module style, include .js extension" — consistent with the eslint comment.
  2. This PR changes CLAUDE.md line 39 to: "ES module style, no .js extension on relative imports (project uses moduleResolution: bundler)".
  3. This PR also changes common/tsconfig/tsconfig.json from module: NodeNext / moduleResolution: NodeNext to module: ESNext / moduleResolution: bundler.
  4. common/eslint-config/eslint.config.mjs:39 is unchanged: // Let the TS resolver handle NodeNext-style imports like "./foo.js".
  5. Result: the eslint config's documented rationale references a NodeNext convention the project no longer uses, directly contradicting the freshly-updated CLAUDE.md.

Why the extensions setting itself is fine

The extensions array tells eslint-import-resolver-typescript which file extensions to try when resolving an import specifier to a source file on disk. Even with extensionless imports under moduleResolution: bundler, the resolver still needs this list to map ./foo to foo.ts / foo.tsx / etc. So the setting should stay; only the reason given for it is stale.

Impact and fix

No functional impact — purely documentation drift. But the PR explicitly migrates the project's import convention and updates CLAUDE.md to match; leaving a contradicting comment in a sibling config file is exactly the kind of partial migration that confuses the next contributor. Update the comment to something like:

// Tell the TS resolver which file extensions to probe when resolving extensionless
// relative imports (project uses moduleResolution: bundler).
extensions: ['.js', '.jsx', '.ts', '.tsx', '.d.ts'],

or simply remove the comment, since the setting is fairly self-explanatory.

- **Formatting**: 2-space indentation, semicolons required, single quotes preferred
- **Testing**: Place tests under each package's `test/` directory (vitest only includes `test/**/*.test.ts`), use descriptive test names
- **Comments**: JSDoc for public APIs, inline comments for complex logic
Expand Down
4 changes: 2 additions & 2 deletions common/tsconfig/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"compilerOptions": {
"target": "esnext",
"lib": ["esnext"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"module": "ESNext",
"moduleResolution": "bundler",
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
Expand Down
16 changes: 9 additions & 7 deletions docs/client-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ Create a `tsconfig.json` in the root of your project:
```json
{
"compilerOptions": {
"target": "ES2023",
"lib": ["ES2023"],
"module": "Node16",
"moduleResolution": "Node16",
"target": "ESNext",
"lib": ["ESNext"],
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
Expand Down Expand Up @@ -236,7 +236,8 @@ Now let's add the core functionality for processing queries and handling tool ca
messages,
});

finalText.push(followUp.content[0].type === 'text' ? followUp.content[0].text : '');
const firstBlock = followUp.content[0];
finalText.push(firstBlock?.type === 'text' ? firstBlock.text : '');
}
}

Expand Down Expand Up @@ -284,13 +285,14 @@ Finally, we'll add the main execution logic:

```ts source="../examples/client-quickstart/src/index.ts#main"
async function main() {
if (process.argv.length < 3) {
const serverScriptPath = process.argv[2];
if (!serverScriptPath) {
console.log('Usage: node build/index.js <path_to_server_script>');
return;
}
const mcpClient = new MCPClient();
try {
await mcpClient.connectToServer(process.argv[2]);
await mcpClient.connectToServer(serverScriptPath);

// Check if we have a valid API key to continue
const apiKey = process.env.ANTHROPIC_API_KEY;
Expand Down
7 changes: 4 additions & 3 deletions docs/server-quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ Create a `tsconfig.json` in the root of your project:
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"target": "ESNext",
"lib": ["ESNext"],
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
Expand Down
1 change: 1 addition & 0 deletions examples/client-quickstart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@modelcontextprotocol/client": "workspace:^"
},
"devDependencies": {
"@modelcontextprotocol/tsconfig": "workspace:^",
"@types/node": "^24.10.1",
"typescript": "catalog:devTools"
}
Expand Down
8 changes: 5 additions & 3 deletions examples/client-quickstart/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ class MCPClient {
messages,
});

finalText.push(followUp.content[0].type === 'text' ? followUp.content[0].text : '');
const firstBlock = followUp.content[0];
finalText.push(firstBlock?.type === 'text' ? firstBlock.text : '');
}
}

Expand Down Expand Up @@ -156,13 +157,14 @@ class MCPClient {

//#region main
async function main() {
if (process.argv.length < 3) {
const serverScriptPath = process.argv[2];
if (!serverScriptPath) {
console.log('Usage: node build/index.js <path_to_server_script>');
return;
}
const mcpClient = new MCPClient();
try {
await mcpClient.connectToServer(process.argv[2]);
await mcpClient.connectToServer(serverScriptPath);

// Check if we have a valid API key to continue
const apiKey = process.env.ANTHROPIC_API_KEY;
Expand Down
12 changes: 4 additions & 8 deletions examples/client-quickstart/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
{
"extends": "@modelcontextprotocol/tsconfig",
"compilerOptions": {
"target": "ES2023",
"lib": ["ES2023"],
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": false,
"declarationMap": false,
"types": ["node"],
"paths": {
"@modelcontextprotocol/client": ["./node_modules/@modelcontextprotocol/client/src/index.ts"],
"@modelcontextprotocol/client/stdio": ["./node_modules/@modelcontextprotocol/client/src/stdio.ts"],
Comment thread
claude[bot] marked this conversation as resolved.
Expand Down
5 changes: 1 addition & 4 deletions examples/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@
"prepack": "pnpm run build:esm && pnpm run build:cjs",
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
"check": "pnpm run typecheck && pnpm run lint",
"start": "pnpm run server",
"server": "tsx watch --clear-screen=false scripts/cli.ts server",
"client": "tsx scripts/cli.ts client"
"check": "pnpm run typecheck && pnpm run lint"
},
"dependencies": {
"@modelcontextprotocol/client": "workspace:^",
Expand Down
2 changes: 1 addition & 1 deletion examples/client/src/elicitationUrlExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
} from '@modelcontextprotocol/client';
import open from 'open';

import { InMemoryOAuthClientProvider } from './simpleOAuthClientProvider.js';
import { InMemoryOAuthClientProvider } from './simpleOAuthClientProvider';

// Set up OAuth (required for this example)
const OAUTH_CALLBACK_PORT = 8090; // Use different port than auth server (3001)
Expand Down
2 changes: 1 addition & 1 deletion examples/client/src/simpleOAuthClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { CallToolResult, ListToolsRequest, OAuthClientMetadata } from '@mod
import { Client, StreamableHTTPClientTransport, UnauthorizedError } from '@modelcontextprotocol/client';
import open from 'open';

import { InMemoryOAuthClientProvider } from './simpleOAuthClientProvider.js';
import { InMemoryOAuthClientProvider } from './simpleOAuthClientProvider';

// Configuration
const DEFAULT_SERVER_URL = 'http://localhost:3000/mcp';
Expand Down
1 change: 1 addition & 0 deletions examples/server-quickstart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"zod": "catalog:runtimeShared"
},
"devDependencies": {
"@modelcontextprotocol/tsconfig": "workspace:^",
"@types/node": "^24.10.1",
"typescript": "catalog:devTools"
}
Expand Down
11 changes: 4 additions & 7 deletions examples/server-quickstart/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
{
"extends": "@modelcontextprotocol/tsconfig",
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": false,
"declarationMap": false,
"types": ["node"],
"paths": {
"@modelcontextprotocol/server": ["./node_modules/@modelcontextprotocol/server/src/index.ts"],
"@modelcontextprotocol/server/stdio": ["./node_modules/@modelcontextprotocol/server/src/stdio.ts"],
Expand Down
5 changes: 1 addition & 4 deletions examples/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@
"prepack": "pnpm run build:esm && pnpm run build:cjs",
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
"check": "pnpm run typecheck && pnpm run lint",
"start": "pnpm run server",
"server": "tsx watch --clear-screen=false scripts/cli.ts server",
"client": "tsx scripts/cli.ts client"
"check": "pnpm run typecheck && pnpm run lint"
},
"dependencies": {
"@hono/node-server": "catalog:runtimeServerOnly",
Expand Down
2 changes: 1 addition & 1 deletion examples/server/src/elicitationUrlExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type { Request, Response } from 'express';
import express from 'express';
import * as z from 'zod/v4';

import { InMemoryEventStore } from './inMemoryEventStore.js';
import { InMemoryEventStore } from './inMemoryEventStore';

// Create an MCP server with implementation details
const getServer = () => {
Expand Down
2 changes: 1 addition & 1 deletion examples/server/src/simpleStreamableHttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import cors from 'cors';
import type { Request, Response } from 'express';
import * as z from 'zod/v4';

import { InMemoryEventStore } from './inMemoryEventStore.js';
import { InMemoryEventStore } from './inMemoryEventStore';

// Check for OAuth flag
const useOAuth = process.argv.includes('--oauth');
Expand Down
2 changes: 1 addition & 1 deletion examples/server/src/ssePollingExample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { McpServer } from '@modelcontextprotocol/server';
import cors from 'cors';
import type { Request, Response } from 'express';

import { InMemoryEventStore } from './inMemoryEventStore.js';
import { InMemoryEventStore } from './inMemoryEventStore';

// Create a fresh MCP server per client connection to avoid shared state between clients
const getServer = () => {
Expand Down
5 changes: 1 addition & 4 deletions examples/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
"check": "pnpm run typecheck && pnpm run lint",
"test": "vitest run",
"test:watch": "vitest",
"start": "pnpm run server",
"server": "tsx watch --clear-screen=false scripts/cli.ts server",
"client": "tsx scripts/cli.ts client"
"test:watch": "vitest"
},
"dependencies": {
"@modelcontextprotocol/core": "workspace:^",
Expand Down
4 changes: 2 additions & 2 deletions examples/shared/src/authServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import cors from 'cors';
import type { Request, Response as ExpressResponse, Router } from 'express';
import express from 'express';

import type { DemoAuth } from './auth.js';
import { createDemoAuth, DEMO_USER_CREDENTIALS } from './auth.js';
import type { DemoAuth } from './auth';
import { createDemoAuth, DEMO_USER_CREDENTIALS } from './auth';

export interface SetupAuthServerOptions {
authServerUrl: URL;
Expand Down
8 changes: 4 additions & 4 deletions examples/shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Auth configuration
export type { CreateDemoAuthOptions, DemoAuth } from './auth.js';
export { createDemoAuth } from './auth.js';
export type { CreateDemoAuthOptions, DemoAuth } from './auth';
export { createDemoAuth } from './auth';

// Auth server setup + demo token verifier (pass to `requireBearerAuth` from @modelcontextprotocol/express)
export type { SetupAuthServerOptions } from './authServer.js';
export { createProtectedResourceMetadataRouter, demoTokenVerifier, getAuth, setupAuthServer } from './authServer.js';
export type { SetupAuthServerOptions } from './authServer';
export { createProtectedResourceMetadataRouter, demoTokenVerifier, getAuth, setupAuthServer } from './authServer';
4 changes: 2 additions & 2 deletions examples/shared/test/demoInMemoryOAuthProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

import { describe, expect, it } from 'vitest';

import type { CreateDemoAuthOptions } from '../src/auth.js';
import { createDemoAuth } from '../src/auth.js';
import type { CreateDemoAuthOptions } from '../src/auth';
import { createDemoAuth } from '../src/auth';

describe('createDemoAuth', () => {
const validOptions: CreateDemoAuthOptions = {
Expand Down
4 changes: 1 addition & 3 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
"check": "pnpm run typecheck && pnpm run lint",
"test": "vitest run",
"test:watch": "vitest",
"server": "tsx watch --clear-screen=false scripts/cli.ts server",
"client": "tsx scripts/cli.ts client"
"test:watch": "vitest"
},
"dependencies": {
"cross-spawn": "catalog:runtimeClientOnly",
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/client/auth.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

import type { AuthorizationServerMetadata } from '@modelcontextprotocol/core';

import type { OAuthClientProvider } from './auth.js';
import { fetchToken } from './auth.js';
import type { OAuthClientProvider } from './auth';
import { fetchToken } from './auth';

/**
* Base class providing no-op implementations of required OAuthClientProvider methods.
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/client/authExtensions.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
* @module
*/

import { ClientCredentialsProvider, createPrivateKeyJwtAuth, PrivateKeyJwtProvider } from './authExtensions.js';
import { StreamableHTTPClientTransport } from './streamableHttp.js';
import { ClientCredentialsProvider, createPrivateKeyJwtAuth, PrivateKeyJwtProvider } from './authExtensions';
import { StreamableHTTPClientTransport } from './streamableHttp';

/**
* Example: Creating a private key JWT authentication function.
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/client/authExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import type { FetchLike, OAuthClientInformation, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/core';
import type { CryptoKey, JWK } from 'jose';

import type { AddClientAuthentication, OAuthClientProvider } from './auth.js';
import type { AddClientAuthentication, OAuthClientProvider } from './auth';

/**
* Helper to produce a `private_key_jwt` client authentication function.
Expand Down
8 changes: 4 additions & 4 deletions packages/client/src/client/client.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

import type { Prompt, Resource, Tool } from '@modelcontextprotocol/core';

import { Client } from './client.js';
import { SSEClientTransport } from './sse.js';
import { StdioClientTransport } from './stdio.js';
import { StreamableHTTPClientTransport } from './streamableHttp.js';
import { Client } from './client';
import { SSEClientTransport } from './sse';
import { StdioClientTransport } from './stdio';
import { StreamableHTTPClientTransport } from './streamableHttp';

/**
* Example: Using listChanged to automatically track tool and prompt updates.
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import {
SdkErrorCode
} from '@modelcontextprotocol/core';

import { ExperimentalClientTasks } from '../experimental/tasks/client.js';
import { ExperimentalClientTasks } from '../experimental/tasks/client';

/**
* Elicitation default application helper. Applies defaults to the `data` based on the `schema`.
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/client/crossAppAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import type { FetchLike } from '@modelcontextprotocol/core';
import { IdJagTokenExchangeResponseSchema, OAuthErrorResponseSchema, OAuthTokensSchema } from '@modelcontextprotocol/core';

import type { ClientAuthMethod } from './auth.js';
import { applyClientAuthentication, discoverAuthorizationServerMetadata } from './auth.js';
import type { ClientAuthMethod } from './auth';
import { applyClientAuthentication, discoverAuthorizationServerMetadata } from './auth';

/**
* Options for requesting a JWT Authorization Grant via RFC 8693 Token Exchange.
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/client/middleware.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
* @module
*/

import type { Middleware } from './middleware.js';
import { applyMiddlewares, createMiddleware } from './middleware.js';
import type { Middleware } from './middleware';
import { applyMiddlewares, createMiddleware } from './middleware';

// Stubs for hypothetical application middleware
declare function withOAuth(provider: unknown, url: string): Middleware;
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/client/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { FetchLike } from '@modelcontextprotocol/core';

import type { OAuthClientProvider } from './auth.js';
import { auth, extractWWWAuthenticateParams, UnauthorizedError } from './auth.js';
import type { OAuthClientProvider } from './auth';
import { auth, extractWWWAuthenticateParams, UnauthorizedError } from './auth';

/**
* Middleware function that wraps and enhances fetch functionality.
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/client/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
import type { ErrorEvent, EventSourceInit } from 'eventsource';
import { EventSource } from 'eventsource';

import type { AuthProvider, OAuthClientProvider } from './auth.js';
import { adaptOAuthProvider, auth, extractWWWAuthenticateParams, isOAuthClientProvider, UnauthorizedError } from './auth.js';
import type { AuthProvider, OAuthClientProvider } from './auth';
import { adaptOAuthProvider, auth, extractWWWAuthenticateParams, isOAuthClientProvider, UnauthorizedError } from './auth';

export class SseError extends Error {
constructor(
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/client/streamableHttp.examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

/* eslint-disable unicorn/consistent-function-scoping -- examples must live inside region blocks */

import type { ReconnectionScheduler } from './streamableHttp.js';
import type { ReconnectionScheduler } from './streamableHttp';

// Stub for a hypothetical platform-specific background scheduling API
declare const platformBackgroundTask: {
Expand Down
Loading
Loading