forked from zereight/gitlab-mcp
-
Notifications
You must be signed in to change notification settings - Fork 1
refactor(variables): CQRS consolidation - 5 tools → 2 tools #9
Copy link
Copy link
Closed
Labels
refactorCode restructuring without behavior changeCode restructuring without behavior change
Description
Summary
Consolidate 5 CI/CD variable tools into 2 CQRS-aligned tools for better MCP client compatibility and cleaner read/write separation.
Current State (5 tools)
| Tool | Type | Description |
|---|---|---|
list_variables |
READ | List CI/CD variables |
get_variable |
READ | Get single variable details |
create_variable |
WRITE | Create new variable |
update_variable |
WRITE | Modify existing variable |
delete_variable |
WRITE | Remove variable |
Target State (2 tools)
browse_variables (Query)
Consolidates all READ operations:
{
action: "list" | "get",
// Common - scope selection
scope: "project" | "group",
projectId?: string, // Required if scope="project"
groupId?: string, // Required if scope="group"
// For "get" action
key: string, // Variable key name
filter?: {
environment_scope?: string // e.g., "production", "*"
}
}Read-only mode: Always allowed
manage_variable (Command)
Consolidates all WRITE operations:
{
action: "create" | "update" | "delete",
// Common - scope selection
scope: "project" | "group",
projectId?: string,
groupId?: string,
// Variable identification
key: string,
// For "create" and "update"
value?: string,
variable_type?: "env_var" | "file",
protected?: boolean,
masked?: boolean,
raw?: boolean,
environment_scope?: string,
description?: string,
// For "delete" with environment scope
filter?: {
environment_scope?: string
}
}Read-only mode: Blocked entirely
Implementation Tasks
- Create new
browse_variableshandler with action dispatch - Create new
manage_variablehandler with action dispatch - Update Zod schemas with flat object pattern (see Schema Pattern below)
- Handle both project and group variable APIs
- Update registry to export new tool names
- Update read-only tools list (only
browse_variables) - Remove old handlers after migration
- Update unit tests
- Update integration tests
Schema Pattern (AI-Compatible)
CRITICAL: Per #29, do NOT use
z.discriminatedUnion()- it generatesoneOfat JSON Schema root level which is incompatible with Claude API.
Use flat z.object() with .refine() for conditional validation:
// browse_variables - READ operations
const BrowseVariablesSchema = z.object({
action: z.enum(["list", "get"]),
scope: z.enum(["project", "group"]),
projectId: z.string().optional(),
groupId: z.string().optional(),
// get-specific
key: z.string().optional(),
environment_scope: z.string().optional(),
}).refine(
(data) => data.scope !== "project" || data.projectId !== undefined,
{ message: "projectId is required when scope is 'project'", path: ["projectId"] }
).refine(
(data) => data.scope !== "group" || data.groupId !== undefined,
{ message: "groupId is required when scope is 'group'", path: ["groupId"] }
).refine(
(data) => data.action !== "get" || data.key !== undefined,
{ message: "key is required for 'get' action", path: ["key"] }
);
// manage_variable - WRITE operations
const ManageVariableSchema = z.object({
action: z.enum(["create", "update", "delete"]),
scope: z.enum(["project", "group"]),
projectId: z.string().optional(),
groupId: z.string().optional(),
key: z.string(),
// create/update fields
value: z.string().optional(),
variable_type: z.enum(["env_var", "file"]).optional(),
protected: z.boolean().optional(),
masked: z.boolean().optional(),
raw: z.boolean().optional(),
environment_scope: z.string().optional(),
description: z.string().optional(),
}).refine(
(data) => data.scope !== "project" || data.projectId !== undefined,
{ message: "projectId is required when scope is 'project'", path: ["projectId"] }
).refine(
(data) => data.scope !== "group" || data.groupId !== undefined,
{ message: "groupId is required when scope is 'group'", path: ["groupId"] }
).refine(
(data) => data.action !== "create" || data.value !== undefined,
{ message: "value is required for 'create' action", path: ["value"] }
);Breaking Changes
| Old Tool | Migration Path |
|---|---|
list_variables |
browse_variables with action: "list" |
get_variable |
browse_variables with action: "get" |
create_variable |
manage_variable with action: "create" |
update_variable |
manage_variable with action: "update" |
delete_variable |
manage_variable with action: "delete" |
Security Considerations
- Variable values may be masked - handle appropriately in responses
- Protected variables require specific branch permissions
- Environment-scoped variables need proper scope handling
Testing Requirements
- Unit tests for flat schema validation with refine
- Test project-level variables
- Test group-level variables
- Test environment-scoped variables
- Test read-only mode blocks
manage_variableentirely - Integration tests for all operations
- Verify JSON Schema output has NO
oneOf/allOf/anyOfat top level
Acceptance Criteria
- Total tools reduced from 5 to 2
- All existing functionality preserved
- Both project and group variables supported
- Environment scope filtering works correctly
- Read-only mode correctly gates write operations
- Clear error messages for invalid action/parameter combinations
- JSON Schema compatible with Claude API (no oneOf/allOf/anyOf at root)
Related
- refactor(schemas): Replace discriminated unions with flat schemas for Claude API compatibility #29 - AI-compatible schema requirement
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
refactorCode restructuring without behavior changeCode restructuring without behavior change