miromiro-mcp 0.3.1 → 0.4.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 +7 -4
  2. package/package.json +1 -1
  3. package/src/index.mjs +26 -14
package/README.md CHANGED
@@ -18,7 +18,7 @@ claude plugin install miromiro@claude-community
18
18
  ```
19
19
 
20
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,
21
+ first use it opens a one-click MiroMiro sign-in that links a free account (300 credits/month,
22
22
  no card).
23
23
 
24
24
  ## Quick start: remote server (nothing to install)
@@ -29,7 +29,7 @@ A hosted server lives at:
29
29
  https://miromiro.app/mcp
30
30
  ```
31
31
 
32
- Paste that URL into any client that accepts a remote MCP server. On first use it opens a one-click MiroMiro sign-in that links a free account (100 credits/month, no card).
32
+ Paste that URL into any client that accepts a remote MCP server. On first use it opens a one-click MiroMiro sign-in that links a free account (300 credits/month, no card).
33
33
 
34
34
  - **Claude (web or desktop):** Settings → Connectors → Add custom connector → paste the URL. Or use the [one-click install](https://miromiro.app/api/mcp).
35
35
  - **ChatGPT:** Settings → Security and login → enable Developer mode, then add the URL as a connector (authentication: OAuth).
@@ -68,7 +68,7 @@ claude mcp add miromiro -s user \
68
68
  |---|---|---|
69
69
  | `extract_code` | A section of a live page as a paste-ready component (Tailwind, HTML+CSS, JSX or Vue) plus the tokens it uses - start here to rebuild real UI | 25 |
70
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 |
71
- | `extract_design_tokens` | Full token system with usage counts; `format: css` or `tailwind` for paste-ready output | 10 |
71
+ | `extract_design_tokens` | Full token system with usage counts; `format: css` or `tailwind` for paste-ready output, `dtcg` for a W3C Design Tokens file (Style Dictionary / Figma variables) | 10 |
72
72
  | `extract_fonts` | Every font file and family the site loads, with weights, formats, and file URLs | 5 |
73
73
  | `extract_videos` | Video files, `og:video`, and player embeds (YouTube, Vimeo, Wistia, Loom), tagged by source | 1 |
74
74
  | `extract_audio` | Audio files, `og:audio`, and podcast enclosures, tagged by source | 1 |
@@ -87,7 +87,10 @@ Remote results are trimmed by default so a big page cannot flood the chat contex
87
87
 
88
88
  The REST API and this local npm server always return everything untrimmed, so `limit` and
89
89
  `full_output` apply to the remote server only. Every tool on both servers accepts
90
- `render: true`.
90
+ `render: true`, plus two more shared arguments:
91
+
92
+ - `fresh: true` - skip the 24-hour cache and re-extract the live page (charged normally); use when the site just changed
93
+ - `screenshot: true` (requires `render: true`) - the result gains a `screenshot` field: a public URL of a 1440x900 capture of the page, live for 7 days, at no extra credits - show it to the user to verify an extraction. Pass `'full'` instead of `true` for the entire page height.
91
94
 
92
95
  ## Examples
93
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miromiro-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.4.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
@@ -42,6 +42,18 @@ const RENDER_ARG = {
42
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
43
  }
44
44
 
45
+ // Shared input arg: skip the 24h cache for this one call (charged normally).
46
+ const FRESH_ARG = {
47
+ type: 'boolean',
48
+ description: 'Skip the 24-hour cache and re-extract the live page - use when the site just changed and a cached result would be stale. Charged normally; the fresh result replaces the cached copy.'
49
+ }
50
+
51
+ // Shared input arg: a screenshot URL alongside the data (requires render=true).
52
+ const SCREENSHOT_ARG = {
53
+ type: ['boolean', 'string'],
54
+ description: "Requires render=true. Adds a screenshot field to the result: a public URL of a capture of the page (live 7 days), taken by the same browser that rendered it. Pass true for the 1440x900 viewport or 'full' for the entire page height. No extra credits beyond the render surcharge. Useful for showing the user what the extracted site looks like."
55
+ }
56
+
45
57
  const TOOLS = [
46
58
  {
47
59
  name: 'extract_code',
@@ -55,8 +67,8 @@ const TOOLS = [
55
67
  url: { type: 'string', description: 'The website URL, e.g. stripe.com' },
56
68
  selector: { type: 'string', description: 'CSS selector for the section to extract, e.g. "#hero" or ".pricing-card". Omit to auto-detect the hero.' },
57
69
  framework: { type: 'string', enum: ['tailwind', 'html', 'jsx', 'vue'], description: 'The code to generate (default tailwind).' },
58
- name: { type: 'string', description: 'Component name for the jsx/vue targets (default ExtractedSection).' },
59
- render: RENDER_ARG
70
+ name: { type: 'string', description: 'Component name for the jsx target (default ExtractedSection). Ignored by tailwind, html and vue. Renaming never re-runs the extraction - it costs 0 credits on a cached section.' },
71
+ render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG
60
72
  },
61
73
  required: ['url']
62
74
  }
@@ -69,7 +81,7 @@ const TOOLS = [
69
81
  "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.",
70
82
  inputSchema: {
71
83
  type: 'object',
72
- properties: { url: { type: 'string', description: 'The website URL, e.g. stripe.com' }, render: RENDER_ARG },
84
+ properties: { url: { type: 'string', description: 'The website URL, e.g. stripe.com' }, render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG },
73
85
  required: ['url']
74
86
  }
75
87
  },
@@ -78,13 +90,13 @@ const TOOLS = [
78
90
  title: 'Extract design tokens',
79
91
  endpoint: 'extract',
80
92
  description:
81
- "Extract the full design-token system of a URL: every color, font family, font size, border radius, box shadow, line height and z-index (with usage counts). Pass format='css' for a :root {} block or format='tailwind' for a tailwind.config — paste-ready. Use when you need the complete token set, not just the brand summary.",
93
+ "Extract the full design-token system of a URL: every color, font family, font size, border radius, box shadow, line height and z-index (with usage counts). Pass format='css' for a :root {} block, format='tailwind' for a tailwind.config, or format='dtcg' for a standalone W3C Design Tokens file (drops into Style Dictionary / Figma variables pipelines). Use when you need the complete token set, not just the brand summary.",
82
94
  inputSchema: {
83
95
  type: 'object',
84
96
  properties: {
85
97
  url: { type: 'string', description: 'The website URL' },
86
- format: { type: 'string', enum: ['json', 'css', 'tailwind'], description: 'Output shape (default json)' },
87
- render: RENDER_ARG
98
+ format: { type: 'string', enum: ['json', 'css', 'tailwind', 'dtcg'], description: "Output shape (default json). 'dtcg' returns a W3C Design Tokens file." },
99
+ render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG
88
100
  },
89
101
  required: ['url']
90
102
  }
@@ -97,7 +109,7 @@ const TOOLS = [
97
109
  "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).",
98
110
  inputSchema: {
99
111
  type: 'object',
100
- properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
112
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG },
101
113
  required: ['url']
102
114
  }
103
115
  },
@@ -109,7 +121,7 @@ const TOOLS = [
109
121
  "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).",
110
122
  inputSchema: {
111
123
  type: 'object',
112
- properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
124
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG },
113
125
  required: ['url']
114
126
  }
115
127
  },
@@ -124,7 +136,7 @@ const TOOLS = [
124
136
  properties: {
125
137
  url: { type: 'string', description: 'The website URL' },
126
138
  fields: { type: 'string', description: "Optional comma-separated fields to return (e.g. 'families')" },
127
- render: RENDER_ARG
139
+ render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG
128
140
  },
129
141
  required: ['url']
130
142
  }
@@ -137,7 +149,7 @@ const TOOLS = [
137
149
  "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
150
  inputSchema: {
139
151
  type: 'object',
140
- properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
152
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG },
141
153
  required: ['url']
142
154
  }
143
155
  },
@@ -149,7 +161,7 @@ const TOOLS = [
149
161
  '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
162
  inputSchema: {
151
163
  type: 'object',
152
- properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
164
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG },
153
165
  required: ['url']
154
166
  }
155
167
  },
@@ -161,7 +173,7 @@ const TOOLS = [
161
173
  '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).',
162
174
  inputSchema: {
163
175
  type: 'object',
164
- properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG },
176
+ properties: { url: { type: 'string', description: 'The website URL' }, render: RENDER_ARG, fresh: FRESH_ARG, screenshot: SCREENSHOT_ARG },
165
177
  required: ['url']
166
178
  }
167
179
  }
@@ -169,7 +181,7 @@ const TOOLS = [
169
181
 
170
182
  // Query params a tool may forward to the API, beyond `url`. Anything else in the
171
183
  // tool arguments is ignored, so a hallucinated param can't reach the endpoint.
172
- const FORWARDED_PARAMS = ['format', 'selector', 'framework', 'name', 'fields', 'render']
184
+ const FORWARDED_PARAMS = ['format', 'selector', 'framework', 'name', 'fields', 'render', 'fresh', 'screenshot']
173
185
 
174
186
  async function callApi(endpoint, args) {
175
187
  const q = new URLSearchParams({ url: args.url })
@@ -192,7 +204,7 @@ async function callApi(endpoint, args) {
192
204
  }
193
205
 
194
206
  const server = new Server(
195
- { name: 'miromiro', version: '0.3.0' },
207
+ { name: 'miromiro', version: '0.4.1' },
196
208
  { capabilities: { tools: {} } }
197
209
  )
198
210