@@ -302,11 +302,9 @@ fn target_schema() -> Value {
302302 )
303303}
304304
305- /// Tools implemented by both Parseable OSS and Enterprise.
306- ///
307- /// Enterprise may extend this catalog, but an OSS server only advertises this
308- /// list so MCP clients never discover enterprise-only capabilities.
309- pub fn oss_tool_specs ( ) -> Vec < ToolSpec > {
305+ /// Read-only tools implemented by Parseable OSS and safe for generic external
306+ /// invocation without a confirmation flow.
307+ pub fn oss_read_tool_specs ( ) -> Vec < ToolSpec > {
310308 vec ! [
311309 ToolSpec :: new(
312310 "list_datasets" ,
@@ -435,33 +433,6 @@ pub fn oss_tool_specs() -> Vec<ToolSpec> {
435433 "List tags used across alerts." ,
436434 empty_schema( ) ,
437435 ) ,
438- ToolSpec :: new(
439- "enable_alert" ,
440- "Enable alert" ,
441- "Enable a disabled alert." ,
442- alert_schema( ) ,
443- ) ,
444- ToolSpec :: new(
445- "disable_alert" ,
446- "Disable alert" ,
447- "Disable an active alert." ,
448- alert_schema( ) ,
449- ) ,
450- ToolSpec :: new(
451- "evaluate_alert" ,
452- "Evaluate alert now" ,
453- "Evaluate an alert immediately. This may trigger notifications." ,
454- alert_schema( ) ,
455- ) ,
456- ToolSpec :: new(
457- "create_alert" ,
458- "Create alert" ,
459- "Create an alert from a complete Parseable alert specification." ,
460- object_schema(
461- json!( { "spec" : { "type" : "object" , "additionalProperties" : true } } ) ,
462- & [ "spec" ] ,
463- ) ,
464- ) ,
465436 ToolSpec :: new(
466437 "list_alert_targets" ,
467438 "List alert targets" ,
@@ -474,15 +445,6 @@ pub fn oss_tool_specs() -> Vec<ToolSpec> {
474445 "Get the configuration for one alert target." ,
475446 target_schema( ) ,
476447 ) ,
477- ToolSpec :: new(
478- "create_alert_target" ,
479- "Create alert target" ,
480- "Create an alert notification target." ,
481- object_schema(
482- json!( { "spec" : { "type" : "object" , "additionalProperties" : true } } ) ,
483- & [ "spec" ] ,
484- ) ,
485- ) ,
486448 ToolSpec :: new(
487449 "get_traces" ,
488450 "Get traces" ,
@@ -639,6 +601,50 @@ pub fn oss_tool_specs() -> Vec<ToolSpec> {
639601 & [ ] ,
640602 ) ,
641603 ) ,
604+ ]
605+ }
606+
607+ /// Effectful OSS tools retained in the canonical registry for internal agents.
608+ /// Generic external invocation must not use this catalog until confirmation is
609+ /// server-verifiable.
610+ pub fn oss_action_tool_specs ( ) -> Vec < ToolSpec > {
611+ vec ! [
612+ ToolSpec :: new(
613+ "enable_alert" ,
614+ "Enable alert" ,
615+ "Enable a disabled alert." ,
616+ alert_schema( ) ,
617+ ) ,
618+ ToolSpec :: new(
619+ "disable_alert" ,
620+ "Disable alert" ,
621+ "Disable an active alert." ,
622+ alert_schema( ) ,
623+ ) ,
624+ ToolSpec :: new(
625+ "evaluate_alert" ,
626+ "Evaluate alert now" ,
627+ "Evaluate an alert immediately. This may trigger notifications." ,
628+ alert_schema( ) ,
629+ ) ,
630+ ToolSpec :: new(
631+ "create_alert" ,
632+ "Create alert" ,
633+ "Create an alert from a complete Parseable alert specification." ,
634+ object_schema(
635+ json!( { "spec" : { "type" : "object" , "additionalProperties" : true } } ) ,
636+ & [ "spec" ] ,
637+ ) ,
638+ ) ,
639+ ToolSpec :: new(
640+ "create_alert_target" ,
641+ "Create alert target" ,
642+ "Create an alert notification target." ,
643+ object_schema(
644+ json!( { "spec" : { "type" : "object" , "additionalProperties" : true } } ) ,
645+ & [ "spec" ] ,
646+ ) ,
647+ ) ,
642648 ToolSpec :: new(
643649 "update_alert" ,
644650 "Update alert" ,
@@ -745,10 +751,21 @@ pub fn oss_tool_specs() -> Vec<ToolSpec> {
745751 ]
746752}
747753
754+ /// Canonical OSS catalog used by internal agents and imported by Enterprise.
755+ pub fn oss_tool_specs ( ) -> Vec < ToolSpec > {
756+ let mut tools = oss_read_tool_specs ( ) ;
757+ tools. extend ( oss_action_tool_specs ( ) ) ;
758+ tools
759+ }
760+
748761pub fn is_oss_tool ( name : & str ) -> bool {
749762 oss_tool_specs ( ) . iter ( ) . any ( |tool| tool. name == name)
750763}
751764
765+ pub fn is_oss_read_tool ( name : & str ) -> bool {
766+ oss_read_tool_specs ( ) . iter ( ) . any ( |tool| tool. name == name)
767+ }
768+
752769pub fn registry_http_response ( request : & HttpRequest , tools : Vec < Value > ) -> HttpResponse {
753770 let payload = json ! ( { "tools" : tools } ) ;
754771 let body = serde_json:: to_string ( & payload) . expect ( "tool registry is JSON serializable" ) ;
@@ -775,7 +792,7 @@ pub fn registry_http_response(request: &HttpRequest, tools: Vec<Value>) -> HttpR
775792
776793/// `GET /api/prism/v1/llm/tools/list`
777794pub async fn list_tool_registry ( request : HttpRequest ) -> HttpResponse {
778- let tools = oss_tool_specs ( )
795+ let tools = oss_read_tool_specs ( )
779796 . into_iter ( )
780797 . map ( |tool| {
781798 json ! ( {
@@ -797,7 +814,10 @@ mod tests {
797814 use actix_web:: { App , http:: header, test, web} ;
798815 use serde_json:: Value ;
799816
800- use super :: { ToolCallRequest , ToolCallResult , list_tool_registry, oss_tool_specs} ;
817+ use super :: {
818+ ToolCallRequest , ToolCallResult , ToolEffect , is_oss_read_tool, list_tool_registry,
819+ oss_action_tool_specs, oss_read_tool_specs, oss_tool_specs,
820+ } ;
801821
802822 #[ actix_web:: test]
803823 async fn oss_catalog_has_unique_names ( ) {
@@ -819,9 +839,42 @@ mod tests {
819839 )
820840 . await ;
821841
822- assert_eq ! ( response[ "tools" ] . as_array( ) . unwrap( ) . len( ) , 47 ) ;
842+ assert_eq ! (
843+ response[ "tools" ] . as_array( ) . unwrap( ) . len( ) ,
844+ oss_read_tool_specs( ) . len( )
845+ ) ;
823846 assert_eq ! ( response[ "tools" ] [ 0 ] [ "name" ] , "list_datasets" ) ;
824847 assert_eq ! ( response[ "tools" ] [ 0 ] [ "inputSchema" ] [ "type" ] , "object" ) ;
848+ assert ! ( oss_read_tool_specs( ) . iter( ) . all( |tool| matches!(
849+ tool. effect( ) ,
850+ ToolEffect :: ReadOnly | ToolEffect :: ExpensiveRead
851+ ) ) ) ;
852+ assert ! (
853+ response[ "tools" ]
854+ . as_array( )
855+ . unwrap( )
856+ . iter( )
857+ . all( |tool| tool[ "name" ] != "create_alert" )
858+ ) ;
859+ }
860+
861+ #[ actix_web:: test]
862+ async fn read_and_action_catalogs_are_structurally_separated ( ) {
863+ let read_tools = oss_read_tool_specs ( ) ;
864+ let action_tools = oss_action_tool_specs ( ) ;
865+
866+ assert_eq ! ( read_tools. len( ) , 34 ) ;
867+ assert_eq ! ( action_tools. len( ) , 13 ) ;
868+ assert ! ( read_tools. iter( ) . all( |tool| matches!(
869+ tool. effect( ) ,
870+ ToolEffect :: ReadOnly | ToolEffect :: ExpensiveRead
871+ ) ) ) ;
872+ assert ! ( action_tools. iter( ) . all( |tool| matches!(
873+ tool. effect( ) ,
874+ ToolEffect :: Mutation | ToolEffect :: ExternalSideEffect
875+ ) ) ) ;
876+ assert ! ( read_tools. iter( ) . all( |tool| is_oss_read_tool( tool. name) ) ) ;
877+ assert ! ( action_tools. iter( ) . all( |tool| !is_oss_read_tool( tool. name) ) ) ;
825878 }
826879
827880 #[ actix_web:: test]
0 commit comments