premanmcp 0.11.0 → 0.12.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.
- package/README.md +25 -7
- package/bin/cli.js +10 -9
- package/bin/desktop.js +58 -4
- package/bin/integrations.js +25 -37
- package/dist/server.js +84 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,14 +4,24 @@ Turn your APIs into MCP tools that coding agents can discover, call, test, and a
|
|
|
4
4
|
|
|
5
5
|
PreMan is agent-first API infrastructure. It lets backend teams expose endpoints to AI coding agents through MCP, add an auth layer around those tools, and see exactly which agent called what.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm exec -y premanmcp@latest -- onboard
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Create the account, verify the email, and get the PreMan app installed and opened
|
|
14
|
+
already signed in. A bare `preman` in a terminal does the same thing.
|
|
15
|
+
|
|
16
|
+
## Connect a coding agent
|
|
8
17
|
|
|
9
18
|
```bash
|
|
10
19
|
npm exec -y premanmcp@latest -- connect
|
|
11
20
|
```
|
|
12
21
|
|
|
13
|
-
Pick your coding agent from the list — Cursor,
|
|
14
|
-
writes that agent's MCP config for you. No hand-edited
|
|
22
|
+
Optional, and separate from starting out. Pick your coding agent from the list — Cursor,
|
|
23
|
+
Claude Code, or Codex — and PreMan writes that agent's MCP config for you. No hand-edited
|
|
24
|
+
config anywhere.
|
|
15
25
|
|
|
16
26
|
Local development form:
|
|
17
27
|
|
|
@@ -82,10 +92,18 @@ preman github # or connect it in the dashboard
|
|
|
82
92
|
preman status # which of those are done
|
|
83
93
|
```
|
|
84
94
|
|
|
85
|
-
`preman onboard` (or `setup
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
`preman onboard` (or `setup`, or a bare `preman` in a terminal) is what someone starting
|
|
96
|
+
out runs: create the account and verify the email, then install the PreMan app and open it
|
|
97
|
+
already signed in, then GitHub, AWS, Slack — one question per step, `b` to go back, and a
|
|
98
|
+
summary at the end. Connecting a coding agent is no longer part of starting out; run
|
|
99
|
+
`preman connect` when you actually want one wired into an IDE. `connect --guide` still runs
|
|
100
|
+
the full pass inside connect itself: discovery, a first test, the runner, the desktop app
|
|
101
|
+
and the integration prompts.
|
|
102
|
+
|
|
103
|
+
The app opens signed in because the CLI leaves the key it just minted in
|
|
104
|
+
`~/.preman/desktop-session.json`, which the app reads once and deletes. An app too old to
|
|
105
|
+
look for it, or a machine that is not macOS, falls back to signing in on the app's own
|
|
106
|
+
login screen with the account you just created.
|
|
89
107
|
|
|
90
108
|
Useful flags: `--agent cursor|claude-code|codex` skips the picker, `--project` writes
|
|
91
109
|
project-local config, `--print` shows the config without writing it, `--no-hook` leaves
|
package/bin/cli.js
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
CONNECT_HELP,
|
|
23
23
|
DISPATCH_HELP,
|
|
24
24
|
connectCommand,
|
|
25
|
-
discoverEndpoints,
|
|
26
25
|
dispatchCommand,
|
|
27
26
|
resolveAgentForPairing,
|
|
28
27
|
writeCursorConfig,
|
|
@@ -39,7 +38,7 @@ import { STATUS_HELP, statusCommand } from "./status.js";
|
|
|
39
38
|
import { HOOK_HELP, hookCommand, scheduleHookRepair } from "./hook.js";
|
|
40
39
|
import { RUNNER_HELP, runnerCommand } from "./runner.js";
|
|
41
40
|
import { VERIFY_HELP, verifyCommand } from "./verify.js";
|
|
42
|
-
import { DESKTOP_HELP, installDesktopCommand } from "./desktop.js";
|
|
41
|
+
import { DESKTOP_HELP, installDesktopCommand, openDesktopSignedIn } from "./desktop.js";
|
|
43
42
|
import { ACCOUNT_HELP, doctorCommand, loginBrowser, logoutCommand, watchCommand } from "./account.js";
|
|
44
43
|
import {
|
|
45
44
|
CREDENTIALS_FILE,
|
|
@@ -57,8 +56,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
57
56
|
const ROOT = path.join(__dirname, "..");
|
|
58
57
|
|
|
59
58
|
const args = process.argv.slice(2);
|
|
59
|
+
// A bare `preman` in a terminal is someone starting out, so it runs setup:
|
|
60
|
+
// account first, then the app. Piped or redirected (no TTY) it is an MCP host
|
|
61
|
+
// launching the server, which must stay the behaviour for `start`.
|
|
60
62
|
const command = args.length === 0 && process.stdin.isTTY
|
|
61
|
-
? "
|
|
63
|
+
? "onboard"
|
|
62
64
|
: args[0] === "--help" || args[0] === "-h"
|
|
63
65
|
? "help"
|
|
64
66
|
: args[0] && !args[0].startsWith("-")
|
|
@@ -89,8 +91,8 @@ function printHelp() {
|
|
|
89
91
|
["runner start|status|stop", "Run PreMan's queued agent work on this machine"],
|
|
90
92
|
["doctor", "Diagnose credentials, backend, target, integrations"],
|
|
91
93
|
["install-desktop", "Download and install the PreMan desktop app"],
|
|
92
|
-
["onboard", "
|
|
93
|
-
["connect [options]", "Pick a coding agent and connect it"],
|
|
94
|
+
["onboard", "Create an account, install the app signed in, then integrations"],
|
|
95
|
+
["connect [options]", "Pick a coding agent and connect it (optional)"],
|
|
94
96
|
["dispatch [options]", "Let PreMan start agent runs for you"],
|
|
95
97
|
["aws | github | slack", "Connect one integration on its own"],
|
|
96
98
|
["login [--browser]", "Create/login to PreMan from the terminal"],
|
|
@@ -254,14 +256,13 @@ async function main() {
|
|
|
254
256
|
} else if (command === "dispatch") {
|
|
255
257
|
await dispatchCommand(commandArgs);
|
|
256
258
|
} else if (command === "onboard" || command === "setup") {
|
|
257
|
-
// makeArgs/authenticateTerminal/
|
|
259
|
+
// makeArgs/authenticateTerminal/the desktop pair are injected rather than
|
|
258
260
|
// imported there, so integrations.js stays free of a cycle back into the CLI.
|
|
259
261
|
await onboardCommand(commandArgs, {
|
|
260
262
|
makeArgs,
|
|
261
263
|
authenticateTerminal,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
runnerCommand,
|
|
264
|
+
installDesktop: installDesktopCommand,
|
|
265
|
+
openDesktopSignedIn,
|
|
265
266
|
});
|
|
266
267
|
} else if (command === "aws") {
|
|
267
268
|
await awsCommand(makeArgs(commandArgs));
|
package/bin/desktop.js
CHANGED
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
|
|
14
14
|
import { spawn, spawnSync } from "node:child_process";
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
16
|
-
import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
17
17
|
import os from "node:os";
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
|
|
20
|
-
import { cliInvocation, makeArgs } from "./shared.js";
|
|
20
|
+
import { CREDENTIALS_DIR, cliInvocation, makeArgs } from "./shared.js";
|
|
21
21
|
|
|
22
22
|
export const DESKTOP_HELP = `
|
|
23
23
|
Install-desktop options:
|
|
@@ -32,6 +32,60 @@ const RELEASE_API = "https://api.github.com/repos/PreMan-Inc/PreMan-Desktop/rele
|
|
|
32
32
|
export const APP_NAME = "PreMan.app";
|
|
33
33
|
const DOWNLOAD_TIMEOUT_MS = 300_000;
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Where the desktop app looks for a session the CLI just established.
|
|
37
|
+
*
|
|
38
|
+
* The key travels through a file rather than the `preman://` URL because a URL
|
|
39
|
+
* is handed to LaunchServices, which records it; the file sits in the directory
|
|
40
|
+
* that already holds the same key, with the same 0600 mode, so this adds no new
|
|
41
|
+
* class of exposure. The app deletes it on read, which is what makes it
|
|
42
|
+
* single-use -- the CLI cannot know whether an installed app is new enough to
|
|
43
|
+
* consume it, so expiry is enforced on the reading side.
|
|
44
|
+
*/
|
|
45
|
+
export const DESKTOP_SESSION_FILE = path.join(CREDENTIALS_DIR, "desktop-session.json");
|
|
46
|
+
|
|
47
|
+
export function installedAppPath(destination = "/Applications") {
|
|
48
|
+
return path.join(destination, APP_NAME);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function desktopAppInstalled(destination = "/Applications") {
|
|
52
|
+
return process.platform === "darwin" && existsSync(installedAppPath(destination));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hand the account the CLI just signed in to over to the desktop app.
|
|
57
|
+
*
|
|
58
|
+
* Returns what actually happened rather than a boolean, because the caller has
|
|
59
|
+
* something different to say in each case: an app that is not installed is not
|
|
60
|
+
* a failure, it is the fallback where the customer signs in themselves.
|
|
61
|
+
*/
|
|
62
|
+
export function writeDesktopSession(creds) {
|
|
63
|
+
const apiKey = String(creds?.api_key || "").trim();
|
|
64
|
+
if (!apiKey.startsWith("pm_live_")) return { state: "no-key" };
|
|
65
|
+
mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
|
|
66
|
+
writeFileSync(
|
|
67
|
+
DESKTOP_SESSION_FILE,
|
|
68
|
+
`${JSON.stringify({ api_key: apiKey, user_email: creds?.user_email ?? null, created_at: new Date().toISOString() }, null, 2)}\n`,
|
|
69
|
+
{ mode: 0o600 }
|
|
70
|
+
);
|
|
71
|
+
return { state: "written", path: DESKTOP_SESSION_FILE };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Install-or-not aside, get the customer into the app signed in.
|
|
76
|
+
*
|
|
77
|
+
* The session is written before the app is launched so a cold start finds it on
|
|
78
|
+
* first read, rather than racing a window that is already loading.
|
|
79
|
+
*/
|
|
80
|
+
export function openDesktopSignedIn(creds, { destination = "/Applications" } = {}) {
|
|
81
|
+
if (!desktopAppInstalled(destination)) {
|
|
82
|
+
return { state: "not-installed" };
|
|
83
|
+
}
|
|
84
|
+
const handoff = writeDesktopSession(creds);
|
|
85
|
+
spawn("open", ["-a", "PreMan"], { stdio: "ignore", detached: true }).unref();
|
|
86
|
+
return { state: handoff.state === "written" ? "opened-signed-in" : "opened", handoff: handoff.state };
|
|
87
|
+
}
|
|
88
|
+
|
|
35
89
|
/**
|
|
36
90
|
* Open a Playground session URL in PreMan.app on macOS when it is installed.
|
|
37
91
|
* Windows/Linux (and Mac without the app) get the website URL printed instead.
|
|
@@ -225,8 +279,8 @@ export async function installDesktopCommand(commandArgs = []) {
|
|
|
225
279
|
chmodSync(target, 0o755);
|
|
226
280
|
|
|
227
281
|
process.stdout.write(
|
|
228
|
-
`\nInstalled ${target}\n\nNext: open PreMan and
|
|
229
|
-
|
|
282
|
+
`\nInstalled ${target}\n\nNext: open PreMan and sign in with the account you use here.\n` +
|
|
283
|
+
`\`${cliInvocation()} onboard\` installs it and opens it already signed in.\n`
|
|
230
284
|
);
|
|
231
285
|
return { state: "installed", version, arch, path: target };
|
|
232
286
|
} finally {
|
package/bin/integrations.js
CHANGED
|
@@ -337,17 +337,6 @@ export async function slackCommand(args) {
|
|
|
337
337
|
// The guided run
|
|
338
338
|
// ---------------------------------------------------------------------------
|
|
339
339
|
|
|
340
|
-
/**
|
|
341
|
-
* Why a step that needs a connected agent cannot run on its own.
|
|
342
|
-
*
|
|
343
|
-
* Both of these drive the agent `connect` just linked, so skipping that step
|
|
344
|
-
* leaves them without one -- which is a thing to say plainly, with the command
|
|
345
|
-
* that does it later, rather than a stack trace about a missing id.
|
|
346
|
-
*/
|
|
347
|
-
function needsAgent(command) {
|
|
348
|
-
return `no coding agent connected yet -- run '${cliInvocation()} connect', then '${cliInvocation()} ${command}'`;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
340
|
/** "yes" | "no" | "back" -- back only offered once there is somewhere to go. */
|
|
352
341
|
async function askStep(question, { assumeYes, canGoBack }) {
|
|
353
342
|
if (assumeYes) return "yes";
|
|
@@ -368,7 +357,7 @@ async function askStep(question, { assumeYes, canGoBack }) {
|
|
|
368
357
|
*/
|
|
369
358
|
export async function onboardCommand(
|
|
370
359
|
commandArgs,
|
|
371
|
-
{ makeArgs, authenticateTerminal,
|
|
360
|
+
{ makeArgs, authenticateTerminal, installDesktop, openDesktopSignedIn }
|
|
372
361
|
) {
|
|
373
362
|
const args = makeArgs(commandArgs);
|
|
374
363
|
const assumeYes = args.has("--yes");
|
|
@@ -378,32 +367,30 @@ export async function onboardCommand(
|
|
|
378
367
|
const creds = await authenticateTerminal(args);
|
|
379
368
|
connected(`Signed in as ${creds.user_email || "your account"}.`);
|
|
380
369
|
|
|
381
|
-
// Which agent the endpoints and runner steps drive. `connect` decided it, by
|
|
382
|
-
// detection or by asking, and this is the answer rather than a second prompt.
|
|
383
|
-
let linked = null;
|
|
384
|
-
|
|
385
370
|
const steps = [
|
|
386
371
|
{
|
|
387
|
-
name: "
|
|
388
|
-
question: "
|
|
372
|
+
name: "PreMan app",
|
|
373
|
+
question: "Install the PreMan app and open it signed in?",
|
|
389
374
|
run: async () => {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
375
|
+
const installed = await installDesktop([...commandArgs]);
|
|
376
|
+
if (installed?.state === "unsupported") {
|
|
377
|
+
// Not a failure: the download link has already been printed, and the
|
|
378
|
+
// account this step exists to create is finished either way.
|
|
379
|
+
process.stdout.write("Sign in there with the account you just used.\n");
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const opened = openDesktopSignedIn(creds);
|
|
383
|
+
if (opened.state === "opened-signed-in") {
|
|
384
|
+
process.stdout.write("Opened PreMan, signed in as this account.\n");
|
|
385
|
+
} else if (opened.state === "not-installed") {
|
|
386
|
+
process.stdout.write(
|
|
387
|
+
"PreMan is not in /Applications yet \u2014 open it once installed and sign in.\n"
|
|
388
|
+
);
|
|
389
|
+
} else {
|
|
390
|
+
// The app is there but the session could not be handed over, so say so
|
|
391
|
+
// rather than let the customer wonder why they are at a login screen.
|
|
392
|
+
process.stdout.write("Opened PreMan \u2014 sign in with the account you just used.\n");
|
|
393
|
+
}
|
|
407
394
|
},
|
|
408
395
|
},
|
|
409
396
|
{ name: "GitHub", question: "Connect GitHub?", run: () => githubCommand(args) },
|
|
@@ -464,8 +451,9 @@ export async function onboardCommand(
|
|
|
464
451
|
|
|
465
452
|
export const INTEGRATIONS_HELP = `
|
|
466
453
|
Setup options:
|
|
467
|
-
preman onboard
|
|
468
|
-
|
|
454
|
+
preman onboard Create or sign in to an account, install the
|
|
455
|
+
PreMan app signed in, then GitHub, AWS and Slack,
|
|
456
|
+
one prompt per step
|
|
469
457
|
preman aws Connect an AWS account and stream a log group
|
|
470
458
|
preman github Install the PreMan GitHub App
|
|
471
459
|
preman slack Add PreMan to a Slack workspace
|
package/dist/server.js
CHANGED
|
@@ -1332,6 +1332,90 @@ export function createServer() {
|
|
|
1332
1332
|
});
|
|
1333
1333
|
}
|
|
1334
1334
|
});
|
|
1335
|
+
// ── Collections test generation ────────────────────────────────────
|
|
1336
|
+
// The dashboard's Generate Tests / Review flagged / Setup fixtures /
|
|
1337
|
+
// Enrich with agent buttons, so an agent can run them without clicking.
|
|
1338
|
+
server.tool("generate_saved_tests", "Generate scheduled test suites for every endpoint already saved in a PreMan workspace. This is the dashboard's Collections 'Generate Tests' button: it clones the workspace's connected GitHub repository, matches the discovered routes onto the saved endpoints by method and path, and writes heuristic cases onto the ones it matches. No model is involved, so nothing invents a body for a DELETE. Write, destructive, low-confidence, and path-parameter routes come back flagged and disabled — clear them with review_generated_tests. This is not generate_tests, which writes cases for a single endpoint you describe.", {
|
|
1339
|
+
action: z
|
|
1340
|
+
.enum(["generate", "status"])
|
|
1341
|
+
.optional()
|
|
1342
|
+
.describe("generate (default) runs the scan; status is read-only counts"),
|
|
1343
|
+
integration_id: z
|
|
1344
|
+
.string()
|
|
1345
|
+
.optional()
|
|
1346
|
+
.describe("Connected repo to scan. Omit to use the one with the most synced endpoints."),
|
|
1347
|
+
workspace_id: z.string().optional().describe("Defaults to the caller's own workspace"),
|
|
1348
|
+
}, async (args) => {
|
|
1349
|
+
try {
|
|
1350
|
+
const result = await callBackend("generate_saved_tests", args);
|
|
1351
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1352
|
+
}
|
|
1353
|
+
catch (e) {
|
|
1354
|
+
return toolError(e.message, inferErrorCode(e.message), {
|
|
1355
|
+
next_actions: [
|
|
1356
|
+
"Connect a GitHub repository to this workspace in Settings → Integrations, then retry.",
|
|
1357
|
+
],
|
|
1358
|
+
related_tools: ["review_generated_tests", "setup_test_fixtures", "enrich_generated_tests"],
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
server.tool("review_generated_tests", "List and approve the generated test suites PreMan flagged for human review. This is the dashboard's 'Review flagged' button. generate_saved_tests deliberately leaves mutating and ambiguous routes disabled; this is how a reviewed suite gets turned on. Approving enables the suite's read-only schedule and does not raise the unattended write policy, so a DELETE stays gated at run time. List first and show the user each suite and its reason; only use approve_all when they asked to clear the whole queue.", {
|
|
1363
|
+
action: z
|
|
1364
|
+
.enum(["list", "approve", "approve_all"])
|
|
1365
|
+
.optional()
|
|
1366
|
+
.describe("list (default) | approve one request_id | approve_all (max 50)"),
|
|
1367
|
+
request_id: z.string().optional().describe("Saved request to approve; required for approve"),
|
|
1368
|
+
workspace_id: z.string().optional().describe("Defaults to the caller's own workspace"),
|
|
1369
|
+
}, async (args) => {
|
|
1370
|
+
try {
|
|
1371
|
+
const result = await callBackend("review_generated_tests", args);
|
|
1372
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1373
|
+
}
|
|
1374
|
+
catch (e) {
|
|
1375
|
+
return toolError(e.message, inferErrorCode(e.message), {
|
|
1376
|
+
next_actions: ["Call review_generated_tests with action='list' to see the queue."],
|
|
1377
|
+
related_tools: ["generate_saved_tests", "enrich_generated_tests"],
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
server.tool("setup_test_fixtures", "Fill the {id} path parameters in saved PreMan endpoints with real record ids. This is the dashboard's 'Setup fixtures' button: a happy-path test for GET /users/{id} needs an id that exists, so PreMan calls the sibling list route read-only, harvests ids from the response, and falls back to the endpoint's schema example. It never mints a fake UUID that would only 404 and never fires a write. Harvested values are stored in workspace settings and returned as a .env snippet. This does not approve anything.", {
|
|
1382
|
+
action: z
|
|
1383
|
+
.enum(["setup", "list"])
|
|
1384
|
+
.optional()
|
|
1385
|
+
.describe("setup (default) harvests ids; list reports what is still missing"),
|
|
1386
|
+
workspace_id: z.string().optional().describe("Defaults to the caller's own workspace"),
|
|
1387
|
+
}, async (args) => {
|
|
1388
|
+
try {
|
|
1389
|
+
const result = await callBackend("setup_test_fixtures", args);
|
|
1390
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1391
|
+
}
|
|
1392
|
+
catch (e) {
|
|
1393
|
+
return toolError(e.message, inferErrorCode(e.message), {
|
|
1394
|
+
next_actions: ["Save at least one endpoint with a {id} path parameter, then retry."],
|
|
1395
|
+
related_tools: ["generate_saved_tests", "review_generated_tests"],
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
server.tool("enrich_generated_tests", "Add model-written test cases on top of PreMan's heuristic suites. This is the dashboard's 'Enrich with agent' button. generate_saved_tests writes deterministic cases only; this pass asks a model for a few extra edge cases per suite. It touches review-cleared suites only, skips destructive and billing-sensitive requests unless workspace policy already allows that risk, skips suites already enriched, and stops after 40 suites per call so a large collection cannot fan out into hundreds of model calls. Leftovers come back as remaining.", {
|
|
1400
|
+
action: z
|
|
1401
|
+
.enum(["enrich", "list"])
|
|
1402
|
+
.optional()
|
|
1403
|
+
.describe("enrich (default) runs the pass; list returns the eligible suites"),
|
|
1404
|
+
workspace_id: z.string().optional().describe("Defaults to the caller's own workspace"),
|
|
1405
|
+
}, async (args) => {
|
|
1406
|
+
try {
|
|
1407
|
+
const result = await callBackend("enrich_generated_tests", args);
|
|
1408
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
1409
|
+
}
|
|
1410
|
+
catch (e) {
|
|
1411
|
+
return toolError(e.message, inferErrorCode(e.message), {
|
|
1412
|
+
next_actions: [
|
|
1413
|
+
"Run generate_saved_tests first, then clear the review queue with review_generated_tests.",
|
|
1414
|
+
],
|
|
1415
|
+
related_tools: ["generate_saved_tests", "review_generated_tests"],
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
});
|
|
1335
1419
|
server.tool("share_endpoints_with_ui", "Push discovered or verified endpoints into the PreMan Playground so the user can see them, test them, and convert selected endpoints into hosted MCP tools. Use this after verify_endpoints_live or when the user explicitly asks to stream endpoints to the UI.", {
|
|
1336
1420
|
endpoints: z.array(z.any()).describe("Endpoints to push. Each item should include method plus path/path_template/url; include schemas when available."),
|
|
1337
1421
|
upstream_base_url: z.string().optional().describe("Default upstream base URL for testing and MCP generation, e.g. http://127.0.0.1:8000 or https://api.example.com"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "premanmcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Turn APIs into agent-callable MCP tools with auth, testing, and audit logs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"open-mcp-preview-in-cursor": "node scripts/open-cursor-preview.mjs",
|
|
16
16
|
"test": "npm run build && npm run test:connect && npm run test:node",
|
|
17
17
|
"test:connect": "node scripts/smoke-connect.mjs",
|
|
18
|
-
"test:node": "node --test --test-timeout=90000 scripts/smoke-account.mjs scripts/smoke-cli-entrypoint.mjs scripts/smoke-launcher-config.mjs scripts/smoke-runner.mjs scripts/smoke-repo-config.mjs scripts/smoke-onboard.mjs scripts/smoke-local-detect.mjs scripts/smoke-prepush-hook.mjs scripts/smoke-cli-identity.mjs scripts/smoke-runner-heartbeat.mjs scripts/smoke-verify-prepush.mjs scripts/smoke-push-diff.mjs scripts/smoke-progress-reporter.mjs scripts/smoke-verify-plan.mjs scripts/smoke-install-desktop.mjs scripts/smoke-api-tools.mjs scripts/smoke-tests-workbench.mjs scripts/smoke-bin-scope.mjs"
|
|
18
|
+
"test:node": "node --test --test-timeout=90000 scripts/smoke-account.mjs scripts/smoke-cli-entrypoint.mjs scripts/smoke-launcher-config.mjs scripts/smoke-runner.mjs scripts/smoke-repo-config.mjs scripts/smoke-onboard.mjs scripts/smoke-local-detect.mjs scripts/smoke-prepush-hook.mjs scripts/smoke-cli-identity.mjs scripts/smoke-runner-heartbeat.mjs scripts/smoke-verify-prepush.mjs scripts/smoke-push-diff.mjs scripts/smoke-progress-reporter.mjs scripts/smoke-verify-plan.mjs scripts/smoke-install-desktop.mjs scripts/smoke-desktop-session.mjs scripts/smoke-api-tools.mjs scripts/smoke-tests-workbench.mjs scripts/smoke-bin-scope.mjs"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@modelcontextprotocol/ext-apps": "^0.1.0",
|