@githits/mcp 0.6.4 → 0.9.1
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/dist/client.d.ts +2 -3
- package/dist/client.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -13
- package/dist/shared/chunk-xd3b13qj.js +1209 -0
- package/dist/smoke-test.js +1 -1
- package/package.json +1 -1
- package/dist/shared/chunk-179kkbsr.js +0 -1209
package/dist/smoke-test.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var EXPECTED_MCP_TOOLS=["get_example","search_language","pkg_info","pkg_deps","pkg_vulns","pkg_changelog","pkg_upgrade_review","docs_list","docs_read","code_files","code_read","code_grep","search","search_status","feedback"];var DEFAULT_TEXT_LIMIT=12000;var SMOKE_PACKAGE_VERSION="5.2.1";var SMOKE_PACKAGE_TARGET={registry:"npm",package_name:"express",version:SMOKE_PACKAGE_VERSION};function assert(condition,message){if(!condition){throw new Error(message)}}function parseJson(text,context){try{return JSON.parse(text)}catch(error){const message=error instanceof Error?error.message:"unknown error";throw new Error(`${context}: expected parseable JSON (${message})`)}}function assertNotJson(text,context){try{JSON.parse(text)}catch{return}throw new Error(`${context}: default response unexpectedly parsed as JSON`)}function assertRecord(value,context){assert(value!==null&&typeof value==="object"&&!Array.isArray(value),`${context}: expected object`)}function headerValue(headers,name){if(!headers)return null;if("get"in headers&&typeof headers.get==="function"){return headers.get(name)}const lowerName=name.toLowerCase();for(const[key,value]of Object.entries(headers)){if(key.toLowerCase()===lowerName)return value??null}return null}function resultText(result,context){assert(Array.isArray(result.content),`${context}: expected content array`);const first=result.content[0];assert(first?.type==="text",`${context}: expected text content`);assert(typeof first.text==="string",`${context}: expected text string`);return first.text}function assertCleanErrorEnvelope(result,context){assert(result.isError===true,`${context}: expected MCP error result`);const payload=parseJson(resultText(result,context),context);assertRecord(payload,context);assert(typeof payload.error==="string"&&payload.error.length>0,`${context}: missing error`);assert(typeof payload.code==="string"&&payload.code.length>0,`${context}: missing code`);assert(typeof payload.retryable==="boolean",`${context}: missing retryable`);return payload}function assertHttpUnauthorizedChallenge(metadata,context="unauthenticated HTTP challenge"){assert(metadata.status===401,`${context}: expected HTTP 401`);const challenge=headerValue(metadata.headers,"www-authenticate");assert(typeof challenge==="string"&&challenge.length>0,`${context}: missing WWW-Authenticate header`);assert(/^Bearer\b/i.test(challenge),`${context}: expected Bearer challenge, got ${challenge}`)}function assertDefaultText(result,context){assert(result.isError!==true,`${context}: expected success`);const text=resultText(result,context);assert(text.length>0,`${context}: expected non-empty text`);assert(text.length<DEFAULT_TEXT_LIMIT,`${context}: default text too large (${text.length} chars)`);assertNotJson(text,context);assert(!text.includes("--lifecycle"),`${context}: leaked CLI lifecycle flag`);assert(!text.includes("--verbose"),`${context}: leaked CLI verbose flag`);return text}function assertJsonResult(result,context){assert(result.isError!==true,`${context}: expected success`);return parseJson(resultText(result,context),context)}function assertErrorCode(result,context,code){const envelope=assertCleanErrorEnvelope(result,context);assert(envelope.code===code,`${context}: expected ${code}, got ${envelope.code}`)}async function callTool(caller,name,args){return await caller.callTool(name,args)}async function callToolText(caller,name,args){const result=await caller.callTool(name,args);const text=resultText(result,name);if(result.isError===true){throw new Error(text)}return text}async function assertLiveOrAuthRequired(caller,logger){const result=await callTool(caller,"search_language",{query:"python"});if(result.isError===true){const envelope=assertCleanErrorEnvelope(result,"search_language auth probe");assert(envelope.code==="AUTH_REQUIRED",`auth probe returned unexpected code ${envelope.code}`);logger.log("AUTH_REQUIRED: live smoke skipped");return false}const text=assertDefaultText(result,"search_language default");assert(text.includes("python (Python)"),"search_language default missing Python display name");assert(text.includes("aliases:"),"search_language default missing aliases");return true}async function runLiveSmoke(caller){const languageJson=assertJsonResult(await callTool(caller,"search_language",{query:"python",format:"json"}),"search_language json");assert(Array.isArray(languageJson),"search_language json: expected array");const exampleText=assertDefaultText(await callTool(caller,"get_example",{query:"express hello world",language:"javascript"}),"get_example default");assert(exampleText.includes("solution_id:"),"get_example default missing solution_id hint");const exampleJson=assertJsonResult(await callTool(caller,"get_example",{query:"express hello world",language:"javascript",format:"json"}),"get_example json");assertRecord(exampleJson,"get_example json");assert(typeof exampleJson.result==="string","get_example json missing result");const pkgInfoText=assertDefaultText(await callTool(caller,"pkg_info",{registry:"npm",package_name:"express"}),"pkg_info default");assert(pkgInfoText.includes("express"),"pkg_info default missing package name");assert(pkgInfoText.includes("Repository")&&pkgInfoText.includes("stars"),"pkg_info default missing repository popularity");assert(pkgInfoText.includes("Vulnerabilities"),"pkg_info default missing vulnerability status");assert(!pkgInfoText.includes("Install")&&!pkgInfoText.includes("Usage"),"pkg_info default should not include quickstart fields");const pkgInfoJson=assertJsonResult(await callTool(caller,"pkg_info",{registry:"npm",package_name:"express",format:"json"}),"pkg_info json");assertRecord(pkgInfoJson,"pkg_info json");assert(pkgInfoJson.registry==="npm","pkg_info json registry mismatch");assert(pkgInfoJson.name==="express","pkg_info json name mismatch");assert(typeof pkgInfoJson.version==="string","pkg_info json missing version");assert(!("install"in pkgInfoJson)&&!("usage"in pkgInfoJson),"pkg_info json should not include quickstart fields");const depsText=assertDefaultText(await callTool(caller,"pkg_deps",{registry:"npm",package_name:"express"}),"pkg_deps default");assert(depsText.includes("Runtime dependencies:"),"pkg_deps default missing runtime heading");assert(depsText.includes('pass lifecycle="all"'),"pkg_deps default missing MCP-native lifecycle hint");const depsAllText=assertDefaultText(await callTool(caller,"pkg_deps",{registry:"npm",package_name:"express",lifecycle:"all"}),"pkg_deps lifecycle all");assert(depsAllText.includes("Dependency groups:"),"pkg_deps lifecycle all missing groups heading");assert(!depsAllText.includes("Hidden groups:"),"pkg_deps lifecycle all still hides groups");const depsJson=assertJsonResult(await callTool(caller,"pkg_deps",{registry:"npm",package_name:"express",format:"json"}),"pkg_deps json");assertRecord(depsJson,"pkg_deps json");assertRecord(depsJson.runtime,"pkg_deps json runtime");const vulnsText=assertDefaultText(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express"}),"pkg_vulns default");assert(vulnsText.includes("express")||vulnsText.includes("vulnerab"),"pkg_vulns default missing context");assert(!vulnsText.includes("use -v"),"pkg_vulns leaked CLI -v hint");const filteredVulnsText=assertDefaultText(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"lodash",version:"4.17.20",min_severity:"high"}),"pkg_vulns filtered default");assert(filteredVulnsText.includes("Filter severity >= high"),"pkg_vulns filtered default missing filter echo");assert(!filteredVulnsText.includes("use -v"),"pkg_vulns filtered default leaked CLI -v hint");const vulnsJson=assertJsonResult(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express",format:"json"}),"pkg_vulns json");assertRecord(vulnsJson,"pkg_vulns json");assert("summary"in vulnsJson||"advisories"in vulnsJson,"pkg_vulns json missing vulnerability data");const filteredVulnsJson=assertJsonResult(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"lodash",version:"4.17.20",min_severity:"high",format:"json"}),"pkg_vulns filtered json");assertRecord(filteredVulnsJson,"pkg_vulns filtered json");assertRecord(filteredVulnsJson.filter,"pkg_vulns filtered json filter");assert(filteredVulnsJson.filter.minSeverity==="high","pkg_vulns filtered json missing severity filter echo");const scopedVulnsText=assertDefaultText(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express",advisory_scope:"non_affecting"}),"pkg_vulns scoped default");assert(scopedVulnsText.includes("Scope historical advisories only"),"pkg_vulns scoped default missing scope echo");assert(scopedVulnsText.includes("No active vulnerabilities affect this version"),"pkg_vulns scoped default missing current-risk statement");assert(!scopedVulnsText.includes("use -v"),"pkg_vulns scoped default leaked CLI -v hint");const scopedVulnsJson=assertJsonResult(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express",advisory_scope:"non_affecting",format:"json"}),"pkg_vulns scoped json");assertRecord(scopedVulnsJson,"pkg_vulns scoped json");assertRecord(scopedVulnsJson.filter,"pkg_vulns scoped json filter");assert(scopedVulnsJson.filter.advisoryScope==="non_affecting","pkg_vulns scoped json missing advisory scope echo");const changelogText=assertDefaultText(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:1}),"pkg_changelog default");assert(!changelogText.includes("--verbose"),"pkg_changelog default leaked CLI verbose flag");if(changelogText.includes("truncated")||changelogText.includes("full bodies")){assert(changelogText.includes('pass verbose=true, body_lines=<n>, or format="json"'),"pkg_changelog truncation hint is not MCP-native")}const changelogBodyLinesText=assertDefaultText(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:2,body_lines:3}),"pkg_changelog body_lines");assert(changelogBodyLinesText.includes('pass verbose=true, body_lines=<n>, or format="json"'),"pkg_changelog body_lines missing MCP-native truncation hint");assert(!changelogBodyLinesText.includes("--verbose"),"pkg_changelog body_lines leaked CLI verbose flag");const changelogJson=assertJsonResult(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:1,format:"json"}),"pkg_changelog json");assertRecord(changelogJson,"pkg_changelog json");assertRecord(changelogJson.entries,"pkg_changelog json entries");const changelogTimeline=assertDefaultText(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:2,omit_bodies:true}),"pkg_changelog timeline");assert(!changelogTimeline.includes("What's Changed"),"pkg_changelog omit_bodies=true still emitted bodies");const upgradeReviewText=assertDefaultText(await callTool(caller,"pkg_upgrade_review",{registry:"npm",package_name:"express",current_version:"5.0.0",target_version:"5.2.1",skip_transitive_security:true}),"pkg_upgrade_review default");assert(upgradeReviewText.includes("pkg_upgrade_review")&&upgradeReviewText.includes("vulnerabilities")&&upgradeReviewText.includes("changes"),"pkg_upgrade_review default missing evidence sections");assert(!upgradeReviewText.includes("recommendation")&&!upgradeReviewText.includes("risk level"),"pkg_upgrade_review default leaked assessment language");const upgradeReviewJson=assertJsonResult(await callTool(caller,"pkg_upgrade_review",{registry:"npm",package_name:"express",current_version:"5.0.0",target_version:"5.2.1",skip_transitive_security:true,format:"json"}),"pkg_upgrade_review json");assertRecord(upgradeReviewJson,"pkg_upgrade_review json");assertRecord(upgradeReviewJson.summary,"pkg_upgrade_review json summary");assert(Array.isArray(upgradeReviewJson.reviews),"pkg_upgrade_review json missing reviews array");const firstUpgradeReview=upgradeReviewJson.reviews[0];assert(firstUpgradeReview,"pkg_upgrade_review json missing first review");for(const forbidden of["risk","riskLevel","recommendation","findings","verification"]){assert(!(forbidden in firstUpgradeReview),`pkg_upgrade_review json leaked judgment field ${forbidden}`)}const docsText=assertDefaultText(await callTool(caller,"docs_list",{...SMOKE_PACKAGE_TARGET,limit:2}),"docs_list default");assert(docsText.includes("docs_read page_id="),"docs_list default missing docs_read follow-up");const docsJson=assertJsonResult(await callTool(caller,"docs_list",{...SMOKE_PACKAGE_TARGET,limit:2,format:"json"}),"docs_list json");assertRecord(docsJson,"docs_list json");assert(Array.isArray(docsJson.pages),"docs_list json missing pages array");const firstPage=docsJson.pages[0];assert(firstPage&&typeof firstPage.pageId==="string","docs_list json missing readable page id");const docReadText=assertDefaultText(await callTool(caller,"docs_read",{page_id:firstPage.pageId,start_line:1,end_line:5}),"docs_read default");assert(docReadText.length>0,"docs_read default missing content");const docReadJson=assertJsonResult(await callTool(caller,"docs_read",{page_id:firstPage.pageId,start_line:1,end_line:5,format:"json"}),"docs_read json");assertRecord(docReadJson,"docs_read json");assert(typeof docReadJson.content==="string","docs_read json missing content");const codeFilesText=assertDefaultText(await callTool(caller,"code_files",{target:SMOKE_PACKAGE_TARGET,path_prefix:"package.json",limit:1}),"code_files default");assert(codeFilesText.includes("package.json"),"code_files default missing package.json");const codeFilesJson=assertJsonResult(await callTool(caller,"code_files",{target:SMOKE_PACKAGE_TARGET,path_prefix:"package.json",limit:1,format:"json"}),"code_files json");assertRecord(codeFilesJson,"code_files json");assert(Array.isArray(codeFilesJson.files),"code_files json missing files array");const codeReadText=assertDefaultText(await callTool(caller,"code_read",{target:SMOKE_PACKAGE_TARGET,path:"package.json",start_line:1,end_line:5}),"code_read default");assert(/^1\s+/m.test(codeReadText),"code_read default missing line numbers");const codeReadJson=assertJsonResult(await callTool(caller,"code_read",{target:SMOKE_PACKAGE_TARGET,path:"package.json",start_line:1,end_line:5,format:"json"}),"code_read json");assertRecord(codeReadJson,"code_read json");assert(codeReadJson.path==="package.json","code_read json path mismatch");const codeGrepText=assertDefaultText(await callTool(caller,"code_grep",{target:SMOKE_PACKAGE_TARGET,pattern:"express",path:"package.json",max_matches:1}),"code_grep default");assert(codeGrepText.includes("package.json"),"code_grep default missing package.json");const codeGrepJson=assertJsonResult(await callTool(caller,"code_grep",{target:SMOKE_PACKAGE_TARGET,pattern:"express",path:"package.json",max_matches:1,format:"json"}),"code_grep json");assertRecord(codeGrepJson,"code_grep json");assert("matches"in codeGrepJson||"totalMatches"in codeGrepJson,"code_grep json missing matches");const searchText=assertDefaultText(await callTool(caller,"search",{target:SMOKE_PACKAGE_TARGET,query:"router",limit:1}),"search default");assert(searchText.includes("code_read")||searchText.includes("docs_read")||searchText.includes("search_status"),"search default missing ready-to-call follow-up");const searchJson=assertJsonResult(await callTool(caller,"search",{target:SMOKE_PACKAGE_TARGET,query:"router",limit:1,format:"json"}),"search json");assertRecord(searchJson,"search json");const searchRef=typeof searchJson.searchRef==="string"?searchJson.searchRef:undefined;if(searchRef){const statusJson=assertJsonResult(await callTool(caller,"search_status",{search_ref:searchRef,format:"json"}),"search_status json");assertRecord(statusJson,"search_status json");assert("completed"in statusJson||"progress"in statusJson,"search_status json missing status data")}else{assertErrorCode(await callTool(caller,"search_status",{search_ref:"smoke-invalid-search-ref"}),"search_status invalid ref","NOT_FOUND")}const feedbackValidation=await callTool(caller,"feedback",{solution_id:"",accepted:true});assert(feedbackValidation.isError===true,"feedback validation should fail before submitting");assert(resultText(feedbackValidation,"feedback validation").includes("MCP error"),"feedback validation missing protocol error text")}async function runMcpSmoke(caller,options={}){const logger=options.logger??console;const includeLiveTools=options.includeLiveTools??true;const toolsResponse=await caller.listTools();const toolNames=new Set(toolsResponse.tools.map((tool)=>tool.name));for(const expected of EXPECTED_MCP_TOOLS){assert(toolNames.has(expected),`listTools missing ${expected}`)}if(!includeLiveTools)return;if(await assertLiveOrAuthRequired(caller,logger)){await runLiveSmoke(caller);logger.log("MCP smoke passed")}}export{runMcpSmoke,resultText,callToolText,assertJsonResult,assertHttpUnauthorizedChallenge,assertDefaultText,assertCleanErrorEnvelope,EXPECTED_MCP_TOOLS};
|
|
1
|
+
var EXPECTED_MCP_TOOLS=["get_example","search_language","pkg_info","pkg_deps","pkg_vulns","pkg_changelog","pkg_upgrade_review","docs_list","docs_read","code_files","code_read","code_grep","search","search_status","feedback"];var DEFAULT_TEXT_LIMIT=12000;var SMOKE_PACKAGE_VERSION="5.2.1";var SMOKE_PACKAGE_TARGET={registry:"npm",package_name:"express",version:SMOKE_PACKAGE_VERSION};function assert(condition,message){if(!condition){throw new Error(message)}}function parseJson(text,context){try{return JSON.parse(text)}catch(error){const message=error instanceof Error?error.message:"unknown error";throw new Error(`${context}: expected parseable JSON (${message})`)}}function assertNotJson(text,context){try{JSON.parse(text)}catch{return}throw new Error(`${context}: default response unexpectedly parsed as JSON`)}function assertRecord(value,context){assert(value!==null&&typeof value==="object"&&!Array.isArray(value),`${context}: expected object`)}function headerValue(headers,name){if(!headers)return null;if("get"in headers&&typeof headers.get==="function"){return headers.get(name)}const lowerName=name.toLowerCase();for(const[key,value]of Object.entries(headers)){if(key.toLowerCase()===lowerName)return value??null}return null}function resultText(result,context){assert(Array.isArray(result.content),`${context}: expected content array`);const first=result.content[0];assert(first?.type==="text",`${context}: expected text content`);assert(typeof first.text==="string",`${context}: expected text string`);return first.text}function assertCleanErrorEnvelope(result,context){assert(result.isError===true,`${context}: expected MCP error result`);const payload=parseJson(resultText(result,context),context);assertRecord(payload,context);assert(typeof payload.error==="string"&&payload.error.length>0,`${context}: missing error`);assert(typeof payload.code==="string"&&payload.code.length>0,`${context}: missing code`);assert(typeof payload.retryable==="boolean",`${context}: missing retryable`);return payload}function assertHttpUnauthorizedChallenge(metadata,context="unauthenticated HTTP challenge"){assert(metadata.status===401,`${context}: expected HTTP 401`);const challenge=headerValue(metadata.headers,"www-authenticate");assert(typeof challenge==="string"&&challenge.length>0,`${context}: missing WWW-Authenticate header`);assert(/^Bearer\b/i.test(challenge),`${context}: expected Bearer challenge, got ${challenge}`)}function assertDefaultText(result,context){assert(result.isError!==true,`${context}: expected success`);const text=resultText(result,context);assert(text.length>0,`${context}: expected non-empty text`);assert(text.length<DEFAULT_TEXT_LIMIT,`${context}: default text too large (${text.length} chars)`);assertNotJson(text,context);assert(!text.includes("--lifecycle"),`${context}: leaked CLI lifecycle flag`);assert(!text.includes("--verbose"),`${context}: leaked CLI verbose flag`);return text}function assertJsonResult(result,context){assert(result.isError!==true,`${context}: expected success`);return parseJson(resultText(result,context),context)}function assertErrorCode(result,context,code){const envelope=assertCleanErrorEnvelope(result,context);assert(envelope.code===code,`${context}: expected ${code}, got ${envelope.code}`)}async function callTool(caller,name,args){return await caller.callTool(name,args)}async function callToolText(caller,name,args){const result=await caller.callTool(name,args);const text=resultText(result,name);if(result.isError===true){throw new Error(text)}return text}async function assertLiveOrAuthRequired(caller,logger){const result=await callTool(caller,"search_language",{query:"python"});if(result.isError===true){const envelope=assertCleanErrorEnvelope(result,"search_language auth probe");assert(envelope.code==="AUTH_REQUIRED",`auth probe returned unexpected code ${envelope.code}`);logger.log("AUTH_REQUIRED: live smoke skipped");return false}const text=assertDefaultText(result,"search_language default");assert(text.includes("python (Python)"),"search_language default missing Python display name");assert(text.includes("aliases:"),"search_language default missing aliases");return true}async function runLiveSmoke(caller){const languageJson=assertJsonResult(await callTool(caller,"search_language",{query:"python",format:"json"}),"search_language json");assert(Array.isArray(languageJson),"search_language json: expected array");const exampleText=assertDefaultText(await callTool(caller,"get_example",{query:"express hello world",language:"javascript"}),"get_example default");assert(exampleText.includes("solution_id:"),"get_example default missing solution_id hint");const exampleJson=assertJsonResult(await callTool(caller,"get_example",{query:"express hello world",language:"javascript",format:"json"}),"get_example json");assertRecord(exampleJson,"get_example json");assert(typeof exampleJson.result==="string","get_example json missing result");const pkgInfoText=assertDefaultText(await callTool(caller,"pkg_info",{registry:"npm",package_name:"express"}),"pkg_info default");assert(pkgInfoText.includes("express"),"pkg_info default missing package name");assert(pkgInfoText.includes("Repository")&&pkgInfoText.includes("stars"),"pkg_info default missing repository popularity");assert(pkgInfoText.includes("Vulnerabilities"),"pkg_info default missing vulnerability status");assert(!pkgInfoText.includes("Install")&&!pkgInfoText.includes("Usage"),"pkg_info default should not include quickstart fields");const pkgInfoJson=assertJsonResult(await callTool(caller,"pkg_info",{registry:"npm",package_name:"express",format:"json"}),"pkg_info json");assertRecord(pkgInfoJson,"pkg_info json");assert(pkgInfoJson.registry==="npm","pkg_info json registry mismatch");assert(pkgInfoJson.name==="express","pkg_info json name mismatch");assert(typeof pkgInfoJson.version==="string","pkg_info json missing version");assert(!("install"in pkgInfoJson)&&!("usage"in pkgInfoJson),"pkg_info json should not include quickstart fields");const depsText=assertDefaultText(await callTool(caller,"pkg_deps",{registry:"npm",package_name:"express"}),"pkg_deps default");assert(depsText.includes("Runtime dependencies:"),"pkg_deps default missing runtime heading");assert(depsText.includes('pass lifecycle="all"'),"pkg_deps default missing MCP-native lifecycle hint");const depsAllText=assertDefaultText(await callTool(caller,"pkg_deps",{registry:"npm",package_name:"express",lifecycle:"all"}),"pkg_deps lifecycle all");assert(depsAllText.includes("Dependency groups:"),"pkg_deps lifecycle all missing groups heading");assert(!depsAllText.includes("Hidden groups:"),"pkg_deps lifecycle all still hides groups");const depsJson=assertJsonResult(await callTool(caller,"pkg_deps",{registry:"npm",package_name:"express",format:"json"}),"pkg_deps json");assertRecord(depsJson,"pkg_deps json");assertRecord(depsJson.runtime,"pkg_deps json runtime");const vulnsText=assertDefaultText(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express"}),"pkg_vulns default");assert(vulnsText.includes("express")||vulnsText.includes("vulnerab"),"pkg_vulns default missing context");assert(!vulnsText.includes("use -v"),"pkg_vulns leaked CLI -v hint");const filteredVulnsText=assertDefaultText(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"lodash",version:"4.17.20",min_severity:"high"}),"pkg_vulns filtered default");assert(filteredVulnsText.includes("Filter severity >= high"),"pkg_vulns filtered default missing filter echo");assert(!filteredVulnsText.includes("use -v"),"pkg_vulns filtered default leaked CLI -v hint");const vulnsJson=assertJsonResult(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express",format:"json"}),"pkg_vulns json");assertRecord(vulnsJson,"pkg_vulns json");assert("summary"in vulnsJson||"advisories"in vulnsJson,"pkg_vulns json missing vulnerability data");const filteredVulnsJson=assertJsonResult(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"lodash",version:"4.17.20",min_severity:"high",format:"json"}),"pkg_vulns filtered json");assertRecord(filteredVulnsJson,"pkg_vulns filtered json");assertRecord(filteredVulnsJson.filter,"pkg_vulns filtered json filter");assert(filteredVulnsJson.filter.minSeverity==="high","pkg_vulns filtered json missing severity filter echo");const scopedVulnsText=assertDefaultText(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express",advisory_scope:"non_affecting"}),"pkg_vulns scoped default");assert(scopedVulnsText.includes("Scope historical advisories only"),"pkg_vulns scoped default missing scope echo");assert(scopedVulnsText.includes("No active vulnerabilities affect this version"),"pkg_vulns scoped default missing current-risk statement");assert(!scopedVulnsText.includes("use -v"),"pkg_vulns scoped default leaked CLI -v hint");const scopedVulnsJson=assertJsonResult(await callTool(caller,"pkg_vulns",{registry:"npm",package_name:"express",advisory_scope:"non_affecting",format:"json"}),"pkg_vulns scoped json");assertRecord(scopedVulnsJson,"pkg_vulns scoped json");assertRecord(scopedVulnsJson.filter,"pkg_vulns scoped json filter");assert(scopedVulnsJson.filter.advisoryScope==="non_affecting","pkg_vulns scoped json missing advisory scope echo");const changelogText=assertDefaultText(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:1}),"pkg_changelog default");assert(!changelogText.includes("--verbose"),"pkg_changelog default leaked CLI verbose flag");if(changelogText.includes("truncated")||changelogText.includes("full bodies")){assert(changelogText.includes('pass verbose=true, body_lines=<n>, or format="json"'),"pkg_changelog truncation hint is not MCP-native")}const changelogBodyLinesText=assertDefaultText(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:2,body_lines:3}),"pkg_changelog body_lines");assert(changelogBodyLinesText.includes('pass verbose=true, body_lines=<n>, or format="json"'),"pkg_changelog body_lines missing MCP-native truncation hint");assert(!changelogBodyLinesText.includes("--verbose"),"pkg_changelog body_lines leaked CLI verbose flag");const changelogJson=assertJsonResult(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:1,format:"json"}),"pkg_changelog json");assertRecord(changelogJson,"pkg_changelog json");assertRecord(changelogJson.entries,"pkg_changelog json entries");const changelogTimeline=assertDefaultText(await callTool(caller,"pkg_changelog",{registry:"npm",package_name:"express",limit:2,omit_bodies:true}),"pkg_changelog timeline");assert(!changelogTimeline.includes("What's Changed"),"pkg_changelog omit_bodies=true still emitted bodies");const upgradeReviewText=assertDefaultText(await callTool(caller,"pkg_upgrade_review",{registry:"npm",package_name:"express",current_version:"5.0.0",target_version:"5.2.1",skip_transitive_security:true}),"pkg_upgrade_review default");assert(upgradeReviewText.includes("pkg_upgrade_review")&&upgradeReviewText.includes("vulnerabilities")&&upgradeReviewText.includes("changes"),"pkg_upgrade_review default missing evidence sections");assert(!upgradeReviewText.includes("recommendation")&&!upgradeReviewText.includes("risk level"),"pkg_upgrade_review default leaked assessment language");const upgradeReviewJson=assertJsonResult(await callTool(caller,"pkg_upgrade_review",{registry:"npm",package_name:"express",current_version:"5.0.0",target_version:"5.2.1",skip_transitive_security:true,format:"json"}),"pkg_upgrade_review json");assertRecord(upgradeReviewJson,"pkg_upgrade_review json");assertRecord(upgradeReviewJson.summary,"pkg_upgrade_review json summary");assert(Array.isArray(upgradeReviewJson.reviews),"pkg_upgrade_review json missing reviews array");const firstUpgradeReview=upgradeReviewJson.reviews[0];assert(firstUpgradeReview,"pkg_upgrade_review json missing first review");for(const forbidden of["risk","riskLevel","recommendation","findings","verification"]){assert(!(forbidden in firstUpgradeReview),`pkg_upgrade_review json leaked judgment field ${forbidden}`)}const docsText=assertDefaultText(await callTool(caller,"docs_list",{...SMOKE_PACKAGE_TARGET,limit:2}),"docs_list default");assert(docsText.includes("docs_read page_id="),"docs_list default missing docs_read follow-up");const docsJson=assertJsonResult(await callTool(caller,"docs_list",{...SMOKE_PACKAGE_TARGET,limit:2,format:"json"}),"docs_list json");assertRecord(docsJson,"docs_list json");assert(Array.isArray(docsJson.pages),"docs_list json missing pages array");const firstPage=docsJson.pages[0];assert(firstPage&&typeof firstPage.pageId==="string","docs_list json missing readable page id");const docReadText=assertDefaultText(await callTool(caller,"docs_read",{page_id:firstPage.pageId,start_line:1,end_line:5}),"docs_read default");assert(docReadText.length>0,"docs_read default missing content");const docReadJson=assertJsonResult(await callTool(caller,"docs_read",{page_id:firstPage.pageId,start_line:1,end_line:5,format:"json"}),"docs_read json");assertRecord(docReadJson,"docs_read json");assert(typeof docReadJson.content==="string","docs_read json missing content");const codeFilesText=assertDefaultText(await callTool(caller,"code_files",{target:SMOKE_PACKAGE_TARGET,path_prefix:"package.json",limit:1}),"code_files default");assert(codeFilesText.includes("package.json"),"code_files default missing package.json");const codeFilesJson=assertJsonResult(await callTool(caller,"code_files",{target:SMOKE_PACKAGE_TARGET,path_prefix:"package.json",limit:1,format:"json"}),"code_files json");assertRecord(codeFilesJson,"code_files json");assert(Array.isArray(codeFilesJson.files),"code_files json missing files array");const codeReadText=assertDefaultText(await callTool(caller,"code_read",{target:SMOKE_PACKAGE_TARGET,path:"package.json",start_line:1,end_line:5}),"code_read default");assert(/^1\s+/m.test(codeReadText),"code_read default missing line numbers");const codeReadJson=assertJsonResult(await callTool(caller,"code_read",{target:SMOKE_PACKAGE_TARGET,path:"package.json",start_line:1,end_line:5,format:"json"}),"code_read json");assertRecord(codeReadJson,"code_read json");assert(codeReadJson.path==="package.json","code_read json path mismatch");const codeGrepText=assertDefaultText(await callTool(caller,"code_grep",{target:SMOKE_PACKAGE_TARGET,pattern:"express",path:"package.json",max_matches:1}),"code_grep default");assert(codeGrepText.includes("package.json"),"code_grep default missing package.json");const codeGrepJson=assertJsonResult(await callTool(caller,"code_grep",{target:SMOKE_PACKAGE_TARGET,pattern:"express",path:"package.json",max_matches:1,format:"json"}),"code_grep json");assertRecord(codeGrepJson,"code_grep json");assert("matches"in codeGrepJson||"totalMatches"in codeGrepJson,"code_grep json missing matches");const searchText=assertDefaultText(await callTool(caller,"search",{target:SMOKE_PACKAGE_TARGET,query:"router",limit:1}),"search default");assert(searchText.includes("code_read")||searchText.includes("docs_read")||searchText.includes("search_status"),"search default missing ready-to-call follow-up");const searchJson=assertJsonResult(await callTool(caller,"search",{target:SMOKE_PACKAGE_TARGET,query:"router",limit:1,format:"json"}),"search json");assertRecord(searchJson,"search json");const searchRef=typeof searchJson.searchRef==="string"?searchJson.searchRef:undefined;if(searchRef){const statusJson=assertJsonResult(await callTool(caller,"search_status",{search_ref:searchRef,wait_timeout_ms:0,format:"json"}),"search_status json");assertRecord(statusJson,"search_status json");assert("completed"in statusJson||"progress"in statusJson,"search_status json missing status data")}else{assertErrorCode(await callTool(caller,"search_status",{search_ref:"smoke-invalid-search-ref",wait_timeout_ms:0}),"search_status invalid ref","NOT_FOUND")}const feedbackValidation=await callTool(caller,"feedback",{solution_id:"",accepted:true});assert(feedbackValidation.isError===true,"feedback validation should fail before submitting");assert(resultText(feedbackValidation,"feedback validation").includes("MCP error"),"feedback validation missing protocol error text")}async function runMcpSmoke(caller,options={}){const logger=options.logger??console;const includeLiveTools=options.includeLiveTools??true;const toolsResponse=await caller.listTools();const toolNames=new Set(toolsResponse.tools.map((tool)=>tool.name));for(const expected of EXPECTED_MCP_TOOLS){assert(toolNames.has(expected),`listTools missing ${expected}`)}if(!includeLiveTools)return;if(await assertLiveOrAuthRequired(caller,logger)){await runLiveSmoke(caller);logger.log("MCP smoke passed")}}export{runMcpSmoke,resultText,callToolText,assertJsonResult,assertHttpUnauthorizedChallenge,assertDefaultText,assertCleanErrorEnvelope,EXPECTED_MCP_TOOLS};
|