Skip to content
nikcli
Docs
Documentation

Embedded SDK

Run the whole nikcli server in your own process — same routes, same handlers, no listener and no socket.

Overview

@nikcli-ai/sdk-next is an embedded host. It builds the same typed client as the network SDK, but hands it a fetch that calls the server's request handler directly in memory. No port is bound, no socket is opened, and nothing leaves the process.

Everything else is unchanged: the same routing, the same middleware, the same handlers, codecs and errors. Code written against the network client works against the embedded host without edits — which is the point.

Availability

In-repo today, not on npm

@nikcli-ai/sdk-next is a workspace package and is not published. It is usable from inside the monorepo — and it is the migration target for the generated @nikcli-ai/sdk — but an external project cannot install it yet. If you are building outside this repository, take the client path.

It also depends on nikcli-ai itself rather than on a contract package, because embedding means running the real server: the whole agent is in your process, not a stub of it.

Creating a host

NikCli.create returns the client surface, so the call sites look like the network client with an Effect wrapper around construction.

ts
import { NikCli } from "@nikcli-ai/sdk-next"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const nikcli = yield* NikCli.create({ directory: process.cwd() })

  const session = yield* Effect.promise(() =>
    nikcli.session.create({ title: "embedded" }),
  )

  return yield* Effect.promise(() =>
    nikcli.session.prompt({
      sessionID: session.data!.id,
      parts: [{ type: "text", text: "What changed in the last commit?" }],
    }),
  )
})

The result envelope is the same one the network client uses — calls resolve with { data, error } rather than rejecting. See The result envelope.

As an Effect layer

The same constructor is available as a service, for code that would rather take the host as a dependency than build one.

ts
import { NikCli } from "@nikcli-ai/sdk-next"
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const nikcli = yield* NikCli.Service
  return yield* Effect.promise(() => nikcli.session.list())
})

await Effect.runPromise(program.pipe(Effect.provide(NikCli.layer)))
The layer is not a second implementation

NikCli.layer only adapts NikCli.create() for dependency injection. There is one host, reachable two ways.

Directory binding

The host binds requests to its directory — defaulting to process.cwd() — by setting x-nikcli-directory on any call that does not already carry its own directory query parameter or header. From there the request flows through the same per-request instance middleware as network traffic.

ts
const nikcli = yield* NikCli.create({ directory: "/Users/me/project" })

// bound to the host directory
yield* Effect.promise(() => nikcli.session.list())

// explicit selection still wins
yield* Effect.promise(() => nikcli.session.list({ directory: "/Users/me/other" }))

Registering tools

The embedded host can do one thing the network client cannot: register tools directly into the instance's ToolRegistry — the same registry sessions consult and GET /experimental/tool/ids reports.

ts
import { NikCli, Tool } from "@nikcli-ai/sdk-next"

const program = Effect.gen(function* () {
  const nikcli = yield* NikCli.create({ directory: process.cwd() })
  yield* nikcli.tools.register(MyTool)
})
Why this is local-only

A tool is executable code. It cannot travel over HTTP, so there is no endpoint that could offer this and no way to do it from the network client. Registration is the reason to embed rather than connect.

Shared instance state

Two hosts in one process are not isolated

Unlike a network server, the embedded host shares process-global instance state. Two create() calls in the same process observe the same projects, sessions and registered tools for a given directory. Treat the host as a process-wide singleton; if you need isolation, you need separate processes.

Embedded or client?

EmbeddedClient
Package@nikcli-ai/sdk-next (in-repo)@nikcli-ai/sdk (npm)
TransportIn-memory handler callHTTP
Needs a running serverNoYes
Register tools in codeYesNo
Cross-process / cross-machineNoYes
Isolation between hostsNone — process-globalPer server
AuthNot applicableBearer, basic, Tailscale

If your program is a script, a bot or a separate interface, connect. If your program is the product and nikcli is a component of it, embed.