Files
gitea-mirror/.github/workflows/mcp.yml
T
ARUNAVO RAYandGitHub 1b60a0334a feat: read only API keys and an MCP endpoint over the REST API (#478)
- Read only API keys: an Access choice when creating a key, stored in
  the key metadata; the middleware refuses writes and most auth calls
  for them with 403 API_KEY_READ_ONLY.
- POST /api/mcp: stateless MCP endpoint with 8 tools that call the REST
  API over loopback with the caller's key. Needs x-api-key, a cookie
  alone gets 401. MCP_INTERNAL_URL overrides the loopback address.
- mcp/: gitea-mirror-mcp, a zero dependency stdio bridge to /api/mcp for
  Claude Desktop and Cursor, with a CI workflow that publishes it on an
  mcp-v* tag.
- e2e spec for keys, the endpoint and the bridge; docs for the endpoint,
  the bridge and read only keys.
2026-10-26 16:07:18 +05:30

121 lines
3.3 KiB
YAML

name: MCP Server
# Tests the stdio bridge in mcp/ and publishes it to npm as gitea-mirror-mcp
# when an mcp-v* tag is pushed. Path filters do not apply to tag pushes. The
# tools themselves live in the app (src/lib/mcp) and are tested with it.
on:
push:
branches: ['**']
tags: ['mcp-v*']
paths:
- 'mcp/**'
- '.github/workflows/mcp.yml'
pull_request:
branches: ['**']
paths:
- 'mcp/**'
- '.github/workflows/mcp.yml'
permissions:
contents: read
env:
BUN_VERSION: '1.3.13'
NODE_VERSION: '22'
jobs:
test:
name: Test MCP bridge
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: mcp
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install dependencies
run: npm ci
- name: Typecheck
run: npm run typecheck
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Smoke test the build on Node
# No app runs here, so the bridge must answer the request itself
# with a clear error instead of hanging or crashing.
run: |
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"ci","version":"0"}}}' \
| GITEA_MIRROR_URL=http://127.0.0.1:9 GITEA_MIRROR_API_KEY=gm_smoke timeout 20 node dist/index.js > smoke.jsonl
node -e '
const lines = require("fs").readFileSync("smoke.jsonl", "utf8").trim().split("\n").map(JSON.parse);
const answer = lines.find((m) => m.id === 1);
if (!answer?.error?.message?.includes("Could not reach Gitea Mirror")) { console.error(lines); process.exit(1); }
console.log(answer.error.message);
'
- name: Show package contents
run: npm pack --dry-run
publish:
name: Publish to npm
needs: test
if: startsWith(github.ref, 'refs/tags/mcp-v')
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
id-token: write
defaults:
run:
working-directory: mcp
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
- name: Check the tag matches the package version
run: |
version=$(node -p 'require("./package.json").version')
if [ "mcp-v${version}" != "${GITHUB_REF_NAME}" ]; then
echo "::error::Tag ${GITHUB_REF_NAME} does not match mcp/package.json version ${version}"
exit 1
fi
- name: Install dependencies
run: npm ci
- name: Publish
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}