diff --git a/docs/api/decorator-api.md b/docs/api/decorator-api.md index 8cd7dc8..e1e6e2a 100644 --- a/docs/api/decorator-api.md +++ b/docs/api/decorator-api.md @@ -139,7 +139,10 @@ class CatalogServer { Use `"module": "Node16"`, `"moduleResolution": "Node16"`, and `"experimentalDecorators": true` for a CommonJS consumer so TypeScript selects -the package's CJS declaration condition. The legacy entrypoint supports public +the package's CJS declaration condition. In the same compilation, static imports +of `@theorvane/type-mcp/http` and `@theorvane/type-mcp/langchain` also select +their CJS `.d.cts` declarations; install `@langchain/core` when importing the +LangChain adapter. The legacy entrypoint supports public instance methods with string names only; parameter, accessor, field, private, and symbol-named decorators are excluded. Do not mix Stage 3 and legacy decorators in one TypeScript compilation unit. diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md index 5cbe02d..95425e0 100644 --- a/docs/guides/configuration.md +++ b/docs/guides/configuration.md @@ -1,6 +1,6 @@ # Configuration and compatibility -`@theorvane/type-mcp@0.3.0` is the published TypeScript declaration and runtime package. Configuration determines whether TypeScript emits standard decorators and whether the runtime can resolve the package's ESM/CJS exports; applications configure their own hosting and transport lifecycle around installed MCP adapters. +`@theorvane/type-mcp@0.3.1` is the published TypeScript declaration and runtime package. Configuration determines whether TypeScript emits standard decorators and whether the runtime can resolve the package's ESM/CJS exports; applications configure their own hosting and transport lifecycle around installed MCP adapters. ## Runtime and package manager @@ -41,14 +41,15 @@ Projects using Babel, SWC, or another TypeScript transpiler must confirm that th ## ESM and CommonJS -The package exports both ESM and CommonJS entry points: +The package exports ESM and CommonJS runtime and declaration conditions for the root, HTTP, and LangChain entrypoints. Node-aware TypeScript resolution selects the matching declaration form: | Consumer | Root loading form | | --- | --- | | ESM / TypeScript NodeNext | `import { McpServer } from "@theorvane/type-mcp"` | +| CommonJS / TypeScript Node16 | `import { createMcpHandler } from "@theorvane/type-mcp/http"` | | CommonJS runtime | `const { McpServer } = require("@theorvane/type-mcp")` | -Decorator syntax is compiled by TypeScript before Node loads the module, so the CommonJS form does not remove the requirement for a standard decorator-compatible compiler configuration. Type-only imports should use TypeScript's `import type` form for the public definition interfaces: +For a CommonJS TypeScript project, use both `"module": "Node16"` and `"moduleResolution": "Node16"`; static imports of `@theorvane/type-mcp/http` and `@theorvane/type-mcp/langchain` then select CJS `.d.cts` declarations. `@theorvane/type-mcp/langchain` also requires its optional `@langchain/core` peer at runtime. Decorator syntax is compiled by TypeScript before Node loads the module, so the CommonJS form does not remove the requirement for a standard decorator-compatible compiler configuration. Type-only imports should use TypeScript's `import type` form for the public definition interfaces: ```ts import type { McpServerDefinition, McpToolOptions } from "@theorvane/type-mcp"; diff --git a/package-lock.json b/package-lock.json index 97dde23..7064f85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@theorvane/type-mcp", - "version": "0.3.0", + "version": "0.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@theorvane/type-mcp", - "version": "0.3.0", + "version": "0.3.1", "license": "MIT", "dependencies": { "@hono/node-server": "2.0.12", diff --git a/package.json b/package.json index 3d72a0e..c282643 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@theorvane/type-mcp", - "version": "0.3.0", + "version": "0.3.1", "description": "Decorator-first TypeScript framework for Model Context Protocol servers", "repository": { "type": "git", @@ -17,19 +17,34 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } }, "./http": { - "types": "./dist/http.d.ts", - "import": "./dist/http.js", - "require": "./dist/http.cjs" + "import": { + "types": "./dist/http.d.ts", + "default": "./dist/http.js" + }, + "require": { + "types": "./dist/http.d.cts", + "default": "./dist/http.cjs" + } }, "./langchain": { - "types": "./dist/langchain.d.ts", - "import": "./dist/langchain.js", - "require": "./dist/langchain.cjs" + "import": { + "types": "./dist/langchain.d.ts", + "default": "./dist/langchain.js" + }, + "require": { + "types": "./dist/langchain.d.cts", + "default": "./dist/langchain.cjs" + } }, "./legacy": { "import": { diff --git a/scripts/verify-legacy-consumer.mjs b/scripts/verify-legacy-consumer.mjs index 9f13f69..62f00fa 100644 --- a/scripts/verify-legacy-consumer.mjs +++ b/scripts/verify-legacy-consumer.mjs @@ -32,6 +32,7 @@ try { "--no-fund", tarballPath, "zod", + "@langchain/core@1.2.3", "@types/node", ], consumer, @@ -57,7 +58,7 @@ try { ); writeFileSync( join(consumer, "server.ts"), - `import { z } from "zod";\nimport { getMcpServerDefinition, McpServer, McpTool } from "@theorvane/type-mcp/legacy";\n\n@McpServer({ name: "legacy-catalog", version: "1.0.0" })\nclass LegacyCatalog {\n @McpTool({ name: "find_product", description: "Finds a product.", input: z.object({ sku: z.string() }) })\n findProduct({ sku }: { readonly sku: string }) { return { sku }; }\n}\n\nconst definition = getMcpServerDefinition(LegacyCatalog);\nif (definition?.tools[0]?.name !== "find_product") throw new Error("Legacy MCP definition was not registered.");\n`, + `import { z } from "zod";\nimport { createMcpHandler } from "@theorvane/type-mcp/http";\nimport { createLangChainTools } from "@theorvane/type-mcp/langchain";\nimport { getMcpServerDefinition, McpServer, McpTool } from "@theorvane/type-mcp/legacy";\n\n@McpServer({ name: "legacy-catalog", version: "1.0.0" })\nclass LegacyCatalog {\n @McpTool({ name: "find_product", description: "Finds a product.", input: z.object({ sku: z.string() }) })\n findProduct({ sku }: { readonly sku: string }) { return { sku }; }\n}\n\nconst definition = getMcpServerDefinition(LegacyCatalog);\nif (definition?.tools[0]?.name !== "find_product") throw new Error("Legacy MCP definition was not registered.");\nif (typeof createMcpHandler !== "function") throw new Error("CJS http export missing.");\nif (typeof createLangChainTools !== "function") throw new Error("CJS langchain export missing.");\n`, ); run( resolve(packageRoot, "node_modules/typescript/bin/tsc"), diff --git a/scripts/verify-package-exports.mjs b/scripts/verify-package-exports.mjs index 5c415b6..a8c6faf 100644 --- a/scripts/verify-package-exports.mjs +++ b/scripts/verify-package-exports.mjs @@ -60,7 +60,8 @@ for (const { key, symbols } of exportsToVerify) { typeof importExport.types !== "string" || typeof requireExport !== "object" || requireExport === null || - typeof requireExport.default !== "string" + typeof requireExport.default !== "string" || + typeof requireExport.types !== "string" ) { throw new Error(`${manifest.name}: invalid ${key} export map`); } @@ -68,11 +69,18 @@ for (const { key, symbols } of exportsToVerify) { const esmPath = resolve(root, importExport.default); const cjsPath = resolve(root, requireExport.default); const typesPath = resolve(root, importExport.types); - await Promise.all([access(esmPath), access(cjsPath), access(typesPath)]); + const cjsTypesPath = resolve(root, requireExport.types); + await Promise.all([ + access(esmPath), + access(cjsPath), + access(typesPath), + access(cjsTypesPath), + ]); - const [esm, typeDeclarations] = await Promise.all([ + const [esm, typeDeclarations, cjsTypeDeclarations] = await Promise.all([ import(pathToFileURL(esmPath).href), readFile(typesPath, "utf8"), + readFile(cjsTypesPath, "utf8"), ]); const require = createRequire(manifestPath); const cjs = require(cjsPath); @@ -83,7 +91,10 @@ for (const { key, symbols } of exportsToVerify) { `${manifest.name}: missing ${key} ${name} runtime export`, ); } - if (!typeDeclarations.includes(name)) { + if ( + !typeDeclarations.includes(name) || + !cjsTypeDeclarations.includes(name) + ) { throw new Error(`${manifest.name}: missing ${key} ${name} type export`); } } diff --git a/scripts/verify-publish-readiness.mjs b/scripts/verify-publish-readiness.mjs index b7b12cb..4863e70 100644 --- a/scripts/verify-publish-readiness.mjs +++ b/scripts/verify-publish-readiness.mjs @@ -24,12 +24,15 @@ for (const expected of [ "dist/index.js", "dist/index.cjs", "dist/index.d.ts", + "dist/index.d.cts", "dist/http.js", "dist/http.cjs", "dist/http.d.ts", + "dist/http.d.cts", "dist/langchain.js", "dist/langchain.cjs", "dist/langchain.d.ts", + "dist/langchain.d.cts", ]) { if (!files.has(expected)) { throw new Error(`${manifest.name}: tarball is missing ${expected}`); diff --git a/test/langchain-package-contract.test.ts b/test/langchain-package-contract.test.ts index b800c9b..a1e613c 100644 --- a/test/langchain-package-contract.test.ts +++ b/test/langchain-package-contract.test.ts @@ -18,9 +18,14 @@ describe("LangChain adapter package contract", () => { ) as PackageManifest; expect(manifest.exports?.["./langchain"]).toEqual({ - types: "./dist/langchain.d.ts", - import: "./dist/langchain.js", - require: "./dist/langchain.cjs", + import: { + types: "./dist/langchain.d.ts", + default: "./dist/langchain.js", + }, + require: { + types: "./dist/langchain.d.cts", + default: "./dist/langchain.cjs", + }, }); expect(manifest.peerDependencies?.["@langchain/core"]).toBe("^1.2.3"); expect(manifest.peerDependenciesMeta?.["@langchain/core"]?.optional).toBe(