@@ -28,7 +28,7 @@ fn format_kv_pairs(values: &[redis::Value]) -> Vec<String> {
2828pub struct FieldDefinition {
2929 /// Field name (or JSONPath for JSON indexes)
3030 pub name : String ,
31- /// Field type: TEXT, NUMERIC, TAG, GEO, VECTOR
31+ /// Field type: TEXT, NUMERIC, TAG, GEO, VECTOR, GEOSHAPE
3232 pub field_type : String ,
3333 /// Make field sortable (enables SORTBY in queries)
3434 #[ serde( default ) ]
@@ -50,7 +50,30 @@ pub struct FieldDefinition {
5050 pub alias : Option < String > ,
5151}
5252
53+ const VALID_FIELD_TYPES : & [ & str ] = & [ "TEXT" , "NUMERIC" , "TAG" , "GEO" , "VECTOR" , "GEOSHAPE" ] ;
54+
5355impl FieldDefinition {
56+ fn validate ( & self ) -> Result < ( ) , McpError > {
57+ let ft = self . field_type . to_uppercase ( ) ;
58+ if !VALID_FIELD_TYPES . contains ( & ft. as_str ( ) ) {
59+ return Err ( McpError :: tool ( format ! (
60+ "Invalid field_type '{}' for field '{}'. Valid types: {}" ,
61+ self . field_type,
62+ self . name,
63+ VALID_FIELD_TYPES . join( ", " ) ,
64+ ) ) ) ;
65+ }
66+ if let Some ( ref sep) = self . separator
67+ && sep. chars ( ) . count ( ) != 1
68+ {
69+ return Err ( McpError :: tool ( format ! (
70+ "Invalid separator '{}' for field '{}'. Must be a single character" ,
71+ sep, self . name,
72+ ) ) ) ;
73+ }
74+ Ok ( ( ) )
75+ }
76+
5477 fn to_args ( & self ) -> Vec < String > {
5578 let mut args = vec ! [ self . name. clone( ) ] ;
5679 if let Some ( ref alias) = self . alias {
@@ -179,6 +202,16 @@ database_tool!(read_only, ft_search, "redis_ft_search",
179202 #[ serde( default ) ]
180203 pub withscores: bool ,
181204 } => |conn, input| {
205+ if let Some ( ref order) = input. sortby_order {
206+ let upper = order. to_uppercase( ) ;
207+ if upper != "ASC" && upper != "DESC" {
208+ return Err ( McpError :: tool( format!(
209+ "Invalid sortby_order '{}'. Valid values: ASC, DESC" ,
210+ order,
211+ ) ) ) ;
212+ }
213+ }
214+
182215 let mut cmd = redis:: cmd( "FT.SEARCH" ) ;
183216 cmd. arg( & input. index) . arg( & input. query) ;
184217
@@ -352,9 +385,17 @@ database_tool!(read_only, ft_profile, "redis_ft_profile",
352385 /// Query to profile
353386 pub query: String ,
354387 } => |conn, input| {
388+ let command_upper = input. command. to_uppercase( ) ;
389+ if command_upper != "SEARCH" && command_upper != "AGGREGATE" {
390+ return Err ( McpError :: tool( format!(
391+ "Invalid command '{}'. Valid values: SEARCH, AGGREGATE" ,
392+ input. command,
393+ ) ) ) ;
394+ }
395+
355396 let result: Vec <redis:: Value > = redis:: cmd( "FT.PROFILE" )
356397 . arg( & input. index)
357- . arg( input . command . to_uppercase ( ) )
398+ . arg( & command_upper )
358399 . arg( "QUERY" )
359400 . arg( & input. query)
360401 . query_async( & mut conn)
@@ -364,7 +405,7 @@ database_tool!(read_only, ft_profile, "redis_ft_profile",
364405 // FT.PROFILE returns [results, profile_data]
365406 let mut output = format!(
366407 "Profile for {} '{}' on '{}':\n \n " ,
367- input . command . to_uppercase ( ) , input. query, input. index
408+ command_upper , input. query, input. index
368409 ) ;
369410 for ( i, val) in result. iter( ) . enumerate( ) {
370411 output. push_str( & format!( "[{}]: {}\n " , i, format_value( val) ) ) ;
@@ -475,6 +516,18 @@ database_tool!(write, ft_create, "redis_ft_create",
475516 if input. schema. is_empty( ) {
476517 return Err ( McpError :: tool( "schema must contain at least one field definition" ) ) ;
477518 }
519+ if let Some ( ref on) = input. on {
520+ let upper = on. to_uppercase( ) ;
521+ if upper != "HASH" && upper != "JSON" {
522+ return Err ( McpError :: tool( format!(
523+ "Invalid 'on' value '{}'. Valid values: HASH, JSON" ,
524+ on,
525+ ) ) ) ;
526+ }
527+ }
528+ for field in & input. schema {
529+ field. validate( ) ?;
530+ }
478531
479532 let mut cmd = redis:: cmd( "FT.CREATE" ) ;
480533 cmd. arg( & input. index) ;
@@ -521,6 +574,8 @@ database_tool!(write, ft_alter, "redis_ft_alter",
521574 /// Field definition to add
522575 pub field: FieldDefinition ,
523576 } => |conn, input| {
577+ input. field. validate( ) ?;
578+
524579 let mut cmd = redis:: cmd( "FT.ALTER" ) ;
525580 cmd. arg( & input. index) . arg( "SCHEMA" ) . arg( "ADD" ) ;
526581 for arg in input. field. to_args( ) {
0 commit comments