forked from zereight/gitlab-mcp
-
Notifications
You must be signed in to change notification settings - Fork 1
refactor(wiki): CQRS consolidation - 5 tools → 2 tools #10
Copy link
Copy link
Labels
refactorCode restructuring without behavior changeCode restructuring without behavior change
Description
Summary
Consolidate 5 wiki tools into 2 CQRS-aligned tools for better MCP client compatibility and cleaner read/write separation.
Current State (5 tools)
| Tool | Type | Description |
|---|---|---|
list_wiki_pages |
READ | List all wiki pages |
get_wiki_page |
READ | Get single wiki page content |
create_wiki_page |
WRITE | Create new wiki page |
update_wiki_page |
WRITE | Modify existing wiki page |
delete_wiki_page |
WRITE | Remove wiki page |
Target State (2 tools)
browse_wiki (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 "list" action
with_content?: boolean, // Include page content in list
// For "get" action
slug: string, // Page slug/path
version?: string, // Specific version SHA
render_html?: boolean // Return rendered HTML
}Read-only mode: Always allowed
manage_wiki (Command)
Consolidates all WRITE operations:
{
action: "create" | "update" | "delete",
// Common - scope selection
scope: "project" | "group",
projectId?: string,
groupId?: string,
// Page identification (for update/delete)
slug?: string,
// For "create" and "update"
title?: string,
content?: string,
format?: "markdown" | "rdoc" | "asciidoc" | "org"
}Read-only mode: Blocked entirely
Implementation Tasks
- Create new
browse_wikihandler with action dispatch - Create new
manage_wikihandler with action dispatch - Update Zod schemas with flat object pattern (see Schema Pattern below)
- Handle both project and group wiki APIs
- Update registry to export new tool names
- Update read-only tools list (only
browse_wiki) - 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_wiki - READ operations
const BrowseWikiSchema = z.object({
action: z.enum(["list", "get"]),
scope: z.enum(["project", "group"]),
projectId: z.string().optional(),
groupId: z.string().optional(),
// list-specific
with_content: z.boolean().optional(),
// get-specific
slug: z.string().optional(),
version: z.string().optional(),
render_html: z.boolean().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.slug !== undefined,
{ message: "slug is required for 'get' action", path: ["slug"] }
);
// manage_wiki - WRITE operations
const ManageWikiSchema = z.object({
action: z.enum(["create", "update", "delete"]),
scope: z.enum(["project", "group"]),
projectId: z.string().optional(),
groupId: z.string().optional(),
slug: z.string().optional(),
title: z.string().optional(),
content: z.string().optional(),
format: z.enum(["markdown", "rdoc", "asciidoc", "org"]).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.slug !== undefined,
{ message: "slug is required for update/delete actions", path: ["slug"] }
).refine(
(data) => data.action !== "create" || data.title !== undefined,
{ message: "title is required for 'create' action", path: ["title"] }
).refine(
(data) => data.action !== "create" || data.content !== undefined,
{ message: "content is required for 'create' action", path: ["content"] }
);Breaking Changes
| Old Tool | Migration Path |
|---|---|
list_wiki_pages |
browse_wiki with action: "list" |
get_wiki_page |
browse_wiki with action: "get" |
create_wiki_page |
manage_wiki with action: "create" |
update_wiki_page |
manage_wiki with action: "update" |
delete_wiki_page |
manage_wiki with action: "delete" |
Feature Flag
Existing USE_GITLAB_WIKI flag continues to control entire wiki entity visibility.
Testing Requirements
- Unit tests for flat schema validation with refine
- Test project-level wiki
- Test group-level wiki (if available)
- Test different content formats (markdown, asciidoc, etc.)
- Test version retrieval
- Test read-only mode blocks
manage_wikientirely - 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 wikis supported
- Content format handling 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