diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog for langchain-hs-openai
 
+## 0.0.6.2 - 2026-11-14
+
+> **Note**: `0.0.6.1` was skipped globally after `langchain-hs-ollama-v0.0.6.1`.
+
+- Added incremental SSE tool-call delta accumulation and `LLMChunk "" (Just toolCall)` emission in `ChatModel.stream`.
+- Coordinated monorepo release alignment to `0.0.6.2` and updated `langchain-hs-core` dependency bound to `>= 0.0.6.2 && < 0.0.7`.
+
 ## 0.0.6.0 - 2026-10-02
 
 - Initial release. Split from the monolithic `langchain-hs` package.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 
 ```yaml
 # stack.yaml extra-deps
-- langchain-hs-openai-0.0.6.0
+- langchain-hs-openai-0.0.6.2
 ```
 
 ```yaml
diff --git a/langchain-hs-openai.cabal b/langchain-hs-openai.cabal
--- a/langchain-hs-openai.cabal
+++ b/langchain-hs-openai.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           langchain-hs-openai
-version:        0.0.6.0
+version:        0.0.6.2
 synopsis:       OpenAI provider and embeddings for langchain-hs
 description:    OpenAI ChatModel provider, streaming support, structured output, and text embeddings integration for langchain-hs. Compatible with OpenAI and OpenAI-compatible endpoints (OpenRouter, Fireworks, Together AI).
 category:       AI
@@ -49,7 +49,7 @@
     , http-client-tls >=0.3 && <0.5
     , http-conduit ==2.*
     , http-types >=0.11 && <0.13
-    , langchain-hs-core ==0.0.6.*
+    , langchain-hs-core >=0.0.6.2 && <0.0.7
     , mtl >=2.2 && <2.4
     , openai >=2.2.1 && <3
     , servant >=0.19 && <0.21
diff --git a/src/Langchain/Provider/OpenAI.hs b/src/Langchain/Provider/OpenAI.hs
--- a/src/Langchain/Provider/OpenAI.hs
+++ b/src/Langchain/Provider/OpenAI.hs
@@ -201,7 +201,7 @@
 
 instance Aeson.FromJSON OpenAIStreamToolCall where
   parseJSON = Aeson.withObject "OpenAIStreamToolCall" $ \obj -> do
-    streamToolCallIndex <- obj Aeson..: "index"
+    streamToolCallIndex <- obj Aeson..:? "index" Aeson..!= 0
     streamToolCallId <- obj Aeson..:? "id"
     streamFunction <- obj Aeson..:? "function"
     let streamToolCallName = streamFunction >>= parseMaybe (Aeson..: "name")
@@ -327,7 +327,9 @@
             , CC.refusal = Nothing
             , CC.name = messageName msg
             , CC.assistant_audio = Nothing
-            , CC.tool_calls = V.fromList . map toOAIToolCall <$> messageToolCalls msg
+            , CC.tool_calls = case messageToolCalls msg of
+                Just tcs@(_ : _) -> Just $ V.fromList (map toOAIToolCall tcs)
+                _ -> Nothing
             }
         Tool ->
           CC.Tool
@@ -453,13 +455,15 @@
               Map.alter (Just . update) streamToolCallIndex toolCalls'
               where
                 update curr =
-                  let prevId = curr >>= partialToolCallId
+                  let nonEmptyTxt (Just t) | not (T.null t) = Just t
+                      nonEmptyTxt _ = Nothing
+                      prevId = curr >>= partialToolCallId
                       prevName = curr >>= partialToolCallName
                       prevArgs = maybe "" partialToolCallArguments curr
                       nextArgs = fromMaybe "" streamToolCallArguments
                    in PartialToolCall
-                        { partialToolCallId = streamToolCallId <|> prevId
-                        , partialToolCallName = streamToolCallName <|> prevName
+                        { partialToolCallId = nonEmptyTxt streamToolCallId <|> prevId
+                        , partialToolCallName = nonEmptyTxt streamToolCallName <|> prevName
                         , partialToolCallArguments = prevArgs <> nextArgs
                         }
 
@@ -479,10 +483,13 @@
                 toolCallName <-
                   fromMaybeOrThrow "OpenAI stream ended with a tool call missing a function name" partialToolCallName
                 toolCallArguments <-
-                  either
-                    (throwLlmError . ("Invalid JSON arguments in OpenAI tool call: " <>) . T.pack)
-                    pure
-                    (decode partialToolCallArguments)
+                  if T.null (T.strip partialToolCallArguments)
+                    then pure (object [])
+                    else
+                      either
+                        (throwLlmError . ("Invalid JSON arguments in OpenAI tool call: " <>) . T.pack)
+                        pure
+                        (decode partialToolCallArguments)
                 pure ToolCall {toolCallId, toolCallType = "function", toolCallName, toolCallArguments}
 
               throwLlmError = throwError . llmError'
diff --git a/test/Test/Langchain/Provider/OpenAI.hs b/test/Test/Langchain/Provider/OpenAI.hs
--- a/test/Test/Langchain/Provider/OpenAI.hs
+++ b/test/Test/Langchain/Provider/OpenAI.hs
@@ -406,6 +406,48 @@
         case result of
           Left _ -> pure ()
           Right events -> assertFailure $ "Expected stream failure, got: " ++ show events
+    , testCase "stream handles 0-argument tool call with empty arguments and empty delta id/name" $ do
+        let frames =
+              [ sseFrame
+                  "{\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"test-model\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_0\",\"type\":\"function\",\"function\":{\"name\":\"get_time\",\"arguments\":\"\"}}]},\"finish_reason\":null}]}"
+              , sseFrame
+                  "{\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"test-model\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"\",\"function\":{\"name\":\"\",\"arguments\":\"\"}}]},\"finish_reason\":\"tool_calls\"}]}"
+              , sseFrame "[DONE]"
+              ]
+            expectedToolCall = ToolCall "call_0" "function" "get_time" (Aeson.object [])
+        result <- collectRawStream frames
+        case result of
+          Left err -> assertFailure $ "Expected stream success, got: " ++ show err
+          Right events -> case events of
+            [LLMStart {}, LLMChunk _ "" (Just tc), LLMEnd _ responseMessage Nothing] -> do
+              tc @?= expectedToolCall
+              messageToolCalls responseMessage @?= Just [expectedToolCall]
+            _ -> assertFailure $ "Unexpected stream events: " ++ show events
+    , testCase "stream accumulates parallel fragmented tool calls by index" $ do
+        let frames =
+              [ sseFrame
+                  "{\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"test-model\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"call_a\",\"type\":\"function\",\"function\":{\"name\":\"add\",\"arguments\":\"{\\\"x\\\":\"}}]},\"finish_reason\":null}]}"
+              , sseFrame
+                  "{\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"test-model\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":1,\"id\":\"call_b\",\"type\":\"function\",\"function\":{\"name\":\"mul\",\"arguments\":\"{\\\"y\\\":\"}}]},\"finish_reason\":null}]}"
+              , sseFrame
+                  "{\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"test-model\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"1}\"}},{\"index\":1,\"function\":{\"arguments\":\"2}\"}}]},\"finish_reason\":\"tool_calls\"}]}"
+              , sseFrame "[DONE]"
+              ]
+            tcA = ToolCall "call_a" "function" "add" (Aeson.object ["x" Aeson..= (1 :: Int)])
+            tcB = ToolCall "call_b" "function" "mul" (Aeson.object ["y" Aeson..= (2 :: Int)])
+        result <- collectRawStream frames
+        case result of
+          Left err -> assertFailure $ "Expected stream success, got: " ++ show err
+          Right events -> case events of
+            [ LLMStart {}
+              , LLMChunk _ "" (Just c0)
+              , LLMChunk _ "" (Just c1)
+              , LLMEnd _ responseMessage Nothing
+              ] -> do
+                c0 @?= tcA
+                c1 @?= tcB
+                messageToolCalls responseMessage @?= Just [tcA, tcB]
+            _ -> assertFailure $ "Unexpected stream events: " ++ show events
     , testCase "stream closes the SSE connection when the consumer stops after a chunk" $ do
         clientClosed <- newEmptyMVar
         withCancellationAwareProvider (putMVar clientClosed ()) $ \provider -> do
