pattern-mcp 0.8.0 → 0.9.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
@@ -1644,74 +1644,96 @@ recommendations.
1644
1644
  Local project memory and the local call log are stored on the machine
1645
1645
  running Pattern. They are not sent anywhere by Pattern itself.
1646
1646
 
1647
- The one exception is opt-in telemetry, off by default -- see
1647
+ The one exception is telemetry, on by default -- see
1648
1648
  [Telemetry](#telemetry) below for exactly what it sends and how to turn
1649
- it on or off.
1649
+ it off.
1650
1650
 
1651
1651
  Review [SECURITY.md](./SECURITY.md) before putting sensitive information
1652
1652
  into fields such as `component_need`, `domain`, or project IDs.
1653
1653
 
1654
1654
  ## Telemetry
1655
1655
 
1656
- Off by default. Nothing is sent anywhere for telemetry purposes unless
1657
- you explicitly set:
1656
+ On by default, as of v0.9.0. To turn it off:
1658
1657
 
1659
1658
  ```
1660
- PATTERN_TELEMETRY=1
1659
+ PATTERN_TELEMETRY=0
1661
1660
  ```
1662
1661
 
1662
+ (`false` and `no` also work, case-insensitively. Before v0.9.0 this was
1663
+ opt-in -- `PATTERN_TELEMETRY=1` to turn on. In practice that meant almost
1664
+ no signal: installs happened, but almost nobody set the env var. This
1665
+ release flips the default and adds a second, standard event stream, both
1666
+ described below, while keeping the same "never sent" guarantees.)
1667
+
1663
1668
  **The one-time notice.** The first time you run this version of Pattern
1664
- -- whether it's a brand-new install or an upgrade from a version before
1665
- telemetry existed -- it prints a short notice to stderr explaining all of
1666
- this and how to opt in. It prints exactly once, ever (tracked by a marker
1667
- file at `~/.pattern/telemetry_notice_shown`), then never again, regardless
1668
- of whether you act on it. There's no interactive y/n prompt: Pattern's
1669
- stdin is the MCP JSON-RPC channel the client uses to talk to it, so
1670
- blocking on stdin for a keypress would fight the protocol handshake
1671
- instead of showing a dialog -- a stderr notice is the safe equivalent for
1672
- a stdio MCP server.
1673
-
1674
- **Why it exists.** Two things about real usage can't be answered from
1669
+ -- whether it's a brand-new install or an upgrade from an older version
1670
+ -- it prints a short notice to stderr explaining all of this and how to
1671
+ opt out. It prints exactly once, ever (tracked by a marker file at
1672
+ `~/.pattern/telemetry_notice_shown`), then never again, regardless of
1673
+ whether you act on it. There's no interactive y/n prompt: Pattern's stdin
1674
+ is the MCP JSON-RPC channel the client uses to talk to it, so blocking on
1675
+ stdin for a keypress would fight the protocol handshake instead of
1676
+ showing a dialog -- a stderr notice is the safe equivalent for a stdio
1677
+ MCP server.
1678
+
1679
+ **Why it exists.** Three things about real usage can't be answered from
1675
1680
  this repo alone: whether people actually come back and use Pattern on a
1676
- second or third project on their own, and how often a BYO Anthropic key
1681
+ second or third project on their own; how often a BYO Anthropic key
1677
1682
  actually hits a rate limit or runs out of credit in real sessions, not
1678
1683
  just the one time that happened during manual testing (see
1679
- [Known limitations](#known-limitations)). Telemetry answers both without
1680
- requiring anyone to fill out a survey.
1681
-
1682
- **What gets sent, when enabled:**
1683
-
1684
- - An anonymous, randomly generated install ID -- a UUID created once and
1685
- stored at `~/.pattern/install_id` (overridable via
1686
- `PATTERN_INSTALL_ID_PATH`), never derived from your machine, username,
1687
- or any other identifying information. This is the only thing that ties
1688
- two events together as "the same install."
1689
- - A one-way SHA-256 hash of `project_id`, truncated to 16 hex characters
1690
- -- never the raw `project_id` string. The hash lets Pattern count how
1691
- many *distinct* projects one install has used, without ever seeing what
1692
- those projects are named.
1693
- - On every `recommend_component` call that reaches the API or the ledger
1694
- cache-hit shortcut: `verdict`, `confidence`, `reason`,
1695
- `ensemble_triggered`, `estimated_cost_usd`, and `served_from_ledger` --
1696
- the same distilled shape already written to the
1697
- [local call log](#local-call-log), not new information.
1698
- - On a failed Anthropic API call specifically: the HTTP status code and a
1699
- coarse classification (`rate_limit`, `insufficient_credit`, or `other`)
1700
- -- never the request or response body.
1684
+ [Known limitations](#known-limitations)); and, previously, whether
1685
+ `recommend_component` gets called at all after install -- opt-in
1686
+ telemetry couldn't answer that last one because the people who'd opt in
1687
+ are a biased, tiny sample of everyone who installs.
1688
+
1689
+ **What gets sent, when on -- two event streams, same distinct ID:**
1690
+
1691
+ 1. Pattern's own events, sent directly to PostHog:
1692
+ - An anonymous, randomly generated install ID -- a UUID created once
1693
+ and stored at `~/.pattern/install_id` (overridable via
1694
+ `PATTERN_INSTALL_ID_PATH`), never derived from your machine,
1695
+ username, or any other identifying information. This is the distinct
1696
+ ID both event streams use, so an install counts as the same "user"
1697
+ in either.
1698
+ - A one-way SHA-256 hash of `project_id`, truncated to 16 hex characters
1699
+ -- never the raw `project_id` string. The hash lets Pattern count how
1700
+ many *distinct* projects one install has used, without ever seeing what
1701
+ those projects are named.
1702
+ - On every `recommend_component` call that reaches the API or the ledger
1703
+ cache-hit shortcut: `verdict`, `confidence`, `reason`,
1704
+ `ensemble_triggered`, `estimated_cost_usd`, and `served_from_ledger` --
1705
+ the same distilled shape already written to the
1706
+ [local call log](#local-call-log), not new information.
1707
+ - On a failed Anthropic API call specifically: the HTTP status code and a
1708
+ coarse classification (`rate_limit`, `insufficient_credit`, or `other`)
1709
+ -- never the request or response body.
1710
+ 2. Standard MCP tool-call analytics, via
1711
+ [`@posthog/mcp`](https://posthog.com/docs/mcp-analytics): which tool
1712
+ was called, call duration, and success/failure, so unique installs and
1713
+ call counts per tool (including `recommend_component`) are visible the
1714
+ same way any other MCP server's usage would be. This SDK's defaults
1715
+ would otherwise also capture full tool call arguments, full response
1716
+ text, and the raw thrown error message on a failure -- Pattern
1717
+ explicitly strips all of that (`$mcp_parameters`, `$mcp_response`,
1718
+ `$mcp_intent`, `$mcp_error_message`) before anything leaves the process,
1719
+ via its `beforeSend` hook. What's left is the same shape as stream 1
1720
+ above: counts, timing, and coarse success/failure, never content.
1701
1721
 
1702
1722
  **What never gets sent, telemetry on or off:** `component_need`,
1703
1723
  `domain`, `framework`, `existing_stack`, `requirements_checked` evidence,
1704
- the raw `project_id`, or your Anthropic API key.
1724
+ the raw `project_id`, a failed API call's request or response body, or
1725
+ your Anthropic API key. This is a claim about both event streams above,
1726
+ not just the first one.
1705
1727
 
1706
- **Where it goes.** Events go to Pattern's PostHog project via its public,
1707
- write-only project key (safe to ship in source -- it can send events, it
1708
- cannot read data back). Set `PATTERN_POSTHOG_KEY` /
1728
+ **Where it goes.** Both streams go to Pattern's PostHog project via its
1729
+ public, write-only project key (safe to ship in source -- it can send
1730
+ events, it cannot read data back). Set `PATTERN_POSTHOG_KEY` /
1709
1731
  `PATTERN_POSTHOG_HOST` to point at a different project, e.g. for
1710
1732
  self-hosting.
1711
1733
 
1712
- **Turning it off** is the default -- just don't set `PATTERN_TELEMETRY`.
1713
- If you'd previously enabled it, unset the variable (or set it to `0`) to
1714
- go back to fully local.
1734
+ **Turning it off:** `PATTERN_TELEMETRY=0` disables both streams entirely
1735
+ -- when it's off, `instrument()` (stream 2) is never even called, and
1736
+ stream 1's `capture()` calls are no-ops.
1715
1737
 
1716
1738
  ## Cost
1717
1739
 
package/dist/index.js CHANGED
@@ -30,12 +30,13 @@
30
30
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
31
31
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
32
32
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
33
+ import { instrument } from "@posthog/mcp";
33
34
  import { execFileSync } from "node:child_process";
34
35
  import { createHash, randomUUID } from "node:crypto";
35
36
  import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
36
37
  import { homedir } from "node:os";
37
38
  import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
38
- import { captureApiError, captureRecommendation, printTelemetryNoticeOnce, shutdownTelemetry, } from "./telemetry.js";
39
+ import { captureApiError, captureRecommendation, getClient as getPostHogClient, installId, printTelemetryNoticeOnce, shutdownTelemetry, TELEMETRY_ENABLED, } from "./telemetry.js";
39
40
  export const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
40
41
  // Configurable so Sonnet vs. Haiku can be A/B tested without a code change.
41
42
  // Defaults to Sonnet 5. Try MODEL=claude-haiku-4-5-20251001 to test the
@@ -3338,6 +3339,45 @@ export function extractJson(text) {
3338
3339
  return text.slice(start, end + 1);
3339
3340
  }
3340
3341
  const server = new Server({ name: "pattern-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
3342
+ // Standard MCP tool-call analytics (tool name, duration, success/failure,
3343
+ // unique installs/sessions) via PostHog's own MCP SDK -- separate from
3344
+ // this file's captureRecommendation()/captureApiError() calls, which carry
3345
+ // the distilled verdict shape. Both are gated by the same TELEMETRY_ENABLED
3346
+ // flag (see telemetry.ts) and share installId() as the distinct_id so an
3347
+ // install counts as the same "user" across both event streams.
3348
+ //
3349
+ // context: false skips injecting an extra `context` argument into every
3350
+ // tool's schema. beforeSend strips $mcp_parameters/$mcp_response/$mcp_intent/
3351
+ // $mcp_error_message before anything is sent -- by default this SDK
3352
+ // captures full tool call arguments, response text, AND the raw thrown
3353
+ // error message (which for a failed Anthropic call is the full HTTP
3354
+ // response body -- verified live: a 401 test call shipped the entire
3355
+ // JSON error body in $mcp_error_message before this strip was added).
3356
+ // SECURITY.md and README.md both promise component_need/domain/framework/
3357
+ // existing_stack are never sent and that a failed API call never sends its
3358
+ // response body; this is what keeps both true for this event stream too.
3359
+ // enableExceptionAutocapture is off for the same reason -- it would also
3360
+ // fan the same raw message out to a separate $exception event.
3361
+ if (TELEMETRY_ENABLED) {
3362
+ const posthogClient = getPostHogClient();
3363
+ if (posthogClient) {
3364
+ instrument(server, posthogClient, {
3365
+ context: false,
3366
+ enableExceptionAutocapture: false,
3367
+ identify: async () => ({ distinctId: installId() }),
3368
+ beforeSend: (event) => {
3369
+ if (event.properties) {
3370
+ delete event.properties["$mcp_parameters"];
3371
+ delete event.properties["$mcp_response"];
3372
+ delete event.properties["$mcp_intent"];
3373
+ delete event.properties["$mcp_intent_source"];
3374
+ delete event.properties["$mcp_error_message"];
3375
+ }
3376
+ return event;
3377
+ },
3378
+ });
3379
+ }
3380
+ }
3341
3381
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
3342
3382
  tools: [
3343
3383
  {
package/dist/telemetry.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Opt-in, anonymous product telemetry.
2
+ * Anonymous product telemetry.
3
3
  *
4
- * Off by default. Enabling it (PATTERN_TELEMETRY=1) answers two questions
5
- * the product can't answer any other way without asking users directly:
4
+ * On by default. Disabling it (PATTERN_TELEMETRY=0) turns off both halves
5
+ * described below. It answers two questions the product can't answer any
6
+ * other way without asking users directly:
6
7
  *
7
8
  * - Do people come back and use Pattern on a second or third project on
8
9
  * their own, unprompted? (tracked via distinct project hashes seen per
@@ -11,16 +12,27 @@
11
12
  * limited in real sessions, not just the one time it happened during
12
13
  * manual testing? (tracked via captureApiError)
13
14
  *
14
- * What gets sent, when enabled: an anonymous, randomly generated install
15
- * ID (see installId() below); a one-way SHA-256 hash of project_id,
16
- * truncated to 16 hex chars -- never the raw project_id string; the verdict
17
- * shape already written to the local call log (verdict, confidence,
18
- * ensemble_triggered, estimated cost); and, on a failed Anthropic API call,
19
- * only the HTTP status and a coarse error classification (rate_limit /
20
- * insufficient_credit / other) -- never the request or response body.
15
+ * There are two halves to what ships, both gated by the same flag:
16
+ *
17
+ * 1. This file's own capture() calls: an anonymous, randomly generated
18
+ * install ID (see installId() below); a one-way SHA-256 hash of
19
+ * project_id, truncated to 16 hex chars -- never the raw project_id
20
+ * string; the verdict shape already written to the local call log
21
+ * (verdict, confidence, ensemble_triggered, estimated cost); and, on a
22
+ * failed Anthropic API call, only the HTTP status and a coarse error
23
+ * classification (rate_limit / insufficient_credit / other) -- never
24
+ * the request or response body.
25
+ * 2. `@posthog/mcp`'s standard MCP instrumentation, wired up in index.ts:
26
+ * tool name, call duration, error/success, and the same anonymous
27
+ * install ID as the distinct_id (via its `identify` option), so both
28
+ * halves count the same "user." Its `beforeSend` hook strips
29
+ * `$mcp_parameters`, `$mcp_response`, and `$mcp_intent`/
30
+ * `$mcp_intent_source` before anything leaves the process -- those
31
+ * would otherwise carry tool call arguments and output text.
32
+ *
21
33
  * component_need text, requirements_checked evidence, and the API key
22
- * itself are never sent. See SECURITY.md and README.md for the full
23
- * disclosure and the exact opt-in instructions.
34
+ * itself are never sent, by either half. See SECURITY.md and README.md for
35
+ * the full disclosure and how to opt out.
24
36
  *
25
37
  * Reuses Pattern's existing PostHog project (the same one the marketing
26
38
  * site sends browser events to) with its public, write-only project key --
@@ -34,12 +46,13 @@ import { createHash, randomUUID } from "node:crypto";
34
46
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
35
47
  import { homedir } from "node:os";
36
48
  import { dirname, join } from "node:path";
37
- // Opt-in, not opt-out -- deliberate, given who this tool is for. See
38
- // README's telemetry section: a local-first tool aimed at developers who
39
- // notice and care about silent tracking is exactly the audience §06 of the
40
- // product brief already flags as sensitive to "no paper trail" trust gaps.
41
- // Any of "1", "true", "yes" (case-insensitive) turns it on.
42
- const TELEMETRY_ENABLED = /^(1|true|yes)$/i.test(process.env.PATTERN_TELEMETRY ?? "");
49
+ // Opt-out, not opt-in. Previously opt-in; changed because opt-in meant
50
+ // near-zero real signal in practice (installs happened, but almost no one
51
+ // ever set the env var) -- see the one-time notice below, which is the
52
+ // disclosure mechanism that replaces an interactive consent prompt for a
53
+ // stdio MCP server. Any of "0", "false", "no" (case-insensitive) turns it
54
+ // off; anything else, including unset, leaves it on.
55
+ export const TELEMETRY_ENABLED = !/^(0|false|no)$/i.test(process.env.PATTERN_TELEMETRY ?? "");
43
56
  // One-time startup notice, printed to stderr -- the closest thing to an
44
57
  // opt-in prompt an MCP stdio server can safely show. stdin is the JSON-RPC
45
58
  // channel the client uses to talk to this process; blocking on it to read
@@ -60,23 +73,24 @@ export function printTelemetryNoticeOnce() {
60
73
  // No marker yet -- fall through and show it.
61
74
  }
62
75
  const status = TELEMETRY_ENABLED
63
- ? "ON, because PATTERN_TELEMETRY is set"
64
- : "OFF (the default -- nothing is sent unless you opt in)";
76
+ ? "ON (the default)"
77
+ : "OFF, because PATTERN_TELEMETRY is set to 0/false/no";
65
78
  console.error([
66
79
  "",
67
80
  "Pattern -- one-time telemetry notice (this will not print again)",
68
81
  `Anonymous usage telemetry is currently ${status}.`,
69
82
  "",
70
- "When enabled, Pattern sends an anonymous per-install ID, a one-way",
71
- "hash of project_id (never the raw string), and the same verdict",
72
- "summary already written to ~/.pattern/calls.log (verdict,",
73
- "confidence, reason, estimated cost). component_need, domain,",
74
- "framework, existing_stack, and your API key are never sent.",
83
+ "When on, Pattern sends an anonymous per-install ID, a one-way hash",
84
+ "of project_id (never the raw string), the same verdict summary",
85
+ "already written to ~/.pattern/calls.log (verdict, confidence,",
86
+ "reason, estimated cost), and standard MCP tool-call analytics (tool",
87
+ "name, duration, success/failure) via PostHog's MCP SDK. Tool call",
88
+ "arguments and responses are stripped before sending -- component_need,",
89
+ "domain, framework, existing_stack, and your API key are never sent.",
75
90
  "Full field list: https://github.com/donaldrichard19-LVD/pattern-mcp#telemetry",
76
91
  "",
77
- "To help improve Pattern by sharing anonymous usage data, opt in:",
78
- " PATTERN_TELEMETRY=1",
79
- "Already on and want it off instead? Unset PATTERN_TELEMETRY (or set it to 0).",
92
+ "To opt out: PATTERN_TELEMETRY=0",
93
+ "Already off and want it back on? Unset PATTERN_TELEMETRY (or set it to 1).",
80
94
  "",
81
95
  ].join("\n"));
82
96
  try {
@@ -109,7 +123,7 @@ let cachedInstallId;
109
123
  // would look like a brand-new anonymous user. Never derived from anything
110
124
  // that identifies a person or machine (no hostname, no MAC, no username) --
111
125
  // purely a random UUID with no way to reverse it to an identity.
112
- function installId() {
126
+ export function installId() {
113
127
  if (cachedInstallId)
114
128
  return cachedInstallId;
115
129
  try {
@@ -155,7 +169,10 @@ export function classifyApiError(message) {
155
169
  return { type: "other", status };
156
170
  }
157
171
  let client;
158
- function getClient() {
172
+ // Exported so index.ts's @posthog/mcp instrument() call shares this same
173
+ // client/project instead of standing up a second PostHog connection --
174
+ // same reason both halves share installId() as their distinct_id.
175
+ export function getClient() {
159
176
  if (!TELEMETRY_ENABLED || !POSTHOG_KEY)
160
177
  return undefined;
161
178
  if (!client) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pattern-mcp",
3
- "version": "0.8.0",
4
- "description": "MCP tool that judges whether a UI component need should be met with an existing shadcn/ui, 21st.dev, or ReUI component or requires a custom build, using field/requirement coverage scored against real component code.",
3
+ "version": "0.9.0",
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",
7
7
  "bin": {
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@modelcontextprotocol/sdk": "^1.0.0",
40
+ "@posthog/mcp": "^0.13.0",
40
41
  "posthog-node": "^5.51.4"
41
42
  },
42
43
  "devDependencies": {