# 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](/docs/build/client), 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](/docs/build/client).

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 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

```
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](/docs/build/client#results).

## 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 

1

2

3

4

5

6

7

8

9

```
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 

1

2

3

4

5

6

7

```
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 

1

2

3

4

5

6

```
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?

|  | Embedded | Client |
| --- | --- | --- |
| Package | `@nikcli-ai/sdk-next` (in-repo) | `@nikcli-ai/sdk` (npm) |
| Transport | In-memory handler call | HTTP |
| Needs a running server | No | Yes |
| Register tools in code | Yes | No |
| Cross-process / cross-machine | No | Yes |
| Isolation between hosts | None — process-global | Per server |
| Auth | Not applicable | Bearer, 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.

## Related

-   [Build The three ways to build on nikcli, and which to pick.](/docs/build)
-   [Client The same surface over HTTP, installable from npm.](/docs/build/client)
-   [API Reference The endpoints the embedded host serves in memory.](/docs/api)
-   [Tools The built-in tools a registered tool sits beside.](/docs/tools)

---

Source: https://nikcli-ai.dev/docs/build/sdk
