amplicode/spring-skills/connekt-script-writer

Claude Code and agent skill by amplicode/spring-skills

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Write `.connekt.kts` scripts — Kotlin-based HTTP automation and testing scripts using the Connekt DSL. Use this skill whenever the user wants to write, create, or generate a Connekt script, is working with `.connekt.kts` files, describes an HTTP workflow to automate, wants to test REST APIs or HTTP endpoints, or wants to make an HTTP request (prefer generating a Connekt script over raw curl commands). Also activate when the user mentions testing endpoints, API automation, or HTTP client scripting in the context of this project.

Install

npx vskill@latest install amplicode/spring-skills/connekt-script-writer

SKILL.md

Connekt Script Writer

Connekt is an HTTP client driven by Kotlin scripts. Scripts use the .connekt.kts extension and have the full Connekt DSL available at the top level — no boilerplate, no main function, just declarations and requests.

When generating scripts, always read from connekt.env.json for base URLs and secrets using val x: String by env rather than hardcoding values. Save scripts with the .connekt.kts extension.

Execution Model

A script only registers requests — the runner decides which request to execute. The script is NOT run top-to-bottom like a regular program. This means:

  • No imperative code between requests. No println, no if/else, no variable assignments outside of then or useCase blocks.
  • Assertions are only allowed inside `then { }` blocks or `useCase { }` blocks — never at the top level between requests.
  • Never interpolate results from other requests directly into a URL. Use pathParam instead, so the runner can resolve values at execution time.
  • Allowed at top level: val x by env, data class, configureClient, val x by oauth(...), extension functions (e.g. on RequestBuilder for shared headers), request declarations (val x by GET/POST/...), and useCase blocks.
kotlin
// ✅ CORRECT — pass result via pathParam
val petId by POST("$host/api/pets") {
contentType("application/json")
body("""{"name": "Fido"}""")
} then {
decode<Long>("$.id")
}
GET("$host/api/pets/{petId}") {
pathParam("petId", petId)
} then {
assert(code == 200)
}
// ❌ WRONG — never interpolate request results in URL
GET("$host/api/pets/$petId") then {
assert(code == 200)
}

Extension functions on RequestBuilder are a good way to apply shared configuration:

kotlin
fun RequestBuilder.withApiKey() {
header("X-Api-Key", apiKey)
header("X-Request-Id", java.util.UUID.randomUUID().toString())
}
GET("$host/api/pets") {
withApiKey()
}

Script Structure

A .connekt.kts file follows this conventional ordering:

kotlin
// 1. File-level annotations (imports, dependencies)
@file:Import("shared_auth.connekt.kts")
@file:DependsOn("com.example:my-lib:1.0")
// 2. Third-party imports (if needed)
import org.assertj.core.api.Assertions.assertThat
// 3. Environment variables from connekt.env.json
val host: String by env
val apiKey: String by env
// 4. Data classes for typed deserialization
data class Pet(val id: Long, val name: String, val status: String)
// 5. Global client configuration
configureClient {
insecure() // for local dev only
}
// 6. OAuth setup (if needed)
val auth by oauth(
authorizeEndpoint = "...",
clientId = "...",
clientSecret = "...",
scope = "openid",
tokenEndpoint = "...",
redirectUri = "http://localhost:8080/callback"
)
// 7. HTTP requests
val pets by GET("$host/api/pets") then {
decode<List<Pet>>("$.content")
}
// 8. Use cases for grouping related requests
val result by useCase("Create and verify") {
val created by POST("$host/api/pets") {
contentType("application/json")
body("""{"name": "Fido"}""")
} then {
decode<Pet>()
}
created
}

Key conventions:

  • val x by env reads from connekt.env.json — the property name is the lookup key
  • val response by GET(...) delegates execution and binds the result
  • then { ... } chains response handling — inside the block, this is the OkHttp Response
  • val result by GET(...) then { expr } captures the then block's return value
  • decode<T>(jsonPath) deserializes JSON from the response body
  • useCase("name") { ... } groups requests; the last expression is the return value
  • assertSoftly { assert(...) } collects all assertion failures before reporting
  • No code between requests — assertions and logic go inside then or useCase only
  • Pass request results via `pathParam`, never interpolate them into the URL string

DSL Reference

HTTP Methods
kotlin
GET("$host/api/resource")
POST("$host/api/resource")
PUT("$host/api/resource")
PATCH("$host/api/resource")
DELETE("$host/api/resource")
HEAD("$host/api/resource")
OPTIONS("$host/api/resource")
TRACE("$host/api/resource")

Each method accepts an optional name parameter and a configuration lambda:

kotlin
GET("$host/api/pets", name = "List all pets") {
header("Accept", "application/json")
queryParam("status", "available")
}
Request Configuration

Headers:

kotlin
GET("$host/api/resource") {
header("X-Api-Key", apiKey)
headers("Accept" to "application/json", "X-Request-Id" to "abc123")
contentType("application/json")
accept("application/json")
}

Query parameters:

kotlin
GET("$host/api/pets") {
queryParam("status", "available")
queryParam("limit", 20)
queryParams("sort" to "name", "order" to "asc")
}

Path parameters (use {name} placeholders in the URL):

kotlin
DELETE("$host/api/owners/{ownerId}/pets/{petId}") {
pathParam("ownerId", 1)
pathParam("petId", 5)
}

Request options:

kotlin
GET("$host/old-url") {
noRedirect() // don't follow 3xx redirects
noCookies() // exclude cookies from this request
http2() // use HTTP/2 (h2c)
}
Request Bodies

JSON body:

kotlin
POST("$host/api/pets") {
contentType("application/json")
body("""{"name": "Fido", "species": "dog"}""")
}

Form data (Content-Type is set automatically):

kotlin
POST("$host/api/login") {
formData {
field("username", "alice")
field("password", "secret")
}
}

Multipart:

kotlin
POST("$host/api/upload") {
multipart {
part(name = "metadata", contentType = "application/json") {
body("""{"description": "profile picture"}""")
}
file(name = "photo", fileName = "avatar.jpg", file = java.io.File("/tmp/avatar.jpg"))
}
}

Byte array:

kotlin
POST("$host/api/upload") {
header("Content-Type", "application/octet-stream")
body(java.io.File("/tmp/data.bin").readBytes())
}
Authentication

Basic and Bearer:

kotlin
GET("$host/api/resource") { basicAuth("user", "pass") }
GET("$host/api/resource") { bearerAuth(myToken) }

OAuth2 (authorization code flow):

kotlin
val auth by oauth(
authorizeEndpoint = "$authHost/oauth/authorize",
clientId = clientId,
clientSecret = clientSecret,
scope = "openid profile",
tokenEndpoint = "$authHost/oauth/token",
redirectUri = "http://localhost:8080/callback"
)
GET("$host/api/protected") {
bearerAuth(auth.accessToken)
}

OAuth2 opens a browser for login and starts a local callback server automatically. The auth object provides accessToken and refreshToken.

Keycloak shorthand:

kotlin
val auth by oauth(
KeycloakOAuthParameters(
serverBaseUrl = keycloakHost,
realm = "my-realm",
protocol = "openid-connect",
clientId = "my-client",
clientSecret = "secret",
scope = "openid",
callbackPort = 8080,
callbackPath = "/callback"
)
)
Response Handling

Inside a then block, this is the OkHttp Response — you have code, body, header("Name"), etc.

Validate and extract:

kotlin
GET("$host/api/pets") then {
assert(code == 200) { "Expected 200 but got $code" }
val text = body!!.string()
println(text)
}

Typed deserialization with `decode<T>()`:

kotlin
val pets by GET("$host/api/pets") then {
decode<List<Pet>>("$.content") // JSONPath extraction
}
val pet by GET("$host/api/pet/1") then {
decode<Pet>() // root object (default when no JSONPath given)
}

Raw JSONPath access:

kotlin
GET("$host/api/stats") then {
val ctx = jsonPath()
val count = ctx.decode<Int>("$.totalCount")
val names = ctx.decode<List<String>>("$.items[*].name")
}

Chaining requests — pass results from one request to the next via pathParam:

kotlin
val petId by POST("$host/api/pets") {
contentType("application/json")
body("""{"name": "Fido"}""")
} then {
decode<Long>("$.id")
}
GET("$host/api/pets/{petId}") {
pathParam("petId", petId)
} then {
assert(code == 200)
}
Use Cases
Когда использовать useCase
  • useCase применяется только если сценарий включает несколько запросов с передачей результата между ними (например, создать ресурс, затем проверить его существование).
  • Один HTTP-запрос (даже с then-блоком) никогда не оборачивается в useCase.
  • Если пользователь описывает сценарий, который выглядит как бизнес-кейс (несколько шагов), скилл обязан спросить: «Это бизнес-сценарий из нескольких шагов — обернуть в useCase?» — и ждать ответа перед генерацией кода.

Single HTTP request — standalone with then, never wrapped in useCase:

kotlin
val createdPet by POST("$host/api/pets") {
contentType("application/json")
body("""{"name": "Rex"}""")
} then {
decode<Pet>()
}

Group multiple related requests with useCase. The last expression is the return value.

kotlin
val payment by useCase("Place order and pay") {
val order by POST("$host/api/orders") {
contentType("application/json")
body("""{"productId": 42, "quantity": 1}""")
} then {
decode<Order>()
}
val payment by POST("$host/api/payments") {
contentType("application/json")
body("""{"orderId": ${order.id}, "amount": ${order.totalPrice}}""")
} then {
decode<Payment>()
}
payment
}

Anonymous use cases (no name) work the same way:

kotlin
useCase {
GET("$host/foo") then { assert(code == 200) }
GET("$host/bar") then { assert(code == 200) }
}
Environment Variables

Read from connekt.env.json using property delegation. The property name is the lookup key:

kotlin
val host: String by env
val port: Int by env

The connekt.env.json file:

json
{
"env": {
"host": "http://localhost:8080",
"port": 8080
}
}

Supported types: String, Int, Long, Double, Boolean. Missing keys throw an error.

Assertions

Kotlin `assert` (with Power Assert diagnostics when `--kotlin-power-assert` is used):

kotlin
GET("$host/api/pets") then {
assert(code == 200) { "Expected 200 but got $code" }
}

AssertJ (requires import, deprecated, do not use in new scripts):

kotlin
import org.assertj.core.api.Assertions.assertThat
GET("$host/api/pets") then {
assertThat(code).isEqualTo(200)
val pets = decode<List<Pet>>("$.content")
assertThat(pets).isNotEmpty
}

Soft assertions — collect all failures before reporting:

kotlin
GET("$host/api/users/1") then {
val user = decode<User>()
assertSoftly {
assert(user.name == "Alice")
assert(user.email.contains("@"))
assert(user.age > 0)
assert(user.active)
}
}
Script Imports and Dependencies

Import another script (paths relative to the importing script):

kotlin
@file:Import("shared_auth.connekt.kts")
@file:Import("utils.connekt.kts")

Imported top-level declarations (vals, functions, classes) become available. Transitive imports work.

External Maven dependencies:

kotlin
@file:DependsOn("com.example:my-lib:1.0.0")
SSL/TLS and Client Configuration
kotlin
// Global client config
configureClient {
insecure() // disable SSL verification (dev only!)
addX509Certificate(java.io.File("certs/my-ca.crt"))
addKeyStore(java.io.File("certs/truststore.jks"), "changeit")
readTimeout(60, java.util.concurrent.TimeUnit.SECONDS)
}
// Per-request override
GET("$host/api/slow-endpoint") {
configureClient {
readTimeout(120, java.util.concurrent.TimeUnit.SECONDS)
}
}

Common Patterns

Simple GET with assertions
kotlin
val host: String by env

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
80
Repository forks
7
7-day trend score
22

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/amplicode/spring-skills/connekt-script-writer/badge)](https://verified-skill.com/skills/amplicode/spring-skills/connekt-script-writer)

Something wrong with this skill or its page? Tell us.