@vibekiln/cutline-mcp-cli 0.1.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.
Files changed (43) hide show
  1. package/Dockerfile +11 -0
  2. package/README.md +248 -0
  3. package/dist/auth/callback.d.ts +6 -0
  4. package/dist/auth/callback.js +97 -0
  5. package/dist/auth/keychain.d.ts +3 -0
  6. package/dist/auth/keychain.js +16 -0
  7. package/dist/commands/init.d.ts +4 -0
  8. package/dist/commands/init.js +309 -0
  9. package/dist/commands/login.d.ts +7 -0
  10. package/dist/commands/login.js +166 -0
  11. package/dist/commands/logout.d.ts +1 -0
  12. package/dist/commands/logout.js +25 -0
  13. package/dist/commands/serve.d.ts +1 -0
  14. package/dist/commands/serve.js +38 -0
  15. package/dist/commands/setup.d.ts +5 -0
  16. package/dist/commands/setup.js +278 -0
  17. package/dist/commands/status.d.ts +3 -0
  18. package/dist/commands/status.js +127 -0
  19. package/dist/commands/upgrade.d.ts +3 -0
  20. package/dist/commands/upgrade.js +112 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +64 -0
  23. package/dist/servers/chunk-DE7R7WKY.js +331 -0
  24. package/dist/servers/chunk-KMUSQOTJ.js +47 -0
  25. package/dist/servers/chunk-OP4EO6FV.js +454 -0
  26. package/dist/servers/chunk-UBBAYTW3.js +946 -0
  27. package/dist/servers/chunk-ZVWDXO6M.js +1063 -0
  28. package/dist/servers/cutline-server.js +10448 -0
  29. package/dist/servers/data-client-FPUZBUO3.js +160 -0
  30. package/dist/servers/exploration-server.js +930 -0
  31. package/dist/servers/graph-metrics-DCNR7JZN.js +12 -0
  32. package/dist/servers/integrations-server.js +107 -0
  33. package/dist/servers/output-server.js +107 -0
  34. package/dist/servers/premortem-server.js +971 -0
  35. package/dist/servers/tools-server.js +287 -0
  36. package/dist/utils/config-store.d.ts +8 -0
  37. package/dist/utils/config-store.js +35 -0
  38. package/dist/utils/config.d.ts +22 -0
  39. package/dist/utils/config.js +48 -0
  40. package/mcpb/manifest.json +77 -0
  41. package/package.json +76 -0
  42. package/server.json +42 -0
  43. package/smithery.yaml +10 -0
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ isWriteTool
4
+ } from "./chunk-KMUSQOTJ.js";
5
+ import {
6
+ guardBoundary,
7
+ guardOutput,
8
+ withPerfTracking
9
+ } from "./chunk-OP4EO6FV.js";
10
+ import {
11
+ cfApplyEdits,
12
+ cfChatWithPersona,
13
+ cfGenerateChatSuggestion,
14
+ cfGenerateTrialRun,
15
+ cfGetWikiMarkdown,
16
+ cfSaveWikiMarkdown,
17
+ getPersona,
18
+ getPodcastIntroductions,
19
+ listPersonas,
20
+ mapErrorToMcp,
21
+ requirePremiumWithAutoAuth,
22
+ validateAuth,
23
+ validateRequestSize
24
+ } from "./chunk-ZVWDXO6M.js";
25
+
26
+ // ../mcp/dist/mcp/src/tools-server.js
27
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
28
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
29
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
30
+ var server = new Server({
31
+ name: "cutline-tools",
32
+ version: "0.1.0"
33
+ }, {
34
+ capabilities: {
35
+ tools: {}
36
+ }
37
+ });
38
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
39
+ return {
40
+ tools: [
41
+ {
42
+ name: "trial_generate",
43
+ description: "Generate a trial run response",
44
+ inputSchema: {
45
+ type: "object",
46
+ properties: {
47
+ prompt: { type: "string" }
48
+ },
49
+ required: ["prompt"]
50
+ }
51
+ },
52
+ {
53
+ name: "agent_chat",
54
+ description: "Get chat suggestions for wiki editing",
55
+ inputSchema: {
56
+ type: "object",
57
+ properties: {
58
+ prompt: { type: "string" },
59
+ wikiMarkdown: { type: "string" },
60
+ auth_token: { type: "string" }
61
+ },
62
+ required: ["prompt"]
63
+ }
64
+ },
65
+ {
66
+ name: "wiki_load",
67
+ description: "Load wiki markdown",
68
+ inputSchema: {
69
+ type: "object",
70
+ properties: {
71
+ projectId: { type: "string" },
72
+ auth_token: { type: "string" }
73
+ },
74
+ required: ["projectId"]
75
+ }
76
+ },
77
+ {
78
+ name: "wiki_save",
79
+ description: "Save wiki markdown",
80
+ inputSchema: {
81
+ type: "object",
82
+ properties: {
83
+ projectId: { type: "string" },
84
+ markdown: { type: "string" },
85
+ auth_token: { type: "string" }
86
+ },
87
+ required: ["projectId", "markdown"]
88
+ }
89
+ },
90
+ {
91
+ name: "wiki_apply_edits",
92
+ description: "Apply edits to wiki",
93
+ inputSchema: {
94
+ type: "object",
95
+ properties: {
96
+ edits: { type: "array", items: { type: "object" } },
97
+ auth_token: { type: "string" }
98
+ },
99
+ required: ["edits"]
100
+ }
101
+ },
102
+ {
103
+ name: "personas_list",
104
+ description: "List personas for the authenticated user, optionally filtered by productId",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ productId: { type: "string", description: "Optional product ID to filter personas" },
109
+ auth_token: { type: "string" }
110
+ },
111
+ required: ["auth_token"]
112
+ }
113
+ },
114
+ {
115
+ name: "personas_get",
116
+ description: "Get a specific persona by ID",
117
+ inputSchema: {
118
+ type: "object",
119
+ properties: {
120
+ personaId: { type: "string" },
121
+ auth_token: { type: "string" }
122
+ },
123
+ required: ["personaId", "auth_token"]
124
+ }
125
+ },
126
+ {
127
+ name: "personas_chat",
128
+ description: "Chat with a persona. The persona object should include name, description, role, segment, demographics, personality, and behaviors.",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ persona: {
133
+ type: "object",
134
+ description: "Persona object with name, description, role, segment, demographics, personality, behaviors"
135
+ },
136
+ userMessage: { type: "string", description: "The user's message to the persona" },
137
+ product: {
138
+ type: "object",
139
+ description: "Optional product context (name, brief, etc.)"
140
+ },
141
+ conversationHistory: {
142
+ type: "array",
143
+ items: {
144
+ type: "object",
145
+ properties: {
146
+ role: { type: "string", enum: ["user", "assistant"] },
147
+ content: { type: "string" }
148
+ }
149
+ },
150
+ description: "Optional conversation history"
151
+ },
152
+ auth_token: { type: "string" }
153
+ },
154
+ required: ["persona", "userMessage", "auth_token"]
155
+ }
156
+ },
157
+ {
158
+ name: "podcast_get_participants",
159
+ description: "Get podcast-ready introductions for all personas in a product. Returns taglines, intro scripts, signature traits, and voice settings for each persona. Use this to have Cutline introduce podcast guests.",
160
+ inputSchema: {
161
+ type: "object",
162
+ properties: {
163
+ productId: { type: "string", description: "The product ID to get personas for" },
164
+ format: {
165
+ type: "string",
166
+ enum: ["json", "script", "bullets"],
167
+ description: "Output format: 'json' for structured data, 'script' for host-readable text, 'bullets' for quick reference"
168
+ },
169
+ auth_token: { type: "string" }
170
+ },
171
+ required: ["productId", "auth_token"]
172
+ }
173
+ }
174
+ ]
175
+ };
176
+ });
177
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
178
+ try {
179
+ const { name, arguments: rawArgs } = request.params;
180
+ if (!rawArgs)
181
+ throw new McpError(ErrorCode.InvalidParams, "Missing arguments");
182
+ validateRequestSize(rawArgs);
183
+ const { args } = guardBoundary(name, rawArgs);
184
+ const rawResponse = await withPerfTracking(name, async () => {
185
+ if (isWriteTool(name)) {
186
+ const peekToken = args.auth_token;
187
+ if (peekToken) {
188
+ const peekDecoded = await validateAuth(peekToken).catch(() => null);
189
+ if (peekDecoded && peekDecoded.accountType === "agent") {
190
+ throw new McpError(ErrorCode.InvalidRequest, "This is a read-only agent account. Write operations require the owner account.");
191
+ }
192
+ }
193
+ }
194
+ switch (name) {
195
+ case "trial_generate": {
196
+ const { prompt } = args;
197
+ const text = await cfGenerateTrialRun(prompt);
198
+ return {
199
+ content: [{ type: "text", text: JSON.stringify({ ok: true, text }) }]
200
+ };
201
+ }
202
+ case "agent_chat": {
203
+ const { prompt, wikiMarkdown, auth_token } = args;
204
+ await requirePremiumWithAutoAuth(auth_token);
205
+ const text = await cfGenerateChatSuggestion(prompt, wikiMarkdown);
206
+ return {
207
+ content: [{ type: "text", text: JSON.stringify({ text }) }]
208
+ };
209
+ }
210
+ case "wiki_load": {
211
+ const { projectId, auth_token } = args;
212
+ await requirePremiumWithAutoAuth(auth_token);
213
+ const markdown = await cfGetWikiMarkdown(projectId);
214
+ return {
215
+ content: [{ type: "text", text: JSON.stringify({ markdown }) }]
216
+ };
217
+ }
218
+ case "wiki_save": {
219
+ const { projectId, markdown, auth_token } = args;
220
+ const decoded = await requirePremiumWithAutoAuth(auth_token);
221
+ await cfSaveWikiMarkdown(projectId, markdown);
222
+ return {
223
+ content: [{ type: "text", text: JSON.stringify({ ok: true }) }]
224
+ };
225
+ }
226
+ case "wiki_apply_edits": {
227
+ const { edits, auth_token } = args;
228
+ await requirePremiumWithAutoAuth(auth_token);
229
+ const result = await cfApplyEdits(edits);
230
+ return {
231
+ content: [{ type: "text", text: JSON.stringify(result) }]
232
+ };
233
+ }
234
+ case "personas_list": {
235
+ const { productId, auth_token } = args;
236
+ const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
237
+ await requirePremiumWithAutoAuth(normalizedAuthToken);
238
+ const personas = await listPersonas(productId);
239
+ return {
240
+ content: [{ type: "text", text: JSON.stringify({ personas }) }]
241
+ };
242
+ }
243
+ case "personas_get": {
244
+ const { personaId, auth_token } = args;
245
+ const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
246
+ await requirePremiumWithAutoAuth(normalizedAuthToken);
247
+ const persona = await getPersona(personaId);
248
+ return {
249
+ content: [{ type: "text", text: JSON.stringify({ persona }) }]
250
+ };
251
+ }
252
+ case "personas_chat": {
253
+ const { persona, userMessage, product, conversationHistory, auth_token } = args;
254
+ const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
255
+ await requirePremiumWithAutoAuth(normalizedAuthToken);
256
+ const result = await cfChatWithPersona(persona, userMessage, product, conversationHistory);
257
+ return {
258
+ content: [{ type: "text", text: JSON.stringify(result) }]
259
+ };
260
+ }
261
+ case "podcast_get_participants": {
262
+ const { productId, format, auth_token } = args;
263
+ const normalizedAuthToken = auth_token && auth_token !== "auto" && auth_token.trim() !== "" ? auth_token : void 0;
264
+ await requirePremiumWithAutoAuth(normalizedAuthToken);
265
+ const result = await getPodcastIntroductions(productId, format || "json");
266
+ return {
267
+ content: [{ type: "text", text: JSON.stringify(result) }]
268
+ };
269
+ }
270
+ default:
271
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
272
+ }
273
+ });
274
+ return guardOutput(name, rawResponse);
275
+ } catch (error) {
276
+ throw mapErrorToMcp(error, { tool: request.params.name });
277
+ }
278
+ });
279
+ async function run() {
280
+ const transport = new StdioServerTransport();
281
+ await server.connect(transport);
282
+ console.error("Cutline Tools MCP Server running on stdio");
283
+ }
284
+ run().catch((error) => {
285
+ console.error("Fatal error running server:", error);
286
+ process.exit(1);
287
+ });
@@ -0,0 +1,8 @@
1
+ export interface McpConfig {
2
+ refreshToken?: string;
3
+ environment?: 'production' | 'staging';
4
+ apiKey?: string;
5
+ }
6
+ export declare function saveConfig(config: McpConfig): void;
7
+ export declare function loadConfig(): McpConfig;
8
+ export declare function getRefreshTokenFromFile(): string | null;
@@ -0,0 +1,35 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.cutline-mcp');
5
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
6
+ function ensureConfigDir() {
7
+ if (!fs.existsSync(CONFIG_DIR)) {
8
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
9
+ }
10
+ }
11
+ export function saveConfig(config) {
12
+ ensureConfigDir();
13
+ const current = loadConfig();
14
+ const newConfig = { ...current, ...config };
15
+ // Remove legacy fields that are no longer stored (e.g. firebaseApiKey)
16
+ // to prevent stale values from causing cross-project auth mismatches
17
+ delete newConfig.firebaseApiKey;
18
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2), { mode: 0o600 });
19
+ }
20
+ export function loadConfig() {
21
+ try {
22
+ if (fs.existsSync(CONFIG_FILE)) {
23
+ const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
24
+ return JSON.parse(content);
25
+ }
26
+ }
27
+ catch (e) {
28
+ // Ignore errors, return empty config
29
+ }
30
+ return {};
31
+ }
32
+ export function getRefreshTokenFromFile() {
33
+ const config = loadConfig();
34
+ return config.refreshToken || null;
35
+ }
@@ -0,0 +1,22 @@
1
+ export interface Config {
2
+ AUTH_URL: string;
3
+ CALLBACK_URL: string;
4
+ BASE_URL: string;
5
+ }
6
+ export interface ConfigWithApiKey extends Config {
7
+ FIREBASE_API_KEY: string;
8
+ }
9
+ /**
10
+ * Get static config (URLs only, no API key)
11
+ */
12
+ export declare function getConfig(options?: {
13
+ staging?: boolean;
14
+ }): Config;
15
+ /**
16
+ * Fetch Firebase API key from the web app's public endpoint.
17
+ * This avoids hardcoding API keys in source code.
18
+ * Falls back to environment variables if fetch fails.
19
+ */
20
+ export declare function fetchFirebaseApiKey(options?: {
21
+ staging?: boolean;
22
+ }): Promise<string>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Get static config (URLs only, no API key)
3
+ */
4
+ export function getConfig(options = {}) {
5
+ if (options.staging) {
6
+ return {
7
+ AUTH_URL: process.env.CUTLINE_AUTH_URL || 'https://cutline-staging.web.app/mcp-auth',
8
+ CALLBACK_URL: 'http://localhost:8765',
9
+ BASE_URL: 'https://cutline-staging.web.app',
10
+ };
11
+ }
12
+ return {
13
+ AUTH_URL: process.env.CUTLINE_AUTH_URL || 'https://thecutline.ai/mcp-auth',
14
+ CALLBACK_URL: 'http://localhost:8765',
15
+ BASE_URL: 'https://thecutline.ai',
16
+ };
17
+ }
18
+ /**
19
+ * Fetch Firebase API key from the web app's public endpoint.
20
+ * This avoids hardcoding API keys in source code.
21
+ * Falls back to environment variables if fetch fails.
22
+ */
23
+ export async function fetchFirebaseApiKey(options = {}) {
24
+ // First check environment variables
25
+ const envKey = process.env.FIREBASE_API_KEY || process.env.NEXT_PUBLIC_FIREBASE_API_KEY;
26
+ if (envKey) {
27
+ return envKey;
28
+ }
29
+ // Fetch from web app endpoint
30
+ const config = getConfig(options);
31
+ try {
32
+ const response = await fetch(`${config.BASE_URL}/api/firebase-config`, {
33
+ headers: { 'Accept': 'application/json' },
34
+ });
35
+ if (!response.ok) {
36
+ throw new Error(`HTTP ${response.status}`);
37
+ }
38
+ const data = await response.json();
39
+ if (!data.apiKey) {
40
+ throw new Error('No apiKey in response');
41
+ }
42
+ return data.apiKey;
43
+ }
44
+ catch (error) {
45
+ throw new Error(`Failed to fetch Firebase config from ${config.BASE_URL}/api/firebase-config. ` +
46
+ `Please set FIREBASE_API_KEY environment variable or ensure network connectivity.`);
47
+ }
48
+ }
@@ -0,0 +1,77 @@
1
+ {
2
+ "manifest_version": "0.3",
3
+ "name": "cutline-mcp",
4
+ "display_name": "Cutline — Engineering Guardrails",
5
+ "version": "0.5.0",
6
+ "description": "Security, reliability, and scalability constraints for your coding agent. Free code audits with 9 compliance frameworks built in.",
7
+ "long_description": "Cutline is a guardrail middleware for AI coding agents. It extracts non-functional requirements (security, scalability, reliability) from your ideas and injects them as structured constraints into your agent's context. Includes free code audits (3/month), SOC 2 / PCI-DSS / HIPAA / GDPR / OWASP LLM Top 10 compliance frameworks, pre-mortem risk analysis, and a Red-Green-Refactor workflow for systematic remediation.",
8
+ "author": {
9
+ "name": "Cutline",
10
+ "url": "https://thecutline.ai"
11
+ },
12
+ "repository": "https://github.com/kylewadegrove/cutline",
13
+ "homepage": "https://thecutline.ai",
14
+ "documentation": "https://thecutline.ai/docs/setup",
15
+ "license": "MIT",
16
+ "keywords": [
17
+ "security",
18
+ "guardrail",
19
+ "vibecoding",
20
+ "code-audit",
21
+ "compliance",
22
+ "soc2",
23
+ "nfr",
24
+ "constraint",
25
+ "pre-mortem"
26
+ ],
27
+ "server": {
28
+ "type": "node",
29
+ "entry_point": "server/cutline-server.js",
30
+ "mcp_config": {
31
+ "command": "npx",
32
+ "args": ["-y", "@vibekiln/cutline-mcp-cli@latest", "serve", "constraints"]
33
+ }
34
+ },
35
+ "tools": [
36
+ {
37
+ "name": "engineering_audit",
38
+ "description": "Premium deep, product-linked engineering audit with RGR remediation planning"
39
+ },
40
+ {
41
+ "name": "code_audit",
42
+ "description": "Free security, reliability, and scalability code audit of your codebase (3/month)"
43
+ },
44
+ {
45
+ "name": "constraints_auto",
46
+ "description": "Auto-inject relevant constraints based on file context"
47
+ },
48
+ {
49
+ "name": "constraints_query",
50
+ "description": "Query product constraints by keyword"
51
+ },
52
+ {
53
+ "name": "exploration_start",
54
+ "description": "Start a guided product idea exploration"
55
+ },
56
+ {
57
+ "name": "premortem_run",
58
+ "description": "Run a full pre-mortem risk analysis on a product idea"
59
+ },
60
+ {
61
+ "name": "graph_metrics",
62
+ "description": "Get engineering readiness scores and NFR coverage"
63
+ }
64
+ ],
65
+ "user_config": {
66
+ "properties": {
67
+ "CUTLINE_ENVIRONMENT": {
68
+ "type": "string",
69
+ "description": "Environment: 'production' or 'staging'",
70
+ "default": "production"
71
+ }
72
+ }
73
+ },
74
+ "compatibility": {
75
+ "clients": ["claude-desktop"]
76
+ }
77
+ }
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@vibekiln/cutline-mcp-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI and MCP servers for Cutline — authenticate, then run constraint-aware MCP servers in Cursor or any MCP client.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "cutline-mcp": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*",
12
+ "README.md",
13
+ "server.json",
14
+ "smithery.yaml",
15
+ "Dockerfile",
16
+ "mcpb/manifest.json"
17
+ ],
18
+ "scripts": {
19
+ "build:cli": "tsc",
20
+ "build:servers": "node scripts/bundle-servers.mjs",
21
+ "build": "npm run build:cli && npm run build:servers",
22
+ "build:mcpb": "bash scripts/build-mcpb.sh",
23
+ "dev": "tsc --watch",
24
+ "start": "node dist/index.js",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "mcpName": "ai.thecutline/cutline-mcp",
28
+ "keywords": [
29
+ "cutline",
30
+ "mcp",
31
+ "mcp-server",
32
+ "model-context-protocol",
33
+ "cli",
34
+ "security",
35
+ "guardrail",
36
+ "vibecoding",
37
+ "cursor",
38
+ "claude",
39
+ "windsurf",
40
+ "code-audit",
41
+ "engineering-audit",
42
+ "nfr",
43
+ "constraint",
44
+ "soc2",
45
+ "hipaa",
46
+ "pci-dss",
47
+ "gdpr",
48
+ "owasp",
49
+ "pre-mortem",
50
+ "ai-safety",
51
+ "cursor-security"
52
+ ],
53
+ "author": "Cutline",
54
+ "license": "MIT",
55
+ "dependencies": {
56
+ "@modelcontextprotocol/sdk": "^1.0.0",
57
+ "chalk": "^4.1.2",
58
+ "commander": "^11.1.0",
59
+ "dotenv": "^16.4.5",
60
+ "open": "^9.1.0",
61
+ "ora": "^5.4.1",
62
+ "zod": "^3.23.8"
63
+ },
64
+ "devDependencies": {
65
+ "@types/express": "^4.17.21",
66
+ "@types/node": "^20.0.0",
67
+ "esbuild": "^0.25.0",
68
+ "typescript": "^5.3.0"
69
+ },
70
+ "engines": {
71
+ "node": ">=18.0.0"
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ }
76
+ }
package/server.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "ai.thecutline/cutline-mcp",
4
+ "title": "Cutline — Engineering Guardrails for Vibecoding",
5
+ "description": "Security, reliability, and scalability constraints for your coding agent. Free code audits, compliance frameworks, and pre-mortem analysis.",
6
+ "version": "0.5.0",
7
+ "repository": {
8
+ "url": "https://github.com/kylewadegrove/cutline",
9
+ "source": "github"
10
+ },
11
+ "websiteUrl": "https://thecutline.ai",
12
+ "icons": {
13
+ "light": "https://thecutline.ai/assets/brand/cutline-icon-light.png",
14
+ "dark": "https://thecutline.ai/assets/brand/cutline-icon-dark.png"
15
+ },
16
+ "packages": [
17
+ {
18
+ "registryType": "npm",
19
+ "identifier": "@vibekiln/cutline-mcp-cli",
20
+ "version": "0.5.0",
21
+ "transport": {
22
+ "type": "stdio"
23
+ },
24
+ "environment_variables": [
25
+ {
26
+ "name": "CUTLINE_AUTH",
27
+ "description": "Authentication is handled via `cutline-mcp login` (stored in keychain). No manual env vars needed.",
28
+ "required": false
29
+ }
30
+ ]
31
+ }
32
+ ],
33
+ "_meta": {
34
+ "io.modelcontextprotocol.registry/publisher-provided": {
35
+ "categories": ["security", "infrastructure", "developer-tools"],
36
+ "compliance_frameworks": ["SOC 2", "PCI-DSS", "HIPAA", "GDPR/CCPA", "OWASP LLM Top 10", "FedRAMP", "GLBA", "FERPA/COPPA"],
37
+ "free_tier": true,
38
+ "free_tools": ["code_audit", "exploration_start", "exploration_chat", "exploration_graduate", "llm_status", "perf_status"],
39
+ "tool_count": 54
40
+ }
41
+ }
42
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,10 @@
1
+ # Smithery registry configuration for Cutline MCP
2
+ # https://smithery.ai/docs/publishing
3
+
4
+ runtime: "container"
5
+
6
+ env:
7
+ NODE_ENV: "production"
8
+
9
+ # The Dockerfile builds a minimal image with the bundled MCP server
10
+ # and exposes the stdio transport for Smithery's container runtime.