pattern-mcp 0.14.1 → 0.16.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 CHANGED
@@ -15,22 +15,22 @@ design reference.
15
15
 
16
16
  [Website](https://usepattern.sh) · [npm](https://www.npmjs.com/package/pattern-mcp) · [Report an issue](https://github.com/donaldrichard19-LVD/pattern-mcp/issues/new/choose)
17
17
 
18
- **Current release: v0.14.1** — the crash/exit telemetry added in
19
- v0.14.0 (`pattern_cli_exited`) is now registered before any of this
20
- file's own module-level code runs, instead of near `main()`, so it
21
- catches more of what can go wrong on startup; both `pattern_cli_started`
22
- and `pattern_cli_exited` now also carry the running package version, so
23
- a crash right around a release can be tied to the old or new binary
24
- instead of staying ambiguous. Previously: v0.14.0 made a crash on
25
- startup diagnosable instead of silent, warned at startup if
26
- `ANTHROPIC_API_KEY` is missing or clearly malformed instead of only
27
- surfacing a raw 401 mid-call, and added one respectful retry on a 429
28
- (honoring `Retry-After`). Before that: v0.13.0 added `npx pattern-mcp init`,
29
- which sets up the connection to your MCP client for you (Claude Code,
30
- Claude Desktop, Cursor detected and configured automatically; Codex CLI
31
- gets manual instructions). Running `npx pattern-mcp` bare in your own
32
- terminal also tells you it needs a client connected, instead of silently
33
- sitting there. See
18
+ **Current release: v0.16.0** — a component request that's on the
19
+ skip-list (`button`, `input`, ...) now succeeds with no
20
+ `ANTHROPIC_API_KEY` at all, instead of failing before it ever reached
21
+ that free, local check. When a real call does fail for a missing key,
22
+ the error now names two concrete fixes (re-run `init`, or export the
23
+ key directly) instead of just naming the problem. Codex CLI's connect
24
+ instructions also now carry whatever API key you entered in the
25
+ wizard, as a ready-to-run shell export, instead of silently dropping
26
+ it. Previously: v0.15.0 made `npx pattern-mcp init` the first and only
27
+ command shown for getting started, everywhere (this README and the
28
+ website), and the connect wizard stopped asking just once -- it now
29
+ keeps offering on later bare runs until it can confirm a client is
30
+ actually connected. Before that: v0.14.1 made the crash/exit telemetry
31
+ added in v0.14.0 (`pattern_cli_exited`) register before any of this
32
+ file's own module-level code runs, and tagged it and
33
+ `pattern_cli_started` with the running package version. See
34
34
  [Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
35
35
  for more details.
36
36
 
@@ -50,11 +50,22 @@ for more details.
50
50
  ## Install
51
51
 
52
52
  ```bash
53
- npx pattern-mcp
53
+ npx pattern-mcp init
54
54
  ```
55
55
 
56
- See [Quick Start](#quick-start) below to add your Anthropic API key and connect
57
- Pattern to your MCP client.
56
+ This is the only command you run yourself. It downloads Pattern,
57
+ detects which MCP client(s) you have (Claude Code, Claude Desktop,
58
+ Cursor, or Codex CLI), connects each one for you, and offers to add
59
+ your Anthropic API key. See [Quick Start](#quick-start) below for what it
60
+ does step by step, or
61
+ [Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
62
+ if you'd rather connect a client by hand.
63
+
64
+ `npx pattern-mcp` on its own (no `init`) is the server command your MCP
65
+ client will use once connected -- running it yourself in a bare
66
+ terminal starts a real process that just sits there waiting for a
67
+ client, since nothing has told it to connect to one yet. Prefer `init`
68
+ above for getting started.
58
69
 
59
70
  ## What Pattern Does
60
71
 
@@ -224,14 +235,40 @@ threshold.
224
235
 
225
236
  ## Quick Start
226
237
 
227
- ### 1. Install
238
+ ### 1. Install and connect
228
239
 
229
240
  ```bash
230
- npx pattern-mcp
241
+ npx pattern-mcp init
231
242
  ```
232
243
 
233
- `npx` runs the `pattern-mcp` command on demand without a separate install
234
- step, used in the client configs below.
244
+ This is the only command you need to run yourself -- `npx` downloads
245
+ `pattern-mcp` on demand, then `init` detects which clients you have
246
+ installed and offers to connect each one:
247
+
248
+ - **Claude Code** -- runs `claude mcp add` for you (asks whether to make
249
+ Pattern available in every project or just this one); skips if already
250
+ connected (`claude mcp list` already shows it).
251
+ - **Claude Desktop** and **Cursor** -- merges a `pattern` entry into the
252
+ client's own config file, showing the exact change before writing it
253
+ and never touching any other server already configured there.
254
+ - **Codex CLI** -- prints the config snippet to add by hand (Codex's
255
+ config is TOML; this doesn't auto-edit it).
256
+
257
+ Optionally pastes your `ANTHROPIC_API_KEY` into whichever configs you set
258
+ up (visible in plain text as you type it, and in the files it writes) --
259
+ press Enter to skip and add it yourself later, see
260
+ [step 2](#2-add-your-anthropic-api-key) below. Run non-interactively with
261
+ `--yes` (skips the API key prompt entirely, accepts every detected
262
+ client).
263
+
264
+ If `init` doesn't detect your client, or you'd rather set it up by
265
+ hand, see [Connect Pattern to your MCP client](#connect-pattern-to-your-mcp-client)
266
+ below for the same configs, per client, done manually. The server
267
+ command either way is `npx pattern-mcp` -- this is what your client's
268
+ config launches; you shouldn't need to run it yourself. If you do run
269
+ it bare in your own terminal (e.g. to double check the install), it
270
+ will just sit there waiting for a client and periodically remind you
271
+ to run `init` -- that's expected, not a hang.
235
272
 
236
273
  <details>
237
274
  <summary>Build from source instead</summary>
@@ -243,14 +280,15 @@ npm install
243
280
  npm run build
244
281
  ```
245
282
 
246
- Use `node /absolute/path/to/pattern-mcp/dist/index.js` as the server
247
- command in place of `npx pattern-mcp` in the examples below.
283
+ Use `node /absolute/path/to/pattern-mcp/dist/index.js` in place of
284
+ `npx pattern-mcp` everywhere in this README, including inside `init`'s
285
+ own generated client configs.
248
286
 
249
287
  </details>
250
288
 
251
289
  ### 2. Add your Anthropic API key
252
290
 
253
- Pattern requires:
291
+ Skipped it above, or want to change it? Pattern requires:
254
292
 
255
293
  ```
256
294
  ANTHROPIC_API_KEY
@@ -265,40 +303,10 @@ Claude Pro or Max subscription does not include API usage.
265
303
 
266
304
  ### Connect Pattern to your MCP client
267
305
 
268
- Pattern is a standard MCP server, so it works with MCP-compatible
269
- clients.
270
-
271
- The server command is:
272
-
273
- ```
274
- npx pattern-mcp
275
- ```
276
-
277
- #### Automatic setup
278
-
279
- ```bash
280
- npx pattern-mcp init
281
- ```
282
-
283
- Detects which clients are installed and offers to connect each one:
284
-
285
- - **Claude Code** -- runs `claude mcp add` for you (asks whether to make
286
- Pattern available in every project or just this one); skips if already
287
- connected (`claude mcp list` already shows it).
288
- - **Claude Desktop** and **Cursor** -- merges a `pattern` entry into the
289
- client's own config file, showing the exact change before writing it
290
- and never touching any other server already configured there.
291
- - **Codex CLI** -- prints the config snippet to add by hand (Codex's
292
- config is TOML; this doesn't auto-edit it).
293
-
294
- Optionally pastes your `ANTHROPIC_API_KEY` into whichever configs you set
295
- up (visible in plain text as you type it, and in the files it writes) --
296
- press Enter to skip and add it yourself later instead. Run
297
- non-interactively with `--yes` (skips the API key prompt entirely,
298
- accepts every detected client).
299
-
300
- If you'd rather do it by hand, or `init` didn't detect your client, the
301
- per-client instructions below cover the same configs manually.
306
+ [Step 1](#1-install-and-connect) above (`npx pattern-mcp init`) does
307
+ this automatically for every client it detects -- the sections below
308
+ are the same configs done by hand, for a client `init` didn't detect,
309
+ or if you'd simply rather edit the config yourself.
302
310
 
303
311
  #### Claude Code
304
312
 
@@ -127,6 +127,28 @@ async function mergeServerConfig(label, path, apiKey, options) {
127
127
  writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf8");
128
128
  console.log(`Written. Restart ${label} to pick it up.`);
129
129
  }
130
+ function clientConfigHasPattern(path) {
131
+ if (!path)
132
+ return false;
133
+ const { config } = readJsonConfig(path);
134
+ return Boolean(config.mcpServers?.pattern);
135
+ }
136
+ // Best-effort, read-only check across every client Pattern knows how to
137
+ // detect a prior successful setup for -- used to decide whether to keep
138
+ // offering the connect wizard on a later bare run (see
139
+ // offerClientConnectSetupOnce below), instead of asking only once ever
140
+ // regardless of outcome. Codex is deliberately excluded: its config is
141
+ // TOML, which this project never parses or writes (see
142
+ // offerCodexInstructions), so there's no way to confirm a Codex-only
143
+ // setup from here. That means a Codex-only user keeps getting offered
144
+ // the wizard -- a false negative, which is the safe failure mode (asks
145
+ // again when already connected) rather than a false positive (goes quiet
146
+ // when it isn't).
147
+ export function isAnyClientConnected(root) {
148
+ return (claudeCodeAlreadyConnected() ||
149
+ clientConfigHasPattern(claudeDesktopConfigPath()) ||
150
+ clientConfigHasPattern(join(root, ".cursor", "mcp.json")));
151
+ }
130
152
  // Claude Desktop isn't shipped on Linux -- there's no config path to
131
153
  // even guess at there, so this target is simply not offered on that
132
154
  // platform rather than writing a file no client will ever read.
@@ -170,10 +192,21 @@ async function setupCursor(root, apiKey, options) {
170
192
  // existing config is worse than just telling them what to add. Detected
171
193
  // the same way as the other targets (evidence it's actually used), but
172
194
  // only ever prints instructions.
173
- function offerCodexInstructions() {
195
+ //
196
+ // Deliberately does NOT print a per-server `env = {...}` TOML snippet for
197
+ // the API key, unlike the command/args lines above -- checked directly
198
+ // (openai/codex#7521, open as of 2026-09): Codex's own maintainers hadn't
199
+ // settled which env-var syntax their TOML config actually supports at the
200
+ // time this was written. Printing a guessed snippet risked giving
201
+ // confidently wrong instructions, worse than the honest "it depends on
202
+ // your version, here's what works regardless" below. A plain shell
203
+ // export is the one method that works the same way across every Codex
204
+ // version and every other client here, since it never depends on a
205
+ // client-specific config format at all.
206
+ function offerCodexInstructions(apiKey) {
174
207
  if (!existsSync(join(homedir(), ".codex")))
175
208
  return;
176
- console.log([
209
+ const lines = [
177
210
  "\nCodex CLI detected (~/.codex exists). Pattern doesn't auto-write Codex's",
178
211
  "TOML config -- add this to ~/.codex/config.toml (or .codex/config.json for",
179
212
  "this project only):",
@@ -181,7 +214,15 @@ function offerCodexInstructions() {
181
214
  ' [mcp_servers.pattern]',
182
215
  ' command = "npx"',
183
216
  ' args = ["pattern-mcp"]',
184
- ].join("\n"));
217
+ "",
218
+ ];
219
+ if (apiKey) {
220
+ lines.push("You entered an API key above. Codex's own per-server TOML env syntax isn't", "reliably documented across versions, so the safest way to get it to Codex", "is exporting it in the shell you launch Codex from:", "", ` export ANTHROPIC_API_KEY="${apiKey}"`);
221
+ }
222
+ else {
223
+ lines.push("Codex also needs ANTHROPIC_API_KEY available in its own environment.", "Export it in the shell you launch Codex from:", "", " export ANTHROPIC_API_KEY=sk-ant-...");
224
+ }
225
+ console.log(lines.join("\n"));
185
226
  }
186
227
  async function promptApiKey(options) {
187
228
  if (options.yes)
@@ -211,7 +252,7 @@ export async function runConnect(root, options) {
211
252
  }
212
253
  if (existsSync(join(homedir(), ".codex"))) {
213
254
  anyDetected = true;
214
- offerCodexInstructions();
255
+ offerCodexInstructions(apiKey);
215
256
  }
216
257
  if (!anyDetected) {
217
258
  console.log("\nNo supported MCP client was detected on this machine automatically.\n" + connectInstructionsText());
@@ -225,36 +266,53 @@ export async function runConnect(root, options) {
225
266
  // Option 1/#1 from the activation-funnel discussion: piggybacks on the
226
267
  // same first-run moment as the telemetry and enforcement-boundary
227
268
  // notices (see telemetry.ts's printTelemetryNoticeOnce, which states the
228
- // stdin constraint first). Always prints a one-time, non-blocking
229
- // mention -- including when a real MCP client has spawned this as a
230
- // subprocess, where it's genuinely irrelevant but harmless, since the
231
- // notice is gated on a marker file the same as the others. Only offers
232
- // the actual interactive "set it up now?" prompt when stdin is a real
233
- // TTY, i.e. a human ran `npx pattern-mcp` bare in their own shell.
269
+ // stdin constraint first). Always prints the full notice once, ever --
270
+ // including when a real MCP client has spawned this as a subprocess,
271
+ // where it's genuinely irrelevant but harmless, since the notice is
272
+ // gated on a marker file the same as the others.
273
+ //
274
+ // The interactive "set it up now?" prompt is different: it used to be
275
+ // gated on that same one-time marker, so a human who ignored or missed
276
+ // it on the very first bare run never saw it again -- a permanent
277
+ // drop-off with no second chance, found while mapping the new-install
278
+ // journey (see project_pattern_activation_funnel memory). It now keeps
279
+ // reappearing on every bare TTY run -- a human running `npx pattern-mcp`
280
+ // in their own shell, never a real client's spawned subprocess -- for as
281
+ // long as isAnyClientConnected() can't confirm a real connection exists
282
+ // yet. This is the concrete fix for "don't rely on the user to figure
283
+ // out how to connect": Pattern keeps offering, not just once, until it
284
+ // can verify success, or until PATTERN_NO_CONNECT_NOTICE opts out.
234
285
  const CONNECT_NOTICE_PATH = process.env.PATTERN_CONNECT_NOTICE_PATH ?? join(homedir(), ".pattern", "connect_notice_shown");
235
286
  export async function offerClientConnectSetupOnce(root) {
236
287
  if (process.env.PATTERN_NO_CONNECT_NOTICE)
237
288
  return;
289
+ let noticeAlreadyShown = true;
238
290
  try {
239
291
  readFileSync(CONNECT_NOTICE_PATH, "utf8");
240
- return;
241
292
  }
242
293
  catch {
243
- // No marker yet -- fall through and show it.
244
- }
245
- console.error(["", "Pattern -- one-time setup notice (this will not print again)", connectInstructionsText(), ""].join("\n"));
246
- try {
247
- mkdirSync(dirname(CONNECT_NOTICE_PATH), { recursive: true });
248
- writeFileSync(CONNECT_NOTICE_PATH, new Date().toISOString(), "utf8");
294
+ noticeAlreadyShown = false;
249
295
  }
250
- catch {
251
- // Couldn't persist the marker -- worst case this prints again next
252
- // run. Never blocks startup over it, same as the other notices.
296
+ if (!noticeAlreadyShown) {
297
+ console.error(["", "Pattern -- one-time setup notice (this will not print again)", connectInstructionsText(), ""].join("\n"));
298
+ try {
299
+ mkdirSync(dirname(CONNECT_NOTICE_PATH), { recursive: true });
300
+ writeFileSync(CONNECT_NOTICE_PATH, new Date().toISOString(), "utf8");
301
+ }
302
+ catch {
303
+ // Couldn't persist the marker -- worst case this prints again next
304
+ // run. Never blocks startup over it, same as the other notices.
305
+ }
253
306
  }
254
307
  if (!process.stdin.isTTY)
255
308
  return;
309
+ if (isAnyClientConnected(root))
310
+ return;
256
311
  try {
257
- const setUpNow = await confirm("Run the connect wizard now?", { yes: false }, true);
312
+ const question = noticeAlreadyShown
313
+ ? "No MCP client is connected to Pattern yet -- run the connect wizard now?"
314
+ : "Run the connect wizard now?";
315
+ const setUpNow = await confirm(question, { yes: false }, true);
258
316
  if (setUpNow) {
259
317
  await runConnect(root, { yes: false }); // closes the shared readline itself
260
318
  }
package/dist/index.js CHANGED
@@ -111,6 +111,19 @@ function warnIfAnthropicKeyLooksWrong() {
111
111
  "\"sk-ant-\"). If a tool call fails with a 401, check this value first.");
112
112
  }
113
113
  }
114
+ // Thrown by recommend_component/extract_requirements when they actually
115
+ // need the API and no key is present -- this is what the calling agent
116
+ // sees as the tool's error result, so unlike warnIfAnthropicKeyLooksWrong's
117
+ // stderr line (invisible in most real clients -- see
118
+ // project_pattern_activation_funnel memory), this is the message a real
119
+ // user is actually likely to see. Gives two concrete fixes rather than
120
+ // just naming the problem: re-running `init` (which already offers to
121
+ // write the key into every client it detects), or exporting it directly --
122
+ // the one method that works the same way across every client, since it
123
+ // doesn't depend on any client-specific config format.
124
+ const MISSING_API_KEY_MESSAGE = "Pattern: ANTHROPIC_API_KEY is not set, so this call can't reach the Anthropic API. Fix it one of two ways: " +
125
+ "re-run `npx pattern-mcp init` to add it to your MCP client's config, or export it directly -- " +
126
+ "`export ANTHROPIC_API_KEY=sk-ant-...` in the shell your client launches Pattern from, then restart the client.";
114
127
  // Configurable so Sonnet vs. Haiku can be A/B tested without a code change.
115
128
  // Defaults to Sonnet 5. Try MODEL=claude-haiku-4-5-20251001 to test the
116
129
  // cheaper tier -- re-run the 5 validated test cases from the product brief
@@ -1173,14 +1186,15 @@ export function estimateExtractionConfidence(componentNeed) {
1173
1186
  return "high";
1174
1187
  }
1175
1188
  async function runSinglePass(input) {
1176
- if (!ANTHROPIC_API_KEY) {
1177
- throw new Error("ANTHROPIC_API_KEY is not set. Export it in the environment running this MCP server.");
1178
- }
1179
1189
  const passStartMs = Date.now();
1180
1190
  const checklistSource = input.checklist && input.checklist.length > 0 ? "provided" : "extracted";
1181
1191
  // Fast path: skip-list check happens locally too, so trivial primitives
1182
1192
  // never spend a real API call. The system prompt also enforces this, but
1183
- // checking here avoids the round-trip entirely for the common case.
1193
+ // checking here avoids the round-trip entirely for the common case. The
1194
+ // ANTHROPIC_API_KEY check used to run before this, unconditionally --
1195
+ // meaning a keyless install couldn't get even this free, local path.
1196
+ // Moved below the skip-list return so a missing/invalid key only ever
1197
+ // blocks the cases that actually need the API.
1184
1198
  if (isSkipListMatch(input.component_need)) {
1185
1199
  const skipListElapsedMs = Math.max(1, Date.now() - passStartMs);
1186
1200
  return {
@@ -1213,6 +1227,9 @@ async function runSinglePass(input) {
1213
1227
  },
1214
1228
  };
1215
1229
  }
1230
+ if (!ANTHROPIC_API_KEY) {
1231
+ throw new Error(MISSING_API_KEY_MESSAGE);
1232
+ }
1216
1233
  // Coverage still computes fresh below regardless of what this finds --
1217
1234
  // memory (MEMORY_PATH/record_component_decision) only ever adds context
1218
1235
  // to the user message, it never short-circuits search/scoring or gets
@@ -1526,9 +1543,6 @@ existing_stack: ${input.existing_stack ?? "(not specified)"}${checklistBlock}${p
1526
1543
  // for the same reason (trivial primitives shouldn't cost an API call here
1527
1544
  // either).
1528
1545
  async function runExtraction(input) {
1529
- if (!ANTHROPIC_API_KEY) {
1530
- throw new Error("ANTHROPIC_API_KEY is not set. Export it in the environment running this MCP server.");
1531
- }
1532
1546
  const startMs = Date.now();
1533
1547
  if (isSkipListMatch(input.component_need)) {
1534
1548
  const elapsedMs = Math.max(1, Date.now() - startMs);
@@ -1546,6 +1560,9 @@ async function runExtraction(input) {
1546
1560
  },
1547
1561
  };
1548
1562
  }
1563
+ if (!ANTHROPIC_API_KEY) {
1564
+ throw new Error(MISSING_API_KEY_MESSAGE);
1565
+ }
1549
1566
  const userMessage = `component_need: ${input.component_need}\ndomain: ${input.domain}`;
1550
1567
  const data = await streamAnthropicMessage({
1551
1568
  model: MODEL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pattern-mcp",
3
- "version": "0.14.1",
3
+ "version": "0.16.0",
4
4
  "description": "MCP server that turns your design guidance into a checkable process -- evaluates UI components from external libraries (shadcn/ui, 21st.dev, ReUI) or your own registered design system against a requirements checklist, then tells the agent whether to reuse an existing component or build one from a concrete design reference.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",