mcpField
A
accessing-github-repos

accessing-github-repos

github.com/oaustegard开发15768 次安装1 天前更新
代码仓库

关于 accessing-github-repos

此技能用于在 git clone 受阻的容器化 AI 环境中访问 GitHub 仓库,改用 GitHub REST API 和原始文件 URL。可通过 PAT 访问私有仓库并执行写入操作。

主要功能

  1. 01通过原始 URL 或 API 获取文件
  2. 02列出仓库目录树并下载归档
  3. 03从环境变量和项目文件检测 PAT
  4. 04创建文件、分支、Issue 和 Pull Request
  5. 05使用 PAT 访问私有仓库

适用场景

  1. 01不使用 git clone 阅读公开仓库文件
  2. 02通过 PAT 访问私有仓库
  3. 03通过 API 更新仓库文件
  4. 04在受限环境中管理 Issue 或创建 Pull Request

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/oaustegard/claude-skills --skill accessing-github-repos

安装后在 Claude Code 中输入/accessing-github-repos确认已加载。

附带文件CHANGELOG.md

详细介绍

访问 GitHub 仓库

在容器化 AI 环境中,git clone 会被阻止(出口代理会拒绝 CONNECT 隧道),但仍可通过 GitHub REST API 和原始文件 URL 完整访问仓库。

快速开始

公开仓库(无需设置)
# Individual file via raw URL
curl -sL "https://raw.githubusercontent.com/OWNER/REPO/BRANCH/path/file"

# Directory tree via API
curl -sL "https://api.github.com/repos/OWNER/REPO/git/trees/BRANCH?recursive=1"
私有仓库或写入权限

需要 GitHub Personal Access Token(PAT)。请参阅下方的“设置”部分。

设置

凭据配置

此技能会自动从环境变量或项目文件中检测 PAT:

环境变量(按以下顺序检查):

  • GITHUB_PAT
  • GH_PAT
  • GITHUB_TOKEN
  • GH_TOKEN

项目文件(Claude.ai):

  • /mnt/project/.env
  • /mnt/project/github.env

格式:

GITHUB_PAT=github_pat_11AAAAAA...
创建 GitHub PAT
  1. GitHub → Settings → Developer settings → Fine-grained tokens
  2. 创建令牌,并将其限定为所需的仓库
  3. 设置权限:
    • Contents: Read - 用于访问私有仓库
    • Contents: Write - 用于推送文件
    • Issues: Write - 用于管理议题
    • Pull requests: Write - 用于创建 PR
网络访问(Claude.ai Projects)

将以下地址添加到网络允许列表:

  • api.github.com
  • raw.githubusercontent.com

不同认证级别的功能

功能无 PAT(仅公开仓库)PAT(读取)PAT(写入)
获取公开文件✅✅✅
获取私有文件❌✅✅
下载 tarball✅ 公开✅✅
创建/更新文件❌❌✅
创建分支❌❌✅
管理议题❌❌✅
创建 PR❌❌✅

Python 辅助函数

凭据检测
import os

def get_github_auth():
    """Returns (token, source) or (None, None)"""
    # Check environment variables
    for var in ['GITHUB_PAT', 'GH_PAT', 'GITHUB_TOKEN', 'GH_TOKEN']:
        if token := os.environ.get(var):
            return token, var

    # Check project .env files
    env_paths = ['/mnt/project/.env', '/mnt/project/github.env']
    for path in env_paths:
        try:
            with open(path) as f:
                for line in f:
                    if '=' in line and not line.startswith('#'):
                        key, val = line.strip().split('=', 1)
                        if key in ['GITHUB_PAT', 'GH_PAT', 'GITHUB_TOKEN']:
                            return val.strip(), f'{path}:{key}'
        except FileNotFoundError:
            continue

    return None, None
获取单个文件
import base64
import urllib.request
import json

def fetch_file(owner: str, repo: str, path: str, ref: str = 'main', token: str = None) -> str:
    """Fetch single file. Uses API if token provided, raw URL otherwise."""
    if token:
        # Use API (works for private repos)
        url = f'https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}'
        req = urllib.request.Request(url, headers={
            'Authorization': f'Bearer {token}',
            'Accept': 'application/vnd.github+json'
        })
        with urllib.request.urlopen(req) as resp:
            data = json.load(resp)
            return base64.b64decode(data['content']).decode()
    else:
        # Use raw URL (public repos only)
        url = f'https://raw.githubusercontent.com/{owner}/{repo}/{ref}/{path}'
        with urllib.request.urlopen(url) as resp:
            return resp.read().decode()
获取仓库 Tarball
def fetch_repo_tarball(owner: str, repo: str, ref: str = 'main', token: str = None) -> bytes:
    """Download full repo as tarball. Requires token for private repos."""
    url = f'https://api.github.com/repos/{owner}/{repo}/tarball/{ref}'
    headers = {'Accept': 'application/vnd.github+json'}
    if token:
        headers['Authorization'] = f'Bearer {token}'

    req = urllib.request.Request(url, headers=headers)
    with urllib.request.urlopen(req) as resp:
        return resp.read()

# Usage:
tarball = fetch_repo_tarball('owner', 'repo', 'main', token)
with open('/tmp/repo.tar.gz', 'wb') as f:
    f.write(tarball)
# Extract: tar -xzf /tmp/repo.tar.gz
创建或更新文件
def push_file(owner: str, repo: str, path: str, content: str,
              message: str, token: str, sha: str = None) -> dict:
    """Create/update file via API. Returns commit info.

    Args:
        sha: Required when updating existing file (get via contents API)
    """
    url = f'https://api.github.com/repos/{owner}/{repo}/contents/{path}'

    payload = {
        'message': message,
        'content': base64.b64encode(content.encode()).decode()
    }
    if sha:  # Update existing file
        payload['sha'] = sha

    req = urllib.request.Request(url,
        data=json.dumps(payload).encode(),
        headers={
            'Authorization': f'Bearer {token}',
            'Accept': 'application/vnd.github+json',
            'Content-Type': 'application/json'
        },
        method='PUT')

    with urllib.request.urlopen(req) as resp:
        return json.load(resp)
获取文件 SHA(用于更新)
def get_file_sha(owner: str, repo: str, path: str, token: str, ref: str = 'main') -> str:
    """Get file SHA needed for updates."""
    url = f'https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}'
    req = urllib.request.Request(url, headers={
        'Authorization': f'Bearer {token}',
        'Accept': 'application/vnd.github+json'
    })
    with urllib.request.urlopen(req) as resp:
        data = json.load(resp)
        return data['sha']

Bash 示例

获取公开文件
curl -sL "https://raw.githubusercontent.com/owner/repo/main/path/file.py"
获取私有文件(使用 PAT)
curl -H "Authorization: Bearer $GITHUB_PAT" \
     -H "Accept: application/vnd.github+json" \
     "https://api.github.com/repos/owner/repo/contents/path/file.py" | \
     python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())"
下载仓库 Tarball
# Public repo
curl -sL "https://api.github.com/repos/owner/repo/tarball/main" -o repo.tar.gz

# Private repo
curl -sL -H "Authorization: Bearer $GITHUB_PAT" \
     "https://api.github.com/repos/owner/repo/tarball/main" -o repo.tar.gz
tar -xzf repo.tar.gz
创建/更新文件
# Encode content
CONTENT=$(cat file.txt | base64 -w0)

# Push (new file)
curl -X PUT \
     -H "Authorization: Bearer $GITHUB_PAT" \
     -H "Accept: application/vnd.github+json" \
     "https://api.github.com/repos/owner/repo/contents/path/file.txt" \
     -d "{\"message\":\"Add file\",\"content\":\"$CONTENT\"}"

# Push (update existing - need SHA first)
SHA=$(curl -s -H "Authorization: Bearer $GITHUB_PAT" \
           "https://api.github.com/repos/owner/repo/contents/path/file.txt" | \
           python3 -c "import sys,json; print(json.load(sys.stdin)['sha'])")

curl -X PUT \
     -H "Authorization: Bearer $GITHUB_PAT" \
     -H "Accept: application/vnd.github+json" \
     "https://api.github.com/repos/owner/repo/contents/path/file.txt" \
     -d "{\"message\":\"Update file\",\"content\":\"$CONTENT\",\"sha\":\"$SHA\"}"
列出目录树
curl -sL "https://api.github.com/repos/owner/repo/git/trees/main?recursive=1" | \
  python3 -c "import json, sys; [print(f['path']) for f in json.load(sys.stdin)['tree'] if f['type']=='blob']"

为什么 git clone 无法使用

容器的出口代理会阻止 git 协议操作:

  • HTTPS 克隆:代理在 CONNECT 隧道上返回 401
  • SSH:容器中没有 ssh 二进制文件
  • git:// 协议:DNS 解析受阻

GitHub REST API 使用标准 HTTPS,可正常通过代理路由。

请勿

  • 绝不要尝试 git clone(浪费时间,而且总会失败)
  • 绝不要建议需要使用 git 协议的变通方法
  • 绝不要尝试使用不同的 git 标志或 SSH URL 重试
  • 绝不要建议使用 git submodules、git archive 或其他 git 协议操作

速率限制

  • 已认证:每小时 5,000 次请求
  • 未认证:每小时 60 次请求

如果使用量较大,请务必提供 PAT。

此技能不适用的情况

  • 原生开发环境:可直接访问 git,请使用标准 git 命令
  • 本地计算机:git clone 可正常使用
  • 配置了 MCP GitHub 服务的环境:请改用 MCP 工具

评论

支持 Markdown