premanmcp 0.3.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PreMan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # PreMan MCP
2
+
3
+ Turn your APIs into MCP tools that coding agents can discover, call, test, and audit.
4
+
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
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npx premanmcp install
11
+ ```
12
+
13
+ This writes an `preman` MCP server into `~/.cursor/mcp.json`:
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "preman": {
19
+ "command": "npx",
20
+ "args": ["-y", "premanmcp"],
21
+ "env": {
22
+ "PREMAN_API_KEY": "pm_live_YOUR_API_KEY_HERE",
23
+ "PREMAN_BACKEND": "https://api.preman.live",
24
+ "PREMAN_FRONTEND": "https://app.preman.live"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Replace `pm_live_YOUR_API_KEY_HERE` with an API key from [PreMan settings](https://app.preman.live/settings/), then restart Cursor or toggle the MCP server off/on.
32
+
33
+ You can also pass the key directly:
34
+
35
+ ```bash
36
+ npx premanmcp install --api-key pm_live_xxx
37
+ ```
38
+
39
+ For project-local installation:
40
+
41
+ ```bash
42
+ npx premanmcp install --project
43
+ ```
44
+
45
+ ## What It Does
46
+
47
+ - Converts API endpoints into agent-callable MCP tools.
48
+ - Lets agents test real backend endpoints from the IDE.
49
+ - Syncs endpoint inventory across backend and frontend workflows.
50
+ - Adds API-key auth for PreMan MCP access.
51
+ - Supports hosted MCPs with consumer tokens for customer-facing agent access.
52
+ - Records per-call observability so teams can audit which agent did what.
53
+
54
+ ## Common Agent Commands
55
+
56
+ After installing, ask your coding agent:
57
+
58
+ ```text
59
+ Use the PreMan MCP to convert my login endpoint into an MCP tool.
60
+ ```
61
+
62
+ ```text
63
+ Use PreMan to test POST /auth/login.
64
+ ```
65
+
66
+ ```text
67
+ Convert these endpoints into an MCP.
68
+ ```
69
+
70
+ ```text
71
+ Show me the audit log for this hosted MCP.
72
+ ```
73
+
74
+ ## Cursor
75
+
76
+ The installer targets Cursor by default:
77
+
78
+ ```bash
79
+ npx premanmcp install
80
+ ```
81
+
82
+ Manual Cursor config:
83
+
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "preman": {
88
+ "command": "npx",
89
+ "args": ["-y", "premanmcp"],
90
+ "env": {
91
+ "PREMAN_API_KEY": "pm_live_YOUR_API_KEY_HERE"
92
+ }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## Claude Code
99
+
100
+ ```bash
101
+ claude mcp add preman -- npx -y premanmcp
102
+ ```
103
+
104
+ ## CLI
105
+
106
+ ```bash
107
+ npx premanmcp # Start the MCP server
108
+ npx premanmcp install # Install Cursor MCP config
109
+ npx premanmcp install --print # Print config without writing
110
+ npx premanmcp install --project # Write .cursor/mcp.json in current repo
111
+ npx premanmcp install --api-key KEY # Install with your PreMan API key
112
+ ```
113
+
114
+ Options:
115
+
116
+ - `--api-key <key>`: PreMan API key.
117
+ - `--backend <url>`: PreMan backend URL. Defaults to `https://api.preman.live`.
118
+ - `--frontend <url>`: PreMan frontend URL. Defaults to `https://app.preman.live`.
119
+ - `--name <name>`: MCP server name. Defaults to `preman`.
120
+ - `--project`: Write `.cursor/mcp.json` in the current project.
121
+ - `--print`: Print the generated MCP config without writing it.
122
+
123
+ ## Environment Variables
124
+
125
+ - `PREMAN_API_KEY`: PreMan API key.
126
+ - `PREMAN_BACKEND`: PreMan backend URL.
127
+ - `PREMAN_FRONTEND`: PreMan frontend URL.
128
+
129
+ ## Product
130
+
131
+ PreMan helps teams make APIs usable by agents without giving up control. Developers can turn endpoints into MCP servers, hand customers an install snippet or hosted MCP URL, and audit every tool call through PreMan.
132
+
133
+ Dashboard: [https://app.preman.live](https://app.preman.live)
134
+
135
+ ## License
136
+
137
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PreMan CLI.
5
+ *
6
+ * Usage:
7
+ * npx premanmcp install
8
+ * npx premanmcp install --api-key pm_live_xxx
9
+ * npx premanmcp
10
+ */
11
+
12
+ import { spawn } from "node:child_process";
13
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
14
+ import os from "node:os";
15
+ import path from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const ROOT = path.join(__dirname, "..");
20
+ const DEFAULT_BACKEND = "https://api.preman.live";
21
+ const DEFAULT_FRONTEND = "https://app.preman.live";
22
+ const API_KEY_PLACEHOLDER = "pm_live_YOUR_API_KEY_HERE";
23
+
24
+ const args = process.argv.slice(2);
25
+ const command = args[0] === "--help" || args[0] === "-h"
26
+ ? "help"
27
+ : args[0] && !args[0].startsWith("-")
28
+ ? args[0]
29
+ : "start";
30
+ const commandArgs = command === "start" ? args : args.slice(1);
31
+
32
+ function argValue(name, fallback = "") {
33
+ const index = commandArgs.indexOf(name);
34
+ if (index === -1) return fallback;
35
+ return commandArgs[index + 1] || fallback;
36
+ }
37
+
38
+ function hasFlag(name) {
39
+ return commandArgs.includes(name);
40
+ }
41
+
42
+ function printHelp() {
43
+ process.stdout.write(`PreMan MCP
44
+
45
+ Usage:
46
+ npx premanmcp install [options] Install PreMan into Cursor MCP config
47
+ npx premanmcp Start the PreMan MCP server
48
+
49
+ Install options:
50
+ --api-key <key> PreMan API key. Defaults to ${API_KEY_PLACEHOLDER}
51
+ --backend <url> PreMan backend URL. Defaults to ${DEFAULT_BACKEND}
52
+ --frontend <url> PreMan frontend URL. Defaults to ${DEFAULT_FRONTEND}
53
+ --name <name> MCP server name. Defaults to preman
54
+ --project Write .cursor/mcp.json in the current project
55
+ --global Write ~/.cursor/mcp.json (default)
56
+ --print Print the config instead of writing it
57
+
58
+ Examples:
59
+ npx premanmcp install
60
+ npx premanmcp install --api-key pm_live_xxx
61
+ npx premanmcp install --project --backend http://127.0.0.1:8000
62
+ `);
63
+ }
64
+
65
+ function readJsonFile(filePath) {
66
+ if (!existsSync(filePath)) return {};
67
+ const raw = readFileSync(filePath, "utf8").trim();
68
+ if (!raw) return {};
69
+ try {
70
+ return JSON.parse(raw);
71
+ } catch (error) {
72
+ throw new Error(`Could not parse ${filePath}: ${error.message}`);
73
+ }
74
+ }
75
+
76
+ function buildServerConfig() {
77
+ const apiKey = argValue("--api-key", process.env.PREMAN_API_KEY || API_KEY_PLACEHOLDER);
78
+ const backend = argValue("--backend", process.env.PREMAN_BACKEND || DEFAULT_BACKEND);
79
+ const frontend = argValue("--frontend", process.env.PREMAN_FRONTEND || DEFAULT_FRONTEND);
80
+
81
+ return {
82
+ command: "npx",
83
+ args: ["-y", "premanmcp"],
84
+ env: {
85
+ PREMAN_API_KEY: apiKey,
86
+ PREMAN_BACKEND: backend,
87
+ PREMAN_FRONTEND: frontend,
88
+ },
89
+ };
90
+ }
91
+
92
+ function installCursorMcp() {
93
+ const serverName = argValue("--name", "preman");
94
+ const projectInstall = hasFlag("--project");
95
+ const serverConfig = buildServerConfig();
96
+
97
+ if (hasFlag("--print")) {
98
+ process.stdout.write(`${JSON.stringify({ mcpServers: { [serverName]: serverConfig } }, null, 2)}\n`);
99
+ return;
100
+ }
101
+
102
+ const configPath = projectInstall
103
+ ? path.join(process.cwd(), ".cursor", "mcp.json")
104
+ : path.join(os.homedir(), ".cursor", "mcp.json");
105
+
106
+ const config = {
107
+ ...readJsonFile(configPath),
108
+ };
109
+ config.mcpServers = {
110
+ ...(config.mcpServers || {}),
111
+ [serverName]: serverConfig,
112
+ };
113
+
114
+ const rendered = `${JSON.stringify(config, null, 2)}\n`;
115
+ mkdirSync(path.dirname(configPath), { recursive: true });
116
+ writeFileSync(configPath, rendered, { mode: 0o600 });
117
+
118
+ const usedPlaceholder = config.mcpServers[serverName].env.PREMAN_API_KEY === API_KEY_PLACEHOLDER;
119
+ process.stdout.write(`PreMan MCP installed in ${configPath}
120
+
121
+ Server name: ${serverName}
122
+ Backend: ${config.mcpServers[serverName].env.PREMAN_BACKEND}
123
+
124
+ Next steps:
125
+ 1. ${usedPlaceholder ? `Replace ${API_KEY_PLACEHOLDER} with your PreMan API key in ${configPath}. Find or create one at https://app.preman.live/settings/.` : "Your PreMan API key was written to the MCP config."}
126
+ 2. Restart Cursor or toggle the PreMan MCP server off/on.
127
+ 3. Ask your agent: "Use the PreMan MCP to convert my login endpoint into an MCP tool." Then open https://app.preman.live/ to see the session and generated MCP.
128
+ `);
129
+ }
130
+
131
+ function startServer() {
132
+ const serverPath = path.join(ROOT, "dist", "server.js");
133
+ if (!existsSync(serverPath)) {
134
+ process.stderr.write(`[preman] MCP server not found at ${serverPath}. Run npm run build before publishing.\n`);
135
+ process.exit(1);
136
+ }
137
+
138
+ const env = { ...process.env };
139
+ const apiKey = argValue("--api-key", argValue("--key", ""));
140
+ const backend = argValue("--backend", "");
141
+ const frontend = argValue("--frontend", "");
142
+ if (apiKey) env.PREMAN_API_KEY = apiKey;
143
+ if (backend) env.PREMAN_BACKEND = backend;
144
+ if (frontend) env.PREMAN_FRONTEND = frontend;
145
+ if (hasFlag("--local")) env.PREMAN_BACKEND = "";
146
+
147
+ const child = spawn(process.execPath, [serverPath, ...commandArgs], {
148
+ env,
149
+ stdio: "inherit",
150
+ });
151
+
152
+ child.on("exit", (code, signal) => {
153
+ if (signal) process.kill(process.pid, signal);
154
+ process.exit(code || 0);
155
+ });
156
+ }
157
+
158
+ if (command === "install") {
159
+ installCursorMcp();
160
+ } else if (command === "help" || command === "--help" || command === "-h") {
161
+ printHelp();
162
+ } else if (command === "start") {
163
+ startServer();
164
+ } else {
165
+ process.stderr.write(`Unknown command: ${command}\n\n`);
166
+ printHelp();
167
+ process.exit(1);
168
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Auth flow endpoints (signup, verify OTP, login, resend OTP) with JSON schemas
3
+ * for share_endpoints_with_ui / agent-sessions push.
4
+ */
5
+ export type AuthFlowEndpoint = {
6
+ method: string;
7
+ path_template: string;
8
+ description: string;
9
+ tags: string[];
10
+ source_file: string;
11
+ request_body_schema: Record<string, unknown>;
12
+ response_schema: Record<string, unknown>;
13
+ mcp_tool?: string;
14
+ };
15
+ /** Core auth endpoints aligned with routes/auth/routes.py and user_auth_* MCP tools. */
16
+ export declare function buildAuthFlowEndpoints(): AuthFlowEndpoint[];
17
+ export type ShareAuthFlowResult = {
18
+ session_id: string;
19
+ url: string;
20
+ endpoint_count: number;
21
+ upstream_base_url: string;
22
+ endpoints: AuthFlowEndpoint[];
23
+ ui: {
24
+ url: string;
25
+ note: string;
26
+ };
27
+ related_tools: string[];
28
+ };
29
+ export declare function shareAuthFlowToUi(opts: {
30
+ backendUrl: string;
31
+ frontendUrl: string;
32
+ upstreamBaseUrl?: string;
33
+ sessionId?: string;
34
+ intent?: string;
35
+ }): Promise<ShareAuthFlowResult>;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Auth flow endpoints (signup, verify OTP, login, resend OTP) with JSON schemas
3
+ * for share_endpoints_with_ui / agent-sessions push.
4
+ */
5
+ const EMAIL_PROP = { type: "string", format: "email", description: "User email" };
6
+ const PASSWORD_PROP = {
7
+ type: "string",
8
+ minLength: 6,
9
+ description: "Password (min 6 characters on the server)",
10
+ };
11
+ const OTP_PROP = { type: "string", description: "6-digit code from email" };
12
+ const TOKEN_RESPONSE = {
13
+ type: "object",
14
+ properties: {
15
+ access_token: { type: "string", description: "JWT bearer token" },
16
+ token_type: { type: "string", enum: ["bearer"] },
17
+ user: {
18
+ type: "object",
19
+ properties: {
20
+ id: { type: "string" },
21
+ email: { type: "string", format: "email" },
22
+ },
23
+ required: ["id", "email"],
24
+ },
25
+ },
26
+ required: ["access_token", "token_type", "user"],
27
+ };
28
+ const OTP_SENT_RESPONSE = {
29
+ type: "object",
30
+ properties: {
31
+ message: { type: "string" },
32
+ email_sent: { type: "boolean" },
33
+ },
34
+ required: ["message", "email_sent"],
35
+ };
36
+ /** Core auth endpoints aligned with routes/auth/routes.py and user_auth_* MCP tools. */
37
+ export function buildAuthFlowEndpoints() {
38
+ return [
39
+ {
40
+ method: "POST",
41
+ path_template: "/auth/signup",
42
+ description: "Register with email and password. Sends OTP to email; next: verify-otp.",
43
+ tags: ["auth", "signup"],
44
+ source_file: "routes/auth/routes.py",
45
+ mcp_tool: "user_auth_signup",
46
+ request_body_schema: {
47
+ type: "object",
48
+ properties: { email: EMAIL_PROP, password: PASSWORD_PROP },
49
+ required: ["email", "password"],
50
+ additionalProperties: false,
51
+ },
52
+ response_schema: {
53
+ type: "object",
54
+ properties: {
55
+ message: { type: "string" },
56
+ user_id: { type: "string" },
57
+ email: { type: "string", format: "email" },
58
+ email_sent: { type: "boolean" },
59
+ },
60
+ required: ["message", "user_id", "email", "email_sent"],
61
+ },
62
+ },
63
+ {
64
+ method: "POST",
65
+ path_template: "/auth/verify-otp",
66
+ description: "Verify email OTP after signup; returns JWT access_token.",
67
+ tags: ["auth", "otp"],
68
+ source_file: "routes/auth/routes.py",
69
+ mcp_tool: "user_auth_verify_otp",
70
+ request_body_schema: {
71
+ type: "object",
72
+ properties: { email: EMAIL_PROP, otp: OTP_PROP },
73
+ required: ["email", "otp"],
74
+ additionalProperties: false,
75
+ },
76
+ response_schema: TOKEN_RESPONSE,
77
+ },
78
+ {
79
+ method: "POST",
80
+ path_template: "/auth/login",
81
+ description: "Login with email and password. Returns access_token if email is verified.",
82
+ tags: ["auth", "login"],
83
+ source_file: "routes/auth/routes.py",
84
+ mcp_tool: "user_auth_login",
85
+ request_body_schema: {
86
+ type: "object",
87
+ properties: { email: EMAIL_PROP, password: PASSWORD_PROP },
88
+ required: ["email", "password"],
89
+ additionalProperties: false,
90
+ },
91
+ response_schema: TOKEN_RESPONSE,
92
+ },
93
+ {
94
+ method: "POST",
95
+ path_template: "/auth/resend-otp",
96
+ description: "Send (resend) verification OTP to email.",
97
+ tags: ["auth", "otp"],
98
+ source_file: "routes/auth/routes.py",
99
+ mcp_tool: "user_auth_resend_otp",
100
+ request_body_schema: {
101
+ type: "object",
102
+ properties: { email: EMAIL_PROP },
103
+ required: ["email"],
104
+ additionalProperties: false,
105
+ },
106
+ response_schema: OTP_SENT_RESPONSE,
107
+ },
108
+ ];
109
+ }
110
+ export async function shareAuthFlowToUi(opts) {
111
+ const backend = opts.backendUrl.replace(/\/+$/, "");
112
+ const frontend = opts.frontendUrl.replace(/\/+$/, "");
113
+ const upstream = (opts.upstreamBaseUrl || backend).replace(/\/+$/, "") || backend;
114
+ const sessionId = opts.sessionId?.trim() || crypto.randomUUID();
115
+ const endpoints = buildAuthFlowEndpoints().map((ep) => ({
116
+ ...ep,
117
+ base_url: upstream,
118
+ }));
119
+ const resp = await fetch(`${backend}/agent-sessions/${encodeURIComponent(sessionId)}/endpoints`, {
120
+ method: "POST",
121
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
122
+ body: JSON.stringify({
123
+ endpoints,
124
+ upstream_base_url: upstream,
125
+ intent: opts.intent || "Auth flow: signup, verify OTP, login, resend OTP",
126
+ }),
127
+ });
128
+ const text = await resp.text();
129
+ let body = {};
130
+ try {
131
+ body = text ? JSON.parse(text) : {};
132
+ }
133
+ catch {
134
+ throw new Error(`Agent session push failed: ${resp.status} ${text.slice(0, 500)}`);
135
+ }
136
+ if (!resp.ok) {
137
+ throw new Error(String(body.detail ?? body.error ?? `Agent session push failed: ${resp.status}`));
138
+ }
139
+ const sid = String(body.id ?? sessionId);
140
+ const url = `${frontend}/try?session=${encodeURIComponent(sid)}`;
141
+ return {
142
+ session_id: sid,
143
+ url,
144
+ endpoint_count: Number(body.endpoint_count ?? endpoints.length),
145
+ upstream_base_url: upstream,
146
+ endpoints: buildAuthFlowEndpoints(),
147
+ ui: {
148
+ url,
149
+ note: "Open in Cursor Agent Browser. Test signup → verify-otp → login, or resend-otp. " +
150
+ "Schemas are prefilled from routes/auth Pydantic models.",
151
+ },
152
+ related_tools: [
153
+ "user_auth_signup",
154
+ "user_auth_verify_otp",
155
+ "user_auth_login",
156
+ "user_auth_resend_otp",
157
+ ],
158
+ };
159
+ }
@@ -0,0 +1,30 @@
1
+ export declare const MCP_PREVIEW_RESOURCE_URI = "ui://preman/mcp-preview";
2
+ export declare const RESOURCE_URI_META_KEY = "ui/resourceUri";
3
+ export declare function escapeHtmlAttr(s: string): string;
4
+ export declare function escapeHtmlText(s: string): string;
5
+ export interface PreviewTool {
6
+ name?: string;
7
+ description?: string;
8
+ inputSchema?: unknown;
9
+ _endpoint_ref?: {
10
+ method?: string;
11
+ path_template?: string;
12
+ tags?: string[];
13
+ source?: string;
14
+ };
15
+ }
16
+ export interface PreviewPayload {
17
+ intent?: string | string[];
18
+ selection_method?: string | string[];
19
+ rationale?: string | string[] | Record<string, string>;
20
+ selected_count?: number;
21
+ spec_preview?: {
22
+ upstream_base_url?: string | string[];
23
+ tools?: PreviewTool[];
24
+ };
25
+ }
26
+ export declare function buildConversionPanelHtml(data: PreviewPayload): string;
27
+ export declare function writeMcpPreviewFile(panelHtml: string): Promise<{
28
+ absolutePath: string;
29
+ fileUrl: string;
30
+ }>;