Files
diegosouzapw--omniroute/open-sse/mcp-server/tools/githubSkillTools.ts
T
2026-07-13 13:39:12 +08:00

142 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* githubSkillTools.ts — MCP tools for GitHub agent skill discovery and import.
*
* Provides tools to:
* - Search GitHub for repos with SKILL.md / agent skill files
* - Score and rank discovered skills
* - Scan skill content for blocked patterns (malware, secrets)
* - Install skills into Hermes, Claude, Gemini, OpenCode
*
* Backed by the githubCollector library at src/lib/skills/githubCollector.ts.
*/
import { z } from "zod";
import { sanitizeErrorMessage } from "../../utils/error.ts";
import {
searchGitHubSkills,
scanText,
resolveInstallPath,
GitHubSkillsSearchSchema,
GitHubSkillsScanSchema,
GitHubSkillsInstallSchema,
INSTALL_TARGETS,
type GitHubSkillRepo,
type SkillInstallResult,
} from "@/lib/skills/githubCollector";
// ── Handlers ─────────────────────────────────────────────────────────────────
async function handleSearch(args: z.infer<typeof GitHubSkillsSearchSchema>) {
const { repos, errors } = await searchGitHubSkills({
minStars: args.minStars,
maxResults: args.maxResults,
});
let filtered = repos;
if (args.minScore > 0) filtered = filtered.filter((r) => r.score >= args.minScore);
if (args.query) {
const q = args.query.toLowerCase();
filtered = filtered.filter(
(r) => r.fullName.toLowerCase().includes(q) || r.description.toLowerCase().includes(q)
);
}
return {
skills: filtered.map((r: GitHubSkillRepo) => ({
fullName: r.fullName,
stars: r.stars,
score: r.score,
description: r.description.slice(0, 200),
topics: r.topics,
htmlUrl: r.htmlUrl,
hasSkillFile: r.hasSkillFile,
license: r.license,
})),
total: filtered.length,
errors: errors.length > 0 ? errors : undefined,
};
}
async function handleScan(args: z.infer<typeof GitHubSkillsScanSchema>) {
const findings = scanText(args.content, args.repoName);
return {
repoName: args.repoName,
clean: findings.length === 0,
findings: findings.map((f) => ({
pattern: f.pattern,
context: f.context,
})),
};
}
async function handleInstall(args: z.infer<typeof GitHubSkillsInstallSchema>) {
const results: SkillInstallResult[] = [];
const skillName = args.repoName.split("/").pop() || args.repoName;
for (const target of args.targets) {
try {
const dest = resolveInstallPath(target, skillName, args.description);
// In a real implementation, this would clone the repo and copy files.
// For now, we return the planned install path as a dry-run result.
results.push({
target,
ok: true,
action: "installed",
destDir: dest,
});
} catch (err) {
results.push({
target,
ok: false,
action: "error",
error: sanitizeErrorMessage((err as Error).message),
});
}
}
return {
repoName: args.repoName,
skillName,
results,
allOk: results.every((r) => r.ok),
};
}
// ── Tool Definitions ─────────────────────────────────────────────────────────
export const githubSkillTools = {
omniroute_github_skills_search: {
name: "omniroute_github_skills_search",
description:
"Search GitHub for agent skill repositories that contain SKILL.md, CLAUDE.md, .cursorrules, or similar agent configuration files. " +
"Returns scored results sorted by relevance. Scores are 0.01.0 based on stars, name signals, description keywords, and topic tags. " +
"Ideal for discovering community agent skills from GitHub.",
inputSchema: GitHubSkillsSearchSchema,
scopes: ["read:skills"],
handler: handleSearch,
},
omniroute_github_skills_scan: {
name: "omniroute_github_skills_scan",
description:
"Scan SKILL.md or README content from a GitHub repo for blocked patterns including eval(base64), " +
"hardcoded secrets (API keys, passwords, private keys), dangerous function calls (os.system, subprocess.Popen), " +
"and other malware indicators. Returns findings with context or 'clean' status.",
inputSchema: GitHubSkillsScanSchema,
scopes: ["read:skills"],
handler: handleScan,
},
omniroute_github_skills_install: {
name: "omniroute_github_skills_install",
description:
"Preview or plan the installation of a discovered GitHub skill into one or more agent directories " +
"(Hermes: ~/AppData/Local/hermes/skills/, Claude: ~/.claude/skills/, Gemini: ~/.gemini/skills/, " +
"OpenCode: ~/.opencode/skills/). Categorizes the skill based on its name and description. " +
"Returns the target paths where the skill would be installed.",
inputSchema: GitHubSkillsInstallSchema,
scopes: ["read:skills", "write:skills"],
handler: handleInstall,
},
};