mcp-git-fetcher 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 +186 -0
- package/dist/analyzer.d.ts +20 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +111 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +275 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +47 -0
- package/dist/config.js.map +1 -0
- package/dist/file-sync.d.ts +21 -0
- package/dist/file-sync.d.ts.map +1 -0
- package/dist/file-sync.js +97 -0
- package/dist/file-sync.js.map +1 -0
- package/dist/git-ops.d.ts +21 -0
- package/dist/git-ops.d.ts.map +1 -0
- package/dist/git-ops.js +85 -0
- package/dist/git-ops.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# mcp-git-fetcher
|
|
2
|
+
|
|
3
|
+
> **MCP 服务**:从公司内部 GitLab 仓库下载 skills/rules 文件到目标 IDE 目录。
|
|
4
|
+
|
|
5
|
+
支持 **Python** 和 **Node.js** 双版本,选一个即可。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 快速开始
|
|
10
|
+
|
|
11
|
+
### 方式 A:Node.js(推荐,可 npx)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y mcp-git-fetcher
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
本地开发:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git clone <repo-url>
|
|
21
|
+
cd mcp-git-fetcher
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 方式 B:Python
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cd D:\AI Coding\mcp-git\mcp-git-fetcher
|
|
30
|
+
pip install -e .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 配置环境变量
|
|
34
|
+
|
|
35
|
+
复制 `.env.example` 为 `.env`,填入实际参数:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
copy .env.example .env
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| 变量 | 说明 | 示例 |
|
|
42
|
+
|------|------|------|
|
|
43
|
+
| `GIT_REPO_URL` | GitLab 仓库地址(**必需**) | `http://10.1.192.33:8090/.../mcp-git-resource.git` |
|
|
44
|
+
| `GIT_AUTH_TOKEN` | GitLab Personal Access Token | `glpat-xxxxxx` |
|
|
45
|
+
| `GIT_BRANCH` | 分支(默认 main) | `main` |
|
|
46
|
+
|
|
47
|
+
> **Token 说明**:Token 只配在 MCP 客户端配置的 `env` 中,不会写进代码,也不会存到 git 缓存目录。
|
|
48
|
+
|
|
49
|
+
### 注册为 MCP 服务
|
|
50
|
+
|
|
51
|
+
#### Hermes Agent
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
mcp_servers:
|
|
55
|
+
git-fetcher:
|
|
56
|
+
command: "npx"
|
|
57
|
+
args: ["-y", "mcp-git-fetcher"]
|
|
58
|
+
env:
|
|
59
|
+
GIT_REPO_URL: "http://10.1.192.33:8090/project/5gboss/wasu/mcp-git-resource.git"
|
|
60
|
+
GIT_AUTH_TOKEN: "glpat-xxxxx" # 只在这里配置
|
|
61
|
+
connect_timeout: 60
|
|
62
|
+
timeout: 300
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Trae CN IDE
|
|
66
|
+
|
|
67
|
+
编辑 `%APPDATA%\Trae CN\User\mcp.json`:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"git-fetcher": {
|
|
73
|
+
"command": "npx",
|
|
74
|
+
"args": ["-y", "mcp-git-fetcher"],
|
|
75
|
+
"env": {
|
|
76
|
+
"GIT_REPO_URL": "http://10.1.192.33:8090/project/5gboss/wasu/mcp-git-resource.git",
|
|
77
|
+
"GIT_AUTH_TOKEN": "glpat-xxxxx"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> 如果用 Python 版,把 `command` 改为 `"uv"`,`args` 改为 `["run", "--directory", "D:\\AI Coding\\mcp-git\\mcp-git-fetcher", "mcp-git-fetcher"]`。
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 使用方式
|
|
89
|
+
|
|
90
|
+
### `fetch_resources`
|
|
91
|
+
|
|
92
|
+
拉取 GitLab 最新代码,同步 `skills/` 和 `rules/` 到指定目录。
|
|
93
|
+
|
|
94
|
+
**参数:**
|
|
95
|
+
|
|
96
|
+
| 参数 | 必需 | 说明 |
|
|
97
|
+
|------|------|------|
|
|
98
|
+
| `target_skills_dir` | ✅ | skills 写入目录(如 `.trae/skills` 的绝对路径) |
|
|
99
|
+
| `target_rules_dir` | ✅ | rules 写入目录(如 `.trae/rules` 的绝对路径) |
|
|
100
|
+
| `repo_url` | ❌ | 临时覆盖仓库 URL |
|
|
101
|
+
|
|
102
|
+
**返回示例:**
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"success": true,
|
|
107
|
+
"message": "Synced 12 files (3 skipped, commit a1b2c3d4)",
|
|
108
|
+
"commit": "a1b2c3d4",
|
|
109
|
+
"skills": { "copied": 8, "skipped": 2, "errors": [] },
|
|
110
|
+
"rules": { "copied": 4, "skipped": 1, "errors": [] },
|
|
111
|
+
"total_copied": 12,
|
|
112
|
+
"total_skipped": 3
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 工作原理
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Agent/IDE → fetch_resources
|
|
122
|
+
├─ ① git clone/pull 到本地缓存 (~/.mcp-git-fetcher/cache/)
|
|
123
|
+
├─ ② 提取仓库中的 skills/ 和 rules/
|
|
124
|
+
└─ ③ 复制到目标目录(SHA-256 去重,相同则跳过)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- **增量更新**:第二次调用只需 git pull,更快
|
|
128
|
+
- **内容去重**:SHA-256 对比,内容不变不写盘
|
|
129
|
+
- **Token 安全**:只存在于客户端配置,不进代码和 git 缓存
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 项目结构
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
D:\AI Coding\mcp-git\mcp-git-fetcher\
|
|
137
|
+
├── mcp_git_fetcher/ # Python 版源码
|
|
138
|
+
│ ├── server.py # MCP 服务入口
|
|
139
|
+
│ ├── git_ops.py # Git 操作
|
|
140
|
+
│ ├── file_sync.py # 文件同步
|
|
141
|
+
│ └── config.py # 配置管理
|
|
142
|
+
├── src/ # Node.js 版源码 (TypeScript)
|
|
143
|
+
│ ├── cli.ts # MCP 服务入口
|
|
144
|
+
│ ├── git-ops.ts # Git 操作
|
|
145
|
+
│ ├── file-sync.ts # 文件同步
|
|
146
|
+
│ └── config.ts # 配置管理
|
|
147
|
+
├── package.json # Node.js 依赖 + npm 发布配置
|
|
148
|
+
├── pyproject.toml # Python 依赖
|
|
149
|
+
├── .env.example # 环境变量模板
|
|
150
|
+
├── .gitignore
|
|
151
|
+
└── README.md
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 环境变量
|
|
157
|
+
|
|
158
|
+
| 变量 | 必需 | 默认值 | 说明 |
|
|
159
|
+
|------|------|--------|------|
|
|
160
|
+
| `GIT_REPO_URL` | ✅ | — | GitLab 仓库 HTTP(S) 地址 |
|
|
161
|
+
| `GIT_AUTH_TOKEN` | ❌ | — | GitLab Personal Access Token |
|
|
162
|
+
| `GIT_BRANCH` | ❌ | `main` | 拉取的分支 |
|
|
163
|
+
| `SKILLS_SUBDIR` | ❌ | `skills` | 仓库内 skills 目录名 |
|
|
164
|
+
| `RULES_SUBDIR` | ❌ | `rules` | 仓库内 rules 目录名 |
|
|
165
|
+
| `CACHE_DIR` | ❌ | `~/.mcp-git-fetcher/cache` | 本地 Git 缓存目录 |
|
|
166
|
+
| `HTTP_PROXY` | ❌ | — | HTTP 代理 |
|
|
167
|
+
| `HTTPS_PROXY` | ❌ | — | HTTPS 代理 |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 开发
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Python 版
|
|
175
|
+
pip install -e .
|
|
176
|
+
python -m mcp_git_fetcher.server
|
|
177
|
+
|
|
178
|
+
# Node.js 版
|
|
179
|
+
npm install
|
|
180
|
+
npm run build
|
|
181
|
+
node dist/cli.js
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ResourceInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
relative_path: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Extract a short description from the beginning of a file.
|
|
8
|
+
*
|
|
9
|
+
* Strategies (tried in order):
|
|
10
|
+
* 1. Python: first docstring
|
|
11
|
+
* 2. Markdown: first # Title or first non-empty paragraph
|
|
12
|
+
* 3. YAML/JS/TS: first comment block
|
|
13
|
+
* 4. Fallback: first 2 meaningful lines
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractDescription(filePath: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Scan the repository for skills and rules files with descriptions.
|
|
18
|
+
*/
|
|
19
|
+
export declare function scanResources(repoRoot: string, skillsSubdir: string, rulesSubdir: string): Record<string, ResourceInfo[]>;
|
|
20
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6D3D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CA0ChC"}
|
package/dist/analyzer.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync, existsSync } from "fs";
|
|
2
|
+
import { join, relative } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* Extract a short description from the beginning of a file.
|
|
5
|
+
*
|
|
6
|
+
* Strategies (tried in order):
|
|
7
|
+
* 1. Python: first docstring
|
|
8
|
+
* 2. Markdown: first # Title or first non-empty paragraph
|
|
9
|
+
* 3. YAML/JS/TS: first comment block
|
|
10
|
+
* 4. Fallback: first 2 meaningful lines
|
|
11
|
+
*/
|
|
12
|
+
export function extractDescription(filePath) {
|
|
13
|
+
let text;
|
|
14
|
+
try {
|
|
15
|
+
text = readFileSync(filePath, "utf-8");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
const lines = text.split(/\r?\n/);
|
|
21
|
+
// Strategy 1: Python docstring
|
|
22
|
+
if (filePath.endsWith(".py")) {
|
|
23
|
+
const m = text.match(/"""(.*?)"""/s);
|
|
24
|
+
if (m) {
|
|
25
|
+
return m[1].trim().split("\n")[0].slice(0, 150);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Strategy 2: Markdown — first # Title
|
|
29
|
+
if (filePath.endsWith(".md") || filePath.endsWith(".mdx")) {
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (trimmed.startsWith("# ")) {
|
|
33
|
+
return trimmed.slice(2).trim();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// fallback: first non-empty paragraph
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const trimmed = line.trim();
|
|
39
|
+
if (trimmed && !trimmed.startsWith("#")) {
|
|
40
|
+
return trimmed.slice(0, 150);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Strategy 3: YAML/JS/TS — first comment
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (trimmed.startsWith("# ")) {
|
|
48
|
+
return trimmed.slice(2).trim().slice(0, 150);
|
|
49
|
+
}
|
|
50
|
+
if (trimmed.startsWith("// ")) {
|
|
51
|
+
return trimmed.slice(3).trim().slice(0, 150);
|
|
52
|
+
}
|
|
53
|
+
if (trimmed.startsWith("/*")) {
|
|
54
|
+
const end = text.indexOf("*/");
|
|
55
|
+
if (end > 0) {
|
|
56
|
+
return text.slice(2, end).trim().slice(0, 150);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Fallback: first non-empty, non-comment line
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const trimmed = line.trim();
|
|
63
|
+
if (trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("//") && !trimmed.startsWith("/*")) {
|
|
64
|
+
return trimmed.slice(0, 150);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Scan the repository for skills and rules files with descriptions.
|
|
71
|
+
*/
|
|
72
|
+
export function scanResources(repoRoot, skillsSubdir, rulesSubdir) {
|
|
73
|
+
const result = { skills: [], rules: [] };
|
|
74
|
+
for (const [category, subdir] of [["skills", skillsSubdir], ["rules", rulesSubdir]]) {
|
|
75
|
+
const srcDir = join(repoRoot, subdir);
|
|
76
|
+
if (!existsSync(srcDir))
|
|
77
|
+
continue;
|
|
78
|
+
function walk(current) {
|
|
79
|
+
const entries = readdirSync(current);
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const full = join(current, entry);
|
|
82
|
+
let stat;
|
|
83
|
+
try {
|
|
84
|
+
stat = statSync(full);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (stat.isDirectory()) {
|
|
90
|
+
if (entry.startsWith("."))
|
|
91
|
+
continue; // skip hidden dirs
|
|
92
|
+
walk(full);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// Skip hidden files
|
|
96
|
+
if (entry.startsWith("."))
|
|
97
|
+
continue;
|
|
98
|
+
const relPath = relative(repoRoot, full).replace(/\\/g, "/");
|
|
99
|
+
const desc = extractDescription(full);
|
|
100
|
+
result[category].push({
|
|
101
|
+
name: entry,
|
|
102
|
+
relative_path: relPath,
|
|
103
|
+
description: desc || "(no description)",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
walk(srcDir);
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs";
|
|
5
|
+
import { createHash } from "crypto";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { loadConfig } from "./config.js";
|
|
8
|
+
import { ensureRepo } from "./git-ops.js";
|
|
9
|
+
import { syncAll } from "./file-sync.js";
|
|
10
|
+
import { scanResources } from "./analyzer.js";
|
|
11
|
+
// ── Tool definitions ──────────────────────────────────────────────────────────
|
|
12
|
+
const TOOLS = [
|
|
13
|
+
{
|
|
14
|
+
name: "fetch_resources",
|
|
15
|
+
description: "Fetch all skills and rules files from the configured internal GitLab " +
|
|
16
|
+
"repository and sync them to target directories. " +
|
|
17
|
+
"Use this for a full sync of all resources.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
target_skills_dir: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Absolute path for skills files. Example: C:/path/to/.trae/skills",
|
|
24
|
+
},
|
|
25
|
+
target_rules_dir: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Absolute path for rules files. Example: C:/path/to/.trae/rules",
|
|
28
|
+
},
|
|
29
|
+
repo_url: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Optional override for the Git repository URL.",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ["target_skills_dir", "target_rules_dir"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "list_resources",
|
|
39
|
+
description: "Scan the configured GitLab repository and list all available skills/rules " +
|
|
40
|
+
"with their descriptions. Shows what each file does and what problem it solves. " +
|
|
41
|
+
"Does not download anything. Use this to see what's available, " +
|
|
42
|
+
"then use download_resources to pick specific ones.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
category: {
|
|
47
|
+
type: "string",
|
|
48
|
+
enum: ["skills", "rules", "all"],
|
|
49
|
+
description: "Filter by category: 'skills', 'rules', or 'all' (default).",
|
|
50
|
+
},
|
|
51
|
+
pattern: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Optional glob pattern to filter by filename (e.g. 'react*', '*.md').",
|
|
54
|
+
},
|
|
55
|
+
repo_url: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Optional override for the Git repository URL.",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "download_resources",
|
|
64
|
+
description: "Download specific skills or rules files from the GitLab repository " +
|
|
65
|
+
"by filename pattern. Use list_resources first to see what's available, " +
|
|
66
|
+
"then download only what you need. Supports glob patterns.",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
target_skills_dir: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "Absolute path for skills files. Required if downloading skills.",
|
|
73
|
+
},
|
|
74
|
+
target_rules_dir: {
|
|
75
|
+
type: "string",
|
|
76
|
+
description: "Absolute path for rules files. Required if downloading rules.",
|
|
77
|
+
},
|
|
78
|
+
patterns: {
|
|
79
|
+
type: "array",
|
|
80
|
+
items: { type: "string" },
|
|
81
|
+
description: "Filename patterns to match (e.g. ['react*', '*.md', 'cod*']). " +
|
|
82
|
+
"Only files matching any pattern will be downloaded.",
|
|
83
|
+
},
|
|
84
|
+
category: {
|
|
85
|
+
type: "string",
|
|
86
|
+
enum: ["skills", "rules", "all"],
|
|
87
|
+
description: "Which category to search: 'skills', 'rules', or 'all' (default).",
|
|
88
|
+
},
|
|
89
|
+
repo_url: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Optional override for the Git repository URL.",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
required: ["patterns"],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
99
|
+
function makeResult(success, message, details) {
|
|
100
|
+
const payload = { success, message };
|
|
101
|
+
if (details)
|
|
102
|
+
Object.assign(payload, details);
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function fileHash(filePath) {
|
|
108
|
+
const hash = createHash("sha256");
|
|
109
|
+
hash.update(readFileSync(filePath));
|
|
110
|
+
return hash.digest("hex");
|
|
111
|
+
}
|
|
112
|
+
function matchGlob(name, pattern) {
|
|
113
|
+
const regexStr = "^" +
|
|
114
|
+
pattern
|
|
115
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
116
|
+
.replace(/\*/g, ".*")
|
|
117
|
+
.replace(/\?/g, ".") +
|
|
118
|
+
"$";
|
|
119
|
+
return new RegExp(regexStr, "i").test(name);
|
|
120
|
+
}
|
|
121
|
+
function filterByPatternSimple(items, patterns) {
|
|
122
|
+
const pats = Array.isArray(patterns) ? patterns : [patterns];
|
|
123
|
+
return items.filter((item) => pats.some((p) => matchGlob(item.name, p)));
|
|
124
|
+
}
|
|
125
|
+
// ── Server setup ──────────────────────────────────────────────────────────────
|
|
126
|
+
const server = new Server({ name: "mcp-git-fetcher", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
127
|
+
// ── Tools/list ────────────────────────────────────────────────────────────────
|
|
128
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
129
|
+
tools: TOOLS,
|
|
130
|
+
}));
|
|
131
|
+
// ── Tools/call ────────────────────────────────────────────────────────────────
|
|
132
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
133
|
+
const { name, arguments: args } = request.params;
|
|
134
|
+
if (!name)
|
|
135
|
+
return makeResult(false, "Missing tool name");
|
|
136
|
+
try {
|
|
137
|
+
switch (name) {
|
|
138
|
+
case "list_resources":
|
|
139
|
+
return await handleListResources(args);
|
|
140
|
+
case "download_resources":
|
|
141
|
+
return await handleDownloadResources(args);
|
|
142
|
+
case "fetch_resources":
|
|
143
|
+
return await handleFetchResources(args);
|
|
144
|
+
default:
|
|
145
|
+
return makeResult(false, `Unknown tool: ${name}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
150
|
+
console.error(`[mcp-git-fetcher] Error: ${msg}`);
|
|
151
|
+
return makeResult(false, msg);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
// ── Handler: fetch_resources ──────────────────────────────────────────────────
|
|
155
|
+
async function handleFetchResources(args) {
|
|
156
|
+
const config = loadConfig();
|
|
157
|
+
if (args.repo_url)
|
|
158
|
+
config.repoUrl = String(args.repo_url);
|
|
159
|
+
const targetSkillsDir = args.target_skills_dir;
|
|
160
|
+
const targetRulesDir = args.target_rules_dir;
|
|
161
|
+
if (!targetSkillsDir || !targetRulesDir) {
|
|
162
|
+
return makeResult(false, "Both target_skills_dir and target_rules_dir are required");
|
|
163
|
+
}
|
|
164
|
+
console.error(`[mcp-git-fetcher] fetch_resources: repo=${config.repoUrl}`);
|
|
165
|
+
const repo = await ensureRepo(config);
|
|
166
|
+
const results = syncAll(repo.repoRoot, config.skillsSubdir, config.rulesSubdir, targetSkillsDir, targetRulesDir);
|
|
167
|
+
const skillsR = results.skills;
|
|
168
|
+
const rulesR = results.rules;
|
|
169
|
+
const totalCopied = (skillsR?.copied ?? 0) + (rulesR?.copied ?? 0);
|
|
170
|
+
const totalSkipped = (skillsR?.skipped ?? 0) + (rulesR?.skipped ?? 0);
|
|
171
|
+
const errors = [...(skillsR?.errors ?? []), ...(rulesR?.errors ?? [])];
|
|
172
|
+
return makeResult(errors.length === 0, `Synced ${totalCopied} files (${totalSkipped} skipped, commit ${repo.commitSha})`, { commit: repo.commitSha, skills: skillsR, rules: rulesR, total_copied: totalCopied, total_skipped: totalSkipped });
|
|
173
|
+
}
|
|
174
|
+
// ── Handler: list_resources ───────────────────────────────────────────────────
|
|
175
|
+
async function handleListResources(args) {
|
|
176
|
+
const config = loadConfig();
|
|
177
|
+
if (args.repo_url)
|
|
178
|
+
config.repoUrl = String(args.repo_url);
|
|
179
|
+
const repo = await ensureRepo(config);
|
|
180
|
+
const category = args.category || "all";
|
|
181
|
+
const pattern = args.pattern || "*";
|
|
182
|
+
const all = scanResources(repo.repoRoot, config.skillsSubdir, config.rulesSubdir);
|
|
183
|
+
const result = {};
|
|
184
|
+
if (category === "skills" || category === "all") {
|
|
185
|
+
result.skills = filterByPatternSimple(all.skills, pattern);
|
|
186
|
+
}
|
|
187
|
+
if (category === "rules" || category === "all") {
|
|
188
|
+
result.rules = filterByPatternSimple(all.rules, pattern);
|
|
189
|
+
}
|
|
190
|
+
const total = Object.values(result).reduce((s, v) => s + v.length, 0);
|
|
191
|
+
return makeResult(true, `Found ${total} resources (commit ${repo.commitSha})`, {
|
|
192
|
+
commit: repo.commitSha,
|
|
193
|
+
resources: result,
|
|
194
|
+
total,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// ── Handler: download_resources ───────────────────────────────────────────────
|
|
198
|
+
async function handleDownloadResources(args) {
|
|
199
|
+
const config = loadConfig();
|
|
200
|
+
if (args.repo_url)
|
|
201
|
+
config.repoUrl = String(args.repo_url);
|
|
202
|
+
const patterns = args.patterns;
|
|
203
|
+
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
204
|
+
return makeResult(false, "At least one pattern is required (e.g. ['react*', '*.md'])");
|
|
205
|
+
}
|
|
206
|
+
const targetSkillsDir = args.target_skills_dir;
|
|
207
|
+
const targetRulesDir = args.target_rules_dir;
|
|
208
|
+
const category = args.category || "all";
|
|
209
|
+
if ((category === "skills" || category === "all") && !targetSkillsDir) {
|
|
210
|
+
return makeResult(false, "target_skills_dir is required when downloading skills");
|
|
211
|
+
}
|
|
212
|
+
if ((category === "rules" || category === "all") && !targetRulesDir) {
|
|
213
|
+
return makeResult(false, "target_rules_dir is required when downloading rules");
|
|
214
|
+
}
|
|
215
|
+
console.error(`[mcp-git-fetcher] download_resources: patterns=${JSON.stringify(patterns)}, category=${category}`);
|
|
216
|
+
const repo = await ensureRepo(config);
|
|
217
|
+
const all = scanResources(repo.repoRoot, config.skillsSubdir, config.rulesSubdir);
|
|
218
|
+
const toDownload = [];
|
|
219
|
+
if (category === "skills" || category === "all") {
|
|
220
|
+
const skillsRoot = join(repo.repoRoot, config.skillsSubdir);
|
|
221
|
+
for (const item of filterByPatternSimple(all.skills, patterns)) {
|
|
222
|
+
const item2 = item;
|
|
223
|
+
const src = join(repo.repoRoot, item2.relative_path);
|
|
224
|
+
const rel = join(skillsRoot, item2.relative_path.replace(config.skillsSubdir + "/", "")); // Hmm, simpler:
|
|
225
|
+
const relpath = item2.relative_path.replace(config.skillsSubdir + "/", "");
|
|
226
|
+
toDownload.push({ src, dst: join(targetSkillsDir, relpath) });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (category === "rules" || category === "all") {
|
|
230
|
+
const rulesRoot = join(repo.repoRoot, config.rulesSubdir);
|
|
231
|
+
for (const item of filterByPatternSimple(all.rules, patterns)) {
|
|
232
|
+
const item2 = item;
|
|
233
|
+
const src = join(repo.repoRoot, item2.relative_path);
|
|
234
|
+
const relpath = item2.relative_path.replace(config.rulesSubdir + "/", "");
|
|
235
|
+
toDownload.push({ src, dst: join(targetRulesDir, relpath) });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// Execute downloads
|
|
239
|
+
let copied = 0;
|
|
240
|
+
let skipped = 0;
|
|
241
|
+
const errors = [];
|
|
242
|
+
for (const { src, dst } of toDownload) {
|
|
243
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
244
|
+
if (existsSync(dst)) {
|
|
245
|
+
try {
|
|
246
|
+
if (fileHash(src) === fileHash(dst)) {
|
|
247
|
+
skipped++;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// fall through
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
copyFileSync(src, dst);
|
|
257
|
+
copied++;
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
errors.push(`Failed to copy ${src}: ${e instanceof Error ? e.message : String(e)}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return makeResult(errors.length === 0, `Downloaded ${copied} files (${skipped} skipped, commit ${repo.commitSha})`, { commit: repo.commitSha, patterns, copied, skipped, errors: errors.length > 0 ? errors : undefined });
|
|
264
|
+
}
|
|
265
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
266
|
+
async function main() {
|
|
267
|
+
const transport = new StdioServerTransport();
|
|
268
|
+
await server.connect(transport);
|
|
269
|
+
console.error("[mcp-git-fetcher] MCP server running on stdio");
|
|
270
|
+
}
|
|
271
|
+
main().catch((e) => {
|
|
272
|
+
console.error("[mcp-git-fetcher] Fatal error:", e);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
});
|
|
275
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface GitFetcherConfig {
|
|
2
|
+
/** GitLab repository URL (HTTP/HTTPS) */
|
|
3
|
+
repoUrl: string;
|
|
4
|
+
/** GitLab personal access token */
|
|
5
|
+
authToken?: string;
|
|
6
|
+
/** Local cache directory for cloned repositories */
|
|
7
|
+
cacheDir: string;
|
|
8
|
+
/** Branch to pull from */
|
|
9
|
+
branch: string;
|
|
10
|
+
/** Subdirectory name for skills in the repo */
|
|
11
|
+
skillsSubdir: string;
|
|
12
|
+
/** Subdirectory name for rules in the repo */
|
|
13
|
+
rulesSubdir: string;
|
|
14
|
+
/** Optional HTTP proxy */
|
|
15
|
+
httpProxy?: string;
|
|
16
|
+
/** Optional HTTPS proxy */
|
|
17
|
+
httpsProxy?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface SyncTargets {
|
|
20
|
+
targetSkillsDir: string;
|
|
21
|
+
targetRulesDir: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function loadConfig(): GitFetcherConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Build repo URL with embedded token for authentication.
|
|
26
|
+
* Transforms: http://host/repo.git → http://token@host/repo.git
|
|
27
|
+
*/
|
|
28
|
+
export declare function getAuthRepoUrl(config: GitFetcherConfig): string;
|
|
29
|
+
export declare function getGitEnv(config: GitFetcherConfig): Record<string, string>;
|
|
30
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,UAAU,IAAI,gBAAgB,CAW7C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAU/D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { config as dotenvConfig } from "dotenv";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
// Load .env from project root and ~/.mcp-git-fetcher/.env
|
|
6
|
+
dotenvConfig();
|
|
7
|
+
const homeEnvPath = join(homedir(), ".mcp-git-fetcher", ".env");
|
|
8
|
+
if (existsSync(homeEnvPath)) {
|
|
9
|
+
dotenvConfig({ path: homeEnvPath });
|
|
10
|
+
}
|
|
11
|
+
export function loadConfig() {
|
|
12
|
+
return {
|
|
13
|
+
repoUrl: process.env.GIT_REPO_URL ?? "",
|
|
14
|
+
authToken: process.env.GIT_AUTH_TOKEN,
|
|
15
|
+
cacheDir: process.env.CACHE_DIR ?? join(homedir(), ".mcp-git-fetcher", "cache"),
|
|
16
|
+
branch: process.env.GIT_BRANCH ?? "main",
|
|
17
|
+
skillsSubdir: process.env.SKILLS_SUBDIR ?? "skills",
|
|
18
|
+
rulesSubdir: process.env.RULES_SUBDIR ?? "rules",
|
|
19
|
+
httpProxy: process.env.HTTP_PROXY,
|
|
20
|
+
httpsProxy: process.env.HTTPS_PROXY,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build repo URL with embedded token for authentication.
|
|
25
|
+
* Transforms: http://host/repo.git → http://token@host/repo.git
|
|
26
|
+
*/
|
|
27
|
+
export function getAuthRepoUrl(config) {
|
|
28
|
+
if (!config.authToken)
|
|
29
|
+
return config.repoUrl;
|
|
30
|
+
if (config.repoUrl.includes("@"))
|
|
31
|
+
return config.repoUrl;
|
|
32
|
+
const idx = config.repoUrl.indexOf("://");
|
|
33
|
+
if (idx === -1)
|
|
34
|
+
return config.repoUrl;
|
|
35
|
+
const protocol = config.repoUrl.slice(0, idx + 3);
|
|
36
|
+
const rest = config.repoUrl.slice(idx + 3);
|
|
37
|
+
return `${protocol}${config.authToken}@${rest}`;
|
|
38
|
+
}
|
|
39
|
+
export function getGitEnv(config) {
|
|
40
|
+
const env = {};
|
|
41
|
+
if (config.httpProxy)
|
|
42
|
+
env.HTTP_PROXY = config.httpProxy;
|
|
43
|
+
if (config.httpsProxy)
|
|
44
|
+
env.HTTPS_PROXY = config.httpsProxy;
|
|
45
|
+
return env;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,UAAU,EAAgB,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,0DAA0D;AAC1D,YAAY,EAAE,CAAC;AACf,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,MAAM,CAAC,CAAC;AAChE,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;IAC5B,YAAY,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;AACtC,CAAC;AA0BD,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE;QACvC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;QACrC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,OAAO,CAAC;QAC/E,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM;QACxC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,QAAQ;QACnD,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO;QAChD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;QACjC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;KACpC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IAC7C,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IAExD,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IAEtC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAC3C,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAwB;IAChD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,MAAM,CAAC,SAAS;QAAE,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC;IACxD,IAAI,MAAM,CAAC,UAAU;QAAE,GAAG,CAAC,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;IAC3D,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface SyncResult {
|
|
2
|
+
copied: number;
|
|
3
|
+
skipped: number;
|
|
4
|
+
errors: string[];
|
|
5
|
+
sourceDir?: string;
|
|
6
|
+
targetDir?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Recursively sync files from source to target.
|
|
10
|
+
*
|
|
11
|
+
* - Creates target dir if missing
|
|
12
|
+
* - Copies files that don't exist at destination
|
|
13
|
+
* - Skips files with identical SHA-256
|
|
14
|
+
* - Overwrites files with different content
|
|
15
|
+
*/
|
|
16
|
+
export declare function syncDirectory(srcDir: string, dstDir: string): SyncResult;
|
|
17
|
+
/**
|
|
18
|
+
* Sync both skills/ and rules/ directories from the repo root.
|
|
19
|
+
*/
|
|
20
|
+
export declare function syncAll(repoRoot: string, skillsSubdir: string, rulesSubdir: string, targetSkillsDir?: string, targetRulesDir?: string): Record<string, SyncResult>;
|
|
21
|
+
//# sourceMappingURL=file-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-sync.d.ts","sourceRoot":"","sources":["../src/file-sync.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CA8DxE;AAED;;GAEG;AACH,wBAAgB,OAAO,CACrB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE,MAAM,EACxB,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAgB5B"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
|
|
3
|
+
import { dirname, join, relative } from "path";
|
|
4
|
+
/**
|
|
5
|
+
* Compute SHA-256 hash of a file's contents.
|
|
6
|
+
*/
|
|
7
|
+
function fileHash(filePath) {
|
|
8
|
+
const hash = createHash("sha256");
|
|
9
|
+
const content = readFileSync(filePath);
|
|
10
|
+
hash.update(content);
|
|
11
|
+
return hash.digest("hex");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Recursively sync files from source to target.
|
|
15
|
+
*
|
|
16
|
+
* - Creates target dir if missing
|
|
17
|
+
* - Copies files that don't exist at destination
|
|
18
|
+
* - Skips files with identical SHA-256
|
|
19
|
+
* - Overwrites files with different content
|
|
20
|
+
*/
|
|
21
|
+
export function syncDirectory(srcDir, dstDir) {
|
|
22
|
+
const result = {
|
|
23
|
+
copied: 0,
|
|
24
|
+
skipped: 0,
|
|
25
|
+
errors: [],
|
|
26
|
+
sourceDir: srcDir,
|
|
27
|
+
targetDir: dstDir,
|
|
28
|
+
};
|
|
29
|
+
if (!existsSync(srcDir)) {
|
|
30
|
+
result.errors.push(`Source directory not found: ${srcDir}`);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
mkdirSync(dstDir, { recursive: true });
|
|
34
|
+
function walk(current) {
|
|
35
|
+
const entries = readdirSync(current);
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
const srcFile = join(current, entry);
|
|
38
|
+
let stat;
|
|
39
|
+
try {
|
|
40
|
+
stat = statSync(srcFile);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (stat.isDirectory()) {
|
|
46
|
+
walk(srcFile);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const relPath = relative(srcDir, srcFile);
|
|
50
|
+
const dstFile = join(dstDir, relPath);
|
|
51
|
+
// Create parent directory
|
|
52
|
+
mkdirSync(dirname(dstFile), { recursive: true });
|
|
53
|
+
// Check if destination exists with same content
|
|
54
|
+
if (existsSync(dstFile)) {
|
|
55
|
+
try {
|
|
56
|
+
if (fileHash(srcFile) === fileHash(dstFile)) {
|
|
57
|
+
result.skipped++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Fall through to copy
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
copyFileSync(srcFile, dstFile);
|
|
67
|
+
result.copied++;
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
const msg = `Failed to copy ${srcFile} → ${dstFile}: ${e instanceof Error ? e.message : String(e)}`;
|
|
71
|
+
result.errors.push(msg);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
walk(srcDir);
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Sync both skills/ and rules/ directories from the repo root.
|
|
80
|
+
*/
|
|
81
|
+
export function syncAll(repoRoot, skillsSubdir, rulesSubdir, targetSkillsDir, targetRulesDir) {
|
|
82
|
+
const results = {};
|
|
83
|
+
if (targetSkillsDir) {
|
|
84
|
+
results.skills = syncDirectory(join(repoRoot, skillsSubdir), targetSkillsDir);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
results.skills = { copied: 0, skipped: 0, errors: ["target_skills_dir not provided"], sourceDir: join(repoRoot, skillsSubdir) };
|
|
88
|
+
}
|
|
89
|
+
if (targetRulesDir) {
|
|
90
|
+
results.rules = syncDirectory(join(repoRoot, rulesSubdir), targetRulesDir);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
results.rules = { copied: 0, skipped: 0, errors: ["target_rules_dir not provided"], sourceDir: join(repoRoot, rulesSubdir) };
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=file-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-sync.js","sourceRoot":"","sources":["../src/file-sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GitFetcherConfig } from "./config.js";
|
|
2
|
+
export declare class GitOperationError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export interface RepoHandle {
|
|
6
|
+
repoRoot: string;
|
|
7
|
+
commitSha: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Ensure the repository is cloned and up-to-date.
|
|
11
|
+
*
|
|
12
|
+
* - cacheDir doesn't exist → git clone
|
|
13
|
+
* - cacheDir exists with .git → git pull (with hard reset fallback)
|
|
14
|
+
*/
|
|
15
|
+
export declare function ensureRepo(config: GitFetcherConfig): Promise<RepoHandle>;
|
|
16
|
+
/**
|
|
17
|
+
* List all files recursively under a subdirectory of the repo root.
|
|
18
|
+
* Returns absolute paths. Returns empty array if subdir doesn't exist.
|
|
19
|
+
*/
|
|
20
|
+
export declare function listSubdirFiles(repoRoot: string, subdir: string): string[];
|
|
21
|
+
//# sourceMappingURL=git-ops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.d.ts","sourceRoot":"","sources":["../src/git-ops.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAA6B,MAAM,aAAa,CAAC;AAO1E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+C9E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAwB1E"}
|
package/dist/git-ops.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import simpleGitModule from "simple-git";
|
|
4
|
+
import { getAuthRepoUrl, getGitEnv } from "./config.js";
|
|
5
|
+
// simple-git ESM compatibility: default export is callable, cast to avoid TS friction
|
|
6
|
+
const createGit = simpleGitModule;
|
|
7
|
+
export class GitOperationError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "GitOperationError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Ensure the repository is cloned and up-to-date.
|
|
15
|
+
*
|
|
16
|
+
* - cacheDir doesn't exist → git clone
|
|
17
|
+
* - cacheDir exists with .git → git pull (with hard reset fallback)
|
|
18
|
+
*/
|
|
19
|
+
export async function ensureRepo(config) {
|
|
20
|
+
const cacheDir = config.cacheDir;
|
|
21
|
+
const gitDir = join(cacheDir, ".git");
|
|
22
|
+
if (!config.repoUrl) {
|
|
23
|
+
throw new GitOperationError("GIT_REPO_URL is not configured. Set the GIT_REPO_URL environment variable.");
|
|
24
|
+
}
|
|
25
|
+
const authUrl = getAuthRepoUrl(config);
|
|
26
|
+
const env = getGitEnv(config);
|
|
27
|
+
if (existsSync(gitDir)) {
|
|
28
|
+
console.error(`[mcp-git-fetcher] Repository cache exists at ${cacheDir}, pulling latest...`);
|
|
29
|
+
const git = createGit(cacheDir);
|
|
30
|
+
await git.fetch();
|
|
31
|
+
const currentBranch = (await git.branch()).current;
|
|
32
|
+
try {
|
|
33
|
+
await git.pull("origin", currentBranch, { "--ff-only": null });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
console.error("[mcp-git-fetcher] Fast-forward failed, doing hard reset...");
|
|
37
|
+
await git.reset(["--hard", `origin/${currentBranch}`]);
|
|
38
|
+
}
|
|
39
|
+
const log = await git.log({ maxCount: 1 });
|
|
40
|
+
return {
|
|
41
|
+
repoRoot: cacheDir,
|
|
42
|
+
commitSha: log.latest?.hash.slice(0, 8) ?? "unknown",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
console.error(`[mcp-git-fetcher] Cloning repository to ${cacheDir}...`);
|
|
46
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
47
|
+
// Clone via the module directly (no cwd) then re-attach
|
|
48
|
+
const tempGit = createGit();
|
|
49
|
+
await tempGit.clone(authUrl, cacheDir, ["--branch", config.branch]);
|
|
50
|
+
const repo = createGit(cacheDir);
|
|
51
|
+
const log = await repo.log({ maxCount: 1 });
|
|
52
|
+
const commitSha = log.latest?.hash.slice(0, 8) ?? "unknown";
|
|
53
|
+
console.error(`[mcp-git-fetcher] Cloned at commit ${commitSha}`);
|
|
54
|
+
return { repoRoot: cacheDir, commitSha };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* List all files recursively under a subdirectory of the repo root.
|
|
58
|
+
* Returns absolute paths. Returns empty array if subdir doesn't exist.
|
|
59
|
+
*/
|
|
60
|
+
export function listSubdirFiles(repoRoot, subdir) {
|
|
61
|
+
const dir = join(repoRoot, subdir);
|
|
62
|
+
if (!existsSync(dir))
|
|
63
|
+
return [];
|
|
64
|
+
const results = [];
|
|
65
|
+
function walk(current) {
|
|
66
|
+
const entries = readdirSync(current);
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const full = join(current, entry);
|
|
69
|
+
try {
|
|
70
|
+
if (statSync(full).isDirectory()) {
|
|
71
|
+
walk(full);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
results.push(full);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Permission or race — skip
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
walk(dir);
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=git-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.js","sourceRoot":"","sources":["../src/git-ops.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-git-fetcher",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server to fetch skills/rules files from internal GitLab repositories",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-git-fetcher": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublish": "tsc",
|
|
15
|
+
"start": "node dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"gitlab",
|
|
20
|
+
"skills",
|
|
21
|
+
"rules",
|
|
22
|
+
"model-context-protocol"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.11.0",
|
|
27
|
+
"simple-git": "^3.27.0",
|
|
28
|
+
"dotenv": "^16.4.7"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.14.0",
|
|
32
|
+
"typescript": "^5.8.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/your-org/mcp-git-fetcher"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
}
|
|
41
|
+
}
|