@@ -94,12 +94,54 @@ def test_mcp_runbook_summary_resource_is_read_only(tmp_path: Path) -> None:
9494 {
9595 "id" : "skill.disk.quick" ,
9696 "title" : "Disk quick check" ,
97+ "step_count" : 1 ,
98+ "safety_posture" : "read_only" ,
9799 "steps" : [{"purpose" : "Show filesystem usage" , "read_only" : True }],
98100 }
99101 ]
100102 assert "df -h" not in response ["result" ]["contents" ][0 ]["text" ]
101103
102104
105+ def test_mcp_runbook_summary_reports_policy_gated_posture (tmp_path : Path ) -> None :
106+ server = McpServer (
107+ DEFAULT_POLICY_ENGINE ,
108+ tmp_path / "audit.log" ,
109+ runbooks = (
110+ Runbook (
111+ id = "skill.service.restart" ,
112+ title = "Restart service" ,
113+ steps = (
114+ RunbookStep (
115+ command = "systemctl status nginx" ,
116+ purpose = "Inspect service status" ,
117+ read_only = True ,
118+ ),
119+ RunbookStep (
120+ command = "systemctl restart nginx" ,
121+ purpose = "Restart service" ,
122+ read_only = False ,
123+ ),
124+ ),
125+ ),
126+ ),
127+ )
128+
129+ response = server .handle (
130+ {
131+ "jsonrpc" : "2.0" ,
132+ "id" : 12 ,
133+ "method" : "resources/read" ,
134+ "params" : {"uri" : "linuxagent://runbooks/summary" },
135+ }
136+ )
137+
138+ assert response is not None
139+ content = json .loads (response ["result" ]["contents" ][0 ]["text" ])
140+ assert content ["runbooks" ][0 ]["step_count" ] == 2
141+ assert content ["runbooks" ][0 ]["safety_posture" ] == "policy_gated"
142+ assert "systemctl restart nginx" not in response ["result" ]["contents" ][0 ]["text" ]
143+
144+
103145def test_mcp_skill_summary_resource_reports_manifest_metadata (tmp_path : Path ) -> None :
104146 server = McpServer (
105147 DEFAULT_POLICY_ENGINE ,
0 commit comments