Created
June 22, 2025 16:12
-
-
Save guillaumegarcia13/be219c1dd32b731e616f5f1d21fdbfcb to your computer and use it in GitHub Desktop.
Dynamic MCP server for ABAP class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "! <p class="shorttext synchronized" lang="en">HRC Software - MCP Server - Dynamic</p> | |
| class /AHRC/CL_MCP_DYNAMIC definition | |
| public | |
| inheriting from ZCL_MCP_SERVER_BASE | |
| final | |
| create public . | |
| public section. | |
| types: | |
| BEGIN OF ty_s_method_details, | |
| class TYPE seocompodf-clsname, | |
| method TYPE seocompodf-cmpname, | |
| visibility TYPE seocompodf-exposure, " 0 Private, 1 Protected, 2 Public | |
| declaration_level TYPE seocompodf-mtddecltyp, " 0 Instance, 1 Static method | |
| is_abstract TYPE seocompodf-mtdabstrct, " 'X' abstract | |
| description TYPE seocompotx-descript, | |
| END OF ty_s_method_details . | |
| data REGISTERED_METHODS type SEO_CMPKEYS read-only . | |
| methods CONSTRUCTOR . | |
| protected section. | |
| methods GET_METHOD_DETAILS | |
| importing | |
| !CLASS type SEOCLSNAME | |
| !METHOD type SEOCMPNAME | |
| !LANGUAGE type SY-LANGU default 'E' | |
| returning | |
| value(METHOD_DETAILS) type TY_S_METHOD_DETAILS . | |
| methods _BUILD_DYNAMIC_SCHEMA_OLD | |
| importing | |
| !CLASS type SEOCLSNAME | |
| !METHOD type SEOCMPNAME | |
| !LANGUAGE type SY-LANGU default 'E' | |
| returning | |
| value(SCHEMA) type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods BUILD_DYNAMIC_SCHEMA | |
| importing | |
| !CLASS type SEOCLSNAME | |
| !METHOD type SEOCMPNAME | |
| !LANGUAGE type SY-LANGU default 'E' | |
| returning | |
| value(SCHEMA) type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods GET_SESSION_MODE | |
| redefinition . | |
| methods HANDLE_CALL_TOOL | |
| redefinition . | |
| methods HANDLE_GET_PROMPT | |
| redefinition . | |
| methods HANDLE_INITIALIZE | |
| redefinition . | |
| methods HANDLE_LIST_PROMPTS | |
| redefinition . | |
| methods HANDLE_LIST_RESOURCES | |
| redefinition . | |
| methods HANDLE_LIST_RES_TMPLS | |
| redefinition . | |
| methods HANDLE_LIST_TOOLS | |
| redefinition . | |
| methods HANDLE_RESOURCES_READ | |
| redefinition . | |
| private section. | |
| methods ADD_TABLE_TO_SCHEMA | |
| importing | |
| !NAME type STRING | |
| !DESCRIPTION type STRING | |
| !REQUIRED type ABAP_BOOL optional | |
| !TYPEDESCR type ref to CL_ABAP_TYPEDESCR | |
| changing | |
| !SCHEMA type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods ADD_STRUCT_TO_SCHEMA | |
| importing | |
| !NAME type STRING | |
| !DESCRIPTION type STRING | |
| !REQUIRED type ABAP_BOOL optional | |
| !TYPEDESCR type ref to CL_ABAP_TYPEDESCR | |
| changing | |
| !SCHEMA type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods ADD_TO_SCHEMA | |
| importing | |
| !NAME type STRING | |
| !DESCRIPTION type STRING | |
| !REQUIRED type ABAP_BOOL optional | |
| !TYPEDESCR type ref to CL_ABAP_TYPEDESCR | |
| changing | |
| !SCHEMA type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods ADD_ELEM_TO_SCHEMA | |
| importing | |
| !NAME type STRING | |
| !DESCRIPTION type STRING | |
| !REQUIRED type ABAP_BOOL optional | |
| !TYPEDESCR type ref to CL_ABAP_TYPEDESCR | |
| changing | |
| !SCHEMA type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods READ | |
| importing | |
| !REQUEST type ref to ZCL_MCP_REQ_CALL_TOOL | |
| changing | |
| !RESPONSE type ZIF_MCP_SERVER=>CALL_TOOL_RESPONSE . | |
| methods SEARCH | |
| importing | |
| !REQUEST type ref to ZCL_MCP_REQ_CALL_TOOL | |
| changing | |
| !RESPONSE type ZIF_MCP_SERVER=>CALL_TOOL_RESPONSE . | |
| methods READ_SCHEMA | |
| returning | |
| value(RESULT) type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods SEARCH_SCHEMA | |
| returning | |
| value(RESULT) type ref to ZCL_MCP_SCHEMA_BUILDER . | |
| methods CREATE_DYNAMIC_STRUCTURE | |
| importing | |
| !COMPONENTS type ABAP_COMPONENT_TAB | |
| returning | |
| value(STRUCT) type ref to DATA . | |
| ENDCLASS. | |
| CLASS /AHRC/CL_MCP_DYNAMIC IMPLEMENTATION. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->ADD_ELEM_TO_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] NAME TYPE STRING | |
| * | [--->] DESCRIPTION TYPE STRING | |
| * | [--->] REQUIRED TYPE ABAP_BOOL(optional) | |
| * | [--->] TYPEDESCR TYPE REF TO CL_ABAP_TYPEDESCR | |
| * | [<-->] SCHEMA TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD add_elem_to_schema. | |
| DATA: | |
| "------------------------------------------------------------------ | |
| " Structure | |
| "------------------------------------------------------------------ | |
| field_infos TYPE dfies, | |
| field_description TYPE string, | |
| "------------------------------------------------------------------ | |
| " Objects | |
| "------------------------------------------------------------------ | |
| elemdescr TYPE REF TO cl_abap_elemdescr, | |
| "------------------------------------------------------------------ | |
| " Objects | |
| "------------------------------------------------------------------ | |
| field_name TYPE string. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| " Preparation | |
| IF name IS INITIAL. | |
| field_name = 'TABLELINE'. | |
| ELSE. | |
| field_name = name. | |
| ENDIF. | |
| " Build | |
| elemdescr ?= typedescr. | |
| IF elemdescr->is_ddic_type( ) EQ abap_true. | |
| elemdescr->get_ddic_field( | |
| RECEIVING | |
| p_flddescr = field_infos | |
| EXCEPTIONS | |
| not_found = 1 | |
| no_ddic_type = 2 | |
| OTHERS = 3 | |
| ). | |
| IF sy-subrc <> 0. | |
| MESSAGE e016(rp) WITH 'DDIC' elemdescr->absolute_name 'not found'. | |
| RETURN. | |
| ENDIF. | |
| ELSE. | |
| field_infos-inttype = elemdescr->type_kind. | |
| ENDIF. | |
| IF description IS INITIAL. | |
| field_description = field_infos-scrtext_l. | |
| ENDIF. | |
| CASE field_infos-inttype. | |
| WHEN cl_abap_elemdescr=>typekind_char | |
| OR cl_abap_elemdescr=>typekind_clike | |
| OR cl_abap_elemdescr=>typekind_csequence | |
| OR cl_abap_elemdescr=>typekind_string. | |
| IF field_infos-domname EQ 'BOOLE' OR | |
| field_infos-domname EQ 'BOOLEAN' OR | |
| field_infos-domname EQ 'ABAP_BOOL'. | |
| schema->add_boolean( name = field_name | |
| description = field_description | |
| required = required ). | |
| ELSE. | |
| schema->add_string( name = field_name | |
| description = field_description | |
| * min_length = | |
| * max_length = | |
| required = required | |
| ). | |
| ENDIF. | |
| WHEN cl_abap_elemdescr=>typekind_decfloat | |
| OR cl_abap_elemdescr=>typekind_decfloat16 | |
| OR cl_abap_elemdescr=>typekind_decfloat34 | |
| OR cl_abap_elemdescr=>typekind_float | |
| OR cl_abap_elemdescr=>typekind_packed | |
| OR cl_abap_elemdescr=>typekind_utclong. | |
| schema->add_number( name = field_name | |
| description = field_description | |
| required = required | |
| * minimum = | |
| * maximum = | |
| ). | |
| WHEN cl_abap_elemdescr=>typekind_date. | |
| schema->add_integer( name = field_name | |
| description = field_description | |
| required = required | |
| minimum = 18000101 | |
| maximum = 99991231 | |
| ). | |
| WHEN cl_abap_elemdescr=>typekind_time. | |
| schema->add_integer( name = field_name | |
| description = field_description | |
| required = required | |
| minimum = 0 | |
| maximum = 23595959 | |
| ). | |
| WHEN cl_abap_elemdescr=>typekind_numeric " ? | |
| OR cl_abap_elemdescr=>typekind_num | |
| OR cl_abap_elemdescr=>typekind_int | |
| OR cl_abap_elemdescr=>typekind_int1 | |
| OR cl_abap_elemdescr=>typekind_int8 | |
| OR cl_abap_elemdescr=>typekind_int2. | |
| schema->add_integer( name = field_name | |
| description = field_description | |
| required = required | |
| * minimum = | |
| * maximum = | |
| ). | |
| WHEN cl_abap_elemdescr=>typekind_hex " TODO | |
| OR cl_abap_elemdescr=>typekind_class | |
| OR cl_abap_elemdescr=>typekind_dref | |
| OR cl_abap_elemdescr=>typekind_intf | |
| OR cl_abap_elemdescr=>typekind_iref | |
| OR cl_abap_elemdescr=>typekind_oref | |
| OR cl_abap_elemdescr=>typekind_simple | |
| OR cl_abap_elemdescr=>typekind_struct1 | |
| OR cl_abap_elemdescr=>typekind_struct2 | |
| OR cl_abap_elemdescr=>typekind_table | |
| OR cl_abap_elemdescr=>typekind_w | |
| OR cl_abap_elemdescr=>typekind_xsequence | |
| OR cl_abap_elemdescr=>typekind_xstring | |
| OR cl_abap_elemdescr=>typekind_bref | |
| OR cl_abap_elemdescr=>typekind_enum. | |
| schema->add_string( name = field_name | |
| description = |Type = { field_infos-inttype }| | |
| * min_length = | |
| * max_length = | |
| required = required | |
| ). | |
| WHEN OTHERS. | |
| * schema->add_string( name = field_name | |
| * description = |Type = { field_description-inttype }| | |
| ** min_length = | |
| ** max_length = | |
| * required = required | |
| * ). | |
| ENDCASE. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->ADD_STRUCT_TO_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] NAME TYPE STRING | |
| * | [--->] DESCRIPTION TYPE STRING | |
| * | [--->] REQUIRED TYPE ABAP_BOOL(optional) | |
| * | [--->] TYPEDESCR TYPE REF TO CL_ABAP_TYPEDESCR | |
| * | [<-->] SCHEMA TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD add_struct_to_schema. | |
| DATA: | |
| "------------------------------------------------------------------ | |
| " Structure | |
| "------------------------------------------------------------------ | |
| components TYPE abap_component_tab, | |
| descr TYPE string, | |
| "------------------------------------------------------------------ | |
| " Objects | |
| "------------------------------------------------------------------ | |
| structdescr TYPE REF TO cl_abap_structdescr. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| structdescr ?= typedescr. | |
| components = structdescr->get_components( ). | |
| CHECK components[] IS NOT INITIAL. | |
| IF name IS NOT INITIAL. " if name is initial -> it's an INCLUDE | |
| schema->begin_object( | |
| name = name | |
| description = description | |
| required = required | |
| ). | |
| ENDIF. | |
| LOOP AT components ASSIGNING FIELD-SYMBOL(<component>). | |
| IF <component>-as_include IS INITIAL. | |
| add_to_schema( EXPORTING name = |{ <component>-name }| | |
| description = '' | |
| * required = | |
| typedescr = <component>-type | |
| CHANGING schema = schema ). | |
| ELSE. | |
| add_struct_to_schema( EXPORTING name = '' | |
| description = '' | |
| * required = | |
| typedescr = <component>-type | |
| CHANGING schema = schema ). | |
| ENDIF. | |
| ENDLOOP. | |
| IF name IS NOT INITIAL. " if name is initial -> it's an INCLUDE | |
| schema->end_object( ). | |
| ENDIF. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->ADD_TABLE_TO_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] NAME TYPE STRING | |
| * | [--->] DESCRIPTION TYPE STRING | |
| * | [--->] REQUIRED TYPE ABAP_BOOL(optional) | |
| * | [--->] TYPEDESCR TYPE REF TO CL_ABAP_TYPEDESCR | |
| * | [<-->] SCHEMA TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD add_table_to_schema. | |
| DATA: | |
| "------------------------------------------------------------------ | |
| " Structure | |
| "------------------------------------------------------------------ | |
| descr TYPE string, | |
| "------------------------------------------------------------------ | |
| " Objects | |
| "------------------------------------------------------------------ | |
| datadescr TYPE REF TO cl_abap_datadescr, | |
| tabledescr TYPE REF TO cl_abap_tabledescr. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| " Open an array | |
| schema->begin_array( | |
| name = name | |
| description = description | |
| required = required | |
| * min_items = | |
| * max_items = | |
| ). | |
| " Process the content of the table: it can be | |
| " . a simple type (tableline) | |
| " . a structure | |
| tabledescr ?= typedescr. | |
| datadescr = tabledescr->get_table_line_type( ). | |
| add_to_schema( EXPORTING name = '' | |
| description = '' | |
| * required = | |
| typedescr = datadescr | |
| CHANGING schema = schema ). | |
| " Close the array | |
| schema->end_array( ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->ADD_TO_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] NAME TYPE STRING | |
| * | [--->] DESCRIPTION TYPE STRING | |
| * | [--->] REQUIRED TYPE ABAP_BOOL(optional) | |
| * | [--->] TYPEDESCR TYPE REF TO CL_ABAP_TYPEDESCR | |
| * | [<-->] SCHEMA TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD add_to_schema. | |
| DATA: | |
| "------------------------------------------------------------------ | |
| " Structure | |
| "------------------------------------------------------------------ | |
| dfies TYPE dfies, | |
| descr TYPE string, | |
| "------------------------------------------------------------------ | |
| " Objects | |
| "------------------------------------------------------------------ | |
| elemdescr TYPE REF TO cl_abap_elemdescr. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| IF typedescr IS INSTANCE OF cl_abap_elemdescr. | |
| add_elem_to_schema( EXPORTING name = name | |
| description = description | |
| required = required | |
| typedescr = typedescr | |
| CHANGING schema = schema ). | |
| ELSEIF typedescr IS INSTANCE OF cl_abap_structdescr. | |
| add_struct_to_schema( EXPORTING name = name | |
| description = description | |
| required = required | |
| typedescr = typedescr | |
| CHANGING schema = schema ). | |
| ELSEIF typedescr IS INSTANCE OF cl_abap_tabledescr. | |
| add_table_to_schema( EXPORTING name = name | |
| description = description | |
| required = required | |
| typedescr = typedescr | |
| CHANGING schema = schema ). | |
| ENDIF. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->BUILD_DYNAMIC_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] CLASS TYPE SEOCLSNAME | |
| * | [--->] METHOD TYPE SEOCMPNAME | |
| * | [--->] LANGUAGE TYPE SY-LANGU (default ='E') | |
| * | [<-()] SCHEMA TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD build_dynamic_schema. | |
| DATA: | |
| "---------------------------------------------------------------- | |
| " Objects | |
| "---------------------------------------------------------------- | |
| classdescr TYPE REF TO cl_abap_classdescr, | |
| typedescr TYPE REF TO cl_abap_typedescr, | |
| datadescr TYPE REF TO cl_abap_datadescr, | |
| structdescr TYPE REF TO cl_abap_structdescr, | |
| tabledescr TYPE REF TO cl_abap_tabledescr, | |
| elemdescr TYPE REF TO cl_abap_elemdescr, | |
| "---------------------------------------------------------------- | |
| " Variables | |
| "---------------------------------------------------------------- | |
| has_interface TYPE abap_bool, | |
| clsname TYPE seocompodf-clsname, | |
| cmpname TYPE seocompodf-cmpname. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| IF method CA '~'. " Class implements interface so use interface instead! | |
| SPLIT method AT '~' INTO clsname | |
| cmpname. | |
| ELSE. | |
| clsname = class. | |
| cmpname = method. | |
| ENDIF.. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| TRY. | |
| schema = NEW zcl_mcp_schema_builder( ). | |
| * schema->add_string( name = 'parameter' required = abap_true ) | |
| * ->add_integer( name = 'count' minimum = 1 ) | |
| * ->begin_object( name = 'options' ) | |
| * ->add_boolean( name = 'flag' ) | |
| * ->end_object( ). | |
| IF method CA '~'. " Class implements interface so use interface instead! | |
| has_interface = abap_true. | |
| SPLIT method AT '~' INTO clsname | |
| cmpname. | |
| ELSE. | |
| clsname = class. | |
| cmpname = method. | |
| ENDIF. | |
| " Descriptions | |
| SELECT df~clsname, | |
| df~cmpname, | |
| df~sconame, | |
| df~pardecltyp, | |
| df~parpasstyp, | |
| df~typtype, | |
| df~type, | |
| df~paroptionl, | |
| tx~descript | |
| INTO TABLE @DATA(parameter_descriptions) | |
| FROM seosubcodf AS df LEFT OUTER JOIN seosubcotx AS tx | |
| ON ( df~clsname EQ tx~clsname | |
| AND df~cmpname EQ tx~cmpname | |
| AND df~sconame EQ tx~sconame | |
| AND tx~langu EQ @language ) | |
| WHERE df~clsname EQ @clsname | |
| AND df~cmpname EQ @cmpname. | |
| " Build schema | |
| classdescr ?= cl_abap_classdescr=>describe_by_name( class ). | |
| READ TABLE classdescr->methods ASSIGNING FIELD-SYMBOL(<method_descr>) WITH TABLE KEY name = method. " TYPE abap_methdescr | |
| IF sy-subrc <> 0. | |
| MESSAGE e016(rp) WITH 'Method' method 'not found'. | |
| RETURN. | |
| ENDIF. | |
| LOOP AT <method_descr>-parameters ASSIGNING FIELD-SYMBOL(<parameter>) WHERE parm_kind EQ 'I'. " (I)mporting | (E)xporting | (C)hanging | (R)eturning | |
| READ TABLE parameter_descriptions INTO DATA(parameter_description) WITH KEY sconame = <parameter>-name. | |
| typedescr = classdescr->get_method_parameter_type( | |
| p_method_name = method | |
| p_parameter_name = <parameter>-name | |
| ). | |
| add_to_schema( EXPORTING name = |{ <parameter>-name }| | |
| description = |{ parameter_description-descript }| | |
| required = boolc( <parameter>-is_optional EQ abap_false ) | |
| typedescr = typedescr | |
| CHANGING schema = schema ). | |
| ENDLOOP. | |
| CATCH zcx_mcp_ajson_error. " JSON error | |
| ENDTRY. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Public Method /AHRC/CL_MCP_DYNAMIC->CONSTRUCTOR | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD constructor. | |
| super->constructor( ). | |
| registered_methods = VALUE #( | |
| ( clsname = '/PLMU/CL_FRW_TEST_BO' cmpname = 'GET_FLIGHT' ) | |
| ( clsname = '/AHRC/CL_DAO_EWM_MATERIAL' cmpname = '/AHRC/IF_DAO_EWM_MATERIAL~READ' ) | |
| ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->CREATE_DYNAMIC_STRUCTURE | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] COMPONENTS TYPE ABAP_COMPONENT_TAB | |
| * | [<-()] STRUCT TYPE REF TO DATA | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD create_dynamic_structure. | |
| DATA: | |
| structdescr TYPE REF TO cl_abap_structdescr. | |
| FIELD-SYMBOLS: | |
| <struct> TYPE any. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| structdescr = cl_abap_structdescr=>create( p_components = components ). | |
| CREATE DATA struct TYPE HANDLE structdescr. | |
| * ASSIGN struct->* TO <result>. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->GET_METHOD_DETAILS | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] CLASS TYPE SEOCLSNAME | |
| * | [--->] METHOD TYPE SEOCMPNAME | |
| * | [--->] LANGUAGE TYPE SY-LANGU (default ='E') | |
| * | [<-()] METHOD_DETAILS TYPE TY_S_METHOD_DETAILS | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD get_method_details. | |
| DATA: | |
| clsname TYPE seocompodf-clsname, | |
| cmpname TYPE seocompodf-cmpname. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| IF method CA '~'. " Class implements interface so use interface instead! | |
| SPLIT method AT '~' INTO clsname | |
| cmpname. | |
| ELSE. | |
| clsname = class. | |
| cmpname = method. | |
| ENDIF. | |
| SELECT SINGLE df~clsname AS class, | |
| df~cmpname AS method, | |
| df~exposure AS exposure, " 0 Private, 1 Protected, 2 Public | |
| df~mtddecltyp AS declaration_level, " 0 Instance, 1 Static method | |
| df~mtdabstrct AS is_abstract, " 'X' abstract | |
| tx~descript AS description | |
| INTO @method_details | |
| FROM seocompodf AS df LEFT OUTER JOIN seocompotx AS tx | |
| ON ( df~clsname EQ tx~clsname | |
| AND df~cmpname EQ tx~cmpname | |
| AND tx~langu EQ @language ) | |
| WHERE df~clsname EQ @clsname | |
| AND df~cmpname EQ @cmpname. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->GET_SESSION_MODE | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [<-()] RESULT TYPE ZMCP_SESSION_MODE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD get_session_mode. | |
| result = zcl_mcp_session=>session_mode_stateless. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_CALL_TOOL | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_CALL_TOOL | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>CALL_TOOL_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_call_tool. | |
| DATA: | |
| classdescr TYPE REF TO cl_abap_classdescr, | |
| structdescr TYPE REF TO cl_abap_structdescr, | |
| instance TYPE REF TO object, | |
| class TYPE seoclsname, | |
| method TYPE seocmpname, | |
| components TYPE abap_component_tab, | |
| components_input TYPE abap_component_tab, | |
| components_output TYPE abap_component_tab, | |
| signature TYPE REF TO data, " full signature of the method (IMPORTING, EXPORTING, CHANGING, RETURNING) | |
| input TYPE REF TO data, | |
| output TYPE REF TO data, | |
| method_params TYPE abap_parmbind_tab. | |
| FIELD-SYMBOLS: | |
| <signature> TYPE any, | |
| <input> TYPE any, | |
| <output> TYPE any, | |
| <field> TYPE any. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| DATA(arguments) = request->get_arguments( ). | |
| "------------------------------------------------------------------ | |
| " Checks | |
| "------------------------------------------------------------------ | |
| " Registration check for class, method) | |
| SPLIT request->get_name( ) AT '|' INTO class | |
| method. | |
| READ TABLE registered_methods TRANSPORTING NO FIELDS WITH KEY clsname = class | |
| cmpname = method. | |
| IF sy-subrc <> 0. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_params. | |
| response-error-message = |Tool { request->get_name( ) } not registered.| ##NO_TEXT. | |
| RETURN. | |
| ENDIF. | |
| " Class & Method check | |
| classdescr ?= cl_abap_classdescr=>describe_by_name( class ). | |
| READ TABLE classdescr->methods ASSIGNING FIELD-SYMBOL(<method_descr>) WITH TABLE KEY name = method. " TYPE abap_methdescr | |
| IF sy-subrc <> 0. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_request. | |
| response-error-message = |Tool { request->get_name( ) } cannot be called since method { method } does not exist.| ##NO_TEXT. | |
| RETURN. | |
| ENDIF. | |
| "------------------------------------------------------------------ | |
| " Schema validation | |
| "------------------------------------------------------------------ | |
| " Validate input parameter via schema validator class | |
| TRY. | |
| DATA(schema) = build_dynamic_schema( | |
| class = class | |
| method = method | |
| ). | |
| DATA(validator) = NEW zcl_mcp_schema_validator( schema->to_json( ) ). | |
| DATA(validation_result) = validator->validate( arguments ). | |
| IF validation_result = abap_false. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_params. | |
| response-error-message = concat_lines_of( validator->get_errors( ) ). | |
| RETURN. | |
| ENDIF. | |
| CATCH zcx_mcp_ajson_error INTO DATA(error). | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-internal_error. | |
| response-error-message = error->get_text( ). | |
| RETURN. | |
| ENDTRY. | |
| "------------------------------------------------------------------ | |
| " Perform call | |
| "------------------------------------------------------------------ | |
| DATA(method_details) = get_method_details( class = class method = method ). | |
| CASE method_details-visibility. | |
| WHEN 0 " Private | |
| OR 1. " Protected | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_request. | |
| response-error-message = |Tool { request->get_name( ) } cannot be called since it is not public.| ##NO_TEXT. | |
| RETURN. | |
| WHEN 2. " Public | |
| " Fine! | |
| ENDCASE. | |
| " - - - - - - - - - - - - - - - - - | |
| " Static VS Instance method | |
| " - - - - - - - - - - - - - - - - - | |
| CASE method_details-declaration_level. | |
| WHEN 0. " Instance method | |
| " - - - - - - - - - - - - - - | |
| " Dynamic instantiation | |
| " - - - - - - - - - - - - - - | |
| IF classdescr->is_instantiatable( ) EQ abap_false. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_request. | |
| response-error-message = |Tool { request->get_name( ) } cannot be called since constructor is not instantiable.| ##NO_TEXT. | |
| RETURN. | |
| ENDIF. | |
| DATA(constructor_details) = get_method_details( class = class method = 'CONSTRUCTOR' ). | |
| TRY. | |
| CASE constructor_details-visibility. | |
| WHEN 0 " Private | |
| OR 1. " Protected | |
| IF class CS '/AHRC/CL_D'. " Special case for HRC Software | |
| * /ahrc/cl_class_factory=>get_class( CHANGING co_object = instance ). | |
| CREATE OBJECT instance TYPE (class). | |
| ELSE. | |
| " We assume the GET_INSTANCE method always has a RETURNING parameter | |
| READ TABLE classdescr->methods ASSIGNING FIELD-SYMBOL(<getinstance_descr>) | |
| WITH TABLE KEY name = 'GET_INSTANCE'. " TYPE abap_methdescr | |
| IF sy-subrc <> 0. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_request. | |
| response-error-message = |Tool { request->get_name( ) } does not have a GET_INSTANCE method.| ##NO_TEXT. | |
| RETURN. | |
| ENDIF. | |
| READ TABLE <getinstance_descr>-parameters INTO DATA(getinstance_return) | |
| WITH KEY parm_kind = 'R'. | |
| IF sy-subrc <> 0. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_request. | |
| response-error-message = |Tool { request->get_name( ) } does not have a GET_INSTANCE method with a RETURNING parameter.| ##NO_TEXT. | |
| RETURN. | |
| ENDIF. | |
| DATA(constructor_params) = VALUE abap_parmbind_tab( | |
| ( name = getinstance_return-name | |
| kind = cl_abap_objectdescr=>receiving | |
| value = REF #( instance ) | |
| ) | |
| ). | |
| CALL METHOD (class)=>('GET_INSTANCE') | |
| PARAMETER-TABLE constructor_params. | |
| ENDIF. | |
| WHEN 2. " Public | |
| CREATE OBJECT instance TYPE (class) | |
| PARAMETER-TABLE constructor_params. | |
| ENDCASE. | |
| CATCH cx_sy_create_object_error INTO DATA(exception). | |
| " Handle creation error | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-internal_error. | |
| response-error-message = exception->get_text( ). | |
| RETURN. | |
| ENDTRY. | |
| WHEN 1. " Static method | |
| " No trouble | |
| ENDCASE. | |
| " - - - - - - - - - - - - - - | |
| " Method call | |
| " - - - - - - - - - - - - - - | |
| " Dynamically create a structure for the entire method signature | |
| LOOP AT <method_descr>-parameters ASSIGNING FIELD-SYMBOL(<parameter>). | |
| DATA(component) = VALUE abap_componentdescr( | |
| name = <parameter>-name | |
| type = classdescr->get_method_parameter_type( | |
| p_method_name = method | |
| p_parameter_name = <parameter>-name | |
| ) | |
| ). | |
| APPEND component TO components. | |
| IF <parameter>-parm_kind EQ cl_abap_objectdescr=>importing. | |
| APPEND component TO components_input. | |
| ELSE. | |
| APPEND component TO components_output. | |
| ENDIF. | |
| ENDLOOP. | |
| * signature = create_dynamic_structure( components ). | |
| input = create_dynamic_structure( components_input ). | |
| output = create_dynamic_structure( components_output ). | |
| ASSIGN: | |
| * signature->* TO <signature>, | |
| input->* TO <input>, | |
| output->* TO <output>. | |
| " Prepare the dynamic call to the method | |
| LOOP AT <method_descr>-parameters ASSIGNING <parameter>. | |
| UNASSIGN: <field>. | |
| CASE <parameter>-parm_kind. | |
| WHEN cl_abap_objectdescr=>importing. | |
| ASSIGN COMPONENT <parameter>-name OF STRUCTURE <input> TO <field>. | |
| WHEN OTHERS. | |
| ASSIGN COMPONENT <parameter>-name OF STRUCTURE <output> TO <field>. | |
| ENDCASE. | |
| INSERT VALUE #( | |
| name = <parameter>-name | |
| kind = COND #( WHEN <parameter>-parm_kind EQ cl_abap_objectdescr=>importing THEN cl_abap_objectdescr=>exporting | |
| WHEN <parameter>-parm_kind EQ cl_abap_objectdescr=>exporting THEN cl_abap_objectdescr=>importing | |
| WHEN <parameter>-parm_kind EQ cl_abap_objectdescr=>changing THEN cl_abap_objectdescr=>changing | |
| WHEN <parameter>-parm_kind EQ cl_abap_objectdescr=>returning THEN cl_abap_objectdescr=>receiving ) | |
| value = REF #( <field> ) | |
| ) INTO TABLE method_params. | |
| ENDLOOP. | |
| " Fill the input parameters | |
| arguments->to_abap( | |
| * EXPORTING iv_corresponding = abap_false | |
| IMPORTING ev_container = <input> | |
| ). | |
| " Dynamic method call | |
| CASE method_details-declaration_level. | |
| WHEN 0. " Instance method | |
| CALL METHOD instance->(method) | |
| PARAMETER-TABLE method_params. | |
| WHEN 1. " Static method | |
| CALL METHOD (class)=>(method) | |
| PARAMETER-TABLE method_params. | |
| ENDCASE. | |
| "-------------------- | |
| " JSON response | |
| "-------------------- | |
| DATA(json) = /ui2/cl_json=>serialize( | |
| EXPORTING | |
| data = output | |
| compress = abap_false " Skip empty elements | |
| ). | |
| " This should actually go to structured content (once implemented!) | |
| " see: https://github.com/abap-ai/mcp/issues/43 | |
| response-result->add_text_content( json ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_GET_PROMPT | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_GET_PROMPT | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>GET_PROMPT_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_get_prompt. | |
| DATA(arguments) = request->get_arguments( ). | |
| CASE request->get_name( ). | |
| * WHEN ``. | |
| * response-result->set_description( `` ) ##NO_TEXT. | |
| * response-result->add_text_message( role = zif_mcp_server=>role_user | |
| * text = || ) ##NO_TEXT. | |
| WHEN OTHERS. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_params. | |
| response-error-message = |Prompt { request->get_name( ) } unknown.| ##NO_TEXT. | |
| ENDCASE. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_INITIALIZE | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_INITIALIZE | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>INITIALIZE_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_initialize. | |
| " Capabilities | |
| response-result->set_capabilities( VALUE #( | |
| prompts = abap_true | |
| resources = abap_true | |
| tools = abap_true | |
| ) ). | |
| " Implementation | |
| response-result->set_implementation( VALUE #( | |
| name = `HRC Software - Dynamic MCP Server` | |
| version = `1.0.0` | |
| ) ). | |
| " Instructions | |
| response-result->set_instructions( `Use the features provided by this server only if explicitely requested. If not sure ask the user!` ) ##NO_TEXT. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_LIST_PROMPTS | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_LIST_PROMPTS | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>LIST_PROMPTS_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_list_prompts. | |
| " In this demo instance we only have two prompts, therefore | |
| " we do not consider cursor and max_list_results. | |
| * response-result->set_prompts( | |
| * VALUE #( | |
| * ( name = `progress` | |
| * description = `Asks the LLM to determine the progress of the Inventory campaign.` | |
| * arguments = VALUE #( ( name = `storage_bin` description = `Storage Bin to inventoriate` required = abap_false ) ) | |
| * ) | |
| * ) | |
| * ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_LIST_RESOURCES | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_LIST_RESOURCES | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>LIST_RESOURCES_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_list_resources. | |
| " Create static resources list | |
| * DATA(resources) = VALUE zcl_mcp_resp_list_resources=>resources( | |
| ** ( uri = 'abap://classes/zcl_my_utility' | |
| ** name = 'My Utility Class' | |
| ** description = 'Utility class for XYZ operations' | |
| ** mime_type = 'text/x-abap' ) | |
| * ( uri = 'users/current' | |
| * name = 'Current user profile' | |
| * description = 'Current user profile information (especially user parameters such as Warehouse Id)' | |
| * mime_type = 'application/json' ) | |
| * ). | |
| * response-result->set_resources( resources ) ##NO_TEXT. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_LIST_RES_TMPLS | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_LIST_RES_TMPLS | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>LIST_RESOURCES_TMPL_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_list_res_tmpls. | |
| " Create template list | |
| DATA(templates) = VALUE zcl_mcp_resp_list_res_tmpl=>resource_templates( | |
| " -- User profile | |
| * ( uritemplate = 'users/{username}/profile' | |
| * name = 'User Profile' | |
| * description = 'User profile information' | |
| * mime_type = 'application/json' ) | |
| ). | |
| " Set the templates in the response | |
| response-result->set_resource_templates( templates ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_LIST_TOOLS | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_LIST_TOOLS | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>LIST_TOOLS_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_list_tools. | |
| DATA: | |
| tools TYPE zcl_mcp_resp_list_tools=>tools. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| TRY. | |
| LOOP AT registered_methods ASSIGNING FIELD-SYMBOL(<registered_method>). | |
| DATA(class) = <registered_method>-clsname. | |
| DATA(method) = <registered_method>-cmpname. | |
| " Method details | |
| DATA(method_details) = get_method_details( | |
| class = class | |
| method = method | |
| ). | |
| " Method parameters | |
| DATA(schema) = build_dynamic_schema( | |
| class = class | |
| method = method | |
| ). | |
| APPEND VALUE #( name = |{ class }{ '|' }{ method }| | |
| description = method_details-description | |
| input_schema = schema->to_json( ) ) | |
| TO tools ##NO_TEXT. | |
| ENDLOOP. | |
| CATCH zcx_mcp_ajson_error INTO DATA(error). | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-internal_error. | |
| response-error-message = error->get_text( ). | |
| ENDTRY. | |
| response-result->set_tools( tools ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->HANDLE_RESOURCES_READ | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_READ_RESOURCE | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>RESOURCES_READ_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD handle_resources_read. | |
| TYPES: | |
| BEGIN OF ty_s_user_parameter, | |
| module TYPE string, | |
| parameter_id TYPE string, | |
| parameter_text TYPE string, | |
| parameter_value TYPE string, | |
| parameter_value_text TYPE string, | |
| END OF ty_s_user_parameter, | |
| ty_t_user_parameters TYPE STANDARD TABLE OF ty_s_user_parameter WITH NON-UNIQUE DEFAULT KEY. | |
| DATA: | |
| "------------------------------------------------------------------ | |
| " Tables, Structures | |
| "------------------------------------------------------------------ | |
| ls_search_parameter TYPE /ahrc/if_dao_cmn_user_params=>ty_s_user_context_search_in, | |
| "------------------------------------------------------------------ | |
| " Objects | |
| "------------------------------------------------------------------ | |
| lo_dao_cmn_user_params TYPE REF TO /ahrc/if_dao_cmn_user_params, | |
| "------------------------------------------------------------------ | |
| " Variables | |
| "------------------------------------------------------------------ | |
| json TYPE string. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| " Check if this is a dynamic resource | |
| DATA(uri) = request->get_uri( ). | |
| " Extract sales order ID from dynamic resource URI | |
| FIND '{' IN uri IN CHARACTER MODE MATCH COUNT DATA(nb_params). | |
| IF nb_params > 0. " Dynamic resource | |
| * CASE request->get_uri( ). | |
| * WHEN 'read'. | |
| * read( EXPORTING request = request CHANGING response = response ). | |
| * ENDCASE. | |
| ELSE. " Static resource | |
| ENDIF. | |
| IF json IS NOT INITIAL. | |
| response-result->add_text_resource( uri = request->get_uri( ) | |
| mime_type = `application/json` | |
| text = json ). | |
| ELSE. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_params. | |
| response-error-message = |Resource { request->get_uri( ) } not found.| ##NO_TEXT. | |
| ENDIF. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->READ | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_CALL_TOOL | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>CALL_TOOL_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD read. | |
| DATA: | |
| "---------------------------------------------------------------- | |
| " Objects | |
| "---------------------------------------------------------------- | |
| lo_dao_ewm_material TYPE REF TO /ahrc/if_dao_ewm_material. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| DATA(input) = request->get_arguments( ). | |
| "------------------------------------------------------------------ | |
| " Schema validation | |
| "------------------------------------------------------------------ | |
| " Validate input parameter via schema validator class | |
| TRY. | |
| DATA(schema) = read_schema( ). | |
| DATA(validator) = NEW zcl_mcp_schema_validator( schema->to_json( ) ). | |
| DATA(validation_result) = validator->validate( input ). | |
| IF validation_result = abap_false. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_params. | |
| response-error-message = concat_lines_of( validator->get_errors( ) ). | |
| RETURN. | |
| ENDIF. | |
| CATCH zcx_mcp_ajson_error INTO DATA(error). | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-internal_error. | |
| response-error-message = error->get_text( ). | |
| RETURN. | |
| ENDTRY. | |
| "------------------------------------------------------------------ | |
| " Perform call | |
| "------------------------------------------------------------------ | |
| " DAO Instantiation | |
| /ahrc/cl_class_factory=>get_class( CHANGING co_object = lo_dao_ewm_material ). | |
| IF lo_dao_ewm_material IS NOT BOUND. | |
| DATA(lv_interface_name) = /ahrc/cl_class_factory=>get_interface_name( ir_variable = REF #( lo_dao_ewm_material ) ). | |
| response-error-code = 'EDAO_NFD'. | |
| MESSAGE e002(/ahrc/clmsg_util) WITH lv_interface_name INTO response-error-message. | |
| RETURN. | |
| ENDIF. | |
| " Read | |
| DATA(warehouse_id) = input->get_string( `warehouse_id` ). | |
| DATA(material_guid) = input->get_string( `material_guid` ). | |
| DATA(material_id) = input->get_string( `material_id` ). | |
| lo_dao_ewm_material->read( | |
| EXPORTING | |
| it_material_guid = COND #( WHEN material_guid IS NOT INITIAL THEN VALUE #( ( material_guid ) ) ) | |
| it_material_id = COND #( WHEN material_id IS NOT INITIAL THEN VALUE #( ( material_id ) ) ) | |
| iv_warehouse = COND #( WHEN warehouse_id IS NOT INITIAL THEN warehouse_id ) | |
| IMPORTING | |
| et_material = DATA(lt_materials) | |
| es_return = DATA(ls_return) | |
| ). | |
| IF /ahrc/cl_util_common=>message_t_check_contains_error( ls_return-return_detail ) IS NOT INITIAL. | |
| LOOP AT ls_return-return_detail ASSIGNING FIELD-SYMBOL(<return_detail>) WHERE type CA 'EAX'. | |
| EXIT. | |
| ENDLOOP. | |
| IF sy-subrc EQ 0. | |
| response-result->add_text_content( |{ <return_detail>-message }| ) ##NO_TEXT. | |
| ENDIF. | |
| RETURN. | |
| ENDIF. | |
| " - - - - - - - - - - - - - | |
| " Markdown response | |
| " - - - - - - - - - - - - - | |
| * " Create markdown table | |
| * DATA(markdown) = |## Inventory Documents Details\n\n|. | |
| * | |
| * " Add table headers | |
| * markdown = |{ markdown }\| Document Year \| Document Id \| Document Type (text) \| Category \| Reason (text) \|\n| ##NO_TEXT. | |
| * markdown = |{ markdown }\|---------------\|-------------\|----------------------\|----------\|---------------\|\n| ##NO_TEXT. | |
| * | |
| * " Add table rows | |
| * LOOP AT ls_inventory-headers ASSIGNING FIELD-SYMBOL(<header>). | |
| * markdown = markdown && | |
| * |\| { <header>-document_year } \| { <header>-document_id } \| { <header>-document_type_text } \| { <header>-inventory_category } \| { <header>-reason_text } \|\n|. | |
| * ENDLOOP. | |
| * IF sy-subrc <> 0. | |
| * markdown = |{ markdown }\| No Inventory document found \|\n| ##NO_TEXT. | |
| * ENDIF. | |
| * | |
| * response-result->add_text_content( markdown ). | |
| " - - - - - - - - - - - - - | |
| " JSON response | |
| " - - - - - - - - - - - - - | |
| " This should actually go to structured content (once implemented!) | |
| " see: https://github.com/abap-ai/mcp/issues/43 | |
| DATA(json) = /ui2/cl_json=>serialize( | |
| EXPORTING | |
| data = lt_materials | |
| compress = abap_false " Skip empty elements | |
| * name = | |
| * pretty_name = | |
| * type_descr = | |
| ). | |
| response-result->add_text_content( json ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->READ_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [<-()] RESULT TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD read_schema. | |
| DATA(schema) = NEW zcl_mcp_schema_builder( ). | |
| " Warehouse | |
| schema->add_string( name = `warehouse_id` | |
| description = `Warehouse number` | |
| min_length = 4 | |
| max_length = 4 | |
| required = abap_true ). | |
| " Material Id | |
| schema->add_string( name = `material_id` | |
| description = `Material or Product Id` | |
| * min_length = 18 | |
| * max_length = 40 | |
| required = abap_true ). | |
| result = schema. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->SEARCH | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] REQUEST TYPE REF TO ZCL_MCP_REQ_CALL_TOOL | |
| * | [<-->] RESPONSE TYPE ZIF_MCP_SERVER=>CALL_TOOL_RESPONSE | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD search. | |
| DATA: | |
| "---------------------------------------------------------------- | |
| " Objects | |
| "---------------------------------------------------------------- | |
| lo_dao_ewm_material TYPE REF TO /ahrc/if_dao_ewm_material, | |
| ls_search_parameters TYPE /ahrc/if_dao_ewm_material=>ty_s_material_search_in. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| DATA(input) = request->get_arguments( ). | |
| "------------------------------------------------------------------ | |
| " Schema validation | |
| "------------------------------------------------------------------ | |
| " Validate input parameter via schema validator class | |
| TRY. | |
| DATA(schema) = search_schema( ). | |
| DATA(validator) = NEW zcl_mcp_schema_validator( schema->to_json( ) ). | |
| DATA(validation_result) = validator->validate( input ). | |
| IF validation_result = abap_false. | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-invalid_params. | |
| response-error-message = concat_lines_of( validator->get_errors( ) ). | |
| RETURN. | |
| ENDIF. | |
| CATCH zcx_mcp_ajson_error INTO DATA(error). | |
| response-error-code = zcl_mcp_jsonrpc=>error_codes-internal_error. | |
| response-error-message = error->get_text( ). | |
| RETURN. | |
| ENDTRY. | |
| "------------------------------------------------------------------ | |
| " Perform call | |
| "------------------------------------------------------------------ | |
| " DAO Instantiation | |
| /ahrc/cl_class_factory=>get_class( CHANGING co_object = lo_dao_ewm_material ). | |
| IF lo_dao_ewm_material IS NOT BOUND. | |
| DATA(lv_interface_name) = /ahrc/cl_class_factory=>get_interface_name( ir_variable = REF #( lo_dao_ewm_material ) ). | |
| response-error-code = 'EDAO_NFD'. | |
| MESSAGE e002(/ahrc/clmsg_util) WITH lv_interface_name INTO response-error-message. | |
| RETURN. | |
| ENDIF. | |
| " Search | |
| DATA(warehouse_id) = input->get_string( `warehouse_id` ). | |
| DATA(material_txt) = input->get_string( `material_txt` ). | |
| DATA(serial_no) = input->get_string( `serial_no` ). | |
| ls_search_parameters = VALUE #( | |
| * warehouse = COND #( WHEN warehouse_id IS NOT INITIAL | |
| * THEN VALUE #( ( sign = 'I' option = 'EQ' low = warehouse_id ) ) ) | |
| material_txt = COND #( WHEN material_txt IS NOT INITIAL | |
| * THEN VALUE #( ( sign = 'I' option = 'EQ' low = material_txt ) ) ) | |
| THEN VALUE #( ( sign = 'I' option = 'CP' low = |*{ material_txt }*| ) ) ) " Contains String logic | |
| * serial_no = COND #( WHEN serial_no IS NOT INITIAL | |
| * THEN VALUE #( ( sign = 'I' option = 'EQ' low = serial_no ) ) ) | |
| ). | |
| lo_dao_ewm_material->search( | |
| EXPORTING | |
| is_search_parameter = ls_search_parameters | |
| IMPORTING | |
| et_materials = DATA(lt_materials) | |
| es_return = DATA(ls_return) | |
| ). | |
| IF /ahrc/cl_util_common=>message_t_check_contains_error( ls_return-return_detail ) IS NOT INITIAL. | |
| LOOP AT ls_return-return_detail ASSIGNING FIELD-SYMBOL(<return_detail>) WHERE type CA 'EAX'. | |
| EXIT. | |
| ENDLOOP. | |
| IF sy-subrc EQ 0. | |
| response-result->add_text_content( |{ <return_detail>-message }| ) ##NO_TEXT. | |
| ENDIF. | |
| RETURN. | |
| ENDIF. | |
| " - - - - - - - - - - - - - | |
| " Markdown response | |
| " - - - - - - - - - - - - - | |
| * " Create markdown table | |
| * DATA(markdown) = |## Inventory Documents Details\n\n|. | |
| * | |
| * " Add table headers | |
| * markdown = |{ markdown }\| Document Year \| Document Id \| Document Type (text) \| Category \| Reason (text) \|\n| ##NO_TEXT. | |
| * markdown = |{ markdown }\|---------------\|-------------\|----------------------\|----------\|---------------\|\n| ##NO_TEXT. | |
| * | |
| * " Add table rows | |
| * LOOP AT ls_inventory-headers ASSIGNING FIELD-SYMBOL(<header>). | |
| * markdown = markdown && | |
| * |\| { <header>-document_year } \| { <header>-document_id } \| { <header>-document_type_text } \| { <header>-inventory_category } \| { <header>-reason_text } \|\n|. | |
| * ENDLOOP. | |
| * IF sy-subrc <> 0. | |
| * markdown = |{ markdown }\| No Inventory document found \|\n| ##NO_TEXT. | |
| * ENDIF. | |
| * | |
| * response-result->add_text_content( markdown ). | |
| " - - - - - - - - - - - - - | |
| " JSON response | |
| " - - - - - - - - - - - - - | |
| " This should actually go to structured content (once implemented!) | |
| " see: https://github.com/abap-ai/mcp/issues/43 | |
| DATA(json) = /ui2/cl_json=>serialize( | |
| EXPORTING | |
| data = lt_materials | |
| compress = abap_false " Skip empty elements | |
| * name = | |
| * pretty_name = | |
| * type_descr = | |
| ). | |
| response-result->add_text_content( json ). | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Private Method /AHRC/CL_MCP_DYNAMIC->SEARCH_SCHEMA | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [<-()] RESULT TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD search_schema. | |
| DATA(schema) = NEW zcl_mcp_schema_builder( ). | |
| " Warehouse (see ticket: AHRC-3878) | |
| schema->add_string( name = `warehouse_id` | |
| description = `Warehouse number` | |
| min_length = 4 | |
| max_length = 4 | |
| required = abap_false ). | |
| " Material text | |
| schema->add_string( name = `material_txt` | |
| description = `Description of the material or product` | |
| required = abap_false ). ##NO_TEXT | |
| " Serial number | |
| schema->add_string( name = `serial_no` | |
| description = `Serial number (only relevant if the material or product is serialized)` | |
| required = abap_false ). ##NO_TEXT | |
| result = schema. | |
| ENDMETHOD. | |
| * <SIGNATURE>---------------------------------------------------------------------------------------+ | |
| * | Instance Protected Method /AHRC/CL_MCP_DYNAMIC->_BUILD_DYNAMIC_SCHEMA_OLD | |
| * +-------------------------------------------------------------------------------------------------+ | |
| * | [--->] CLASS TYPE SEOCLSNAME | |
| * | [--->] METHOD TYPE SEOCMPNAME | |
| * | [--->] LANGUAGE TYPE SY-LANGU (default ='E') | |
| * | [<-()] SCHEMA TYPE REF TO ZCL_MCP_SCHEMA_BUILDER | |
| * +--------------------------------------------------------------------------------------</SIGNATURE> | |
| METHOD _BUILD_DYNAMIC_SCHEMA_OLD. | |
| DATA: | |
| "---------------------------------------------------------------- | |
| " Objects | |
| "---------------------------------------------------------------- | |
| typedescr TYPE REF TO cl_abap_typedescr, | |
| datadescr TYPE REF TO cl_abap_datadescr, | |
| structdescr TYPE REF TO cl_abap_structdescr, | |
| tabledescr TYPE REF TO cl_abap_tabledescr, | |
| elemdescr TYPE REF TO cl_abap_elemdescr, | |
| "---------------------------------------------------------------- | |
| " Variables | |
| "---------------------------------------------------------------- | |
| clsname TYPE seocompodf-clsname, | |
| cmpname TYPE seocompodf-cmpname. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| IF method CA '~'. " Class implements interface so use interface instead! | |
| SPLIT method AT '~' INTO clsname | |
| cmpname. | |
| ELSE. | |
| clsname = class. | |
| cmpname = method. | |
| ENDIF.. | |
| "================================================================== | |
| " Processing logic | |
| "================================================================== | |
| TRY. | |
| schema = NEW zcl_mcp_schema_builder( ). | |
| * schema->add_string( name = 'parameter' required = abap_true ) | |
| * ->add_integer( name = 'count' minimum = 1 ) | |
| * ->begin_object( name = 'options' ) | |
| * ->add_boolean( name = 'flag' ) | |
| * ->end_object( ). | |
| SELECT df~clsname, | |
| df~cmpname, | |
| df~sconame, | |
| df~pardecltyp, | |
| df~parpasstyp, | |
| df~typtype, | |
| df~type, | |
| df~paroptionl, | |
| tx~descript | |
| INTO TABLE @DATA(parameters) | |
| FROM seosubcodf AS df LEFT OUTER JOIN seosubcotx AS tx | |
| ON ( df~clsname EQ tx~clsname | |
| AND df~cmpname EQ tx~cmpname | |
| AND df~sconame EQ tx~sconame | |
| AND tx~langu EQ @language ) | |
| WHERE df~clsname EQ @clsname | |
| AND df~cmpname EQ @cmpname. | |
| CHECK sy-subrc EQ 0. | |
| DELETE parameters WHERE pardecltyp <> 0. | |
| LOOP AT parameters ASSIGNING FIELD-SYMBOL(<parameter>). " WHERE pardecltyp EQ 0. " 0 Importing | 1 Exporting | 2 Changing | 3 Returning | |
| typedescr = cl_abap_typedescr=>describe_by_name( <parameter>-type ). | |
| add_to_schema( EXPORTING name = |{ <parameter>-sconame }| | |
| description = |{ <parameter>-descript }| | |
| required = boolc( <parameter>-paroptionl IS NOT INITIAL ) | |
| typedescr = typedescr | |
| CHANGING schema = schema ). | |
| ENDLOOP. | |
| CATCH zcx_mcp_ajson_error. " JSON error | |
| ENDTRY. | |
| ENDMETHOD. | |
| ENDCLASS. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment