[FEATURE]: Support for Structured Outputs in the OpenCode SDK #3595

Closed
opened 2026-02-16 17:40:48 -05:00 by yindo · 6 comments
Owner

Originally created by @K-Mistele on GitHub (Dec 16, 2025).

Originally assigned to: @thdxr on GitHub.

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

I would love to use opencode for some existing workflows we have which currently depend on the Claude Code SDK's structured output feature which allows generating JSON (generateObject-style) from an extant session:

https://platform.claude.com/docs/en/agent-sdk/structured-outputs

Any change this would be possible to add to OpenCode?

Originally created by @K-Mistele on GitHub (Dec 16, 2025). Originally assigned to: @thdxr on GitHub. ### Feature hasn't been suggested before. - [x] I have verified this feature I'm about to request hasn't been suggested before. ### Describe the enhancement you want to request I would love to use opencode for some existing workflows we have which currently depend on the Claude Code SDK's structured output feature which allows generating JSON (`generateObject`-style) from an extant session: https://platform.claude.com/docs/en/agent-sdk/structured-outputs Any change this would be possible to add to OpenCode?
yindo added the discussion label 2026-02-16 17:40:48 -05:00
yindo closed this issue 2026-02-16 17:40:48 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Dec 16, 2025):

This issue might be a duplicate of existing issues. Please check:

  • #992: AI_APICallError: "stream=true" with structured outputs is currently not supported for reasoning models.

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Dec 16, 2025): This issue might be a duplicate of existing issues. Please check: - #992: AI_APICallError: "stream=true" with structured outputs is currently not supported for reasoning models. Feel free to ignore if none of these address your specific case.
Author
Owner

@seuros commented on GitHub (Dec 16, 2025):

Possible once https://github.com/sst/opencode/pull/5463 is merged.

The experimental client does no support structure.

@seuros commented on GitHub (Dec 16, 2025): Possible once https://github.com/sst/opencode/pull/5463 is merged. The experimental client does no support structure.
Author
Owner

@AshwanthramKL commented on GitHub (Jan 12, 2026):

@seuros Any update on this?

@AshwanthramKL commented on GitHub (Jan 12, 2026): @seuros Any update on this?
Author
Owner

@seuros commented on GitHub (Jan 12, 2026):

It possible now, i can open a PR if thdxr 👍🏼 .

It looks like mcp is not top priority for now for them.

@seuros commented on GitHub (Jan 12, 2026): It possible now, i can open a PR if thdxr 👍🏼 . It looks like mcp is not top priority for now for them.
Author
Owner

@K-Mistele commented on GitHub (Jan 12, 2026):

Will try to ship a PR for it this week

@K-Mistele commented on GitHub (Jan 12, 2026): Will try to ship a PR for it this week
Author
Owner

@eXamadeus commented on GitHub (Jan 19, 2026):

I actually posted a review on #8161, but part of it is a discussion, so I'll bring it back up here.

Note

[Context] the PR implements a solution using the following pattern:

  1. inject a system prompt that requests the model call a specific tool to terminate
  2. provides a tool which enforces the schema called (the structured output tool)
  3. this tool enforces schema compliance after the LLM is done iterating
  4. retry with the LLM if failed (not sure this is actually implemented in the PR yet)

Relevant section of my review:

discussion: architecture

Is this the best path to take? I understand this is reverse-engineered from Claude Code, but let's be honest: I'm here because Claude Code was pissing me off. They couldn't even figure out how to remove MCP tools from their prompts when the tools are disabled for subagents.

That said, what do you think about an approach that utilizes the provider-level structured outputs? Almost all providers implement something for their models, and I think we could likely provide a clean abstraction that utilizes those features.

I personally built a system similar to this PR this inside my own plugin. Instead of a tool call, it simply injected the expected response format and requested the model reply in an object that matched. It does work, there's no doubt of that, but it does have it's limitations. In some scenarios, assuming the retry logic is implemented, you end up with a few extra round trips to correct the outputs. It's easy enough to enforce this at the tool layer, but different models have different levels of "willingness to cooperate".

Regarding my implementation, I injected the Zod schemas directly (in a format the LLM could understand) and asked it to just generate that output, instead of calling a tool. I found this method to be slightly more reliable than requesting a tool call, because many models see tool calls and "non-terminal" to their execution loops. Or at least, this is a working theory I have based off how many times I've seen OpenAI models get sassy when I request they end with tool calls.

Anyway, food for thought.

So I want to raise that question here: should we try to use provider-level support for structured outputs, should we use the tool/validate/retry method, a combo, or something else entirely?

I will say the tool/validate/retry method is more "universally supported" but you also do not get deterministic results. You are at the mercy of the model obeying the injected prompt and calling the tool. I have seen the tool/validate/retry not work so well, in my own attempts, but that's purely anecdotal and it only affected the "lower end" models. I wouldn't put much stock into that assessment. It does fine on most decent models.

The provider-level support is definitely more reliable; however, the implementation might be a little more complex. We would need some abstraction (shouldn't be too hard) along with implementations for each provider type...this might be hard? I don't honestly know.

Overall I feel like a provider-level solution (with a tool/validate/retry fallback) is the best approach long term, but would love to hear yalls thoughts on the matter.

@eXamadeus commented on GitHub (Jan 19, 2026): I actually posted a [review](https://github.com/anomalyco/opencode/pull/8161#pullrequestreview-3678640467) on #8161, but part of it is a discussion, so I'll bring it back up here. > [!NOTE] > [**Context**] the PR implements a solution using the following pattern: > 1. inject a system prompt that requests the model call a specific tool to terminate > 2. provides a tool which enforces the schema called (the structured output tool) > 3. this tool enforces schema compliance after the LLM is done iterating > 4. retry with the LLM if failed (not sure this is actually implemented in the PR yet) Relevant section of my review: > ### discussion: architecture > Is this the best path to take? I understand this is reverse-engineered from Claude Code, but let's be honest: I'm here because Claude Code was pissing me off. They couldn't even figure out how to remove MCP tools from their prompts when the tools are disabled for subagents. > > That said, what do you think about an approach that utilizes the provider-level structured outputs? Almost all providers implement something for their models, and I think we could likely provide a clean abstraction that utilizes those features. > > I personally built a system similar to this PR this inside my own plugin. Instead of a tool call, it simply injected the expected response format and requested the model reply in an object that matched. It does work, there's no doubt of that, but it does have it's limitations. In some scenarios, assuming the retry logic is implemented, you end up with a few extra round trips to correct the outputs. It's easy enough to enforce this at the tool layer, but different models have different levels of "willingness to cooperate". > > Regarding my implementation, I injected the Zod schemas directly (in a format the LLM could understand) and asked it to just generate that output, instead of calling a tool. I found this method to be slightly more reliable than requesting a tool call, because many models see tool calls and "non-terminal" to their execution loops. Or at least, this is a working theory I have based off how many times I've seen OpenAI models get sassy when I request they end with tool calls. > > Anyway, food for thought. So I want to raise that question here: should we try to use provider-level support for structured outputs, should we use the tool/validate/retry method, a combo, or something else entirely? I will say the tool/validate/retry method is more "universally supported" but you also do not get deterministic results. You are at the mercy of the model obeying the injected prompt and calling the tool. I have seen the tool/validate/retry not work so well, in my own attempts, but that's purely anecdotal and it only affected the "lower end" models. I wouldn't put much stock into that assessment. It does fine on most decent models. The provider-level support is definitely more reliable; however, the implementation might be a little more complex. We would need some abstraction (shouldn't be too hard) along with implementations for each provider type...this might be hard? I don't honestly know. Overall I feel like a provider-level solution (with a tool/validate/retry fallback) is the best approach long term, but would love to hear yalls thoughts on the matter.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#3595