@toolrelay/cli 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/LICENSE +6 -0
  2. package/README.md +113 -309
  3. package/package.json +3 -2
package/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) 2024-2026 ToolRelay, Inc. All rights reserved.
2
+
3
+ This software is proprietary and confidential. Unauthorized copying, distribution,
4
+ modification, or use of this software, via any medium, is strictly prohibited.
5
+
6
+ Use of this software is governed by the terms of service at https://toolrelay.io/terms.
package/README.md CHANGED
@@ -1,30 +1,60 @@
1
1
  # @toolrelay/cli
2
2
 
3
- CLI for [ToolRelay](https://toolrelay.io) — define your API tools in a `toolrelay.json` config file, validate it, serve a local MCP server, and deploy to the cloud. Every tool call produces a detailed audit trace.
3
+ Turn any REST API into an MCP server that AI agents can use in one command.
4
+
5
+ Define your API endpoints in a JSON config, and the CLI gives you a local [Model Context Protocol](https://modelcontextprotocol.io) server that Claude, GPT, and other AI agents can connect to instantly. No SDK integration, no code changes, no infrastructure to manage.
6
+
7
+ ## Why
8
+
9
+ AI agents need tools. Your API already exists. The gap between the two is boilerplate: building an MCP server, handling auth, mapping parameters, debugging what the agent actually sent. This CLI closes that gap.
10
+
11
+ - **Zero code** — describe your API in JSON, get a working MCP server
12
+ - **Works with any REST API** — GET, POST, PUT, DELETE with path, query, body, and header parameters
13
+ - **Built-in auth** — static tokens, API keys, OAuth 2.0 (with PKCE), custom headers
14
+ - **Audit traces** — every tool call logs the full request/response cycle so you can see exactly what happened
15
+ - **Session viewer** — browse past sessions in a local web UI, like Playwright's HTML reporter for API calls
16
+ - **Environment variable interpolation** — `${VAR}` syntax keeps secrets out of committed config files
17
+ - **Deploy when ready** — one command to go from local testing to a hosted MCP endpoint at [toolrelay.io](https://toolrelay.io)
4
18
 
5
19
  ## Quick start
6
20
 
7
21
  ```bash
8
- # Interactive wizard — walks you through app and tools
9
22
  npx @toolrelay/cli init
23
+ ```
10
24
 
11
- # Or skip prompts with a starter template
12
- npx @toolrelay/cli init --yes
25
+ The wizard walks you through your API — name, base URL, auth, and endpoints. It generates a `toolrelay.json` config file.
13
26
 
14
- # Validate config
15
- npx @toolrelay/cli validate toolrelay.json
27
+ Then start the MCP server:
16
28
 
17
- # Start a local MCP server for Claude Desktop
29
+ ```bash
18
30
  npx @toolrelay/cli serve toolrelay.json
31
+ ```
32
+
33
+ That's it. Add the printed URL to Claude Desktop's config and your API is available as tools.
34
+
35
+ ## How it works
19
36
 
20
- # Deploy to ToolRelay
21
- npx @toolrelay/cli login
22
- npx @toolrelay/cli publish toolrelay.json
23
37
  ```
38
+ toolrelay.json
39
+ |
40
+ npx @toolrelay/cli serve toolrelay.json
41
+ |
42
+ Local MCP Server (localhost:8787)
43
+ / | \
44
+ tools/list tools/call health
45
+ |
46
+ Your REST API backend
47
+ ```
48
+
49
+ 1. You describe your API in `toolrelay.json` — endpoints, parameters, auth
50
+ 2. `serve` starts a local MCP Streamable HTTP server
51
+ 3. AI agents connect and discover your tools via `tools/list`
52
+ 4. When an agent calls a tool, the CLI maps parameters, injects auth, and proxies the request to your backend
53
+ 5. Every call is logged with full audit traces — request, response, timing, errors
24
54
 
25
- ## Config file format (`toolrelay.json`)
55
+ ## Config file
26
56
 
27
- The `init` wizard generates this file for you, or you can create it manually:
57
+ The `init` wizard generates this, or create it by hand:
28
58
 
29
59
  ```json
30
60
  {
@@ -32,7 +62,7 @@ The `init` wizard generates this file for you, or you can create it manually:
32
62
  "name": "My API",
33
63
  "base_url": "http://localhost:3000",
34
64
  "auth_type": "static_token",
35
- "auth_config": { "token": "my-dev-key" }
65
+ "auth_config": { "token": "${API_TOKEN}" }
36
66
  },
37
67
  "tools": [
38
68
  {
@@ -48,125 +78,46 @@ The `init` wizard generates this file for you, or you can create it manually:
48
78
  }
49
79
  ```
50
80
 
51
- > **Tip:** `description` on both tools and parameters is how AI agents understand what your API does. Always include clear descriptions — they directly affect how well agents use your tools.
52
-
53
- ### Environment variable interpolation
81
+ > **Tip:** `description` on both tools and parameters is how AI agents decide which tool to use. Clear descriptions directly improve how well agents interact with your API.
54
82
 
55
- Use `${VAR_NAME}` syntax to reference environment variables in any string field. This keeps secrets out of your config file:
83
+ ### Environment variables
56
84
 
57
- ```json
58
- {
59
- "app": {
60
- "base_url": "${API_BASE_URL}",
61
- "auth_type": "static_token",
62
- "auth_config": { "token": "${API_TOKEN}" }
63
- }
64
- }
65
- ```
85
+ Use `${VAR_NAME}` in any string field to reference environment variables. Secrets stay out of your config:
66
86
 
67
87
  ```bash
68
- export API_BASE_URL=http://localhost:3000
69
- export API_TOKEN=my-secret-key
88
+ export API_TOKEN=sk-my-secret-key
70
89
  npx @toolrelay/cli serve toolrelay.json
71
90
  ```
72
91
 
73
- If a referenced variable is missing, the CLI fails with a clear error listing the missing variable names. This works for `base_url`, `auth_config` values, `headers_template`, and any other string field.
92
+ If a referenced variable is missing, the CLI fails with a clear error listing the missing names.
74
93
 
75
94
  ### Auth types
76
95
 
77
- | `auth_type` | `auth_config` fields | Description |
96
+ | `auth_type` | `auth_config` | Description |
78
97
  |---|---|---|
79
98
  | `none` | — | No authentication |
80
99
  | `static_token` | `{ "token": "..." }` | Bearer token in Authorization header |
81
100
  | `api_key_relay` | `{ "header_name": "X-API-Key" }` | Consumer API keys relayed to backend |
82
- | `custom_header` | `{ "headers": { "X-Custom": "value" } }` | Custom headers with static values |
83
- | `oauth2` | `{ "authorize_url", "token_url", "client_id"?, "client_secret"?, "scopes"?, "use_pkce"? }` | OAuth 2.0 authorization code flow (see [OAuth2 section](#oauth2-local-testing)) |
101
+ | `custom_header` | `{ "headers": { "X-Custom": "value" } }` | Custom header name/value pairs |
102
+ | `oauth2` | `{ "authorize_url", "token_url", ... }` | OAuth 2.0 with PKCE (see [OAuth2 section](#oauth2)) |
84
103
 
85
104
  ### Parameter mapping
86
105
 
87
- Each parameter in `parameter_mapping` specifies:
106
+ Each parameter in `parameter_mapping` tells the CLI how to translate what the agent sends into what your API expects:
88
107
 
89
108
  | Field | Required | Description |
90
109
  |---|---|---|
91
110
  | `name` | yes | Parameter name (what the AI agent sends) |
92
111
  | `type` | yes | `string`, `number`, `boolean`, `object`, or `array` |
93
112
  | `required` | yes | Whether the parameter is required |
94
- | `target` | yes | Where the parameter goes: `path`, `query`, `body`, or `header` |
113
+ | `target` | yes | Where it goes: `path`, `query`, `body`, or `header` |
95
114
  | `backend_key` | no | Backend field name if different from `name` |
96
- | `default` | no | Default value when not provided |
97
- | `description` | no | Human-readable description (shown to AI agents) |
98
-
99
- #### Path parameters
100
-
101
- Path parameters replace `{placeholder}` segments in the `endpoint_path`. The parameter `name` (or `backend_key` if set) must match the placeholder name.
102
-
103
- ```json
104
- {
105
- "name": "create-order",
106
- "http_method": "POST",
107
- "endpoint_path": "/api/customers/{customer_id}/orders",
108
- "parameter_mapping": [
109
- { "name": "customer_id", "type": "string", "required": true, "target": "path" }
110
- ]
111
- }
112
- ```
113
-
114
- Agent sends `{ "customer_id": "cust_42" }` → request goes to `/api/customers/cust_42/orders`.
115
-
116
- #### Query parameters
117
-
118
- Query parameters are appended as `?key=value` to the URL. Use `backend_key` when the backend expects a different name than what the agent sends, and `default` for optional filters.
115
+ | `default_value` | no | Default when not provided |
116
+ | `description` | no | Shown to AI agents — always include this |
119
117
 
120
- ```json
121
- {
122
- "name": "search_products",
123
- "http_method": "GET",
124
- "endpoint_path": "/api/products",
125
- "parameter_mapping": [
126
- { "name": "query", "type": "string", "required": true, "target": "query", "backend_key": "q" },
127
- { "name": "category", "type": "string", "required": false, "target": "query" },
128
- { "name": "page", "type": "number", "required": false, "target": "query", "default": 1 },
129
- { "name": "limit", "type": "number", "required": false, "target": "query", "default": 20 }
130
- ]
131
- }
132
- ```
133
-
134
- Agent sends `{ "query": "bluetooth speaker", "category": "electronics" }` → request goes to `/api/products?q=bluetooth+speaker&category=electronics&page=1&limit=20`.
135
-
136
- #### Body parameters
137
-
138
- Body parameters are sent as JSON in the request body. Use dot-notation in `backend_key` to create nested objects (e.g., `address.city` becomes `{ "address": { "city": "..." } }`).
139
-
140
- ```json
141
- {
142
- "name": "create_contact",
143
- "http_method": "POST",
144
- "endpoint_path": "/api/contacts",
145
- "parameter_mapping": [
146
- { "name": "name", "type": "string", "required": true, "target": "body" },
147
- { "name": "email", "type": "string", "required": true, "target": "body" },
148
- { "name": "phone", "type": "string", "required": false, "target": "body" },
149
- { "name": "city", "type": "string", "required": false, "target": "body", "backend_key": "address.city" },
150
- { "name": "state", "type": "string", "required": false, "target": "body", "backend_key": "address.state" },
151
- { "name": "tags", "type": "array", "required": false, "target": "body", "default": [] }
152
- ]
153
- }
154
- ```
155
-
156
- Agent sends `{ "name": "Alice", "email": "alice@example.com", "city": "Austin", "state": "TX" }` → request body becomes:
157
-
158
- ```json
159
- {
160
- "name": "Alice",
161
- "email": "alice@example.com",
162
- "address": { "city": "Austin", "state": "TX" },
163
- "tags": []
164
- }
165
- ```
166
-
167
- #### Mixing targets
118
+ **Path** parameters replace `{placeholder}` in the URL. **Query** parameters are appended as `?key=value`. **Body** parameters are sent as JSON (use dot-notation in `backend_key` for nesting, e.g. `address.city`). **Header** parameters are injected as HTTP headers.
168
119
 
169
- A single tool can combine path, query, body, and header parameters:
120
+ A single tool can mix all four targets:
170
121
 
171
122
  ```json
172
123
  {
@@ -175,286 +126,139 @@ A single tool can combine path, query, body, and header parameters:
175
126
  "endpoint_path": "/api/items/{item_id}",
176
127
  "parameter_mapping": [
177
128
  { "name": "item_id", "type": "string", "required": true, "target": "path" },
178
- { "name": "dry_run", "type": "boolean", "required": false, "target": "query", "default": false },
129
+ { "name": "dry_run", "type": "boolean", "required": false, "target": "query", "default_value": false },
179
130
  { "name": "title", "type": "string", "required": true, "target": "body" },
180
- { "name": "price", "type": "number", "required": true, "target": "body" },
181
131
  { "name": "idempotency_key", "type": "string", "required": false, "target": "header", "backend_key": "Idempotency-Key" }
182
132
  ]
183
133
  }
184
134
  ```
185
135
 
186
- Agent sends `{ "item_id": "abc", "title": "Widget", "price": 9.99, "idempotency_key": "req-1" }` → `PUT /api/items/abc?dry_run=false` with body `{ "title": "Widget", "price": 9.99 }` and header `Idempotency-Key: req-1`.
187
-
188
136
  ## Commands
189
137
 
190
- ### `init` — Generate a config file
191
-
192
- ```bash
193
- npx @toolrelay/cli init # Interactive wizard
194
- npx @toolrelay/cli init --yes # Starter template (no prompts)
195
- ```
196
-
197
- Walks you through configuring your app (name, base URL, auth type) and defining tools (endpoints, parameters).
198
-
199
- ### `validate` — Check config
200
-
201
- ```bash
202
- npx @toolrelay/cli validate toolrelay.json
203
- ```
204
-
205
- Validates the config without making any HTTP calls. Catches:
206
-
207
- - **Schema errors**: Missing required fields, invalid enum values, malformed URLs
208
- - **Path parameter mismatches**: `{placeholder}` in `endpoint_path` without a matching `target: "path"` parameter (and vice versa)
209
- - **Auth config**: Required fields per auth type (e.g., `token` for `static_token`, `authorize_url` for `oauth2`)
210
-
211
- Run this in CI to catch config issues before deploying.
212
-
213
138
  ### `serve` — Local MCP server
214
139
 
215
140
  ```bash
216
141
  npx @toolrelay/cli serve toolrelay.json
217
- npx @toolrelay/cli serve toolrelay.json --port 8787
142
+ npx @toolrelay/cli serve toolrelay.json --port 9000 --verbose
218
143
  ```
219
144
 
220
- Starts a local MCP Streamable HTTP server that Claude Desktop (or any MCP client) can connect to. Every `tools/call` produces a full audit trace in your terminal.
145
+ Starts a local MCP Streamable HTTP server. On startup it prints a Claude Desktop config snippet you can copy directly.
221
146
 
222
147
  | Option | Description |
223
148
  |---|---|
224
149
  | `--base-url <url>` | Override `base_url` from config |
225
- | `-p, --port <number>` | Port to listen on (default: 8787) |
150
+ | `-p, --port <number>` | Port (default: 8787) |
226
151
  | `-v, --verbose` | Show response headers and extra detail |
227
152
 
228
- On startup it prints the Claude Desktop config snippet:
229
-
230
- ```json
231
- {
232
- "mcpServers": {
233
- "my-api": {
234
- "url": "http://localhost:8787/mcp"
235
- }
236
- }
237
- }
238
- ```
239
-
240
- The server implements the full MCP Streamable HTTP protocol (`initialize`, `tools/list`, `tools/call`, `ping`, SSE, session management).
241
-
242
153
  **Endpoints:**
243
154
 
244
155
  | Endpoint | Description |
245
156
  |---|---|
246
- | `POST /mcp` | MCP JSON-RPC endpoint |
247
- | `GET /mcp` | SSE stream (with `Mcp-Session-Id` header) |
248
- | `GET /health` | Health check (tool count, call count, OAuth status) |
157
+ | `POST /mcp` | MCP JSON-RPC (tools/list, tools/call, etc.) |
158
+ | `GET /mcp` | SSE stream (with `Mcp-Session-Id`) |
159
+ | `GET /health` | Health check + OAuth status |
249
160
  | `GET /timeline` | Call timeline as JSON |
250
- | `GET /oauth/start` | Trigger OAuth flow (OAuth2 apps only) |
251
- | `GET /oauth/status` | Check OAuth state (OAuth2 apps only) |
252
-
253
- **Call timeline:** Every tool invocation is tracked with sequence numbers, timestamps, arguments, status, latency, and the gap between calls (how long the AI spent "thinking"). On shutdown (Ctrl+C), a formatted timeline and audit summary are printed.
254
-
255
- **Session persistence:** Sessions are saved to `.toolrelay/sessions/` on shutdown. Browse them with `toolrelay ui`.
256
-
257
- ### `ui` — Session viewer
258
-
259
- ```bash
260
- npx @toolrelay/cli ui
261
- npx @toolrelay/cli ui --port 8788 --no-open
262
- ```
263
-
264
- Opens a local web UI to browse past `serve` sessions. Inspect call timelines, drill into individual tool calls to see parameter resolution, request/response bodies, diagnostics, and errors.
161
+ | `GET /oauth/start` | Trigger OAuth flow (OAuth2 apps) |
162
+ | `GET /oauth/status` | Check OAuth state |
265
163
 
266
- | Option | Description |
267
- |---|---|
268
- | `-p, --port <number>` | Port to listen on (default: 8788) |
269
- | `--no-open` | Don't auto-open the browser |
164
+ Every tool call is tracked with sequence number, timing, arguments, status, and the gap between calls. On Ctrl+C, a formatted timeline and summary are printed. Sessions are saved to `.toolrelay/sessions/` — browse them with `toolrelay ui`.
270
165
 
271
- ### `login` — Authenticate with ToolRelay
166
+ ### `init` — Generate a config
272
167
 
273
168
  ```bash
274
- npx @toolrelay/cli login
169
+ npx @toolrelay/cli init # Interactive wizard
170
+ npx @toolrelay/cli init --yes # Starter template
275
171
  ```
276
172
 
277
- Opens your browser to sign in to [toolrelay.io](https://toolrelay.io). On success, your credentials are saved to `~/.toolrelay/credentials.json` (file mode `0600`).
278
-
279
- For CI/CD pipelines, set the `TOOLRELAY_DEPLOY_TOKEN` environment variable instead — it takes priority over the credentials file.
280
-
281
- ### `logout` — Clear saved credentials
173
+ ### `validate` Check config
282
174
 
283
175
  ```bash
284
- npx @toolrelay/cli logout
176
+ npx @toolrelay/cli validate toolrelay.json
285
177
  ```
286
178
 
287
- Removes `~/.toolrelay/credentials.json`. Does not revoke the token server-side.
179
+ Validates without making HTTP calls. Catches schema errors, path parameter mismatches, and missing auth fields. Run in CI to catch config issues before deploying.
288
180
 
289
- ### `whoami` — Show current user
181
+ ### `ui` — Session viewer
290
182
 
291
183
  ```bash
292
- npx @toolrelay/cli whoami
184
+ npx @toolrelay/cli ui
293
185
  ```
294
186
 
295
- Prints the authenticated user's email, name, and account tier. Useful for verifying which account the CLI is using.
187
+ Opens a local web UI to browse past `serve` sessions. Drill into individual tool calls to see parameter resolution, request/response bodies, diagnostics, and errors.
296
188
 
297
- ### `publish` — Deploy to ToolRelay
189
+ ### `publish` — Deploy to production
298
190
 
299
191
  ```bash
192
+ npx @toolrelay/cli login
300
193
  npx @toolrelay/cli publish toolrelay.json
301
194
  npx @toolrelay/cli publish toolrelay.json --dry-run
302
- npx @toolrelay/cli publish toolrelay.json --prune
303
195
  ```
304
196
 
305
- Deploys your `toolrelay.json` config to the ToolRelay platform. The command is **idempotent**it creates the app and tools on first run, then updates only what changed on subsequent runs.
306
-
307
- | Option | Description |
308
- |---|---|
309
- | `--dry-run` | Show what would change without making any API calls |
310
- | `--prune` | Delete remote tools that are no longer in the config file |
311
-
312
- **How it works:**
197
+ Deploys your config to [ToolRelay](https://toolrelay.io). The command is idempotent — creates on first run, updates only what changed after that.
313
198
 
314
- 1. Validates your config for production readiness (see [Pre-publish checks](#pre-publish-checks) below)
315
- 2. Authenticates using `TOOLRELAY_DEPLOY_TOKEN` (env var) or `~/.toolrelay/credentials.json` (from `login`)
316
- 3. Matches your local app to a remote app by slug (derived from `app.name`)
317
- 4. Creates the app if it doesn't exist, or updates it if the config changed
318
- 5. Syncs tools — creates new ones, updates changed ones, and optionally prunes removed ones
319
- 6. Auto-publishes the app if it hasn't been published yet
199
+ What you get after publishing:
320
200
 
321
- **What you get after publishing:**
322
-
323
- - A hosted MCP endpoint at `https://proxy.toolrelay.io/mcp/<your-slug>` connect any AI agent
324
- - An API portal at `https://toolrelay.io/portal/<your-slug>` consumers can request API keys
325
- - Auto-generated OpenAPI spec, SKILL.md, and landing page
326
-
327
- **Tier limits** are enforced by the API. If your plan doesn't support the number of apps or tools you're deploying, the command will fail with a clear error message.
328
-
329
- #### Pre-publish checks
330
-
331
- Before making any API calls, `publish` validates your config to catch common mistakes:
332
-
333
- | Check | Severity | Description |
334
- |---|---|---|
335
- | Localhost / private IP | Error | `base_url` cannot point to `localhost`, `127.0.0.1`, `192.168.*`, etc. |
336
- | HTTP base URL | Warning | Production APIs should use HTTPS |
337
- | Missing tool descriptions | Warning | AI agents rely on descriptions to pick the right tool |
338
- | OAuth2 required fields | Error | `authorize_url` and `token_url` must be set (`client_id`/`client_secret` are optional for public client PKCE) |
339
- | OAuth2 localhost URLs | Error | OAuth URLs must be publicly reachable |
340
- | Missing static token | Error | `static_token` auth requires a `token` in `auth_config` |
341
- | Duplicate tool names | Error | Each tool must have a unique name |
342
-
343
- Errors block the deploy. Warnings are printed but don't prevent publishing.
344
-
345
- **Authentication:**
201
+ - **Hosted MCP endpoint** at `https://proxy.toolrelay.io/mcp/<your-slug>`
202
+ - **Consumer portal** for API key self-service
203
+ - **Auto-generated docs** OpenAPI spec, SKILL.md, landing page
204
+ - **Usage analytics** call volume, latency, error rates per tool
205
+ - **Rate limiting and caching** — per-consumer, per-tier controls
346
206
 
347
- | Method | Use case |
207
+ | Option | Description |
348
208
  |---|---|
349
- | `toolrelay login` | Local development opens browser, saves credentials to `~/.toolrelay/` |
350
- | `TOOLRELAY_DEPLOY_TOKEN` | CI/CD set as environment variable, takes priority over credentials file |
209
+ | `--dry-run` | Show what would change without making API calls |
210
+ | `--prune` | Delete remote tools no longer in config |
351
211
 
352
- Generate a deploy token at **[toolrelay.io/dashboard/settings](https://toolrelay.io/dashboard/settings)** (Settings > Deploy Tokens). The token is shown once — copy it and store it as a CI secret.
212
+ For CI/CD, use `TOOLRELAY_DEPLOY_TOKEN` instead of `toolrelay login`. Generate one at [toolrelay.io/dashboard/settings](https://toolrelay.io/dashboard/settings).
353
213
 
354
- **Environment variables:**
355
-
356
- | Variable | Description |
357
- |---|---|
358
- | `TOOLRELAY_DEPLOY_TOKEN` | Deploy token for authentication (takes priority over credentials file) |
359
- | `TOOLRELAY_API_URL` | Override the API endpoint (default: `https://api.toolrelay.io`) |
214
+ ## OAuth2
360
215
 
361
- ## OAuth2 local testing
362
-
363
- When your app uses `auth_type: "oauth2"`, the `serve` command runs a full browser-based OAuth2 authorization code flow locally.
216
+ When `auth_type` is `"oauth2"`, the `serve` command handles the full browser-based authorization code flow locally:
364
217
 
365
218
  ```json
366
219
  {
367
- "app": {
368
- "name": "My OAuth API",
369
- "base_url": "http://localhost:3000",
370
- "auth_type": "oauth2",
371
- "auth_config": {
372
- "authorize_url": "https://provider.com/oauth/authorize",
373
- "token_url": "https://provider.com/oauth/token",
374
- "client_id": "${OAUTH_CLIENT_ID}",
375
- "client_secret": "${OAUTH_CLIENT_SECRET}",
376
- "scopes": "read write"
377
- }
378
- },
379
- "tools": [...]
220
+ "auth_config": {
221
+ "authorize_url": "https://provider.com/oauth/authorize",
222
+ "token_url": "https://provider.com/oauth/token",
223
+ "client_id": "${OAUTH_CLIENT_ID}",
224
+ "client_secret": "${OAUTH_CLIENT_SECRET}",
225
+ "scopes": "read write"
226
+ }
380
227
  }
381
228
  ```
382
229
 
383
- **How it works:**
384
-
385
- 1. On startup, a temporary HTTP server starts on a random port for the OAuth callback
386
- 2. Your browser opens to the upstream provider's authorize URL (with PKCE by default)
387
- 3. After you authorize, the provider redirects to `http://localhost:<port>/callback`
388
- 4. The CLI exchanges the authorization code for access/refresh tokens
389
- 5. Tokens are stored in memory — every tool call gets `Authorization: Bearer <token>` injected automatically
390
- 6. If the access token expires and a refresh token is available, it's refreshed automatically
230
+ On startup, a browser window opens for authorization. After you approve, tokens are stored in memory and injected into every tool call automatically. Refresh is handled transparently.
391
231
 
392
- **Important:** Register `http://localhost` (any port) as an allowed redirect URI in your OAuth provider's settings.
232
+ `client_id` and `client_secret` are optional public client PKCE flows work without them.
393
233
 
394
- If the browser can't auto-open (headless environments, SSH), the authorize URL is printed to the terminal. You can also manually trigger the flow at any time via `GET /oauth/start`.
234
+ Register `http://localhost` (any port) as a redirect URI in your OAuth provider.
395
235
 
396
- ### Disabling PKCE
236
+ ### PKCE
397
237
 
398
- PKCE (Proof Key for Code Exchange) is enabled by default. Some providers — like AWS Cognito configured as a confidential client, or certain custom OAuth servers — may reject the extra `code_challenge` / `code_verifier` parameters. Set `"use_pkce": false` in `auth_config` to disable it:
399
-
400
- ```json
401
- {
402
- "app": {
403
- "name": "Cognito API",
404
- "base_url": "https://api.example.com",
405
- "auth_type": "oauth2",
406
- "auth_config": {
407
- "authorize_url": "https://mypool.auth.us-east-1.amazoncognito.com/oauth2/authorize",
408
- "token_url": "https://mypool.auth.us-east-1.amazoncognito.com/oauth2/token",
409
- "client_id": "${COGNITO_CLIENT_ID}",
410
- "client_secret": "${COGNITO_CLIENT_SECRET}",
411
- "scopes": "openid profile",
412
- "use_pkce": false
413
- }
414
- },
415
- "tools": [...]
416
- }
417
- ```
238
+ PKCE is enabled by default. Set `"use_pkce": false` for providers that reject it (e.g., AWS Cognito confidential clients):
418
239
 
419
- | Provider | PKCE support | Recommendation |
240
+ | Provider | PKCE | Recommendation |
420
241
  |---|---|---|
421
- | Auth0 | Yes | Leave default (`use_pkce: true`) |
422
- | Supabase | Yes (default since 2024) | Leave default |
423
- | Okta | Yes | Leave default |
424
- | Google | Yes | Leave default |
242
+ | Auth0, Okta, Google, Supabase | Yes | Leave default |
425
243
  | AWS Cognito (public client) | Yes | Leave default |
426
- | AWS Cognito (confidential client) | May reject PKCE | Set `"use_pkce": false` |
427
- | Custom OAuth servers | Varies | Try default first; set `false` if you get errors about unknown parameters |
428
-
429
- ## Local to production
244
+ | AWS Cognito (confidential client) | May reject | Set `"use_pkce": false` |
430
245
 
431
- The typical workflow:
246
+ ## Workflow
432
247
 
433
248
  ```
434
- 1. toolrelay init → Generate toolrelay.json
435
- 2. toolrelay serve → Test locally with Claude Desktop
249
+ 1. npx @toolrelay/cli init → Generate toolrelay.json
250
+ 2. npx @toolrelay/cli serve → Test locally with Claude Desktop
436
251
  3. (iterate on config)
437
- 4. toolrelay login Authenticate with toolrelay.io
438
- 5. toolrelay publish → Deploy to production
252
+ 4. npx @toolrelay/cli publish Deploy to production
439
253
  ```
440
254
 
441
- Once published, your app gets:
442
-
443
- - **Hosted MCP endpoint** (`/mcp/<slug>`) — any AI agent can connect
444
- - **Consumer portal** (`/portal/<slug>`) — API key self-service for developers
445
- - **Auto-generated docs** — OpenAPI spec, SKILL.md, landing page
446
- - **Usage analytics** — call volume, latency, error rates per tool
447
- - **Rate limiting and caching** — per-consumer, per-tier controls
448
-
449
- Sign up at [toolrelay.io](https://toolrelay.io) to get started.
450
-
451
255
  ### File locations
452
256
 
453
257
  | Path | Purpose |
454
258
  |---|---|
455
- | `toolrelay.json` | Your app + tool config (project root, committed to git) |
456
- | `.toolrelay/sessions/` | Local MCP session logs (project-local, auto-gitignored) |
457
- | `~/.toolrelay/credentials.json` | Login credentials (home directory, mode `0600`) |
259
+ | `toolrelay.json` | App + tool config (committed to git) |
260
+ | `.toolrelay/sessions/` | Local session logs (auto-gitignored) |
261
+ | `~/.toolrelay/credentials.json` | Login credentials (mode `0600`) |
458
262
 
459
263
  ## License
460
264
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolrelay/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "CLI for ToolRelay — validate configs, serve local MCP servers, and deploy to production with audit traces",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "main": "./dist/index.js",
10
10
  "files": [
11
11
  "dist",
12
- "README.md"
12
+ "README.md",
13
+ "LICENSE"
13
14
  ],
14
15
  "keywords": [
15
16
  "toolrelay",