AgentStack
MCP verified MIT Self-run

Agents KT

mcp-deep-codeai-agents-kt · by Deep-CodeAI

Typed Kotlin DSL framework for AI agent systems.

No reviews yet
0 installs
1 views
0.0% view→install

Install

$ agentstack add mcp-deep-codeai-agents-kt

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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 Used
  • Environment & secrets Used
  • Dynamic code execution Used

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.

Are you the author of Agents KT? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

The typed agent runtime for the JVM. Compiler-enforced boundaries. Least-privilege tools. Audit evidence by construction. MCP-native.

Built for teams that need to say exactly what an AI system is allowed to do — with the in-runtime boundaries it does enforce, and the non-goals it doesn't, stated up front: see Security Model and the threat model.


Agents.KT is built for teams that need to know exactly what an AI system is allowed to do. Every agent is Agent: one input type, one output type, one job. Type mismatches and wrong compositions are caught by the compiler where composition is purely type-driven, and structural misuses fail fast at construction time.

The 0.6–0.7 line turns those boundaries into reviewable evidence: deterministic permission manifests, runtime manifestHash correlation, JSONL audit export, a tamper-evident ToolAuditLedger, OTel/LangSmith/Langfuse bridge adapters, before-interceptor policy hooks, declarative tool policy metadata, reasoning/thinking stream, vendor-neutral prompt caching with prefix-stability guard, snapshot/resume with manifest-hash restore guard, and typed audit events for hallucinated tool calls. It is not a compliance product, and it does not OS-sandbox arbitrary tool code — [Security Model](#security-model) is precise about which boundaries are kernel-enforced versus deployer-owned. Agents.KT is the runtime behind agents-kt.dev.


First 10 Minutes

Requirements: JDK 21+, Kotlin 2.x, Gradle

// build.gradle.kts
dependencies {
    implementation("ai.deep-code:agents-kt:0.8.2")
}

Or clone and build from source:

git clone https://github.com/Deep-CodeAI/Agents.KT.git
cd Agents.KT
./gradlew test
# #2807 — static analysis. Baseline freezes existing violations;
# new code is held to the rules in `detekt.yml`.
./gradlew detekt

Building an agent

An Agent has one input type, one output type, one or more skills, and an optional model for the agentic path. Skills come in two shapes:

  • implementedBy { input -> output } — deterministic Kotlin lambda. No LLM. Fastest and fully testable.
  • tools(...) with a model { } block — agentic. The framework runs a multi-turn loop where the LLM picks tools from the skill's allowlist; the runtime refuses anything outside it (authorization, not just prompting).
val coder = agent("coder") {
    model { claude("claude-opus-4-7"); apiKey = System.getenv("ANTHROPIC_API_KEY") }
    lateinit var writeFile: Tool, Any?>
    lateinit var compile:   Tool, Any?>
    tools {
        writeFile = tool("write_file", "Write a source file") { args -> writeFile(args) }
        compile   = tool("compile",    "Compile the bundle")  { args -> compile(args)   }
    }
    skills {
        skill("write-code", "Implement endpoints") { tools(writeFile, compile) }
    }
}

When multiple skills can take the same input type, the LLM (or a manual skillSelection { }) routes between them.

Composing agents

Composition is purely type-driven — the compiler enforces that boundaries line up. Six primitives ship today:

| Primitive | Shape | What it does | |---|---|---| | a then b | Pipeline | Sequential. a.OUT must equal b.IN, enforced at compile time. | | a / b | Parallel> | Run both branches concurrently against the same input; collect results. | | agent.branch { … } | Branch | Route per source-output shape (onClass then …, onElse then …); sealed sources are exhaustiveness-checked. | | teacher wrap student | Pipeline | Teacher-student: teacher.OUT (a String) becomes student's per-call system prompt. | | forum { members(…); captain = … } | Forum | Council of members with a captain that emits the verdict. | | triage handoff { … } | Branch | Named hand-off to specialists on the source's typed output (#3871). Same routing as branch + an audit signal (onHandoff / HandoffPerformed); the target never sees the source's history — typed input only, unlike Swarm-style handoff. |

A single agent instance can only be placed in one composition — wiring it into two spots fails fast at construction. See [docs/composition.md](docs/composition.md) for the operator reference, [docs/patterns.md](docs/patterns.md) for the Anthropic "Building Effective Agents" catalog mapped 1:1 onto these primitives, and [docs/comparison.md](docs/comparison.md) for the release narrative.

One typed pipeline

val parse = agent("parse") {
    skills {
        skill("parse-spec", "Splits raw text into a structured specification") {
            implementedBy { input -> Specification(input.text.split(",").map { it.trim() }) }
        }
    }
}
val generate = agent("generate") {
    skills {
        skill("gen-code", "Generates stub functions for each endpoint") {
            implementedBy { spec -> CodeBundle(spec.endpoints.joinToString("\n") { "fun $it() {}" }) }
        }
    }
}
val review = agent("review") {
    skills {
        skill("review-code", "Approves code if it is non-empty") {
            implementedBy { code -> ReviewResult(approved = code.source.isNotBlank()) }
        }
    }
}

// Compiler checks every boundary
val pipeline = parse then generate then review
// Pipeline

val result = pipeline(RawText("getUsers, createUser, deleteUser"))
// ReviewResult(approved=true)

Testing details — task names, integration test setup, mutation testing, and how to write tests with a stub ModelClient — are in [docs/testing.md](docs/testing.md). Build prerequisites are on the Building From Source wiki page. macOS contributors: the Linux sandbox tests (bwrap/firejail) need a Linux kernel, so they auto-skip on macOS — CI runs them on a native Ubuntu runner; see [docs/testing.md → Linux sandbox tests](docs/testing.md#linux-sandbox-tests-bwrap--firejail).


What Agents.KT Owns

Agents.KT owns the runtime boundary model:

  • Typed Agent contracts and composition operators.
  • Per-skill tool authorization and typed tool handles.
  • MCP client/server surfaces that share the same tool/skill shape.
  • Permission manifests, declarative tool policies, and runtime audit correlation.
  • JSONL audit export plus OTel, LangSmith, and Langfuse adapters through ObservabilityBridge.
  • Local-first JVM execution with Ollama by default and cloud providers when you choose them.

These are the pieces the framework can make deterministic, testable, and reviewable in code. Start with [permission manifests](docs/permission-manifest.md), the [threat model](docs/threat-model.md), the [regulated deployment guide](docs/regulated-deployment.md), and the [comparison page](docs/comparison.md) for the release narrative. The manifest is also reachable outside Gradle via the standalone [agents-kt CLI](docs/cli.md) (generate / inspect / verify) — a drop-in CI gate that fails when a change widens a capability boundary (#1923).

What Agents.KT Does Not Own

Agents.KT emits evidence and enforces in-runtime boundaries; it does not replace your deployment controls:

  • It is not a legal compliance product. It produces compliance-supporting artifacts and audit-ready evidence; your counsel and compliance team still classify the use case.
  • It does not yet OS-sandbox arbitrary Kotlin lambdas. A tool's declared ToolPolicy is now enforced in-JVM for filesystem-path arguments (Layer 1, #2890 — a declared write/read glob blocks out-of-policy absolute paths before the executor runs; see [docs/tool-policy-enforcement.md](docs/tool-policy-enforcement.md)), but a lambda can still touch paths it doesn't take as arguments, and network/environment isolation needs the Layer-2 OS/container sandbox (#1916), which remains a deployer responsibility for now.
  • It does not rate-limit public MCP ingress. Use McpServer auth/policy plus your gateway.
  • It does not ship a universal prompt-injection classifier. Wire your chosen detector through onBeforeTurn.
  • It does not try to be a vector-store, eval-suite, or hosted orchestration platform. It is the typed JVM runtime boundary underneath those integrations.

Why Agents.KT

Most agent frameworks let you wire anything to anything. Agents.KT says no.

| Problem | Agents.KT answer | |---------|-----------------| | God-agents with unlimited responsibilities | Agent — one type contract, compiler-enforced SRP | | Runtime type mismatches between agents | then requires A.OUT == B.IN — compile error otherwise | | The same agent instance wired into two places | Single-placement rule — IllegalArgumentException at construction time | | LLM doesn't know which skill to use | Manual skillSelection {} routing or automatic LLM routing — descriptions sell each skill to the router | | LLM doesn't know what context to load | knowledge("key", "description") { } entries — LLM reads descriptions before deciding to call | | Flat pipelines only | Composition operators covering sequential, forum, parallel, iterative, and branching patterns | | LLM output is an untyped string | @Generable + @Guide — JSON Schema, provider constrained decoding, prompt fragments, lenient deserializer, and PartiallyGenerated; KSP-generated metadata avoids runtime reflection when present | | MCP tools are wrappers, not first-class | McpClient.tools() returns first-class McpTool handles, while toolSkills() keeps the prompt-style skill adapter; agents can also be exposed as MCP servers via McpServer.from(agent) | | Permission model is stringly-typed | grants { tools(writeFile, compile) } — actual Tool references, compiler-validated (planned Phase 2) | | No testing story | AgentUnit — deterministic through semantic assertions (planned) | | JVM frameworks require Java installed | Native CLI binary via GraalVM (planned Phase 2 Priority) |


What's Shipped

This section is the index — every claim below points to working code in main, with the issue number that established it. Topical detail lives in [docs/](docs/).

Implemented today

These APIs work in main, are unit-tested, and are exercised by integration tests (./gradlew test for default suite, ./gradlew integrationTest for live-LLM):

  • Typed agentsAgent with at least one skill producing OUT, validated at construction. See [docs/skills.md](docs/skills.md).
  • Skills with knowledgeskill { knowledge("key", "...") { } }, lazy-loaded per call. See [docs/skills.md#shared-knowledge](docs/skills.md#shared-knowledge).
  • Agentic loop with tool calling — multi-turn chat ↔ tools driven by the model. See [docs/model-and-tools.md](docs/model-and-tools.md).
  • Eight model providersmodel { ollama(...) } for local/cloud Ollama, model { claude("claude-opus-4-7"); apiKey = ... } for Anthropic's Messages API, model { openai("gpt-4o"); apiKey = ... } for OpenAI Chat Completions, model { deepseek("deepseek-v4-flash"); apiKey = ... } for DeepSeek's OpenAI-compatible API, model { kimi("moonshot-v1-8k"); apiKey = ... } for Kimi (Moonshot AI) — long-context (-32k / -128k) variants via the model string (#2697), model { openrouter("anthropic/claude-3.5-sonnet"); apiKey = ...; openRouterHttpReferer = "https://your.app"; openRouterXTitle = "Your App" } for OpenRouter — the OpenAI-compatible multi-provider aggregator that fronts hundreds of upstream models behind one API, with optional HTTP-Referer / X-Title attribution headers honored across the catalog (#2701), model { perplexity("sonar"); apiKey = ... } for Perplexity's Sonar web-grounded API (OpenAI-compatible; sonar / sonar-pro / sonar-reasoning-pro / sonar-deep-research, #3675), and model { gemini("gemini-2.5-flash"); apiKey = ... } for Google Gemini's Generative Language API — a full from-scratch adapter (not OpenAI-compatible): contents/parts with user/model roles, systemInstruction, functionDeclarations tool calling, native SSE streaming, responseJsonSchema constrained decoding, thought-summary reasoning, and inlineData vision (#1917). All eight go through one ModelClient interface — LlmMessage / LlmResponse are provider-agnostic, tools/system/role mapping is per-adapter (#1644, #1656).
  • Typed tools via @Generabletool(...) with reflection-built JSON Schema; additionalProperties: false; sealed-discriminator validation (#658, #661, #699).
  • Provider-neutral tool handles — local typed tool handles and MCP-discovered tools share Tool; McpClient.tools() returns McpTool, String> for grants/manifests/policy work while toolSkills() remains available for primary-skill use (#1948).
  • Provider constrained decoding for @Generable outputs — agentic skills returning @Generable types pass their JSON Schema to supporting providers automatically: OpenAI response_format.json_schema, Ollama format, and Anthropic's forced structured-output tool pattern (#1949).
  • Reasoning/thinking stream — opt-in model { reasoning(budgetTokens = ..., effort = ...) } surfaces a model's reasoning as AgentEvent.Reasoning, a channel separate from the answer Token stream, so a production UI can render live reasoning instead of a spinner. Claude (extended thinking), DeepSeek (reasoning_content), Ollama (thinking), and Gemini (thought summaries via thinkingConfig.includeThoughts) emit reasoning text; OpenAI Chat Completions reports reasoning_effort + reasoning token counts only (no text). Off by default (#2406). See [docs/streaming.md](docs/streaming.md).
  • Typed tool refs in skill allowliststool(...) returns a Tool handle; skill { tools(writeFile, compile) } accepts handles, the IDE catches typos (#1015–#1017). The legacy tools("name") string form remains for built-in tools and runtime-discovered MCP names but produces a deprecation warning.
  • Declarative tool policies + in-JVM filesystem enforcementtool { policy { risk = ToolRisk.Medium; filesystem { write("/uploads/**") }; network { denyAll() } } } records expected filesystem/network/environment scope for manifests and audit events, and the declared filesystem path-arguments are now enforced at runtime by default (Layer 1, #2890): an out-of-policy absolute path is denied before the executor runs, surfacing via onToolDenied / PipelineEvent.ToolDenied. Opt-in by declaration; enforceToolPolicies = false opts out. OS/container sandboxing of executors (network/env, subprocess isolation) is the separate Layer-2 work (#1916). See [docs/tool-policy-enforcement.md](docs/tool-policy-enforcement.md).
  • Permission manifestsagent.permissionManifest() and pipeline.permissionManifest() emit deterministic JSON/YAML capability graphs with agents, skills, tools, memory, MCP, providers, budgets, guardrails, composition structure, masked secrets, and a SHA-256 hash that is attached to runtime events (#1912). See [docs/permission-manifest.md](docs/permission-manifest.md).
  • Per-skill tool authorization — runtime allowlist; the prompt's "Available tools" listing is descriptive, the security boundary is the runtime check (#630). See [docs/model-and-tools.md#tool-authorization-model](docs/model-and-tools.md#tool-authorization-model).
  • Before interceptorsonBeforeSkill, onBeforeTurn, and onBeforeToolCall return Decision (Proceed, ProceedWith, Deny, Substitute) for dynamic policy, prompt filtering, argument mutation, and synthetic results (#1907). See [docs/interceptors.md](docs/interceptors.md).
  • Inline tool-call fallback — auto-recovery when an Ollama model rejects native tools (e.g. gemma3:4b) — strips the field, injects inline JSON format prompt, retries (#702, #706). See [docs/model-and-tools.md#inline-tool-call-fallback-ollama-models-without-native-tool-support](docs/model-and-tools.md#inline-tool-call-fallback-ollama-models-without-native-tool-support).
  • **Compositi

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.