Skip to main content
Glama
approval.ts6.65 kB
/** * Approval 类工具 * 审批流程管理 */ import { z } from 'zod'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { ApprovalManager } from '../../core/approval-manager.js'; export function registerApprovalTools( server: McpServer, approvalManager: ApprovalManager ): void { /** * 获取审批状态 */ server.tool( 'openspec_get_approval_status', 'Get approval status for a change', { changeId: z.string().describe('Change ID'), }, async ({ changeId }) => { const record = await approvalManager.getApprovalStatus(changeId); if (!record) { return { content: [ { type: 'text', text: `No approval record found for: ${changeId}\n\nUse openspec_request_approval to start the approval process.`, }, ], }; } // 格式化输出 let text = `Approval Status: ${changeId}\n`; text += `========================\n\n`; text += `Status: ${formatStatus(record.status)}\n`; if (record.requestedAt) { text += `Requested: ${record.requestedAt}\n`; text += `Requested by: ${record.requestedBy || 'unknown'}\n`; } if (record.reviewers && record.reviewers.length > 0) { text += `Reviewers: ${record.reviewers.join(', ')}\n`; } if (record.approvals.length > 0) { text += `\nApprovals:\n`; for (const approval of record.approvals) { text += ` ✅ ${approval.approver} at ${approval.approvedAt}\n`; if (approval.comment) { text += ` Comment: ${approval.comment}\n`; } } } if (record.rejections.length > 0) { text += `\nRejections:\n`; for (const rejection of record.rejections) { text += ` ❌ ${rejection.rejector} at ${rejection.rejectedAt}\n`; text += ` Reason: ${rejection.reason}\n`; } } return { content: [{ type: 'text', text }], }; } ); /** * 请求审批 */ server.tool( 'openspec_request_approval', 'Request approval for a change', { changeId: z.string().describe('Change ID'), requestedBy: z.string().describe('Who is requesting approval'), reviewers: z .array(z.string()) .optional() .describe('List of reviewers'), }, async ({ changeId, requestedBy, reviewers }) => { try { const record = await approvalManager.requestApproval( changeId, requestedBy, reviewers ); let text = `✅ Approval requested for: ${changeId}\n\n`; text += `Status: ${formatStatus(record.status)}\n`; text += `Requested by: ${requestedBy}\n`; if (reviewers && reviewers.length > 0) { text += `Reviewers: ${reviewers.join(', ')}\n`; } return { content: [{ type: 'text', text }], }; } catch (error: any) { return { content: [ { type: 'text', text: `❌ Failed to request approval: ${error.message}`, }, ], isError: true, }; } } ); /** * 审批通过 */ server.tool( 'openspec_approve_change', 'Approve a change', { changeId: z.string().describe('Change ID'), approver: z.string().describe('Who is approving'), comment: z.string().optional().describe('Optional approval comment'), }, async ({ changeId, approver, comment }) => { try { const record = await approvalManager.approve(changeId, approver, comment); let text = `✅ Change approved: ${changeId}\n\n`; text += `Approved by: ${approver}\n`; text += `Status: ${formatStatus(record.status)}\n`; if (comment) { text += `Comment: ${comment}\n`; } return { content: [{ type: 'text', text }], }; } catch (error: any) { return { content: [ { type: 'text', text: `❌ Failed to approve: ${error.message}`, }, ], isError: true, }; } } ); /** * 审批拒绝 */ server.tool( 'openspec_reject_change', 'Reject a change', { changeId: z.string().describe('Change ID'), rejector: z.string().describe('Who is rejecting'), reason: z.string().describe('Rejection reason'), }, async ({ changeId, rejector, reason }) => { try { const record = await approvalManager.reject(changeId, rejector, reason); let text = `❌ Change rejected: ${changeId}\n\n`; text += `Rejected by: ${rejector}\n`; text += `Reason: ${reason}\n`; text += `Status: ${formatStatus(record.status)}\n`; return { content: [{ type: 'text', text }], }; } catch (error: any) { return { content: [ { type: 'text', text: `❌ Failed to reject: ${error.message}`, }, ], isError: true, }; } } ); /** * 列出待审批的变更 */ server.tool( 'openspec_list_pending_approvals', 'List all changes pending approval', {}, async () => { const pending = await approvalManager.listPendingApprovals(); if (pending.length === 0) { return { content: [ { type: 'text', text: `No pending approvals.\n\nAll changes are either approved, rejected, or not yet submitted for approval.`, }, ], }; } let text = `Pending Approvals\n`; text += `=================\n\n`; for (const record of pending) { text += `📋 ${record.changeId}\n`; text += ` Requested: ${record.requestedAt}\n`; text += ` By: ${record.requestedBy || 'unknown'}\n`; if (record.reviewers && record.reviewers.length > 0) { text += ` Reviewers: ${record.reviewers.join(', ')}\n`; } text += `\n`; } return { content: [{ type: 'text', text }], }; } ); } /** * 格式化状态显示 */ function formatStatus(status: string): string { const statusMap: Record<string, string> = { draft: '📝 Draft', pending_approval: '⏳ Pending Approval', approved: '✅ Approved', rejected: '❌ Rejected', implementing: '🔧 Implementing', completed: '🎉 Completed', }; return statusMap[status] || status; }

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/Lumiaqian/openspec-mcp'

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