Refactor mcplint project structure and update dependencies - #1
Conversation
- Transitioned to a monorepo setup, renaming the project to `mcplint-monorepo`. - Removed `package-lock.json` and added `pnpm-lock.yaml` for dependency management. - Updated `.gitignore` to include Next.js and environment files. - Created new `apps/web` directory with Next.js application structure, including configuration files and initial components. - Added environment configuration example and database connection settings for the web app. - Enhanced README for clarity on usage and project structure. - Implemented initial API routes for linting and report generation.
📝 WalkthroughWalkthroughThis PR converts the repository into a monorepo with a core MCP linter and Next.js web application. It adds linting rules, hosted auditing, SSRF protection, rate limiting, report persistence, sharing, installation pages, tests, CI, deployment, and release automation. Changesmcplint platform
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant LintForm
participant LintRoute
participant LintEngine
participant Store
Visitor->>LintForm: Submit snapshot or HTTPS URL
LintForm->>LintRoute: POST /api/lint
LintRoute->>LintEngine: Build snapshot and run rules
LintEngine->>Store: Persist report and snapshot
Store->>LintRoute: Return report id and delete token
LintRoute->>LintForm: Return id and composite score
LintForm->>Visitor: Navigate to report page
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Checkov (3.3.8).claude/launch.jsonTraceback (most recent call last): .github/workflows/ci.ymlTraceback (most recent call last): .github/workflows/release.ymlTraceback (most recent call last):
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (7)
apps/web/app/globals.css (1)
352-354: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReplace deprecated
break-wordvalue.The
break-wordvalue for theword-breakproperty is deprecated. Consider usingoverflow-wrap: anywhereoroverflow-wrap: break-wordinstead to achieve the same wrapping behavior without relying on a deprecated CSS feature.💡 Proposed fix
white-space: pre-wrap; - word-break: break-word; + overflow-wrap: break-word; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/app/globals.css` around lines 352 - 354, Replace the deprecated word-break: break-word declaration in the affected global style with overflow-wrap: anywhere, while preserving the existing white-space: pre-wrap behavior.Source: Linters/SAST tools
apps/web/components/AuditCta.tsx (1)
46-59: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winHandle fetch errors and loading states.
The form submission lacks error handling and a loading state. If the network request fails, the user will be left in a broken state without feedback. Additionally, consider disabling the submit button while the request is in flight.
Alternatively, since you are using React 19, you could use Server Actions or the new
actionprop for forms withuseActionStateto handle this more idiomatically.💡 Proposed fix using a simple try/catch and loading state
const [email, setEmail] = useState(""); const [done, setDone] = useState(false); + const [busy, setBusy] = useState(false); if (done) { @@ -46,14 +47,19 @@ <form className="row" style={{ marginTop: 0 }} onSubmit={async (event) => { event.preventDefault(); - await fetch("/api/interest", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ email, runId: id }) - }); - track("interest_submitted", { id }); - setDone(true); + setBusy(true); + try { + await fetch("/api/interest", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ email, runId: id }) + }); + track("interest_submitted", { id }); + setDone(true); + } catch (err) { + console.error("Failed to submit interest:", err); + } finally { + setBusy(false); + } }} > @@ -75,3 +81,3 @@ /> - <button className="primary" type="submit"> + <button className="primary" type="submit" disabled={busy}> Tell me when it ships🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/components/AuditCta.tsx` around lines 46 - 59, Update the form submission handler in AuditCta to track an in-flight loading state, disable the submit button while the request is pending, and wrap the interest fetch in try/catch. Only call track and setDone after a successful response, and provide user feedback when the request fails.apps/web/components/Analytics.tsx (1)
7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrevent
useEffectfrom accidentally returning a value.If
initAnalytics()is or becomes asynchronous, the implicit return will pass a Promise to React, which triggers a runtime warning (React expects eitherundefinedor a synchronous cleanup function). Wrapping the call ensuresundefinedis always returned.♻️ Proposed fix
- useEffect(() => initAnalytics(), []); + useEffect(() => { + initAnalytics(); + }, []);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/components/Analytics.tsx` at line 7, Update the useEffect callback in Analytics to use a block body and invoke initAnalytics() without implicitly returning its result, ensuring the effect always returns undefined even if initAnalytics becomes asynchronous.apps/web/lib/ssrf.test.ts (1)
7-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: add coverage for 6to4/Teredo/rfc6052/rfc6145 ranges.
V4_IN_V6_RANGESinssrf.tsalso names"rfc6145","rfc6052","6to4", and"teredo", but only theipv4Mappedcase is exercised here (lines 25-27). A couple of addresses from those ranges (e.g. a2002::/166to4 literal) would confirm the "refuse outright" branch behaves as documented — low risk since the fallback already fails closed, but cheap to add.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/lib/ssrf.test.ts` around lines 7 - 48, Extend the blocked-address cases in the isBlockedAddress test suite to include representative addresses from the 6to4, Teredo, RFC6052, and RFC6145 ranges named by V4_IN_V6_RANGES in ssrf.ts. Assert each is blocked, preserving the existing table-driven test structure and allowed-address coverage.packages/core/src/config.ts (1)
31-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAvoid the Time-of-Check to Time-of-Use (TOCTOU) anti-pattern.
Using
fs.accessto check if a file exists before reading it is not recommended in Node.js as it introduces a minor race condition and requires redundant system calls. It is more idiomatic to attempt reading the file directly and catch theENOENTerror if it is missing.(Note: If applied, you can also safely remove
accessfrom yournode:fs/promisesimport at the top of the file).♻️ Proposed refactor
static async load(explicitPath?: string, cwd = process.cwd()): Promise<McplintConfig> { const path = explicitPath ?? resolve(cwd, this.defaultFileName); - if (!explicitPath && !(await this.exists(path))) return this.empty(); - const raw = await readFile(path, "utf8"); - return configSchema.parse(JSON.parse(raw)); - } - - private static async exists(path: string): Promise<boolean> { - try { - await access(path); - return true; - } catch { - return false; - } + try { + const raw = await readFile(path, "utf8"); + return configSchema.parse(JSON.parse(raw)); + } catch (error) { + if (!explicitPath && (error as any).code === "ENOENT") { + return this.empty(); + } + throw error; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/config.ts` around lines 31 - 46, Refactor Config.load to remove the pre-read exists check and directly attempt readFile for the resolved path. Catch only ENOENT errors and return this.empty() for missing files; rethrow all other read or parsing errors. Remove the now-unused exists method and access import.packages/core/src/engine.ts (1)
28-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsolidate rule resolution logic.
You can simplify this method by extracting the
severityandoptionsresolution inline. This avoids duplicating theResolvedRuleobject shape across multiple distinct branches.♻️ Proposed refactor
private resolveRule(rule: Rule): ResolvedRule | undefined { - const setting: RuleSetting | undefined = this.config.rules[rule.id]; + const setting = this.config.rules[rule.id]; if (setting === "off") return undefined; - if (setting === undefined) { - return { rule, severity: rule.severity, options: { ...rule.defaultOptions } }; - } - if (typeof setting === "string") { - return { rule, severity: setting, options: { ...rule.defaultOptions } }; - } - return { - rule, - severity: setting.severity ?? rule.severity, - options: { ...rule.defaultOptions, ...(setting.options ?? {}) } - }; + + const severity = typeof setting === "string" ? setting : setting?.severity ?? rule.severity; + const options = { ...rule.defaultOptions, ...(typeof setting === "object" ? setting.options : {}) }; + + return { rule, severity, options }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/engine.ts` around lines 28 - 42, Refactor resolveRule so it handles the "off" case separately, then computes severity and merged options once from the configured setting or rule defaults before returning a single ResolvedRule shape. Preserve the existing precedence for configured severity, default severity, configured options, and default options.packages/core/README.md (1)
15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSpecify a language for the fenced code block.
As highlighted by static analysis, this fenced code block lacks a language identifier. Consider adding
textto improve syntax highlighting and satisfy markdown linters.📝 Proposed fix
-``` +```text🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/README.md` at line 15, Update the fenced code block in the README to include the text language identifier, changing the opening fence to use text while preserving the block’s contents and closing fence.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/app/api/cron/purge/route.ts`:
- Around line 13-19: Update the authentication guard in GET so a missing
CRON_SECRET is treated as unauthorized rather than bypassing validation. Require
both a configured secret and an exact matching Bearer authorization header
before continuing; otherwise return the existing 401 Unauthorized response.
In `@apps/web/app/api/interest/route.ts`:
- Around line 17-36: Add endpoint/IP-based rate limiting in the POST handler
before the getDb().insert(interest) call, rejecting requests that exceed the
configured limit with an appropriate throttling response. Ensure the limit
applies to public database writes while preserving validation, local no-database
behavior, and successful inserts for allowed requests.
In `@apps/web/components/ShareControls.tsx`:
- Around line 19-32: Update toggle() to wrap its fetch and success handling in
try...finally, ensuring setBusy(false) always runs when the request throws or
completes. Apply the same try...finally loading-state cleanup to the deletion
fetch at apps/web/components/ShareControls.tsx lines 40-46; both sites require
direct changes.
In `@apps/web/lib/lint.ts`:
- Around line 50-97: Update withTimeout and the MCP capture flow so timeout
expiration actively aborts the in-flight request rather than only rejecting
Promise.race. Thread an AbortSignal (or equivalent) from buildSnapshot through
McpCapture.fromHttp into the HTTP transport, trigger it when the timer fires,
and preserve the existing LintError timeout behavior while preventing later
background rejection.
In `@apps/web/lib/rate-limit.ts`:
- Around line 15-35: Make the production path distinguish missing Upstash
configuration from intentional non-production bypasses: update the configuration
handling around configured, redis, and checkRateLimit so absent
UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN in production emits a clear
misconfiguration signal and does not silently allow every request. Preserve the
existing limiter behavior when configuration is present and the intended
fail-open behavior outside production.
In `@apps/web/lib/store.ts`:
- Around line 39-42: Update hashIp to require IP_HASH_SALT before hashing,
removing the public development fallback; fail clearly when the secret is
missing so no IP hash is persisted with a predictable salt.
- Around line 126-151: Update PostgresStore.create() to wrap the runs insert and
conditional runFindings insert in a single db.batch([...]) operation, preserving
the existing values and skipping the findings insert when rows is empty so both
writes commit atomically.
In `@packages/core/src/rules/design-client-directives.ts`:
- Around line 24-28: Update isDirective to match referenced tool names
case-insensitively and only as whole tokens: normalize the sentence and each
name consistently, escape names for use in a regular expression, and apply
explicit token boundaries instead of sentence.includes(name). Preserve the
existing DIRECTIVE_VERB and some-based matching flow.
- Around line 42-56: Update blockCounts construction in the design-client
directive analysis to count each normalized directive key at most once per tool.
Deduplicate the keys derived from a tool’s directives before incrementing
blockCounts, while preserving the existing duplicated lookup and findings
behavior.
In `@packages/core/src/rules/schemas-param-desc-missing.ts`:
- Around line 19-21: Update the property guard in the schema iteration within
the rules-param description validation to exclude null values before accessing
prop.description or prop.$ref. Preserve the existing behavior for non-object
values and valid object properties while preventing malformed null schema
entries from reaching those property accesses.
---
Nitpick comments:
In `@apps/web/app/globals.css`:
- Around line 352-354: Replace the deprecated word-break: break-word declaration
in the affected global style with overflow-wrap: anywhere, while preserving the
existing white-space: pre-wrap behavior.
In `@apps/web/components/Analytics.tsx`:
- Line 7: Update the useEffect callback in Analytics to use a block body and
invoke initAnalytics() without implicitly returning its result, ensuring the
effect always returns undefined even if initAnalytics becomes asynchronous.
In `@apps/web/components/AuditCta.tsx`:
- Around line 46-59: Update the form submission handler in AuditCta to track an
in-flight loading state, disable the submit button while the request is pending,
and wrap the interest fetch in try/catch. Only call track and setDone after a
successful response, and provide user feedback when the request fails.
In `@apps/web/lib/ssrf.test.ts`:
- Around line 7-48: Extend the blocked-address cases in the isBlockedAddress
test suite to include representative addresses from the 6to4, Teredo, RFC6052,
and RFC6145 ranges named by V4_IN_V6_RANGES in ssrf.ts. Assert each is blocked,
preserving the existing table-driven test structure and allowed-address
coverage.
In `@packages/core/README.md`:
- Line 15: Update the fenced code block in the README to include the text
language identifier, changing the opening fence to use text while preserving the
block’s contents and closing fence.
In `@packages/core/src/config.ts`:
- Around line 31-46: Refactor Config.load to remove the pre-read exists check
and directly attempt readFile for the resolved path. Catch only ENOENT errors
and return this.empty() for missing files; rethrow all other read or parsing
errors. Remove the now-unused exists method and access import.
In `@packages/core/src/engine.ts`:
- Around line 28-42: Refactor resolveRule so it handles the "off" case
separately, then computes severity and merged options once from the configured
setting or rule defaults before returning a single ResolvedRule shape. Preserve
the existing precedence for configured severity, default severity, configured
options, and default options.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5cd71402-8e75-47a2-bce1-ec1af032a177
⛔ Files ignored due to path filters (3)
package-lock.jsonis excluded by!**/package-lock.jsonpackages/core/tests/__snapshots__/report-golden.test.ts.snapis excluded by!**/*.snappnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (89)
.claude/launch.json.gitignoreREADME.mdapps/web/.env.exampleapps/web/app/api/cron/purge/route.tsapps/web/app/api/interest/route.tsapps/web/app/api/lint/route.tsapps/web/app/globals.cssapps/web/app/layout.tsxapps/web/app/page.tsxapps/web/app/r/[id]/opengraph-image.tsxapps/web/app/r/[id]/page.tsxapps/web/app/rules/page.tsxapps/web/components/Analytics.tsxapps/web/components/AuditCta.tsxapps/web/components/LintForm.tsxapps/web/components/ReportView.tsxapps/web/components/ShareControls.tsxapps/web/drizzle.config.tsapps/web/drizzle/0000_nice_silver_centurion.sqlapps/web/drizzle/meta/0000_snapshot.jsonapps/web/drizzle/meta/_journal.jsonapps/web/lib/analytics.tsapps/web/lib/db/client.tsapps/web/lib/db/schema.tsapps/web/lib/guarded-fetch.tsapps/web/lib/lint.tsapps/web/lib/rate-limit.tsapps/web/lib/ssrf.test.tsapps/web/lib/ssrf.tsapps/web/lib/store.tsapps/web/lib/version.tsapps/web/next.config.tsapps/web/package.jsonapps/web/tsconfig.jsonapps/web/vercel.jsonpackage.jsonpackages/core/README.mdpackages/core/docs/rules.mdpackages/core/fixtures/bad-server.jsonpackages/core/fixtures/good-server.jsonpackages/core/fixtures/private/.gitkeeppackages/core/package.jsonpackages/core/src/cli.tspackages/core/src/config.tspackages/core/src/engine.tspackages/core/src/index.tspackages/core/src/ingest/index.tspackages/core/src/ingest/mcp-capture.tspackages/core/src/ingest/snapshot-schema.tspackages/core/src/project.tspackages/core/src/reporters/json.tspackages/core/src/reporters/md.tspackages/core/src/reporters/tty.tspackages/core/src/rules/BaseRule.tspackages/core/src/rules/annotations-missing-hints.tspackages/core/src/rules/descriptions-missing.tspackages/core/src/rules/descriptions-too-long.tspackages/core/src/rules/descriptions-too-short.tspackages/core/src/rules/design-client-directives.tspackages/core/src/rules/design-confusable-params.tspackages/core/src/rules/design-crud-mirror.tspackages/core/src/rules/design-duplicate-leading-words.tspackages/core/src/rules/design-enum-combination-unencoded.tspackages/core/src/rules/design-enum-in-prose.tspackages/core/src/rules/design-list-no-limit.tspackages/core/src/rules/design-negative-guidance-present.tspackages/core/src/rules/design-overlap-cluster.tspackages/core/src/rules/docs.tspackages/core/src/rules/index.tspackages/core/src/rules/naming-convention.tspackages/core/src/rules/schemas-complexity-budget.tspackages/core/src/rules/schemas-loose.tspackages/core/src/rules/schemas-param-desc-missing.tspackages/core/src/rules/surface-token-budget.tspackages/core/src/rules/surface-tool-budget.tspackages/core/src/scoring.tspackages/core/src/tokens.tspackages/core/src/types.tspackages/core/tests/core-api.test.tspackages/core/tests/engine.test.tspackages/core/tests/helpers.tspackages/core/tests/report-golden.test.tspackages/core/tests/rules-tier1.test.tspackages/core/tests/rules-tier2.test.tspackages/core/tsconfig.jsonpackages/core/tsup.config.tspnpm-workspace.yamlsrc/rules/docs.ts
💤 Files with no reviewable changes (1)
- src/rules/docs.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 10
🧹 Nitpick comments (7)
apps/web/app/globals.css (1)
352-354: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReplace deprecated
break-wordvalue.The
break-wordvalue for theword-breakproperty is deprecated. Consider usingoverflow-wrap: anywhereoroverflow-wrap: break-wordinstead to achieve the same wrapping behavior without relying on a deprecated CSS feature.💡 Proposed fix
white-space: pre-wrap; - word-break: break-word; + overflow-wrap: break-word; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/app/globals.css` around lines 352 - 354, Replace the deprecated word-break: break-word declaration in the affected global style with overflow-wrap: anywhere, while preserving the existing white-space: pre-wrap behavior.Source: Linters/SAST tools
apps/web/components/AuditCta.tsx (1)
46-59: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winHandle fetch errors and loading states.
The form submission lacks error handling and a loading state. If the network request fails, the user will be left in a broken state without feedback. Additionally, consider disabling the submit button while the request is in flight.
Alternatively, since you are using React 19, you could use Server Actions or the new
actionprop for forms withuseActionStateto handle this more idiomatically.💡 Proposed fix using a simple try/catch and loading state
const [email, setEmail] = useState(""); const [done, setDone] = useState(false); + const [busy, setBusy] = useState(false); if (done) { @@ -46,14 +47,19 @@ <form className="row" style={{ marginTop: 0 }} onSubmit={async (event) => { event.preventDefault(); - await fetch("/api/interest", { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ email, runId: id }) - }); - track("interest_submitted", { id }); - setDone(true); + setBusy(true); + try { + await fetch("/api/interest", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ email, runId: id }) + }); + track("interest_submitted", { id }); + setDone(true); + } catch (err) { + console.error("Failed to submit interest:", err); + } finally { + setBusy(false); + } }} > @@ -75,3 +81,3 @@ /> - <button className="primary" type="submit"> + <button className="primary" type="submit" disabled={busy}> Tell me when it ships🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/components/AuditCta.tsx` around lines 46 - 59, Update the form submission handler in AuditCta to track an in-flight loading state, disable the submit button while the request is pending, and wrap the interest fetch in try/catch. Only call track and setDone after a successful response, and provide user feedback when the request fails.apps/web/components/Analytics.tsx (1)
7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrevent
useEffectfrom accidentally returning a value.If
initAnalytics()is or becomes asynchronous, the implicit return will pass a Promise to React, which triggers a runtime warning (React expects eitherundefinedor a synchronous cleanup function). Wrapping the call ensuresundefinedis always returned.♻️ Proposed fix
- useEffect(() => initAnalytics(), []); + useEffect(() => { + initAnalytics(); + }, []);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/components/Analytics.tsx` at line 7, Update the useEffect callback in Analytics to use a block body and invoke initAnalytics() without implicitly returning its result, ensuring the effect always returns undefined even if initAnalytics becomes asynchronous.apps/web/lib/ssrf.test.ts (1)
7-48: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: add coverage for 6to4/Teredo/rfc6052/rfc6145 ranges.
V4_IN_V6_RANGESinssrf.tsalso names"rfc6145","rfc6052","6to4", and"teredo", but only theipv4Mappedcase is exercised here (lines 25-27). A couple of addresses from those ranges (e.g. a2002::/166to4 literal) would confirm the "refuse outright" branch behaves as documented — low risk since the fallback already fails closed, but cheap to add.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/lib/ssrf.test.ts` around lines 7 - 48, Extend the blocked-address cases in the isBlockedAddress test suite to include representative addresses from the 6to4, Teredo, RFC6052, and RFC6145 ranges named by V4_IN_V6_RANGES in ssrf.ts. Assert each is blocked, preserving the existing table-driven test structure and allowed-address coverage.packages/core/src/config.ts (1)
31-46: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAvoid the Time-of-Check to Time-of-Use (TOCTOU) anti-pattern.
Using
fs.accessto check if a file exists before reading it is not recommended in Node.js as it introduces a minor race condition and requires redundant system calls. It is more idiomatic to attempt reading the file directly and catch theENOENTerror if it is missing.(Note: If applied, you can also safely remove
accessfrom yournode:fs/promisesimport at the top of the file).♻️ Proposed refactor
static async load(explicitPath?: string, cwd = process.cwd()): Promise<McplintConfig> { const path = explicitPath ?? resolve(cwd, this.defaultFileName); - if (!explicitPath && !(await this.exists(path))) return this.empty(); - const raw = await readFile(path, "utf8"); - return configSchema.parse(JSON.parse(raw)); - } - - private static async exists(path: string): Promise<boolean> { - try { - await access(path); - return true; - } catch { - return false; - } + try { + const raw = await readFile(path, "utf8"); + return configSchema.parse(JSON.parse(raw)); + } catch (error) { + if (!explicitPath && (error as any).code === "ENOENT") { + return this.empty(); + } + throw error; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/config.ts` around lines 31 - 46, Refactor Config.load to remove the pre-read exists check and directly attempt readFile for the resolved path. Catch only ENOENT errors and return this.empty() for missing files; rethrow all other read or parsing errors. Remove the now-unused exists method and access import.packages/core/src/engine.ts (1)
28-42: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsolidate rule resolution logic.
You can simplify this method by extracting the
severityandoptionsresolution inline. This avoids duplicating theResolvedRuleobject shape across multiple distinct branches.♻️ Proposed refactor
private resolveRule(rule: Rule): ResolvedRule | undefined { - const setting: RuleSetting | undefined = this.config.rules[rule.id]; + const setting = this.config.rules[rule.id]; if (setting === "off") return undefined; - if (setting === undefined) { - return { rule, severity: rule.severity, options: { ...rule.defaultOptions } }; - } - if (typeof setting === "string") { - return { rule, severity: setting, options: { ...rule.defaultOptions } }; - } - return { - rule, - severity: setting.severity ?? rule.severity, - options: { ...rule.defaultOptions, ...(setting.options ?? {}) } - }; + + const severity = typeof setting === "string" ? setting : setting?.severity ?? rule.severity; + const options = { ...rule.defaultOptions, ...(typeof setting === "object" ? setting.options : {}) }; + + return { rule, severity, options }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/engine.ts` around lines 28 - 42, Refactor resolveRule so it handles the "off" case separately, then computes severity and merged options once from the configured setting or rule defaults before returning a single ResolvedRule shape. Preserve the existing precedence for configured severity, default severity, configured options, and default options.packages/core/README.md (1)
15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSpecify a language for the fenced code block.
As highlighted by static analysis, this fenced code block lacks a language identifier. Consider adding
textto improve syntax highlighting and satisfy markdown linters.📝 Proposed fix
-``` +```text🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/README.md` at line 15, Update the fenced code block in the README to include the text language identifier, changing the opening fence to use text while preserving the block’s contents and closing fence.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/app/api/cron/purge/route.ts`:
- Around line 13-19: Update the authentication guard in GET so a missing
CRON_SECRET is treated as unauthorized rather than bypassing validation. Require
both a configured secret and an exact matching Bearer authorization header
before continuing; otherwise return the existing 401 Unauthorized response.
In `@apps/web/app/api/interest/route.ts`:
- Around line 17-36: Add endpoint/IP-based rate limiting in the POST handler
before the getDb().insert(interest) call, rejecting requests that exceed the
configured limit with an appropriate throttling response. Ensure the limit
applies to public database writes while preserving validation, local no-database
behavior, and successful inserts for allowed requests.
In `@apps/web/components/ShareControls.tsx`:
- Around line 19-32: Update toggle() to wrap its fetch and success handling in
try...finally, ensuring setBusy(false) always runs when the request throws or
completes. Apply the same try...finally loading-state cleanup to the deletion
fetch at apps/web/components/ShareControls.tsx lines 40-46; both sites require
direct changes.
In `@apps/web/lib/lint.ts`:
- Around line 50-97: Update withTimeout and the MCP capture flow so timeout
expiration actively aborts the in-flight request rather than only rejecting
Promise.race. Thread an AbortSignal (or equivalent) from buildSnapshot through
McpCapture.fromHttp into the HTTP transport, trigger it when the timer fires,
and preserve the existing LintError timeout behavior while preventing later
background rejection.
In `@apps/web/lib/rate-limit.ts`:
- Around line 15-35: Make the production path distinguish missing Upstash
configuration from intentional non-production bypasses: update the configuration
handling around configured, redis, and checkRateLimit so absent
UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN in production emits a clear
misconfiguration signal and does not silently allow every request. Preserve the
existing limiter behavior when configuration is present and the intended
fail-open behavior outside production.
In `@apps/web/lib/store.ts`:
- Around line 39-42: Update hashIp to require IP_HASH_SALT before hashing,
removing the public development fallback; fail clearly when the secret is
missing so no IP hash is persisted with a predictable salt.
- Around line 126-151: Update PostgresStore.create() to wrap the runs insert and
conditional runFindings insert in a single db.batch([...]) operation, preserving
the existing values and skipping the findings insert when rows is empty so both
writes commit atomically.
In `@packages/core/src/rules/design-client-directives.ts`:
- Around line 24-28: Update isDirective to match referenced tool names
case-insensitively and only as whole tokens: normalize the sentence and each
name consistently, escape names for use in a regular expression, and apply
explicit token boundaries instead of sentence.includes(name). Preserve the
existing DIRECTIVE_VERB and some-based matching flow.
- Around line 42-56: Update blockCounts construction in the design-client
directive analysis to count each normalized directive key at most once per tool.
Deduplicate the keys derived from a tool’s directives before incrementing
blockCounts, while preserving the existing duplicated lookup and findings
behavior.
In `@packages/core/src/rules/schemas-param-desc-missing.ts`:
- Around line 19-21: Update the property guard in the schema iteration within
the rules-param description validation to exclude null values before accessing
prop.description or prop.$ref. Preserve the existing behavior for non-object
values and valid object properties while preventing malformed null schema
entries from reaching those property accesses.
---
Nitpick comments:
In `@apps/web/app/globals.css`:
- Around line 352-354: Replace the deprecated word-break: break-word declaration
in the affected global style with overflow-wrap: anywhere, while preserving the
existing white-space: pre-wrap behavior.
In `@apps/web/components/Analytics.tsx`:
- Line 7: Update the useEffect callback in Analytics to use a block body and
invoke initAnalytics() without implicitly returning its result, ensuring the
effect always returns undefined even if initAnalytics becomes asynchronous.
In `@apps/web/components/AuditCta.tsx`:
- Around line 46-59: Update the form submission handler in AuditCta to track an
in-flight loading state, disable the submit button while the request is pending,
and wrap the interest fetch in try/catch. Only call track and setDone after a
successful response, and provide user feedback when the request fails.
In `@apps/web/lib/ssrf.test.ts`:
- Around line 7-48: Extend the blocked-address cases in the isBlockedAddress
test suite to include representative addresses from the 6to4, Teredo, RFC6052,
and RFC6145 ranges named by V4_IN_V6_RANGES in ssrf.ts. Assert each is blocked,
preserving the existing table-driven test structure and allowed-address
coverage.
In `@packages/core/README.md`:
- Line 15: Update the fenced code block in the README to include the text
language identifier, changing the opening fence to use text while preserving the
block’s contents and closing fence.
In `@packages/core/src/config.ts`:
- Around line 31-46: Refactor Config.load to remove the pre-read exists check
and directly attempt readFile for the resolved path. Catch only ENOENT errors
and return this.empty() for missing files; rethrow all other read or parsing
errors. Remove the now-unused exists method and access import.
In `@packages/core/src/engine.ts`:
- Around line 28-42: Refactor resolveRule so it handles the "off" case
separately, then computes severity and merged options once from the configured
setting or rule defaults before returning a single ResolvedRule shape. Preserve
the existing precedence for configured severity, default severity, configured
options, and default options.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5cd71402-8e75-47a2-bce1-ec1af032a177
⛔ Files ignored due to path filters (3)
package-lock.jsonis excluded by!**/package-lock.jsonpackages/core/tests/__snapshots__/report-golden.test.ts.snapis excluded by!**/*.snappnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (89)
.claude/launch.json.gitignoreREADME.mdapps/web/.env.exampleapps/web/app/api/cron/purge/route.tsapps/web/app/api/interest/route.tsapps/web/app/api/lint/route.tsapps/web/app/globals.cssapps/web/app/layout.tsxapps/web/app/page.tsxapps/web/app/r/[id]/opengraph-image.tsxapps/web/app/r/[id]/page.tsxapps/web/app/rules/page.tsxapps/web/components/Analytics.tsxapps/web/components/AuditCta.tsxapps/web/components/LintForm.tsxapps/web/components/ReportView.tsxapps/web/components/ShareControls.tsxapps/web/drizzle.config.tsapps/web/drizzle/0000_nice_silver_centurion.sqlapps/web/drizzle/meta/0000_snapshot.jsonapps/web/drizzle/meta/_journal.jsonapps/web/lib/analytics.tsapps/web/lib/db/client.tsapps/web/lib/db/schema.tsapps/web/lib/guarded-fetch.tsapps/web/lib/lint.tsapps/web/lib/rate-limit.tsapps/web/lib/ssrf.test.tsapps/web/lib/ssrf.tsapps/web/lib/store.tsapps/web/lib/version.tsapps/web/next.config.tsapps/web/package.jsonapps/web/tsconfig.jsonapps/web/vercel.jsonpackage.jsonpackages/core/README.mdpackages/core/docs/rules.mdpackages/core/fixtures/bad-server.jsonpackages/core/fixtures/good-server.jsonpackages/core/fixtures/private/.gitkeeppackages/core/package.jsonpackages/core/src/cli.tspackages/core/src/config.tspackages/core/src/engine.tspackages/core/src/index.tspackages/core/src/ingest/index.tspackages/core/src/ingest/mcp-capture.tspackages/core/src/ingest/snapshot-schema.tspackages/core/src/project.tspackages/core/src/reporters/json.tspackages/core/src/reporters/md.tspackages/core/src/reporters/tty.tspackages/core/src/rules/BaseRule.tspackages/core/src/rules/annotations-missing-hints.tspackages/core/src/rules/descriptions-missing.tspackages/core/src/rules/descriptions-too-long.tspackages/core/src/rules/descriptions-too-short.tspackages/core/src/rules/design-client-directives.tspackages/core/src/rules/design-confusable-params.tspackages/core/src/rules/design-crud-mirror.tspackages/core/src/rules/design-duplicate-leading-words.tspackages/core/src/rules/design-enum-combination-unencoded.tspackages/core/src/rules/design-enum-in-prose.tspackages/core/src/rules/design-list-no-limit.tspackages/core/src/rules/design-negative-guidance-present.tspackages/core/src/rules/design-overlap-cluster.tspackages/core/src/rules/docs.tspackages/core/src/rules/index.tspackages/core/src/rules/naming-convention.tspackages/core/src/rules/schemas-complexity-budget.tspackages/core/src/rules/schemas-loose.tspackages/core/src/rules/schemas-param-desc-missing.tspackages/core/src/rules/surface-token-budget.tspackages/core/src/rules/surface-tool-budget.tspackages/core/src/scoring.tspackages/core/src/tokens.tspackages/core/src/types.tspackages/core/tests/core-api.test.tspackages/core/tests/engine.test.tspackages/core/tests/helpers.tspackages/core/tests/report-golden.test.tspackages/core/tests/rules-tier1.test.tspackages/core/tests/rules-tier2.test.tspackages/core/tsconfig.jsonpackages/core/tsup.config.tspnpm-workspace.yamlsrc/rules/docs.ts
💤 Files with no reviewable changes (1)
- src/rules/docs.ts
🛑 Comments failed to post (10)
apps/web/app/api/cron/purge/route.ts (1)
13-19: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Fail closed when
CRON_SECRETis missing.An unset secret currently bypasses authentication entirely, exposing the purge operation publicly.
Proposed fix
const secret = process.env.CRON_SECRET; - if (secret && request.headers.get("authorization") !== `Bearer ${secret}`) { + if (!secret || request.headers.get("authorization") !== `Bearer ${secret}`) { return NextResponse.json({ error: "Unauthorized." }, { status: 401 }); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export async function GET(request: Request): Promise<NextResponse> { // Vercel signs cron invocations; reject anything else so this is not a public // "delete a bunch of rows" button. const secret = process.env.CRON_SECRET; if (!secret || request.headers.get("authorization") !== `Bearer ${secret}`) { return NextResponse.json({ error: "Unauthorized." }, { status: 401 }); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/app/api/cron/purge/route.ts` around lines 13 - 19, Update the authentication guard in GET so a missing CRON_SECRET is treated as unauthorized rather than bypassing validation. Require both a configured secret and an exact matching Bearer authorization header before continuing; otherwise return the existing 401 Unauthorized response.apps/web/app/api/interest/route.ts (1)
17-36: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Rate-limit this public database write.
A bot can submit unlimited distinct interest records, inflating storage and filling the database with unsolicited email addresses. Apply endpoint/IP rate limiting before the insert.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/app/api/interest/route.ts` around lines 17 - 36, Add endpoint/IP-based rate limiting in the POST handler before the getDb().insert(interest) call, rejecting requests that exceed the configured limit with an appropriate throttling response. Ensure the limit applies to public database writes while preserving validation, local no-database behavior, and successful inserts for allowed requests.apps/web/components/ShareControls.tsx (1)
19-32: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Prevent stuck loading states on fetch errors.
If a network error occurs,
fetchwill throw an exception. Because the state resets are not enclosed in afinallyblock, the component will fail to callsetBusy(false), leaving the buttons permanently disabled for the session.
apps/web/components/ShareControls.tsx#L19-L32: wrap thefetchand success logic in atry...finallyblock to ensuresetBusy(false)is always executed.apps/web/components/ShareControls.tsx#L40-L46: wrap the deletionfetchsimilarly to guarantee the loading state is cleared on failure.📍 Affects 1 file
apps/web/components/ShareControls.tsx#L19-L32(this comment)apps/web/components/ShareControls.tsx#L40-L46🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/components/ShareControls.tsx` around lines 19 - 32, Update toggle() to wrap its fetch and success handling in try...finally, ensuring setBusy(false) always runs when the request throws or completes. Apply the same try...finally loading-state cleanup to the deletion fetch at apps/web/components/ShareControls.tsx lines 40-46; both sites require direct changes.apps/web/lib/lint.ts (1)
50-97: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
set -euo pipefail echo "== locate symbols ==" rg -n "withTimeout|McpCapture\.fromHttp|class McpCapture|fromHttp\(|createGuardedFetch|AbortController|signal" apps/web lib . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' || true echo "== file list candidates ==" fd -a "lint.ts|guarded-fetch|mcp.*capture|capture" apps/web lib . -t f || trueRepository: DLeibner/mcplint
Length of output: 3116
🏁 Script executed:
set -euo pipefail # Read the target file and nearby definitions with line numbers. echo "== apps/web/lib/lint.ts ==" wc -l apps/web/lib/lint.ts sed -n '1,180p' apps/web/lib/lint.ts | cat -n echo "== guarded fetch candidates ==" fd -a "guarded-fetch.ts|guarded-fetch.js|guarded-fetch.mjs" apps web . -t f || true echo "== McpCapture definition candidates ==" fd -a "capture.ts|mcp*.ts|*.ts" apps web . -t f | rg "capture|mcp" || trueRepository: DLeibner/mcplint
Length of output: 4928
🏁 Script executed:
set -euo pipefail python3 - <<'PY' from pathlib import Path for p in [Path('apps/web/lib/lint.ts')]: if p.exists(): print(f"== {p} ==") for i, line in enumerate(p.read_text().splitlines(), 1): if 1 <= i <= 130: print(f"{i:4d}: {line}") PY echo "== symbol search ==" rg -n "fromHttp\s*\(|class McpCapture|createGuardedFetch\s*\(" apps web . -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' || trueRepository: DLeibner/mcplint
Length of output: 5029
🏁 Script executed:
set -euo pipefail echo "== mcp-capture.ts ==" wc -l packages/core/src/ingest/mcp-capture.ts sed -n '1,220p' packages/core/src/ingest/mcp-capture.ts | cat -n echo "== guarded-fetch.ts ==" wc -l apps/web/lib/guarded-fetch.ts sed -n '1,220p' apps/web/lib/guarded-fetch.ts | cat -nRepository: DLeibner/mcplint
Length of output: 8810
🏁 Script executed:
set -euo pipefail echo "== packages/core/src/ingest/mcp-capture.ts ==" wc -l packages/core/src/ingest/mcp-capture.ts sed -n '1,220p' packages/core/src/ingest/mcp-capture.ts | cat -n echo "== apps/web/lib/guarded-fetch.ts ==" wc -l apps/web/lib/guarded-fetch.ts sed -n '1,220p' apps/web/lib/guarded-fetch.ts | cat -nRepository: DLeibner/mcplint
Length of output: 8848
Abort the capture when the timeout fires.
Promise.raceonly stops waiting; it doesn’t stopMcpCapture.fromHttp(...), so timed-out requests keep running in the background and can still reject later.HttpCaptureOptionsdoesn’t expose an abort path today, so this needs asignal(or equivalent) threaded throughMcpCapture.fromHttpand into the HTTP transport.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/lib/lint.ts` around lines 50 - 97, Update withTimeout and the MCP capture flow so timeout expiration actively aborts the in-flight request rather than only rejecting Promise.race. Thread an AbortSignal (or equivalent) from buildSnapshot through McpCapture.fromHttp into the HTTP transport, trigger it when the timer fires, and preserve the existing LintError timeout behavior while preventing later background rejection.apps/web/lib/rate-limit.ts (1)
15-35: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Fail-open rate limiting is indistinguishable from a production misconfiguration.
If
UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKENare absent for any reason in production (typo, unset env var, wrong environment scope),checkRateLimitsilently returns{ ok: true }for every request — exactly the amplification-abuse scenario theurlmode limiter exists to prevent, per this file's own rationale. Nothing here signals that this happened.🛡️ Proposed guard
const configured = Boolean(process.env.UPSTASH_REDIS_REST_URL) && Boolean(process.env.UPSTASH_REDIS_REST_TOKEN); + +if (!configured && process.env.NODE_ENV === "production") { + console.error( + "Rate limiting is disabled in production: UPSTASH_REDIS_REST_URL/TOKEN are not set." + ); +}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const configured = Boolean(process.env.UPSTASH_REDIS_REST_URL) && Boolean(process.env.UPSTASH_REDIS_REST_TOKEN); if (!configured && process.env.NODE_ENV === "production") { console.error( "Rate limiting is disabled in production: UPSTASH_REDIS_REST_URL/TOKEN are not set." ); } const redis = configured ? Redis.fromEnv() : undefined; const limiters = redis ? { url: new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(10, "1 h"), prefix: "mcplint:url", analytics: true }), paste: new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(60, "1 h"), prefix: "mcplint:paste", analytics: true }) } : undefined;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/lib/rate-limit.ts` around lines 15 - 35, Make the production path distinguish missing Upstash configuration from intentional non-production bypasses: update the configuration handling around configured, redis, and checkRateLimit so absent UPSTASH_REDIS_REST_URL or UPSTASH_REDIS_REST_TOKEN in production emits a clear misconfiguration signal and does not silently allow every request. Preserve the existing limiter behavior when configuration is present and the intended fail-open behavior outside production.apps/web/lib/store.ts (2)
39-42: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Do not fall back to a public salt when persisting IP hashes.
If
IP_HASH_SALTis omitted in production, hashes become predictable and linkable across deployments. Require a secret salt before hashing.Proposed fix
export function hashIp(ip: string): string { - const salt = process.env.IP_HASH_SALT ?? "mcplint-dev-salt"; + const salt = process.env.IP_HASH_SALT; + if (!salt) throw new Error("IP_HASH_SALT is not set."); return createHash("sha256").update(`${salt}:${ip}`).digest("hex").slice(0, 32); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export function hashIp(ip: string): string { const salt = process.env.IP_HASH_SALT; if (!salt) throw new Error("IP_HASH_SALT is not set."); return createHash("sha256").update(`${salt}:${ip}`).digest("hex").slice(0, 32); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/lib/store.ts` around lines 39 - 42, Update hashIp to require IP_HASH_SALT before hashing, removing the public development fallback; fail clearly when the secret is missing so no IP hash is persisted with a predictable salt.
126-151: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # Inspect the declared Drizzle/Neon versions and existing atomic-write patterns. fd -a 'package.json|pnpm-lock.yaml' . -x rg -n -C2 \ '"drizzle-orm"|"`@neondatabase/serverless`"|drizzle-orm@|`@neondatabase/serverless`@' {} rg -n -C3 --type=ts '\.(transaction|batch)\s*\(' .Repository: DLeibner/mcplint
Length of output: 1378
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '== store.ts outline ==\n' ast-grep outline apps/web/lib/store.ts --view expanded || true printf '\n== relevant slice ==\n' wc -l apps/web/lib/store.ts sed -n '1,240p' apps/web/lib/store.ts | cat -n printf '\n== transaction/batch usage ==\n' rg -n -C 2 '\b(transaction|batch)\s*\(' apps web packages . --glob '!**/node_modules/**' --glob '!**/.next/**' || trueRepository: DLeibner/mcplint
Length of output: 10944
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '== apps/web/lib/store.ts ==\n' sed -n '1,240p' apps/web/lib/store.ts | cat -n printf '\n== drizzle / neon setup files ==\n' fd -a 'db.ts|drizzle.ts|neon.ts|database.ts|postgres.ts' apps web . -t f | sed -n '1,40p' printf '\n== transaction-like usage in repo ==\n' rg -n -C 2 '\bdb\.(transaction|batch)\s*\(' apps web . --glob '!**/node_modules/**' --glob '!**/.next/**' || trueRepository: DLeibner/mcplint
Length of output: 8889
🌐 Web query:
Drizzle ORM Neon HTTP driver transaction support batch atomic writes@neondatabase/serverless1.1.0💡 Result:
The Drizzle ORM
neon-httpdriver does not support interactive transactions (e.g.,db.transaction(async (tx) => { ... })) because the underlying Neon HTTP protocol is stateless and does not maintain the session state required for interactive transactions [1][2][3]. Attempting to use thedb.transaction()method with theneon-httpdriver will result in an error: "No transactions support in neon-http driver" [1][2][4]. However, the driver does support "batch" operations, which allow you to execute multiple queries in a single HTTP request [5][1]. While these are not ACID-compliant interactive transactions, they are atomic in the sense that they are sent to the server together in a single non-interactive batch [2]. You can use thedb.batch()API for this purpose: const batchResponse = await db.batch([ db.insert(usersTable).values({ id: 1, name: 'John' }), db.update(usersTable).set({ name: 'Dan' }).where(eq(usersTable.id, 1)), ]); If you require full ACID transaction support, you should use theneon-websocketsdriver instead ofneon-http, as it supports standard PostgreSQL transaction workflows [3]. Regarding the@neondatabase/serverlesspackage compatibility, ensure you are using a version (e.g., 1.1.0) compatible with your Drizzle ORM version to avoid known issues with query execution syntax [6].Citations:
- 1: https://github.com/drizzle-team/drizzle-orm/blob/main/drizzle-orm/src/neon-http/session.ts
- 2: https://www.answeroverflow.com/m/1149370348593217619
- 3: drizzle-team/drizzle-orm#3334
- 4: https://github.com/drizzle-team/drizzle-orm/blob/48e54060/drizzle-orm/src/neon-http/session.ts
- 5: https://orm.drizzle.team/docs/batch-api
- 6: drizzle-team/drizzle-orm#5208
🏁 Script executed:
#!/bin/bash set -euo pipefail printf '== relevant files ==\n' git ls-files | rg '^(apps/web/lib/db/(client|schema)\.ts|apps/web/package.json|pnpm-lock\.yaml|package\.json)$' || true printf '\n== db client ==\n' sed -n '1,220p' apps/web/lib/db/client.ts | cat -n printf '\n== schema ==\n' sed -n '1,240p' apps/web/lib/db/schema.ts | cat -n printf '\n== package.json (root + apps/web) ==\n' sed -n '1,220p' package.json | cat -n printf '\n--- apps/web/package.json ---\n' sed -n '1,220p' apps/web/package.json | cat -n printf '\n== batch/transaction references ==\n' rg -n -C 2 '\bdb\.(transaction|batch)\s*\(|\btransaction\s*\(|\bbatch\s*\(' apps/web . --glob '!**/node_modules/**' --glob '!**/.next/**' || trueRepository: DLeibner/mcplint
Length of output: 6998
Persist the run and finding-count rows in one batch.
PostgresStore.create()does two separate inserts, so arunFindingsfailure leaves therunsrow committed without its denormalized counts and breaks analytics. Usedb.batch([...])here.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/lib/store.ts` around lines 126 - 151, Update PostgresStore.create() to wrap the runs insert and conditional runFindings insert in a single db.batch([...]) operation, preserving the existing values and skipping the findings insert when rows is empty so both writes commit atomically.packages/core/src/rules/design-client-directives.ts (2)
24-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Match referenced tool names case-insensitively and at boundaries.
sentence.includes(name)misses casing differences and treats short names embedded in unrelated words as references. Normalize casing and use escaped token boundaries.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/rules/design-client-directives.ts` around lines 24 - 28, Update isDirective to match referenced tool names case-insensitively and only as whole tokens: normalize the sentence and each name consistently, escape names for use in a regular expression, and apply explicit token boundaries instead of sentence.includes(name). Preserve the existing DIRECTIVE_VERB and some-based matching flow.
42-56: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Count duplication across distinct tools, not sentence occurrences.
If one description repeats the same directive twice,
blockCountsreaches two and incorrectly reports it as duplicated across multiple tools. Deduplicate normalized keys within each tool before incrementing the counts.Proposed fix
for (const directives of perTool.values()) { - for (const sentence of directives) { - const key = BaseRule.words(sentence).join(" "); + const keys = new Set(directives.map((sentence) => BaseRule.words(sentence).join(" "))); + for (const key of keys) { blockCounts.set(key, (blockCounts.get(key) ?? 0) + 1); } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const blockCounts = new Map<string, number>(); for (const directives of perTool.values()) { const keys = new Set( directives.map((sentence) => BaseRule.words(sentence).join(" ")) ); for (const key of keys) { blockCounts.set(key, (blockCounts.get(key) ?? 0) + 1); } } const findings: Finding[] = []; for (const [toolName, directives] of perTool) { const block = directives.join(" "); const tokens = TokenCounter.count(block); const duplicated = directives.some( (s) => (blockCounts.get(BaseRule.words(s).join(" ")) ?? 0) >= 2 );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/rules/design-client-directives.ts` around lines 42 - 56, Update blockCounts construction in the design-client directive analysis to count each normalized directive key at most once per tool. Deduplicate the keys derived from a tool’s directives before incrementing blockCounts, while preserving the existing duplicated lookup and findings behavior.packages/core/src/rules/schemas-param-desc-missing.ts (1)
19-21: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Prevent null-pointer dereference on malformed schemas.
In JavaScript,
typeof null === "object". If a third-party server supplies a malformed JSON schema where a property is explicitlynull, this check will allow it through andprop.descriptionwill trigger a runtimeTypeError, crashing the lint validation for the entire server.Guard against
nullto ensure stability against invalid inputs.🔒️ Proposed fix
for (const [name, prop] of Object.entries(BaseRule.properties(schema))) { - if (typeof prop !== "object") continue; + if (typeof prop !== "object" || prop === null) continue; if (!prop.description && !prop.$ref) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.for (const [name, prop] of Object.entries(BaseRule.properties(schema))) { if (typeof prop !== "object" || prop === null) continue; if (!prop.description && !prop.$ref) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/rules/schemas-param-desc-missing.ts` around lines 19 - 21, Update the property guard in the schema iteration within the rules-param description validation to exclude null values before accessing prop.description or prop.$ref. Preserve the existing behavior for non-object values and valid object properties while preventing malformed null schema entries from reaching those property accesses.
- Updated `.gitignore` to exclude test results and Playwright reports. - Added `DEPLOYMENT.md` for deployment instructions and service authentication. - Introduced GitHub Actions workflows for CI and publishing the CLI. - Integrated Playwright for end-to-end testing and added related configurations. - Enhanced README with installation and usage details for the MCP server. - Implemented new API routes for handling MCP requests and report management. - Added tests for the MCP API and report routes to ensure functionality. - Improved styling and layout in the web application for better user experience.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
.github/workflows/ci.yml (2)
22-23: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falsein the checkout step.By default,
actions/checkoutpersists the GitHub token in the local git configuration. To improve security and prevent malicious dependencies or test scripts from exfiltrating or using the token, it's a best practice to disable this.🔒️ Proposed fix to prevent credential persistence
- name: Check out repository uses: actions/checkout@v4 + with: + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 22 - 23, Update the “Check out repository” actions/checkout@v4 step to set persist-credentials to false, preventing the GitHub token from being stored in the local Git configuration.Source: Linters/SAST tools
22-23: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falsein the checkout step.By default,
actions/checkoutpersists the GitHub token in the local git configuration. To improve security and prevent malicious dependencies or test scripts from exfiltrating or using the token, it's a best practice to disable this.🔒️ Proposed fix to prevent credential persistence
- name: Check out repository uses: actions/checkout@v4 + with: + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 22 - 23, Update the actions/checkout@v4 step in the CI workflow to set persist-credentials to false, preventing the GitHub token from being stored in the local Git configuration.Source: Linters/SAST tools
.github/workflows/publish.yml (2)
15-15: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falsein the checkout step.By default,
actions/checkoutpersists the GitHub token in the local git configuration. To improve security during the build and publish steps, it is recommended to disable this.🔒️ Proposed fix to prevent credential persistence
- - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/publish.yml at line 15, Update the actions/checkout@v4 step in the workflow to set persist-credentials to false, ensuring the checkout action does not retain the GitHub token in local Git configuration.Source: Linters/SAST tools
15-15: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falsein the checkout step.By default,
actions/checkoutpersists the GitHub token in the local git configuration. To improve security during the build and publish steps, it is recommended to disable this.🔒️ Proposed fix to prevent credential persistence
- - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/publish.yml at line 15, Update the actions/checkout@v4 step in the workflow to set persist-credentials to false, ensuring the checkout action does not retain the GitHub token in local Git configuration.Source: Linters/SAST tools
apps/web/app/globals.css (2)
153-163: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse modern
clip-pathfor the visually hidden utility.The
clipCSS property is deprecated. It's recommended to useclip-path: inset(50%);instead, while keepingcliponly as a legacy fallback if strictly needed (though modern browsers fully supportclip-path).♻️ Proposed fix
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; - clip: rect(0, 0, 0, 0); + clip-path: inset(50%); white-space: nowrap; border: 0; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/app/globals.css` around lines 153 - 163, Update the .visually-hidden utility to use the modern clip-path: inset(50%) declaration instead of the deprecated clip property, retaining the existing accessibility-hiding styles and only preserving clip as a legacy fallback if required.Source: Linters/SAST tools
153-163: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse modern
clip-pathfor the visually hidden utility.The
clipCSS property is deprecated. It's recommended to useclip-path: inset(50%);instead, while keepingcliponly as a legacy fallback if strictly needed (though modern browsers fully supportclip-path).♻️ Proposed fix
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; - clip: rect(0, 0, 0, 0); + clip-path: inset(50%); white-space: nowrap; border: 0; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/app/globals.css` around lines 153 - 163, Update the .visually-hidden utility to use modern clip-path with inset(50%) instead of relying on the deprecated clip property; retain clip only if needed as a legacy fallback.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/app/api/report/`[id]/route.ts:
- Around line 10-14: Replace the dynamic RegExp in ownerToken with plain string
parsing to locate the mcplint_owner_${id} cookie and extract its value. Preserve
returning undefined when the cookie is absent, while avoiding regex evaluation
on attacker-controlled id and cookie header.
In `@apps/web/lib/mcp-server.ts`:
- Around line 73-105: Replace the `checkMcpServerInputSchema` `superRefine`
schema with a plain introspectable `z.object(...)` registration so clients
retain the url, headers, and snapshot metadata; enforce the
exactly-one-of-url-or-snapshot and headers-with-url constraints separately in
the tool’s runtime validation path.
In `@apps/web/lib/rate-limit.ts`:
- Around line 44-52: Update the missing-limiters guard in checkRateLimit so
every mode, including "paste", returns the existing not_configured failure
result when NODE_ENV is production. Preserve the unlimited success fallback for
non-production environments and keep the configured-limiters path unchanged.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 22-23: Update the “Check out repository” actions/checkout@v4 step
to set persist-credentials to false, preventing the GitHub token from being
stored in the local Git configuration.
- Around line 22-23: Update the actions/checkout@v4 step in the CI workflow to
set persist-credentials to false, preventing the GitHub token from being stored
in the local Git configuration.
In @.github/workflows/publish.yml:
- Line 15: Update the actions/checkout@v4 step in the workflow to set
persist-credentials to false, ensuring the checkout action does not retain the
GitHub token in local Git configuration.
- Line 15: Update the actions/checkout@v4 step in the workflow to set
persist-credentials to false, ensuring the checkout action does not retain the
GitHub token in local Git configuration.
In `@apps/web/app/globals.css`:
- Around line 153-163: Update the .visually-hidden utility to use the modern
clip-path: inset(50%) declaration instead of the deprecated clip property,
retaining the existing accessibility-hiding styles and only preserving clip as a
legacy fallback if required.
- Around line 153-163: Update the .visually-hidden utility to use modern
clip-path with inset(50%) instead of relying on the deprecated clip property;
retain clip only if needed as a legacy fallback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 79c9df27-19c6-4a56-a3b0-ccaefecda50b
⛔ Files ignored due to path filters (2)
packages/core/tests/__snapshots__/report-golden.test.ts.snapis excluded by!**/*.snappnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (38)
.github/workflows/ci.yml.github/workflows/publish.yml.gitignoreDEPLOYMENT.mdREADME.mdapps/web/.env.exampleapps/web/app/api/cron/purge/route.tsapps/web/app/api/lint/route.tsapps/web/app/api/mcp/route.test.tsapps/web/app/api/mcp/route.tsapps/web/app/api/report/[id]/route.test.tsapps/web/app/api/report/[id]/route.tsapps/web/app/globals.cssapps/web/app/install/page.tsxapps/web/app/layout.tsxapps/web/app/page.tsxapps/web/app/r/[id]/page.tsxapps/web/components/InstallTabs.tsxapps/web/components/LintForm.tsxapps/web/components/ShareControls.tsxapps/web/e2e/playground.spec.tsapps/web/lib/analytics.tsapps/web/lib/guarded-fetch.tsapps/web/lib/install-links.test.tsapps/web/lib/install-links.tsapps/web/lib/lint.tsapps/web/lib/mcp-server.tsapps/web/lib/rate-limit.tsapps/web/lib/server-metadata.test.tsapps/web/lib/site.tsapps/web/lib/store.tsapps/web/next.config.tsapps/web/package.jsonapps/web/playwright.config.tsapps/web/vitest.config.tspackages/core/src/rules/docs.tspackages/core/tests/core-api.test.tsserver.json
💤 Files with no reviewable changes (1)
- apps/web/lib/lint.ts
🚧 Files skipped from review as they are similar to previous changes (13)
- apps/web/app/layout.tsx
- apps/web/.env.example
- apps/web/package.json
- apps/web/app/api/cron/purge/route.ts
- apps/web/app/r/[id]/page.tsx
- packages/core/tests/core-api.test.ts
- .gitignore
- README.md
- apps/web/app/api/lint/route.ts
- apps/web/lib/guarded-fetch.ts
- apps/web/components/ShareControls.tsx
- apps/web/lib/store.ts
- packages/core/src/rules/docs.ts
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Added `workspaces-update=false` to `.npmrc` to prevent npm's workspace tree update. - Revised deployment steps in `DEPLOYMENT.md` for clarity and accuracy regarding version checks and artifact validation. - Updated `package.json` to include workspaces configuration. - Modified GitHub Actions workflow to validate and publish release artifacts correctly. - Removed obsolete `sync-version.mjs` script as version synchronization is now handled differently. - Adjusted tests to ensure version validation aligns with the new structure.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)
85-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
mcp-publisherdownload/checksum/extract logic across jobs.The download-verify-extract sequence for
mcp-publisher(curl, sha256sum check, tar extract) is duplicated between theverifyjob andpublish-registryjob. Since both reference the same pinned version/checksum, extracting this into a small composite action would reduce drift risk if the pin is ever updated in only one place.Also applies to: 250-259
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/release.yml around lines 85 - 101, Extract the repeated mcp-publisher setup sequence—curl download, sha256sum verification, and tar extraction—from the verify and publish-registry jobs into a small composite action. Have both jobs invoke that action while preserving the shared pinned version, checksum, archive location, and extracted binary behavior, so future pin updates occur in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 30-33: Update both checkout steps in the release workflow to set
persist-credentials to false alongside fetch-depth, ensuring the checkout action
does not retain the GITHUB_TOKEN in Git configuration for either job.
---
Nitpick comments:
In @.github/workflows/release.yml:
- Around line 85-101: Extract the repeated mcp-publisher setup sequence—curl
download, sha256sum verification, and tar extraction—from the verify and
publish-registry jobs into a small composite action. Have both jobs invoke that
action while preserving the shared pinned version, checksum, archive location,
and extracted binary behavior, so future pin updates occur in one place.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c256d54f-9098-4c54-a1e1-34de89b13c62
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
.github/workflows/release.yml.npmrcDEPLOYMENT.mdapps/web/lib/server-metadata.test.tsapps/web/package.jsonpackage.jsonpackages/core/package.jsonscripts/smoke-production.mjs
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/web/lib/server-metadata.test.ts
- apps/web/package.json
- package.json
- packages/core/package.json
- Updated `.npmrc` to set `tag-version-prefix` to an empty string for bare semver tags. - Revised `DEPLOYMENT.md` to reflect changes in version tagging and installation commands. - Added `package-lock.json` for npm dependency management and removed `pnpm-lock.yaml`. - Modified `package.json` scripts to utilize npm commands instead of pnpm. - Updated CI workflows to use npm for dependency installation and build processes. - Adjusted README to guide users on using npm for development and testing.
…ing checkout - Modified `.github/workflows/ci.yml` and `.github/workflows/release.yml` to set `persist-credentials: false` for the checkout action, enhancing security by preventing GitHub Actions from persisting credentials in the environment. - Updated error messages in `apps/web/lib/mcp-server.ts` and `apps/web/app/api/lint/route.ts` for clarity. - Refactored cookie handling in `apps/web/app/api/report/[id]/route.ts` for improved readability and performance.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
DEPLOYMENT.md (1)
153-161: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winMake the
mcp-publisherpin reproducible.These lines claim a checksum-verified
v1.8.0binary but provide no download source, platform artifact, checksum, or verification command. Add those steps, or reference the workflow’s pinned installation logic, so operators cannot accidentally run an unverified binary. The official Registry documents GitHub OIDC authentication, while upstream releases expose platform-specific checksums. (modelcontextprotocol.io)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@DEPLOYMENT.md` around lines 153 - 161, Update the mcp-publisher v1.8.0 deployment instructions to include a reproducible, checksum-verified installation: specify the official download source, platform-specific artifact, expected checksum, and verification command, or explicitly reference the workflow’s existing pinned installation logic. Keep the validate, login github-oidc, and publish commands unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@DEPLOYMENT.md`:
- Line 207: Update the npm version example in DEPLOYMENT.md by replacing the
pipe-separated alternatives for version type and workspace target with separate,
independently copyable shell command examples. Preserve the documented minor,
major, `@mcplint/web`, and mcplint options without using shell pipe syntax.
---
Nitpick comments:
In `@DEPLOYMENT.md`:
- Around line 153-161: Update the mcp-publisher v1.8.0 deployment instructions
to include a reproducible, checksum-verified installation: specify the official
download source, platform-specific artifact, expected checksum, and verification
command, or explicitly reference the workflow’s existing pinned installation
logic. Keep the validate, login github-oidc, and publish commands unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 262c6a43-a04d-4b30-90b6-2a7169adbc43
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (14)
.claude/launch.json.github/workflows/ci.yml.github/workflows/release.yml.npmrcDEPLOYMENT.mdREADME.mdapps/web/app/api/lint/route.tsapps/web/app/api/report/[id]/route.tsapps/web/lib/mcp-server.tsapps/web/lib/rate-limit.tsapps/web/lib/store.tsapps/web/package.jsonapps/web/playwright.config.tspackage.json
🚧 Files skipped from review as they are similar to previous changes (10)
- .claude/launch.json
- .npmrc
- apps/web/package.json
- apps/web/playwright.config.ts
- apps/web/lib/rate-limit.ts
- apps/web/lib/mcp-server.ts
- README.md
- apps/web/lib/store.ts
- apps/web/app/api/report/[id]/route.ts
- apps/web/app/api/lint/route.ts
| ```bash | ||
| npm version patch -w @mcplint/web --include-workspace-root | ||
| # or: npm version patch -w mcplint --include-workspace-root | ||
| # or: npm version minor|major -w @mcplint/web|mcplint --include-workspace-root |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Replace the pipe characters with separate shell examples.
minor|major and @mcplint/web|mcplint are parsed as shell pipelines, not alternatives, so copying this command will fail or execute unintended commands.
Proposed fix
- npm version minor|major -w `@mcplint/web`|mcplint --include-workspace-root
+ npm version minor -w `@mcplint/web` --include-workspace-root
+ npm version major -w `@mcplint/web` --include-workspace-root
+ npm version minor -w mcplint --include-workspace-root
+ npm version major -w mcplint --include-workspace-root📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # or: npm version minor|major -w @mcplint/web|mcplint --include-workspace-root | |
| # or: npm version minor -w `@mcplint/web` --include-workspace-root | |
| # or: npm version major -w `@mcplint/web` --include-workspace-root | |
| # or: npm version minor -w mcplint --include-workspace-root | |
| # or: npm version major -w mcplint --include-workspace-root |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@DEPLOYMENT.md` at line 207, Update the npm version example in DEPLOYMENT.md
by replacing the pipe-separated alternatives for version type and workspace
target with separate, independently copyable shell command examples. Preserve
the documented minor, major, `@mcplint/web`, and mcplint options without using
shell pipe syntax.
mcplint-monorepo.package-lock.jsonand addedpnpm-lock.yamlfor dependency management..gitignoreto include Next.js and environment files.apps/webdirectory with Next.js application structure, including configuration files and initial components.Summary by CodeRabbit
check_mcp_server, plus installation guidance for supported clients.