@bridge-ds/mcp 0.0.0-stage → 0.3.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
@@ -1,3 +1,131 @@
1
- # Temporary Holding Version
1
+ # @bridge-ds/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An MCP server that connects your own Claude (Claude Code, Claude Desktop or any MCP client) to your own
4
+ design system repo. Claude can read your DTCG tokens, components and docs, run your contrast gate, and
5
+ propose token changes as reviewable branches and pull requests. It never writes to `main`.
6
+
7
+ Free and open source (MIT). Local, no backend, no account: it reads files on your disk, or a GitHub repo
8
+ with your own token.
9
+
10
+ > On npm: `npx -y @bridge-ds/mcp` runs it, nothing to build. The local path form (`node packages/mcp/dist/index.js`)
11
+ > is for working on the server itself.
12
+
13
+ ## Run it
14
+
15
+ ```sh
16
+ npm run mcp:build # builds packages/mcp and packages/cli
17
+ node packages/mcp/dist/index.js --root . # local repo (default: current directory)
18
+ node packages/mcp/dist/index.js --repo owner/name # GitHub repo, read-only
19
+ GITHUB_TOKEN=... node packages/mcp/dist/index.js --repo owner/name # GitHub repo, can open PRs
20
+ ```
21
+
22
+ Connect it to Claude Code:
23
+
24
+ ```sh
25
+ claude mcp add bridge -- npx -y @bridge-ds/mcp --root /abs/path/to/your-ds
26
+ # or a GitHub repo (pull requests need a token):
27
+ claude mcp add bridge -s user -e GITHUB_TOKEN=your-github-token -- npx -y @bridge-ds/mcp --repo owner/name
28
+ ```
29
+
30
+ Or commit a project `.mcp.json` (this repo has one), or run `bridge init`. Claude Desktop, other clients
31
+ and the full guide: [docs/AI.md](../../docs/AI.md).
32
+
33
+ ## Modes
34
+
35
+ | Mode | Start with | Reads | Writes |
36
+ | --- | --- | --- | --- |
37
+ | Local | `--root <dir>` (default cwd) | working tree | a commit on a new local `bridge/*` branch, via git plumbing: your checkout, index and working tree are untouched. Never pushes. |
38
+ | Remote, no token | `--repo owner/name` | GitHub (raw files, one API call for the tree) | nothing |
39
+ | Remote, token | `--repo owner/name` + `GITHUB_TOKEN` | GitHub API | new `bridge/*` branch, commit, draft PR labelled `source: mcp` and `tier: N` |
40
+
41
+ ## Tools
42
+
43
+ | Tool | What it does |
44
+ | --- | --- |
45
+ | `bridge_get_config` | Source, write access, tiers, modes, the effective `bridge.config.json` |
46
+ | `bridge_list_tokens` | Tokens with resolved values, filtered by tier, `$type`, path prefix, mode |
47
+ | `bridge_get_token` | One token: value per mode with alias chain, defining files, tokens aliasing it, components using it |
48
+ | `bridge_search_tokens` | By words, CSS variable or value (exact and near colors) |
49
+ | `bridge_check_contrast` | The contrast gate in every brand x theme; with `edits`, before/after and new failures |
50
+ | `bridge_validate_tokens` | DTCG shapes, names, broken/circular aliases, type mismatches, light/dark parity |
51
+ | `bridge_list_components` | Components with family and summary |
52
+ | `bridge_get_component` | Props parsed from TypeScript, docs.json usage guidance, CSS variables used |
53
+ | `bridge_draft_docs` | docs.json pre-fill from the code (props, tokens, anatomy, contrast, changelog line, missing keys) and a prompt to write the prose; writes nothing |
54
+ | `bridge_get_design_language` | Sections of the design language doc |
55
+ | `bridge_get_guidelines` | AGENTS.md rules, risk tiers, what Bridge enforces |
56
+ | `bridge_propose_token_change` | Edits, validation, contrast, tier, changeset, PR body; branch (local) or PR (remote) |
57
+ | `bridge_list_brands` | Brands: slug, name, default, primary action color, theme override files |
58
+ | `bridge_plan_brand` | Dry run of a new brand: generated ramps, contrast for every theme, files, tier 3, PR body |
59
+ | `bridge_propose_brand` | The new brand as a local `bridge/brand-<slug>` branch or a draft PR; blocked when a pair fails |
60
+ | `bridge_list_reviews` | Open PRs (Bridge ones flagged) and local `bridge/*` branches |
61
+ | `bridge_get_review` | Diff table, contrast before/after, impact, tier check, CI status, approvals |
62
+ | `bridge_review_pr` | Comment, approve or request changes (token required) |
63
+ | `bridge_scaffold_component` | New component folder per the repo checklist |
64
+ | `bridge_visual_impact` | Before/after screenshots of the impacted stories and admin screens, pixel diff, manifest (local, needs Chrome; `plan` for the selection only) |
65
+ | `bridge_get_history` | Token history: versions, merged PRs and commits, who, when, why, tier |
66
+ | `bridge_get_token_history` | Every change of one token, before and after per mode |
67
+ | `bridge_plan_revert` | Dry run of a revert (or a one-token restore) against the current base, conflicts shown |
68
+ | `bridge_propose_revert` | The revert as a local bridge/* branch or a draft PR labelled revert |
69
+ | `bridge_audit` | Adoption in a product folder or file (local): token and component adoption, hardcoded values with the closest token, foreign components with the DS one to use, top files and fixes |
70
+ | `bridge_list_requests` | Component change requests (issues labeled `component-change`) with status and linked pull requests |
71
+ | `bridge_get_request` | One request with the component's files and a brief to implement it in a pull request that says "Closes #n" |
72
+ | `bridge_create_request` | File a request as an issue labeled `component-change` (token required; `dryRun` previews; `exploration` targets an exploration branch) |
73
+ | `bridge_start_exploration` | Start an exploration: `explore/<name>` branch and a draft pull request labeled `exploration`, previews and a snapshot package, never merged as is |
74
+ | `bridge_list_explorations` | Explorations with status, previews, the snapshot install command and linked requests |
75
+
76
+ Resources: `bridge://tokens/resolved`, `bridge://design-language`, `bridge://guidelines`,
77
+ `bridge://components/{name}`. Prompts: `review-token-pr`, `add-component`, `audit-ui-for-tokens`,
78
+ `migrate-file-to-ds`.
79
+
80
+ ## Configuration
81
+
82
+ `bridge.config.json` at the repo root says where tokens live, how they layer per mode, and what Bridge may
83
+ write. It is the same file the Figma plugin and the docs site read, with one schema:
84
+ [`packages/core/bridge.config.schema.json`](../core/bridge.config.schema.json). Without one, Bridge detects a
85
+ config (token directory, tiers by folder name, light/dark and density files as modes); `bridge init` writes the
86
+ detected one so you can pin and edit it.
87
+
88
+ ```jsonc
89
+ {
90
+ "$schema": "./packages/core/bridge.config.schema.json",
91
+ "repo": { "owner": "acme", "name": "design-system", "defaultBranch": "main" },
92
+ "tokens": {
93
+ "root": "packages/tokens/src",
94
+ "include": ["**/*.tokens.json"],
95
+ "tiers": { "primitive": ["primitives"], "semantic": ["semantic"], "component": ["components"] },
96
+ // lowest first; {mode} placeholders pick the file per mode value
97
+ "layers": [
98
+ { "files": "primitives/*.tokens.json" },
99
+ { "files": "brands/{brand}.tokens.json" },
100
+ { "files": "semantic/{theme}.tokens.json" },
101
+ { "files": "brands/{brand}.{theme}.tokens.json", "optional": true },
102
+ { "files": "components/*.tokens.json" }
103
+ ]
104
+ },
105
+ "modes": { "theme": { "values": ["light", "dark"], "default": "light", "parity": true } },
106
+ "contrast": { "path": "packages/tokens/src/contrast.checks.json" },
107
+ "components": { "dir": "packages/react/src/components" },
108
+ "changesets": { "dir": ".changeset", "package": "@acme/tokens" },
109
+ "governance": {
110
+ "protectedBranches": ["main", "master"],
111
+ "branchPrefix": "bridge/",
112
+ "labels": ["source: mcp"],
113
+ "owners": { "design": ["designer-login"], "tech": ["engineer-login"] }
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## Develop
119
+
120
+ ```sh
121
+ npm run mcp:test # builds both packages, runs vitest (real repo tokens, a fixture repo, fake GitHub, stdio smoke test)
122
+ ```
123
+
124
+ All design-system logic comes from `@bridge-ds/core`, through `src/core-adapter.ts`: layered token sets,
125
+ validation, contrast, minimal-diff edits, diffs, the PR body, changesets, the GitHub client, the local git
126
+ writer (`@bridge-ds/core/node`) and the risk classifier the Figma plugin and the docs site use too. `npm run
127
+ mcp:build` bundles the server and the CLI with esbuild, compiling `@bridge-ds/core` in.
128
+
129
+ ## License
130
+
131
+ MIT
@@ -0,0 +1,15 @@
1
+ import { type TokenSet } from './core-adapter.js';
2
+ export interface Finding {
3
+ line: number;
4
+ text: string;
5
+ kind: 'color' | 'length' | 'duration' | 'z-index' | 'shadow' | 'easing' | 'font-weight';
6
+ property?: string;
7
+ suggestions: {
8
+ cssVar: string;
9
+ path: string;
10
+ value: string;
11
+ tier?: string;
12
+ exact: boolean;
13
+ }[];
14
+ }
15
+ export declare function auditSnippet(set: TokenSet, code: string, max?: number): Finding[];
@@ -0,0 +1,263 @@
1
+ import { type BrandRadiusInput } from '@bridge-ds/core';
2
+ import type { BridgeContext } from './context.js';
3
+ export interface BrandArgs {
4
+ slug: string;
5
+ name?: string;
6
+ from?: string;
7
+ /** Hex ("#1F5F3A") or OKLCH hue in degrees. */
8
+ primary?: string | number;
9
+ neutral?: string | number;
10
+ accent?: string | number;
11
+ link?: string | number;
12
+ /** A preset id (soft, balanced, crisp) or px per role. */
13
+ radius?: string | BrandRadiusInput;
14
+ fonts?: {
15
+ heading?: string;
16
+ text?: string;
17
+ };
18
+ notes?: string;
19
+ }
20
+ /** Radius from a preset id, "6,12,20,20" (inset, control, container, overlay) or an object. */
21
+ export declare function parseRadius(r: BrandArgs['radius']): BrandRadiusInput | undefined;
22
+ export declare function listBrands(ctx: BridgeContext): Promise<{
23
+ brands: import("@bridge-ds/core").BrandSummary[];
24
+ hint: string;
25
+ }>;
26
+ /** Dry run: the brand file, ramps, contrast for every theme, tier and PR body. Writes nothing. */
27
+ export declare function planBrand(ctx: BridgeContext, a: BrandArgs): Promise<{
28
+ ok: boolean;
29
+ slug: string;
30
+ name: string;
31
+ from: string;
32
+ title: string;
33
+ tier: import("@bridge-ds/core").RiskTier;
34
+ bump: "minor";
35
+ label: string;
36
+ reasons: string[];
37
+ files: {
38
+ path: string;
39
+ action: "create" | "modify";
40
+ }[];
41
+ actionStep: number;
42
+ ramps: {
43
+ [k: string]: {
44
+ [k: string]: string;
45
+ };
46
+ };
47
+ contrast: {
48
+ checks: number;
49
+ passing: number;
50
+ themes: string[];
51
+ line: string;
52
+ failing: {
53
+ pair: string;
54
+ modes: string;
55
+ ratio: number | null;
56
+ min: number;
57
+ }[];
58
+ };
59
+ issues: import("@bridge-ds/core").BrandIssue[];
60
+ prBody: string;
61
+ status: string;
62
+ }>;
63
+ export declare function proposeBrand(ctx: BridgeContext, a: BrandArgs & {
64
+ dryRun?: boolean;
65
+ }): Promise<{
66
+ hint: string;
67
+ ok: boolean;
68
+ slug: string;
69
+ name: string;
70
+ from: string;
71
+ title: string;
72
+ tier: import("@bridge-ds/core").RiskTier;
73
+ bump: "minor";
74
+ label: string;
75
+ reasons: string[];
76
+ files: {
77
+ path: string;
78
+ action: "create" | "modify";
79
+ }[];
80
+ actionStep: number;
81
+ ramps: {
82
+ [k: string]: {
83
+ [k: string]: string;
84
+ };
85
+ };
86
+ contrast: {
87
+ checks: number;
88
+ passing: number;
89
+ themes: string[];
90
+ line: string;
91
+ failing: {
92
+ pair: string;
93
+ modes: string;
94
+ ratio: number | null;
95
+ min: number;
96
+ }[];
97
+ };
98
+ issues: import("@bridge-ds/core").BrandIssue[];
99
+ prBody: string;
100
+ status: string;
101
+ why: string;
102
+ } | {
103
+ branch: string;
104
+ fileContents: {
105
+ path: string;
106
+ content: string | null;
107
+ }[];
108
+ ok: boolean;
109
+ slug: string;
110
+ name: string;
111
+ from: string;
112
+ title: string;
113
+ tier: import("@bridge-ds/core").RiskTier;
114
+ bump: "minor";
115
+ label: string;
116
+ reasons: string[];
117
+ files: {
118
+ path: string;
119
+ action: "create" | "modify";
120
+ }[];
121
+ actionStep: number;
122
+ ramps: {
123
+ [k: string]: {
124
+ [k: string]: string;
125
+ };
126
+ };
127
+ contrast: {
128
+ checks: number;
129
+ passing: number;
130
+ themes: string[];
131
+ line: string;
132
+ failing: {
133
+ pair: string;
134
+ modes: string;
135
+ ratio: number | null;
136
+ min: number;
137
+ }[];
138
+ };
139
+ issues: import("@bridge-ds/core").BrandIssue[];
140
+ prBody: string;
141
+ status: string;
142
+ } | {
143
+ branch: string;
144
+ fileContents: {
145
+ path: string;
146
+ content: string | null;
147
+ }[];
148
+ ok: boolean;
149
+ slug: string;
150
+ name: string;
151
+ from: string;
152
+ title: string;
153
+ tier: import("@bridge-ds/core").RiskTier;
154
+ bump: "minor";
155
+ label: string;
156
+ reasons: string[];
157
+ files: {
158
+ path: string;
159
+ action: "create" | "modify";
160
+ }[];
161
+ actionStep: number;
162
+ ramps: {
163
+ [k: string]: {
164
+ [k: string]: string;
165
+ };
166
+ };
167
+ contrast: {
168
+ checks: number;
169
+ passing: number;
170
+ themes: string[];
171
+ line: string;
172
+ failing: {
173
+ pair: string;
174
+ modes: string;
175
+ ratio: number | null;
176
+ min: number;
177
+ }[];
178
+ };
179
+ issues: import("@bridge-ds/core").BrandIssue[];
180
+ prBody: string;
181
+ status: string;
182
+ message: string;
183
+ } | {
184
+ branch: string;
185
+ base: string;
186
+ commit: string;
187
+ next: string;
188
+ ok: boolean;
189
+ slug: string;
190
+ name: string;
191
+ from: string;
192
+ title: string;
193
+ tier: import("@bridge-ds/core").RiskTier;
194
+ bump: "minor";
195
+ label: string;
196
+ reasons: string[];
197
+ files: {
198
+ path: string;
199
+ action: "create" | "modify";
200
+ }[];
201
+ actionStep: number;
202
+ ramps: {
203
+ [k: string]: {
204
+ [k: string]: string;
205
+ };
206
+ };
207
+ contrast: {
208
+ checks: number;
209
+ passing: number;
210
+ themes: string[];
211
+ line: string;
212
+ failing: {
213
+ pair: string;
214
+ modes: string;
215
+ ratio: number | null;
216
+ min: number;
217
+ }[];
218
+ };
219
+ issues: import("@bridge-ds/core").BrandIssue[];
220
+ prBody: string;
221
+ status: string;
222
+ } | {
223
+ branch: string;
224
+ commit: string;
225
+ pr: number;
226
+ url: string;
227
+ labels: string[];
228
+ draft: boolean;
229
+ ok: boolean;
230
+ slug: string;
231
+ name: string;
232
+ from: string;
233
+ title: string;
234
+ tier: import("@bridge-ds/core").RiskTier;
235
+ bump: "minor";
236
+ label: string;
237
+ reasons: string[];
238
+ files: {
239
+ path: string;
240
+ action: "create" | "modify";
241
+ }[];
242
+ actionStep: number;
243
+ ramps: {
244
+ [k: string]: {
245
+ [k: string]: string;
246
+ };
247
+ };
248
+ contrast: {
249
+ checks: number;
250
+ passing: number;
251
+ themes: string[];
252
+ line: string;
253
+ failing: {
254
+ pair: string;
255
+ modes: string;
256
+ ratio: number | null;
257
+ min: number;
258
+ }[];
259
+ };
260
+ issues: import("@bridge-ds/core").BrandIssue[];
261
+ prBody: string;
262
+ status: string;
263
+ }>;