Sample code for 30+ languages & platforms
DataFlex Requires Chilkat v11.6.0+

Streaming End-to-End MCP Tool Use with an AI Model

See more AI Examples

Demonstrates Ai.UseMcp in a streaming Model Context Protocol (MCP) workflow. This is the same idea as the non-streaming MCP example, but the answer is streamed token-by-token and the automatic MCP tool call is driven by the application's event loop rather than happening invisibly inside Ask.

A tool-using conversation spans more than one streamed turn. In the first turn the model streams a js_function_call event; the example hands that event's JSON to StreamingJsToolCall, which routes MCP tools to the server and appends the result to the transcript, and the turn ends with null_terminator. The example then calls Ask again (with no new input, since the tool result is already in the transcript) so the model can continue and stream its final answer. The loop ends when a streamed turn finishes with no tool call outstanding.

Tip: Code to parse the returned JSON can be generated with Chilkat's online tool at https://tools.chilkat.io/jsonParse.

Background. Automatic tool use requires a conversation (created with NewConvo); it cannot be used with a stateless query. The event loop polls with PollAi and reads events with NextAiEvent: a js_function_call event carries a tool request, empty means nothing to report this poll, and null_terminator marks the end of a streamed turn. The Mcp object must stay connected and in scope for the whole conversation. This example uses DeepWiki, a free, public, no-authentication Streamable HTTP MCP server; swap in any URL, setting AuthToken before Connect if a bearer token is required.
Using multiple MCP servers. More than one MCP server can be used in the same conversation. Connect each server and call UseMcp once per server — one call per MCP server — giving each a distinct prefix (for example deepwiki, weather, github) so tools from different servers never collide.

Chilkat DataFlex Downloads

DataFlex
Use ChilkatAx-win32.pkg

Procedure Test
    Boolean iSuccess
    Variant vMcp
    Handle hoMcp
    Handle hoAi
    Variant vSbEventName
    Handle hoSbEventName
    Variant vSbDelta
    Handle hoSbDelta
    Handle hoSbFullResponse
    Boolean iBConversationDone
    Boolean iBToolCallPending
    Boolean iBCaseSensitive
    Boolean iBAbort
    Integer iLoopGuard
    Integer iResult
    Variant vConvoJson
    Handle hoConvoJson
    String sTemp1
    String sTemp2
    Boolean bTemp1

    Move False To iSuccess

    //  Streaming, end-to-end MCP example.  Same idea as the non-streaming MCP example, but the answer is
    //  streamed token-by-token, and the automatic MCP tool call is driven by the application's event loop
    //  instead of happening invisibly inside Ask.
    //  
    //  A tool-using conversation spans more than one streamed turn:
    //  
    //    turn 1:  the model streams a "js_function_call" event (it wants a tool).  We hand that event's
    //             JSON to StreamingJsToolCall, which routes MCP tools to the server (Mcp.CallTool) and
    //             appends the result to the transcript.  The turn then ends with "null_terminator".
    //    turn 2:  we call Ask again (no new input -- the tool result is already in the transcript) so the
    //             model can continue.  It streams the final answer and ends with "null_terminator", with
    //             no pending tool call.
    //  
    //  The loop ends when a streamed turn finishes with no tool call outstanding.
    //  
    //  MCP server: DeepWiki -- a free, public, no-authentication Streamable HTTP server that answers
    //  questions about public GitHub repositories.  Swap in any URL; set mcp.AuthToken before Connect if
    //  it needs a bearer token.

    //  1) Connect to the MCP server.
    Get Create (RefClass(cComChilkatMcp)) To hoMcp
    If (Not(IsComObjectCreated(hoMcp))) Begin
        Send CreateComObject of hoMcp
    End
    //  mcp.AuthToken = "...";   // <-- only if the server requires a bearer token
    Get ComConnect Of hoMcp "https://mcp.deepwiki.com/mcp" To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoMcp To sTemp1
        Showln sTemp1
        Procedure_Return
    End

    Get ComServerName Of hoMcp To sTemp1
    Get ComServerVersion Of hoMcp To sTemp2
    Showln "Connected to MCP server: " sTemp1 " (version " sTemp2 ")"

    //  2) Set up the AI conversation and register the MCP tools.
    Get Create (RefClass(cComChilkatAi)) To hoAi
    If (Not(IsComObjectCreated(hoAi))) Begin
        Send CreateComObject of hoAi
    End
    Set ComProvider Of hoAi To "anthropic"
    //  The API key should come from a secure source rather than being hard-coded.
    Set ComApiKey Of hoAi To "AI_PROVIDER_API_KEY"
    Set ComModel Of hoAi To "claude-sonnet-5"

    //  Automatic tool use requires a conversation (not a stateless query).
    Get ComNewConvo Of hoAi "test_conversation" "You are a helpful assistant.  Use the available tools when they can help answer the question." "" To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoAi To sTemp1
        Showln sTemp1
        Procedure_Return
    End

    //  Namespace the server's tools with "deepwiki" -- e.g. "deepwiki_ask_question".  The Mcp object must
    //  stay connected and in scope for the whole conversation.
    Get pvComObject of hoMcp to vMcp
    Get ComUseMcp Of hoAi vMcp "deepwiki" To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoAi To sTemp1
        Showln sTemp1
        Procedure_Return
    End

    //  Multiple MCP servers can be used in the same conversation.  Connect each server and call UseMcp
    //  once per server, giving each a distinct prefix so their tool names cannot collide.  For example:
    //      success = ai.UseMcp(mcpWeather,"weather");
    //      success = ai.UseMcp(mcpGitHub,"github");

    Get ComInputAddText Of hoAi 'Use the available tools to look up the GitHub repository "modelcontextprotocol/modelcontextprotocol", then give me a brief, sourced summary of which transports the Model Context Protocol defines.' To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoAi To sTemp1
        Showln sTemp1
        Procedure_Return
    End

    //  3) Start streaming.
    Set ComStreaming Of hoAi To True
    Get ComAsk Of hoAi "text" To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoAi To sTemp1
        Showln sTemp1
        Procedure_Return
    End

    //  4) Drive the streaming event loop.
    Get Create (RefClass(cComChilkatStringBuilder)) To hoSbEventName
    If (Not(IsComObjectCreated(hoSbEventName))) Begin
        Send CreateComObject of hoSbEventName
    End
    Get Create (RefClass(cComChilkatStringBuilder)) To hoSbDelta
    If (Not(IsComObjectCreated(hoSbDelta))) Begin
        Send CreateComObject of hoSbDelta
    End
    Get Create (RefClass(cComChilkatStringBuilder)) To hoSbFullResponse
    If (Not(IsComObjectCreated(hoSbFullResponse))) Begin
        Send CreateComObject of hoSbFullResponse
    End
    Move False To iBConversationDone
    Move False To iBToolCallPending
    Move True To iBCaseSensitive
    Move False To iBAbort
    Move 0 To iLoopGuard
    Move 0 To iResult

    While ((iBConversationDone <> True) And (iLoopGuard < 8000))
        Move (iLoopGuard + 1) To iLoopGuard
        Get ComPollAi Of hoAi iBAbort To iResult
        If (iResult < 0) Begin
            Get ComLastErrorText Of hoAi To sTemp1
            Showln sTemp1
            Procedure_Return
        End

        If (iResult = 0) Begin
            //  No event ready yet.
            Send ComSleepMs To hoAi 100
        End
        Else Begin
            Get pvComObject of hoSbEventName to vSbEventName
            Get pvComObject of hoSbDelta to vSbDelta
            Get ComNextAiEvent Of hoAi 5000 vSbEventName vSbDelta To iSuccess
            If (iSuccess = False) Begin
                Get ComLastErrorText Of hoAi To sTemp1
                Showln sTemp1
                Procedure_Return
            End

            Get ComContentsEqual Of hoSbEventName "js_function_call" iBCaseSensitive To bTemp1
            If (bTemp1 = True) Begin
                //  (a) The model is requesting a tool call.  sbDelta holds the function-call JSON.
                //  StreamingJsToolCall runs the tool -- routing MCP tools to the server -- and appends the
                //  result to the transcript.  It must be called before this turn's null_terminator.
                Showln "[tool call requested]"
                Get ComGetAsString Of hoSbDelta To sTemp1
                Showln sTemp1
                Get pvComObject of hoSbDelta to vSbDelta
                Get ComStreamingJsToolCall Of hoAi vSbDelta To iSuccess
                If (iSuccess = False) Begin
                    Get ComLastErrorText Of hoAi To sTemp1
                    Showln sTemp1
                    Procedure_Return
                End

                Move True To iBToolCallPending
            End
            Else Begin
                Get ComContentsEqual Of hoSbEventName "null_terminator" iBCaseSensitive To bTemp1
                If (bTemp1 = True) Begin
                    //  (c) This streamed turn has finished.
                    If (iBToolCallPending = True) Begin
                        //  Continue the conversation so the model can use the tool result.  No new
                        //  InputAddText -- the tool result is already in the transcript.
                        Move False To iBToolCallPending
                        Get ComAsk Of hoAi "text" To iSuccess
                        If (iSuccess = False) Begin
                            Get ComLastErrorText Of hoAi To sTemp1
                            Showln sTemp1
                            Procedure_Return
                        End

                    End
                    Else Begin
                        Move True To iBConversationDone
                    End

                End
                Else Begin
                    //  (b) An "empty" event reports nothing this poll.  (d) Any other event is a normal
                    //  streamed text delta, which we display and accumulate.
                    Get ComContentsEqual Of hoSbEventName "empty" iBCaseSensitive To bTemp1
                    If (bTemp1 <> True) Begin
                        Get ComGetAsString Of hoSbEventName To sTemp1
                        Get ComGetAsString Of hoSbDelta To sTemp2
                        Showln "Event: " sTemp1 "  Delta: " sTemp2
                        Get pvComObject of hoSbDelta to vSbDelta
                        Get ComAppendSb Of hoSbFullResponse vSbDelta To iSuccess
                    End

                End

            End

        End

    Loop

    Showln "Final streamed response:"
    Get ComGetAsString Of hoSbFullResponse To sTemp1
    Showln sTemp1
    Showln "----"

    //  (Optional) The full transcript, including the tool call and its result.
    Get Create (RefClass(cComChilkatJsonObject)) To hoConvoJson
    If (Not(IsComObjectCreated(hoConvoJson))) Begin
        Send CreateComObject of hoConvoJson
    End
    Set ComEmitCompact Of hoConvoJson To False
    Get pvComObject of hoConvoJson to vConvoJson
    Get ComExportConvo Of hoAi "test_conversation" vConvoJson To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoAi To sTemp1
        Showln sTemp1
        Procedure_Return
    End

    Showln "Full Conversation:"
    Get ComEmit Of hoConvoJson To sTemp1
    Showln sTemp1

    //  5) Close the MCP session.
    Get ComClose Of hoMcp To iSuccess
    If (iSuccess = False) Begin
        Get ComLastErrorText Of hoMcp To sTemp1
        Showln sTemp1
        Procedure_Return
    End



End_Procedure