vscode MCP support is already available!

Post Reply
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

vscode MCP support is already available!

Post by Antonio Linares »

From version 1.99.2 released April 10
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Harbour MCP server 64 bits (must be 64 bits)

go64.bat

Code: Select all | Expand

@setlocal
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64
c:\harbour\bin\win\msvc64\hbmk2 hbmcp.hbp -comp=msvc64
@endlocal
hbmcp.hbp

Code: Select all | Expand

hbmcp.prg

-lgdiplus
-lole32
-lOleDlg
-lversion
-lucrt
-luxtheme

xhb.hbc
hbct.hbc
hbwin.hbc
hbmzip.hbc
hbziparc.hbc
hbfoxpro.hbc

-ldflag=/NODEFAULTLIB:msvcrt 
-ldflag+=/NODEFAULTLIB:libucrt
hbmcp.prg

Code: Select all | Expand

PROCEDURE Main()

    LOCAL cInput, cResponse

    // Bucle para leer continuamente desde stdin
    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
            EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        // Procesar el mensaje
        hb_memoWrit( "c:\temp\stdin.txt", cInput )
        cResponse := ProcessMessage( cInput )
        IF !Empty( cResponse )
            StdOut( cResponse )
        ENDIF
    END

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId, cMethod, hParams, cToolName

    // Decodificar el JSON para obtener el método y el ID
    hJson := hb_jsonDecode( cInput )
    IF HB_ISHASH( hJson ) .AND. "method" $ hJson .AND. "id" $ hJson
        cMethod := hJson[ "method" ]
        cId := ExtractId( cInput )

        // Manejar el método "initialize"
        IF cMethod == "initialize"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2025-03-26",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '}' + ;
                '}' + ;
                '}' + hb_eol()

        // Manejar el método "tools/list"
        ELSEIF cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in YYYY-MM-DD HH:MM:SS format",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()

        // Manejar el método "tools/call"
        ELSEIF cMethod == "tools/call"
            hParams := hJson[ "params" ]
            IF HB_ISHASH( hParams ) .AND. "name" $ hParams .AND. "arguments" $ hParams
                cToolName := hParams[ "name" ]
                IF cToolName == "get_time" .AND. HB_ISHASH( hParams[ "arguments" ] ) .AND. Empty( hParams[ "arguments" ] )
                    // Obtener la hora actual en formato "YYYY-MM-DD HH:MM:SS"
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"result":"' + hb_DateTime() + '"' + ;
                        '}' + hb_eol()
                ELSEIF cToolName == "hb_version" .AND. HB_ISHASH( hParams[ "arguments" ] ) .AND. Empty( hParams[ "arguments" ] )
                    // Obtener la versión de Harbour
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"result":"' + StrTran( Version(), '"', '\"' ) + '"' + ;
                        '}' + hb_eol()
                ELSEIF cToolName == "hb_compiler" .AND. HB_ISHASH( hParams[ "arguments" ] ) .AND. Empty( hParams[ "arguments" ] )
                    // Obtener el nombre del compilador
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"result":"' + StrTran( hb_Compiler(), '"', '\"' ) + '"' + ;
                        '}' + hb_eol()
                ELSE
                    // Error: herramienta no encontrada o argumentos inválidos
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"error":{' + ;
                        '"code":-32602,' + ;
                        '"message":"Invalid params"' + ;
                        '}' + ;
                        '}' + hb_eol()
                ENDIF
            ELSE
                // Error: parámetros inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

        // Manejar métodos no soportados
        ELSE
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"error":{' + ;
                '"code":-32601,' + ;
                '"message":"Method not found"' + ;
                '}' + ;
                '}' + hb_eol()
        ENDIF
    ELSE
        // JSON inválido o sin método/ID
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":null,' + ;
            '"error":{' + ;
            '"code":-32700,' + ;
            '"message":"Parse error"' + ;
            '}' + ;
            '}' + hb_eol()
    ENDIF

RETURN cResponse

// Función para extraer el ID parseando el JSON con hb_jsonDecode()
STATIC FUNCTION ExtractId( cInput )
    LOCAL hJson, cId := '"1"'  // Valor por defecto si falla el parseo
    LOCAL xId

    // Decodificar el JSON en un hash
    hJson := hb_jsonDecode( cInput )
    IF HB_ISHASH( hJson ) .AND. "id" $ hJson
        xId := hJson[ "id" ]
        IF HB_ISSTRING( xId )
            // Si el id es string, incluir comillas
            cId := '"' + xId + '"'
        ELSEIF HB_ISNUMERIC( xId )
            // Si el id es numérico, convertir a string sin comillas
            cId := hb_ntos( xId )
        ELSEIF xId == NIL
            // Si el id es null, retornar "null"
            cId := "null"
        ENDIF
    ENDIF

RETURN cId

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Setting for vscode:

Code: Select all | Expand

    "mcp": {
        "servers": {
            "Harbour": {
                "type": "stdio",
                "command": "c:\\temp\\hbmcp.exe",
                "args": []
            }
        }
    }
Image
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Enhanced version:

hbmcp.prg

Code: Select all | Expand

#define LogFile( cKey, cValue ) hb_memoWrit( "c:\temp\hbmcp.log", hb_memoRead( "c:\temp\hbmcp.log" ) + hb_eol() + cKey + cValue )

PROCEDURE Main()

    LOCAL cInput, cResponse

    // Bucle para leer continuamente desde stdin
    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
            EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        // Procesar el mensaje
        hb_memoWrit( "c:\temp\hbmcp.log", hb_memoRead( "c:\temp\hbmcp.log" ) + hb_eol() + "in: " + cInput )
        cResponse := ProcessMessage( cInput )
        IF ! Empty( cResponse )
            hb_memoWrit( "c:\temp\hbmcp.log", hb_memoRead( "c:\temp\hbmcp.log" ) + hb_eol() + "out: " + cResponse )
            StdOut( cResponse )
        ENDIF
    END

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId := "1", cMethod, hParams, cToolName

    // Decodificar el JSON para obtener el método y el ID
    hb_jsonDecode( cInput, @hJson )
    cMethod = hJson[ "method" ]
    LogFile( "method: ", cMethod )
    // if hb_HHasKey( hJson, "id" )
    //     cId = hJson[ "id" ]
    // else
    //     cId = "null"  // Si no hay id, asignar null
    // endif
    LogFile( "id: ", cId )

    do case
        case cMethod == "initialize"
            LogFile( "initialize: ", cMethod )
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2025-03-26",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '}' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "notifications/initialized"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"result":{' + ;
                    '"protocolVersion":"2025-03-26",' + ;
                    '"capabilities":{' + ;
                    '"tools":{},' + ;
                    '"resources":{},' + ;
                    '"prompts":{}' + ;
                    '}' + ;
                    '}' + ;
                    '}' + hb_eol()

        case cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in YYYY-MM-DD HH:MM:SS format",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "tools/call"
            hParams := hJson[ "params" ]
            IF HB_ISHASH( hParams ) .AND. "name" $ hParams .AND. "arguments" $ hParams
                cToolName := hParams[ "name" ]
                IF cToolName == "get_time" .AND. HB_ISHASH( hParams[ "arguments" ] ) .AND. Empty( hParams[ "arguments" ] )
                    // Obtener la hora actual en formato "YYYY-MM-DD HH:MM:SS"
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"result":"' + hb_DateTime() + '"' + ;
                        '}' + hb_eol()
                ELSEIF cToolName == "hb_version" .AND. HB_ISHASH( hParams[ "arguments" ] ) .AND. Empty( hParams[ "arguments" ] )
                    // Obtener la versión de Harbour
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"result":"' + StrTran( Version(), '"', '\"' ) + '"' + ;
                        '}' + hb_eol()
                ELSEIF cToolName == "hb_compiler" .AND. HB_ISHASH( hParams[ "arguments" ] ) .AND. Empty( hParams[ "arguments" ] )
                    // Obtener el nombre del compilador
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"result":"' + StrTran( hb_Compiler(), '"', '\"' ) + '"' + ;
                        '}' + hb_eol()
                ELSE
                    // Error: herramienta no encontrada o argumentos inválidos
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"error":{' + ;
                        '"code":-32602,' + ;
                        '"message":"Invalid params"' + ;
                        '}' + ;
                        '}' + hb_eol()
                ENDIF
            ELSE
                // Error: parámetros inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

    // Manejar métodos no soportados
    otherwise
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":' + cId + ',' + ;
            '"error":{' + ;
            '"code":-32601,' + ;
            '"message":"Method not found"' + ;
            '}' + ;
            '}' + hb_eol()
    endcase

    hb_memoWrit( "c:\temp\hbmcp.log", hb_memoRead( "c:\temp\hbmcp.log" ) + hb_eol() + "response: " + cResponse )

RETURN cResponse

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Working version !!!! :D

hbmcp.prg

Code: Select all | Expand

#define LogFile( cKey, cValue ) hb_memoWrit( "c:\temp\hbmcp.log", hb_memoRead( "c:\temp\hbmcp.log" ) + hb_eol() + cKey + cValue )

PROCEDURE Main()

    LOCAL cInput, cResponse

    // Bucle para leer continuamente desde stdin
    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
           EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        // Procesar el mensaje
        LogFile( "in: ", cInput )
        cResponse := ProcessMessage( cInput )
        IF ! Empty( cResponse )
            LogFile( "out: ", cResponse )
            StdOut( cResponse )
        ENDIF
    END

    LogFile( "exit: ", "termina" )

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId, cMethod, hParams, cToolName

    // Decodificar el JSON para obtener el método y el ID
    hb_jsonDecode( cInput, @hJson )
    cMethod = hJson[ "method" ]
    LogFile( "method: ", cMethod )
    if hb_HHasKey( hJson, "id" )
        cId = AllTrim( Str( hJson[ "id" ] ) )
    endif

    do case
        case cMethod == "initialize"
            LogFile( "initialize: ", cMethod )
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2025-03-26",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '}' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "notifications/initialized"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"result":{' + ;
                    '"protocolVersion":"2025-03-26",' + ;
                    '"capabilities":{' + ;
                    '"tools":{},' + ;
                    '"resources":{},' + ;
                    '"prompts":{}' + ;
                    '}' + ;
                    '}' + ;
                    '}' + hb_eol()

        case cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in YYYY-MM-DD HH:MM:SS format",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "tools/call"
            cToolName = hJson[ "params" ][ "name" ]

            IF cToolName == "get_time" 
                // Obtener la hora actual en formato "YYYY-MM-DD HH:MM:SS"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Time() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_version"
                // Obtener la versión de Harbour
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Version() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_compiler"
                // Obtener el nombre del compilador
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + hb_Compiler() + '"}]}' + ;
                    '}' + hb_eol()
            ELSE
                // Error: herramienta no encontrada o argumentos inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

    // Manejar métodos no soportados
    otherwise
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":' + cId + ',' + ;
            '"error":{' + ;
            '"code":-32604,' + ;
            '"message":"Method not found"' + cMethod +;
            ' }' + ;
            '}' + hb_eol()
    endcase

RETURN cResponse

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Enhanced version:

hbmcp.prg

Code: Select all | Expand

#include "fileio.ch"

PROCEDURE Main()

    LOCAL cInput, cResponse

    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
           EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        LogFile( "in: ", cInput )
        cResponse := ProcessMessage( cInput )
        IF ! Empty( cResponse )
            LogFile( "out: ", cResponse )
            StdOut( cResponse )
        ENDIF
    END

    LogFile( "exit: ", "termina" )

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId, cMethod, hParams, cToolName

    // Decodificar el JSON para obtener el método y el ID
    hb_jsonDecode( cInput, @hJson )
    cMethod = hJson[ "method" ]
    if hb_HHasKey( hJson, "id" )
        cId = AllTrim( Str( hJson[ "id" ] ) )
    endif

    do case
        case cMethod == "initialize"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2025-03-26",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '}' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "notifications/initialized"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"result":{' + ;
                    '"protocolVersion":"2025-03-26",' + ;
                    '"capabilities":{' + ;
                    '"tools":{},' + ;
                    '"resources":{},' + ;
                    '"prompts":{}' + ;
                    '}' + ;
                    '}' + ;
                    '}' + hb_eol()

        case cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in YYYY-MM-DD HH:MM:SS format",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "tools/call"
            cToolName = hJson[ "params" ][ "name" ]

            IF cToolName == "get_time" 
                // Obtener la hora actual en formato "YYYY-MM-DD HH:MM:SS"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Time() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_version"
                // Obtener la versión de Harbour
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Version() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_compiler"
                // Obtener el nombre del compilador
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + hb_Compiler() + '"}]}' + ;
                    '}' + hb_eol()
            ELSE
                // Error: herramienta no encontrada o argumentos inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

    // Manejar métodos no soportados
    otherwise
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":' + cId + ',' + ;
            '"error":{' + ;
            '"code":-32604,' + ;
            '"message":"Method not found"' + cMethod +;
            ' }' + ;
            '}' + hb_eol()
    endcase

RETURN cResponse

FUNCTION LogFile( cKey, cValue )
    LOCAL nHandle, lSuccess
    // Considerar hacer la ruta configurable
    if ! File( "c:\temp\hbmcp.log" )
       // Crear el archivo si no existe
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE + FO_CREAT )
    else
       // Abrir el archivo para añadir contenido
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE )
    endif
    IF nHandle > 0
       FSeek( nHandle, 0, FS_END ) // Mover el puntero al final del archivo
       lSuccess = fWrite( nHandle, cKey + cValue + hb_eol() )
       fclose( nHandle )
       RETURN lSuccess // Opcional: retornar éxito/fallo
    ENDIF
 RETURN .F.

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

No errors with Claude desktop !!! Still we need some fine tunning but it properly loads ! :D

hbmcp.prg

Code: Select all | Expand

#include "fileio.ch"

PROCEDURE Main()

    LOCAL cInput, cResponse

    ErrorBlock( { | oError| LogFile( "error: ", oError:Description ) } )

    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
           EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        LogFile( "in: ", cInput )
        cResponse := ProcessMessage( cInput )
        IF ! Empty( cResponse )
            LogFile( "out: ", cResponse )
            StdOut( cResponse )
        ENDIF
    END

    LogFile( "exit: ", "termina" )

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId, cMethod, hParams, cToolName

    // Decodificar el JSON para obtener el método y el ID
    hb_jsonDecode( cInput, @hJson )
    cMethod = hJson[ "method" ]
    if hb_HHasKey( hJson, "id" )
        cId = AllTrim( Str( hJson[ "id" ] ) )
    endif

    do case
        case cMethod == "initialize"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2024-11-05",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '},' + ;
                '"serverInfo":{"name":"Harbour-xbase","version":"0.0.2"}' + ;  
                '}' + ;
                '}' + hb_eol()

        case cMethod == "notifications/initialized"
                cResponse := hb_eol()

        case cMethod == "resources/list"    
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2024-11-05",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '},' + ;
                '"serverInfo":{"name":"Harbour-xbase","version":"0.0.2"}' + ;  
                '}' + ;
                '}' + hb_eol()

        case cMethod == "prompts/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"prompts":{}' + ;
                '}' + ;
                '}' + hb_eol()        

        case cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in YYYY-MM-DD HH:MM:SS format",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment",' + ;
                '"schema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"additionalProperties":false,' + ;
                '"returns":{' + ;
                '"type":"string"' + ;
                '}' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "tools/call"
            cToolName = hJson[ "params" ][ "name" ]

            IF cToolName == "get_time" 
                // Obtener la hora actual en formato "YYYY-MM-DD HH:MM:SS"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Time() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_version"
                // Obtener la versión de Harbour
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Version() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_compiler"
                // Obtener el nombre del compilador
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + hb_Compiler() + '"}]}' + ;
                    '}' + hb_eol()
            ELSE
                // Error: herramienta no encontrada o argumentos inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

    // Manejar métodos no soportados
    otherwise
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":' + cId + ',' + ;
            '"error":{' + ;
            '"code":-32604,' + ;
            '"message":"Method not found"' + cMethod +;
            ' }' + ;
            '}' + hb_eol()
    endcase

RETURN cResponse

FUNCTION LogFile( cKey, cValue )
    LOCAL nHandle, lSuccess
    // Considerar hacer la ruta configurable
    if ! File( "c:\temp\hbmcp.log" )
       // Crear el archivo si no existe
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE + FO_CREAT )
    else
       // Abrir el archivo para añadir contenido
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE )
    endif
    IF nHandle > 0
       FSeek( nHandle, 0, FS_END ) // Mover el puntero al final del archivo
       lSuccess = fWrite( nHandle, cKey + cValue + hb_eol() )
       fclose( nHandle )
       RETURN lSuccess // Opcional: retornar éxito/fallo
    ENDIF
 RETURN .F.

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Working fine with Claude Desktop !!!!!!!!! :D :D :D

hbmcp.prg

Code: Select all | Expand

#include "fileio.ch"

PROCEDURE Main()

    LOCAL cInput, cResponse

    ErrorBlock( { | oError| LogFile( "error: ", oError:Description ) } )

    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
           EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        LogFile( "in: ", cInput )
        cResponse := ProcessMessage( cInput )
        IF ! Empty( cResponse )
            LogFile( "out: ", cResponse )
            StdOut( cResponse )
        ENDIF
    END

    LogFile( "exit: ", "termina" )

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId, cMethod, hParams, cToolName

    // Decodificar el JSON para obtener el método y el ID
    hb_jsonDecode( cInput, @hJson )
    cMethod = hJson[ "method" ]
    if hb_HHasKey( hJson, "id" )
        cId = AllTrim( Str( hJson[ "id" ] ) )
    endif

    do case
        case cMethod == "initialize"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2024-11-05",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '},' + ;
                '"serverInfo":{"name":"Harbour-xbase","version":"0.0.2"}' + ;  
                '}' + ;
                '}' + hb_eol()

        case cMethod == "notifications/initialized"
                cResponse := hb_eol()

        case cMethod == "resources/list"    
            cResponse := ;
                '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{"code":-32601,"message":"Method not found"}' + ;
                '}' + hb_eol()           

        case cMethod == "prompts/list"
            cResponse := ;
                '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{"code":-32601,"message":"Method not found"}' + ;
                '}' + hb_eol()           

        case cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in YYYY-MM-DD HH:MM:SS format. Useful for logging or timestamping operations within the Harbour runtime environment.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"required":[],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment. Use this to verify compatibility or for debugging purposes.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"required":[],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment. Helpful for diagnosing build-related issues.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"required":[],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()
            
        case cMethod == "tools/call"
            cToolName = hJson[ "params" ][ "name" ]

            IF cToolName == "get_time" 
                // Obtener la hora actual en formato "YYYY-MM-DD HH:MM:SS"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Time() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_version"
                // Obtener la versión de Harbour
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Version() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_compiler"
                // Obtener el nombre del compilador
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + hb_Compiler() + '"}]}' + ;
                    '}' + hb_eol()
            ELSE
                // Error: herramienta no encontrada o argumentos inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

    // Manejar métodos no soportados
    otherwise
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":' + cId + ',' + ;
            '"error":{' + ;
            '"code":-32604,' + ;
            '"message":"Method not found"' + cMethod +;
            ' }' + ;
            '}' + hb_eol()
    endcase

RETURN cResponse

FUNCTION LogFile( cKey, cValue )
    LOCAL nHandle, lSuccess
    // Considerar hacer la ruta configurable
    if ! File( "c:\temp\hbmcp.log" )
       // Crear el archivo si no existe
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE + FO_CREAT )
    else
       // Abrir el archivo para añadir contenido
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE )
    endif
    IF nHandle > 0
       FSeek( nHandle, 0, FS_END ) // Mover el puntero al final del archivo
       lSuccess = fWrite( nHandle, cKey + cValue + hb_eol() )
       fclose( nHandle )
       RETURN lSuccess // Opcional: retornar éxito/fallo
    ENDIF
 RETURN .F.

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

Image

Image

Image
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 42837
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 170 times
Been thanked: 123 times
Contact:

Re: vscode MCP support is already available!

Post by Antonio Linares »

New hb_macro implemented tool:

hbmcp.prg

Code: Select all | Expand

#include "fileio.ch"

PROCEDURE Main()

    LOCAL cInput, cResponse

    ErrorBlock( { | oError| LogFile( "error: ", oError:Description ) } )

    WHILE .T.
        cInput := StdIn()
        IF Empty( cInput )
           EXIT  // Salir si no hay entrada (EOF)
        ENDIF

        LogFile( "in: ", cInput )
        cResponse := ProcessMessage( cInput )
        IF ! Empty( cResponse )
            LogFile( "out: ", cResponse )
            StdOut( cResponse )
        ENDIF
    END

    LogFile( "exit: ", "termina" )

RETURN

// Función para procesar mensajes JSON-RPC
FUNCTION ProcessMessage( cInput )
    LOCAL cResponse := ""
    LOCAL hJson, cId, cMethod, hParams, cToolName
    LOCAL cCode, xResult, cResult

    // Decodificar el JSON para obtener el método y el ID
    hb_jsonDecode( cInput, @hJson )
    cMethod = hJson[ "method" ]
    if hb_HHasKey( hJson, "id" )
        cId = AllTrim( Str( hJson[ "id" ] ) )
    endif

    do case
        case cMethod == "initialize"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"protocolVersion":"2024-11-05",' + ;
                '"capabilities":{' + ;
                '"tools":{},' + ;
                '"resources":{},' + ;
                '"prompts":{}' + ;
                '},' + ;
                '"serverInfo":{"name":"Harbour-xbase","version":"0.0.2"}' + ;  
                '}' + ;
                '}' + hb_eol()

        case cMethod == "notifications/initialized"
                cResponse := hb_eol()

        case cMethod == "resources/list"    
            cResponse := ;
                '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{"code":-32601,"message":"Method not found"}' + ;
                '}' + hb_eol()           

        case cMethod == "prompts/list"
            cResponse := ;
                '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{"code":-32601,"message":"Method not found"}' + ;
                '}' + hb_eol()           

        case cMethod == "tools/list"
            cResponse := ;
                '{' + ;
                '"jsonrpc":"2.0",' + ;
                '"id":' + cId + ',' + ;
                '"result":{' + ;
                '"tools": [' + ;
                '{' + ;
                '"name":"get_time",' + ;
                '"description":"Returns the current system time in HH:MM:SS format. Useful for logging or timestamping operations within the Harbour runtime environment.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"required":[],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_version",' + ;
                '"description":"Returns the version of the Harbour runtime environment. Use this to verify compatibility or for debugging purposes.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"required":[],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '},' + ;
                '{' + ;
                '"name":"hb_compiler",' + ;
                '"description":"Returns the name of the compiler used to build the Harbour runtime environment. Helpful for diagnosing build-related issues.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{},' + ;
                '"required":[],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '},' + ;
                '{' + ;  // Nueva herramienta hb_macro
                '"name":"hb_macro",' + ;
                '"description":"Executes a Harbour macro expression provided as a string and returns the result as a string. Useful for dynamic code execution within the Harbour runtime environment.",' + ;
                '"inputSchema":{' + ;
                '"type":"object",' + ;
                '"properties":{"code":{"type":"string"}},' + ;
                '"required":["code"],' + ;
                '"additionalProperties":false,' + ;
                '"$schema":"http://json-schema.org/draft-07/schema#"' + ;
                '}' + ;
                '}' + ;
                ']' + ;
                '}' + ;
                '}' + hb_eol()

        case cMethod == "tools/call"
            cToolName = hJson[ "params" ][ "name" ]

            IF cToolName == "get_time" 
                // Obtener la hora actual en formato "HH:MM:SS"
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Time() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_version"
                // Obtener la versión de Harbour
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + Version() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_compiler"
                // Obtener el nombre del compilador
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"result":{"content":[{"type":"text","text":"' + hb_Compiler() + '"}]}' + ;
                    '}' + hb_eol()
            ELSEIF cToolName == "hb_macro"
                // Nueva herramienta: ejecutar macro y devolver resultado como cadena
                // Verificar que el parámetro "code" existe y es una cadena
                IF hb_HHasKey( hJson[ "params" ], "arguments" ) .AND. ;
                   hb_HHasKey( hJson[ "params" ][ "arguments" ], "code" ) .AND. ;
                   ValType( hJson[ "params" ][ "arguments" ][ "code" ] ) == "C"
                    cCode := hJson[ "params" ][ "arguments" ][ "code" ]
                    // Ejecutar la macro y convertir el resultado a cadena
                    BEGIN SEQUENCE
                        xResult := &( cCode )
                        cResult := hb_ValToExp( xResult )
                        cResponse := ;
                            '{' + ;
                            '"jsonrpc":"2.0",' + ;
                            '"id":' + cId + ',' + ;
                            '"result":{"content":[{"type":"text","text":"' + cResult + '"}]}' + ;
                            '}' + hb_eol()
                        cResponse := StrTran( cResponse, '""', '"' )
                    RECOVER
                        // En caso de error en la macro, devolver un error JSON-RPC
                        cResponse := ;
                            '{' + ;
                            '"jsonrpc":"2.0",' + ;
                            '"id":' + cId + ',' + ;
                            '"error":{"code":-32602,"message":"Invalid macro expression"}' + ;
                            '}' + hb_eol()
                    END SEQUENCE
                ELSE
                    // Error: parámetro "code" no proporcionado o inválido
                    cResponse := ;
                        '{' + ;
                        '"jsonrpc":"2.0",' + ;
                        '"id":' + cId + ',' + ;
                        '"error":{"code":-32602,"message":"Missing or invalid code parameter"}' + ;
                        '}' + hb_eol()
                ENDIF                    
            ELSE
                // Error: herramienta no encontrada o argumentos inválidos
                cResponse := ;
                    '{' + ;
                    '"jsonrpc":"2.0",' + ;
                    '"id":' + cId + ',' + ;
                    '"error":{' + ;
                    '"code":-32602,' + ;
                    '"message":"Invalid params"' + ;
                    '}' + ;
                    '}' + hb_eol()
            ENDIF

    // Manejar métodos no soportados
    otherwise
        cResponse := ;
            '{' + ;
            '"jsonrpc":"2.0",' + ;
            '"id":' + cId + ',' + ;
            '"error":{' + ;
            '"code":-32604,' + ;
            '"message":"Method not found"' + cMethod +;
            ' }' + ;
            '}' + hb_eol()
    endcase

RETURN cResponse

FUNCTION LogFile( cKey, cValue )
    LOCAL nHandle, lSuccess
    // Considerar hacer la ruta configurable
    if ! File( "c:\temp\hbmcp.log" )
       // Crear el archivo si no existe
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE + FO_CREAT )
    else
       // Abrir el archivo para añadir contenido
       nHandle := fopen( "c:\temp\hbmcp.log", FO_WRITE )
    endif
    IF nHandle > 0
       FSeek( nHandle, 0, FS_END ) // Mover el puntero al final del archivo
       lSuccess = fWrite( nHandle, cKey + cValue + hb_eol() )
       fclose( nHandle )
       RETURN lSuccess // Opcional: retornar éxito/fallo
    ENDIF
 RETURN .F.

#pragma BEGINDUMP

#include <hbapi.h>

HB_FUNC( STDIN )
{
   char buffer[ 1024 ];
      
   if( fgets( buffer, sizeof( buffer ), stdin ) != NULL )
   {
      // Eliminar salto de línea final, si existe
      size_t len = strlen( buffer );
      if( len > 0 && buffer[ len - 1 ] == '\n' )
         buffer[ len - 1 ] = '\0';
      hb_retc( buffer );      
   }
   else
   {
      hb_retc( "" );  // Retornar cadena vacía en caso de EOF
   }
}

HB_FUNC( STDOUT )
{
   if( HB_ISCHAR( 1 ) )
   {
      fputs( hb_parc( 1 ), stdout );
      fflush( stdout );       // Forzar la escritura inmediata
   }
}

#pragma ENDDUMP
regards, saludos

Antonio Linares
www.fivetechsoft.com
Post Reply