@leanmcp/cli 0.3.1 → 0.4.1
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.
- package/README.md +219 -294
- package/dist/index.js +124 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,316 +1,295 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.svg"
|
|
4
|
+
alt="LeanMCP Logo"
|
|
5
|
+
width="400"
|
|
6
|
+
/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>@leanmcp/cli</strong><br/>
|
|
11
|
+
Command-line tool for creating, developing, and deploying LeanMCP projects.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@leanmcp/cli">
|
|
16
|
+
<img src="https://img.shields.io/npm/v/@leanmcp/cli" alt="npm version" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@leanmcp/cli">
|
|
19
|
+
<img src="https://img.shields.io/npm/dm/@leanmcp/cli" alt="npm downloads" />
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://docs.leanmcp.com/sdk/cli">
|
|
22
|
+
<img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://discord.com/invite/DsRcA3GwPy">
|
|
25
|
+
<img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://x.com/LeanMcp">
|
|
28
|
+
<img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
|
|
29
|
+
</a>
|
|
30
|
+
</p>
|
|
4
31
|
|
|
5
32
|
## Features
|
|
6
33
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **Example service** - Includes working examples to get started
|
|
13
|
-
- **Pure ESM** - Modern ES modules with full TypeScript support
|
|
34
|
+
- **Quick Scaffolding** — Create production-ready MCP servers in seconds
|
|
35
|
+
- **Hot Reload Development** — `leanmcp dev` with UI component hot-reload
|
|
36
|
+
- **Cloud Deployment** — Deploy to LeanMCP Cloud with custom subdomains
|
|
37
|
+
- **Project Management** — List, view, and delete cloud projects
|
|
38
|
+
- **Interactive Setup** — Guided prompts for dependencies and dev server
|
|
14
39
|
|
|
15
40
|
## Installation
|
|
16
41
|
|
|
17
42
|
```bash
|
|
18
|
-
# npm
|
|
19
43
|
npm install -g @leanmcp/cli
|
|
20
|
-
|
|
21
|
-
# GitHub Packages
|
|
22
|
-
npm install -g @leanmcp/cli --registry=https://npm.pkg.github.com
|
|
23
44
|
```
|
|
24
45
|
|
|
25
|
-
Or
|
|
46
|
+
Or run without installing:
|
|
26
47
|
```bash
|
|
27
48
|
npx @leanmcp/cli create my-mcp-server
|
|
28
49
|
```
|
|
29
50
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
### Create a New Project
|
|
51
|
+
## Commands Overview
|
|
33
52
|
|
|
34
53
|
```bash
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
# Local development
|
|
55
|
+
leanmcp create <name> # Create a new project
|
|
56
|
+
leanmcp add <service> # Add a service to existing project
|
|
57
|
+
leanmcp dev # Start development server with hot-reload
|
|
58
|
+
leanmcp build # Build for production
|
|
59
|
+
leanmcp start # Start production server
|
|
60
|
+
|
|
61
|
+
# Cloud commands
|
|
62
|
+
leanmcp login # Authenticate with LeanMCP Cloud
|
|
63
|
+
leanmcp logout # Remove API key
|
|
64
|
+
leanmcp whoami # Show login status
|
|
65
|
+
leanmcp deploy <folder> # Deploy to LeanMCP Cloud
|
|
66
|
+
leanmcp projects list # List your cloud projects
|
|
67
|
+
leanmcp projects get <id> # Get project details
|
|
68
|
+
leanmcp projects delete <id> # Delete a project
|
|
41
69
|
```
|
|
42
70
|
|
|
43
|
-
|
|
71
|
+
---
|
|
44
72
|
|
|
45
|
-
|
|
46
|
-
$ leanmcp create my-sentiment-tool
|
|
47
|
-
✔ Project my-sentiment-tool created!
|
|
48
|
-
|
|
49
|
-
Success! Your MCP server is ready.
|
|
73
|
+
## Local Development
|
|
50
74
|
|
|
51
|
-
|
|
52
|
-
cd my-sentiment-tool
|
|
75
|
+
### create
|
|
53
76
|
|
|
54
|
-
|
|
55
|
-
✔ Dependencies installed successfully!
|
|
56
|
-
? Would you like to start the development server? (Y/n) Yes
|
|
77
|
+
Create a new MCP server project:
|
|
57
78
|
|
|
58
|
-
|
|
79
|
+
```bash
|
|
80
|
+
leanmcp create my-sentiment-tool
|
|
81
|
+
```
|
|
59
82
|
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
Interactive prompts will guide you through:
|
|
84
|
+
1. Creating the project structure
|
|
85
|
+
2. Installing dependencies (optional)
|
|
86
|
+
3. Starting the dev server (optional)
|
|
62
87
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
88
|
+
**Generated structure:**
|
|
89
|
+
```
|
|
90
|
+
my-mcp-server/
|
|
91
|
+
├── main.ts # Entry point with HTTP server
|
|
92
|
+
├── package.json # Dependencies and scripts
|
|
93
|
+
├── tsconfig.json # TypeScript configuration
|
|
94
|
+
└── mcp/ # Services directory
|
|
95
|
+
└── example/
|
|
96
|
+
└── index.ts # Example service with tools
|
|
66
97
|
```
|
|
67
98
|
|
|
68
|
-
###
|
|
99
|
+
### add
|
|
69
100
|
|
|
70
|
-
|
|
101
|
+
Add a new service to an existing project:
|
|
71
102
|
|
|
72
103
|
```bash
|
|
73
|
-
|
|
104
|
+
cd my-mcp-server
|
|
105
|
+
leanmcp add weather
|
|
74
106
|
```
|
|
75
107
|
|
|
76
|
-
This
|
|
77
|
-
- Creates
|
|
78
|
-
- Includes example Tool, Prompt, and Resource decorators
|
|
108
|
+
This:
|
|
109
|
+
- Creates `mcp/weather/index.ts` with example Tool, Prompt, and Resource
|
|
79
110
|
- Automatically registers the service in `main.ts`
|
|
80
|
-
- Includes
|
|
111
|
+
- Includes `@SchemaConstraint` validation examples
|
|
112
|
+
|
|
113
|
+
### dev
|
|
81
114
|
|
|
82
|
-
|
|
115
|
+
Start the development server with hot-reload:
|
|
83
116
|
|
|
84
117
|
```bash
|
|
85
|
-
|
|
86
|
-
✔ Created new service: weather
|
|
87
|
-
File: mcp/weather.ts
|
|
88
|
-
Tool: greet
|
|
89
|
-
Prompt: welcomePrompt
|
|
90
|
-
Resource: getStatus
|
|
91
|
-
|
|
92
|
-
Service automatically registered in main.ts!
|
|
118
|
+
leanmcp dev
|
|
93
119
|
```
|
|
94
120
|
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
121
|
+
This command:
|
|
122
|
+
- Scans for `@UIApp` components and builds them
|
|
123
|
+
- Starts the HTTP server with `tsx watch`
|
|
124
|
+
- Watches `mcp/` directory for changes
|
|
125
|
+
- Automatically rebuilds UI components when modified
|
|
126
|
+
- Hot-reloads when adding/removing `@UIApp` decorators
|
|
99
127
|
|
|
100
|
-
|
|
128
|
+
```bash
|
|
129
|
+
$ leanmcp dev
|
|
101
130
|
|
|
102
|
-
|
|
131
|
+
LeanMCP Development Server
|
|
103
132
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
├── main.ts # Entry point with HTTP server
|
|
107
|
-
├── package.json # Dependencies and scripts
|
|
108
|
-
├── tsconfig.json # TypeScript configuration
|
|
109
|
-
└── mcp/ # Services directory
|
|
110
|
-
└── example.ts # Example service with tools
|
|
111
|
-
```
|
|
133
|
+
ℹ Found 2 @UIApp component(s)
|
|
134
|
+
ℹ UI components built
|
|
112
135
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
### main.ts
|
|
116
|
-
Entry point that:
|
|
117
|
-
- Loads environment variables
|
|
118
|
-
- Creates MCP server instance
|
|
119
|
-
- Registers services
|
|
120
|
-
- Starts HTTP server with session management
|
|
121
|
-
|
|
122
|
-
### mcp/example.ts
|
|
123
|
-
Example service demonstrating:
|
|
124
|
-
- `@Tool` decorator for callable functions
|
|
125
|
-
- `@Resource` decorator for data sources
|
|
126
|
-
- `@Prompt` decorator for prompt templates
|
|
127
|
-
- Class-based schema validation with `@SchemaConstraint`
|
|
128
|
-
- Input/output type safety
|
|
129
|
-
|
|
130
|
-
### package.json
|
|
131
|
-
Includes:
|
|
132
|
-
- `@leanmcp/core` - Core MCP functionality
|
|
133
|
-
- `@modelcontextprotocol/sdk` - Official MCP SDK
|
|
134
|
-
- `express` - HTTP server
|
|
135
|
-
- `tsx` - TypeScript execution with hot reload
|
|
136
|
-
- All type definitions
|
|
137
|
-
|
|
138
|
-
### tsconfig.json
|
|
139
|
-
Configured with:
|
|
140
|
-
- ESNext modules
|
|
141
|
-
- Decorator support
|
|
142
|
-
- Strict type checking
|
|
143
|
-
- Source maps
|
|
136
|
+
Starting development server...
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
[HTTP][INFO] Server running on http://localhost:3001
|
|
139
|
+
[HTTP][INFO] MCP endpoint: http://localhost:3001/mcp
|
|
140
|
+
```
|
|
146
141
|
|
|
147
|
-
|
|
142
|
+
### build
|
|
143
|
+
|
|
144
|
+
Build the project for production:
|
|
148
145
|
|
|
149
146
|
```bash
|
|
150
|
-
|
|
151
|
-
npm run build # Build for production
|
|
152
|
-
npm run start # Run production build
|
|
153
|
-
npm run clean # Remove build artifacts
|
|
147
|
+
leanmcp build
|
|
154
148
|
```
|
|
155
149
|
|
|
156
|
-
|
|
150
|
+
Compiles TypeScript and bundles UI components.
|
|
157
151
|
|
|
158
|
-
###
|
|
152
|
+
### start
|
|
159
153
|
|
|
160
|
-
|
|
154
|
+
Start the production server:
|
|
161
155
|
|
|
162
156
|
```bash
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
leanmcp start
|
|
158
|
+
```
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
# 1. Create project structure
|
|
168
|
-
# 2. Ask if you want to install dependencies (Y/n)
|
|
169
|
-
# 3. If yes, ask if you want to start dev server (Y/n)
|
|
170
|
-
# 4. If yes, start server with hot reload
|
|
160
|
+
Runs the compiled production build.
|
|
171
161
|
|
|
172
|
-
|
|
173
|
-
cd my-mcp-server
|
|
174
|
-
npm install
|
|
175
|
-
npm run dev
|
|
176
|
-
```
|
|
162
|
+
---
|
|
177
163
|
|
|
178
|
-
|
|
164
|
+
## Cloud Commands
|
|
179
165
|
|
|
180
|
-
|
|
166
|
+
### login
|
|
181
167
|
|
|
182
|
-
|
|
183
|
-
# 1. Create project (answer "No" to prompts)
|
|
184
|
-
leanmcp create my-mcp-server
|
|
168
|
+
Authenticate with LeanMCP Cloud:
|
|
185
169
|
|
|
186
|
-
|
|
187
|
-
|
|
170
|
+
```bash
|
|
171
|
+
leanmcp login
|
|
172
|
+
```
|
|
188
173
|
|
|
189
|
-
|
|
190
|
-
|
|
174
|
+
Steps:
|
|
175
|
+
1. Go to [ship.leanmcp.com/api-keys](https://ship.leanmcp.com/api-keys)
|
|
176
|
+
2. Create an API key with "BUILD_AND_DEPLOY" scope
|
|
177
|
+
3. Enter the key when prompted
|
|
191
178
|
|
|
192
|
-
|
|
193
|
-
npm run dev
|
|
179
|
+
### logout
|
|
194
180
|
|
|
195
|
-
|
|
196
|
-
# - Endpoint: http://localhost:3001/mcp
|
|
197
|
-
# - Health check: http://localhost:3001/health
|
|
198
|
-
# - Hot reload enabled
|
|
181
|
+
Remove your API key:
|
|
199
182
|
|
|
200
|
-
|
|
201
|
-
|
|
183
|
+
```bash
|
|
184
|
+
leanmcp logout
|
|
202
185
|
```
|
|
203
186
|
|
|
204
|
-
|
|
187
|
+
### whoami
|
|
188
|
+
|
|
189
|
+
Check your current login status:
|
|
205
190
|
|
|
206
|
-
Test with curl:
|
|
207
191
|
```bash
|
|
208
|
-
|
|
209
|
-
curl http://localhost:3001/mcp \
|
|
210
|
-
-X POST \
|
|
211
|
-
-H "Content-Type: application/json" \
|
|
212
|
-
-d '{
|
|
213
|
-
"jsonrpc": "2.0",
|
|
214
|
-
"id": 1,
|
|
215
|
-
"method": "tools/list"
|
|
216
|
-
}'
|
|
217
|
-
|
|
218
|
-
# Call a tool
|
|
219
|
-
curl http://localhost:3001/mcp \
|
|
220
|
-
-X POST \
|
|
221
|
-
-H "Content-Type: application/json" \
|
|
222
|
-
-d '{
|
|
223
|
-
"jsonrpc": "2.0",
|
|
224
|
-
"id": 1,
|
|
225
|
-
"method": "tools/call",
|
|
226
|
-
"params": {
|
|
227
|
-
"name": "calculate",
|
|
228
|
-
"arguments": {
|
|
229
|
-
"a": 10,
|
|
230
|
-
"b": 5,
|
|
231
|
-
"operation": "add"
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}'
|
|
192
|
+
leanmcp whoami
|
|
235
193
|
```
|
|
236
194
|
|
|
237
|
-
|
|
195
|
+
### deploy
|
|
238
196
|
|
|
239
|
-
|
|
197
|
+
Deploy your MCP server to LeanMCP Cloud:
|
|
240
198
|
|
|
241
|
-
|
|
199
|
+
```bash
|
|
200
|
+
leanmcp deploy .
|
|
201
|
+
# Or specify a folder
|
|
202
|
+
leanmcp deploy ./my-project
|
|
203
|
+
```
|
|
242
204
|
|
|
243
|
-
|
|
205
|
+
Deployment process:
|
|
206
|
+
1. Creates project (or updates existing)
|
|
207
|
+
2. Packages and uploads code
|
|
208
|
+
3. Builds container image
|
|
209
|
+
4. Deploys to serverless Lambda
|
|
210
|
+
5. Configures custom subdomain
|
|
244
211
|
|
|
245
212
|
```bash
|
|
246
|
-
leanmcp
|
|
247
|
-
```
|
|
213
|
+
$ leanmcp deploy .
|
|
248
214
|
|
|
249
|
-
|
|
215
|
+
LeanMCP Deploy
|
|
250
216
|
|
|
251
|
-
|
|
217
|
+
Generated project name: swift-coral-sunset
|
|
218
|
+
Path: /path/to/my-project
|
|
252
219
|
|
|
253
|
-
|
|
220
|
+
? Subdomain for your deployment: my-api
|
|
221
|
+
✔ Subdomain 'my-api' is available
|
|
254
222
|
|
|
255
|
-
|
|
256
|
-
// mcp/weather.ts
|
|
257
|
-
import { Tool } from "@leanmcp/core";
|
|
223
|
+
? Proceed with deployment? Yes
|
|
258
224
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
```
|
|
225
|
+
✔ Project created: 7f4a3b2c...
|
|
226
|
+
✔ Project uploaded
|
|
227
|
+
✔ Build complete (45s)
|
|
228
|
+
✔ Deployed
|
|
229
|
+
✔ Subdomain configured
|
|
267
230
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
231
|
+
============================================================
|
|
232
|
+
DEPLOYMENT SUCCESSFUL!
|
|
233
|
+
============================================================
|
|
271
234
|
|
|
272
|
-
server
|
|
273
|
-
```
|
|
235
|
+
Your MCP server is now live:
|
|
274
236
|
|
|
275
|
-
|
|
237
|
+
URL: https://my-api.leanmcp.dev
|
|
276
238
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
239
|
+
Test endpoints:
|
|
240
|
+
curl https://my-api.leanmcp.dev/health
|
|
241
|
+
curl https://my-api.leanmcp.dev/mcp
|
|
280
242
|
```
|
|
281
243
|
|
|
282
|
-
|
|
244
|
+
### projects
|
|
283
245
|
|
|
284
|
-
|
|
246
|
+
Manage your cloud projects:
|
|
285
247
|
|
|
286
|
-
Set in environment variable:
|
|
287
248
|
```bash
|
|
288
|
-
|
|
249
|
+
# List all projects
|
|
250
|
+
leanmcp projects list
|
|
251
|
+
|
|
252
|
+
# Get project details
|
|
253
|
+
leanmcp projects get <project-id>
|
|
254
|
+
|
|
255
|
+
# Delete a project
|
|
256
|
+
leanmcp projects delete <project-id>
|
|
257
|
+
leanmcp projects delete <project-id> --force # Skip confirmation
|
|
289
258
|
```
|
|
290
259
|
|
|
291
|
-
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## NPM Scripts
|
|
263
|
+
|
|
264
|
+
Generated projects include:
|
|
265
|
+
|
|
292
266
|
```bash
|
|
293
|
-
|
|
267
|
+
npm run dev # Start with hot reload (tsx watch)
|
|
268
|
+
npm run build # Build for production
|
|
269
|
+
npm run start # Run production build
|
|
270
|
+
npm run clean # Remove build artifacts
|
|
294
271
|
```
|
|
295
272
|
|
|
296
|
-
##
|
|
273
|
+
## Configuration
|
|
297
274
|
|
|
298
|
-
###
|
|
275
|
+
### Port
|
|
299
276
|
|
|
300
277
|
```bash
|
|
301
|
-
|
|
302
|
-
|
|
278
|
+
PORT=4000 npm run dev
|
|
279
|
+
# Or in .env file
|
|
280
|
+
PORT=4000
|
|
303
281
|
```
|
|
304
282
|
|
|
305
|
-
|
|
283
|
+
### LeanMCP Config
|
|
306
284
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
-
|
|
313
|
-
|
|
285
|
+
Stored in `~/.leanmcp/config.json`:
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"apiKey": "airtrain_...",
|
|
289
|
+
"apiUrl": "https://api.leanmcp.com",
|
|
290
|
+
"lastUpdated": "2024-01-15T10:30:00.000Z"
|
|
291
|
+
}
|
|
292
|
+
```
|
|
314
293
|
|
|
315
294
|
## Troubleshooting
|
|
316
295
|
|
|
@@ -323,106 +302,52 @@ PORT=3002
|
|
|
323
302
|
|
|
324
303
|
### Module Not Found Errors
|
|
325
304
|
|
|
326
|
-
Ensure
|
|
305
|
+
Ensure dependencies are installed:
|
|
327
306
|
```bash
|
|
328
307
|
npm install
|
|
329
308
|
```
|
|
330
309
|
|
|
331
|
-
### TypeScript Errors
|
|
332
|
-
|
|
333
|
-
Check your `tsconfig.json` and ensure:
|
|
334
|
-
- `experimentalDecorators: true`
|
|
335
|
-
- `emitDecoratorMetadata: true`
|
|
310
|
+
### TypeScript Decorator Errors
|
|
336
311
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
312
|
+
Ensure your `tsconfig.json` has:
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"compilerOptions": {
|
|
316
|
+
"experimentalDecorators": true,
|
|
317
|
+
"emitDecoratorMetadata": true
|
|
318
|
+
}
|
|
319
|
+
}
|
|
342
320
|
```
|
|
343
321
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
Currently supports:
|
|
347
|
-
- **MCP Server** - Standard MCP server with HTTP transport
|
|
322
|
+
### Deploy: Not Logged In
|
|
348
323
|
|
|
349
|
-
|
|
350
|
-
- MCP Server with Auth
|
|
351
|
-
- MCP Server with Database
|
|
352
|
-
- MCP Server with File Storage
|
|
324
|
+
Run `leanmcp login` first to authenticate with your API key.
|
|
353
325
|
|
|
354
|
-
|
|
326
|
+
### Deploy: Subdomain Taken
|
|
355
327
|
|
|
356
|
-
|
|
357
|
-
- [basic-sentiment-tool](../../examples/basic-sentiment-tool) - Simple sentiment analysis
|
|
358
|
-
- [slack-with-auth](../../examples/slack-with-auth) - Slack integration with Cognito auth
|
|
328
|
+
Choose a different subdomain when prompted.
|
|
359
329
|
|
|
360
330
|
## Requirements
|
|
361
331
|
|
|
362
332
|
- Node.js >= 18.0.0
|
|
363
333
|
- npm >= 9.0.0
|
|
364
334
|
|
|
365
|
-
##
|
|
366
|
-
|
|
367
|
-
```bash
|
|
368
|
-
leanmcp create <name> # Create new project
|
|
369
|
-
leanmcp add <service> # Add new service to existing project
|
|
370
|
-
leanmcp --version # Show version
|
|
371
|
-
leanmcp --help # Show help
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
### Command Details
|
|
335
|
+
## Documentation
|
|
375
336
|
|
|
376
|
-
|
|
377
|
-
Creates a complete MCP server project with:
|
|
378
|
-
- Entry point (`main.ts`)
|
|
379
|
-
- Example service with Tool, Resource, and Prompt decorators
|
|
380
|
-
- TypeScript configuration
|
|
381
|
-
- Package.json with all dependencies
|
|
382
|
-
- Development and build scripts
|
|
337
|
+
- [Full Documentation](https://docs.leanmcp.com/sdk/cli)
|
|
383
338
|
|
|
384
|
-
|
|
385
|
-
- Asks if you want to install dependencies
|
|
386
|
-
- If installed, asks if you want to start dev server
|
|
387
|
-
- Runs commands in the project directory automatically
|
|
388
|
-
|
|
389
|
-
#### `add <service-name>`
|
|
390
|
-
Adds a new service to an existing project:
|
|
391
|
-
- Must be run inside a LeanMCP project directory
|
|
392
|
-
- Creates `mcp/<service-name>.ts` with template code
|
|
393
|
-
- Automatically imports and registers in `main.ts`
|
|
394
|
-
- Includes example Tool, Prompt, and Resource implementations
|
|
395
|
-
- Uses schema validation with `@SchemaConstraint` decorators
|
|
396
|
-
|
|
397
|
-
## 🌟 Showcase Your MCP Server
|
|
398
|
-
|
|
399
|
-
Built something cool with LeanMCP? We'd love to feature it!
|
|
339
|
+
## Related Packages
|
|
400
340
|
|
|
401
|
-
|
|
341
|
+
- [@leanmcp/core](https://www.npmjs.com/package/@leanmcp/core) — Core MCP server functionality
|
|
342
|
+
- [@leanmcp/auth](https://www.npmjs.com/package/@leanmcp/auth) — Authentication decorators
|
|
343
|
+
- [@leanmcp/ui](https://www.npmjs.com/package/@leanmcp/ui) — MCP App UI components
|
|
402
344
|
|
|
403
|
-
|
|
404
|
-
2. **Share** your project on GitHub
|
|
405
|
-
3. **Submit** for showcase:
|
|
406
|
-
- Open an issue: [Request Showcase](https://github.com/LeanMCP/leanmcp-sdk/issues/new?title=[Showcase]%20Your%20Project%20Name)
|
|
407
|
-
- Include:
|
|
408
|
-
- Project name and description
|
|
409
|
-
- GitHub repository link
|
|
410
|
-
- What makes it unique
|
|
411
|
-
- Screenshots or demo
|
|
345
|
+
## Links
|
|
412
346
|
|
|
347
|
+
- [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
|
|
348
|
+
- [NPM Package](https://www.npmjs.com/package/@leanmcp/cli)
|
|
349
|
+
- [LeanMCP Dashboard](https://ship.leanmcp.com)
|
|
413
350
|
|
|
414
351
|
## License
|
|
415
352
|
|
|
416
353
|
MIT
|
|
417
|
-
|
|
418
|
-
## Related Packages
|
|
419
|
-
|
|
420
|
-
- [@leanmcp/core](../core) - Core MCP server functionality
|
|
421
|
-
- [@leanmcp/auth](../auth) - Authentication decorators
|
|
422
|
-
- [@leanmcp/utils](../utils) - Utility functions
|
|
423
|
-
|
|
424
|
-
## Links
|
|
425
|
-
|
|
426
|
-
- [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
|
|
427
|
-
- [Documentation](https://github.com/LeanMCP/leanmcp-sdk#readme)
|
|
428
|
-
- [MCP Specification](https://spec.modelcontextprotocol.io/)
|
package/dist/index.js
CHANGED
|
@@ -41,7 +41,7 @@ async function scanUIApp(projectDir) {
|
|
|
41
41
|
for (const relativeFile of tsFiles) {
|
|
42
42
|
const filePath = path.join(mcpDir, relativeFile);
|
|
43
43
|
const content = await fs.readFile(filePath, "utf-8");
|
|
44
|
-
if (!content.includes("@UIApp") || !content.includes("@leanmcp/ui")) {
|
|
44
|
+
if (!content.includes("@UIApp") && !content.includes("@GPTApp") || !content.includes("@leanmcp/ui")) {
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
47
|
const uiApps = parseUIAppDecorators(content, filePath);
|
|
@@ -55,11 +55,13 @@ function parseUIAppDecorators(content, filePath) {
|
|
|
55
55
|
const classMatch = content.match(/export\s+class\s+(\w+)/);
|
|
56
56
|
const serviceName = classMatch ? classMatch[1] : "Unknown";
|
|
57
57
|
const importMap = parseImports(content, filePath);
|
|
58
|
-
const uiAppRegex = /@UIApp\s*\(\s*\{([
|
|
58
|
+
const uiAppRegex = /@(UIApp|GPTApp)\s*\(\s*\{([\s\S]+?)\}\s*\)\s*(?:async\s+)?(\w+)/g;
|
|
59
59
|
let match;
|
|
60
60
|
while ((match = uiAppRegex.exec(content)) !== null) {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
61
|
+
const decoratorName = match[1];
|
|
62
|
+
const decoratorBody = match[2];
|
|
63
|
+
const methodName = match[3];
|
|
64
|
+
const isGPTApp = decoratorName === "GPTApp";
|
|
63
65
|
let componentPath;
|
|
64
66
|
let componentName;
|
|
65
67
|
const stringMatch = decoratorBody.match(/component\s*:\s*['"]([^'"]+)['"]/);
|
|
@@ -88,14 +90,32 @@ function parseUIAppDecorators(content, filePath) {
|
|
|
88
90
|
}
|
|
89
91
|
if (!componentPath) continue;
|
|
90
92
|
const servicePrefix = serviceName.replace(/Service$/i, "").toLowerCase();
|
|
91
|
-
|
|
93
|
+
let resourceUri = `ui://${servicePrefix}/${methodName}`;
|
|
94
|
+
const uriMatch = decoratorBody.match(/uri\s*:\s*['"]([^'"]+)['"]/);
|
|
95
|
+
if (uriMatch) {
|
|
96
|
+
resourceUri = uriMatch[1];
|
|
97
|
+
}
|
|
98
|
+
let gptOptions = void 0;
|
|
99
|
+
if (isGPTApp) {
|
|
100
|
+
gptOptions = {};
|
|
101
|
+
if (decoratorBody.includes("widgetAccessible: true")) gptOptions.widgetAccessible = true;
|
|
102
|
+
if (decoratorBody.includes("prefersBorder: true")) gptOptions.prefersBorder = true;
|
|
103
|
+
const visibilityMatch = decoratorBody.match(/visibility\s*:\s*['"](public|private)['"]/);
|
|
104
|
+
if (visibilityMatch) gptOptions.visibility = visibilityMatch[1];
|
|
105
|
+
const domainMatch = decoratorBody.match(/widgetDomain\s*:\s*['"]([^'"]+)['"]/);
|
|
106
|
+
if (domainMatch) gptOptions.widgetDomain = domainMatch[1];
|
|
107
|
+
const descriptionMatch = decoratorBody.match(/widgetDescription\s*:\s*['"]([^'"]+)['"]/);
|
|
108
|
+
if (descriptionMatch) gptOptions.widgetDescription = descriptionMatch[1];
|
|
109
|
+
}
|
|
92
110
|
results.push({
|
|
93
111
|
servicePath: filePath,
|
|
94
112
|
componentPath,
|
|
95
113
|
componentName,
|
|
96
114
|
resourceUri,
|
|
97
115
|
methodName,
|
|
98
|
-
serviceName
|
|
116
|
+
serviceName,
|
|
117
|
+
isGPTApp,
|
|
118
|
+
gptOptions
|
|
99
119
|
});
|
|
100
120
|
}
|
|
101
121
|
return results;
|
|
@@ -130,6 +150,8 @@ __name(parseImports, "parseImports");
|
|
|
130
150
|
import * as vite from "vite";
|
|
131
151
|
import react from "@vitejs/plugin-react";
|
|
132
152
|
import { viteSingleFile } from "vite-plugin-singlefile";
|
|
153
|
+
import tailwindcss from "tailwindcss";
|
|
154
|
+
import autoprefixer from "autoprefixer";
|
|
133
155
|
import fs2 from "fs-extra";
|
|
134
156
|
import path2 from "path";
|
|
135
157
|
function resolveReactDependency(startDir, packageName) {
|
|
@@ -313,7 +335,30 @@ module.exports = {
|
|
|
313
335
|
}
|
|
314
336
|
`);
|
|
315
337
|
const relativeComponentPath = path2.relative(tempDir, componentPath).replace(/\\/g, "/");
|
|
316
|
-
|
|
338
|
+
const isGPTApp = uiApp.isGPTApp;
|
|
339
|
+
const entryContent = isGPTApp ? `
|
|
340
|
+
import React, { StrictMode } from 'react';
|
|
341
|
+
import { createRoot } from 'react-dom/client';
|
|
342
|
+
import { GPTAppProvider, Toaster } from '@leanmcp/ui';
|
|
343
|
+
import '@leanmcp/ui/styles.css';
|
|
344
|
+
import './styles.css';
|
|
345
|
+
import { ${componentName} } from '${relativeComponentPath.replace(/\.tsx?$/, "")}';
|
|
346
|
+
|
|
347
|
+
function App() {
|
|
348
|
+
return (
|
|
349
|
+
<GPTAppProvider appName="${componentName}">
|
|
350
|
+
<${componentName} />
|
|
351
|
+
<Toaster />
|
|
352
|
+
</GPTAppProvider>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
createRoot(document.getElementById('root')!).render(
|
|
357
|
+
<StrictMode>
|
|
358
|
+
<App />
|
|
359
|
+
</StrictMode>
|
|
360
|
+
);
|
|
361
|
+
` : `
|
|
317
362
|
import React, { StrictMode } from 'react';
|
|
318
363
|
import { createRoot } from 'react-dom/client';
|
|
319
364
|
import { AppProvider, Toaster } from '@leanmcp/ui';
|
|
@@ -340,7 +385,8 @@ createRoot(document.getElementById('root')!).render(
|
|
|
340
385
|
<App />
|
|
341
386
|
</StrictMode>
|
|
342
387
|
);
|
|
343
|
-
|
|
388
|
+
`;
|
|
389
|
+
await fs2.writeFile(entryJs, entryContent);
|
|
344
390
|
try {
|
|
345
391
|
const reactPath = resolveReactDependency(projectDir, "react");
|
|
346
392
|
const reactDomPath = resolveReactDependency(projectDir, "react-dom");
|
|
@@ -362,10 +408,10 @@ createRoot(document.getElementById('root')!).render(
|
|
|
362
408
|
css: {
|
|
363
409
|
postcss: {
|
|
364
410
|
plugins: [
|
|
365
|
-
|
|
411
|
+
tailwindcss({
|
|
366
412
|
config: tailwindConfig
|
|
367
413
|
}),
|
|
368
|
-
|
|
414
|
+
autoprefixer
|
|
369
415
|
]
|
|
370
416
|
}
|
|
371
417
|
},
|
|
@@ -374,6 +420,8 @@ createRoot(document.getElementById('root')!).render(
|
|
|
374
420
|
emptyOutDir: false,
|
|
375
421
|
sourcemap: isDev ? "inline" : false,
|
|
376
422
|
minify: !isDev,
|
|
423
|
+
// Force cache invalidation between builds to reduce memory accumulation
|
|
424
|
+
watch: null,
|
|
377
425
|
rollupOptions: {
|
|
378
426
|
input: entryHtml,
|
|
379
427
|
output: {
|
|
@@ -477,7 +525,15 @@ async function devCommand() {
|
|
|
477
525
|
for (const app of uiApps) {
|
|
478
526
|
const result = await buildUIComponent(app, cwd, true);
|
|
479
527
|
if (result.success) {
|
|
480
|
-
|
|
528
|
+
if (app.isGPTApp) {
|
|
529
|
+
manifest[app.resourceUri] = {
|
|
530
|
+
htmlPath: result.htmlPath,
|
|
531
|
+
isGPTApp: true,
|
|
532
|
+
gptMeta: app.gptOptions
|
|
533
|
+
};
|
|
534
|
+
} else {
|
|
535
|
+
manifest[app.resourceUri] = result.htmlPath;
|
|
536
|
+
}
|
|
481
537
|
} else {
|
|
482
538
|
errors.push(`${app.componentName}: ${result.error}`);
|
|
483
539
|
}
|
|
@@ -543,7 +599,15 @@ async function devCommand() {
|
|
|
543
599
|
console.log(chalk.cyan(`${action} ${app.componentName}...`));
|
|
544
600
|
const result = await buildUIComponent(app, cwd, true);
|
|
545
601
|
if (result.success) {
|
|
546
|
-
|
|
602
|
+
if (app.isGPTApp) {
|
|
603
|
+
manifest[app.resourceUri] = {
|
|
604
|
+
htmlPath: result.htmlPath,
|
|
605
|
+
isGPTApp: true,
|
|
606
|
+
gptMeta: app.gptOptions
|
|
607
|
+
};
|
|
608
|
+
} else {
|
|
609
|
+
manifest[app.resourceUri] = result.htmlPath;
|
|
610
|
+
}
|
|
547
611
|
if (await fs3.pathExists(app.componentPath)) {
|
|
548
612
|
componentHashCache.set(app.resourceUri, computeHash(app.componentPath));
|
|
549
613
|
}
|
|
@@ -556,19 +620,33 @@ async function devCommand() {
|
|
|
556
620
|
previousUIApps = currentUIApps;
|
|
557
621
|
}, 150);
|
|
558
622
|
});
|
|
623
|
+
const isWindows = process.platform === "win32";
|
|
559
624
|
let isCleaningUp = false;
|
|
560
625
|
const cleanup = /* @__PURE__ */ __name(() => {
|
|
561
626
|
if (isCleaningUp) return;
|
|
562
627
|
isCleaningUp = true;
|
|
563
628
|
console.log(chalk.gray("\nShutting down..."));
|
|
564
|
-
if (watcher)
|
|
565
|
-
|
|
629
|
+
if (watcher) {
|
|
630
|
+
watcher.close();
|
|
631
|
+
watcher = null;
|
|
632
|
+
}
|
|
633
|
+
if (!isWindows && !devServer.killed) {
|
|
634
|
+
devServer.kill("SIGTERM");
|
|
635
|
+
}
|
|
566
636
|
}, "cleanup");
|
|
567
|
-
process.
|
|
568
|
-
process.
|
|
569
|
-
devServer.on("
|
|
570
|
-
|
|
571
|
-
|
|
637
|
+
process.once("SIGINT", cleanup);
|
|
638
|
+
process.once("SIGTERM", cleanup);
|
|
639
|
+
devServer.on("error", (err) => {
|
|
640
|
+
console.error(chalk.red(`Dev server error: ${err.message}`));
|
|
641
|
+
});
|
|
642
|
+
devServer.on("exit", (code, signal) => {
|
|
643
|
+
if (watcher) {
|
|
644
|
+
watcher.close();
|
|
645
|
+
watcher = null;
|
|
646
|
+
}
|
|
647
|
+
setImmediate(() => {
|
|
648
|
+
process.exit(code ?? (signal ? 1 : 0));
|
|
649
|
+
});
|
|
572
650
|
});
|
|
573
651
|
}
|
|
574
652
|
__name(devCommand, "devCommand");
|
|
@@ -601,7 +679,11 @@ async function buildCommand() {
|
|
|
601
679
|
for (const app of uiApps) {
|
|
602
680
|
const result = await buildUIComponent(app, cwd, false);
|
|
603
681
|
if (result.success) {
|
|
604
|
-
manifest[app.resourceUri] =
|
|
682
|
+
manifest[app.resourceUri] = {
|
|
683
|
+
htmlPath: result.htmlPath,
|
|
684
|
+
isGPTApp: app.isGPTApp,
|
|
685
|
+
gptMeta: app.gptOptions
|
|
686
|
+
};
|
|
605
687
|
} else {
|
|
606
688
|
errors.push(`${app.componentName}: ${result.error}`);
|
|
607
689
|
}
|
|
@@ -676,7 +758,15 @@ async function startCommand() {
|
|
|
676
758
|
for (const app of uiApps) {
|
|
677
759
|
const result = await buildUIComponent(app, cwd, false);
|
|
678
760
|
if (result.success) {
|
|
679
|
-
|
|
761
|
+
if (app.isGPTApp) {
|
|
762
|
+
manifest[app.resourceUri] = {
|
|
763
|
+
htmlPath: result.htmlPath,
|
|
764
|
+
isGPTApp: true,
|
|
765
|
+
gptMeta: app.gptOptions
|
|
766
|
+
};
|
|
767
|
+
} else {
|
|
768
|
+
manifest[app.resourceUri] = result.htmlPath;
|
|
769
|
+
}
|
|
680
770
|
} else {
|
|
681
771
|
errors.push(`${app.componentName}: ${result.error}`);
|
|
682
772
|
}
|
|
@@ -725,17 +815,25 @@ async function startCommand() {
|
|
|
725
815
|
stdio: "inherit",
|
|
726
816
|
shell: true
|
|
727
817
|
});
|
|
818
|
+
const isWindows = process.platform === "win32";
|
|
728
819
|
let isCleaningUp = false;
|
|
729
820
|
const cleanup = /* @__PURE__ */ __name(() => {
|
|
730
821
|
if (isCleaningUp) return;
|
|
731
822
|
isCleaningUp = true;
|
|
732
823
|
console.log(chalk3.gray("\nShutting down..."));
|
|
733
|
-
server.
|
|
824
|
+
if (!isWindows && !server.killed) {
|
|
825
|
+
server.kill("SIGTERM");
|
|
826
|
+
}
|
|
734
827
|
}, "cleanup");
|
|
735
|
-
process.
|
|
736
|
-
process.
|
|
737
|
-
server.on("
|
|
738
|
-
|
|
828
|
+
process.once("SIGINT", cleanup);
|
|
829
|
+
process.once("SIGTERM", cleanup);
|
|
830
|
+
server.on("error", (err) => {
|
|
831
|
+
console.error(chalk3.red(`Server error: ${err.message}`));
|
|
832
|
+
});
|
|
833
|
+
server.on("exit", (code, signal) => {
|
|
834
|
+
setImmediate(() => {
|
|
835
|
+
process.exit(code ?? (signal ? 1 : 0));
|
|
836
|
+
});
|
|
739
837
|
});
|
|
740
838
|
}
|
|
741
839
|
__name(startCommand, "startCommand");
|