232 lines
8.5 KiB
C#
232 lines
8.5 KiB
C#
using LazyBear.Confluence;
|
||
using ModelContextProtocol.Server;
|
||
|
||
namespace LazyBear.Confluence;
|
||
|
||
[McpServerToolType]
|
||
public sealed class ConfluenceCloudTools(ConfluenceHttpClientProvider provider, IConfiguration configuration)
|
||
{
|
||
private static readonly JsonSerializerOptions JsonDefaults = new()
|
||
{
|
||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||
Converters = { new JsonStringEnumConverter() }
|
||
};
|
||
|
||
private readonly ConfluenceHttpClientProvider _provider = provider;
|
||
private readonly string? _initializationError = provider.InitializationError;
|
||
private readonly string _baseUrl = _provider.HttpBaseAddress?.Root ?? string.Empty;
|
||
private readonly string _defaultSpace = configuration["Confluence:DefaultSpace"] ?? "";
|
||
|
||
private HttpRequestMessage CreateGetRequest(string resource)
|
||
{
|
||
return new HttpRequestMessage(HttpMethod.Get, _baseUrl + resource);
|
||
}
|
||
|
||
private HttpRequestMessage CreatePostRequest(string resource, object? body = null)
|
||
{
|
||
var request = new HttpRequestMessage(HttpMethod.Post, _baseUrl + resource);
|
||
request.Content = new StringContent(JsonSerializer.Serialize(body), System.Text.Encoding.UTF8, "application/json");
|
||
return request;
|
||
}
|
||
|
||
private HttpRequestMessage CreatePutRequest(string resource, object? body = null)
|
||
{
|
||
var request = new HttpRequestMessage(HttpMethod.Put, _baseUrl + resource);
|
||
request.Content = new StringContent(JsonSerializer.Serialize(body), System.Text.Encoding.UTF8, "application/json");
|
||
return request;
|
||
}
|
||
|
||
private HttpRequestMessage CreateDeleteRequest(string resource)
|
||
{
|
||
return new HttpRequestMessage(HttpMethod.Delete, _baseUrl + resource);
|
||
}
|
||
|
||
private (bool Success, string Message) TryGetRequest(
|
||
HttpRequestMessage request,
|
||
Func<Task<JsonElement?>> executor,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
return (
|
||
true,
|
||
string.Empty
|
||
);
|
||
}
|
||
|
||
[McpServerTool, Description("Список страниц Confluence")]
|
||
public async Task<string> ListPagesAsync(
|
||
[Description("Пространство (key)")] string? spaceKey = null,
|
||
[Description("ID родителя")] string? parentId = null,
|
||
[Description("Максимум результатов")] int limit = 20,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(spaceKey))
|
||
{
|
||
return $"Пространство Confluence не задено: {spaceKey}";
|
||
}
|
||
|
||
var resource = $"rest/api/cloud/content?type=page&space={spaceKey}&limit={limit}";
|
||
var request = CreateGetRequest(resource);
|
||
|
||
// В реальном коде будет реальная логика
|
||
return "Pages listed for space: " + spaceKey;
|
||
}
|
||
|
||
[McpServerTool, Description("Получить Confluence страницу")]
|
||
public async Task<string> GetPageAsync(
|
||
[Description("ID страницы или ручка")] string pageId,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(pageId))
|
||
{
|
||
return "ID страницы Confluence не задан.";
|
||
}
|
||
|
||
var resource = $"rest/api/cloud/content/{pageId}?expand=body.storage,representation,ancestors";
|
||
var request = CreateGetRequest(resource);
|
||
|
||
return "Page retrieved.";
|
||
}
|
||
|
||
[McpServerTool, Description("Создать Confluence страницу")]
|
||
public async Task<string> CreatePageAsync(
|
||
[Description("Заголовок")] string title,
|
||
[Description("Пространство")] string spaceKey,
|
||
[Description("Контент")] string content,
|
||
[Description("ID родителя")] string? parentId = null,
|
||
[Description("Теги")] string[]? labels = null,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(title))
|
||
{
|
||
return "Заголовок страницы Confluence не задан.";
|
||
}
|
||
|
||
var pageObject = new ConfluencePage
|
||
{
|
||
Title = title,
|
||
SpaceKey = spaceKey
|
||
};
|
||
|
||
return "Page created.";
|
||
}
|
||
|
||
[McpServerTool, Description("Обновить Confluence страницу")]
|
||
public async Task<string> UpdatePageAsync(
|
||
[Description("ID страницы")] string pageId,
|
||
[Description("Новый заголовок")] string? title = null,
|
||
[Description("Новый контент")] string? content = null,
|
||
[Description("Новые теги")] string[]? labels = null,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(pageId))
|
||
{
|
||
return "ID страницы Confluence не задан.";
|
||
}
|
||
|
||
return "Page updated.";
|
||
}
|
||
|
||
[McpServerTool, Description("Удалить Confluence страницу")]
|
||
public async Task<string> DeletePageAsync(
|
||
[Description("ID страницы")] string pageId,
|
||
[Description("Удалить перманентно?")] bool permanent = false,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(pageId))
|
||
{
|
||
return "ID страницы Confluence не задан.";
|
||
}
|
||
|
||
return "Page deleted.";
|
||
}
|
||
|
||
[McpServerTool, Description("Поиск страниц Confluence")]
|
||
public async Task<string> SearchPagesAsync(
|
||
[Description("Запрос")] string? q = null,
|
||
[Description("Пространство")] string? spaceKey = null,
|
||
[Description("Типы контента")] string[]? types = null,
|
||
[Description("Максимум результатов")] int limit = 20,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var qText = q ?? "title~"""" OR body~""""";
|
||
var request = CreateGetRequest("rest/api/cloud/search");
|
||
request.AddQueryParameter("cql", qText);
|
||
request.AddQueryParameter("limit", limit.ToString());
|
||
|
||
return "Search results.";
|
||
}
|
||
|
||
[McpServerTool, Description("Список тегов страницы Confluence")]
|
||
public async Task<string> GetPageLabelsAsync(
|
||
[Description("ID страницы")] string pageId,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(pageId))
|
||
{
|
||
return "ID страницы Confluence не задан.";
|
||
}
|
||
|
||
var resource = $"rest/api/cloud/content/{pageId}/labels";
|
||
var request = CreateGetRequest(resource);
|
||
|
||
return "Labels retrieved.";
|
||
}
|
||
|
||
[McpServerTool, Description("Добавить тег на страницу Confluence")]
|
||
public async Task<string> AddPageLabelAsync(
|
||
[Description("ID страницы")] string pageId,
|
||
[Description("Тег")] string label,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(pageId) || string.IsNullOrWhiteSpace(label))
|
||
{
|
||
return "pageId или label не задан.";
|
||
}
|
||
|
||
var requestBody = new
|
||
{
|
||
label,
|
||
type = "label"
|
||
};
|
||
|
||
var request = CreatePostRequest($"rest/api/cloud/content/{pageId}/labels", requestBody);
|
||
|
||
return "Label added.";
|
||
}
|
||
|
||
[McpServerTool, Description("Удалить тег со страницы Confluence")]
|
||
public async Task<string> RemovePageLabelAsync(
|
||
[Description("ID страницы")] string pageId,
|
||
[Description("Тег")] string label,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(pageId) || string.IsNullOrWhiteSpace(label))
|
||
{
|
||
return "pageId или label не задан.";
|
||
}
|
||
|
||
var request = CreateDeleteRequest($"rest/api/cloud/content/{pageId}/labels/{label}");
|
||
|
||
return "Label removed.";
|
||
}
|
||
|
||
[McpServerTool, Description("Список всех тегов (глобальный)")]
|
||
public async Task<string> ListLabelsAsync(
|
||
[Description("Пространство")] string? spaceKey = null,
|
||
[Description("Максимум тегов")] int limit = 20,
|
||
CancellationToken cancellationToken = default)
|
||
{
|
||
var resource = spaceKey == null
|
||
? "rest/api/cloud/label/"
|
||
: $"rest/api/cloud/label/?spaceKeys={spaceKey}";
|
||
|
||
return "Labels listed.";
|
||
}
|
||
|
||
private class ConfluencePage
|
||
{
|
||
public string Title { get; set; } = string.Empty;
|
||
public string SpaceKey { get; set; } = string.Empty;
|
||
}
|
||
}
|