Effect Services

Handlers can return synchronous values, promises, or Effect values. Simple handlers do not need to import Effect.

When to use this: add Effect when the handler needs services, layers, cleanup, or structured errors. Keep the Ohtools boundary the same: validated input in, structured output out.

This app still runs on Bun. MCP exposure remains stdio-only in 0.1.

// docs-snippet: skip
import { Ohtools, jsonSchema } from "@bosun-sh/ohtools";
import { Effect } from "effect";

export const app = new Ohtools().tool("status.read", {
  description: "Read service status.",
  input: jsonSchema<{ service: string }>({
    type: "object",
    properties: { service: { type: "string" } },
    required: ["service"],
    additionalProperties: false
  }),
  run: ({ service }) =>
    Effect.succeed({
      service,
      status: "ready"
    })
});

Keep Effects Behind Services

For larger apps, inject an application service into a tool factory and let that service own the Effect program.

// docs-snippet: skip
import { defineTool, jsonSchema } from "@bosun-sh/ohtools";
import type { StatusService } from "../application/status-service";

export function statusReadTool(service: StatusService) {
  return defineTool({
    id: "status.read",
    description: "Read service status.",
    input: jsonSchema<{ service: string }>({
      type: "object",
      properties: { service: { type: "string" } },
      required: ["service"],
      additionalProperties: false
    }),
    run: (input) => service.read(input)
  });
}

This preserves a clean registry while still letting your application use Effect for runtime concerns.