Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ Operations: get, search, overview

val data = buildJsonObject {
put("item", workItemToJson(item))
put("childCounts", roleCounToJson(childCounts))
put("childCounts", roleCountToJson(childCounts))
put("children", JsonArray(children.map { workItemToMinimalJson(it) }))
}

Expand Down Expand Up @@ -508,7 +508,7 @@ Operations: get, search, overview
put("title", JsonPrimitive(item.title))
put("role", JsonPrimitive(item.role.name.lowercase()))
put("priority", JsonPrimitive(item.priority.name.lowercase()))
put("childCounts", roleCounToJson(childCounts))
put("childCounts", roleCountToJson(childCounts))
if (includeChildren) {
val children = when (val result = context.workItemRepository().findChildren(item.id)) {
is Result.Success -> result.data
Expand Down Expand Up @@ -567,7 +567,7 @@ Operations: get, search, overview
item.tags?.let { put("tags", JsonPrimitive(it)) }
}

private fun roleCounToJson(counts: Map<Role, Int>): JsonObject = buildJsonObject {
private fun roleCountToJson(counts: Map<Role, Int>): JsonObject = buildJsonObject {
for (role in Role.entries) {
put(role.name.lowercase(), JsonPrimitive(counts[role] ?: 0))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
newSuspendedTransaction(db = databaseManager.getDatabase()) {
val row = WorkItemsTable.selectAll().where { WorkItemsTable.id eq id }.singleOrNull()
if (row != null) {
Result.Success(mapRowToWorkItem(row))
Result.Success(toWorkItem(row))
} else {
Result.Error(RepositoryError.NotFound(id, "WorkItem not found with id: $id"))
}
Expand Down Expand Up @@ -133,7 +133,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val items = WorkItemsTable.selectAll()
.where { WorkItemsTable.parentId eq parentId }
.limit(limit)
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand All @@ -145,7 +145,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val items = WorkItemsTable.selectAll()
.where { WorkItemsTable.role eq role.name.lowercase() }
.limit(limit)
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand All @@ -157,7 +157,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val items = WorkItemsTable.selectAll()
.where { WorkItemsTable.depth eq depth }
.limit(limit)
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand All @@ -169,7 +169,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val row = WorkItemsTable.selectAll()
.where { WorkItemsTable.parentId.isNull() and (WorkItemsTable.depth eq 0) }
.singleOrNull()
Result.Success(row?.let { mapRowToWorkItemSafe(it) })
Result.Success(row?.let { toWorkItemOrNull(it) })
}
} catch (e: Exception) {
Result.Error(RepositoryError.DatabaseError("Failed to find root WorkItem: ${e.message}", e))
Expand All @@ -183,7 +183,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
(WorkItemsTable.title like pattern) or (WorkItemsTable.summary like pattern)
}
.limit(limit)
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand All @@ -203,7 +203,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
newSuspendedTransaction(db = databaseManager.getDatabase()) {
val items = WorkItemsTable.selectAll()
.where { WorkItemsTable.parentId eq parentId }
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand Down Expand Up @@ -273,7 +273,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
.orderBy(sortColumn, order)
.limit(limit)
.offset(offset.toLong())
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }

Result.Success(items)
}
Expand Down Expand Up @@ -333,7 +333,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
newSuspendedTransaction(db = databaseManager.getDatabase()) {
val counts = WorkItemsTable.selectAll()
.where { WorkItemsTable.parentId eq parentId }
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
.groupBy { it.role }
.mapValues { (_, items) -> items.size }
Result.Success(counts)
Expand All @@ -347,7 +347,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val items = WorkItemsTable.selectAll()
.where { WorkItemsTable.parentId.isNull() }
.limit(limit)
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand All @@ -364,7 +364,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val current = queue.removeFirst()
val children = WorkItemsTable.selectAll()
.where { WorkItemsTable.parentId eq current }
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
results.addAll(children)
queue.addAll(children.map { it.id })
}
Expand All @@ -383,7 +383,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
Result.Success(
WorkItemsTable.selectAll()
.where { WorkItemsTable.id inList entityIds }
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
)
}
} catch (e: Exception) {
Expand Down Expand Up @@ -416,7 +416,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
WorkItemsTable.id.castTo<String>(VarCharColumnType(36)).like("$formattedPrefix%")
}
.limit(limit)
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
Result.Success(items)
}
} catch (e: Exception) {
Expand Down Expand Up @@ -452,7 +452,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val inputEntityIds = itemIds.map { EntityID(it, WorkItemsTable) }
val inputItems = WorkItemsTable.selectAll()
.where { WorkItemsTable.id inList inputEntityIds }
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
inputItems.forEach { cache[it.id.toString()] = it }

// BFS upward: collect all parentIds that need fetching
Expand All @@ -461,7 +461,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
val fetchEntityIds = toFetch.map { EntityID(UUID.fromString(it), WorkItemsTable) }
val fetched = WorkItemsTable.selectAll()
.where { WorkItemsTable.id inList fetchEntityIds }
.mapNotNull { mapRowToWorkItemSafe(it) }
.mapNotNull { toWorkItemOrNull(it) }
fetched.forEach { cache[it.id.toString()] = it }
toFetch = fetched.mapNotNull { it.parentId }.map { it.toString() }.toSet() - cache.keys
}
Expand Down Expand Up @@ -506,7 +506,7 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
}.reduce { acc, op -> acc or op }
}

private fun mapRowToWorkItem(row: ResultRow): WorkItem {
private fun toWorkItem(row: ResultRow): WorkItem {
return WorkItem(
id = row[WorkItemsTable.id].value,
parentId = row[WorkItemsTable.parentId],
Expand All @@ -530,15 +530,15 @@ class SQLiteWorkItemRepository(private val databaseManager: DatabaseManager) : W
}

/**
* Safe variant of [mapRowToWorkItem] for bulk-read operations.
* Safe variant of [toWorkItem] that returns null on failure, for bulk-read operations.
*
* Returns null and logs a warning if a row contains data that fails domain validation
* (e.g. an oversized title written by an older version of the server). This prevents
* a single corrupt row from crashing an entire list query.
*/
private fun mapRowToWorkItemSafe(row: ResultRow): WorkItem? {
private fun toWorkItemOrNull(row: ResultRow): WorkItem? {
return try {
mapRowToWorkItem(row)
toWorkItem(row)
} catch (e: Exception) {
logger.warn(
"Skipping corrupt WorkItem row (id={}): {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,7 @@ class CurrentMcpServer(
val toolContext = ToolExecutionContext(repositoryProvider, noteSchemaService)
logger.info("Repository provider and tool context initialized")

// Configure MCP server
val serverName = System.getenv("MCP_SERVER_NAME") ?: "mcp-task-orchestrator-current"
val server = configureServer(serverName)
mcpSdkServer = server

// Register MCP tools
val adapter = McpToolAdapter()
// Build tool list
val tools = listOf(
// Phase 1: CRUD
ManageItemsTool(),
Expand All @@ -106,6 +100,15 @@ class CurrentMcpServer(
// Phase 3: Context
GetContextTool()
)

// Configure MCP server
val serverName = System.getenv("MCP_SERVER_NAME") ?: "mcp-task-orchestrator-current"
val toolNames = tools.joinToString(", ") { it.name }
val server = configureServer(serverName, tools.size, toolNames)
mcpSdkServer = server

// Register MCP tools
val adapter = McpToolAdapter()
adapter.registerToolsWithServer(server, tools, toolContext)
logger.info("Registered ${tools.size} MCP tools")

Expand All @@ -129,6 +132,15 @@ class CurrentMcpServer(
mcpSdkServer?.close()
}

private fun registerCommonCleanup(server: Server) {
shutdownCoordinator?.addCleanupAction("Close MCP Server") {
runBlocking { server.close() }
}
shutdownCoordinator?.addCleanupAction("Close Database") {
databaseManager.shutdown()
}
}

private suspend fun runStdioTransport(server: Server, serverName: String, toolCount: Int) {
logger.info("Starting MCP server with stdio transport...")

Expand All @@ -137,12 +149,7 @@ class CurrentMcpServer(
outputStream = System.out.asSink().buffered()
)

shutdownCoordinator?.addCleanupAction("Close MCP Server") {
runBlocking { server.close() }
}
shutdownCoordinator?.addCleanupAction("Close Database") {
databaseManager.shutdown()
}
registerCommonCleanup(server)

val done = Job()
server.onClose {
Expand Down Expand Up @@ -176,11 +183,11 @@ class CurrentMcpServer(
ktorServer.stop(gracePeriodMillis = 1000, timeoutMillis = 5000)
done.complete()
}
shutdownCoordinator?.addCleanupAction("Close MCP Server") {
runBlocking { server.close() }
}
shutdownCoordinator?.addCleanupAction("Close Database") {
databaseManager.shutdown()
registerCommonCleanup(server)

server.onClose {
logger.info("Server closed")
if (shutdownCoordinator == null) databaseManager.shutdown()
}

if (shutdownCoordinator == null) {
Expand All @@ -203,7 +210,7 @@ class CurrentMcpServer(
/**
* Configures the MCP SDK server with capabilities for tools, prompts, and resources.
*/
private fun configureServer(serverName: String): Server {
private fun configureServer(serverName: String, toolCount: Int, toolNames: String): Server {
return Server(
serverInfo = Implementation(
name = serverName,
Expand All @@ -217,7 +224,7 @@ class CurrentMcpServer(
logging = JsonObject(emptyMap())
)
),
instructions = "Current (v3) MCP Task Orchestrator — 13 tools: manage_items, query_items, manage_notes, query_notes, manage_dependencies, query_dependencies, advance_item, get_next_status, get_next_item, get_blocked_items, complete_tree, create_work_tree, get_context"
instructions = "Current (v3) MCP Task Orchestrator — $toolCount tools: $toolNames"
)
}
}
Loading