From 5b1f0be2be3809edc01c98b42ba579286a9d8747 Mon Sep 17 00:00:00 2001 From: Jason Walker <913443@dadeschools.net> Date: Sun, 5 Jul 2026 15:41:49 -0400 Subject: [PATCH] feat: fix MCP registration/naming for Jenkins/GlitchTip and add discoverability negative assertions (#146) - Updated mcp_server.py operator guide entries to use jenkins-mcp / glitchtip-mcp names matching mcp-control-plane. - Added reload/reconnect instructions for clients in notes. - Added test for 'enabled but no usable tools' negative assertion. - Updated EXPECTED_SKILLS and assertions in test_operator_guide.py. Scoped to issue #146. Author profile. Refs #146 --- mcp_server.py | 12 ++++++------ tests/test_operator_guide.py | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/mcp_server.py b/mcp_server.py index 4a52b97..5f26704 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -2307,26 +2307,26 @@ _PROJECT_SKILLS = { "committed.", ], }, - "jenkins-readonly": { + "jenkins-mcp": { "description": "Read-only Jenkins CI inspection (jobs, builds, " - "logs). Actual server name: jenkins-mcp (see mcp-control-plane).", + "logs).", "when_to_use": "Checking CI state once Jenkins MCP tools exist.", "required_operations": ["jenkins.read"], "status": "designed-not-implemented", - "notes": "Server code exists in mcp-control-plane as jenkins-mcp (read tools + gated trigger); registration pending (#55); docs in Gitea-Tools use historical name. Report SKIPPED if not connected. Do not substitute shell/API. Trigger requires dedicated profile (see #56).", + "notes": "Server code exists in mcp-control-plane as jenkins-mcp (read tools + gated trigger); registration pending. To register for discoverability in clients (Codex/Gemini/Grok/etc.): add to client MCP config under the jenkins-mcp name, reconnect/reload the client session after registration. Report SKIPPED if not connected. Do not substitute shell/API. Trigger requires dedicated profile (see #56).", "steps": [ "Confirm a Jenkins MCP server is connected (jenkins-mcp); if not, report " "SKIPPED.", "Use read-only operations only; never trigger unless using dedicated profile + confirmation.", ], }, - "glitchtip-readonly": { - "description": "Read-only GlitchTip error/event inspection. Actual server name: glitchtip-mcp (see mcp-control-plane).", + "glitchtip-mcp": { + "description": "Read-only GlitchTip error/event inspection.", "when_to_use": "Investigating reported errors once GlitchTip MCP " "tools exist.", "required_operations": ["glitchtip.read"], "status": "designed-not-implemented", - "notes": "Server code exists in mcp-control-plane as glitchtip-mcp (read-only tools); registration pending (#55); filing orchestrator is partial in mcp-control-plane (see #57). Report SKIPPED if not connected. Filing to Gitea is separate orchestrator, not in this server.", + "notes": "Server code exists in mcp-control-plane as glitchtip-mcp (read-only tools); registration pending. To register for discoverability in clients (Codex/Gemini/Grok/etc.): add to client MCP config under the glitchtip-mcp name, reconnect/reload the client session after registration. Filing orchestrator is partial in mcp-control-plane (see #57). Report SKIPPED if not connected. Filing to Gitea is separate orchestrator, not in this server.", "steps": [ "Confirm a GlitchTip MCP server is connected (glitchtip-mcp); if not, report " "SKIPPED.", diff --git a/tests/test_operator_guide.py b/tests/test_operator_guide.py index b01d978..36767aa 100644 --- a/tests/test_operator_guide.py +++ b/tests/test_operator_guide.py @@ -46,8 +46,8 @@ EXPECTED_SKILLS = [ "gitea-resolve-task-capability", "profile-switching", "redaction-security-review", - "jenkins-readonly", - "glitchtip-readonly", + "jenkins-mcp", + "glitchtip-mcp", "release-operator", ] @@ -234,8 +234,8 @@ class TestProjectSkills(GuideTestBase): with patch.dict(os.environ, AUTHOR_ENV, clear=True): r = mcp_list_project_skills() by_name = {s["name"]: s for s in r["skills"]} - self.assertNotEqual(by_name["jenkins-readonly"]["status"], "available") - self.assertNotEqual(by_name["glitchtip-readonly"]["status"], "available") + self.assertNotEqual(by_name["jenkins-mcp"]["status"], "available") + self.assertNotEqual(by_name["glitchtip-mcp"]["status"], "available") def test_no_urls_in_registry(self): with patch.dict(os.environ, AUTHOR_ENV, clear=True): @@ -245,6 +245,17 @@ class TestProjectSkills(GuideTestBase): self.assertNotIn("http://", blob) self.assertNotIn("keychain:", blob) + def test_enabled_but_no_usable_tools_negative_assertion(self): + """Negative assertion for 'enabled but no usable tools' (per issue #146).""" + with patch.dict(os.environ, AUTHOR_ENV, clear=True): + r = mcp_list_project_skills() + by_name = {s["name"]: s for s in r["skills"]} + # jenkins-mcp is designed-not-implemented; even if "enabled" in config, + # it should not be usable/available to current profile without tools. + self.assertIn("jenkins-mcp", by_name) + self.assertEqual(by_name["jenkins-mcp"]["status"], "designed-not-implemented") + self.assertFalse(by_name["jenkins-mcp"].get("available_to_current_profile", False)) + # --------------------------------------------------------------------------- # mcp_get_skill_guide