{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"codehealth","informationUri":"https://codehealth.canine.dev","rules":[{"id":"D1","name":"Cyclomatic Complexity","shortDescription":{"text":"Cyclomatic Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D1"},{"id":"D2","name":"Cognitive Complexity","shortDescription":{"text":"Cognitive Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D2"},{"id":"D3","name":"God Classes","shortDescription":{"text":"God Classes"},"helpUri":"https://codehealth.canine.dev/dimensions/D3"},{"id":"D4","name":"Code Duplication","shortDescription":{"text":"Code Duplication"},"helpUri":"https://codehealth.canine.dev/dimensions/D4"},{"id":"D5","name":"Coupling","shortDescription":{"text":"Coupling"},"helpUri":"https://codehealth.canine.dev/dimensions/D5"},{"id":"D7","name":"Architectural Integrity","shortDescription":{"text":"Architectural Integrity"},"helpUri":"https://codehealth.canine.dev/dimensions/D7"},{"id":"D8","name":"Code Coverage","shortDescription":{"text":"Code Coverage"},"helpUri":"https://codehealth.canine.dev/dimensions/D8"},{"id":"D9","name":"Test Distribution","shortDescription":{"text":"Test Distribution"},"helpUri":"https://codehealth.canine.dev/dimensions/D9"},{"id":"D11","name":"Test Reliability","shortDescription":{"text":"Test Reliability"},"helpUri":"https://codehealth.canine.dev/dimensions/D11"},{"id":"D12","name":"Dependency Hygiene","shortDescription":{"text":"Dependency Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/D12"},{"id":"D13","name":"Secret Scanning","shortDescription":{"text":"Secret Scanning"},"helpUri":"https://codehealth.canine.dev/dimensions/D13","relationships":[{"target":{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"]}},{"id":"D14","name":"License Compliance","shortDescription":{"text":"License Compliance"},"helpUri":"https://codehealth.canine.dev/dimensions/D14"},{"id":"D15","name":"Churn \u00D7 Complexity Hotspots","shortDescription":{"text":"Churn \u00D7 Complexity Hotspots"},"helpUri":"https://codehealth.canine.dev/dimensions/D15"},{"id":"D19","name":"Documentation Quality","shortDescription":{"text":"Documentation Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D19"},{"id":"D20","name":"ADR Quality","shortDescription":{"text":"ADR Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D20"},{"id":"D21","name":"Naming Consistency","shortDescription":{"text":"Naming Consistency"},"helpUri":"https://codehealth.canine.dev/dimensions/D21"},{"id":"D25","name":"ADR Conformance","shortDescription":{"text":"ADR Conformance"},"helpUri":"https://codehealth.canine.dev/dimensions/D25"},{"id":"D27","name":"Navigability","shortDescription":{"text":"Navigability"},"helpUri":"https://codehealth.canine.dev/dimensions/D27"},{"id":"D28","name":"Secrets (history)","shortDescription":{"text":"Secrets (history)"},"helpUri":"https://codehealth.canine.dev/dimensions/D28","relationships":[{"target":{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"]}},{"id":"D29","name":"Static Analysis (SAST)","shortDescription":{"text":"Static Analysis (SAST)"},"helpUri":"https://codehealth.canine.dev/dimensions/D29","relationships":[{"target":{"id":"CWE-79","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-89","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-78","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-94","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-77","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-79","CWE-89","CWE-78","CWE-94","CWE-77"]}},{"id":"D30","name":"Dependency Vulnerabilities","shortDescription":{"text":"Dependency Vulnerabilities"},"helpUri":"https://codehealth.canine.dev/dimensions/D30","relationships":[{"target":{"id":"CWE-1395","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-937","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1395","CWE-937"]}},{"id":"D34","name":"Knowledge Freshness","shortDescription":{"text":"Knowledge Freshness"},"helpUri":"https://codehealth.canine.dev/dimensions/D34"},{"id":"D35","name":"Change Coupling","shortDescription":{"text":"Change Coupling"},"helpUri":"https://codehealth.canine.dev/dimensions/D35"},{"id":"D36","name":"Supply-chain Provenance \u0026 Signing","shortDescription":{"text":"Supply-chain Provenance \u0026 Signing"},"helpUri":"https://codehealth.canine.dev/dimensions/D36","relationships":[{"target":{"id":"CWE-1357","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-494","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1357","CWE-494"]}},{"id":"D37","name":"Vulnerability-disclosure Policy","shortDescription":{"text":"Vulnerability-disclosure Policy"},"helpUri":"https://codehealth.canine.dev/dimensions/D37","relationships":[{"target":{"id":"CWE-1059","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1059"]}},{"id":"D43","name":"Malicious Dependencies","shortDescription":{"text":"Malicious Dependencies"},"helpUri":"https://codehealth.canine.dev/dimensions/D43","relationships":[{"target":{"id":"CWE-506","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-506"]}},{"id":"D44","name":"Platform End-of-Life","shortDescription":{"text":"Platform End-of-Life"},"helpUri":"https://codehealth.canine.dev/dimensions/D44"},{"id":"AX10","name":"Code composition","shortDescription":{"text":"Code composition"},"helpUri":"https://codehealth.canine.dev/dimensions/AX10"},{"id":"AX3","name":"Project dependency cycles","shortDescription":{"text":"Project dependency cycles"},"helpUri":"https://codehealth.canine.dev/dimensions/AX3"},{"id":"AX4","name":"Dependency direction","shortDescription":{"text":"Dependency direction"},"helpUri":"https://codehealth.canine.dev/dimensions/AX4"},{"id":"AX8","name":"Test isolation","shortDescription":{"text":"Test isolation"},"helpUri":"https://codehealth.canine.dev/dimensions/AX8"},{"id":"AXB2","name":"Runtime readiness","shortDescription":{"text":"Runtime readiness"},"helpUri":"https://codehealth.canine.dev/dimensions/AXB2"},{"id":"ED5","name":"Idempotency","shortDescription":{"text":"Idempotency"},"helpUri":"https://codehealth.canine.dev/dimensions/ED5"},{"id":"M1","name":"Documentation (README)","shortDescription":{"text":"Documentation (README)"},"helpUri":"https://codehealth.canine.dev/dimensions/M1"},{"id":"M2","name":"Architecture documentation","shortDescription":{"text":"Architecture documentation"},"helpUri":"https://codehealth.canine.dev/dimensions/M2"},{"id":"M3","name":"Folder \u0026 project structure","shortDescription":{"text":"Folder \u0026 project structure"},"helpUri":"https://codehealth.canine.dev/dimensions/M3"},{"id":"M4","name":"Documentation accuracy","shortDescription":{"text":"Documentation accuracy"},"helpUri":"https://codehealth.canine.dev/dimensions/M4"},{"id":"P1","name":"CI/CD gates","shortDescription":{"text":"CI/CD gates"},"helpUri":"https://codehealth.canine.dev/dimensions/P1"},{"id":"P12","name":"CI test-gate honesty","shortDescription":{"text":"CI test-gate honesty"},"helpUri":"https://codehealth.canine.dev/dimensions/P12"},{"id":"P3","name":"Security \u0026 performance tooling","shortDescription":{"text":"Security \u0026 performance tooling"},"helpUri":"https://codehealth.canine.dev/dimensions/P3"},{"id":"P4","name":"Deployment \u0026 Rollback","shortDescription":{"text":"Deployment \u0026 Rollback"},"helpUri":"https://codehealth.canine.dev/dimensions/P4"},{"id":"P6","name":"Release Hygiene","shortDescription":{"text":"Release Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/P6"},{"id":"PF3","name":"Async \u0026 latency hygiene","shortDescription":{"text":"Async \u0026 latency hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/PF3"},{"id":"SC1","name":"Supply-chain hygiene","shortDescription":{"text":"Supply-chain hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/SC1"},{"id":"X10","name":"Duplicated predicate","shortDescription":{"text":"Duplicated predicate"},"helpUri":"https://codehealth.canine.dev/dimensions/X10"},{"id":"X28","name":"Index access outside its own emptiness guard","shortDescription":{"text":"Index access outside its own emptiness guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X28"},{"id":"X6","name":"Hand-rolled structured-format parsing","shortDescription":{"text":"Hand-rolled structured-format parsing"},"helpUri":"https://codehealth.canine.dev/dimensions/X6"},{"id":"X7","name":"Silent fallback defaults","shortDescription":{"text":"Silent fallback defaults"},"helpUri":"https://codehealth.canine.dev/dimensions/X7"},{"id":"X9","name":"Subsumed condition operand","shortDescription":{"text":"Subsumed condition operand"},"helpUri":"https://codehealth.canine.dev/dimensions/X9"}]}},"results":[{"ruleId":"D1","level":"warning","message":{"text":"verify-npm-audit.validateAudit (cyclomatic 50): verify-npm-audit.validateAudit has cyclomatic complexity 50 (threshold 15). Of this number, 49 points are the body\u0027s own statements and 1 belongs to one function literal inside it that branches. To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-npm-audit.mjs"},"region":{"startLine":299}}}],"partialFingerprints":{"codehealthFindingId/v1":"627265e567931ec02a780282221c27f6bda50067fa1bff97000942f0bcb46138"}},{"ruleId":"D1","level":"warning","message":{"text":"verify-release.verify_package (cyclomatic 25): verify-release.verify_package has cyclomatic complexity 25 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-release.py"},"region":{"startLine":93}}}],"partialFingerprints":{"codehealthFindingId/v1":"de0e959600ab8ee73380096f3c23bd303e6607ac27ab8e9d8418cc2a9413dfce"}},{"ruleId":"D1","level":"warning","message":{"text":"McpClientModule.convertContentBlock (cyclomatic 17): McpClientModule.convertContentBlock has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Client/McpClient.fs"},"region":{"startLine":279}}}],"partialFingerprints":{"codehealthFindingId/v1":"e8773f9f374b8914378eb2d75b5ec1b38c6123ff78e31fe2118a0c94555fa4f7"}},{"ruleId":"D1","level":"warning","message":{"text":"Interop.fromSdkContentBlock (cyclomatic 16): Interop.fromSdkContentBlock has cyclomatic complexity 16 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Core/Interop.fs"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"22f8f0fb974ae47ff3cdc1b92ff77faf717881173582d18aa7d5b696d8cb7b9d"}},{"ruleId":"D2","level":"warning","message":{"text":"verify-npm-audit.validateAudit (cognitive 74): verify-npm-audit.validateAudit has cognitive complexity 74 (threshold 15). Drivers by points: if/else 31 (62 pts), boolean chains 9, ternaries 2, loops 1 (nesting depth added 31). Of this number, 73 points are the body\u0027s own statements and 1 belongs to one function literal inside it that branches. To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body. This file is where this pass\u0027s cognitive complexity CONCENTRATES: scripts/verify-npm-audit.mjs holds 2 of the 10 methods over the threshold \u2014 including the worst \u2014 and 60 of the 143 points over it (42%), 2.6\u00D7 the next-largest file (src/FsMcp.Client/McpClient.fs at 23). No single row can show this, because each is measured only against the threshold: reducing this one file moves this dimension further than any other file in the repository."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-npm-audit.mjs"},"region":{"startLine":299}}}],"partialFingerprints":{"codehealthFindingId/v1":"06742bff48e6c610baa67ebe156ac1e198d462ea7ea661c94ce56dd1bbe2bef5"}},{"ruleId":"D2","level":"warning","message":{"text":"McpClientModule.convertContentBlock (cognitive 36): McpClientModule.convertContentBlock has cognitive complexity 36 (threshold 15). Drivers by points: if/else 14 (25 pts), match/switch 5 (11 pts) (nesting depth added 17). The drivers above price the dispatch low by construction \u2014 a dispatch is charged once however many cases it lists, while each branch inside an arm is charged in full \u2014 so most of this count is what the case bodies hold, and the arms are where it can be reduced. To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Client/McpClient.fs"},"region":{"startLine":279}}}],"partialFingerprints":{"codehealthFindingId/v1":"15dca955b5ca48ed8e78ac9146addf0c3ef25be855ed9ef804eb27b8e173e3f0"}},{"ruleId":"D2","level":"warning","message":{"text":"Interop.fromSdkContentBlock (cognitive 35): Interop.fromSdkContentBlock has cognitive complexity 35 (threshold 15). Drivers by points: if/else 12 (24 pts), match/switch 5 (11 pts) (nesting depth added 18). The drivers above price the dispatch low by construction \u2014 a dispatch is charged once however many cases it lists, while each branch inside an arm is charged in full \u2014 so most of this count is what the case bodies hold, and the arms are where it can be reduced. To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Core/Interop.fs"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"eccb4e2098b9c7c5c8984c10e48c12a8f8a3ea7182ddba5a24a7683af7b12ea7"}},{"ruleId":"D2","level":"warning","message":{"text":"verify-nuget-audit.vulnerabilities (cognitive 30): verify-nuget-audit.vulnerabilities has cognitive complexity 30 (threshold 15). Drivers by points: if/else 5 (16 pts), loops 4 (10 pts), boolean chains 4 (nesting depth added 17). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-nuget-audit.py"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"5f8788962ff905b3e116640afbab233d5e81e617923e5d1adb075a1b12e31c4d"}},{"ruleId":"D2","level":"warning","message":{"text":"SchemaGen.generateSchema (cognitive 27): SchemaGen.generateSchema has cognitive complexity 27 (threshold 15). Drivers by points: match/switch 6 (17 pts), if/else 2 (6 pts), loops 1 (3 pts), boolean chains 1 (nesting depth added 17). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/TypedHandlers.fs"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"5d1795d0b9af2172994b87a3fc5d03188d9f197f6a4cc08f295f36abb207f343"}},{"ruleId":"D2","level":"warning","message":{"text":"verify-release.verify_package (cognitive 24): verify-release.verify_package has cognitive complexity 24 (threshold 15). Drivers by points: if/else 19, boolean chains 4, ternaries 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-release.py"},"region":{"startLine":93}}}],"partialFingerprints":{"codehealthFindingId/v1":"5337c09b9649d4f3467dd59b7b4eb66db11722895c7b7fefc6a241e1880d8f69"}},{"ruleId":"D2","level":"warning","message":{"text":"ResourceSubscriptions.subscribe (cognitive 17): ResourceSubscriptions.subscribe has cognitive complexity 17 (threshold 15). Drivers by points: if/else 8 (13 pts), boolean chains 2, match/switch 1 (2 pts) (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/Subscriptions.fs"},"region":{"startLine":73}}}],"partialFingerprints":{"codehealthFindingId/v1":"1f61bfc82980075113a54311b9112ab4ce8e0422af50b348d1fc6c2d537aadbb"}},{"ruleId":"D2","level":"warning","message":{"text":"McpClientModule.convertResourceContent (cognitive 17): McpClientModule.convertResourceContent has cognitive complexity 17 (threshold 15). Drivers by points: if/else 8 (12 pts), match/switch 3 (5 pts) (nesting depth added 6). The drivers above price the dispatch low by construction \u2014 a dispatch is charged once however many cases it lists, while each branch inside an arm is charged in full \u2014 so most of this count is what the case bodies hold, and the arms are where it can be reduced. To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Client/McpClient.fs"},"region":{"startLine":429}}}],"partialFingerprints":{"codehealthFindingId/v1":"0f6115c32aff37744a801c57403667e245e1ea9e13bf0e899562328c31663a54"}},{"ruleId":"D2","level":"warning","message":{"text":"EnterpriseTokenExchangeSecurityHandler.SendAsync (cognitive 17): EnterpriseTokenExchangeSecurityHandler.SendAsync has cognitive complexity 17 (threshold 15). Drivers by points: if/else 6 (10 pts), loops 1 (3 pts), boolean chains 2, error handling 2 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Client/EnterpriseManagedAuthorization.fs"},"region":{"startLine":596}}}],"partialFingerprints":{"codehealthFindingId/v1":"7bdc51739bd7fac5702095024157d1312f8d093ae60e82e5bdfbcf45191c06fd"}},{"ruleId":"D2","level":"warning","message":{"text":"verify-npm-audit.validateDependencyPolicy (cognitive 16): verify-npm-audit.validateDependencyPolicy has cognitive complexity 16 (threshold 15). Drivers by points: if/else 9 (12 pts), boolean chains 2, loops 2 (nesting depth added 3). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body. This file is where this pass\u0027s cognitive complexity CONCENTRATES: scripts/verify-npm-audit.mjs holds 2 of the 10 methods over the threshold \u2014 including the worst \u2014 and 60 of the 143 points over it (42%), 2.6\u00D7 the next-largest file (src/FsMcp.Client/McpClient.fs at 23). No single row can show this, because each is measured only against the threshold: reducing this one file moves this dimension further than any other file in the repository."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-npm-audit.mjs"},"region":{"startLine":241}}}],"partialFingerprints":{"codehealthFindingId/v1":"332df1060fee05edac6f8fe3d173f837e1e3c691bb69156c32875a0d32750fc8"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: verify-npm-audit.runNegativeSelfTests: FunctionTooLong \u2014 runNegativeSelfTests runs 285 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 185 over it, 2.85\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-npm-audit.mjs"},"region":{"startLine":500}}}],"partialFingerprints":{"codehealthFindingId/v1":"ffb7b44d5030fdaf1e70a7c3b2a23cc6f4265d8ea636d3e60eeb240e93255da3"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: scripts/verify-npm-audit.mjs: FileTooLong \u2014 786 significant lines (blank, comment-only and punctuation-only lines excluded). The bar is 500 significant lines; this is 286 over it, 1.57\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/verify-npm-audit.mjs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"188b526b0c22d1b8beaf5f14a7a320d11ed04d81bf49cea840da092ed54be0f1"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: FsMcp.Client/EnterpriseManagedAuthorization.fs: FileTooLong \u2014 782 significant lines (blank, comment-only and punctuation-only lines excluded). The bar is 500 significant lines; this is 282 over it, 1.56\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Client/EnterpriseManagedAuthorization.fs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"594318519157cf8a1eb32f288e92a82eae992128909b51813e3456fd5ebcd146"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFunctions: McpClientModule: TooManyFunctions \u2014 37 functions. The bar is 30 functions; this is 7 over it, 1.23\u00D7 the bar. The counted members are a module\u0027s functions \u2014 a module holds no instance state, so there is no shared data to group them by and no type to move them onto. To reduce it, extract each cohesive family of functions into a new module of its own and have this one delegate to it, so no single module carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Client/McpClient.fs"},"region":{"startLine":113}}}],"partialFingerprints":{"codehealthFindingId/v1":"943333286d5f3514aaa01c08b1849fe29deea0957a9108f9eb7b88b8eca348d8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17\u201318 lines \u00D7 3): src/FsMcp.Server/Streaming.fs:67-83 | src/FsMcp.Server/ToolBuilder.fs:27-43 | src/FsMcp.Server/TypedHandlers.fs:162-179 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 3 call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060src/FsMcp.Server/Streaming.fs:67\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/Streaming.fs"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"70cfd2a30fbca0e0d9d24ef76c1400ba5b512f2bc769ba7eb9f6233c8adab08b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): src/FsMcp.Server/Transport.fs:172-180 | src/FsMcp.Server/Transport.fs:220-228 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060src/FsMcp.Server/Transport.fs:172\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/Transport.fs"},"region":{"startLine":172}}}],"partialFingerprints":{"codehealthFindingId/v1":"3e89655cc5a6de24739e08f7f19b457da3a0796b303a5298439cd38cee7ca1be"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): src/FsMcp.Server/TypedHandlers.fs:211-217 | src/FsMcp.Server/TypedHandlers.fs:267-273 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060src/FsMcp.Server/TypedHandlers.fs:211\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/TypedHandlers.fs"},"region":{"startLine":211}}}],"partialFingerprints":{"codehealthFindingId/v1":"f5d4a185255b7f68b158a65479ebdc077d715b763db302612772d3a5a35ab745"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): src/FsMcp.Server/TypedHandlers.fs:218-228 | src/FsMcp.Server/TypedHandlers.fs:274-284 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/TypedHandlers.fs"},"region":{"startLine":218}}}],"partialFingerprints":{"codehealthFindingId/v1":"34fd023ec6a78b90a071a16a6446cb02e8831085efec6077ca374f7dcf7393fb"}},{"ruleId":"D8","level":"warning","message":{"text":"Low coverage: src/FsMcp.Core/Interop.fs: 17.9% line coverage (7/39)."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Core/Interop.fs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f8063696377eab59e2d08b63e6c4bbf2cab8d8f41d65e5fc0f4cc6ff21dd7c32"}},{"ruleId":"D8","level":"warning","message":{"text":"Low coverage: src/FsMcp.Server/DynamicServer.fs: 11.1% line coverage (2/18)."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/FsMcp.Server/DynamicServer.fs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"e95ea5f8741ece7e3a79b503ed8bb8388c1376caaf04112c80304f632f7a41dc"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no architecture or design documentation: The architecture/design markdown files are referenced but not visible in the summary: the root README\u0027s only architecture mention is a one-line CI badge, and no architecture document appears among the 20 architecture/Docs markdown files. Add an architecture section describing how FsMcp composes with the Microsoft MCP SDK (CE vs middleware, transport options) so readers can understand why each package exists."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"docs/index.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"c334a70505afa4daa2af8f154917ed2cd6ce2b63062cc24f27ed83dc966043b4"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no project overview: The \u0027Server Guide\u0027 section describes mcpServer, tool registration, ServerConfig validation, and transport options but does not state what the project is or what it does beyond this CE. Add a one-line overview sentence above the guide stating that this document shows how to define MCP tools, resources, and prompts using FsMcp."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"website/docs/server-guide.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"97e52ad572f0070b516959b5f1985de7b732c0ce80382302638ee56726638c54"}},{"ruleId":"D27","level":"note","message":{"text":"Scattered collaborators: 91 % of calls cross a namespace and only 38 % of collaborators are co-located \u2014 group each feature\u0027s code into a vertical slice so a call\u0027s collaborators sit together."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"5aad4a4530bac3928d3a065a664be89d023ecfc865f9bf79ac05754b3bffe322"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"1b213f6eedd4b140d0bc37bdf1496f72811a643f34064f12518b32e9e83bcfc7"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"0e17f71490e4d120a48b2b2881ab866c93b272bef2febb673a3e8e42b2c288ab"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"759dca709f1a032fb6f624ce672e6afb5558fce53ecf01fb73618c4d33923164"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"eb00976a698a5d68999b7ee6d27206fd374e916853e7ff1ead5eed42386f043f"}},{"ruleId":"P12","level":"warning","message":{"text":"Coverage collected but not gated: CI collects a coverage report but no step enforces a minimum \u2014 coverage could halve and CI stays green. Add a step that fails the build when coverage drops below a floor (your coverage tool\u0027s minimum-threshold flag, or a coverage-gate action) so the number guards something. What was searched, so you can tell an absence from a miss: this repository\u0027s CI files AND its coverage configuration \u2014 the well-known coverage and test-runner config files, read at the repository root and inside workspace package directories two levels down, so a floor declared beside the tests rather than in the pipeline is credited \u2014 matched against the threshold settings this check knows by name. A floor set in your coverage service\u0027s web UI rather than in a committed file, or under a setting whose name is not one of those, is not seen here."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"7f63c06044cf998d9a0698692df30d8873e29bc9b0c98ee829255017c1193d33"}},{"ruleId":"P3","level":"note","message":{"text":"No SAST: No static application security testing detected. For this repository\u0027s stack, add \u0060semgrep --config=auto\u0060 plus gitleaks for committed secrets (F# is not a CodeQL language and has no language-specific SAST engine) as a CI step. What was searched, so you can tell an absence from a miss: the 11838 CI workflow file(s) in this repository, and the scanner and linter configuration checked in beside them. A scan that runs outside CI, one configured in your forge\u0027s web UI rather than in a committed file, or a tool whose name is none of those this check carries, is not seen \u2014 if that is your case the row is wrong, and saying so is more useful than adding a second scanner."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"6e54424179c892f03ef2fd003130ac4bd43f39bbf3b1ca0a0143e25acb80ec87"}}],"taxonomies":[{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d","organization":"MITRE","informationUri":"https://cwe.mitre.org/","isComprehensive":false,"shortDescription":{"text":"The MITRE Common Weakness Enumeration (CWE)."},"taxa":[{"id":"CWE-1059","guid":"a2381a08-60f6-9554-a8b8-f3018cfaaca5","name":"Insufficient Technical Documentation","shortDescription":{"text":"Insufficient Technical Documentation"},"helpUri":"https://cwe.mitre.org/data/definitions/1059.html"},{"id":"CWE-1357","guid":"e4d2e772-757e-0a5c-bd7d-77052949d866","name":"Reliance on Insufficiently Trustworthy Component","shortDescription":{"text":"Reliance on Insufficiently Trustworthy Component"},"helpUri":"https://cwe.mitre.org/data/definitions/1357.html"},{"id":"CWE-1395","guid":"800e09e7-c11a-8654-9fa6-86f398995fed","name":"Dependency on Vulnerable Third-Party Component","shortDescription":{"text":"Dependency on Vulnerable Third-Party Component"},"helpUri":"https://cwe.mitre.org/data/definitions/1395.html"},{"id":"CWE-259","guid":"ae9ad959-fbb6-9d5e-892d-3dca66da0b69","name":"Use of Hard-coded Password","shortDescription":{"text":"Use of Hard-coded Password"},"helpUri":"https://cwe.mitre.org/data/definitions/259.html"},{"id":"CWE-494","guid":"b8a65e0d-e459-4a55-a931-fc1136482375","name":"Download of Code Without Integrity Check","shortDescription":{"text":"Download of Code Without Integrity Check"},"helpUri":"https://cwe.mitre.org/data/definitions/494.html"},{"id":"CWE-506","guid":"401d6455-56e3-0552-9a39-f77461673e3f","name":"CWE-506","shortDescription":{"text":"CWE-506"},"helpUri":"https://cwe.mitre.org/data/definitions/506.html"},{"id":"CWE-77","guid":"332c8ade-6612-9f56-a06b-d8d90b1a8750","name":"Command Injection","shortDescription":{"text":"Command Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/77.html"},{"id":"CWE-78","guid":"2e31ceaf-c7ae-2e5e-9661-cfb1362789cf","name":"OS Command Injection","shortDescription":{"text":"OS Command Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/78.html"},{"id":"CWE-79","guid":"fd45580b-e8c4-fc5e-8c2f-aa8fab0b4dbf","name":"Cross-site Scripting (XSS)","shortDescription":{"text":"Cross-site Scripting (XSS)"},"helpUri":"https://cwe.mitre.org/data/definitions/79.html"},{"id":"CWE-798","guid":"5e8f057d-fee3-995a-a0cb-9fc5b0d174d1","name":"Use of Hard-coded Credentials","shortDescription":{"text":"Use of Hard-coded Credentials"},"helpUri":"https://cwe.mitre.org/data/definitions/798.html"},{"id":"CWE-89","guid":"6d08fdad-37eb-c150-bbf0-d7d946863407","name":"SQL Injection","shortDescription":{"text":"SQL Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/89.html"},{"id":"CWE-937","guid":"16f316ae-415c-b354-a59b-1f7905f756e9","name":"Using Components with Known Vulnerabilities","shortDescription":{"text":"Using Components with Known Vulnerabilities"},"helpUri":"https://cwe.mitre.org/data/definitions/937.html"},{"id":"CWE-94","guid":"75e7f50c-6c2f-dd52-bf40-bf6c52b861fd","name":"Code Injection","shortDescription":{"text":"Code Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/94.html"}]}],"properties":{"codehealthPublication":{"public":true,"notice":"This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings \u2014 which rule fired, in which file, on which line, and how to fix it \u2014 are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.","securityFindingsRedacted":4,"secretScannerRunsExcluded":0}},"redactionTokens":["A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."]}]}