harness-code-security-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Harness Code Security MCP
2
+
3
+ `harness-code-security-mcp` is the Qwiet AI by Harness MCP server for agent-driven security workflows:
4
+
5
+ - run `sl analyze`
6
+ - list and triage findings
7
+ - fetch data flows
8
+ - request and apply Qwiet AutoFix recommendations
9
+ - look up package CVEs
10
+
11
+ The npm package ships a bundled stdio MCP runtime and a small launcher. It is a proprietary runtime distribution, not a source-code distribution. The launcher is the public `harness-code-security-mcp` binary; it checks for a newer npm-published runtime at startup at most once per day and falls back to the bundled runtime if the update check fails.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install -g harness-code-security-mcp
17
+ ```
18
+
19
+ Then configure your agent MCP server to run:
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "harness-code-security-mcp": {
25
+ "command": "harness-code-security-mcp",
26
+ "env": {
27
+ "QWIET_API_HOST": "app.shiftleft.io",
28
+ "SL_HOME": "${HOME}/.shiftleft"
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Credentials
36
+
37
+ Run `sl auth` first so `~/.shiftleft/config.json` contains `orgId` and `accessToken`.
38
+
39
+ The MCP server can install or update the Qwiet CLI under `~/.shiftleft` through the `sl_ensure_cli` tool. Code analysis runs locally through `sl analyze`; scan results upload to Qwiet AI by Harness.
40
+
41
+ ## Launcher Controls
42
+
43
+ - `harness-code-security-mcp --no-auto-update` disables startup update checks.
44
+ - `harness-code-security-mcp --version-pin x.y.z` runs a specific npm package version from the launcher's local cache.
45
+ - `harness-code-security-mcp --debug` prints launcher diagnostics to stderr.
46
+
47
+ The launcher never writes progress to stdout during normal MCP operation; stdout is reserved for MCP JSON-RPC.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ var __import_meta_url__ = require('url').pathToFileURL(__filename).href;
3
+
4
+ "use strict";var x=Object.create;var _=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var B=(e,r,n,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of O(r))!H.call(e,s)&&s!==n&&_(e,s,{get:()=>r[s],enumerable:!(t=z(r,s))||t.enumerable});return e};var P=(e,r,n)=>(n=e!=null?x(W(e)):{},B(r||!e||!e.__esModule?_(n,"default",{value:e,enumerable:!0}):n,e));var o=P(require("fs"),1),L=P(require("os"),1),c=P(require("path"),1),$=require("child_process"),g=require("url");var k=require("module"),K=(0,k.createRequire)(__import_meta_url__),T="harness-code-security-mcp",A="0.1.0";function p(){try{let e=K("../package.json");return{name:typeof e.name=="string"&&e.name!==""?e.name:T,version:typeof e.version=="string"&&e.version!==""?e.version:A}}catch{return{name:T,version:A}}}var X=1440*60*1e3,q="slmcp",U="last_updated.log",G=12e4,J=3e4,Q=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/,M=/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/,N=process.platform==="win32"?"C:\\Windows\\System32;C:\\Windows;C:\\Program Files\\nodejs":"/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin",R=!1;function f(e){R&&process.stderr.write(`[slmcp-launcher] ${e}
5
+ `)}function Y(){return c.default.join(L.default.homedir(),".shiftleft")}function v(){return c.default.join(Y(),q)}function V(){return process.platform==="win32"?"npm.cmd":"npm"}function C(){return process.platform==="win32"?{Path:N}:{PATH:N}}function S(e){return c.default.join(e,"dist","slmcp.cjs")}function E(){return c.default.join(c.default.dirname((0,g.fileURLToPath)(__import_meta_url__)),"..","dist","slmcp.cjs")}function y(e,r){let n=r.startsWith("@")?r.split("/"):[r];return c.default.join(e,"node_modules",...n)}function h(e){if(!Q.test(e))throw new Error(`Invalid npm package name: ${e}`);return e}function l(e,r="version"){let n=e.replace(/^v/,"");if(!M.test(n))throw new Error(`Invalid ${r}: ${e}`);return n}function I(e){return e instanceof Error&&"code"in e&&(e.code==="EEXIST"||e.code==="ENOTEMPTY"||e.code==="ERR_FS_CP_EEXIST")}function w(e,r){let n=j(e),t=j(r);if(n===null||t===null)return 0;for(let s of["major","minor","patch"]){if(n[s]>t[s])return 1;if(n[s]<t[s])return-1}return 0}function j(e){let r=/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(e);return r===null?null:{major:Number(r[1]),minor:Number(r[2]),patch:Number(r[3])}}function Z(){try{let e=o.default.readFileSync(c.default.join(v(),U),"utf8").trim().split(`
6
+ `)[0],r=Number.parseInt(e,10);return Number.isNaN(r)?null:r*1e3}catch{return null}}function ee(e){o.default.mkdirSync(v(),{recursive:!0,mode:493}),o.default.writeFileSync(c.default.join(v(),U),`${String(Math.floor(e.getTime()/1e3))}
7
+ `,{mode:420})}function re(e){if(!e.autoUpdate||e.versionPin!==null)return f("auto-update disabled by launcher options"),!1;let r=Z();return r===null||Date.now()-r>=X}function ne(e){let r=h(e),n=(0,$.spawnSync)(V(),["view",r,"version"],{encoding:"utf8",shell:!1,timeout:J,env:C()});if(n.status!==0)throw new Error(n.stderr.trim()||`npm view failed for ${r}`);return l(n.stdout.trim(),`npm version for ${r}`)}function F(e,r){let n=h(e),t=l(r);return c.default.join(v(),"versions",t,n.replaceAll("/","__"))}function te(e,r){let n=h(e),t=l(r,"bundled version"),s=c.default.join(v(),"versions"),i=null;try{for(let a of o.default.readdirSync(s,{withFileTypes:!0})){if(!a.isDirectory())continue;let u=a.name;if(!M.test(u)||w(u,t)<=0)continue;let d=S(y(F(n,u),n));o.default.existsSync(d)&&(i===null||w(u,i.version)>0)&&(i={version:u,serverPath:d})}}catch{return null}return i}function D(e,r){let n=h(e),t=l(r),s=F(n,t),i=S(y(s,n)),a=o.default.mkdtempSync(c.default.join("/tmp","slmcp-install-")),u=(0,$.spawnSync)(V(),["install","--omit=dev","--ignore-scripts","--prefix",a,`${n}@${t}`],{encoding:"utf8",shell:!1,timeout:G,stdio:["ignore","ignore","pipe"],env:C()});if(u.status!==0)throw o.default.rmSync(a,{recursive:!0,force:!0}),new Error(u.stderr.trim()||`npm install failed for ${n}@${t}`);let d=S(y(a,n));if(!o.default.existsSync(d))throw o.default.rmSync(a,{recursive:!0,force:!0}),new Error(`installed package did not contain ${c.default.relative(a,d)}`);o.default.mkdirSync(c.default.dirname(s),{recursive:!0,mode:493});try{o.default.renameSync(a,s)}catch(m){if(I(m))return o.default.rmSync(a,{recursive:!0,force:!0}),i;if(m instanceof Error&&"code"in m&&m.code==="EXDEV"){try{o.default.cpSync(a,s,{recursive:!0,force:!1,errorOnExist:!0})}catch(b){if(I(b))return o.default.rmSync(a,{recursive:!0,force:!0}),i;throw b}return o.default.rmSync(a,{recursive:!0,force:!0}),i}throw m}return i}function se(e){let r=p(),n=h(r.name),t=l(r.version,"package version");if(e.versionPin!==null)return D(n,l(e.versionPin,"--version-pin"));let s=te(n,t);if(!re(e))return s!==null?(f(`using cached ${n}@${s.version}`),s.serverPath):E();try{let i=ne(n);return ee(new Date),w(i,t)<=0?s!==null?(f(`using cached ${n}@${s.version}`),s.serverPath):(f(`bundled version ${t} is current`),E()):(f(`installing ${n}@${i}`),D(n,i))}catch(i){return f(`update check failed: ${i instanceof Error?i.message:String(i)}`),s!==null?(f(`using cached ${n}@${s.version}`),s.serverPath):E()}}function ie(){let e=p().name;process.stdout.write(`${e} ${p().version}
8
+
9
+ Usage:
10
+ ${e} Start the MCP stdio server
11
+ ${e} --version Print the bundled launcher version
12
+ ${e} --help Print this help
13
+
14
+ Environment:
15
+ The server process inherits the current environment for Qwiet credentials and settings.
16
+
17
+ Options:
18
+ --no-auto-update Disable startup update checks
19
+ --version-pin x.y.z Run a specific npm package version
20
+ --debug Print launcher diagnostics to stderr
21
+ `)}function oe(e){let r={autoUpdate:!0,debug:!1,help:!1,serverArgs:[],version:!1,versionPin:null};for(let n=0;n<e.length;n+=1){let t=e[n];t==="--help"?r.help=!0:t==="--version"?r.version=!0:t==="--no-auto-update"?r.autoUpdate=!1:t==="--debug"?r.debug=!0:t==="--version-pin"?(r.versionPin=l(e[n+1]||"","--version-pin"),n+=1):t.startsWith("--version-pin=")?r.versionPin=l(t.slice(14),"--version-pin"):r.serverArgs.push(t)}return r}async function ce(e,r){process.argv=[process.argv[0],e,...r],await import((0,g.pathToFileURL)(e).href)}async function ae(){let e=oe(process.argv.slice(2));R=e.debug,e.help?ie():e.version?process.stdout.write(`${p().version}
22
+ `):await ce(se(e),e.serverArgs)}ae().catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
23
+ `),process.exit(1)});