Skip to main content

Default behavior

When your handler returns a plain object, the platform responds with 200 OK and Content-Type: application/json:
handler: async (input) => {
  return { name: "Maria", balance: 150.00 };
}
// → 200 OK
// → { "name": "Maria", "balance": 150.00 }
When you need a different status code or custom headers, use the response builder.

Response builder

Import response from @jelou/functions:
import { define, response, z } from "@jelou/functions";

Custom status code

export default define({
  description: "Create a user",
  input: z.object({ name: z.string(), email: z.string().email() }),
  output: z.object({ id: z.string(), name: z.string() }),
  handler: async (input) => {
    const user = await createUser(input);

    return response
      .status(201)
      .json({ id: user.id, name: user.name });
  },
});

Custom headers

handler: async (input) => {
  return response
    .header("X-Request-Id", crypto.randomUUID())
    .header("Cache-Control", "max-age=300")
    .json({ data: [] });
}

Multiple headers at once

handler: async (input) => {
  return response
    .headers({
      "X-Request-Id": crypto.randomUUID(),
      "Cache-Control": "no-store",
    })
    .json({ ok: true });
}

Full chaining

The builder is immutable — each method returns a new instance:
handler: async (input) => {
  return response
    .status(201)
    .header("Location", `/users/${input.id}`)
    .header("X-Created-By", "api")
    .json({ id: input.id, name: input.name });
}
// → 201 Created
// → Location: /users/abc123
// → { "id": "abc123", "name": "Maria" }

API

MethodDescription
response.status(code)Set HTTP status (default: 200)
response.header(name, value)Add a header
response.headers(init)Add multiple headers
response.json(body)Finalize and return JsonResponse
.json() is the terminal method — after calling it you get a JsonResponse, not a builder.

Practical examples

handler: async (input) => {
  const ticket = await createTicket(input);
  return response
    .status(201)
    .header("Location", `/tickets/${ticket.id}`)
    .json({ ticketId: ticket.id, status: "open" });
}

Output validation

When using response.json(body) with an output schema, validation applies to the body — same as with plain objects. Validation never blocks the response.

MCP behavior

When invoked via MCP (Brain Studio AI agents):
  • The body is emitted as structuredContent
  • Status code and headers are ignored — MCP has no HTTP status concept
  • The body is also sent as JSON text for compatibility
No need to condition your code for HTTP vs MCP — use the response builder normally. The platform extracts the body and discards HTTP metadata for MCP invocations.

Limitations

LimitationDetail
JSON only.json() is the only terminal method — no .text(), .html()
No native Web ResponseReturning new Response() from define()/app() doesn’t work
Fixed Content-TypeAlways application/json, cannot be overridden
OpenAPISpec at /openapi.json only documents 200 responses for now
For advanced needs (streaming, binary, dynamic content-type), use raw mode.