- packages/core/api: HttpApi contract with ToolsApi + SecretsApi groups SDK tagged errors pass through directly with status annotations (ToolNotFoundError→404, PolicyDeniedError→403, etc.) - apps/server: API handler implementation via HttpApiBuilder Exports createApiHandler() for reuse (no baked-in server runtime) Handlers are pure pass-through — no manual error catching - apps/web: Vite + React frontend served on same port as API Vite plugin forwards /v1/*, /docs, /openapi.json to Effect handler effect-atom integration: AtomHttpApi.Tag typed client, query/mutation atoms RegistryProvider + useAtomValue with Result pattern matching - Catalog pins @effect/platform, platform-bun, platform-node, cluster - All 9 packages typecheck clean
Showing the changes to packages/core/api/src/tools (1 of 26 files). Show all
Unified · Split
| @@ -0,0 +1,87 @@ | |||
| 1 | import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"; | ||
| 2 | import { Schema } from "effect"; | ||
| 3 | import { | ||
| 4 | ScopeId, | ||
| 5 | ToolId, | ||
| 6 | ToolNotFoundError, | ||
| 7 | ToolInvocationError, | ||
| 8 | PolicyDeniedError, | ||
| 9 | ElicitationDeclinedError, | ||
| 10 | } from "@executor/sdk"; | ||
| 11 | |||
| 12 | // --------------------------------------------------------------------------- | ||
| 13 | // Params | ||
| 14 | // --------------------------------------------------------------------------- | ||
| 15 | |||
| 16 | const scopeIdParam = HttpApiSchema.param("scopeId", ScopeId); | ||
| 17 | const toolIdParam = HttpApiSchema.param("toolId", ToolId); | ||
| 18 | |||
| 19 | // --------------------------------------------------------------------------- | ||
| 20 | // Response schemas | ||
| 21 | // --------------------------------------------------------------------------- | ||
| 22 | |||
| 23 | const ToolMetadataResponse = Schema.Struct({ | ||
| 24 | id: ToolId, | ||
| 25 | name: Schema.String, | ||
| 26 | description: Schema.optional(Schema.String), | ||
| 27 | tags: Schema.Array(Schema.String), | ||
| 28 | mayElicit: Schema.optional(Schema.Boolean), | ||
| 29 | }); | ||
| 30 | |||
| 31 | const ToolSchemaResponse = Schema.Struct({ | ||
| 32 | id: ToolId, | ||
| 33 | inputSchema: Schema.optional(Schema.Unknown), | ||
| 34 | outputSchema: Schema.optional(Schema.Unknown), | ||
| 35 | }); | ||
| 36 | |||
| 37 | const ToolInvokePayload = Schema.Struct({ | ||
| 38 | args: Schema.Unknown, | ||
| 39 | }); | ||
| 40 | |||
| 41 | const ToolInvokeResponse = Schema.Struct({ | ||
| 42 | data: Schema.Unknown, | ||
| 43 | error: Schema.NullOr(Schema.Unknown), | ||
| 44 | status: Schema.optional(Schema.Number), | ||
| 45 | }); | ||
| 46 | |||
| 47 | // --------------------------------------------------------------------------- | ||
| 48 | // Error schemas with HTTP status annotations | ||
| 49 | // --------------------------------------------------------------------------- | ||
| 50 | |||
| 51 | const ToolNotFound = ToolNotFoundError.annotations( | ||
| 52 | HttpApiSchema.annotations({ status: 404 }), | ||
| 53 | ); | ||
| 54 | const ToolInvocation = ToolInvocationError.annotations( | ||
| 55 | HttpApiSchema.annotations({ status: 500 }), | ||
| 56 | ); | ||
| 57 | const PolicyDenied = PolicyDeniedError.annotations( | ||
| 58 | HttpApiSchema.annotations({ status: 403 }), | ||
| 59 | ); | ||
| 60 | const ElicitationDeclined = ElicitationDeclinedError.annotations( | ||
| 61 | HttpApiSchema.annotations({ status: 422 }), | ||
| 62 | ); | ||
| 63 | |||
| 64 | // --------------------------------------------------------------------------- | ||
| 65 | // Group | ||
| 66 | // --------------------------------------------------------------------------- | ||
| 67 | |||
| 68 | export class ToolsApi extends HttpApiGroup.make("tools") | ||
| 69 | .add( | ||
| 70 | HttpApiEndpoint.get("list")`/scopes/${scopeIdParam}/tools` | ||
| 71 | .addSuccess(Schema.Array(ToolMetadataResponse)), | ||
| 72 | ) | ||
| 73 | .add( | ||
| 74 | HttpApiEndpoint.get("schema")`/scopes/${scopeIdParam}/tools/${toolIdParam}/schema` | ||
| 75 | .addSuccess(ToolSchemaResponse) | ||
| 76 | .addError(ToolNotFound), | ||
| 77 | ) | ||
| 78 | .add( | ||
| 79 | HttpApiEndpoint.post("invoke")`/scopes/${scopeIdParam}/tools/${toolIdParam}/invoke` | ||
| 80 | .setPayload(ToolInvokePayload) | ||
| 81 | .addSuccess(ToolInvokeResponse) | ||
| 82 | .addError(ToolNotFound) | ||
| 83 | .addError(ToolInvocation) | ||
| 84 | .addError(PolicyDenied) | ||
| 85 | .addError(ElicitationDeclined), | ||
| 86 | ) | ||
| 87 | .prefix("/v1") {} | ||