AgentSkillsCN

github

通过托管式 OAuth 集成 GitHub API。访问仓库、问题、拉取请求、提交记录、分支以及用户。 当用户想要与 GitHub 仓库交互,管理问题与 PR,搜索代码,或自动化工作流程时,这款技能将为您提供便利。 对于其他第三方应用,可选用 api-gateway 技能(https://clawhub.ai/byungkyu/api-gateway)。

SKILL.md
--- frontmatter
name: github
description: |
  GitHub API integration with managed OAuth. Access repositories, issues, pull requests, commits, branches, and users.
  Use this skill when users want to interact with GitHub repositories, manage issues and PRs, search code, or automate workflows.
  For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
compatibility: Requires network access and valid Maton API key
metadata:
  author: maton
  version: "1.0"

GitHub

Access the GitHub REST API with managed OAuth authentication. Manage repositories, issues, pull requests, commits, branches, users, and more.

Quick Start

bash
# Get authenticated user
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/github/user')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Base URL

code
https://gateway.maton.ai/github/{native-api-path}

Replace {native-api-path} with the actual GitHub API endpoint path. The gateway proxies requests to api.github.com and automatically injects your OAuth token.

Authentication

All requests require the Maton API key in the Authorization header:

code
Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

bash
export MATON_API_KEY="YOUR_API_KEY"

Getting Your API Key

  1. Sign in or create an account at maton.ai
  2. Go to maton.ai/settings
  3. Copy your API key

Connection Management

Manage your GitHub OAuth connections at https://ctrl.maton.ai.

List Connections

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=github&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Create Connection

bash
python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'github'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Get Connection

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Response:

json
{
  "connection": {
    "connection_id": "83e7c665-60f6-4a64-816c-5e287ea8982f",
    "status": "ACTIVE",
    "creation_time": "2026-02-06T03:00:43.860014Z",
    "last_updated_time": "2026-02-06T03:01:06.027323Z",
    "url": "https://connect.maton.ai/?session_token=...",
    "app": "github",
    "metadata": {}
  }
}

Open the returned url in a browser to complete OAuth authorization.

Delete Connection

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Specifying Connection

If you have multiple GitHub connections, specify which one to use with the Maton-Connection header:

bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/github/user')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '83e7c665-60f6-4a64-816c-5e287ea8982f')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

If omitted, the gateway uses the default (oldest) active connection.

API Reference

Users

Get Authenticated User

bash
GET /github/user

Get User by Username

bash
GET /github/users/{username}

List Users

bash
GET /github/users?since={user_id}&per_page=30

Repositories

List User Repositories

bash
GET /github/user/repos?per_page=30&sort=updated

Query parameters: type (all, owner, public, private, member), sort (created, updated, pushed, full_name), direction (asc, desc), per_page, page

List Organization Repositories

bash
GET /github/orgs/{org}/repos?per_page=30

Get Repository

bash
GET /github/repos/{owner}/{repo}

Create Repository (User)

bash
POST /github/user/repos
Content-Type: application/json

{
  "name": "my-new-repo",
  "description": "A new repository",
  "private": true,
  "auto_init": true
}

Create Repository (Organization)

bash
POST /github/orgs/{org}/repos
Content-Type: application/json

{
  "name": "my-new-repo",
  "description": "A new repository",
  "private": true
}

Update Repository

bash
PATCH /github/repos/{owner}/{repo}
Content-Type: application/json

{
  "description": "Updated description",
  "has_issues": true,
  "has_wiki": false
}

Delete Repository

bash
DELETE /github/repos/{owner}/{repo}

Repository Contents

List Contents

bash
GET /github/repos/{owner}/{repo}/contents/{path}

Get File Contents

bash
GET /github/repos/{owner}/{repo}/contents/{path}?ref={branch}

Create or Update File

bash
PUT /github/repos/{owner}/{repo}/contents/{path}
Content-Type: application/json

{
  "message": "Create new file",
  "content": "SGVsbG8gV29ybGQh",
  "branch": "main"
}

Note: content must be Base64 encoded.

Delete File

bash
DELETE /github/repos/{owner}/{repo}/contents/{path}
Content-Type: application/json

{
  "message": "Delete file",
  "sha": "{file_sha}",
  "branch": "main"
}

Branches

List Branches

bash
GET /github/repos/{owner}/{repo}/branches?per_page=30

Get Branch

bash
GET /github/repos/{owner}/{repo}/branches/{branch}

Rename Branch

bash
POST /github/repos/{owner}/{repo}/branches/{branch}/rename
Content-Type: application/json

{
  "new_name": "new-branch-name"
}

Merge Branches

bash
POST /github/repos/{owner}/{repo}/merges
Content-Type: application/json

{
  "base": "main",
  "head": "feature-branch",
  "commit_message": "Merge feature branch"
}

Commits

List Commits

bash
GET /github/repos/{owner}/{repo}/commits?per_page=30

Query parameters: sha (branch name or commit SHA), path (file path), author, committer, since, until, per_page, page

Get Commit

bash
GET /github/repos/{owner}/{repo}/commits/{ref}

Compare Two Commits

bash
GET /github/repos/{owner}/{repo}/compare/{base}...{head}

Issues

List Repository Issues

bash
GET /github/repos/{owner}/{repo}/issues?state=open&per_page=30

Query parameters: state (open, closed, all), labels, assignee, creator, mentioned, sort, direction, since, per_page, page

Get Issue

bash
GET /github/repos/{owner}/{repo}/issues/{issue_number}

Create Issue

bash
POST /github/repos/{owner}/{repo}/issues
Content-Type: application/json

{
  "title": "Found a bug",
  "body": "Bug description here",
  "labels": ["bug"],
  "assignees": ["username"]
}

Update Issue

bash
PATCH /github/repos/{owner}/{repo}/issues/{issue_number}
Content-Type: application/json

{
  "state": "closed",
  "state_reason": "completed"
}

Lock Issue

bash
PUT /github/repos/{owner}/{repo}/issues/{issue_number}/lock
Content-Type: application/json

{
  "lock_reason": "resolved"
}

Unlock Issue

bash
DELETE /github/repos/{owner}/{repo}/issues/{issue_number}/lock

Issue Comments

List Issue Comments

bash
GET /github/repos/{owner}/{repo}/issues/{issue_number}/comments?per_page=30

Create Issue Comment

bash
POST /github/repos/{owner}/{repo}/issues/{issue_number}/comments
Content-Type: application/json

{
  "body": "This is a comment"
}

Update Issue Comment

bash
PATCH /github/repos/{owner}/{repo}/issues/comments/{comment_id}
Content-Type: application/json

{
  "body": "Updated comment"
}

Delete Issue Comment

bash
DELETE /github/repos/{owner}/{repo}/issues/comments/{comment_id}

Labels

List Labels

bash
GET /github/repos/{owner}/{repo}/labels?per_page=30

Create Label

bash
POST /github/repos/{owner}/{repo}/labels
Content-Type: application/json

{
  "name": "priority:high",
  "color": "ff0000",
  "description": "High priority issues"
}

Milestones

List Milestones

bash
GET /github/repos/{owner}/{repo}/milestones?state=open&per_page=30

Create Milestone

bash
POST /github/repos/{owner}/{repo}/milestones
Content-Type: application/json

{
  "title": "v1.0",
  "state": "open",
  "description": "First release",
  "due_on": "2026-03-01T00:00:00Z"
}

Pull Requests

List Pull Requests

bash
GET /github/repos/{owner}/{repo}/pulls?state=open&per_page=30

Query parameters: state (open, closed, all), head, base, sort, direction, per_page, page

Get Pull Request

bash
GET /github/repos/{owner}/{repo}/pulls/{pull_number}

Create Pull Request

bash
POST /github/repos/{owner}/{repo}/pulls
Content-Type: application/json

{
  "title": "New feature",
  "body": "Description of changes",
  "head": "feature-branch",
  "base": "main",
  "draft": false
}

Update Pull Request

bash
PATCH /github/repos/{owner}/{repo}/pulls/{pull_number}
Content-Type: application/json

{
  "title": "Updated title",
  "state": "closed"
}

List Pull Request Commits

bash
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/commits?per_page=30

List Pull Request Files

bash
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/files?per_page=30

Check If Merged

bash
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/merge

Merge Pull Request

bash
PUT /github/repos/{owner}/{repo}/pulls/{pull_number}/merge
Content-Type: application/json

{
  "commit_title": "Merge pull request",
  "merge_method": "squash"
}

Merge methods: merge, squash, rebase

Pull Request Reviews

List Reviews

bash
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/reviews?per_page=30

Create Review

bash
POST /github/repos/{owner}/{repo}/pulls/{pull_number}/reviews
Content-Type: application/json

{
  "body": "Looks good!",
  "event": "APPROVE"
}

Events: APPROVE, REQUEST_CHANGES, COMMENT

Search

Search Repositories

bash
GET /github/search/repositories?q={query}&per_page=30

Example queries:

  • tetris+language:python - Repositories with "tetris" in Python
  • react+stars:>10000 - Repositories with "react" and 10k+ stars

Search Issues

bash
GET /github/search/issues?q={query}&per_page=30

Example queries:

  • bug+is:open+is:issue - Open issues containing "bug"
  • author:username+is:pr - Pull requests by author

Search Code

bash
GET /github/search/code?q={query}&per_page=30

Example queries:

  • addClass+repo:facebook/react - Search for "addClass" in a specific repo
  • function+extension:js - JavaScript functions

Note: Code search may timeout on broad queries.

Search Users

bash
GET /github/search/users?q={query}&per_page=30

Organizations

List User Organizations

bash
GET /github/user/orgs?per_page=30

Note: Requires read:org scope.

Get Organization

bash
GET /github/orgs/{org}

List Organization Members

bash
GET /github/orgs/{org}/members?per_page=30

Rate Limit

Get Rate Limit

bash
GET /github/rate_limit

Response:

json
{
  "rate": {
    "limit": 5000,
    "remaining": 4979,
    "reset": 1707200000
  },
  "resources": {
    "core": { "limit": 5000, "remaining": 4979 },
    "search": { "limit": 30, "remaining": 28 }
  }
}

Pagination

GitHub uses page-based and link-based pagination:

bash
GET /github/repos/{owner}/{repo}/issues?per_page=30&page=2

Response headers include pagination links:

  • Link: <url>; rel="next", <url>; rel="last"

Common pagination parameters:

  • per_page: Results per page (max 100, default 30)
  • page: Page number (default 1)

Some endpoints use cursor-based pagination with since parameter (e.g., listing users).

Code Examples

JavaScript

javascript
const response = await fetch(
  'https://gateway.maton.ai/github/repos/owner/repo/issues?state=open&per_page=10',
  {
    headers: {
      'Authorization': `Bearer ${process.env.MATON_API_KEY}`
    }
  }
);
const issues = await response.json();

Python

python
import os
import requests

response = requests.get(
    'https://gateway.maton.ai/github/repos/owner/repo/issues',
    headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
    params={'state': 'open', 'per_page': 10}
)
issues = response.json()

Notes

  • Repository names are case-insensitive but the API preserves case
  • Issue numbers and PR numbers share the same sequence per repository
  • Content must be Base64 encoded when creating/updating files
  • Rate limits: 5000 requests/hour for authenticated users, 30 searches/minute
  • Search queries may timeout on very broad patterns
  • Some endpoints require specific OAuth scopes (e.g., read:org for organization operations)
  • IMPORTANT: When using curl commands, use curl -g when URLs contain brackets to disable glob parsing
  • IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments

Error Handling

StatusMeaning
400Missing GitHub connection
401Invalid or missing Maton API key
403Forbidden - insufficient permissions or scope
404Resource not found
408Request timeout (common for complex searches)
422Validation failed
429Rate limited
4xx/5xxPassthrough error from GitHub API

Troubleshooting: Invalid API Key

When you receive a "Invalid API key" error, ALWAYS follow these steps before concluding there is an issue:

  1. Check that the MATON_API_KEY environment variable is set:
bash
echo $MATON_API_KEY
  1. Verify the API key is valid by listing connections:
bash
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Resources