Skip to main content

max / makenotwork

5.2 KB · 87 lines History Blame Raw
1 # Kberg
2
3 MCP bridge for connecting local LLMs to desktop apps.
4
5 Named after Königsberg — the city whose seven bridges seeded graph theory (Euler, 1736). The library is a bridge in that sense: it routes tool-call edges between an LLM node and a set of app nodes.
6
7 ## Ships
8
9 - `Tool` trait + `ToolRegistry` — the app-side action surface.
10 - `ResourceRegistry` — the app-side object surface: live things a client lists, reads, and subscribes to. Dynamic (register/remove at runtime); `notify_updated` pushes a change to subscribed clients.
11 - Streamable HTTP MCP server (JSON-RPC 2.0) — how any MCP-speaking client reaches your app. POST for requests, a GET SSE stream for server-initiated notifications, DELETE to end a session.
12 - Provider-agnostic `Agent` + `InferenceProvider` trait — the tool-use loop is decoupled from the LLM backend.
13 - `provider::ollama::OllamaProvider` — reference implementation for a local Ollama server.
14
15 ## Scope
16
17 Kberg is a *bridge*, not a model manager. You bring the inference endpoint (a local Ollama, LM Studio, vLLM, or a hosted API); Kberg wires it to the app-side tool surface. Future providers (OpenAI, Anthropic, OpenAI-compatible) drop in behind `InferenceProvider` without touching the agent loop.
18
19 ## Guardrails
20
21 Every tool is classified when it's registered:
22
23 | Kind | Meaning |
24 |---|---|
25 | `Read` | No state change. Freely callable subject to app-level connection auth. |
26 | `Write(WriteCapability)` | Mutates app state. Bound to a named capability, granted per session. |
27
28 Writes are the finite, app-declared vocabulary of permissions the LLM can be given — webhook-style. A driver session holds a `HashSet<String>` of granted capability IDs; any write tool call whose capability is not in the set is refused with a stable, model-visible error.
29
30 Capability IDs are a public contract the app owns. Renaming means a new capability + a deprecated old one — you do not silently reassign an existing id.
31
32 Arguments are then checked against the tool's own `input_schema` before the tool runs, in that order: a caller who may not invoke a tool at all learns that, rather than receiving a critique of arguments it was never entitled to send. Validation covers the schema keywords tool surfaces actually use (`type`, `properties`, `required`, `enum`, `additionalProperties: false`) and treats anything it does not recognise as unchecked, never as invalid. Failures name each offending field, and the agent loop hands that back to the model as a correctable diagnostic.
33
34 ## v1 primitive matrix
35
36 | Primitive | Status | Purpose |
37 |---|---|---|
38 | `Tool` + `ToolRegistry` | shipped | register app functions as MCP tools |
39 | `ToolKind::Read / Write(capability)` | shipped | webhook-style write permissions |
40 | Streamable HTTP MCP server | shipped | speak MCP over local HTTP (POST + GET SSE + DELETE) |
41 | `ResourceRegistry` (list / read) | shipped | expose live objects a client lists and reads |
42 | Resource subscriptions + server push | shipped | `resources/subscribe` + `notifications/resources/updated` over SSE |
43 | Request cancellation | shipped | `notifications/cancelled` both ways; app aborts an in-flight call and tells the client |
44 | `InferenceProvider` + `Agent` | shipped | provider-agnostic tool-use loop |
45 | Ollama provider | shipped | drive tools from a local Ollama model |
46 | Hard refusals (`Refusal`) | shipped | registered "not offered" tools with stable reason |
47 | Compact surface projection | shipped | `SurfaceProjection::Compact` filters to `small_model_safe` tools |
48 | Argument validation | shipped | args checked against the tool's own `input_schema` before dispatch; failures name the offending fields |
49 | Repair turn | shipped | the agent loop feeds a slot diagnostic back so a model corrects a bad call instead of guessing |
50 | Handles | planned | opaque, wire-safe entity IDs (never leak SHA-256 / row PKs to the model) |
51 | Preview / commit | planned | two-step writes: dry-run returns a preview, commit executes |
52 | Scope | planned | first-class scope arg (`book`, `library`, `inbox`) so small models don't re-specify it |
53 | Human-confirm gating | planned | writes above app-defined thresholds return `pending_human_confirm(ticket)` |
54 | Audit log | planned | every call recorded, especially writes |
55 | Sidecar aggregator | planned | one driver, N apps, namespaced tool ids |
56 | Additional providers | planned | OpenAI, Anthropic, generic OpenAI-compatible |
57
58 ## Layout
59
60 ```
61 src/
62 lib.rs
63 error.rs
64 tool.rs # Tool, ToolKind, WriteCapability, ToolRegistry, Refusal, SurfaceProjection
65 resource.rs # ResourceRegistry, ResourceDescriptor, ResourceContents (+ notify_updated)
66 agent.rs # Agent, InferenceProvider, Message, RunOutcome
67 server/ # feature = "server"
68 mod.rs # POST/GET-SSE/DELETE routes, JSON-RPC dispatch
69 protocol.rs
70 session.rs # per-session subscriptions, SSE out channel, cancellation
71 stdio.rs
72 provider/ # provider adapters (feature-gated)
73 mod.rs
74 ollama.rs # feature = "ollama"
75 examples/
76 toy.rs # end-to-end: register tools, serve, drive via Ollama
77 ```
78
79 ## Example
80
81 ```
82 ollama pull qwen2.5
83 cargo run --example toy
84 ```
85
86 Override the model with `KBERG_MODEL=llama3.2 cargo run --example toy`.
87