Install
$ agentstack add skill-kora-projects-kora-skills-kora-http-server ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Kora HTTP Server
Declarative HTTP request handlers compiled (not reflected) into a router. Annotate a @Component @HttpController with @HttpRoute methods; Kora generates the handler/router at build time via the annotation processor.
The base path lives on @HttpRoute, not on @HttpController — @HttpController takes no path argument.
Quick Start
1. Dependencies (Java)
configurations {
koraBom
annotationProcessor.extendsFrom(koraBom)
implementation.extendsFrom(koraBom)
}
dependencies {
koraBom platform("ru.tinkoff.kora:kora-parent:1.2.17")
annotationProcessor "ru.tinkoff.kora:annotation-processors" // mandatory
implementation "ru.tinkoff.kora:http-server-undertow"
implementation "ru.tinkoff.kora:json-module"
implementation "ru.tinkoff.kora:config-hocon"
implementation "ru.tinkoff.kora:logging-logback"
}
Kotlin uses KSP instead: ksp "ru.tinkoff.kora:symbol-processors". All ru.tinkoff.kora:* artifacts inherit their version from the BOM — never pin them individually.
2. Application graph
@KoraApp
public interface Application extends
HoconConfigModule,
JsonModule,
LogbackModule,
UndertowHttpServerModule {
static void main(String[] args) {
KoraApplication.run(ApplicationGraph::graph);
}
}
3. Controller
@Component
@HttpController
public final class HelloController {
@HttpRoute(method = HttpMethod.GET, path = "/hello/{name}")
public String hello(@Path String name) {
return "Hello " + name; // 200 OK, text/plain
}
}
4. Config (application.conf)
httpServer {
publicApiHttpPort = 8080 // application traffic
privateApiHttpPort = 8085 // metrics / probes
telemetry.logging.enabled = true
}
CRUD controller
Adapted from kora-java-guide-http-server-app. JSON requires @Json on the method and on the body parameter; optional inputs are marked @Nullable (Kotlin: a nullable type).
@Component
@HttpController
public final class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@HttpRoute(method = HttpMethod.GET, path = "/users/{userId}")
@Json
public UserResponse getUser(@Path String userId) {
return userService.getUser(userId)
.orElseThrow(() -> HttpServerResponseException.of(404, "User not found: " + userId));
}
@HttpRoute(method = HttpMethod.GET, path = "/users")
@Json
public List getUsers(@Nullable @Query("page") Integer page,
@Nullable @Query("size") Integer size) {
return userService.getUsers(page == null ? 0 : page, size == null ? 10 : size);
}
@HttpRoute(method = HttpMethod.POST, path = "/users")
@Json
public HttpResponseEntity createUser(@Json UserRequest request) {
UserResponse user = userService.createUser(request);
return HttpResponseEntity.of(201, HttpHeaders.of("Location", "/users/" + user.id()), user);
}
@HttpRoute(method = HttpMethod.DELETE, path = "/users/{userId}")
public HttpServerResponse deleteUser(@Path String userId) {
userService.deleteUser(userId);
return HttpServerResponse.of(204, HttpBody.empty());
}
}
DTOs are plain records annotated @Json: UserRequest(String email, String name), UserResponse(String id, String email, String name).
Key annotations
| Annotation | Level | Purpose | |---|---|---| | @HttpController | class | Marks an HTTP controller (no path argument) | | @HttpRoute(method, path) | method | Binds an HttpMethod + path to a handler | | @Path | parameter | Path segment {name}; name defaults to the argument name | | @Query | parameter | Query parameter; name defaults to the argument name | | @Header | parameter | Request header value | | @Cookie | parameter | Cookie value | | @Json | method / parameter | JSON serialization for the body | | @Mapping(X.class) | parameter / method | Custom request/response mapper | | @InterceptWith(X.class) | method / class | Apply an interceptor to a route or controller | | @Nullable | parameter | Marks a request parameter optional (Java) |
HttpMethod values: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE.
Response types
| Return type | Status | Notes | |---|---|---| | String / byte[] / ByteBuffer | 200 | Body written directly with the matching content type | | T + @Json on method | 200 | Body serialized to JSON | | HttpResponseEntity + @Json | any | Body + custom status code + headers | | HttpServerResponse | any | Full control over status, headers and raw body |
// JSON body with custom status and headers
return HttpResponseEntity.of(201, HttpHeaders.of("Location", "/users/" + id), user);
// Full manual control
return HttpServerResponse.of(200, HttpHeaders.of("X-Trace", traceId), HttpBody.plaintext("OK"));
return HttpServerResponse.of(204, HttpBody.empty());
Async signatures are supported: Java CompletionStage / Mono, Kotlin suspend fun.
See [Response Types](references/response-types-reference.md) for HttpBody, HttpHeaders, and custom HttpServerResponseMapper.
Error handling
Throw HttpServerResponseException.of(code, message) from a handler to short-circuit with a status code. Centralize cross-cutting error translation in a global interceptor (there is no dedicated "exception handler" type — error handling is an interceptor concern).
@HttpRoute(method = HttpMethod.GET, path = "/users/{id}")
@Json
public UserResponse get(@Path String id) {
return userService.find(id)
.orElseThrow(() -> HttpServerResponseException.of(404, "Not found: " + id));
}
See [Error Handling](references/error-handling-reference.md) for the global error interceptor.
Interceptors
HttpServerInterceptor.intercept(Context, HttpServerRequest, InterceptChain) returns CompletionStage. Call chain.process(context, request) to continue the chain; wrap it with .whenComplete(...) / .exceptionally(...) for after/error logic.
@Tag(HttpServerModule.class) // makes it global (only one global interceptor allowed)
@Component
public final class LoggingInterceptor implements HttpServerInterceptor {
private static final Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);
@Override
public CompletionStage intercept(Context context,
HttpServerRequest request,
InterceptChain chain) throws Exception {
long started = System.nanoTime();
return chain.process(context, request).whenComplete((response, error) -> {
long ms = (System.nanoTime() - started) / 1_000_000;
int code = response != null ? response.code() : 500;
log.info("{} {} -> {} ({} ms)", request.method(), request.path(), code, ms);
});
}
}
Scopes:
- Global — one interceptor tagged
@Tag(HttpServerModule.class), runs for every route. - Controller —
@InterceptWith(X.class)on the controller class. - Method —
@InterceptWith(X.class)on a single@HttpRoutemethod.
Order: global -> controller -> method -> handler.
See [Interceptors](references/interceptors-reference.md).
Authentication & Principal
For custom authentication in Kora 1.2.x, use the interceptor pattern — do NOT use Principal as a controller parameter (not auto-wired in 1.2.x, causes 401→400 downgrade).
Pattern: Global @Tag(HttpServerModule) interceptor → store principal in Context → read via Principal.current() in controller.
See [Authentication & Principal](references/authentication-reference.md) for complete guide with examples.
Context Propagation
Pass computed values from interceptor to controller (auth session, user profile, request metadata) via ru.tinkoff.kora.common.Context:
- Define
Context.Keystatic singleton - Interceptor sets value BEFORE
chain.process() - Controller reads with null-check
See [Context Propagation](references/context-propagation-reference.md) for complete guide.
Configuration
httpServer {
publicApiHttpPort = 8080
privateApiHttpPort = 8085
privateApiHttpMetricsPath = "/metrics"
privateApiHttpReadinessPath = "/system/readiness"
privateApiHttpLivenessPath = "/system/liveness"
virtualThreadsEnabled = false // true requires Java 21+
maxRequestBodySize = "256MiB"
telemetry {
logging { enabled = false }
metrics { enabled = true }
tracing { enabled = true }
}
}
Keep publicApiHttpPort (traffic) and privateApiHttpPort (metrics/probes) separate. See [Configuration](references/configuration-reference.md) for every key and env substitution.
References
| Reference | Covers | |---|---| | [Controller & Routing](references/controller-routing-reference.md) | @HttpController, @HttpRoute, path composition | | [Request Mapping](references/request-mapping-reference.md) | @Path, @Query, @Header, @Cookie, @Json, bodies, @Mapping | | [Response Types](references/response-types-reference.md) | HttpServerResponse, HttpResponseEntity, HttpBody, mappers | | [Interceptors](references/interceptors-reference.md) | HttpServerInterceptor, @InterceptWith, @Tag, order | | [Error Handling](references/error-handling-reference.md) | HttpServerResponseException, global error interceptor | | [Configuration](references/configuration-reference.md) | httpServer keys, ports, telemetry, env vars | | [Authentication & Principal](references/authentication-reference.md) | Custom auth via interceptor, Principal pattern (Kora 1.2.x) | | [Context Propagation](references/context-propagation-reference.md) | Passing values from interceptor to controller via Context |
Assets
| Template | Purpose | |---|---| | [UserController.java](assets/templates/java/UserController.java.template) | CRUD controller (Java) | | [UserController.kt](assets/templates/kotlin/UserController.kt.template) | CRUD controller (Kotlin) | | [LoggingInterceptor.java](assets/templates/java/LoggingInterceptor.java.template) | Global logging interceptor (Java) | | [LoggingInterceptor.kt](assets/templates/kotlin/LoggingInterceptor.kt.template) | Global logging interceptor (Kotlin) | | [ErrorInterceptor.java](assets/templates/java/ErrorInterceptor.java.template) | Global error-to-JSON interceptor (Java) |
Pitfalls
| Symptom | Fix | |---|---| | 404 on a valid URL | The {var} in @HttpRoute path must have a matching @Path argument; base path lives on @HttpRoute, not @HttpController | | Request body is null / not parsed | Add @Json on the method and on the body parameter, and depend on json-module | | @HttpController("/api") does not compile | @HttpController takes no value; put the prefix in each @HttpRoute(path = "/api/...") | | Required parameter throws when missing | Mark it @Nullable (Java) or use a nullable type (Kotlin) | | Global interceptor never runs | Add @Tag(HttpServerModule.class) and @Component; only one global interceptor is allowed | | Looking for HttpServerResponse.ok() builder | It does not exist — use HttpServerResponse.of(...) / HttpResponseEntity.of(...) | | Principal as controller parameter returns 400 | Kora 1.2.x doesn't auto-bridge HttpServerPrincipalExtractor — use interceptor + Context pattern (see [Authentication](references/authentication-reference.md)) | | Context.get() returns null in controller | Value must be set in interceptor BEFORE chain.process(); add null-check (see [Context Propagation](references/context-propagation-reference.md)) |
Evals
Self-check rubric: [evals/evals.json](evals/evals.json).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kora-projects
- Source: kora-projects/kora-skills
- License: Apache-2.0
- Homepage: http://kora-projects.github.io/kora-docs
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.