Skip to content

Commit 4e47544

Browse files
Swiftworkroncohen
andauthored
Update mcp command to setup the remote Bucket MCP connection (#403)
Draft until the remote MCP server is generally available. ## Changes Repurposed the `mcp` command to help set up the connection to the remotely hosted Bucket MCP server instead of creating a local MCP server. https://github.com/user-attachments/assets/9c48dd42-1ce3-4b66-a380-e89bbfe797c4 ## AI Summary This pull request includes updates to the `packages/cli` documentation and codebase to improve usability, error handling, and maintainability. The most significant changes involve reordering CLI command arguments for consistency, enhancing error handling in various actions, and updating the MCP setup process to use a remote server. ### Documentation Updates: * **Reordered CLI command arguments for consistency**: Updated the order of arguments in commands like `bucket new`, `bucket features create`, and others to place `--app-id` before other options, improving clarity and consistency across the CLI documentation. (`packages/cli/README.md`: [[1]](diffhunk://#diff-15986190ef9581ab59bcd5483b2c09e7fd0bd439d6f6cddbc94b0b1de094ee51L102-R102) [[2]](diffhunk://#diff-15986190ef9581ab59bcd5483b2c09e7fd0bd439d6f6cddbc94b0b1de094ee51L141-R141) [[3]](diffhunk://#diff-15986190ef9581ab59bcd5483b2c09e7fd0bd439d6f6cddbc94b0b1de094ee51L208-R208) * **Updated MCP setup instructions**: Replaced local MCP server setup with instructions for connecting to a remote MCP server. Added new options like `--editor` and `--scope` to configure editors/clients and clarified compatibility with MCP STDIO and HTTP streaming. (`packages/cli/README.md`: [packages/cli/README.mdL269-R298](diffhunk://#diff-15986190ef9581ab59bcd5483b2c09e7fd0bd439d6f6cddbc94b0b1de094ee51L269-R298)) ### Codebase Enhancements: * **Improved error handling**: Added try-catch blocks around calls to `getApp` in multiple actions (e.g., `createFeatureAction`, `generateTypesAction`, `featureAccessAction`) to handle errors gracefully and provide better feedback to users. (`packages/cli/commands/features.ts`: [[1]](diffhunk://#diff-62cb1138f98fd9620a8f451d3171b4558f8690fa8fad668d98cd60a507f04315L57-R64) [[2]](diffhunk://#diff-62cb1138f98fd9620a8f451d3171b4558f8690fa8fad668d98cd60a507f04315L145-R158) [[3]](diffhunk://#diff-62cb1138f98fd9620a8f451d3171b4558f8690fa8fad668d98cd60a507f04315L205-R223) * **Refactored imports for clarity**: Renamed `path.js` to `urls.js` and updated imports across affected files to better reflect the file's purpose. (`packages/cli/commands/companies.ts`: [[1]](diffhunk://#diff-bfb1192584bcb365fdd137d53b596b850d601a2beb7ad959d33d13ce7bd737cdL14-R14) `packages/cli/commands/features.ts`: [[2]](diffhunk://#diff-62cb1138f98fd9620a8f451d3171b4558f8690fa8fad668d98cd60a507f04315L39-R39) ### Usability Improvements: * **Simplified app selection in `initAction`**: Automatically defaults to the first non-demo app if available and sets it as the default choice in the app selection prompt, streamlining the initialization process. (`packages/cli/commands/init.ts`: [packages/cli/commands/init.tsL45-R53](diffhunk://#diff-76aae8c1424cc73067ec33d08cef49f3f8745b2827c72ed4d1e408b1141ed2d8L45-R53)) --------- Co-authored-by: Ron Cohen <ron@bucket.co>
1 parent 856d136 commit 4e47544

21 files changed

Lines changed: 503 additions & 2073 deletions

packages/cli/README.md

Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ All-in-one command to get started quickly. This command combines `init`, feature
9999
and type generation in a single step. Use this for the fastest way to get up and running with Bucket.
100100

101101
```bash
102-
npx bucket new "My Feature" [--key my-feature] [--app-id ap123456789] [--key-format custom] [--out gen/features.ts] [--format react]
102+
npx bucket new "My Feature" [--app-id ap123456789] [--key my-feature] [--key-format custom] [--out gen/features.ts] [--format react]
103103
```
104104

105105
Options:
@@ -138,7 +138,7 @@ Create a new feature in your Bucket app.
138138
The command guides you through the feature creation process with interactive prompts if options are not provided.
139139

140140
```bash
141-
npx bucket features create "My Feature" [--key my-feature] [--app-id ap123456789] [--key-format custom]
141+
npx bucket features create "My Feature" [--app-id ap123456789] [--key my-feature] [--key-format custom]
142142
```
143143

144144
Options:
@@ -205,7 +205,7 @@ Grant or revoke access to specific features for companies, segments, and users.
205205
If no feature key is provided, you'll be prompted to select one from a list.
206206

207207
```bash
208-
npx bucket companies features access [featureKey] [--enable|--disable] [--companies <id...>] [--segments <id...>] [--users <id...>] [--app-id ap123456789]
208+
npx bucket companies features access [--app-id ap123456789] [featureKey] [--enable|--disable] [--companies <id...>] [--segments <id...>] [--users <id...>]
209209
```
210210

211211
Arguments:
@@ -252,7 +252,7 @@ Bucket provides powerful AI-assisted development capabilities through rules and
252252
The `rules` command helps you set up AI-specific rules for your project. These rules enable AI tools to better understand how to work with Bucket and feature flags and how they should be used in your codebase.
253253

254254
```bash
255-
npx bucket rules [--format cursor|copilot] [--yes]
255+
npx bucket rules [--format <cursor|copilot>] [--yes]
256256
```
257257

258258
Options:
@@ -266,67 +266,36 @@ This command will add rules to your project that provide AI tools with context a
266266

267267
## Model Context Protocol
268268

269-
The Model Context Protocol (MCP) is an open protocol that provides a standardized way to connect AI models to different data sources and tools. In the context of Bucket, MCP enables your development environment to understand your feature flags, their states, and their relationships within your codebase. This creates a seamless bridge between your feature management workflow and AI-powered development tools. MCP is in a very early stage of development and changes are frequent, if something isn't working please check out the [Model Context Protocol Website](https://modelcontextprotocol.io/) and open an [issue ticket here](https://github.com/bucketco/bucket-javascript-sdk/issues).
269+
The Model Context Protocol (MCP) is an open protocol that provides a standardized way to connect AI models to different data sources and tools. In the context of Bucket, MCP enables your code editor to understand your feature flags, their states, and their relationships within your codebase. This creates a seamless bridge between your feature management workflow and AI-powered development tools. The MCP server is hosted by Bucket, so it's very easy to get started.
270+
271+
_\*\*Note: The Bucket `mcp` CLI command was previously used for a \_local_ server. However, in recent versions of the Bucket CLI, the `mcp` command has been repurposed to help you connect to the new remote MCP server.\*\*\_
270272

271273
### Setting up MCP
272274

273-
MCP servers currently run locally on your machine. To start the MCP server run the CLI command from your Bucket initialized project directory:
275+
The `mcp` command helps you configure your editor or AI client to connect with Bucket's remote MCP server. This allows your AI tools to understand your feature flags and provide more contextual assistance.
274276

275277
```bash
276-
npx bucket mcp [--port <number|"auto">] [--app-id ap123456789]
278+
npx bucket mcp [--app-id <id>] [--editor <editor>] [--scope <local|global>]
277279
```
278280

279281
Options:
280282

281-
- `--port`: Port to run the SSE server on (defaults to 8050, "auto" for random port).
282-
- `--app-id`: App ID to use.
283-
284-
This will start an SSE server at `http://localhost:8050/sse` by default which you can connect to using your [client of choice](https://modelcontextprotocol.io/clients). Below are examples that work for [Cursor IDE](https://www.cursor.com/) and [Claude Desktop](https://claude.ai/download).
285-
286-
#### Server-Side Events (SSE)
287-
288-
```json
289-
{
290-
"mcpServers": {
291-
"Bucket": {
292-
"url": "http://localhost:8050/sse"
293-
}
294-
}
295-
}
296-
```
297-
298-
#### STDIO Proxy
299-
300-
Some clients don't support SSE and can instead interface with the MCP server over a STDIO proxy.
301-
302-
```json
303-
{
304-
"mcpServers": {
305-
"Bucket": {
306-
"command": "npx",
307-
"args": ["-y", "supergateway", "--sse", "http://localhost:8050/sse"]
308-
}
309-
}
310-
}
311-
```
312-
313-
### Cursor IDE
314-
315-
To enable MCP features in [Cursor IDE](https://www.cursor.com/):
316-
317-
1. Open Cursor IDE.
318-
2. Go to `Settings > MCP`.
319-
3. Click `Add new global MCP server` and paste the `SSE` config.
320-
4. Save and go back to Cursor.
283+
- `--app-id`: App ID to use for the MCP connection.
284+
- `--editor`: The editor/client to configure:
285+
- `cursor`: [Cursor IDE](https://www.cursor.com/)
286+
- `vscode`: [Visual Studio Code](https://code.visualstudio.com/)
287+
- `claude`: [Claude Desktop](https://claude.ai/download)
288+
- `windsurf`: [Windsurf](https://windsurf.com/editor)
289+
- `--scope`: Whether to configure settings globally or locally for the project.
321290

322-
### Claude Desktop
291+
The command will guide you through:
323292

324-
To enable MCP features in [Claude Desktop](https://claude.ai/download):
293+
1. Selecting which editor/client to configure.
294+
2. Choosing which Bucket app to connect to.
295+
3. Deciding between global or project-local configuration.
296+
4. Setting up the appropriate configuration file for your chosen editor .
325297

326-
1. Open Claude Desktop.
327-
2. Go to `Settings > Developer`.
328-
3. Click `Edit config` and paste the `STDIO` config.
329-
4. Save and restart Claude Desktop.
298+
_**Note: The setup uses [mcp-remote](https://github.com/geelen/mcp-remote) as a compatibility layer allowing the remote hosted Bucket MCP server to work with all editors/clients that support MCP STDIO servers. If your editor/client supports HTTP Streaming with OAuth you can connect to the Bucket MCP server directly.**_
330299

331300
## Development
332301

packages/cli/commands/companies.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
MissingEnvIdError,
1212
} from "../utils/errors.js";
1313
import { appIdOption, companyFilterOption } from "../utils/options.js";
14-
import { baseUrlSuffix } from "../utils/path.js";
14+
import { baseUrlSuffix } from "../utils/urls.js";
1515

1616
export const listCompaniesAction = async (options: { filter?: string }) => {
1717
const { baseUrl, appId } = configStore.getConfig();
@@ -20,13 +20,14 @@ export const listCompaniesAction = async (options: { filter?: string }) => {
2020
if (!appId) {
2121
return handleError(new MissingAppIdError(), "Companies List");
2222
}
23-
const app = getApp(appId);
24-
const production = app.environments.find((e) => e.isProduction);
25-
if (!production) {
26-
return handleError(new MissingEnvIdError(), "Companies List");
27-
}
2823

2924
try {
25+
const app = getApp(appId);
26+
const production = app.environments.find((e) => e.isProduction);
27+
if (!production) {
28+
return handleError(new MissingEnvIdError(), "Companies List");
29+
}
30+
3031
spinner = ora(
3132
`Loading companies for app ${chalk.cyan(app.name)}${baseUrlSuffix(baseUrl)}...`,
3233
).start();

packages/cli/commands/features.ts

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Argument, Command } from "commander";
44
import { relative } from "node:path";
55
import ora, { Ora } from "ora";
66

7-
import { getApp, getOrg } from "../services/bootstrap.js";
7+
import { App, getApp, getOrg } from "../services/bootstrap.js";
88
import {
99
createFeature,
1010
Feature,
@@ -36,7 +36,7 @@ import {
3636
typesOutOption,
3737
userIdsOption,
3838
} from "../utils/options.js";
39-
import { baseUrlSuffix, featureUrl } from "../utils/path.js";
39+
import { baseUrlSuffix, featureUrl } from "../utils/urls.js";
4040

4141
const lf = new Intl.ListFormat("en");
4242

@@ -54,7 +54,14 @@ export const createFeatureAction = async (
5454
if (!appId) {
5555
return handleError(new MissingAppIdError(), "Features Create");
5656
}
57-
const app = getApp(appId);
57+
58+
let app: App;
59+
try {
60+
app = getApp(appId);
61+
} catch (error) {
62+
return handleError(error, "Features Create");
63+
}
64+
5865
const production = app.environments.find((e) => e.isProduction);
5966

6067
try {
@@ -102,13 +109,13 @@ export const listFeaturesAction = async () => {
102109
if (!appId) {
103110
return handleError(new MissingAppIdError(), "Features Create");
104111
}
105-
const app = getApp(appId);
106-
const production = app.environments.find((e) => e.isProduction);
107-
if (!production) {
108-
return handleError(new MissingEnvIdError(), "Features Types");
109-
}
110112

111113
try {
114+
const app = getApp(appId);
115+
const production = app.environments.find((e) => e.isProduction);
116+
if (!production) {
117+
return handleError(new MissingEnvIdError(), "Features Types");
118+
}
112119
spinner = ora(
113120
`Loading features of app ${chalk.cyan(app.name)}${baseUrlSuffix(baseUrl)}...`,
114121
).start();
@@ -142,7 +149,13 @@ export const generateTypesAction = async () => {
142149
return handleError(new MissingAppIdError(), "Features Types");
143150
}
144151

145-
const app = getApp(appId);
152+
let app: App;
153+
try {
154+
app = getApp(appId);
155+
} catch (error) {
156+
return handleError(error, "Features Types");
157+
}
158+
146159
const production = app.environments.find((e) => e.isProduction);
147160
if (!production) {
148161
return handleError(new MissingEnvIdError(), "Features Types");
@@ -161,8 +174,7 @@ export const generateTypesAction = async () => {
161174
);
162175
} catch (error) {
163176
spinner?.fail("Loading features failed.");
164-
void handleError(error, "Features Types");
165-
return;
177+
return handleError(error, "Features Types");
166178
}
167179

168180
try {
@@ -202,7 +214,13 @@ export const featureAccessAction = async (
202214
return handleError(new MissingAppIdError(), "Feature Access");
203215
}
204216

205-
const app = getApp(appId);
217+
let app: App;
218+
try {
219+
app = getApp(appId);
220+
} catch (error) {
221+
return handleError(error, "Features Types");
222+
}
223+
206224
const production = app.environments.find((e) => e.isProduction);
207225
if (!production) {
208226
return handleError(new MissingEnvIdError(), "Feature Access");

packages/cli/commands/init.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const initAction = async (args: InitArgs = {}) => {
3232

3333
// Load apps
3434
spinner = ora(`Loading apps from ${chalk.cyan(baseUrl)}...`).start();
35-
apps = await listApps();
35+
apps = listApps();
3636
spinner.succeed(`Loaded apps from ${chalk.cyan(baseUrl)}.`);
3737
} catch (error) {
3838
spinner?.fail("Loading apps failed.");
@@ -42,20 +42,15 @@ export const initAction = async (args: InitArgs = {}) => {
4242

4343
try {
4444
let appId: string | undefined;
45-
const nonDemoApps = apps.filter((app) => !app.demo);
45+
const nonDemoApp = apps.find((app) => !app.demo);
4646

47-
// If there is only one non-demo app, select it automatically
4847
if (apps.length === 0) {
4948
throw new Error("You don't have any apps yet. Please create one.");
50-
} else if (nonDemoApps.length === 1) {
51-
appId = nonDemoApps[0].id;
52-
console.log(
53-
`Automatically selected app ${chalk.cyan(nonDemoApps[0].name)} (${chalk.cyan(appId)}).`,
54-
);
5549
} else {
5650
const longestName = Math.max(...apps.map((app) => app.name.length));
5751
appId = await select({
5852
message: "Select an app",
53+
default: nonDemoApp?.id,
5954
choices: apps.map((app) => ({
6055
name: `${app.name.padEnd(longestName, " ")}${app.demo ? " [Demo]" : ""}`,
6156
value: app.id,

0 commit comments

Comments
 (0)