MCPcopy
hub / github.com/upstash/context7 / downloadSkillFromGitHub

Function downloadSkillFromGitHub

packages/cli/src/utils/github.ts:250–311  ·  view source on GitHub ↗
(
  skill: Skill & { project: string }
)

Source from the content-addressed store, hash-verified

248}
249
250export async function downloadSkillFromGitHub(
251 skill: Skill & { project: string }
252): Promise<{ files: SkillFile[]; error?: string }> {
253 try {
254 const parsed = parseGitHubUrl(skill.url);
255
256 if (!parsed) {
257 return { files: [], error: `Invalid GitHub URL: ${skill.url}` };
258 }
259
260 const { owner, repo, branch, path: skillPath } = parsed;
261
262 const ghHeaders = getGitHubHeaders();
263
264 const treeData = await fetchRepoTree(owner, repo, branch, ghHeaders);
265 if ("error" in treeData) {
266 const hint =
267 !ghHeaders["Authorization"] && /403|429|rate/.test(treeData.error)
268 ? " — run `gh auth login` or set the GITHUB_TOKEN env var to increase rate limits"
269 : "";
270 return { files: [], error: `GitHub API error: ${treeData.error}${hint}` };
271 }
272
273 const skillFiles = treeData.tree.filter(
274 (item) => item.type === "blob" && item.path.startsWith(skillPath + "/")
275 );
276
277 if (skillFiles.length === 0) {
278 return { files: [], error: `No files found in ${skillPath}` };
279 }
280
281 const files: SkillFile[] = [];
282 for (const item of skillFiles) {
283 const rawUrl = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${item.path}`;
284 const fileResponse = await fetch(rawUrl, { headers: ghHeaders });
285
286 if (!fileResponse.ok) {
287 console.warn(`Failed to fetch ${item.path}: ${fileResponse.status}`);
288 continue;
289 }
290
291 const content = await fileResponse.text();
292 const relativePath = item.path.slice(skillPath.length + 1);
293
294 // Reject paths that attempt directory traversal
295 if (relativePath.includes("..")) {
296 console.warn(`Skipping file with unsafe path: ${item.path}`);
297 continue;
298 }
299
300 files.push({
301 path: relativePath,
302 content,
303 });
304 }
305
306 return { files };
307 } catch (err) {

Callers 1

downloadSkillFunction · 0.85

Calls 3

parseGitHubUrlFunction · 0.85
getGitHubHeadersFunction · 0.85
fetchRepoTreeFunction · 0.85

Tested by

no test coverage detected