github-mcp-lite
by MatinMHF
README.md
<p align="center">
<b>English</b> | <a href="README.fa.md">فارسی</a>
</p>
# github-mcp-lite
A minimal [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server
for creating GitHub repositories, committing files, and publishing releases
using a plain Personal Access Token (PAT).
No Docker, no local Git installation, and no GitHub Copilot subscription
required — unlike the [hosted `api.githubcopilot.com/mcp/`
server](https://github.com/github/github-mcp-server), which gates access
behind a Copilot entitlement. This server talks directly to the regular
GitHub REST API (`api.github.com`) over plain `fetch`, so it's a single
dependency-light file you can read top to bottom in a few minutes.
## Why this exists
Most MCP GitHub integrations either require a full local git toolchain, a
Docker container, or a Copilot-gated hosted endpoint. `github-mcp-lite` is
for cases where you just want an AI assistant to be able to:
- spin up a new repo,
- push a batch of files to it in one commit, and
- tag a release,
using nothing but a token you already have.
## Tools
| Tool | Description | Destructive? |
| --- | --- | --- |
| `get_authenticated_user` | Return the login/profile for the account owning the configured token. | No |
| `list_repositories` | List repositories the token can see, newest-updated first. | No |
| `create_repository` | Create a repo owned by the authenticated user. Auto-initialized with a README so a default branch exists immediately. | No |
| `commit_files` | Create or update one or more text files in a single commit. | Yes (overwrites file contents) |
| `commit_directory` | Upload an entire local directory in one commit. Handles binary files and respects `.gitignore` inside a git work tree. | Yes (overwrites file contents) |
| `create_release` | Publish a tagged GitHub release. | No |
| `delete_repository` | Permanently delete a repository. Requires the `delete_repo` token scope. | **Yes, irreversible** |
| `github_request` | Authenticated passthrough to any `api.github.com` path, any verb. | **Yes — full token authority** |
Each tool is annotated with `readOnlyHint` / `destructiveHint` /
`openWorldHint` metadata so MCP clients can surface appropriate confirmation
prompts.
### `get_authenticated_user`
No input. Returns `{ login, html_url, name }` for the token's owner.
### `list_repositories`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `affiliation` | string | no | Comma-separated: `owner`, `collaborator`, `organization_member`. Defaults to all three |
| `visibility` | `all` \| `public` \| `private` | no | Defaults to `all` |
| `per_page` | number | no | 1–100, defaults to `100` |
| `page` | number | no | Defaults to `1` |
Wraps `GET /user/repos`, sorted by most recently updated. Returns an array of
`{ full_name, private, language, updated_at, html_url }`. Results are paginated
— a full page means you should ask for the next one.
### `create_repository`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `name` | string | yes | Repository name |
| `description` | string | no | Repository description |
| `private` | boolean | no | Defaults to `false` (public) |
Returns `{ full_name, html_url, clone_url, default_branch }`.
### `commit_files`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `owner` | string | yes | Repository owner |
| `repo` | string | yes | Repository name |
| `branch` | string | no | Defaults to the repo's default branch |
| `message` | string | yes | Commit message |
| `files` | array of `{ path, content }` | yes | UTF-8 text content only; at least one file |
Internally this creates blobs, a tree, and a commit via the Git Data API,
then fast-forwards the branch ref — so it's a real single commit, not one
commit per file. Returns `{ commit_sha, html_url, files_changed }`.
### `commit_directory`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `owner` | string | yes | Repository owner |
| `repo` | string | yes | Repository name |
| `directory` | string | yes | Absolute path to the local directory to upload |
| `branch` | string | no | Defaults to the repo's default branch |
| `message` | string | yes | Commit message |
| `exclude` | array of string | no | Extra path substrings to skip, e.g. `['.env', 'secrets/']` |
Use this instead of `commit_files` when pushing a whole project. Inside a git
work tree it defers to `git ls-files` so `.gitignore` is honoured; otherwise it
walks the directory and prunes the usual junk (`node_modules`, `.git`, `dist`,
`build`, `.next`, `coverage`). Binary files are detected and uploaded as blobs;
text files are inlined into the tree, and the tree is built in chunks so a
large project stays within a handful of round trips. Returns
`{ commit_sha, html_url, files_uploaded, skipped }`.
> **Note:** this merges onto the existing tree — it adds and updates files but
> never deletes. A file removed locally will still be present on GitHub.
### `create_release`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `owner` | string | yes | Repository owner |
| `repo` | string | yes | Repository name |
| `tag_name` | string | yes | e.g. `v1.0.0` |
| `name` | string | no | Release title |
| `body` | string | no | Release notes (Markdown) |
| `target_commitish` | string | no | Branch or commit SHA to tag; defaults to the default branch |
| `draft` | boolean | no | Defaults to `false` |
| `prerelease` | boolean | no | Defaults to `false` |
Returns `{ html_url, id, tag_name }`.
### `delete_repository`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `owner` | string | yes | Repository owner |
| `repo` | string | yes | Repository name |
Permanently deletes the repository. Returns `{ deleted: true, full_name }`.
Requires a classic PAT with the `delete_repo` scope — fine-grained tokens
generally cannot delete repositories.
### `github_request`
| Param | Type | Required | Notes |
| --- | --- | --- | --- |
| `path` | string | yes | API path beginning with `/`, query string included |
| `method` | `GET` \| `POST` \| `PATCH` \| `PUT` \| `DELETE` | no | Defaults to `GET` |
| `body` | object | no | JSON body for `POST` / `PATCH` / `PUT` |
An escape hatch for anything the dedicated tools don't cover: code scanning and
secret scanning alerts, deployments, commit statuses, repository invitations,
issues, pull requests, orgs, gists. `path` is everything after
`https://api.github.com`, e.g. `/repos/OWNER/REPO/code-scanning/alerts`.
Returns the parsed JSON response.
One generic tool rather than a wrapper per endpoint means the server can't fall
behind your token — a scope you add later is reachable with no code change.
> **Warning:** this carries the token's full authority, including `DELETE` on
> any resource the token can reach. It is annotated `destructiveHint: true`.
> If you want the reach without the risk, narrow the `method` enum in
> `index.js` to `GET` and it becomes a read-only window over every scope.
Paths are validated to begin with a single `/`, so absolute URLs
(`https://…`) and protocol-relative paths (`//…`) are rejected — without that,
a crafted path would send your bearer token to another host. `test.js` covers
this; run it with `node test.js`.
## Installation
### Prerequisites
- [Node.js](https://nodejs.org/) 18+ (for native `fetch` support)
- A GitHub [Personal Access Token](https://github.com/settings/tokens) with
the `repo` scope (add `delete_repo` too if you want to use
`delete_repository`)
> **Note:** Repository *creation* isn't reliably supported by fine-grained
> PATs, so a **classic** token is recommended.
### Clone and install dependencies
```bash
git clone https://github.com/MatinMHF/github-mcp-lite.git
cd github-mcp-lite
npm install
```
### Configure your token
The server reads the token from either `GITHUB_PERSONAL_ACCESS_TOKEN` or
`GITHUB_TOKEN` in its environment. Set one of these before it starts — the
process exits immediately with an error if neither is present.
## Usage
### Run directly
```bash
GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx node index.js
```
The server communicates over stdio, so it's meant to be launched by an MCP
client (Claude Code, Claude Desktop, etc.), not run interactively on its own.
### Register with Claude Code
```bash
claude mcp add github-lite -s user -e GITHUB_PERSONAL_ACCESS_TOKEN=<your_token> -- node /path/to/index.js
```
On Windows PowerShell, quote the separator as `"--"` — PowerShell silently
strips a bare `--` before it reaches the script's arguments, which breaks
argument parsing:
```powershell
claude mcp add github-lite -s user -e GITHUB_PERSONAL_ACCESS_TOKEN=<your_token> "--" node "C:/path/to/index.js"
```
### Register with Claude Desktop (or any MCP-compatible client)
Add an entry to your client's MCP server config (e.g.
`claude_desktop_config.json`):
```json
{
"mcpServers": {
"github-lite": {
"command": "node",
"args": ["C:/path/to/github-mcp-lite/index.js"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxx"
}
}
}
}
```
### Example workflow
A typical session once the server is registered with your MCP client:
1. **"Who am I authenticated as?"** → calls `get_authenticated_user`
2. **"Create a public repo called `my-project`"** → calls `create_repository`
(auto-initializes with a README so a default branch exists)
3. **"Commit these files to it"** → calls `commit_files` with an array of
`{ path, content }` entries, written in a single commit
4. **"Publish a v1.0.0 release"** → calls `create_release`
## Security notes
- The token is read once from the environment at startup and never logged.
- `commit_files`, `commit_directory`, `delete_repository` and `github_request`
are marked `destructiveHint: true` in their tool annotations — well-behaved
MCP clients should prompt for confirmation before invoking them.
- `github_request` is the widest of these: it can reach any endpoint the token
is allowed to reach, with any verb. On a classic PAT the `repo` scope also
grants its children — `repo:status`, `repo_deployment`, `public_repo`,
`repo:invite` and `security_events` — which cannot be unticked individually.
That last one means read *and write* access to code scanning and secret
scanning alerts across every repo you own. A fine-grained token scoped to
Contents, Administration and Metadata avoids it, at the cost of repository
creation being unreliable.
- Because this server has `openWorldHint: true` on every tool (it makes
real network calls to `api.github.com`), only run it with a token scoped
to what you actually need.
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. This project follows a
[Code of Conduct](CODE_OF_CONDUCT.md). To report a security issue, see
[SECURITY.md](SECURITY.md).
## License
MIT
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues