diff --git a/baikai-claude.cabal b/baikai-claude.cabal
--- a/baikai-claude.cabal
+++ b/baikai-claude.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.4
 name:          baikai-claude
-version:       0.1.0.0
+version:       0.1.1.0
 synopsis:      Anthropic Claude providers for the baikai abstraction
 description:
   Wraps the claude Haskell package as a Baikai Provider for both the Anthropic API and the
@@ -38,7 +38,7 @@
 
   build-depends:
     , aeson
-    , baikai             ^>=0.1.0
+    , baikai             ^>=0.1.1
     , base               >=4.20   && <5
     , base64-bytestring
     , bytestring
@@ -65,10 +65,12 @@
   -- cradle (used by Baikai.Provider.Claude.Interactive) requires the threaded RTS.
   ghc-options:    -threaded -with-rtsopts=-N
   build-depends:
-    , baikai         ^>=0.1.0
+    , aeson
+    , baikai         ^>=0.1.1
     , baikai-claude
     , base           >=4.20   && <5
     , bytestring
+    , claude
     , generic-lens
     , lens           ^>=5.3
     , streamly-core  >=0.3    && <0.5
diff --git a/src/Baikai/Provider/Claude/Api.hs b/src/Baikai/Provider/Claude/Api.hs
--- a/src/Baikai/Provider/Claude/Api.hs
+++ b/src/Baikai/Provider/Claude/Api.hs
@@ -22,6 +22,7 @@
   ( register,
     registerWithRegistry,
     claudeMessagesStream,
+    mapRequest,
   )
 where
 
@@ -42,6 +43,7 @@
     globalProviderRegistry,
     registerApiProviderWith,
   )
+import Baikai.ResponseFormat (ResponseFormat (..))
 import Baikai.StopReason qualified as Stop
 import Baikai.Stream (streamingComplete)
 import Baikai.Stream.Event
@@ -63,7 +65,7 @@
 import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
 import Control.Exception (SomeException, displayException, try)
 import Control.Lens ((%~), (&), (.~), (^.))
-import Data.Aeson (Value)
+import Data.Aeson (Value, (.=))
 import Data.Aeson qualified as Aeson
 import Data.ByteString.Base64 qualified as Base64
 import Data.ByteString.Lazy qualified as BSL
@@ -564,6 +566,7 @@
         Just Tool.ToolChoiceNone -> Nothing
         Just tc -> Just (mkAnthropicToolChoice tc)
         Nothing -> Nothing
+      outputConfigField = fmap mkAnthropicOutputConfig (opts ^. #responseFormat)
   pure
     Messages._CreateMessage
       { Messages.model = m ^. #modelId,
@@ -574,8 +577,24 @@
         Messages.tools = toolsField,
         Messages.tool_choice = toolChoiceField,
         Messages.cache_control = cacheControlField,
-        Messages.thinking = thinkingField
+        Messages.thinking = thinkingField,
+        Messages.output_config = outputConfigField
       }
+
+-- | Map a baikai 'ResponseFormat' onto the upstream Anthropic
+-- 'Messages.OutputConfig'. 'JsonSchema' forwards the schema
+-- verbatim via 'Messages.jsonSchemaConfig'. Anthropic's structured
+-- outputs are always schema-enforcing, so the baikai 'strict' flag
+-- has no wire analog and is dropped. 'JsonObject' (plain-JSON mode)
+-- has no native Anthropic equivalent — 'output_config' requires a
+-- schema — so it downgrades to a permissive @{"type":"object"}@
+-- schema, which still forces the model to emit a JSON object.
+mkAnthropicOutputConfig :: ResponseFormat -> Messages.OutputConfig
+mkAnthropicOutputConfig = \case
+  JsonSchema {schema = s} -> Messages.jsonSchemaConfig s
+  JsonObject ->
+    Messages.jsonSchemaConfig
+      (Aeson.object ["type" .= ("object" :: Text)])
 
 -- | Translate the call-time 'Baikai.CacheRetention' preference into
 -- the Anthropic SDK's @cache_control@ shape.
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -3,24 +3,61 @@
 import Baikai
 import Baikai.Provider.Claude.Api
 import Baikai.Provider.Claude.Interactive
+import Claude.V1.Messages qualified as Messages
 import Control.Lens ((&), (.~), (^.))
+import Data.Aeson qualified as Aeson
 import Data.ByteString.Char8 qualified as BS8
 import Data.Generics.Labels ()
 import Data.Text qualified as Text
 import Data.Vector qualified as Vector
 import Streamly.Data.Stream qualified as Stream
 import Test.Tasty (TestTree, defaultMain, testGroup)
-import Test.Tasty.HUnit (assertBool, testCase, (@?=))
+import Test.Tasty.HUnit (assertBool, assertFailure, testCase, (@?=))
 
 main :: IO ()
 main =
   defaultMain $
     testGroup
-      "Baikai.Provider.Claude.Interactive"
+      "Baikai.Provider.Claude"
       [ commandRenderingTest,
         compatDetectionTest,
-        rejectsImageToolResultsTest
+        rejectsImageToolResultsTest,
+        responseFormatMappingTest
       ]
+
+-- | A 'JsonSchema' on 'Options.responseFormat' maps onto Anthropic's
+-- native @output_config@, forwarding the schema 'Value' verbatim via
+-- 'Messages.jsonSchemaConfig'. Pure: 'mapRequest' is
+-- 'Either Text Messages.CreateMessage'.
+responseFormatMappingTest :: TestTree
+responseFormatMappingTest =
+  testCase "responseFormat JsonSchema maps onto Anthropic output_config" $ do
+    let model =
+          _Model
+            & #modelId .~ "claude-haiku-4-5-20251001"
+            & #api .~ AnthropicMessages
+            & #provider .~ "anthropic"
+        personSchema =
+          Aeson.object
+            [ "type" Aeson..= ("object" :: Text.Text),
+              "properties"
+                Aeson..= Aeson.object
+                  [ "name" Aeson..= Aeson.object ["type" Aeson..= ("string" :: Text.Text)],
+                    "age" Aeson..= Aeson.object ["type" Aeson..= ("integer" :: Text.Text)]
+                  ],
+              "required" Aeson..= (["name", "age"] :: [Text.Text]),
+              "additionalProperties" Aeson..= False
+            ]
+        ctx = _Context
+        opts =
+          _Options
+            & #responseFormat
+              .~ Just (JsonSchema {name = "person", schema = personSchema, strict = True})
+    case mapRequest model ctx opts of
+      Left e -> assertFailure ("mapRequest failed: " <> Text.unpack e)
+      Right req ->
+        Messages.output_config req
+          @?= Just (Messages.jsonSchemaConfig personSchema)
 
 commandRenderingTest :: TestTree
 commandRenderingTest =
