feat(mcp): MCP dashboard enhancements — JSON config import, token overhead badges, and batch health testing (#1037)

This commit is contained in:
M57 authored and GitHub committed 2026-09-01 20:18:45 +03:00
1 parent d8385a2b02
commit 6ec277af8b
9 files changed
+963 -17

No files matched your search

@@ -0,0 +1,244 @@
package com.m57.hermescontrol.ui.mcp
import com.m57.hermescontrol.data.model.McpServerTestResponse
import com.m57.hermescontrol.data.model.McpServerToolInfo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class McpJsonParserTest {
@Test
fun testParseClaudeDesktopMcpServersWrapper() {
val json =
"""
{
"mcpServers": {
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/path/to/db.sqlite"],
"env": {
"DEBUG": "1"
}
},
"github": {
"url": "https://api.github.com/mcp",
"headers": {
"Authorization": "Bearer gh_secret_123"
}
}
}
}
""".trimIndent()
val parsed = McpJsonParser.parse(json)
assertEquals(2, parsed.size)
val sqlite = parsed.find { it.name == "sqlite" }
assertNotNull(sqlite)
assertEquals("uvx", sqlite?.command)
assertEquals(listOf("mcp-server-sqlite", "--db-path", "/path/to/db.sqlite"), sqlite?.args)
assertEquals(mapOf("DEBUG" to "1"), sqlite?.env)
assertEquals("none", sqlite?.auth)
val github = parsed.find { it.name == "github" }
assertNotNull(github)
assertEquals("https://api.github.com/mcp", github?.url)
assertEquals("header", github?.auth)
assertEquals("gh_secret_123", github?.bearerToken)
}
@Test
fun testParseSnakeCaseMcpServersWrapper() {
val json =
"""
{
"mcp_servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
}
""".trimIndent()
val parsed = McpJsonParser.parse(json)
assertEquals(1, parsed.size)
assertEquals("filesystem", parsed[0].name)
assertEquals("npx", parsed[0].command)
assertEquals(listOf("-y", "@modelcontextprotocol/server-filesystem", "/tmp"), parsed[0].args)
}
@Test
fun testParseDirectServerMap() {
val json =
"""
{
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/db"]
}
}
""".trimIndent()
val parsed = McpJsonParser.parse(json)
assertEquals(1, parsed.size)
assertEquals("postgres", parsed[0].name)
assertEquals("npx", parsed[0].command)
}
@Test
fun testParseSingleServerObject() {
val json =
"""
{
"name": "custom-tools",
"command": "python3",
"args": ["-m", "mcp_server"],
"auth": "oauth"
}
""".trimIndent()
val parsed = McpJsonParser.parse(json)
assertEquals(1, parsed.size)
assertEquals("custom-tools", parsed[0].name)
assertEquals("python3", parsed[0].command)
assertEquals("oauth", parsed[0].auth)
}
@Test
fun testParseInvalidJsonReturnsEmpty() {
val parsed = McpJsonParser.parse("not json at all")
assertTrue(parsed.isEmpty())
}
@Test
fun testParseMalformedArgsAndEnvDoesNotCrash() {
val json =
"""
{
"mcpServers": {
"faulty": {
"command": "npx",
"args": ["valid", {"nested": "bad"}, ["array"], 123],
"env": {
"VALID_KEY": "valid_val",
"BAD_KEY": {"nested": "bad"},
"BAD_LIST": [1, 2]
}
}
}
}
""".trimIndent()
val parsed = McpJsonParser.parse(json)
assertEquals(1, parsed.size)
val server = parsed[0]
assertEquals("faulty", server.name)
assertEquals(listOf("valid", "123"), server.args)
assertEquals(mapOf("VALID_KEY" to "valid_val"), server.env)
}
@Test
fun testTokenOverheadEstimator() {
val tools =
listOf(
McpServerToolInfo(name = "read_file", schemaChars = 400),
McpServerToolInfo(name = "write_file", schemaChars = 401),
McpServerToolInfo(name = "no_chars", schemaChars = null),
)
// ceil(400/4) = 100, ceil(401/4) = 101 -> sum = 201
val est = McpTokenEstimator.estimateTokens(tools)
assertEquals(201, est)
val formatted = McpTokenEstimator.formatTokenOverhead(tools.size, est)
assertEquals("3 tools • ~201 tokens", formatted)
}
@Test
fun testTokenOverheadLargeFormatting() {
val tools =
listOf(
McpServerToolInfo(name = "tool_huge", schemaChars = 12800),
)
// ceil(12800/4) = 3200 tokens -> ~3.2k tokens
val est = McpTokenEstimator.estimateTokens(tools)
assertEquals(3200, est)
val formatted = McpTokenEstimator.formatTokenOverhead(49, est)
assertEquals("49 tools • ~3.2k tokens", formatted)
}
@Test
fun testTokenOverheadNoSchemaChars() {
val tools =
listOf(
McpServerToolInfo(name = "tool1", schemaChars = null),
McpServerToolInfo(name = "tool2", schemaChars = 0),
)
val est = McpTokenEstimator.estimateTokens(tools)
assertNull(est)
assertEquals("2 tools", McpTokenEstimator.formatTokenOverhead(2, est))
}
@Test
fun testHealthStatusResolution() {
// In flight
assertEquals(
McpHealthStatus.TESTING,
McpHealthStatus.resolve(
isTesting = true,
testResult = null,
serverStatus = "ok",
serverError = null,
),
)
// Healthy result
assertEquals(
McpHealthStatus.HEALTHY,
McpHealthStatus.resolve(
isTesting = false,
testResult = McpServerTestResponse(ok = true),
serverStatus = null,
serverError = null,
),
)
// Auth needed
assertEquals(
McpHealthStatus.AUTH_REQUIRED,
McpHealthStatus.resolve(
isTesting = false,
testResult = McpServerTestResponse(ok = false, error = "OAuth authentication required"),
serverStatus = null,
serverError = null,
),
)
// Error
assertEquals(
McpHealthStatus.ERROR,
McpHealthStatus.resolve(
isTesting = false,
testResult = McpServerTestResponse(ok = false, error = "Process exited with 1"),
serverStatus = null,
serverError = null,
),
)
// Fallback from status
assertEquals(
McpHealthStatus.HEALTHY,
McpHealthStatus.resolve(
isTesting = false,
testResult = null,
serverStatus = "running",
serverError = null,
),
)
}
}