premanmcp 0.5.0 → 0.7.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 +34 -16
- package/bin/api_tools.js +215 -0
- package/bin/cli.js +54 -13
- package/bin/connect.js +321 -27
- package/bin/integrations.js +405 -0
- package/bin/shared.js +34 -2
- package/dist/server.js +121 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ PreMan is agent-first API infrastructure. It lets backend teams expose endpoints
|
|
|
7
7
|
## Connect
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
npm exec -y premanmcp@latest -- connect
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Pick your coding agent from the list — Cursor, Claude Code, or Codex — and PreMan
|
|
@@ -29,8 +29,8 @@ agent actually reads (`~/.cursor/mcp.json`, Claude Code's MCP config, or
|
|
|
29
29
|
{
|
|
30
30
|
"mcpServers": {
|
|
31
31
|
"preman": {
|
|
32
|
-
"command": "
|
|
33
|
-
"args": ["-y", "premanmcp@latest"],
|
|
32
|
+
"command": "npm",
|
|
33
|
+
"args": ["exec", "-y", "premanmcp@latest", "--"],
|
|
34
34
|
"env": {
|
|
35
35
|
"PREMAN_BACKEND": "https://api.preman.live",
|
|
36
36
|
"PREMAN_FRONTEND": "https://app.preman.live"
|
|
@@ -42,8 +42,21 @@ agent actually reads (`~/.cursor/mcp.json`, Claude Code's MCP config, or
|
|
|
42
42
|
|
|
43
43
|
Restart your agent afterwards, then ask it to `run preman_status` to finish linking.
|
|
44
44
|
|
|
45
|
+
`connect` reads the config back after writing it, and prints a copy-paste snippet if it
|
|
46
|
+
cannot confirm the entry landed. It then waits for your agent to check in — that
|
|
47
|
+
check-in is the only real proof the agent loaded the server.
|
|
48
|
+
|
|
49
|
+
Once the agent has checked in, `connect` walks you into your first test: it runs one
|
|
50
|
+
against an endpoint you already have, or prints the discovery brief to hand your agent,
|
|
51
|
+
followed by `preman endpoints setup` and `preman test`. Pass `--no-guide` to skip it.
|
|
52
|
+
|
|
45
53
|
Useful flags: `--agent cursor|claude-code|codex` skips the picker, `--project` writes
|
|
46
|
-
project-local config, `--print` shows the config without writing it
|
|
54
|
+
project-local config, `--print` shows the config without writing it, `--no-guide` skips
|
|
55
|
+
the guided first run.
|
|
56
|
+
|
|
57
|
+
In CI or any non-interactive shell, run `connect --agent <name> --api-key pm_live_…`.
|
|
58
|
+
Without `--agent` there is nothing to prompt on, so `connect` prints ready-to-paste
|
|
59
|
+
setup blocks for all three agents and exits 2.
|
|
47
60
|
|
|
48
61
|
`preman install` still exists and does the Cursor-only half of this.
|
|
49
62
|
|
|
@@ -63,13 +76,13 @@ npm exec -y premanmcp@latest -- login
|
|
|
63
76
|
You can also pass the key directly:
|
|
64
77
|
|
|
65
78
|
```bash
|
|
66
|
-
|
|
79
|
+
npm exec -y premanmcp@latest -- connect --api-key pm_live_xxx
|
|
67
80
|
```
|
|
68
81
|
|
|
69
82
|
For project-local config:
|
|
70
83
|
|
|
71
84
|
```bash
|
|
72
|
-
|
|
85
|
+
npm exec -y premanmcp@latest -- connect --project
|
|
73
86
|
```
|
|
74
87
|
|
|
75
88
|
## What It Does
|
|
@@ -82,6 +95,7 @@ npx premanmcp@latest connect --project
|
|
|
82
95
|
- Supports hosted MCPs with consumer tokens for customer-facing agent access.
|
|
83
96
|
- Records per-call observability so teams can audit which agent did what.
|
|
84
97
|
- Hands failing-endpoint alerts to your agent as fix tasks (`preman_get_fix_task` → repro curl → `preman_complete_fix_task`).
|
|
98
|
+
- Connects your production logs from the terminal (`connect_logs`): your agent asks where the logs live, then deploys a read-only CloudFormation role for AWS, or wires a shipper to the ingest endpoint for everything else.
|
|
85
99
|
|
|
86
100
|
## Common Agent Commands
|
|
87
101
|
|
|
@@ -111,10 +125,14 @@ Show me the audit log for this hosted MCP.
|
|
|
111
125
|
Pull my pending PreMan fix tasks and fix the failing endpoint.
|
|
112
126
|
```
|
|
113
127
|
|
|
128
|
+
```text
|
|
129
|
+
Connect my production logs to PreMan.
|
|
130
|
+
```
|
|
131
|
+
|
|
114
132
|
## Cursor
|
|
115
133
|
|
|
116
134
|
```bash
|
|
117
|
-
|
|
135
|
+
npm exec -y premanmcp@latest -- connect --agent cursor
|
|
118
136
|
```
|
|
119
137
|
|
|
120
138
|
Manual Cursor config, if you would rather write it yourself:
|
|
@@ -123,8 +141,8 @@ Manual Cursor config, if you would rather write it yourself:
|
|
|
123
141
|
{
|
|
124
142
|
"mcpServers": {
|
|
125
143
|
"preman": {
|
|
126
|
-
"command": "
|
|
127
|
-
"args": ["-y", "premanmcp@latest"]
|
|
144
|
+
"command": "npm",
|
|
145
|
+
"args": ["exec", "-y", "premanmcp@latest", "--"]
|
|
128
146
|
}
|
|
129
147
|
}
|
|
130
148
|
}
|
|
@@ -133,19 +151,19 @@ Manual Cursor config, if you would rather write it yourself:
|
|
|
133
151
|
## Claude Code
|
|
134
152
|
|
|
135
153
|
```bash
|
|
136
|
-
|
|
154
|
+
npm exec -y premanmcp@latest -- connect --agent claude-code
|
|
137
155
|
```
|
|
138
156
|
|
|
139
157
|
Equivalent manual command:
|
|
140
158
|
|
|
141
159
|
```bash
|
|
142
|
-
claude mcp add preman --
|
|
160
|
+
claude mcp add preman -- npm exec -y premanmcp@latest --
|
|
143
161
|
```
|
|
144
162
|
|
|
145
163
|
## Codex
|
|
146
164
|
|
|
147
165
|
```bash
|
|
148
|
-
|
|
166
|
+
npm exec -y premanmcp@latest -- connect --agent codex
|
|
149
167
|
```
|
|
150
168
|
|
|
151
169
|
Writes an `[mcp_servers.preman]` block into `~/.codex/config.toml`.
|
|
@@ -153,10 +171,10 @@ Writes an `[mcp_servers.preman]` block into `~/.codex/config.toml`.
|
|
|
153
171
|
## CLI
|
|
154
172
|
|
|
155
173
|
```bash
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
174
|
+
npm exec -y premanmcp@latest -- connect # Pick an agent and connect it
|
|
175
|
+
npm exec -y premanmcp@latest -- connect --agent codex # Skip the picker
|
|
176
|
+
npm exec -y premanmcp@latest -- connect --project # Write project-local config
|
|
177
|
+
npm exec -y premanmcp@latest -- connect --print # Print config without writing
|
|
160
178
|
npm exec -y premanmcp@latest -- # Start the MCP server
|
|
161
179
|
npm exec -y premanmcp@latest -- login # Create/login and generate a PreMan API key
|
|
162
180
|
npm exec -y premanmcp@latest -- install # Cursor-only installer (legacy)
|
package/bin/api_tools.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `preman endpoints` and `preman test` — the API-testing surface of the CLI.
|
|
3
|
+
*
|
|
4
|
+
* Both are thin proxies over `POST /mcp/call-tool`, exactly like the MCP
|
|
5
|
+
* server's tools, so the CLI and an agent calling the same tool see identical
|
|
6
|
+
* behaviour. Requires a pm_live_ key (flag, env, or stored credentials).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
|
|
11
|
+
import { callBackendJson, makeArgs, resolveApiKey } from "./shared.js";
|
|
12
|
+
|
|
13
|
+
export const ENDPOINTS_HELP = `
|
|
14
|
+
Endpoints:
|
|
15
|
+
preman endpoints list [--status s] [--method m] [--workbench] [--json-out]
|
|
16
|
+
List endpoints saved in PreMan (add --workbench for runnable requests).
|
|
17
|
+
preman endpoints discover [--framework hint]
|
|
18
|
+
Print the codebase-discovery brief for your coding agent to execute.
|
|
19
|
+
preman endpoints setup (--file endpoints.json | --ids id1,id2) [--base-url url] [--no-workbench]
|
|
20
|
+
Register discovered endpoints and set them up as runnable requests.
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export const TEST_HELP = `
|
|
24
|
+
Testing:
|
|
25
|
+
preman test <request-or-endpoint-id> [options]
|
|
26
|
+
Generate scenario tests for one endpoint and run them.
|
|
27
|
+
--scenario "..." Add a described scenario (repeatable)
|
|
28
|
+
--no-run Generate only, don't execute
|
|
29
|
+
--allow-writes Permit mutating cases / write endpoints
|
|
30
|
+
--code Emit unit-test files (prints filename + content)
|
|
31
|
+
--framework pytest|jest Framework for --code (default pytest)
|
|
32
|
+
--stress Run a bounded load test instead
|
|
33
|
+
--duration n --rps n --concurrency n Stress parameters
|
|
34
|
+
--json-out Print the raw JSON result
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
class CliError extends Error {
|
|
38
|
+
constructor(message, exitCode = 1) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.exitCode = exitCode;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function callTool(args, tool, toolArguments) {
|
|
45
|
+
const token = resolveApiKey(args);
|
|
46
|
+
if (!token) {
|
|
47
|
+
throw new CliError(
|
|
48
|
+
"No PreMan API key. Run `preman login` or pass --api-key pm_live_...",
|
|
49
|
+
2,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const result = await callBackendJson(args, "POST", "/mcp/call-tool", {
|
|
53
|
+
json: { tool, arguments: toolArguments },
|
|
54
|
+
token,
|
|
55
|
+
});
|
|
56
|
+
if (!result.ok) {
|
|
57
|
+
const detail = result.detail || result.message || result.raw || "backend error";
|
|
58
|
+
throw new CliError(`${tool} failed: ${result.status_code} ${detail}`);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function printJson(value) {
|
|
64
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function collectRepeated(commandArgs, flag) {
|
|
68
|
+
const out = [];
|
|
69
|
+
for (let i = 0; i < commandArgs.length; i += 1) {
|
|
70
|
+
if (commandArgs[i] === flag && commandArgs[i + 1]) out.push(commandArgs[i + 1]);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function endpointsCommand(commandArgs) {
|
|
76
|
+
const sub = commandArgs[0] && !commandArgs[0].startsWith("-") ? commandArgs[0] : "list";
|
|
77
|
+
const rest = sub === commandArgs[0] ? commandArgs.slice(1) : commandArgs;
|
|
78
|
+
const args = makeArgs(rest);
|
|
79
|
+
|
|
80
|
+
if (sub === "list") {
|
|
81
|
+
const toolArguments = {
|
|
82
|
+
status: args.value("--status", "") || undefined,
|
|
83
|
+
method: args.value("--method", "") || undefined,
|
|
84
|
+
include_workbench: args.has("--workbench"),
|
|
85
|
+
limit: Number(args.value("--limit", "50")),
|
|
86
|
+
};
|
|
87
|
+
const result = await callTool(args, "get_endpoints", toolArguments);
|
|
88
|
+
if (args.has("--json-out")) return printJson(result);
|
|
89
|
+
const rows = result.endpoints || [];
|
|
90
|
+
process.stdout.write(`${rows.length} registry endpoint(s)\n`);
|
|
91
|
+
for (const ep of rows) {
|
|
92
|
+
const status = ep.status ? ` [${ep.status}]` : "";
|
|
93
|
+
process.stdout.write(` ${ep.method || "GET"} ${ep.path_template || ep.url || ""}${status} ${ep.id || ""}\n`);
|
|
94
|
+
}
|
|
95
|
+
const workbench = result.workbench_requests || [];
|
|
96
|
+
if (workbench.length) {
|
|
97
|
+
process.stdout.write(`${workbench.length} runnable workbench request(s)\n`);
|
|
98
|
+
for (const r of workbench) {
|
|
99
|
+
process.stdout.write(` ${r.method} ${r.url} ${r.id}\n`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (sub === "discover") {
|
|
106
|
+
const result = await callTool(args, "discover_endpoints_from_codebase", {
|
|
107
|
+
base_path: args.value("--path", "."),
|
|
108
|
+
framework_hint: args.value("--framework", "") || undefined,
|
|
109
|
+
});
|
|
110
|
+
if (args.has("--json-out")) return printJson(result);
|
|
111
|
+
for (const line of result.instructions || []) process.stdout.write(`${line}\n`);
|
|
112
|
+
process.stdout.write(
|
|
113
|
+
"\nHand this brief to your coding agent, then run `preman endpoints setup --file endpoints.json`.\n",
|
|
114
|
+
);
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (sub === "setup") {
|
|
119
|
+
const file = args.value("--file", "");
|
|
120
|
+
const ids = args.value("--ids", "");
|
|
121
|
+
const toolArguments = {
|
|
122
|
+
base_url: args.value("--base-url", "") || undefined,
|
|
123
|
+
setup_workbench: !args.has("--no-workbench"),
|
|
124
|
+
};
|
|
125
|
+
if (file) {
|
|
126
|
+
let parsed;
|
|
127
|
+
try {
|
|
128
|
+
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw new CliError(`could not read ${file}: ${error.message}`, 2);
|
|
131
|
+
}
|
|
132
|
+
toolArguments.endpoints = Array.isArray(parsed) ? parsed : parsed.endpoints;
|
|
133
|
+
} else if (ids) {
|
|
134
|
+
toolArguments.endpoint_ids = ids.split(",").map((s) => s.trim()).filter(Boolean);
|
|
135
|
+
} else {
|
|
136
|
+
throw new CliError("pass --file endpoints.json or --ids id1,id2", 2);
|
|
137
|
+
}
|
|
138
|
+
const result = await callTool(args, "register_discovered_endpoints", toolArguments);
|
|
139
|
+
if (args.has("--json-out")) return printJson(result);
|
|
140
|
+
process.stdout.write(
|
|
141
|
+
`registered ${result.registered || 0} endpoint(s), skipped ${result.skipped || 0}\n`,
|
|
142
|
+
);
|
|
143
|
+
const requests = result.workbench_request_ids || [];
|
|
144
|
+
if (requests.length) {
|
|
145
|
+
process.stdout.write(`runnable as: ${requests.join(", ")}\n`);
|
|
146
|
+
}
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
throw new CliError(`unknown endpoints subcommand: ${sub}${ENDPOINTS_HELP}`, 2);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Print the scenario list and run outcome from generate_endpoint_tests. */
|
|
154
|
+
export function printTestSummary(result) {
|
|
155
|
+
process.stdout.write(`${result.count || 0} scenario(s) for ${result.request_id}\n`);
|
|
156
|
+
for (const c of result.cases || []) {
|
|
157
|
+
process.stdout.write(` - ${c.name} (${c.kind})\n`);
|
|
158
|
+
}
|
|
159
|
+
const run = result.run;
|
|
160
|
+
if (!run) return;
|
|
161
|
+
process.stdout.write(
|
|
162
|
+
`run ${run.status}: ${run.passed}/${run.total} passed, ${run.skipped} skipped` +
|
|
163
|
+
`${run.aborted_reason ? `, aborted: ${run.aborted_reason}` : ""}\n`,
|
|
164
|
+
);
|
|
165
|
+
for (const o of run.outcomes || []) {
|
|
166
|
+
if (o.status !== "passed") {
|
|
167
|
+
process.stdout.write(` FAIL ${o.case_name}: ${o.response_status ?? o.error ?? ""}\n`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function testCommand(commandArgs) {
|
|
173
|
+
const target = commandArgs[0] && !commandArgs[0].startsWith("-") ? commandArgs[0] : "";
|
|
174
|
+
const rest = target ? commandArgs.slice(1) : commandArgs;
|
|
175
|
+
const args = makeArgs(rest);
|
|
176
|
+
if (!target) {
|
|
177
|
+
throw new CliError(`pass a request or endpoint id${TEST_HELP}`, 2);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (args.has("--stress")) {
|
|
181
|
+
const result = await callTool(args, "run_stress_test", {
|
|
182
|
+
target,
|
|
183
|
+
duration_seconds: Number(args.value("--duration", "15")),
|
|
184
|
+
rps: Number(args.value("--rps", "5")),
|
|
185
|
+
concurrency: Number(args.value("--concurrency", "5")),
|
|
186
|
+
allow_writes: args.has("--allow-writes"),
|
|
187
|
+
});
|
|
188
|
+
if (args.has("--json-out")) return printJson(result);
|
|
189
|
+
process.stdout.write(
|
|
190
|
+
`stress ${result.status}: ${result.total_requests} requests, ` +
|
|
191
|
+
`${Math.round((result.error_rate || 0) * 1000) / 10}% errors, ` +
|
|
192
|
+
`p50 ${result.p50_ms ?? "n/a"}ms p95 ${result.p95_ms ?? "n/a"}ms p99 ${result.p99_ms ?? "n/a"}ms, ` +
|
|
193
|
+
`${Math.round((result.throughput_rps || 0) * 10) / 10} rps\n`,
|
|
194
|
+
);
|
|
195
|
+
if (result.aborted_reason) process.stdout.write(`aborted: ${result.aborted_reason}\n`);
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const result = await callTool(args, "generate_endpoint_tests", {
|
|
200
|
+
target,
|
|
201
|
+
scenarios: collectRepeated(rest, "--scenario"),
|
|
202
|
+
run: !args.has("--no-run"),
|
|
203
|
+
allow_writes: args.has("--allow-writes"),
|
|
204
|
+
max_cases: Number(args.value("--max-cases", "10")),
|
|
205
|
+
include_code: args.has("--code"),
|
|
206
|
+
test_framework: args.value("--framework", "pytest"),
|
|
207
|
+
});
|
|
208
|
+
if (args.has("--json-out")) return printJson(result);
|
|
209
|
+
|
|
210
|
+
printTestSummary(result);
|
|
211
|
+
for (const artifact of result.code_artifacts || []) {
|
|
212
|
+
process.stdout.write(`\n===== ${artifact.filename} =====\n${artifact.content}\n`);
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
package/bin/cli.js
CHANGED
|
@@ -16,10 +16,19 @@ import os from "node:os";
|
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
|
|
19
|
+
import { ENDPOINTS_HELP, TEST_HELP, endpointsCommand, testCommand } from "./api_tools.js";
|
|
19
20
|
import { CONNECT_HELP, connectCommand, writeCursorConfig } from "./connect.js";
|
|
21
|
+
import {
|
|
22
|
+
INTEGRATIONS_HELP,
|
|
23
|
+
awsCommand,
|
|
24
|
+
githubCommand,
|
|
25
|
+
onboardCommand,
|
|
26
|
+
slackCommand,
|
|
27
|
+
} from "./integrations.js";
|
|
20
28
|
import { HOSTED_HELP, linkCommand, runCommand, toolsCommand } from "./hosted.js";
|
|
21
29
|
import {
|
|
22
30
|
CREDENTIALS_FILE,
|
|
31
|
+
cliInvocation,
|
|
23
32
|
DEFAULT_BACKEND,
|
|
24
33
|
DEFAULT_FRONTEND,
|
|
25
34
|
authenticateTerminal,
|
|
@@ -50,20 +59,38 @@ function hasFlag(name) {
|
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
function printHelp() {
|
|
62
|
+
// Say "preman x" only to people who can actually run it; everyone else gets
|
|
63
|
+
// the form that works from a bare npm install.
|
|
64
|
+
const cli = cliInvocation();
|
|
65
|
+
// Padded here rather than hand-aligned: the invocation prefix is 6 characters
|
|
66
|
+
// for a global install and 38 for npm exec, so a fixed layout is wrong for
|
|
67
|
+
// one of them.
|
|
68
|
+
const usage = [
|
|
69
|
+
["onboard", "Sign in, then connect agent, GitHub, AWS, Slack"],
|
|
70
|
+
["connect [options]", "Pick a coding agent and connect it"],
|
|
71
|
+
["aws | github | slack", "Connect one integration on its own"],
|
|
72
|
+
["login", "Create/login to PreMan from the terminal"],
|
|
73
|
+
["install [options]", "Install PreMan into Cursor MCP config"],
|
|
74
|
+
["", "Start the PreMan MCP server"],
|
|
75
|
+
["link|tools|run ...", "Drive a published hosted MCP"],
|
|
76
|
+
["endpoints list|discover|setup ...", "Discover, list, and set up API endpoints"],
|
|
77
|
+
["test <id> [--scenario ...] [--stress]", "Generate + run tests for an endpoint"],
|
|
78
|
+
];
|
|
79
|
+
const width = Math.max(...usage.map(([command]) => `${cli} ${command}`.trimEnd().length));
|
|
80
|
+
const usageLines = usage
|
|
81
|
+
.map(([command, blurb]) => ` ${`${cli} ${command}`.trimEnd().padEnd(width)} ${blurb}`)
|
|
82
|
+
.join("\n");
|
|
83
|
+
|
|
53
84
|
process.stdout.write(`PreMan MCP
|
|
54
85
|
|
|
55
86
|
Usage:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
npm exec -y premanmcp@latest -- install [options] Install PreMan into Cursor MCP config
|
|
59
|
-
npm exec -y premanmcp@latest -- Start the PreMan MCP server
|
|
60
|
-
preman link|tools|run ... Drive a published hosted MCP
|
|
61
|
-
${CONNECT_HELP}
|
|
87
|
+
${usageLines}
|
|
88
|
+
${INTEGRATIONS_HELP}${CONNECT_HELP}${ENDPOINTS_HELP}${TEST_HELP}
|
|
62
89
|
Login options:
|
|
63
90
|
--email <email> Pre-fill the email prompt
|
|
64
91
|
--backend <url> PreMan backend URL. Defaults to ${DEFAULT_BACKEND}
|
|
65
92
|
|
|
66
|
-
Install options (Cursor only — prefer '
|
|
93
|
+
Install options (Cursor only — prefer '${cli} connect'):
|
|
67
94
|
--api-key <key> PreMan API key. If omitted, stored CLI credentials are used
|
|
68
95
|
--backend <url> PreMan backend URL. Defaults to ${DEFAULT_BACKEND}
|
|
69
96
|
--frontend <url> PreMan frontend URL. Defaults to ${DEFAULT_FRONTEND}
|
|
@@ -74,9 +101,9 @@ Install options (Cursor only — prefer 'preman connect'):
|
|
|
74
101
|
--print Print the config instead of writing it
|
|
75
102
|
|
|
76
103
|
Examples:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
104
|
+
npm exec -y premanmcp@latest -- connect
|
|
105
|
+
${cli} connect --agent claude-code
|
|
106
|
+
${cli} login
|
|
80
107
|
npm exec -y premanmcp@latest -- install --project --backend http://127.0.0.1:8000
|
|
81
108
|
${HOSTED_HELP}`);
|
|
82
109
|
}
|
|
@@ -91,7 +118,7 @@ API key: ${creds.api_key}
|
|
|
91
118
|
Saved to: ${CREDENTIALS_FILE}
|
|
92
119
|
|
|
93
120
|
You can now run:
|
|
94
|
-
|
|
121
|
+
${cli} connect
|
|
95
122
|
`);
|
|
96
123
|
}
|
|
97
124
|
|
|
@@ -126,12 +153,12 @@ Server name: ${serverName}
|
|
|
126
153
|
Backend: ${serverConfig.env.PREMAN_BACKEND}
|
|
127
154
|
|
|
128
155
|
Next steps:
|
|
129
|
-
1. ${hasInlineKey ? "Your PreMan API key was written to the MCP config." : hasStoredKey ? `Your PreMan API key is saved in ${CREDENTIALS_FILE}; the MCP server will load it automatically.` : "Run
|
|
156
|
+
1. ${hasInlineKey ? "Your PreMan API key was written to the MCP config." : hasStoredKey ? `Your PreMan API key is saved in ${CREDENTIALS_FILE}; the MCP server will load it automatically.` : "Run ${cli} login to create/connect your account and generate an API key."}
|
|
130
157
|
2. Restart Cursor or toggle the PreMan MCP server off/on.
|
|
131
158
|
3. In your API repo, ask your coding agent:
|
|
132
159
|
"Use PreMan to convert the endpoints I choose into a hosted MCP server, then give me the Cursor/Claude install snippet."
|
|
133
160
|
|
|
134
|
-
Tip: '
|
|
161
|
+
Tip: '${cli} connect' also supports Claude Code and Codex, and links the agent to your account.
|
|
135
162
|
`);
|
|
136
163
|
}
|
|
137
164
|
|
|
@@ -167,6 +194,16 @@ async function main() {
|
|
|
167
194
|
await loginCommand();
|
|
168
195
|
} else if (command === "connect") {
|
|
169
196
|
await connectCommand(commandArgs);
|
|
197
|
+
} else if (command === "onboard" || command === "setup") {
|
|
198
|
+
// makeArgs/authenticateTerminal/connectCommand are injected rather than
|
|
199
|
+
// imported there, so integrations.js stays free of a cycle back into the CLI.
|
|
200
|
+
await onboardCommand(commandArgs, { makeArgs, authenticateTerminal, connectCommand });
|
|
201
|
+
} else if (command === "aws") {
|
|
202
|
+
await awsCommand(makeArgs(commandArgs));
|
|
203
|
+
} else if (command === "github") {
|
|
204
|
+
await githubCommand(makeArgs(commandArgs));
|
|
205
|
+
} else if (command === "slack") {
|
|
206
|
+
await slackCommand(makeArgs(commandArgs));
|
|
170
207
|
} else if (command === "install") {
|
|
171
208
|
await installCursorMcp();
|
|
172
209
|
} else if (command === "link") {
|
|
@@ -175,6 +212,10 @@ async function main() {
|
|
|
175
212
|
await toolsCommand(commandArgs);
|
|
176
213
|
} else if (command === "run") {
|
|
177
214
|
await runCommand(commandArgs);
|
|
215
|
+
} else if (command === "endpoints") {
|
|
216
|
+
await endpointsCommand(commandArgs);
|
|
217
|
+
} else if (command === "test") {
|
|
218
|
+
await testCommand(commandArgs);
|
|
178
219
|
} else if (command === "help" || command === "--help" || command === "-h") {
|
|
179
220
|
printHelp();
|
|
180
221
|
} else if (command === "start") {
|