Skip to main content
Glama

gitea_workflow_sync_status

Sync issue status labels with project board columns in Gitea repositories. Choose label-to-board, board-to-label, or bidirectional synchronization to maintain workflow consistency.

Instructions

Synchronize issue status labels with project board column positions. Supports label-to-board, board-to-label, or bidirectional sync.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ownerNoRepository owner. Uses context if not provided
repoNoRepository name. Uses context if not provided
directionYesSync direction

Implementation Reference

  • The core handler function implementing the 'gitea_workflow_sync_status' tool. Loads workflow config, initializes BoardSyncManager, fetches projects and issues, calculates sync actions based on direction (label-to-board, board-to-label, or both), and returns sync report.
    export async function workflowSyncStatus(
      ctx: WorkflowToolsContext,
      args: {
        owner?: string;
        repo?: string;
        config?: WorkflowConfig;
        direction: 'label-to-board' | 'board-to-label' | 'both';
      }
    ): Promise<{
      success: boolean;
      synced_count: number;
      actions: SyncAction[];
      error?: string;
    }> {
      logger.debug({ args: { ...args, config: args.config ? '[provided]' : undefined } }, 'Syncing status');
    
      const { owner, repo } = ctx.contextManager.resolveOwnerRepo(args.owner, args.repo);
    
      // 获取配置
      let config = args.config;
      if (!config) {
        const loadResult = await workflowLoadConfig(ctx, { owner, repo });
        if (!loadResult.success || !loadResult.config) {
          return {
            success: false,
            synced_count: 0,
            actions: [],
            error: loadResult.error || '无法加载配置',
          };
        }
        config = loadResult.config;
      }
    
      const boardSyncManager = new BoardSyncManager(config);
      const actions: SyncAction[] = [];
    
      try {
        // 获取项目和列
        const projects = await ctx.client.get<Array<{ id: number; title: string }>>(
          `/repos/${owner}/${repo}/projects`
        );
    
        const workflowProject = projects.find((p) => p.title === config.board.name);
        if (!workflowProject) {
          return {
            success: false,
            synced_count: 0,
            actions: [],
            error: `未找到项目看板: ${config.board.name}`,
          };
        }
    
        // 获取开放的 Issue
        const issues = await ctx.client.get<Issue[]>(`/repos/${owner}/${repo}/issues?state=open&limit=100`);
    
        // 计算同步操作(这里只返回建议的操作,实际同步需要更复杂的实现)
        for (const issue of issues) {
          const syncActions = boardSyncManager.calculateSyncActions(issue, null, args.direction);
          actions.push(...syncActions);
        }
    
        logger.info({ owner, repo, actions: actions.length }, 'Status sync calculated');
    
        return {
          success: true,
          synced_count: actions.length,
          actions,
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        logger.error({ owner, repo, error: errorMessage }, 'Failed to sync status');
    
        return {
          success: false,
          synced_count: 0,
          actions: [],
          error: errorMessage,
        };
      }
    }
  • Registers the 'gitea_workflow_sync_status' tool with the MCP server, defining its title, description, input schema, and wiring the handler from WorkflowTools.
    mcpServer.registerTool(
      'gitea_workflow_sync_status',
      {
        title: '状态双向同步',
        description:
          'Synchronize issue status labels with project board column positions. Supports label-to-board, board-to-label, or bidirectional sync.',
        inputSchema: z.object({
          owner: z.string().optional().describe('Repository owner. Uses context if not provided'),
          repo: z.string().optional().describe('Repository name. Uses context if not provided'),
          direction: z
            .enum(['label-to-board', 'board-to-label', 'both'])
            .describe('Sync direction'),
        }),
      },
      async (args) => {
        try {
          const result = await WorkflowTools.workflowSyncStatus(
            { client: ctx.client, contextManager: ctx.contextManager },
            args
          );
          return {
            content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
            isError: !result.success,
          };
        } catch (error: unknown) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          return {
            content: [{ type: 'text' as const, text: `Error: ${errorMessage}` }],
            isError: true,
          };
        }
      }
    );
  • Zod input schema validation for the tool, defining parameters: owner (optional string), repo (optional string), direction (enum: 'label-to-board', 'board-to-label', 'both').
    inputSchema: z.object({
      owner: z.string().optional().describe('Repository owner. Uses context if not provided'),
      repo: z.string().optional().describe('Repository name. Uses context if not provided'),
      direction: z
        .enum(['label-to-board', 'board-to-label', 'both'])
        .describe('Sync direction'),
    }),
  • Key helper method in BoardSyncManager used by the handler to compute synchronization actions between issue labels and board columns based on the specified direction.
    calculateSyncActions(
      issue: Issue,
      currentColumn: ProjectColumn | null,
      direction: SyncDirection
    ): SyncAction[] {
      const actions: SyncAction[] = [];
      const statusLabel = this.getStatusLabel(issue);
      const expectedColumn = statusLabel ? this.findColumnByLabel(statusLabel) : null;
    
      if (direction === 'label-to-board' || direction === 'both') {
        // 标签 → 看板:根据状态标签移动卡片
        if (statusLabel && expectedColumn) {
          if (!currentColumn) {
            // Issue 不在看板上,需要创建卡片
            actions.push({
              type: 'create_card',
              issue_number: issue.number,
              details: {
                to_column: expectedColumn.name,
              },
              reason: `根据标签 ${statusLabel} 添加到看板列 ${expectedColumn.name}`,
            });
          } else if (currentColumn.name !== expectedColumn.name) {
            // 卡片在错误的列,需要移动
            actions.push({
              type: 'move_card',
              issue_number: issue.number,
              details: {
                from_column: currentColumn.name,
                to_column: expectedColumn.name,
              },
              reason: `根据标签 ${statusLabel} 移动卡片`,
            });
          }
        }
      }
    
      if (direction === 'board-to-label' || direction === 'both') {
        // 看板 → 标签:根据卡片位置更新标签
        if (currentColumn) {
          const expectedLabel = this.findLabelByColumn(currentColumn.name);
          if (expectedLabel && expectedLabel !== statusLabel) {
            // 需要更新状态标签
            if (statusLabel) {
              actions.push({
                type: 'remove_label',
                issue_number: issue.number,
                details: { label: statusLabel },
                reason: `移除旧状态标签 ${statusLabel}`,
              });
            }
            actions.push({
              type: 'add_label',
              issue_number: issue.number,
              details: { label: expectedLabel },
              reason: `根据看板位置 ${currentColumn.name} 添加标签`,
            });
          }
        }
      }
    
      return actions;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SupenBysz/gitea-mcp-tool'

If you have feedback or need assistance with the MCP directory API, please join our Discord server