Go SDK

# Go SDK The `pkg/kit` package lets you embed Kit as a library in your Go applications. ## Installation ```bash go get github.com/mark3labs/kit/pkg/kit ``` ## Basic usage ```go package main import ( "context" "log" kit "github.com/mark3labs/kit/pkg/kit" ) func main() { ctx := context.Background() // Create Kit instance with default configuration host, err := kit.New(ctx, nil) if err != nil { log.Fatal(err) } defer host.Close() // Send a prompt response, err := host.Prompt(ctx, "What is 2+2?") if err != nil { log.Fatal(err) } println(response) } ``` ## Functional options (`NewAgent`) For simple programmatic setups, `kit.NewAgent` offers an ergonomic functional-options front door over `kit.New`. Streaming is **enabled by default**; pass `kit.WithStreaming(false)` to opt out. ```go host, err := kit.NewAgent(ctx, kit.WithModel("anthropic/claude-sonnet-4-5-20250929"), kit.WithSystemPrompt("You are a helpful assistant."), kit.WithMaxTokens(8192), kit.WithThinkingLevel("medium"), kit.Ephemeral(), // in-memory session, no persistence ) if err != nil { log.Fatal(err) } defer host.Close() ``` Available options: | Option | Sets | |--------|------| | `WithModel(string)` | `Options.Model` (provider/model format) | | `WithSystemPrompt(string)` | `Options.SystemPrompt` (inline text or file path) | | `WithStreaming(bool)` | `Options.Streaming` (default `true` under `NewAgent`) | | `WithMaxTokens(int)` | `Options.MaxTokens` | | `WithThinkingLevel(string)` | `Options.ThinkingLevel` | | `WithTools(...Tool)` | `Options.Tools` (replaces the default set) | | `WithExtraTools(...Tool)` | `Options.ExtraTools` (adds alongside defaults) | | `WithProviderAPIKey(string)` | `Options.ProviderAPIKey` | | `WithProviderURL(string)` | `Options.ProviderURL` | | `WithConfigFile(string)` | `Options.ConfigFile` | | `WithDebug()` | `Options.Debug = true` | | `Ephemeral()` | `Options.NoSession = true` | Options are applied in order, so later options override earlier ones. `Option` is a plain `func(*Options)`, so you can define your own. For advanced configuration not covered by the helpers (custom MCP config, in-process MCP servers, session backends, MCP task tuning) construct an `Options` value explicitly and call `kit.New`. ### When to use which | Constructor | Use when | |-------------|----------| | `kit.NewAgent(ctx, ...Option)` | Quick programmatic setups; you only need the common fields. Streaming defaults on. | | `kit.New(ctx, *Options)` | You need fields without a `With*` helper (`MCPConfig`, `InProcessMCPServers`, `SessionManager`, MCP task tuning, etc.), or you already hold an `Options` value. | ## Per-instance config isolation Each `kit.New` / `kit.NewAgent` call owns an **isolated configuration store**, so constructing multiple Kit instances in the same process is safe: setting the model, thinking level, or generation parameters on one never affects another, and runtime mutators (`SetModel`, `SetThinkingLevel`) only touch the owning instance. This makes subagent spawning and multi-Kit embedding race-free with no external synchronization required. ```go a, _ := kit.NewAgent(ctx, kit.WithThinkingLevel("low")) b, _ := kit.NewAgent(ctx, kit.WithThinkingLevel("high")) a.SetThinkingLevel(ctx, "medium") // a.GetThinkingLevel() == "medium"; b.GetThinkingLevel() is still "high" ``` ## Multi-turn conversations Conversations retain context automatically across calls: ```go host.Prompt(ctx, "My name is Alice") response, _ := host.Prompt(ctx, "What's my name?") // response: "Your name is Alice" ``` ## Additional prompt methods The SDK provides several prompt variants: | Method | Description | |--------|-------------| | `Prompt(ctx, message)` | Simple prompt, returns response string | | `PromptWithOptions(ctx, message, opts)` | With per-call options | | `PromptResult(ctx, message)` | Returns full `TurnResult` with usage stats | | `PromptResultWithFiles(ctx, message, files)` | Multimodal with file attachments | | `Steer(ctx, instruction)` | System-level steering without user message | | `FollowUp(ctx, text)` | Continue without new user input | ## Custom tools Create custom tools with `kit.NewTool`. The JSON schema is auto-generated from the input struct — no external dependencies required: ```go type WeatherInput struct { City string `json:"city" description:"City name"` } weatherTool := kit.NewTool("get_weather", "Get current weather for a city", func(ctx context.Context, input WeatherInput) (kit.ToolOutput, error) { return kit.TextResult("72°F, sunny in " + input.City), nil }, ) host, _ := kit.New(ctx, &kit.Options{ ExtraTools: []kit.Tool{weatherTool}, }) ``` Struct tags control the schema: - `json:"name"` — parameter name - `description:"..."` — description shown to the LLM - `enum:"a,b,c"` — restrict valid values - `omitempty` — marks the parameter as optional Return values: | Helper | Description | |--------|-------------| | `kit.TextResult(s)` | Successful text result | | `kit.ErrorResult(s)` | Error result (LLM sees it as a tool error) | | `kit.ImageResult(s, data, mediaType)` | Image result with binary data (e.g. `"image/png"`) | | `kit.MediaResult(s, data, mediaType)` | Non-image media result (e.g. `"audio/mpeg"`) | Binary data (images, audio, etc.) in `ToolOutput.Data` is automatically forwarded to the LLM when `MediaType` is set. For advanced use, return a `kit.ToolOutput` struct directly with `Data`, `MediaType`, and `Metadata` fields. Use `kit.NewParallelTool` for tools that are safe to run concurrently. Use `kit.ToolCallIDFromContext(ctx)` to retrieve the LLM-assigned call ID for logging or tracing. ## Generation & provider overrides SDK consumers can configure generation parameters and provider endpoints entirely in-code via `Options`, without touching `.kit.yml` or `viper.Set()`: ```go host, _ := kit.New(ctx, &kit.Options{ Model: "anthropic/claude-sonnet-4-5-20250929", MaxTokens: 16384, // 0 = auto-resolve (env → config → per-model → floor) ThinkingLevel: "high", // "off" | "none" | "minimal" | "low" | "medium" | "high" Temperature: ptrFloat32(0.2), // nil = provider/per-model default ProviderAPIKey: os.Getenv("MY_SECRET"), // overrides pre-existing viper state ProviderURL: "https://proxy.internal/v1", }) func ptrFloat32(v float32) *float32 { return &v } ``` See [Options](/sdk/options#generation-parameters) for the full field reference, including `TopP`, `TopK`, `FrequencyPenalty`, `PresencePenalty`, and `TLSSkipVerify`. ## Event system Subscribe to events for monitoring: ```go unsubscribe := host.OnToolCall(func(event kit.ToolCallEvent) { fmt.Println("Tool called:", event.Name) }) defer unsubscribe() host.OnToolResult(func(event kit.ToolResultEvent) { fmt.Println("Tool result:", event.Name) }) host.OnMessageUpdate(func(event kit.MessageUpdateEvent) { fmt.Print(event.Chunk) }) ``` ## Model management Switch models at runtime: ```go host.SetModel(ctx, "openai/gpt-4o") info := host.GetModelInfo() models := host.GetAvailableModels() ``` ## Dynamic MCP servers Add and remove MCP servers at runtime: ```go n, err := host.AddMCPServer(ctx, "github", kit.MCPServerConfig{ Command: []string{"npx", "-y", "@modelcontextprotocol/server-github"}, }) fmt.Printf("Loaded %d tools\n", n) err = host.RemoveMCPServer("github") servers := host.ListMCPServers() // []kit.MCPServerStatus ``` ### In-process MCP servers Register mcp-go servers running in the same process — zero subprocess overhead: ```go import ( "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) mcpSrv := server.NewMCPServer("my-tools", "1.0.0", server.WithToolCapabilities(true), ) mcpSrv.AddTool(mcp.NewTool("search_docs", mcp.WithDescription("Search documentation"), mcp.WithString("query", mcp.Required()), ), searchHandler) // At init time host, _ := kit.New(ctx, &kit.Options{ InProcessMCPServers: map[string]*kit.MCPServer{ "docs": mcpSrv, }, }) // Or at runtime n, _ := host.AddInProcessMCPServer(ctx, "docs", mcpSrv) ``` ## Runtime skills and context files Kit auto-discovers skills and `AGENTS.md`-style context files during `New()`, but multi-tenant hosts (chatbots, web services, per-user agents) often need to swap these **after** construction. The runtime mutators below recompose the system prompt and apply it to the agent so the next turn picks up the updated instructions — no restart, no file shuffling. ```go // Add a programmatic skill — no file on disk required. host.AddSkill(&kit.Skill{ Name: "polite-french", Description: "Respond in French and always greet the user.", Content: "Always reply in French. Open every response with 'Bonjour'.", }) // Or load one from disk. host.LoadAndAddSkill("/var/skills/refund-policy.md") // Project context (AGENTS.md equivalents): inline content from a DB... host.AddContextFileContent( fmt.Sprintf("session://%s/AGENTS.md", userID), rulesFromDB, ) // ...or load from disk. host.LoadAndAddContextFile("/etc/agents/tenant-acme.md") // Remove individually when a session ends. host.RemoveSkill("polite-french") host.RemoveContextFile(fmt.Sprintf("session://%s/AGENTS.md", userID)) // Or replace the whole set in one call. host.SetSkills(activeSkillsForUser) host.SetContextFiles(activeContextForUser) // Inspect current state (snapshot copies — safe to mutate). skills := host.GetSkills() ctxFiles := host.GetContextFiles() ``` Key points: - **Auto-refresh.** Every `Add*` / `Remove*` / `Set*` call recomposes the system prompt against the captured base prompt (preserving per-model overrides and `--system-prompt` resolution) and pushes the result onto the agent. Call `host.RefreshSystemPrompt()` only if you mutate state through a different path and need to force a re-render. - **Dedup keys.** Skills dedupe by `Name`; context files dedupe by `Path`. Re-adding the same key replaces the entry instead of appending a duplicate. - **Path is opaque.** `ContextFile.Path` does not have to point at a real file — it's only used for dedup and for the `Instructions from: ` header injected into the prompt. URIs like `session://user-123/AGENTS.md` work fine. - **Thread safety.** All readers and mutators are safe to call concurrently from multiple goroutines; the underlying state is guarded by an internal `RWMutex`. - **Init-time options still apply.** `Options.Skills`, `Options.SkillsDir`, `Options.NoSkills`, and `Options.NoContextFiles` continue to control the startup set; the runtime API mutates from whatever state `New()` produced. See [SDK options](/sdk/options#skills--configuration). ## MCP prompts and resources Query prompts and resources exposed by connected MCP servers: ```go // List and expand prompts prompts := host.ListMCPPrompts() result, _ := host.GetMCPPrompt(ctx, "server", "prompt-name", map[string]string{"key": "value"}) // List and read resources resources := host.ListMCPResources() content, _ := host.ReadMCPResource(ctx, "server", "file:///path") ``` ## MCP tasks (long-running tools) Kit advertises [MCP task support](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) during `initialize`, so cooperating servers can return a `taskId` immediately and let Kit poll `tasks/get` / `tasks/result` until the operation completes. This avoids HTTP/SSE proxy timeouts on long tools and gives you clean cancellation via context. ```go host, _ := kit.New(ctx, &kit.Options{ MCPTaskMode: map[string]kit.MCPTaskMode{ "build-server": kit.MCPTaskModeAlways, }, MCPTaskProgress: func(p kit.MCPTaskProgress) { log.Printf("%s: %s", p.TaskID, p.Status) }, }) // Inspect / cancel in-flight tasks tasks, _ := host.ListMCPTasks(ctx, "build-server") _, _ = host.CancelMCPTask(ctx, "build-server", tasks[0].TaskID) ``` Defaults to `MCPTaskModeAuto` per server, so any existing MCP server keeps its previous synchronous behaviour. See [SDK options → MCP Tasks](/sdk/options#mcp-tasks) for the full surface. ## Context and compaction Monitor and manage context usage: ```go tokens := host.EstimateContextTokens() stats := host.GetContextStats() if host.ShouldCompact() { result, err := host.Compact(ctx, nil, "") } ``` ## In-process subagents Spawn child Kit instances without subprocess overhead: ```go result, err := host.Subagent(ctx, kit.SubagentConfig{ Prompt: "Analyze the test files", Model: "anthropic/claude-haiku-3-5-20241022", NoSession: true, Timeout: 2 * time.Minute, }) ``` See [Options](/sdk/options), [Callbacks](/sdk/callbacks), and [Sessions](/sdk/sessions) for more details.