@@ -22,6 +22,240 @@ OpenControl is a gateway that lets an LLM access any function from your codebase
2222- ** Universal** : Works with any LLM that supports a context protocol, like Anthropic's MCP or OpenAI's Tools.
2323- ** Secure** : Supports authentication through any OAuth provider.
2424
25+ ## Get started
26+
27+ 1 . ** Install dependencies**
28+
29+ ``` bash
30+ npm install opencontrol hono @ai-sdk/anthropic
31+ ```
32+
33+ Here are we are going to use Anthropic's Claude.
34+
35+ 2 . ** Create the server**
36+
37+ ``` bash
38+ touch src/opencontrol.ts
39+ ```
40+
41+ ``` ts title=src/opencontrol.ts
42+ import { create } from " opencontrol"
43+ import { handle } from " hono/aws-lambda"
44+
45+ const app = create ({
46+ // model: ...,
47+ // tools: [ ]
48+ })
49+
50+ export const handler = handle (app )
51+ ```
52+
53+ 3 . ** Pick the model**
54+
55+ ``` diff lang=ts title=src/opencontrol.ts
56+ + import { Resource } from "sst"
57+ + import { createAnthropic } from "@ai-sdk/anthropic"
58+
59+ const app = create({
60+ + model: createAnthropic({
61+ + apiKey: Resource.AnthropicKey.value,
62+ + })("claude-3-7-sonnet-20250219")
63+ })
64+ ```
65+
66+ 4 . ** Define your tools**
67+
68+ ``` diff lang=ts title=src/opencontrol.ts
69+ + import { tool } from "opencontrol/tool"
70+ + import { Inventory } from "@acme/core/inventory/index"
71+
72+ + const inventory = tool({
73+ + name: "inventory_record",
74+ + description: "Record new inventory event to track in or out amounts",
75+ + args: Inventory.record.schema,
76+ + async run(input) {
77+ + return Inventory.record(input)
78+ + }
79+ + })
80+
81+ const app = create({
82+ tools: [
83+ + inventory
84+ ]
85+ })
86+ ```
87+
88+ 5 . ** Infrastructure**
89+
90+ We are using SST here, but you can since ** OpenControl is just a Hono app, you can deploy it however you want** .
91+
92+ ``` ts title="sst.config.ts" {1,6}
93+ const anthropicKey = new sst .Secret (" AnthropicKey" )
94+
95+ const server = new sst .aws .OpenControl (" MyServer" , {
96+ server: {
97+ handler: " src/opencontrol.handler" ,
98+ link: [anthropicKey ]
99+ }
100+ })
101+ ```
102+
103+ We are defining a secret for the Anthropic API key and linking it to our OpenControl server.
104+
105+ 6 . ** Link any resources**
106+
107+ ``` ts title="sst.config.ts" {6}
108+ const bucket = new sst .aws .Bucket (" MyBucket" )
109+
110+ const server = new sst .aws .OpenControl (" MyServer" , {
111+ server: {
112+ handler: " src/opencontrol.handler" ,
113+ link: [bucket ]
114+ }
115+ })
116+ ```
117+
118+ If your tools need to access to your resources, you can link them as well.
119+
120+ 7 . ** Grant permissions**
121+
122+ If you are using the AWS tool, you'll need to give your OpenControl server permissions to access your AWS account.
123+
124+ ``` ts title="sst.config.ts" {4-6}
125+ const server = new sst .aws .OpenControl (" MyServer" , {
126+ server: {
127+ handler: " src/opencontrol.handler" ,
128+ policies: $dev
129+ ? [" arn:aws:iam::aws:policy/AdministratorAccess" ]
130+ : [" arn:aws:iam::aws:policy/ReadOnlyAccess" ]
131+ }
132+ })
133+ ```
134+
135+ Here we are giving it admin access in dev but read-only access in prod.
136+
137+ 8 . ** Deploy**
138+
139+ Currently, OpenControl uses basic auth but we'll be adding support for OAuth soon.
140+
141+ ``` ts title="sst.config.ts"
142+ return {
143+ OpenControlUrl: server .url ,
144+ OpenControlPassword: server .password
145+ }
146+ ```
147+
148+ You can print out the URL of your server and it's password and deploy.
149+
150+ ``` bash
151+ sst deploy
152+ ```
153+
154+ Now head over to the URL, login with the password, and you can use AI to talk to your infrastructure.
155+
156+ ## Examples
157+
158+ Check out how [ Terminal] ( https://www.terminal.shop/ ) uses OpenControl.
159+
160+ - [ Server] ( https://github.com/terminaldotshop/terminal/blob/dev/packages/functions/src/opencontrol.ts )
161+ - [ Infrastructure] ( https://github.com/terminaldotshop/terminal/blob/dev/infra/opencontrol.ts )
162+
163+ ## Tools
164+
165+ Here are some examples of the tools you can use with OpenControl.
166+
167+ - ** AWS**
168+
169+ ``` ts title=src/opencontrol.ts
170+ import { z } from " zod"
171+ import AWS from " aws-sdk"
172+ import { tool } from " opencontrol/tool"
173+
174+ const aws = tool ({
175+ name: " aws" ,
176+ description: " Make a call to the AWS SDK for JavaScript v2" ,
177+ args: z .object ({
178+ client: z .string ().describe (" Class name of the client to use" ),
179+ command: z .string ().describe (" Command to call on the client" ),
180+ args: z
181+ .record (z .string (), z .any ())
182+ .optional ()
183+ .describe (" Arguments to pass to the command" ),
184+ }),
185+ async run(input ) {
186+ // @ts-ignore
187+ const client = new AWS [input .client ]()
188+ return await client [input .command ](input .args ).promise ()
189+ }
190+ })
191+ ```
192+
193+ - ** Stripe**
194+
195+ ``` ts title=src/opencontrol.ts
196+ import { z } from " zod"
197+ import { tool } from " opencontrol/tool"
198+
199+ const stripe = tool ({
200+ name: " stripe" ,
201+ description: " make a call to the stripe api" ,
202+ args: z .object ({
203+ method: z .string ().describe (" HTTP method to use" ),
204+ path: z .string ().describe (" Path to call" ),
205+ query: z .record (z .string ()).optional ().describe (" Query params" ),
206+ contentType: z .string ().optional ().describe (" HTTP content type to use" ),
207+ body: z .string ().optional ().describe (" HTTP body to use if it is not GET" ),
208+ }),
209+ async run(input ) {
210+ const url = new URL (" https://api.stripe.com" + input .path )
211+ if (input .query ) url .search = new URLSearchParams (input .query ).toString ()
212+ const response = await fetch (url .toString (), {
213+ method: input .method ,
214+ headers: {
215+ Authorization: ` Bearer ${Resource .StripeSecret .value } ` ,
216+ " Content-Type" : input .contentType ,
217+ },
218+ body: input .body ? input .body : undefined ,
219+ })
220+ if (! response .ok ) throw new Error (await response .text ())
221+ return response .text ()
222+ }
223+ })
224+ ```
225+
226+ - ** SQL Database**
227+
228+ ``` ts title=src/opencontrol.ts
229+ import { z } from " zod"
230+ import { tool } from " opencontrol/tool"
231+ import { db } from " @acme/core/drizzle/index"
232+
233+ const databaseRead = tool ({
234+ name: " database_query_readonly" ,
235+ description:
236+ " Readonly database query for MySQL, use this if there are no direct tools" ,
237+ args: z .object ({ query: z .string () }),
238+ async run(input ) {
239+ return db .transaction (async (tx ) => tx .execute (input .query ), {
240+ accessMode: " read only" ,
241+ isolationLevel: " read committed"
242+ })
243+ }
244+ })
245+
246+ const databaseWrite = tool ({
247+ name: " database_query_write" ,
248+ description:
249+ " DANGEROUS operation that writes to the database. You MUST triple check with the user before using this tool - show them the query you are about to run." ,
250+ args: z .object ({ query: z .string () }),
251+ async run(input ) {
252+ return db .transaction (async (tx ) => tx .execute (input .query ), {
253+ isolationLevel: " read committed"
254+ })
255+ }
256+ })
257+ ```
258+
25259---
26260
27261OpenControl is created by the maintainers of [ SST] ( https://sst.dev ) .
0 commit comments