Research conducted on modern AI coding assistants (Cursor, GitHub Copilot, Cline,
Aider, Windsurf, Replit Agent) to understand architecture patterns, context management,
code editing workflows, and tool use protocols.
Key Decision: Pivoted from building full CLI (40-50h) to validation-driven MCP-first
approach (10-15h). Build 5 core CODEX MCP tools that work with ANY coding assistant,
validate adoption over 2-4 weeks, then decide on full CLI if demand proven.
Files:
- research/ai-systems/modern-coding-assistants-architecture.md (comprehensive research)
- research/ai-systems/codex-coding-assistant-implementation-plan.md (original CLI plan, preserved)
- research/ai-systems/codex-mcp-tools-implementation-plan.md (approved MCP-first plan)
- ideas/registry.json (updated with approved MCP tools proposal)
Architech Validation: APPROVED with pivot to MCP-first approach
Human Decision: Approved (pragmatic validation-driven development)
Next: Begin Phase 1 implementation (10-15 hours, 5 core MCP tools)
🤖 Generated with CODEX Research System
Co-Authored-By: The Archivist <archivist@codex.svrnty.io>
Co-Authored-By: The Architech <architech@codex.svrnty.io>
Co-Authored-By: Mathias Beaulieu-Duncan <mat@svrnty.io>
143 lines
4.5 KiB
C#
143 lines
4.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using OpenHarbor.MCP.Core;
|
|
|
|
namespace CodexMcpServer.Tools;
|
|
|
|
/// <summary>
|
|
/// MCP tool for listing CODEX documents with pagination support.
|
|
/// Calls CODEX API GET /api/documents endpoint.
|
|
/// </summary>
|
|
public class ListDocumentsTool : IMcpTool
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private static readonly JsonDocument _schema = JsonDocument.Parse("""
|
|
{
|
|
"type": "object",
|
|
"properties": {
|
|
"page": {
|
|
"type": "integer",
|
|
"description": "Page number for pagination (1-based)"
|
|
},
|
|
"pageSize": {
|
|
"type": "integer",
|
|
"description": "Number of items per page"
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"description": "Maximum number of items to return"
|
|
},
|
|
"offset": {
|
|
"type": "integer",
|
|
"description": "Number of items to skip"
|
|
}
|
|
}
|
|
}
|
|
""");
|
|
|
|
public ListDocumentsTool(HttpClient httpClient)
|
|
{
|
|
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
|
|
|
// Set base address if not already set
|
|
if (_httpClient.BaseAddress == null)
|
|
{
|
|
_httpClient.BaseAddress = new Uri("http://localhost:5050");
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public string Name => "list_documents";
|
|
|
|
/// <inheritdoc/>
|
|
public string Description => "List all CODEX documents with optional pagination. Returns document summaries with metadata.";
|
|
|
|
/// <inheritdoc/>
|
|
public JsonDocument Schema => _schema;
|
|
|
|
/// <inheritdoc/>
|
|
public async Task<JsonDocument> ExecuteAsync(JsonDocument? arguments)
|
|
{
|
|
try
|
|
{
|
|
// Build query string from optional pagination parameters
|
|
var queryParams = new List<string>();
|
|
|
|
if (arguments != null)
|
|
{
|
|
var root = arguments.RootElement;
|
|
|
|
if (root.TryGetProperty("page", out var pageElement) && pageElement.ValueKind == JsonValueKind.Number)
|
|
{
|
|
queryParams.Add($"page={pageElement.GetInt32()}");
|
|
}
|
|
|
|
if (root.TryGetProperty("pageSize", out var pageSizeElement) && pageSizeElement.ValueKind == JsonValueKind.Number)
|
|
{
|
|
queryParams.Add($"pageSize={pageSizeElement.GetInt32()}");
|
|
}
|
|
|
|
if (root.TryGetProperty("limit", out var limitElement) && limitElement.ValueKind == JsonValueKind.Number)
|
|
{
|
|
queryParams.Add($"limit={limitElement.GetInt32()}");
|
|
}
|
|
|
|
if (root.TryGetProperty("offset", out var offsetElement) && offsetElement.ValueKind == JsonValueKind.Number)
|
|
{
|
|
queryParams.Add($"offset={offsetElement.GetInt32()}");
|
|
}
|
|
}
|
|
|
|
// Build final URL with query string
|
|
var url = "/api/documents";
|
|
if (queryParams.Any())
|
|
{
|
|
url += "?" + string.Join("&", queryParams);
|
|
}
|
|
|
|
// Call CODEX API
|
|
var response = await _httpClient.GetAsync(url);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
var errorContent = await response.Content.ReadAsStringAsync();
|
|
return CreateErrorResponse(
|
|
$"CODEX API returned error: {response.StatusCode}. {errorContent}"
|
|
);
|
|
}
|
|
|
|
// Parse and return response
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
try
|
|
{
|
|
return JsonDocument.Parse(responseContent);
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
return CreateErrorResponse($"Failed to parse CODEX API response: {ex.Message}");
|
|
}
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
return CreateErrorResponse($"HTTP request failed: {ex.Message}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return CreateErrorResponse($"Unexpected error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private static JsonDocument CreateErrorResponse(string message)
|
|
{
|
|
var error = new
|
|
{
|
|
error = message
|
|
};
|
|
return JsonDocument.Parse(JsonSerializer.Serialize(error));
|
|
}
|
|
}
|