---
phase: 41-chatgpt-mcp-apps-upgraded-version
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/types/protocol.rs
  - src/types/ui.rs
  - src/types/mcp_apps.rs
  - src/server/mcp_apps/adapter.rs
  - src/server/simple_resources.rs
  - src/server/workflow/conversion.rs
  - src/composition/mcp_client.rs
  - src/lib.rs
  - src/server/workflow/prompt_handler.rs
autonomous: true
requirements: [P41-01, P41-02, P41-03]

must_haves:
  truths:
    - "Content::Resource variant has an optional _meta field that serializes as '_meta'"
    - "UIResourceContents has an optional _meta field for resource-level widget metadata"
    - "ChatGptAdapter.preferred_mime_type() returns HtmlMcpApp not HtmlSkybridge"
    - "ChatGptAdapter.mime_type() (UIAdapter impl) returns HtmlMcpApp not HtmlSkybridge"
    - "All existing Content::Resource construction sites compile with _meta: None"
  artifacts:
    - path: "src/types/protocol.rs"
      provides: "Content::Resource with _meta field"
      contains: "_meta"
    - path: "src/types/ui.rs"
      provides: "UIResourceContents with _meta field"
      contains: "_meta"
    - path: "src/types/mcp_apps.rs"
      provides: "ChatGpt preferred_mime_type returns HtmlMcpApp"
      contains: "HtmlMcpApp"
    - path: "src/server/mcp_apps/adapter.rs"
      provides: "ChatGptAdapter UIAdapter::mime_type returns HtmlMcpApp"
      contains: "HtmlMcpApp"
  key_links:
    - from: "src/types/protocol.rs"
      to: "src/server/simple_resources.rs"
      via: "Content::Resource { ... _meta: None }"
      pattern: "_meta: None"
    - from: "src/types/mcp_apps.rs"
      to: "src/types/ui.rs"
      via: "WidgetMeta.to_meta_map() usable for resource _meta"
      pattern: "to_meta_map"
    - from: "src/server/mcp_apps/adapter.rs"
      to: "src/types/mcp_apps.rs"
      via: "ChatGptAdapter::mime_type() returns ExtendedUIMimeType::HtmlMcpApp"
      pattern: "HtmlMcpApp"
---

<objective>
Add `_meta` field to `Content::Resource` enum variant and `UIResourceContents` struct, fix ChatGPT adapter MIME type from `HtmlSkybridge` to `HtmlMcpApp` in BOTH `mcp_apps.rs` (HostType) AND `adapter.rs` (UIAdapter impl), and update all existing construction sites.

Purpose: ChatGPT's MCP Apps protocol requires resource-level `_meta` (widgetDescription, CSP, domain) on `resources/read` content items, and the correct MIME type `text/html;profile=mcp-app`.
Output: Compilable codebase with resource-level metadata support and correct MIME type.
</objective>

<execution_context>
@./.claude/get-shit-done/workflows/execute-plan.md
@./.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/41-chatgpt-mcp-apps-upgraded-version/41-RESEARCH.md

@src/types/protocol.rs
@src/types/ui.rs
@src/types/mcp_apps.rs
@src/server/mcp_apps/adapter.rs
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Add _meta field to Content::Resource and UIResourceContents</name>
  <files>src/types/protocol.rs, src/types/ui.rs</files>
  <behavior>
    - Test: Content::Resource serializes with _meta when Some, omits when None
    - Test: Content::Resource deserializes correctly with and without _meta
    - Test: Existing Content::Resource JSON (without _meta) still deserializes
    - Test: UIResourceContents serializes _meta when present
  </behavior>
  <action>
1. In `src/types/protocol.rs`, add to the `Content::Resource` variant:
   ```rust
   /// Optional metadata for resource content (e.g., widget metadata for MCP Apps)
   #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
   meta: Option<serde_json::Map<String, serde_json::Value>>,
   ```
   Note: Use field name `meta` (not `_meta` which is invalid Rust identifier) with `#[serde(rename = "_meta")]`.

2. In `src/types/ui.rs`, add to `UIResourceContents`:
   ```rust
   /// Optional metadata (widget description, CSP, domain)
   #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
   pub meta: Option<serde_json::Map<String, serde_json::Value>>,
   ```
   Update `UIResourceContents::html()` constructor to set `meta: None`.

3. Update ALL existing `Content::Resource { ... }` construction sites to add `meta: None`:
   - `src/server/simple_resources.rs` (~9 sites)
   - `src/lib.rs` (~2 sites in doc examples)
   - `src/server/workflow/conversion.rs` (~3 sites including the `MessageContent::Resource` conversions)
   - `src/composition/mcp_client.rs` (~3 sites)
   - `src/server/workflow/prompt_handler.rs` (~1 site in match arm)
   - `src/client/mod.rs` (doc examples if any)

4. For match arms that destructure `Content::Resource { uri, text, mime_type }`, add `meta` or `..` to avoid compile errors. Check `prompt_handler.rs` line 534 and similar.

5. Write tests in `src/types/protocol.rs` tests module:
   - `test_content_resource_meta_serialization`: Content::Resource with meta serializes `_meta` key
   - `test_content_resource_no_meta_serialization`: Content::Resource with meta: None omits `_meta`
   - `test_content_resource_meta_deserialization`: JSON with `_meta` deserializes correctly
   - `test_content_resource_backward_compat`: JSON without `_meta` deserializes with meta as None
  </action>
  <verify>
    <automated>cargo test --features full --lib types::protocol::tests -- content_resource_meta</automated>
  </verify>
  <done>Content::Resource has optional _meta field; all construction sites compile; UIResourceContents has optional meta field; serialization round-trips correctly</done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: Fix ChatGptAdapter MIME type to HtmlMcpApp in both mcp_apps.rs and adapter.rs</name>
  <files>src/types/mcp_apps.rs, src/server/mcp_apps/adapter.rs</files>
  <behavior>
    - Test: ChatGptAdapter preferred_mime_type returns HtmlMcpApp
    - Test: ChatGptAdapter UIAdapter::mime_type() returns HtmlMcpApp (not HtmlSkybridge)
    - Test: ChatGptAdapter supports_mime_type accepts both HtmlSkybridge and HtmlMcpApp
    - Test: ExtendedUIMimeType::HtmlMcpApp.to_string() returns "text/html;profile=mcp-app"
  </behavior>
  <action>
1. In `src/types/mcp_apps.rs`, change `ChatGptAdapter::preferred_mime_type()`:
   ```rust
   Self::ChatGpt => ExtendedUIMimeType::HtmlMcpApp,
   ```
   (was `HtmlSkybridge`)

2. In `src/server/mcp_apps/adapter.rs`, change `ChatGptAdapter::mime_type()` (the UIAdapter trait impl at line 91-92):
   ```rust
   fn mime_type(&self) -> ExtendedUIMimeType {
       ExtendedUIMimeType::HtmlMcpApp
   }
   ```
   (was `HtmlSkybridge`)

3. Update the test at adapter.rs line 639 that asserts `transformed.mime_type` equals `HtmlSkybridge` -- change to `HtmlMcpApp`:
   ```rust
   assert_eq!(transformed.mime_type, ExtendedUIMimeType::HtmlMcpApp);
   ```

4. Verify `supports_mime_type` for ChatGpt already accepts both `HtmlSkybridge | HtmlMcpApp` -- it should based on the existing match arm. If not, ensure it does.

5. Update any other existing test that asserts `preferred_mime_type` returns `HtmlSkybridge` for ChatGpt -- change assertion to `HtmlMcpApp`.

6. Add a deprecation doc comment on `ExtendedUIMimeType::HtmlSkybridge`:
   ```rust
   /// Legacy MIME type for ChatGPT MCP Apps.
   /// Deprecated: use `HtmlMcpApp` (`text/html;profile=mcp-app`) instead.
   ```
  </action>
  <verify>
    <automated>cargo test --features full --lib types::mcp_apps::tests && cargo test --features full --lib server::mcp_apps::adapter::tests</automated>
  </verify>
  <done>ChatGptAdapter returns HtmlMcpApp as preferred MIME type from both HostType::preferred_mime_type() and UIAdapter::mime_type(); HtmlSkybridge kept but documented as deprecated; adapter test updated; all tests pass</done>
</task>

</tasks>

<verification>
cargo test --features full --lib
cargo clippy --features full -- -D warnings
</verification>

<success_criteria>
- `Content::Resource` has `meta` field that round-trips as `_meta` in JSON
- `UIResourceContents` has `meta` field
- `ChatGptAdapter` prefers `text/html;profile=mcp-app` MIME type via both code paths
- `adapter.rs` test asserts `HtmlMcpApp` (not `HtmlSkybridge`)
- Full test suite passes
- Zero clippy warnings
</success_criteria>

<output>
After completion, create `.planning/phases/41-chatgpt-mcp-apps-upgraded-version/41-01-SUMMARY.md`
</output>
