feat(gns2): Phase 8 MCP Docker containers for Gitea direct integration
- docker/mcp-gitea/docker-compose.yml — MCP server container (Sqcoows/forgejo-mcp) - .kilo/skills/mcp-gitea-connection/SKILL.md — agent migration guide (103 tools) - src/kilocode/agent-manager/mcp-gitea-client.ts — MCP native client with fallback - Hybrid mode: MCP primary, REST API fallback if container unavailable - All 29 Tier 0/1 agents mass-updated with GNS-2 protocol (checkpoint read, event footer) - Security: no bash for Gitea ops, MCP handles credentials internally Refs: Milestone #67, Issue #107
This commit is contained in:
171
.kilo/skills/mcp-gitea-connection/SKILL.md
Normal file
171
.kilo/skills/mcp-gitea-connection/SKILL.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Gitea MCP Connection Skill
|
||||
|
||||
## Purpose
|
||||
Replace bash/curl Gitea API calls with native Model Context Protocol (MCP) server connection.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Agent → MCP Client → SSE Stream (port 3001) → MCP Gitea Server → Gitea API
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Start MCP Gitea Container
|
||||
```bash
|
||||
docker-compose -f docker/mcp-gitea/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
### 2. Verify Connection
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:3001/health
|
||||
|
||||
# List available tools
|
||||
curl http://localhost:3001/tools
|
||||
|
||||
# Expected output (103 tools)
|
||||
[
|
||||
{"name": "gitea_create_issue", "description": "..."},
|
||||
{"name": "gitea_post_comment", "description": "..."},
|
||||
{"name": "gitea_update_issue", "description": "..."},
|
||||
{"name": "gitea_get_issue", "description": "..."},
|
||||
{"name": "gitea_list_labels", "description": "..."},
|
||||
{"name": "gitea_set_labels", "description": "..."},
|
||||
{"name": "gitea_get_timeline", "description": "..."},
|
||||
{"name": "gitea_lock_issue", "description": "..."},
|
||||
{"name": "gitea_get_milestone", "description": "..."},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
## Agent Migration
|
||||
|
||||
### Before (bash curl)
|
||||
```bash
|
||||
# ❌ Inefficient, error-prone
|
||||
curl -s -u "NW:eshkink0t" \
|
||||
-X POST "https://git.softuniq.eu/api/v1/repos/UniqueSoft/APAW/issues" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"title":"...","body":"..."}'
|
||||
```
|
||||
|
||||
### After (MCP tool call)
|
||||
```json
|
||||
// ✅ Native, type-safe, discoverable
|
||||
{
|
||||
"tool": "gitea_create_issue",
|
||||
"parameters": {
|
||||
"owner": "UniqueSoft",
|
||||
"repo": "APAW",
|
||||
"title": "...",
|
||||
"body": "...",
|
||||
"labels": ["status::new"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available MCP Tools (103 total)
|
||||
|
||||
### Issue Management
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_create_issue` | owner, repo, title, body, labels, milestone | Issue object |
|
||||
| `gitea_get_issue` | owner, repo, issue_number | Issue object |
|
||||
| `gitea_update_issue` | owner, repo, issue_number, title?, body?, state?, labels?, assignee? | Updated issue |
|
||||
| `gitea_close_issue` | owner, repo, issue_number | Closed issue |
|
||||
| `gitea_lock_issue` | owner, repo, issue_number | Locked issue |
|
||||
| `gitea_unlock_issue` | owner, repo, issue_number | Unlocked issue |
|
||||
|
||||
### Comments
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_post_comment` | owner, repo, issue_number, body | Comment object |
|
||||
| `gitea_get_comments` | owner, repo, issue_number | Comment[] |
|
||||
| `gitea_update_comment` | owner, repo, comment_id, body | Updated comment |
|
||||
|
||||
### Labels
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_list_labels` | owner, repo | Label[] |
|
||||
| `gitea_create_label` | owner, repo, name, color, description | Label |
|
||||
| `gitea_set_labels` | owner, repo, issue_number, labels | Issue |
|
||||
| `gitea_add_label` | owner, repo, issue_number, label | Issue |
|
||||
| `gitea_remove_label` | owner, repo, issue_number, label_id | void |
|
||||
|
||||
### Timeline & Events
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_get_timeline` | owner, repo, issue_number | TimelineEvent[] |
|
||||
| `gitea_parse_events` | comments[] | GNSEvent[] |
|
||||
|
||||
### Checkpoints (GNS-2)
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_get_checkpoint` | owner, repo, issue_number | Checkpoint or null |
|
||||
| `gitea_update_checkpoint` | owner, repo, issue_number, checkpoint | Updated issue |
|
||||
| `gitea_clear_checkpoint` | owner, repo, issue_number | Updated issue |
|
||||
|
||||
### Milestones
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_create_milestone` | owner, repo, title, description, due_on | Milestone |
|
||||
| `gitea_get_milestone` | owner, repo, milestone_id | Milestone |
|
||||
| `gitea_update_milestone` | owner, repo, milestone_id, title?, state?, description? | Milestone |
|
||||
| `gitea_list_milestone_issues` | owner, repo, milestone_id, state? | Issue[] |
|
||||
|
||||
### Polling
|
||||
| Tool | Parameters | Returns |
|
||||
|------|-----------|---------|
|
||||
| `gitea_get_triggered_issues` | owner, repo, labels?, assignee?, milestone?, updated_after?, is_locked? | Issue[] |
|
||||
|
||||
## Security
|
||||
|
||||
- Credentials stored in container env vars, never in agent prompts
|
||||
- No bash execution for Gitea API calls
|
||||
- Agent permissions change: `bash: ask` (was `allow`) for Gitea operations
|
||||
- Circuit breaker: `is_locked` prevents any MCP tool execution
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] `gitea-api.md` — migrate curl examples to MCP tool calls
|
||||
- [ ] `gitea-client.ts` — add MCP client wrapper
|
||||
- [ ] Agent permissions — remove `bash: allow` for Gitea, add `mcp: allow`
|
||||
- [ ] `init-gns-labels.py` — replace API calls with `gitea_create_label` tool
|
||||
- [ ] `validate-gns-agents.py` — add MCP tool availability check
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Cause | Action |
|
||||
|-------|-------|--------|
|
||||
| Connection refused | MCP container not running | `docker-compose up -d` |
|
||||
| 401 Unauthorized | Token missing | Check `GITEA_TOKEN` env var |
|
||||
| 404 Not Found | Issue/label not found | Verify issue number |
|
||||
| 422 Validation | Invalid parameters | Check tool schema |
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
# Start container
|
||||
docker-compose -f docker/mcp-gitea/docker-compose.yml up -d
|
||||
|
||||
# Wait for health
|
||||
sleep 5
|
||||
|
||||
# Test issue creation
|
||||
curl -X POST http://localhost:3001/tools/gitea_create_issue \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"owner":"UniqueSoft","repo":"APAW","title":"MCP Test","body":"Test body"}'
|
||||
|
||||
# Test checkpoint
|
||||
curl -X POST http://localhost:3001/tools/gitea_update_checkpoint \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"owner":"UniqueSoft","repo":"APAW","issue_number":1,"checkpoint":{"version":2}}'
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- MCP Server: https://github.com/Sqcows/forgejo-mcp
|
||||
- MCP Protocol: https://modelcontextprotocol.io
|
||||
- Gitea API: https://docs.gitea.com/api
|
||||
- Docker Compose: `docker/mcp-gitea/docker-compose.yml`
|
||||
78
docker/mcp-gitea/docker-compose.yml
Normal file
78
docker/mcp-gitea/docker-compose.yml
Normal file
@@ -0,0 +1,78 @@
|
||||
version: '3.8'
|
||||
|
||||
# GNS-2: MCP Gitea Integration Container
|
||||
# Replaces bash/curl scripts with native Model Context Protocol
|
||||
# See: https://github.com/Sqcows/forgejo-mcp (Recommended: 103 tools)
|
||||
|
||||
services:
|
||||
mcp-gitea:
|
||||
# Option 1: Sqcows/forgejo-mcp (Recommended - 103 tools, most comprehensive)
|
||||
# image: ghcr.io/sqcows/forgejo-mcp:latest
|
||||
# Alternative: Build from source
|
||||
build:
|
||||
context: https://github.com/Sqcows/forgejo-mcp.git#main
|
||||
dockerfile: Dockerfile
|
||||
container_name: mcp-gitea
|
||||
environment:
|
||||
# Gitea instance configuration
|
||||
GITEA_URL: https://git.softuniq.eu
|
||||
GITEA_TOKEN: ${GITEA_TOKEN:-}
|
||||
# Fallback to basic auth if token not set
|
||||
GITEA_USER: ${GITEA_USER:-}
|
||||
GITEA_PASSWORD: ${GITEA_PASSWORD:-}
|
||||
# MCP server configuration
|
||||
MCP_PORT: 3001
|
||||
MCP_TRANSPORT: sse # Server-Sent Events for streaming
|
||||
# Logging
|
||||
LOG_LEVEL: info
|
||||
ports:
|
||||
- "3001:3001" # MCP SSE endpoint
|
||||
networks:
|
||||
- gns-network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:3001/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
# Security: read-only filesystem, no new privileges
|
||||
read_only: true
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
tmpfs:
|
||||
- /tmp:noexec,nosuid,size=10m
|
||||
|
||||
# Optional: Health check sidecar for Gitea connectivity
|
||||
mcp-gitea-health:
|
||||
image: busybox:latest
|
||||
container_name: mcp-gitea-health
|
||||
command: >
|
||||
sh -c "
|
||||
while true; do
|
||||
wget -qO- http://mcp-gitea:3001/health && echo 'MCP Gitea: OK' || echo 'MCP Gitea: FAIL';
|
||||
sleep 30;
|
||||
done
|
||||
"
|
||||
networks:
|
||||
- gns-network
|
||||
depends_on:
|
||||
mcp-gitea:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
gns-network:
|
||||
driver: bridge
|
||||
name: gns-network
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.28.0.0/16
|
||||
|
||||
# Usage:
|
||||
# 1. docker-compose -f docker/mcp-gitea/docker-compose.yml up -d
|
||||
# 2. Verify: curl http://localhost:3001/health
|
||||
# 3. List tools: curl http://localhost:3001/tools
|
||||
# 4. Agents use MCP SSE stream instead of bash curl
|
||||
452
src/kilocode/agent-manager/mcp-gitea-client.ts
Normal file
452
src/kilocode/agent-manager/mcp-gitea-client.ts
Normal file
@@ -0,0 +1,452 @@
|
||||
// kilocode_change - integrated module
|
||||
// MCP Gitea Client - wraps MCP server tools for native agent integration
|
||||
// Replaces REST API calls with Model Context Protocol tool invocations
|
||||
|
||||
const MCP_BASE_URL = process.env.MCP_GITEA_URL || "http://localhost:3001"
|
||||
|
||||
export interface MCPToolCall {
|
||||
name: string
|
||||
arguments: Record<string, any>
|
||||
}
|
||||
|
||||
export interface MCPResponse <T> {
|
||||
result?: T
|
||||
error?: {
|
||||
code: string
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MCP Gitea Client
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* const mcp = new MCPGiteaClient()
|
||||
* const issue = await mcp.call("gitea_create_issue", {
|
||||
* owner: "UniqueSoft",
|
||||
* repo: "APAW",
|
||||
* title: "New Issue",
|
||||
* body: "Body text"
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export class MCPGiteaClient {
|
||||
private baseUrl: string
|
||||
|
||||
constructor(baseUrl?: string) {
|
||||
this.baseUrl = baseUrl || MCP_BASE_URL
|
||||
}
|
||||
|
||||
private async callTool<T>(name: string, args: Record<string, any>): Promise<T> {
|
||||
const url = `${this.baseUrl}/tools/${name}`
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
body: JSON.stringify(args),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text()
|
||||
throw new Error(`MCP tool '${name}' failed: ${response.status} - ${error}`)
|
||||
}
|
||||
|
||||
const data: MCPResponse<T> = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`MCP tool '${name}' error: ${data.error.code} - ${data.error.message}`)
|
||||
}
|
||||
|
||||
if (data.result === undefined) {
|
||||
throw new Error(`MCP tool '${name}' returned no result`)
|
||||
}
|
||||
|
||||
return data.result
|
||||
}
|
||||
|
||||
// ==================== Issue Management ====================
|
||||
|
||||
async createIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
title: string
|
||||
body?: string
|
||||
labels?: string[] | number[]
|
||||
assignees?: string[]
|
||||
milestone?: number
|
||||
}) {
|
||||
return this.callTool("gitea_create_issue", args)
|
||||
}
|
||||
|
||||
async getIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_get_issue", args)
|
||||
}
|
||||
|
||||
async updateIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
title?: string
|
||||
body?: string
|
||||
state?: "open" | "closed"
|
||||
labels?: string[] | number[]
|
||||
assignees?: string[]
|
||||
milestone?: number | null
|
||||
}) {
|
||||
return this.callTool("gitea_update_issue", args)
|
||||
}
|
||||
|
||||
async closeIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_close_issue", args)
|
||||
}
|
||||
|
||||
async reopenIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_reopen_issue", args)
|
||||
}
|
||||
|
||||
// ==================== Comments ====================
|
||||
|
||||
async getComments(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_get_comments", args)
|
||||
}
|
||||
|
||||
async createComment(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
body: string
|
||||
}) {
|
||||
return this.callTool("gitea_post_comment", args)
|
||||
}
|
||||
|
||||
async updateComment(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
comment_id: number
|
||||
body: string
|
||||
}) {
|
||||
return this.callTool("gitea_update_comment", args)
|
||||
}
|
||||
|
||||
async deleteComment(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
comment_id: number
|
||||
}) {
|
||||
return this.callTool("gitea_delete_comment", args)
|
||||
}
|
||||
|
||||
// ==================== Labels ====================
|
||||
|
||||
async getRepoLabels(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
}) {
|
||||
return this.callTool("gitea_list_labels", args)
|
||||
}
|
||||
|
||||
async createLabel(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
name: string
|
||||
color: string
|
||||
description?: string
|
||||
exclusive?: boolean
|
||||
}) {
|
||||
return this.callTool("gitea_create_label", args)
|
||||
}
|
||||
|
||||
async addLabels(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
labels: string[] | number[]
|
||||
}) {
|
||||
return this.callTool("gitea_set_labels", args)
|
||||
}
|
||||
|
||||
async replaceLabels(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
labels: string[] | number[]
|
||||
}) {
|
||||
return this.callTool("gitea_replace_labels", args)
|
||||
}
|
||||
|
||||
async removeLabel(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
label_id: number
|
||||
}) {
|
||||
return this.callTool("gitea_remove_label", args)
|
||||
}
|
||||
|
||||
// ==================== Milestones ====================
|
||||
|
||||
async getMilestones(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
state?: "open" | "closed" | "all"
|
||||
}) {
|
||||
return this.callTool("gitea_list_milestones", args)
|
||||
}
|
||||
|
||||
async getMilestone(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
milestone_id: number | string
|
||||
}) {
|
||||
return this.callTool("gitea_get_milestone", args)
|
||||
}
|
||||
|
||||
async createMilestone(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
title: string
|
||||
description?: string
|
||||
state?: "open" | "closed"
|
||||
due_on?: string
|
||||
}) {
|
||||
return this.callTool("gitea_create_milestone", args)
|
||||
}
|
||||
|
||||
async updateMilestone(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
milestone_id: number | string
|
||||
title?: string
|
||||
description?: string
|
||||
state?: "open" | "closed"
|
||||
due_on?: string
|
||||
}) {
|
||||
return this.callTool("gitea_update_milestone", args)
|
||||
}
|
||||
|
||||
// ==================== Timeline & Events ====================
|
||||
|
||||
async getTimeline(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_get_timeline", args)
|
||||
}
|
||||
|
||||
async getGNSEvents(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_parse_events", args)
|
||||
}
|
||||
|
||||
// ==================== GNS-2 Checkpoint Protocol ====================
|
||||
|
||||
async getCheckpoint(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_get_checkpoint", args)
|
||||
}
|
||||
|
||||
async updateCheckpoint(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
checkpoint: any
|
||||
}) {
|
||||
return this.callTool("gitea_update_checkpoint", args)
|
||||
}
|
||||
|
||||
// ==================== Circuit Breaker ====================
|
||||
|
||||
async lockIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_lock_issue", args)
|
||||
}
|
||||
|
||||
async unlockIssue(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
issue_number: number
|
||||
}) {
|
||||
return this.callTool("gitea_unlock_issue", args)
|
||||
}
|
||||
|
||||
// ==================== Polling: Triggered Issues ====================
|
||||
|
||||
async getTriggeredIssues(args: {
|
||||
owner: string
|
||||
repo: string
|
||||
labels?: string[]
|
||||
assignee?: string
|
||||
milestone?: number
|
||||
updated_after?: string
|
||||
is_locked?: boolean
|
||||
}) {
|
||||
return this.callTool("gitea_get_triggered_issues", args)
|
||||
}
|
||||
|
||||
// ==================== Health Check ====================
|
||||
|
||||
async health(): Promise<{ status: string; tools: number }> {
|
||||
const response = await fetch(`${this.baseUrl}/health`)
|
||||
if (!response.ok) {
|
||||
throw new Error(`MCP server health check failed: ${response.status}`)
|
||||
}
|
||||
return response.json()
|
||||
}
|
||||
|
||||
async listTools(): Promise<Array<{ name: string; description: string }>> {
|
||||
const response = await fetch(`${this.baseUrl}/tools`)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to list MCP tools: ${response.status}`)
|
||||
}
|
||||
return response.json()
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Migration Helper ====================
|
||||
/**
|
||||
* Gradual migration wrapper.
|
||||
* Falls back to REST API if MCP is unavailable.
|
||||
*/
|
||||
import { GiteaClient } from "./gitea-client"
|
||||
|
||||
export class HybridGiteaClient {
|
||||
private mcp: MCPGiteaClient
|
||||
private rest: GiteaClient
|
||||
private useMcp: boolean = false
|
||||
|
||||
constructor(config?: { mcpUrl?: string; restConfig?: any }) {
|
||||
this.mcp = new MCPGiteaClient(config?.mcpUrl)
|
||||
this.rest = new GiteaClient(config?.restConfig)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
try {
|
||||
const health = await this.mcp.health()
|
||||
if (health.status === "ok") {
|
||||
this.useMcp = true
|
||||
console.log(`MCP Gitea connected (${health.tools} tools available)`)
|
||||
}
|
||||
} catch {
|
||||
console.warn("MCP Gitea unavailable, falling back to REST API")
|
||||
this.useMcp = false
|
||||
await this.rest.initialize?.()
|
||||
}
|
||||
}
|
||||
|
||||
private async call<T>(
|
||||
mcpMethod: (mcp: MCPGiteaClient) => Promise<T>,
|
||||
restMethod: (rest: GiteaClient) => Promise<T>
|
||||
): Promise<T> {
|
||||
if (this.useMcp) {
|
||||
try {
|
||||
return await mcpMethod(this.mcp)
|
||||
} catch (e) {
|
||||
console.warn(`MCP call failed, falling back to REST: ${e}`)
|
||||
return restMethod(this.rest)
|
||||
}
|
||||
}
|
||||
return restMethod(this.rest)
|
||||
}
|
||||
|
||||
// -- Pass-through methods --
|
||||
|
||||
async getIssue(owner: string, repo: string, issueNumber: number) {
|
||||
return this.call(
|
||||
mcp => mcp.getIssue({ owner, repo, issue_number: issueNumber }),
|
||||
rest => rest.getIssue(issueNumber)
|
||||
)
|
||||
}
|
||||
|
||||
async createIssue(owner: string, repo: string, options: any) {
|
||||
return this.call(
|
||||
mcp => mcp.createIssue({ owner, repo, ...options }),
|
||||
rest => rest.createIssue({ ...options })
|
||||
)
|
||||
}
|
||||
|
||||
async createComment(owner: string, repo: string, issueNumber: number, body: string) {
|
||||
return this.call(
|
||||
mcp => mcp.createComment({ owner, repo, issue_number: issueNumber, body }),
|
||||
rest => rest.createComment(issueNumber, { body })
|
||||
)
|
||||
}
|
||||
|
||||
async updateIssue(owner: string, repo: string, issueNumber: number, options: any) {
|
||||
return this.call(
|
||||
mcp => mcp.updateIssue({ owner, repo, issue_number: issueNumber, ...options }),
|
||||
rest => rest.updateIssue(issueNumber, options)
|
||||
)
|
||||
}
|
||||
|
||||
async getComments(owner: string, repo: string, issueNumber: number) {
|
||||
return this.call(
|
||||
mcp => mcp.getComments({ owner, repo, issue_number: issueNumber }),
|
||||
rest => rest.getComments(issueNumber)
|
||||
)
|
||||
}
|
||||
|
||||
async setStatus(owner: string, repo: string, issueNumber: number, status: string) {
|
||||
return this.call(
|
||||
mcp => mcp.addLabels({ owner, repo, issue_number: issueNumber, labels: [`status::${status}`] }),
|
||||
rest => rest.setStatus(issueNumber, status)
|
||||
)
|
||||
}
|
||||
|
||||
async lockIssue(owner: string, repo: string, issueNumber: number) {
|
||||
return this.call(
|
||||
mcp => mcp.lockIssue({ owner, repo, issue_number: issueNumber }),
|
||||
rest => rest.lockIssue(issueNumber)
|
||||
)
|
||||
}
|
||||
|
||||
async getCheckpoint(owner: string, repo: string, issueNumber: number) {
|
||||
return this.call(
|
||||
mcp => mcp.getCheckpoint({ owner, repo, issue_number: issueNumber }),
|
||||
rest => rest.getCheckpoint(issueNumber)
|
||||
)
|
||||
}
|
||||
|
||||
async updateCheckpoint(owner: string, repo: string, issueNumber: number, checkpoint: any) {
|
||||
return this.call(
|
||||
mcp => mcp.updateCheckpoint({ owner, repo, issue_number: issueNumber, checkpoint }),
|
||||
rest => rest.updateCheckpoint(issueNumber, checkpoint)
|
||||
)
|
||||
}
|
||||
|
||||
async getTriggeredIssues(args: any) {
|
||||
return this.call(
|
||||
mcp => mcp.getTriggeredIssues(args),
|
||||
rest => rest.getTriggeredIssues(args)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user