TypeScript/Playwright MCP server exposing 9 Gitea tools (list_repos, view_repo, view_file, list/create/view/comment/merge/close PR). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
import { chromium, type Browser, type Page } from 'playwright';
|
|
import 'dotenv/config';
|
|
|
|
function requireEnv(key: string): string {
|
|
const value = process.env[key];
|
|
if (!value) throw new Error(`Missing required env var: ${key}`);
|
|
return value;
|
|
}
|
|
|
|
export const GITEA_URL = requireEnv('GITEA_URL');
|
|
export const USERNAME = requireEnv('GITEA_USERNAME');
|
|
const PASSWORD = requireEnv('GITEA_PASSWORD');
|
|
|
|
export interface Session {
|
|
browser: Browser;
|
|
page: Page;
|
|
}
|
|
|
|
export async function createSession(): Promise<Session> {
|
|
const browser = await chromium.launch({ headless: true });
|
|
const page = await browser.newPage();
|
|
|
|
await page.goto(`${GITEA_URL}/user/login`, { waitUntil: 'load' });
|
|
await page.fill('input[name="user_name"]', USERNAME);
|
|
await page.fill('input[name="password"]', PASSWORD);
|
|
|
|
await Promise.all([
|
|
page.waitForNavigation({ waitUntil: 'load' }),
|
|
page.getByRole('button', { name: /sign in/i }).click(),
|
|
]);
|
|
|
|
if (page.url().includes('change_password')) {
|
|
await page.fill('input[name="password"]', PASSWORD);
|
|
await page.fill('input[name="retype"]', PASSWORD);
|
|
await Promise.all([
|
|
page.waitForNavigation({ waitUntil: 'load' }),
|
|
page.getByRole('button', { name: /update password/i }).click(),
|
|
]);
|
|
}
|
|
|
|
const loggedIn = (await page.$('.avatar')) !== null;
|
|
if (!loggedIn) {
|
|
await browser.close();
|
|
throw new Error('Login failed');
|
|
}
|
|
|
|
return { browser, page };
|
|
}
|