Skip to content

Commit f980df9

Browse files
Merge pull request #386 from basementstudio/canary
v0.5.6
2 parents 19e20d4 + 6d3f3c0 commit f980df9

File tree

40 files changed

+779
-243
lines changed

40 files changed

+779
-243
lines changed

.github/workflows/auto-create-promotion-pr.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,23 @@ jobs:
3131
env:
3232
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3333
run: |
34-
EXISTING_PR=$(gh pr list --base main --head canary --state open --json number --jq '.[0].number')
35-
34+
SOURCE_BRANCH="${{ github.event.pull_request.head.ref }}"
35+
36+
EXISTING_PR=$(gh pr list --base main --head "$SOURCE_BRANCH" --state open --json number --jq '.[0].number')
37+
3638
if [ -z "$EXISTING_PR" ]; then
3739
gh pr create \
3840
--base main \
39-
--head canary \
41+
--head "$SOURCE_BRANCH" \
4042
--title "${{ github.event.pull_request.title }}" \
4143
--body "Auto-generated PR to promote changes from canary to main.
42-
43-
**Original PR:** #${{ github.event.pull_request.number }}
44+
45+
**Original PR to canary:** #${{ github.event.pull_request.number }}
4446
**Merged by:** @${{ github.event.pull_request.merged_by.login }}
45-
**Docs changes detected:**
46-
47+
**Docs changes detected**
48+
4749
---
48-
50+
4951
${{ github.event.pull_request.body }}"
5052
echo "PR created successfully"
5153
else

apps/website/content/docs/core-concepts/tools.mdx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,36 @@ export const metadata: ToolMetadata = {
251251
- `domain` - Optional dedicated subdomain for the widget's sandbox origin
252252
- `prefersBorder` - Request visible border + background (`true`/`false`/omitted)
253253

254+
### MCP Apps metadata
255+
256+
<Callout variant="info">
257+
Unlike OpenAI widgets, MCP Apps do not require specific metadata configuration. Widgets work automatically without additional setup.
258+
</Callout>
259+
260+
```typescript
261+
export const metadata: ToolMetadata = {
262+
name: "show-analytics",
263+
description: "Display analytics dashboard",
264+
_meta: {
265+
ui: {
266+
csp: {
267+
connectDomains: ["https://api.analytics.com"],
268+
resourceDomains: ["https://cdn.analytics.com"],
269+
},
270+
domain: "https://analytics-widget.example.com",
271+
prefersBorder: true,
272+
},
273+
},
274+
};
275+
```
276+
277+
**Resource-specific properties:**
278+
279+
- `csp.connectDomains` - Origins for fetch/XHR/WebSocket connections
280+
- `csp.resourceDomains` - Origins for images, scripts, stylesheets, fonts, media
281+
- `domain` - Optional dedicated subdomain for the widget's sandbox origin
282+
- `prefersBorder` - Request visible border + background (`true`/`false`/omitted)
283+
254284
## Handler Types
255285

256286
Tools support three types of handlers, each suited for different use cases:

apps/website/content/docs/getting-started/installation.mdx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ On installation, you'll see the following prompts:
2727

2828
<TerminalPrompt>
2929
{
30-
"? What is your project named? (my-xmcp-app)\n? Select a package manager: (Use arrow keys)\n❯ npm \n yarn \n pnpm \n bun\n? Select the transport you want to use: (Use arrow keys)\n❯ HTTP (runs on a server) \n STDIO (runs on the user's machine)\n? Select components to initialize: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)\n❯◉ Tools\n ◉ Prompts\n ◉ Resources"
30+
"? What is your project named? (my-xmcp-app)\n? Select a template: (Use arrow keys)\n❯ Default (Standard MCP server)\n GPT App (ChatGPT/OpenAI widgets)\n MCP App (React widgets for ext-apps)"
31+
}
32+
</TerminalPrompt>
33+
34+
You can also skip prompts using flags: `--gpt` (GPT App), `--ui` (MCP App), `--tailwind` or `--tw` (Tailwind CSS). Run `npx create-xmcp-app --help` for all options.
35+
36+
<TerminalPrompt>
37+
{
38+
"? Select a package manager: (Use arrow keys)\n❯ npm \n yarn \n pnpm \n bun\n? Select the transport you want to use: (Use arrow keys)\n❯ HTTP (runs on a server) \n STDIO (runs on the user's machine)\n? Select components to initialize: (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)\n❯◉ Tools\n ◉ Prompts\n ◉ Resources"
3139
}
3240
</TerminalPrompt>
3341

packages/create-xmcp-app/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ You will be asked for the project name and then guided through a series of promp
3535
- `--skip-install`: Skip installing dependencies
3636
- `--http`: Enable HTTP transport
3737
- `--stdio`: Enable STDIO transport
38+
- `--gpt`: Initialize with GPT App template (ChatGPT/OpenAI widgets)
39+
- `--ui`: Initialize with MCP App template (React widgets for ext-apps)
40+
- `--tailwind, --tw`: Use Tailwind CSS (only with --gpt or --ui, defaults to --ui if used alone)
3841

3942
## Getting Started
4043

packages/create-xmcp-app/src/helpers/create.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ interface ProjectOptions {
1919
skipInstall?: boolean;
2020
paths?: string[];
2121
template?: string;
22+
tailwind?: boolean;
2223
}
2324

2425
/**
@@ -41,40 +42,39 @@ export function createProject(options: ProjectOptions): void {
4142
transports,
4243
packageVersion,
4344
skipInstall,
44-
paths = ["tools", "prompts"],
45+
paths = ["tools", "prompts", "resources"],
4546
template = "typescript",
47+
tailwind = false,
4648
} = options;
4749

4850
// Ensure the project directory exists
4951
fs.ensureDirSync(projectPath);
5052

5153
// Get the template directory path
52-
const templateDir = path.join(__dirname, "../../templates", template);
54+
let templateDir: string;
55+
if (template === "gpt-apps" || template === "mcp-apps") {
56+
const subTemplate = tailwind ? "tailwind" : "default";
57+
templateDir = path.join(
58+
__dirname,
59+
"../../templates",
60+
template,
61+
subTemplate
62+
);
63+
} else {
64+
templateDir = path.join(__dirname, "../../templates", template);
65+
}
5366

5467
// Copy template files to project directory
5568
copyTemplate(templateDir, projectPath, paths);
5669

5770
// Rename special files (e.g., _gitignore to .gitignore)
5871
renameFiles(projectPath);
5972

60-
// For openai and react templates, skip config generation and package.json update
61-
// as they're already provided in the template
62-
if (template === "openai" || template === "react") {
63-
// Update package.json name only
64-
const packageJsonPath = path.join(projectPath, "package.json");
65-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
66-
packageJson.name = projectName;
67-
fs.writeFileSync(
68-
packageJsonPath,
69-
JSON.stringify(packageJson, null, 2) + "\n"
70-
);
71-
} else {
72-
// Generate xmcp.config.ts based on selected transports and paths
73-
generateConfig(projectPath, transports, paths);
73+
// Generate xmcp.config.ts based on selected transports and paths
74+
generateConfig(projectPath, transports, paths);
7475

75-
// Update package.json with project configuration
76-
updatePackageJson(projectPath, projectName, transports);
77-
}
76+
// Update package.json with project configuration
77+
updatePackageJson(projectPath, projectName, transports);
7878

7979
// Create necessary project directories
8080
createProjectDirectories(projectPath);

packages/create-xmcp-app/src/index.ts

Lines changed: 80 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,18 @@ const program = new Command()
3939
.option("--skip-install", "Skip installing dependencies", false)
4040
.option("--http", "Enable HTTP transport", false)
4141
.option("--stdio", "Enable STDIO transport", false)
42+
.option("--gpt", "Initialize with GPT App template", false)
43+
.option("--ui", "Initialize with MCP App template", false)
4244
.option(
43-
"--gpt",
44-
"[DEPRECATED] Initialize with OpenAI/ChatGPT widgets template",
45+
"--tailwind, --tw",
46+
"Use Tailwind CSS (only with --gpt or --ui)",
4547
false
4648
)
47-
.option("--ui", "[DEPRECATED] Initialize with React widgets template", false)
4849
.action(async (projectDir, options) => {
4950
console.log(chalk.bold(`\ncreate-xmcp-app@${packageJson.version}`));
5051

51-
// Show deprecation warnings for template flags
52-
if (options.gpt || options.ui) {
53-
console.log(
54-
chalk.yellow(
55-
"\nDEPRECATION WARNING: The --gpt and --ui flags will be removed in a future version."
56-
)
57-
);
58-
console.log(
59-
chalk.dim(
60-
"React components and OpenAI widgets now work natively with any xmcp app."
61-
)
62-
);
63-
console.log(
64-
chalk.dim("See: https://xmcp.dev/docs for more information.\n")
65-
);
52+
if (options.tailwind && !options.gpt && !options.ui) {
53+
options.ui = true;
6654
}
6755

6856
// If project directory wasn't specified, ask for it
@@ -106,29 +94,85 @@ const program = new Command()
10694
let transports = ["http"];
10795
let selectedPaths = ["tools", "prompts", "resources"];
10896
let template = "typescript";
97+
let templateChoice = "default";
98+
let tailwind = false;
10999

110-
// If --gpt flag is set, use openai template and force certain settings
100+
// Handle --gpt flag
111101
if (options.gpt) {
112-
template = "openai";
102+
template = "gpt-apps";
113103
transports = ["http"];
114-
selectedPaths = ["tools"]; // new OpenAI template doesn't use prompts or resources
104+
selectedPaths = ["tools"];
105+
templateChoice = "gpt-app";
106+
tailwind = options.tailwind || false;
115107
}
116108

117-
// If --ui flag is set, use react template and force certain settings
109+
// Handle --ui flag
118110
if (options.ui) {
119-
template = "react";
111+
template = "mcp-apps";
120112
transports = ["http"];
121-
selectedPaths = ["tools"]; // React template uses only tools
113+
selectedPaths = ["tools"];
114+
templateChoice = "mcp-app";
115+
tailwind = options.tailwind || false;
122116
}
123117

124-
// Handle transport selection from CLI options (only for non-gpt/ui templates)
125118
if (!options.gpt && !options.ui && (options.http || options.stdio)) {
126119
transports = [];
127120
if (options.http) transports.push("http");
128121
if (options.stdio) transports.push("stdio");
129122
}
130123

131124
if (!options.yes) {
125+
if (!options.gpt && !options.ui) {
126+
const templateAnswers = await inquirer.prompt([
127+
{
128+
type: "list",
129+
name: "template",
130+
message: "Select a template:",
131+
choices: [
132+
{
133+
name: "Default (Standard MCP server)",
134+
value: "default",
135+
},
136+
{
137+
name: "GPT App (ChatGPT/OpenAI widgets)",
138+
value: "gpt-app",
139+
},
140+
{
141+
name: "MCP App (React widgets for ext-apps)",
142+
value: "mcp-app",
143+
},
144+
],
145+
default: "default",
146+
},
147+
]);
148+
templateChoice = templateAnswers.template;
149+
150+
if (templateChoice === "gpt-app") {
151+
template = "gpt-apps";
152+
transports = ["http"];
153+
selectedPaths = ["tools"];
154+
} else if (templateChoice === "mcp-app") {
155+
template = "mcp-apps";
156+
transports = ["http"];
157+
selectedPaths = ["tools"];
158+
}
159+
160+
if (
161+
(templateChoice === "gpt-app" || templateChoice === "mcp-app") &&
162+
!options.tailwind
163+
) {
164+
const tailwindAnswers = await inquirer.prompt([
165+
{
166+
type: "confirm",
167+
name: "tailwind",
168+
message: "Would you like to use Tailwind CSS?",
169+
default: true,
170+
},
171+
]);
172+
tailwind = tailwindAnswers.tailwind;
173+
}
174+
}
175+
132176
if (options.useYarn) packageManager = "yarn";
133177
if (options.usePnpm) packageManager = "pnpm";
134178
if (options.useBun) packageManager = "bun";
@@ -156,8 +200,7 @@ const program = new Command()
156200
packageManager = pmAnswers.packageManager;
157201
}
158202

159-
// Transport selection (skip if already specified via CLI options or using --gpt/--ui)
160-
if (!options.gpt && !options.ui && !options.http && !options.stdio) {
203+
if (templateChoice === "default" && !options.http && !options.stdio) {
161204
const transportAnswers = await inquirer.prompt([
162205
{
163206
type: "list",
@@ -179,8 +222,8 @@ const program = new Command()
179222
transports = [transportAnswers.transport];
180223
}
181224

182-
// Path selection checklist (skip for --gpt/--ui template)
183-
if (!options.gpt && !options.ui) {
225+
// Path selection checklist (only for none template)
226+
if (templateChoice === "default") {
184227
const pathAnswers = await inquirer.prompt([
185228
{
186229
type: "checkbox",
@@ -232,10 +275,15 @@ const program = new Command()
232275
if (options.usePnpm) packageManager = "pnpm";
233276
if (options.useBun) packageManager = "bun";
234277

235-
// Use all paths by default in non-interactive mode (unless --gpt or --ui is set)
236-
if (!options.gpt && !options.ui) {
278+
// Use all paths by default in non-interactive mode (unless template is set)
279+
if (templateChoice === "default") {
237280
selectedPaths = ["tools", "prompts", "resources"];
238281
}
282+
283+
// Default to Tailwind for GPT and UI templates in non-interactive mode
284+
if (templateChoice === "gpt-app" || templateChoice === "mcp-app") {
285+
tailwind = true;
286+
}
239287
}
240288

241289
const spinner = ora("Creating your xmcp app...").start();
@@ -249,6 +297,7 @@ const program = new Command()
249297
skipInstall,
250298
paths: selectedPaths,
251299
template,
300+
tailwind,
252301
});
253302

254303
spinner.succeed(chalk.green("Your xmcp app is ready"));

packages/create-xmcp-app/templates/openai/README.md renamed to packages/create-xmcp-app/templates/gpt-apps/default/README.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,3 @@ npm run dev
99
```
1010

1111
This will start the development server with HTTP transport enabled.
12-
13-
## Learn More
14-
15-
- [xmcp Documentation](https://xmcp.dev)
16-
- [ChatGPT Widgets Guide](https://xmcp.dev/docs/integrations/chatgpt)

packages/create-xmcp-app/templates/openai/_gitignore renamed to packages/create-xmcp-app/templates/gpt-apps/default/_gitignore

File renamed without changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "create-xmcp-app",
3+
"version": "0.1.0",
4+
"description": "An xmcp application with ChatGPT widgets",
5+
"keywords": [
6+
"chatgpt"
7+
],
8+
"engines": {
9+
"node": ">=20.0.0"
10+
},
11+
"scripts": {
12+
"build": "xmcp build",
13+
"dev": "xmcp dev"
14+
},
15+
"dependencies": {
16+
"react": "^19.2.3",
17+
"react-dom": "^19.2.3",
18+
"zod": "^4.0.0"
19+
},
20+
"devDependencies": {
21+
"@types/react": "^19.2.7",
22+
"@types/react-dom": "^19.2.3"
23+
}
24+
}

packages/create-xmcp-app/templates/react/src/tools/weather.tsx renamed to packages/create-xmcp-app/templates/gpt-apps/default/src/tools/weather.tsx

File renamed without changes.

0 commit comments

Comments
 (0)