What happened?
The v1-to-v2 codemod's project-type inference treats any quoted MCP SDK path as an import, even when the path appears only in an ordinary string literal.
In a client-only project with:
- a real client import in
client.ts,
- a shared type import in
shared.ts, and
- a server SDK path stored as example text in
example.ts,
the codemod classifies the project as using both client and server code.
Running:
pnpm --filter @modelcontextprotocol/codemod exec tsx src/cli.ts v1-to-v2 /tmp/fixture --transforms imports --verbose
rewrites the shared import to:
import type { CallToolResult } from '@modelcontextprotocol/server';
It also adds both @modelcontextprotocol/client and @modelcontextprotocol/server to package.json.
The string in example.ts is never imported or executed:
const serverImportExample = '@modelcontextprotocol/sdk/server/mcp.js';
The source scanner uses regular expressions that match quoted paths anywhere in a file. It does not verify that the match belongs to an import, export, dynamic import, or require() call.
I added this focused regression test locally:
it('ignores an SDK subpath that appears only in a string literal', () => {
const dir = v1Project({
'a.ts': [
`import { Client } from '@modelcontextprotocol/sdk/client/index.js';`,
`const example = '@modelcontextprotocol/sdk/server/mcp.js';`,
''
].join('\n')
});
expect(analyzeProject(dir).projectType).toBe('client');
});
Running:
pnpm --filter @modelcontextprotocol/codemod test -- projectAnalyzer.test.ts
produces one focused failure while the other 629 tests pass:
Expected: "client"
Received: "both"
What did you expect?
Only actual module specifiers should affect project-type inference.
The client-only fixture should remain classified as client. Its shared type import should move to @modelcontextprotocol/client, and the codemod should not add @modelcontextprotocol/server.
Code to reproduce
// client.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
void Client;
// shared.ts
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
declare const result: CallToolResult;
void result;
// example.ts
const serverImportExample = '@modelcontextprotocol/sdk/server/mcp.js';
void serverImportExample;
Use a package.json containing:
{
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
}
}
Then run the v1-to-v2 codemod with the imports transform.
SDK version
@modelcontextprotocol/codemod@2.0.0, tested from main commit 5119ee7fd7790e335a3fb60ef36f85334e2a6326.
Area
Migration / codemod
What happened?
The v1-to-v2 codemod's project-type inference treats any quoted MCP SDK path as an import, even when the path appears only in an ordinary string literal.
In a client-only project with:
client.ts,shared.ts, andexample.ts,the codemod classifies the project as using both client and server code.
Running:
pnpm --filter @modelcontextprotocol/codemod exec tsx src/cli.ts v1-to-v2 /tmp/fixture --transforms imports --verboserewrites the shared import to:
It also adds both
@modelcontextprotocol/clientand@modelcontextprotocol/servertopackage.json.The string in
example.tsis never imported or executed:The source scanner uses regular expressions that match quoted paths anywhere in a file. It does not verify that the match belongs to an import, export, dynamic import, or
require()call.I added this focused regression test locally:
Running:
pnpm --filter @modelcontextprotocol/codemod test -- projectAnalyzer.test.tsproduces one focused failure while the other 629 tests pass:
What did you expect?
Only actual module specifiers should affect project-type inference.
The client-only fixture should remain classified as
client. Its shared type import should move to@modelcontextprotocol/client, and the codemod should not add@modelcontextprotocol/server.Code to reproduce
Use a
package.jsoncontaining:{ "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0" } }Then run the v1-to-v2 codemod with the
importstransform.SDK version
@modelcontextprotocol/codemod@2.0.0, tested frommaincommit5119ee7fd7790e335a3fb60ef36f85334e2a6326.Area
Migration / codemod