import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import * as ts from "typescript";
import { TypeIndexer } from "./type-indexer.js";
import { TypeValidator } from "./type-validator.js";
import { WorkspaceDetector } from "./workspace-detector.js";
import { ProjectConfig } from "./types.js";
// Define proper tool argument types
interface ToolArguments {
lookup_type: { typeName: string; packageName?: string };
validate_type_usage: { code: string; expectedType?: string };
find_interfaces: { pattern: string };
get_package_types: { packageName: string };
validate_interface_implementation: {
implementation: string;
interfaceName: string;
interfaceDefinition: string;
};
check_type_compatibility: { sourceType: string; targetType: string };
reinitialize_indexer: { workingDir?: string };
}
export class TypeScriptDefinitionsMCPServer {
private server: Server;
private typeIndexer: TypeIndexer;
private typeValidator: TypeValidator;
private config: ProjectConfig;
constructor(workingDir?: string) {
this.config = WorkspaceDetector.detectProjectConfig(workingDir);
this.typeIndexer = new TypeIndexer(this.config);
this.typeValidator = new TypeValidator(this.getCompilerOptions());
this.server = new Server({
name: "@claude-code/typescript-definitions-mcp",
version: "0.1.0",
});
this.setupToolHandlers();
}
private getCompilerOptions(): ts.CompilerOptions {
// This would ideally read from the project's tsconfig
return {
target: ts.ScriptTarget.ES2020,
module: ts.ModuleKind.CommonJS,
lib: ["ES2020", "DOM"],
declaration: true,
esModuleInterop: true,
skipLibCheck: true,
strict: true,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
allowSyntheticDefaultImports: true,
resolveJsonModule: true
};
}
// Type-safe argument validation
private validateArgs<T>(args: unknown): T {
if (!args || typeof args !== 'object') {
throw new Error('Invalid arguments: expected object');
}
return args as T;
}
private setupToolHandlers(): void {
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "lookup_type",
description: "Look up TypeScript type definitions by name and optional package",
inputSchema: {
type: "object",
properties: {
typeName: {
type: "string",
description: "The name of the type to look up"
},
packageName: {
type: "string",
description: "Optional package name to filter results"
}
},
required: ["typeName"]
}
},
{
name: "validate_type_usage",
description: "Validate TypeScript code for type correctness",
inputSchema: {
type: "object",
properties: {
code: {
type: "string",
description: "The TypeScript code to validate"
},
expectedType: {
type: "string",
description: "Optional expected type to validate against"
}
},
required: ["code"]
}
},
{
name: "find_interfaces",
description: "Find interfaces matching a pattern",
inputSchema: {
type: "object",
properties: {
pattern: {
type: "string",
description: "Pattern to match interface names (supports wildcards with *)"
}
},
required: ["pattern"]
}
},
{
name: "get_package_types",
description: "Get all type definitions from a specific package",
inputSchema: {
type: "object",
properties: {
packageName: {
type: "string",
description: "Name of the package to get types from"
}
},
required: ["packageName"]
}
},
{
name: "validate_interface_implementation",
description: "Validate if code correctly implements an interface",
inputSchema: {
type: "object",
properties: {
implementation: {
type: "string",
description: "The implementation code to validate"
},
interfaceName: {
type: "string",
description: "Name of the interface being implemented"
},
interfaceDefinition: {
type: "string",
description: "The interface definition"
}
},
required: ["implementation", "interfaceName", "interfaceDefinition"]
}
},
{
name: "check_type_compatibility",
description: "Check if two types are compatible/assignable",
inputSchema: {
type: "object",
properties: {
sourceType: {
type: "string",
description: "The source type"
},
targetType: {
type: "string",
description: "The target type"
}
},
required: ["sourceType", "targetType"]
}
},
{
name: "get_project_info",
description: "Get information about the current TypeScript project",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false
}
},
{
name: "reinitialize_indexer",
description: "Reinitialize the type indexer (useful after package installations)",
inputSchema: {
type: "object",
properties: {
workingDir: {
type: "string",
description: "Optional working directory to reinitialize from"
}
},
additionalProperties: false
}
}
] as Tool[]
};
});
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "lookup_type": {
const lookupArgs = this.validateArgs<ToolArguments["lookup_type"]>(args);
return await this.handleLookupType(lookupArgs.typeName, lookupArgs.packageName);
}
case "validate_type_usage": {
const validateArgs = this.validateArgs<ToolArguments["validate_type_usage"]>(args);
return await this.handleValidateTypeUsage(validateArgs.code, validateArgs.expectedType);
}
case "find_interfaces": {
const interfaceArgs = this.validateArgs<ToolArguments["find_interfaces"]>(args);
return await this.handleFindInterfaces(interfaceArgs.pattern);
}
case "get_package_types": {
const packageArgs = this.validateArgs<ToolArguments["get_package_types"]>(args);
return await this.handleGetPackageTypes(packageArgs.packageName);
}
case "validate_interface_implementation": {
const implArgs = this.validateArgs<ToolArguments["validate_interface_implementation"]>(args);
return await this.handleValidateInterfaceImplementation(
implArgs.implementation,
implArgs.interfaceName,
implArgs.interfaceDefinition
);
}
case "check_type_compatibility": {
const compatArgs = this.validateArgs<ToolArguments["check_type_compatibility"]>(args);
return await this.handleCheckTypeCompatibility(
compatArgs.sourceType,
compatArgs.targetType
);
}
case "get_project_info":
return await this.handleGetProjectInfo();
case "reinitialize_indexer": {
const reinitArgs = this.validateArgs<ToolArguments["reinitialize_indexer"]>(args);
return await this.handleReinitializeIndexer(reinitArgs.workingDir);
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: `Error: ${errorMessage}`
}
],
isError: true
};
}
});
}
private async handleLookupType(typeName: string, packageName?: string) {
const results = await this.typeIndexer.findType(typeName, packageName);
return {
content: [
{
type: "text",
text: JSON.stringify({
query: { typeName, packageName },
results,
count: results.length
}, null, 2)
}
]
};
}
private async handleValidateTypeUsage(code: string, expectedType?: string) {
const result = this.typeValidator.validateTypeUsage(code, expectedType);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
private async handleFindInterfaces(pattern: string) {
const results = await this.typeIndexer.findInterfaces(pattern);
return {
content: [
{
type: "text",
text: JSON.stringify({
pattern,
results,
count: results.length
}, null, 2)
}
]
};
}
private async handleGetPackageTypes(packageName: string) {
const results = await this.typeIndexer.getPackageTypes(packageName);
return {
content: [
{
type: "text",
text: JSON.stringify({
packageName,
results,
count: results.length
}, null, 2)
}
]
};
}
private async handleValidateInterfaceImplementation(
implementation: string,
interfaceName: string,
interfaceDefinition: string
) {
const result = this.typeValidator.validateInterfaceImplementation(
implementation,
interfaceName,
interfaceDefinition
);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
private async handleCheckTypeCompatibility(sourceType: string, targetType: string) {
const result = this.typeValidator.checkTypeCompatibility(sourceType, targetType);
return {
content: [
{
type: "text",
text: JSON.stringify({
sourceType,
targetType,
...result
}, null, 2)
}
]
};
}
private async handleGetProjectInfo() {
const packages = WorkspaceDetector.getInstalledPackages(this.config.rootPath);
return {
content: [
{
type: "text",
text: JSON.stringify({
config: this.config,
installedPackages: packages.length,
packagesWithTypes: packages.filter(p => p.hasTypes).length,
packages: packages.slice(0, 20) // Limit to first 20 for readability
}, null, 2)
}
]
};
}
private async handleReinitializeIndexer(workingDir?: string) {
try {
if (workingDir) {
this.config = WorkspaceDetector.detectProjectConfig(workingDir);
this.typeIndexer = new TypeIndexer(this.config);
}
await this.typeIndexer.initialize();
return {
content: [
{
type: "text",
text: JSON.stringify({
message: "Type indexer reinitialized successfully",
config: this.config
}, null, 2)
}
]
};
} catch (error) {
throw new Error(`Failed to reinitialize indexer: ${error instanceof Error ? error.message : String(error)}`);
}
}
async initialize(): Promise<void> {
await this.typeIndexer.initialize();
}
async run(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("TypeScript Definitions MCP Server running on stdio");
}
}