Files
operations/docs/response-envelopes.md

111 lines
2.9 KiB
Markdown

# Response Envelopes
All operation results are wrapped in a `ResponseEnvelope<T>` that carries transport metadata alongside the data. This provides a uniform result type regardless of whether the operation ran locally, over HTTP, or via MCP.
## Structure
```ts
interface ResponseEnvelope<T = unknown> {
data: T;
meta: ResponseMeta;
}
```
`meta.source` is the discriminant:
| Source | Type | Carries |
|--------|------|---------|
| `"local"` | `LocalResponseMeta` | `operationId`, `timestamp` |
| `"http"` | `HTTPResponseMeta` | `statusCode`, `headers`, `contentType` |
| `"mcp"` | `MCPResponseMeta` | `isError`, `content[]`, `structuredContent?`, `_meta?` |
## Creating Envelopes
### localEnvelope
For in-process results:
```ts
import { localEnvelope } from "@alkdev/operations";
const env = localEnvelope({ id: "123", title: "My task" }, "task.create");
```
### httpEnvelope
For HTTP-sourced results (e.g., OpenAPI adapter):
```ts
import { httpEnvelope } from "@alkdev/operations";
const env = httpEnvelope(data, {
statusCode: 200,
headers: { "content-type": "application/json" },
contentType: "application/json",
});
```
### mcpEnvelope
For MCP-sourced results:
```ts
import { mcpEnvelope } from "@alkdev/operations";
const env = mcpEnvelope(data, {
isError: false,
content: [{ type: "text", text: "Done" }],
});
```
## Unwrapping
```ts
import { unwrap } from "@alkdev/operations";
const result = unwrap(envelope);
```
Returns `envelope.data`, discarding transport metadata.
## Detecting Envelopes
```ts
import { isResponseEnvelope } from "@alkdev/operations";
if (isResponseEnvelope(maybeEnvelope)) {
// TypeScript narrows to ResponseEnvelope
}
```
Checks that the value has `data` and `meta` with a recognized `source` ("local" | "http" | "mcp") and the appropriate source-specific fields.
## Automatic Wrapping
When an operation handler returns a plain value (not a `ResponseEnvelope`), `registry.execute()` and `subscribe()` wrap it in a `localEnvelope()` automatically. If the handler already returns a `ResponseEnvelope`, it passes through unchanged.
## MCP Content Blocks
MCP responses include structured content blocks:
```ts
type MCPContentBlock =
| { type: "text"; text: string; annotations?: MCPAnnotations }
| { type: "image"; data: string; mimeType: string; annotations?: MCPAnnotations }
| { type: "audio"; data: string; mimeType: string; annotations?: MCPAnnotations }
| { type: "resource"; resource: MCPResourceContent; annotations?: MCPAnnotations }
| { type: "resource_link"; uri: string; name: string; description?: string; mimeType?: string }
```
## TypeBox Schemas
Both `ResponseEnvelopeSchema` and `ResponseMetaSchema` are available for runtime validation:
```ts
import { ResponseEnvelopeSchema, ResponseMetaSchema } from "@alkdev/operations";
import { Value } from "@alkdev/typebox/value";
if (Value.Check(ResponseEnvelopeSchema, value)) {
// valid envelope
}
```