Sample code for 30+ languages & platforms
PowerBuilder 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 PowerBuilder Downloads

PowerBuilder
integer li_rc
integer li_Success
oleobject loo_Mcp
oleobject loo_Ai
oleobject loo_SbEventName
oleobject loo_SbDelta
oleobject loo_SbFullResponse
integer li_BConversationDone
integer li_BToolCallPending
integer li_BCaseSensitive
integer li_BAbort
integer li_LoopGuard
integer li_Result
oleobject loo_ConvoJson

li_Success = 0

//  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.
loo_Mcp = create oleobject
li_rc = loo_Mcp.ConnectToNewObject("Chilkat.Mcp")
if li_rc < 0 then
    destroy loo_Mcp
    MessageBox("Error","Connecting to COM object failed")
    return
end if
//  mcp.AuthToken = "...";   // <-- only if the server requires a bearer token
li_Success = loo_Mcp.Connect("https://mcp.deepwiki.com/mcp")
if li_Success = 0 then
    Write-Debug loo_Mcp.LastErrorText
    destroy loo_Mcp
    return
end if

Write-Debug "Connected to MCP server: " + loo_Mcp.ServerName + " (version " + loo_Mcp.ServerVersion + ")"

//  2) Set up the AI conversation and register the MCP tools.
loo_Ai = create oleobject
li_rc = loo_Ai.ConnectToNewObject("Chilkat.Ai")

loo_Ai.Provider = "anthropic"
//  The API key should come from a secure source rather than being hard-coded.
loo_Ai.ApiKey = "AI_PROVIDER_API_KEY"
loo_Ai.Model = "claude-sonnet-5"

//  Automatic tool use requires a conversation (not a stateless query).
li_Success = loo_Ai.NewConvo("test_conversation","You are a helpful assistant.  Use the available tools when they can help answer the question.","")
if li_Success = 0 then
    Write-Debug loo_Ai.LastErrorText
    destroy loo_Mcp
    destroy loo_Ai
    return
end if

//  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.
li_Success = loo_Ai.UseMcp(loo_Mcp,"deepwiki")
if li_Success = 0 then
    Write-Debug loo_Ai.LastErrorText
    destroy loo_Mcp
    destroy loo_Ai
    return
end if

//  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");

li_Success = loo_Ai.InputAddText("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.")
if li_Success = 0 then
    Write-Debug loo_Ai.LastErrorText
    destroy loo_Mcp
    destroy loo_Ai
    return
end if

//  3) Start streaming.
loo_Ai.Streaming = 1
li_Success = loo_Ai.Ask("text")
if li_Success = 0 then
    Write-Debug loo_Ai.LastErrorText
    destroy loo_Mcp
    destroy loo_Ai
    return
end if

//  4) Drive the streaming event loop.
loo_SbEventName = create oleobject
li_rc = loo_SbEventName.ConnectToNewObject("Chilkat.StringBuilder")

loo_SbDelta = create oleobject
li_rc = loo_SbDelta.ConnectToNewObject("Chilkat.StringBuilder")

loo_SbFullResponse = create oleobject
li_rc = loo_SbFullResponse.ConnectToNewObject("Chilkat.StringBuilder")

li_BConversationDone = 0
li_BToolCallPending = 0
li_BCaseSensitive = 1
li_BAbort = 0
li_LoopGuard = 0
li_Result = 0

do while (li_BConversationDone <> 1) AND (li_LoopGuard < 8000)
    li_LoopGuard = li_LoopGuard + 1
    li_Result = loo_Ai.PollAi(li_BAbort)
    if li_Result < 0 then
        Write-Debug loo_Ai.LastErrorText
        destroy loo_Mcp
        destroy loo_Ai
        destroy loo_SbEventName
        destroy loo_SbDelta
        destroy loo_SbFullResponse
        return
    end if

    if li_Result = 0 then
        //  No event ready yet.
        loo_Ai.SleepMs(100)
    else
        li_Success = loo_Ai.NextAiEvent(5000,loo_SbEventName,loo_SbDelta)
        if li_Success = 0 then
            Write-Debug loo_Ai.LastErrorText
            destroy loo_Mcp
            destroy loo_Ai
            destroy loo_SbEventName
            destroy loo_SbDelta
            destroy loo_SbFullResponse
            return
        end if

        if loo_SbEventName.ContentsEqual("js_function_call",li_BCaseSensitive) = 1 then
            //  (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.
            Write-Debug "[tool call requested]"
            Write-Debug loo_SbDelta.GetAsString()
            li_Success = loo_Ai.StreamingJsToolCall(loo_SbDelta)
            if li_Success = 0 then
                Write-Debug loo_Ai.LastErrorText
                destroy loo_Mcp
                destroy loo_Ai
                destroy loo_SbEventName
                destroy loo_SbDelta
                destroy loo_SbFullResponse
                return
            end if

            li_BToolCallPending = 1
        else
            if loo_SbEventName.ContentsEqual("null_terminator",li_BCaseSensitive) = 1 then
                //  (c) This streamed turn has finished.
                if li_BToolCallPending = 1 then
                    //  Continue the conversation so the model can use the tool result.  No new
                    //  InputAddText -- the tool result is already in the transcript.
                    li_BToolCallPending = 0
                    li_Success = loo_Ai.Ask("text")
                    if li_Success = 0 then
                        Write-Debug loo_Ai.LastErrorText
                        destroy loo_Mcp
                        destroy loo_Ai
                        destroy loo_SbEventName
                        destroy loo_SbDelta
                        destroy loo_SbFullResponse
                        return
                    end if

                else
                    li_BConversationDone = 1
                end if

            else
                //  (b) An "empty" event reports nothing this poll.  (d) Any other event is a normal
                //  streamed text delta, which we display and accumulate.
                if loo_SbEventName.ContentsEqual("empty",li_BCaseSensitive) <> 1 then
                    Write-Debug "Event: " + loo_SbEventName.GetAsString() + "  Delta: " + loo_SbDelta.GetAsString()
                    loo_SbFullResponse.AppendSb(loo_SbDelta)
                end if

            end if

        end if

    end if

loop

Write-Debug "Final streamed response:"
Write-Debug loo_SbFullResponse.GetAsString()
Write-Debug "----"

//  (Optional) The full transcript, including the tool call and its result.
loo_ConvoJson = create oleobject
li_rc = loo_ConvoJson.ConnectToNewObject("Chilkat.JsonObject")

loo_ConvoJson.EmitCompact = 0
li_Success = loo_Ai.ExportConvo("test_conversation",loo_ConvoJson)
if li_Success = 0 then
    Write-Debug loo_Ai.LastErrorText
    destroy loo_Mcp
    destroy loo_Ai
    destroy loo_SbEventName
    destroy loo_SbDelta
    destroy loo_SbFullResponse
    destroy loo_ConvoJson
    return
end if

Write-Debug "Full Conversation:"
Write-Debug loo_ConvoJson.Emit()

//  5) Close the MCP session.
li_Success = loo_Mcp.Close()
if li_Success = 0 then
    Write-Debug loo_Mcp.LastErrorText
    destroy loo_Mcp
    destroy loo_Ai
    destroy loo_SbEventName
    destroy loo_SbDelta
    destroy loo_SbFullResponse
    destroy loo_ConvoJson
    return
end if



destroy loo_Mcp
destroy loo_Ai
destroy loo_SbEventName
destroy loo_SbDelta
destroy loo_SbFullResponse
destroy loo_ConvoJson