chore: update mcp-envelope-integration task status to completed

This commit is contained in:
2026-05-11 01:59:52 +00:00
parent e58d95a6dc
commit f833fe3d53

View File

@@ -0,0 +1,92 @@
---
id: mcp-envelope-integration
name: Update from_mcp adapter to use mcpEnvelope, structuredContent, and outputSchema extraction
status: completed
depends_on: [response-envelope-types]
scope: moderate
risk: medium
impact: component
level: implementation
---
## Description
Update `src/from_mcp.ts` to align with the response envelope specification documented in `response-envelopes.md` § MCP Adapter, `adapters.md` § from_mcp, and the drift tables in `call-protocol.md` and `api-surface.md`.
Changes needed (current source → target):
| What | Current source | Target |
|------|---------------|--------|
| `outputSchema` at discovery time | `Type.Unknown()` for all tools | `tool.outputSchema ? FromSchema(tool.outputSchema) : Type.Unknown()` |
| Handler return value | Returns `result.content` (line 79) | Returns `mcpEnvelope(data, meta)` |
| `structuredContent` | Not used | Prefer as `data` when present; fall back to `mapMCPContentBlocks(result.content)` |
| `isError` handling | Throws `Error` (line 76) | Wraps in envelope with `meta.isError: true`, does NOT throw |
| `Value.Cast()` | Not used | If `structuredContent && outputSchema !== Unknown`, cast `Value.Cast(outputSchema, structuredContent)` |
Implementation details:
1. **`outputSchema` extraction**: When `tool.outputSchema` is present (MCP spec 2025-06-18+), convert it to TypeBox via `FromSchema(tool.outputSchema)`. Fall back to `Type.Unknown()`.
2. **Handler behavior**: The handler should use `mcpEnvelope()` to wrap results:
```ts
handler: async (input, context) => {
const result = await client.callTool({ name: tool.name, arguments: input })
const data = result.structuredContent
? (spec.outputSchema !== Type.Unknown()
? Value.Cast(spec.outputSchema, result.structuredContent)
: result.structuredContent)
: mapMCPContentBlocks(result.content)
return mcpEnvelope(data, {
isError: result.isError ?? false,
content: mapMCPContentBlocks(result.content),
structuredContent: result.structuredContent,
_meta: result._meta,
})
}
```
3. **`mapMCPContentBlocks()`**: A helper function that maps SDK `ContentBlock[]` to our `MCPContentBlock[]` types. Unknown block types are mapped to `{ type: "text", text: JSON.stringify(block) }` as fallback.
4. **`isError` no longer throws**: MCP errors are represented as data, not thrown exceptions. Only transport-level errors (connection failure, tool not found) throw.
5. **The tool iteration** needs to capture `outputSchema` per tool since the handler closure needs it for `Value.Cast`.
## Acceptance Criteria
- [ ] `tool.outputSchema` is converted via `FromSchema()` when present; falls back to `Type.Unknown()`
- [ ] Handler returns `mcpEnvelope()` — never raw content and never throws on `isError`
- [ ] `structuredContent` is preferred as data when present
- [ ] `mapMCPContentBlocks()` helper maps SDK content blocks to our `MCPContentBlock[]` types
- [ ] Unknown MCP content block types fall back to `{ type: "text", text: JSON.stringify(block) }`
- [ ] `Value.Cast()` normalization applied when `outputSchema !== Type.Unknown()` and `structuredContent` is present
- [ ] `isError: true` results are wrapped in envelope, not thrown
- [ ] Transport-level errors (connection, etc.) still throw `CallError`
- [ ] `MCPClientWrapper.tools` type still returns `Array<OperationSpec & { handler: OperationHandler }>`
- [ ] Existing tests updated; new tests for envelope wrapping, structuredContent, isError handling
- [ ] `npm run build` passes, `npm run lint` passes, `npm test` passes
## References
- docs/architecture/adapters.md § from_mcp (Implementation changes needed table)
- docs/architecture/response-envelopes.md § MCP Adapter, § Handler behavior change
- docs/architecture/api-surface.md § Source vs. Spec Drift (from_mcp row)
- src/from_mcp.ts
## Notes
Merged `feat/response-envelope-types` branch first to get `ResponseEnvelope` types. The `Kind` symbol from `@alkdev/typebox` is used to detect `Type.Unknown()` schema at runtime, with a fallback to empty-object detection.
## Summary
Implemented MCP adapter envelope integration in `src/from_mcp.ts`:
- Created: `test/from_mcp.test.ts` (20 tests)
- Modified: `src/from_mcp.ts`
- `outputSchema` extraction from `tool.outputSchema` via `FromSchema()`, falls back to `Type.Unknown()`
- Handler returns `mcpEnvelope()` with structured/legacy data path
- `structuredContent` preferred as `data` when present, `Value.Cast()` applied when `outputSchema` is not Unknown
- `isError: true` wrapped in envelope meta, NOT thrown
- Transport-level config errors throw `CallError`
- Added `mapMCPContentBlocks()` exported helper mapping SDK `ContentBlock[]` to our `MCPContentBlock[]`
- Unknown block types fall back to `{ type: "text", text: JSON.stringify(block) }`
- Removed unused `OperationContext` import
- Build, lint, and all 95 tests pass