diff --git a/.github/workflows/release-promotion.yml b/.github/workflows/release-promotion.yml new file mode 100644 index 0000000..2f2b40f --- /dev/null +++ b/.github/workflows/release-promotion.yml @@ -0,0 +1,23 @@ +name: Release promotion + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + release-promotion: + name: release-promotion + runs-on: ubuntu-latest + steps: + - name: Require dev as the promotion source + run: test "${{ github.head_ref }}" = "dev" + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run check diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 3f9e520..ea34350 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -25,3 +25,4 @@ jobs: - run: npm run example:policy - run: npm run example:typemcp - run: npm run example:bridge + - run: npm run audit:prod diff --git a/README.md b/README.md index 02c8cdf..9bc6131 100644 --- a/README.md +++ b/README.md @@ -30,19 +30,24 @@ These examples show declaration and adapter boundaries, not a full hosted applic - The bridge is in-process only. It adapts a decorated TypeMCP class to LangChain tools; it does not create an MCP client or network connection. - No model provider is configured. Add a model only in your application after deciding its credentials, authorization, and runtime policy. +## Dependency-security boundary + +These examples use the published `@theorvane/type-mcp@^0.2.2` remediation. Its consumer-enforceable dependency contract resolves `@modelcontextprotocol/sdk@1.30.0` and `@hono/node-server@2.0.12`; `npm run audit:prod` verifies the installed production graph with no local npm override. + ## Development ```bash npm run lint npm run build npm test +npm run audit:prod npm run check ``` ## Packages - [`@theorvane/type-chain`](https://www.npmjs.com/package/@theorvane/type-chain) `0.1.1` -- [`@theorvane/type-mcp`](https://www.npmjs.com/package/@theorvane/type-mcp) `0.2.0` +- [`@theorvane/type-mcp`](https://www.npmjs.com/package/@theorvane/type-mcp) `0.2.2` or later within the `0.2.x` range ## License diff --git a/package-lock.json b/package-lock.json index 4e67c10..fe0b1eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@langchain/core": "^1.2.3", "@theorvane/type-chain": "^0.1.1", - "@theorvane/type-mcp": "^0.2.0", + "@theorvane/type-mcp": "^0.2.2", "langchain": "^1.5.4", "zod": "^4.4.3" }, @@ -674,12 +674,12 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.17", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.17.tgz", - "integrity": "sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.12.tgz", + "integrity": "sha512-eWpQYr67tqJLeaSUl0Q+TquuYfUdTibpOJlUMV2FfUP7+KqCC5TufnwnlXL6mobZBJbGAYRd7ZvEBDCbLInjhg==", "license": "MIT", "engines": { - "node": ">=18.14.1" + "node": ">=20" }, "peerDependencies": { "hono": "^4" @@ -815,12 +815,12 @@ "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", - "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.9", + "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -1216,12 +1216,13 @@ } }, "node_modules/@theorvane/type-mcp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@theorvane/type-mcp/-/type-mcp-0.2.1.tgz", - "integrity": "sha512-PafubwBhNu9b4nhsT0YX4gZRyiRQtqYmm2hKBPmd9xXG7NX87lOiAEXnkwfWsUwjf0bS88kHdNt3NRhY+/kPvw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@theorvane/type-mcp/-/type-mcp-0.2.2.tgz", + "integrity": "sha512-Oj7ZNnJsC0SWdXrCiUNMfd3HYLtBkm7rNyL5xSsvVkUvowoTT+f+5ojl6Lq0DMLeKuMMRwFIMimwdzT35MWI2w==", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "1.26.0", + "@hono/node-server": "2.0.12", + "@modelcontextprotocol/sdk": "1.30.0", "zod": "^4.4.3" }, "engines": { diff --git a/package.json b/package.json index 2780dc9..afe6f62 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "node": ">=20" }, "scripts": { + "audit:prod": "npm audit --omit=dev --audit-level=low", "build": "tsc --noEmit", - "check": "npm run format:check && npm run build && npm test", + "check": "npm run format:check && npm run build && npm test && npm run audit:prod", "example:typechain": "tsx examples/typechain-tool-definition.ts", "example:policy": "tsx examples/typechain-policy-guard.ts", "example:typemcp": "tsx examples/typemcp-server-definition.ts", @@ -22,7 +23,7 @@ "dependencies": { "@langchain/core": "^1.2.3", "@theorvane/type-chain": "^0.1.1", - "@theorvane/type-mcp": "^0.2.0", + "@theorvane/type-mcp": "^0.2.2", "langchain": "^1.5.4", "zod": "^4.4.3" }, diff --git a/test/dependency-security.test.ts b/test/dependency-security.test.ts new file mode 100644 index 0000000..c557313 --- /dev/null +++ b/test/dependency-security.test.ts @@ -0,0 +1,33 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const repositoryRoot = resolve(fileURLToPath(new URL("..", import.meta.url))); + +type Lockfile = { + readonly packages: Record; +}; + +describe("production dependency security", () => { + it("uses the published TypeMCP remediation without a local override", () => { + const packageJson = JSON.parse( + readFileSync(resolve(repositoryRoot, "package.json"), "utf8"), + ) as { + readonly dependencies?: { + readonly "@theorvane/type-mcp"?: string; + }; + }; + const lockfile = JSON.parse( + readFileSync(resolve(repositoryRoot, "package-lock.json"), "utf8"), + ) as Lockfile; + + expect(packageJson.dependencies?.["@theorvane/type-mcp"]).toBe("^0.2.2"); + expect( + lockfile.packages["node_modules/@modelcontextprotocol/sdk"]?.version, + ).toBe("1.30.0"); + expect(lockfile.packages["node_modules/@hono/node-server"]?.version).toBe( + "2.0.12", + ); + }); +});