|
@@ -182,17 +182,56 @@ func (h *handlers) editPage(ctx context.Context, _ *mcp.CallToolRequest, in edit
|
|
|
return nil, mutationOutput{}, err
|
|
return nil, mutationOutput{}, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Read the current page so that fields the caller did not explicitly set
|
|
|
|
|
+ // (publish state, privacy, locale, tags) are preserved instead of being
|
|
|
|
|
+ // overwritten with defaults. The pages.update mutation requires all of
|
|
|
|
|
+ // them, so "leave unchanged" is only possible by re-sending the existing
|
|
|
|
|
+ // values.
|
|
|
|
|
+ existing, err := h.wiki.PageByID(ctx, in.ID)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, mutationOutput{}, fmt.Errorf("failed to load page: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if existing == nil {
|
|
|
|
|
+ return nil, mutationOutput{}, fmt.Errorf("failed to update page: Page not found")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ path := in.Path
|
|
|
|
|
+ if strings.TrimSpace(path) == "" {
|
|
|
|
|
+ path = existing.Path
|
|
|
|
|
+ }
|
|
|
|
|
+ locale := in.Locale
|
|
|
|
|
+ if strings.TrimSpace(locale) == "" {
|
|
|
|
|
+ locale = existing.Locale
|
|
|
|
|
+ }
|
|
|
|
|
+ if strings.TrimSpace(locale) == "" {
|
|
|
|
|
+ locale = "en"
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isPublished := existing.IsPublished
|
|
|
|
|
+ if in.IsPublished != nil {
|
|
|
|
|
+ isPublished = *in.IsPublished
|
|
|
|
|
+ }
|
|
|
|
|
+ isPrivate := existing.IsPrivate
|
|
|
|
|
+ if in.IsPrivate != nil {
|
|
|
|
|
+ isPrivate = *in.IsPrivate
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ tags := existingTags(existing.Tags)
|
|
|
|
|
+ if in.Tags != nil {
|
|
|
|
|
+ tags = nonNilStrings(in.Tags)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
result, err := h.wiki.UpdatePage(ctx, wikijs.UpdatePageInput{
|
|
result, err := h.wiki.UpdatePage(ctx, wikijs.UpdatePageInput{
|
|
|
ID: in.ID,
|
|
ID: in.ID,
|
|
|
Title: in.Title,
|
|
Title: in.Title,
|
|
|
Content: in.Content,
|
|
Content: in.Content,
|
|
|
Description: in.Description,
|
|
Description: in.Description,
|
|
|
- Path: in.Path,
|
|
|
|
|
|
|
+ Path: path,
|
|
|
Editor: defaultValue(in.Editor, "markdown"),
|
|
Editor: defaultValue(in.Editor, "markdown"),
|
|
|
- IsPublished: boolValue(in.IsPublished, true),
|
|
|
|
|
- IsPrivate: boolValue(in.IsPrivate, false),
|
|
|
|
|
- Locale: defaultValue(in.Locale, "en"),
|
|
|
|
|
- Tags: nonNilStrings(in.Tags),
|
|
|
|
|
|
|
+ IsPublished: isPublished,
|
|
|
|
|
+ IsPrivate: isPrivate,
|
|
|
|
|
+ Locale: locale,
|
|
|
|
|
+ Tags: tags,
|
|
|
})
|
|
})
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, mutationOutput{}, err
|
|
return nil, mutationOutput{}, err
|
|
@@ -265,6 +304,19 @@ func nonNilStrings(values []string) []string {
|
|
|
return values
|
|
return values
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// existingTags converts the tags of an existing page (Tag structs) into the
|
|
|
|
|
+// string slice expected by UpdatePageInput, preserving them when the caller
|
|
|
|
|
+// did not pass tags.
|
|
|
|
|
+func existingTags(tags []wikijs.Tag) []string {
|
|
|
|
|
+ out := make([]string, 0, len(tags))
|
|
|
|
|
+ for _, t := range tags {
|
|
|
|
|
+ if t.Tag != "" {
|
|
|
|
|
+ out = append(out, t.Tag)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return out
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// nullResult preserves the original server's "null" response for a page that
|
|
// nullResult preserves the original server's "null" response for a page that
|
|
|
// does not exist. A typed nil page would be coerced to an empty object by the
|
|
// does not exist. A typed nil page would be coerced to an empty object by the
|
|
|
// MCP SDK's output schema handling.
|
|
// MCP SDK's output schema handling.
|