From 634bf3353f8d2fe48b9cebfda6efc933505c1b94 Mon Sep 17 00:00:00 2001 From: agent Date: Tue, 24 Mar 2026 09:23:22 -0400 Subject: [PATCH] Add create_repo tool to MCP server Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 1 + lib/gitea.ts | 25 +++++++++++++++++++++++++ server.ts | 16 ++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 405df36..57d2cad 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -36,6 +36,7 @@ The scripts in this repo should collectively cover: | Tool | Description | |---|---| +| `create_repo` | Create a new repository | | `list_repos` | List repos accessible to the agent account | | `view_repo` | File list, default branch, README excerpt | | `view_file` | Raw content of a file at a given branch/path | diff --git a/lib/gitea.ts b/lib/gitea.ts index a1aae3f..472f879 100644 --- a/lib/gitea.ts +++ b/lib/gitea.ts @@ -38,6 +38,31 @@ export interface PRDetails { isMerged: boolean; } +// Create a new repository +export async function createRepo( + { page }: Session, + name: string, + description = '', + isPrivate = false, +): Promise<{ url: string; cloneUrl: string }> { + await page.goto(`${GITEA_URL}/repo/create`, { waitUntil: 'load' }); + + await page.fill('input[name="repo_name"]', name); + if (description) await page.fill('textarea[name="description"]', description); + if (isPrivate) await page.check('input[name="private"]'); + + await Promise.all([ + page.waitForNavigation({ waitUntil: 'load' }), + page.getByRole('button', { name: 'Create Repository' }).click(), + ]); + + const cloneUrl = + (await page.locator('#repo-clone-https, input#clone-url').inputValue().catch(() => '')) || + `${GITEA_URL}/${USERNAME}/${name}.git`; + + return { url: page.url(), cloneUrl }; +} + // List repos visible to the logged-in user export async function listRepos({ page }: Session): Promise { await page.goto(`${GITEA_URL}/${USERNAME}`, { waitUntil: 'load' }); diff --git a/server.ts b/server.ts index b0c48ad..c64ee8f 100644 --- a/server.ts +++ b/server.ts @@ -19,6 +19,19 @@ async function withSession(fn: (session: Session) => Promise): Promise } const TOOLS = [ + { + name: 'create_repo', + description: 'Create a new repository on Gitea.', + inputSchema: { + type: 'object', + required: ['name'], + properties: { + name: { type: 'string', description: 'Repository name' }, + description: { type: 'string', description: 'Optional description' }, + private: { type: 'boolean', description: 'Make the repo private (default: false)' }, + }, + }, + }, { name: 'list_repos', description: 'List repositories accessible to the Gitea agent account.', @@ -144,6 +157,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { let result: unknown; switch (name) { + case 'create_repo': + result = await withSession(s => gitea.createRepo(s, a.name as string, a.description as string | undefined, a.private as boolean | undefined)); + break; case 'list_repos': result = await withSession(s => gitea.listRepos(s)); break;