miromiro-mcp 0.2.1 → 0.3.1

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 (3) hide show
  1. package/README.md +20 -2
  2. package/package.json +1 -1
  3. package/src/index.mjs +48 -13
package/README.md CHANGED
@@ -7,6 +7,20 @@ MCP server for the [MiroMiro](https://miromiro.app/api) design-extraction API. G
7
7
 
8
8
  Works with Claude (web, desktop, Claude Code), ChatGPT, Cursor, Windsurf, Codex, and any MCP client.
9
9
 
10
+ ## Claude Code plugin
11
+
12
+ Install as a plugin to get the tools plus three skills that teach Claude when to reach for
13
+ each one (`clone-section`, `match-brand`, `extract-assets`):
14
+
15
+ ```bash
16
+ claude plugin marketplace add anthropics/claude-plugins-community
17
+ claude plugin install miromiro@claude-community
18
+ ```
19
+
20
+ The plugin uses the remote server, so there is nothing to install and no key to paste - on
21
+ first use it opens a one-click MiroMiro sign-in that links a free account (100 credits/month,
22
+ no card).
23
+
10
24
  ## Quick start: remote server (nothing to install)
11
25
 
12
26
  A hosted server lives at:
@@ -56,6 +70,8 @@ claude mcp add miromiro -s user \
56
70
  | `extract_brand` | Named color roles (primary/accent/background/text), palette, fonts split by role, logo, social profiles, and a 0-1 confidence score | 15 |
57
71
  | `extract_design_tokens` | Full token system with usage counts; `format: css` or `tailwind` for paste-ready output | 10 |
58
72
  | `extract_fonts` | Every font file and family the site loads, with weights, formats, and file URLs | 5 |
73
+ | `extract_videos` | Video files, `og:video`, and player embeds (YouTube, Vimeo, Wistia, Loom), tagged by source | 1 |
74
+ | `extract_audio` | Audio files, `og:audio`, and podcast enclosures, tagged by source | 1 |
59
75
  | `extract_lottie` | Lottie animation JSON files declared in the page | 3 |
60
76
  | `extract_svgs` | Inline SVG code (sanitized, paste-ready) plus external SVG file URLs | 1 |
61
77
  | `extract_assets` | Every image URL on a page, tagged by source, with alt text | 1 |
@@ -69,7 +85,9 @@ Remote results are trimmed by default so a big page cannot flood the chat contex
69
85
  - `limit` (up to 200) - raise the item cap: "give me 80 SVGs, not 20"
70
86
  - `full_output: true` - complete long strings, like entire inline SVG markup
71
87
 
72
- The REST API and this local npm server always return everything untrimmed.
88
+ The REST API and this local npm server always return everything untrimmed, so `limit` and
89
+ `full_output` apply to the remote server only. Every tool on both servers accepts
90
+ `render: true`.
73
91
 
74
92
  ## Examples
75
93
 
@@ -87,7 +105,7 @@ The agent calls `extract_fonts` and returns every family with its font file URLs
87
105
 
88
106
  ## Notes
89
107
 
90
- - The API reads static HTML and resolves the full CSS cascade. It does not execute JavaScript, so a fully client-rendered app may return little for images and Lottie (tokens, fonts, brand, and code come from CSS and are unaffected).
108
+ - The API reads static HTML and resolves the full CSS cascade by default, so a fully client-rendered app may return little for images, video, audio and Lottie (tokens, fonts, brand, and code come from CSS and are unaffected). Pass `render: true` on any remote-server tool to run the page in a real browser first so its JavaScript executes (+5 credits).
91
109
  - Respect the sites you extract from: check their terms before reusing assets commercially.
92
110
 
93
111
  ## Links
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miromiro-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "MCP server for the MiroMiro design-extraction API — give your coding agent the real code, design tokens, assets, SVGs, fonts, and Lottie of any website.",
5
5
  "mcpName": "app.miromiro/miromiro",
6
6
  "type": "module",
package/src/index.mjs CHANGED
@@ -34,20 +34,29 @@ const TOOL_ANNOTATIONS = {
34
34
  openWorldHint: true
35
35
  }
36
36
 
37
+ // Shared input arg: every tool accepts render=true to execute the page's
38
+ // JavaScript in a real (rented) browser before extraction - the fix for
39
+ // client-rendered React/Next/SPA sites that return little statically.
40
+ const RENDER_ARG = {
41
+ type: 'boolean',
42
+ description: 'Run the page in a real browser first so its JavaScript executes - use for client-rendered (React/Next/SPA) sites, or when a normal call came back thin. Slower, and adds 5 credits to the call.'
43
+ }
44
+
37
45
  const TOOLS = [
38
46
  {
39
47
  name: 'extract_code',
40
48
  title: 'Extract section as code',
41
49
  endpoint: 'code',
42
50
  description:
43
- "Turn a section of a LIVE website into clean, paste-ready component code. Give it a URL and (optionally) a CSS selector; it resolves the page's real CSS cascade and returns a self-contained component in Tailwind, plain HTML+CSS, React (JSX) or Vue — plus the design tokens that section actually uses. The response also includes `sections`: every extractable section on the page with a human-readable label and a ready selector — when the user wants a specific part (pricing, footer, testimonials), find it there and call again with that selector. USE THIS whenever the user asks to rebuild, clone, port, or copy a section of a real website: it returns the actual code with true colors, fonts, spacing and copy, so you adapt real values instead of guessing from a screenshot. Note: it reads static HTML/CSS and does not execute JavaScript, so a fully client-rendered app may return little.",
51
+ "Turn a section of a LIVE website into clean, paste-ready component code. Give it a URL and (optionally) a CSS selector; it resolves the page's real CSS cascade and returns a self-contained component in Tailwind, plain HTML+CSS, React (JSX) or Vue — plus the design tokens that section actually uses. The response also includes `sections`: every extractable section on the page with a human-readable label and a ready selector — when the user wants a specific part (pricing, footer, testimonials), find it there and call again with that selector. For jsx/vue, repeated structures (pricing tiers, testimonial cards, feature lists) are collapsed into a data array rendered with .map()/v-for - one template plus the data, like a human would write it. The section's ::before/::after decorations come back as real elements, dark-mode styles as dark: variants, and its @keyframes ship in the css field. USE THIS whenever the user asks to rebuild, clone, port, or copy a section of a real website: it returns the actual code with true colors, fonts, spacing and copy, so you adapt real values instead of guessing from a screenshot. Note: it reads static HTML/CSS by default - summary.styledCoverage (0-100) tells you how much styling resolved, and a client-rendered app that returns little includes a hint field saying to call again with render=true (+5 credits). Follow it.",
44
52
  inputSchema: {
45
53
  type: 'object',
46
54
  properties: {
47
55
  url: { type: 'string', description: 'The website URL, e.g. stripe.com' },
48
56
  selector: { type: 'string', description: 'CSS selector for the section to extract, e.g. "#hero" or ".pricing-card". Omit to auto-detect the hero.' },
49
57
  framework: { type: 'string', enum: ['tailwind', 'html', 'jsx', 'vue'], description: 'The code to generate (default tailwind).' },
50
- name: { type: 'string', description: 'Component name for the jsx/vue targets (default ExtractedSection).' }
58
+ name: { type: 'string', description: 'Component name for the jsx/vue targets (default ExtractedSection).' },
59
+ render: RENDER_ARG
51
60
  },
52
61
  required: ['url']
53
62
  }
@@ -60,7 +69,7 @@ const TOOLS = [
60
69
  "Get a website's curated brand identity: color roles (primary, accent, background, text) read from the site's own CSS variables and logo — each as hex plus a human-readable name like 'Royal Blue' — dark/light theme detection, the brand palette, fonts split by role (primary/mono), logo, social image, the brand's social profiles (X, GitHub, LinkedIn, …), and a 0-1 confidence score for how much the site declared vs how much was inferred. Use this when you only need a site's STYLE (colors/fonts) rather than its code — to build new UI 'in the style of' a reference site. If you need the actual markup of a section, use extract_code instead.",
61
70
  inputSchema: {
62
71
  type: 'object',
63
- properties: { url: { type: 'string', description: 'The website URL, e.g. stripe.com' } },
72
+ properties: { url: { type: 'string', description: 'The website URL, e.g. stripe.com' }, render: RENDER_ARG },
64
73
  required: ['url']
65
74
  }
66
75
  },
@@ -74,7 +83,8 @@ const TOOLS = [
74
83
  type: 'object',
75
84
  properties: {
76
85
  url: { type: 'string', description: 'The website URL' },
77
- format: { type: 'string', enum: ['json', 'css', 'tailwind'], description: 'Output shape (default json)' }
86
+ format: { type: 'string', enum: ['json', 'css', 'tailwind'], description: 'Output shape (default json)' },
87
+ render: RENDER_ARG
78
88
  },
79
89
  required: ['url']
80
90
  }
@@ -84,10 +94,10 @@ const TOOLS = [
84
94
  title: 'Extract images',
85
95
  endpoint: 'images',
86
96
  description:
87
- "List every image on a page (img, srcset, picture, CSS background, favicon, og:image) as resolved URLs tagged by source. Use to recover imagery from a site or pull reference assets.",
97
+ "List every image on a page (img, srcset, picture, CSS background, favicon, og:image) as resolved URLs tagged by source. Use to recover imagery from a site or pull reference assets. Best-effort from static HTML: if images are mounted by JavaScript, call again with render=true (+5 credits).",
88
98
  inputSchema: {
89
99
  type: 'object',
90
- properties: { url: { type: 'string', description: 'The website URL' } },
100
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
91
101
  required: ['url']
92
102
  }
93
103
  },
@@ -96,10 +106,10 @@ const TOOLS = [
96
106
  title: 'Extract SVGs',
97
107
  endpoint: 'svg',
98
108
  description:
99
- "Extract inline SVGs (as paste-ready code) and SVG file references from a page. Use to grab icons and vector logos.",
109
+ "Extract inline SVGs (as paste-ready code) and SVG file references from a page. Use to grab icons and vector logos. Best-effort from static HTML: if icons are mounted by JavaScript, call again with render=true (+5 credits).",
100
110
  inputSchema: {
101
111
  type: 'object',
102
- properties: { url: { type: 'string', description: 'The website URL' } },
112
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
103
113
  required: ['url']
104
114
  }
105
115
  },
@@ -113,7 +123,8 @@ const TOOLS = [
113
123
  type: 'object',
114
124
  properties: {
115
125
  url: { type: 'string', description: 'The website URL' },
116
- fields: { type: 'string', description: "Optional comma-separated fields to return (e.g. 'families')" }
126
+ fields: { type: 'string', description: "Optional comma-separated fields to return (e.g. 'families')" },
127
+ render: RENDER_ARG
117
128
  },
118
129
  required: ['url']
119
130
  }
@@ -123,10 +134,34 @@ const TOOLS = [
123
134
  title: 'Extract Lottie animations',
124
135
  endpoint: 'lottie',
125
136
  description:
126
- "Find Lottie/animation JSON files declared on a page. Best-effort from static HTML (runtime-injected animations need the MiroMiro browser extension). Use to recover animations.",
137
+ "Find Lottie/animation JSON files declared on a page. Best-effort from static HTML - most Lotties are runtime-injected, so pass render=true to execute the page JS first and catch those too (+5 credits). Use to recover animations.",
138
+ inputSchema: {
139
+ type: 'object',
140
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
141
+ required: ['url']
142
+ }
143
+ },
144
+ {
145
+ name: 'extract_videos',
146
+ title: 'Extract videos',
147
+ endpoint: 'videos',
148
+ description:
149
+ 'List every video a page declares: <video>/<source> files, og:video, player embeds (YouTube, Vimeo, Wistia, Loom) and direct video-file links - each tagged by source, with a poster image when declared. Note `source: "embed"` is a player page, not a downloadable file. Best-effort from static HTML: most sites mount their player with JavaScript, so if the result is thin or empty, call again with render=true to execute the page JS first (+5 credits).',
150
+ inputSchema: {
151
+ type: 'object',
152
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
153
+ required: ['url']
154
+ }
155
+ },
156
+ {
157
+ name: 'extract_audio',
158
+ title: 'Extract audio',
159
+ endpoint: 'audio',
160
+ description:
161
+ 'List every audio file a page declares: <audio>/<source> files, og:audio, and direct audio-file links such as podcast enclosures and download buttons - each tagged by source. Best-effort from static HTML: if a JavaScript player mounts the audio at runtime, call again with render=true (+5 credits).',
127
162
  inputSchema: {
128
163
  type: 'object',
129
- properties: { url: { type: 'string', description: 'The website URL' } },
164
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
130
165
  required: ['url']
131
166
  }
132
167
  }
@@ -134,7 +169,7 @@ const TOOLS = [
134
169
 
135
170
  // Query params a tool may forward to the API, beyond `url`. Anything else in the
136
171
  // tool arguments is ignored, so a hallucinated param can't reach the endpoint.
137
- const FORWARDED_PARAMS = ['format', 'selector', 'framework', 'name', 'fields']
172
+ const FORWARDED_PARAMS = ['format', 'selector', 'framework', 'name', 'fields', 'render']
138
173
 
139
174
  async function callApi(endpoint, args) {
140
175
  const q = new URLSearchParams({ url: args.url })
@@ -157,7 +192,7 @@ async function callApi(endpoint, args) {
157
192
  }
158
193
 
159
194
  const server = new Server(
160
- { name: 'miromiro', version: '0.2.1' },
195
+ { name: 'miromiro', version: '0.3.0' },
161
196
  { capabilities: { tools: {} } }
162
197
  )
163
198