GitHub

SkillSearch

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).

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the GitHub skill

What this skill tells your AI

The instructions your AI receives, as published by craftos-dev/craftbot in skills/github-api/SKILL.md and read by ahel’s review.

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

Quick Start

# 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

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:

Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

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

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

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

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:

{
  "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

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:

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
GET /github/user
Get User by Username
GET /github/users/{username}
List Users
GET /github/users?since={user_id}&per_page=30

Repositories

List User Repositories
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
GET /github/orgs/{org}/repos?per_page=30
Get Repository
GET /github/repos/{owner}/{repo}
Create Repository (User)
POST /github/user/repos
Content-Type: application/json

{
  "name": "my-new-repo",
  "description": "A new repository",
  "private": true,
  "auto_init": true
}
Create Repository (Organization)
POST /github/orgs/{org}/repos
Content-Type: application/json

{
  "name": "my-new-repo",
  "description": "A new repository",
  "private": true
}
Update Repository
PATCH /github/repos/{owner}/{repo}
Content-Type: application/json

{
  "description": "Updated description",
  "has_issues": true,
  "has_wiki": false
}
Delete Repository
DELETE /github/repos/{owner}/{repo}

Repository Contents

List Contents
GET /github/repos/{owner}/{repo}/contents/{path}
Get File Contents
GET /github/repos/{owner}/{repo}/contents/{path}?ref={branch}
Create or Update File
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
DELETE /github/repos/{owner}/{repo}/contents/{path}
Content-Type: application/json

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

Branches

List Branches
GET /github/repos/{owner}/{repo}/branches?per_page=30
Get Branch
GET /github/repos/{owner}/{repo}/branches/{branch}
Rename Branch
POST /github/repos/{owner}/{repo}/branches/{branch}/rename
Content-Type: application/json

{
  "new_name": "new-branch-name"
}
Merge Branches
POST /github/repos/{owner}/{repo}/merges
Content-Type: application/json

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

Commits

List Commits
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
GET /github/repos/{owner}/{repo}/commits/{ref}
Compare Two Commits
GET /github/repos/{owner}/{repo}/compare/{base}...{head}

Issues

List Repository Issues
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
GET /github/repos/{owner}/{repo}/issues/{issue_number}
Create Issue
POST /github/repos/{owner}/{repo}/issues
Content-Type: application/json

{
  "title": "Found a bug",
  "body": "Bug description here",
  "labels": ["bug"],
  "assignees": ["username"]
}
Update Issue
PATCH /github/repos/{owner}/{repo}/issues/{issue_number}
Content-Type: application/json

{
  "state": "closed",
  "state_reason": "completed"
}
Lock Issue
PUT /github/repos/{owner}/{repo}/issues/{issue_number}/lock
Content-Type: application/json

{
  "lock_reason": "resolved"
}
Unlock Issue
DELETE /github/repos/{owner}/{repo}/issues/{issue_number}/lock

Issue Comments

List Issue Comments
GET /github/repos/{owner}/{repo}/issues/{issue_number}/comments?per_page=30
Create Issue Comment
POST /github/repos/{owner}/{repo}/issues/{issue_number}/comments
Content-Type: application/json

{
  "body": "This is a comment"
}
Update Issue Comment
PATCH /github/repos/{owner}/{repo}/issues/comments/{comment_id}
Content-Type: application/json

{
  "body": "Updated comment"
}
Delete Issue Comment
DELETE /github/repos/{owner}/{repo}/issues/comments/{comment_id}

Labels

List Labels
GET /github/repos/{owner}/{repo}/labels?per_page=30
Create Label
POST /github/repos/{owner}/{repo}/labels
Content-Type: application/json

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

Milestones

List Milestones
GET /github/repos/{owner}/{repo}/milestones?state=open&per_page=30
Create Milestone
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
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
GET /github/repos/{owner}/{repo}/pulls/{pull_number}
Create Pull Request
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
PATCH /github/repos/{owner}/{repo}/pulls/{pull_number}
Content-Type: application/json

{
  "title": "Updated title",
  "state": "closed"
}
List Pull Request Commits
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/commits?per_page=30
List Pull Request Files
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/files?per_page=30
Check If Merged
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/merge
Merge Pull Request
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
GET /github/repos/{owner}/{repo}/pulls/{pull_number}/reviews?per_page=30
Create Review
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
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
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
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
GET /github/search/users?q={query}&per_page=30

Organizations

List User Organizations
GET /github/user/orgs?per_page=30

Note: Requires read:org scope.

Get Organization
GET /github/orgs/{org}
List Organization Members
GET /github/orgs/{org}/members?per_page=30

Rate Limit

Get Rate Limit
GET /github/rate_limit

Response:

{
  "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:

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

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

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). If you receive a scope error, contact Maton support at support@maton.ai with the specific operations/APIs you need and your use-case
  • 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: API Key Issues

  1. Check that the MATON_API_KEY environment variable is set:
echo $MATON_API_KEY
  1. Verify the API key is valid by listing connections:
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

Troubleshooting: Invalid App Name

  1. Ensure your URL path starts with github. For example:
  • Correct: https://gateway.maton.ai/github/user
  • Incorrect: https://gateway.maton.ai/api.github.com/user

Resources

Signals

GitHub stars
382
Forks
45
Last commit
Sep 2026

ahel review

  • S4info
    community integration — published by craftos-dev, not github

Automated review, not a security audit. Ruleset v1.

Advanced
Catalog kind
skill
Gateway key
github-api
Source
github.com/craftos-dev/craftbot