@shuffleio/shuffle-mcps 0.0.4 → 0.0.6
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 +201 -18
- package/dist/index.cjs +7583 -481
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +460 -5
- package/dist/index.d.ts +460 -5
- package/dist/index.js +7627 -480
- package/dist/index.js.map +1 -1
- package/dist/{shuffle-mcp-XZEVQXW2.css → shuffle-mcp-JQWKQNUP.css} +268 -21
- package/package.json +17 -2
package/README.md
CHANGED
|
@@ -8,7 +8,6 @@ Turn 3,000+ SaaS tools into MCP servers your agents can use. One search, one cli
|
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@shuffleio/shuffle-mcps)
|
|
10
10
|
[](https://www.npmjs.com/package/@shuffleio/shuffle-mcps)
|
|
11
|
-
[](https://github.com/Shuffle/singul.js)
|
|
12
11
|
|
|
13
12
|
</div>
|
|
14
13
|
|
|
@@ -29,7 +28,7 @@ Turn 3,000+ SaaS tools into MCP servers your agents can use. One search, one cli
|
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
|
|
32
|
-
> **See it live:** [security.shuffler.io/
|
|
31
|
+
> **See it live:** [security.shuffler.io/shuffle-mcp-demo](https://security.shuffler.io/shuffle-mcp-demo) — a fully working demo of every component in this package (inline search, search drawer, app detail drawer, auth, Try MCP, and Try individual actions). No login required.
|
|
33
32
|
|
|
34
33
|
> **No SDK required.** Once a tool is authenticated in Shuffle, it is live as an MCP server immediately — call it from any agent over plain HTTP. This npm package is the optional UI layer for embedding the search-and-connect flow inside your own product.
|
|
35
34
|
|
|
@@ -74,7 +73,7 @@ import { ShuffleMCP } from 'shuffle-mcps';
|
|
|
74
73
|
export default function App() {
|
|
75
74
|
return (
|
|
76
75
|
<ShuffleMCP
|
|
77
|
-
|
|
76
|
+
apiKey="YOUR_SHUFFLE_API_KEY"
|
|
78
77
|
inline
|
|
79
78
|
layout="grid"
|
|
80
79
|
gridColumns={3}
|
|
@@ -84,28 +83,168 @@ export default function App() {
|
|
|
84
83
|
}
|
|
85
84
|
```
|
|
86
85
|
|
|
87
|
-
##
|
|
86
|
+
## The components
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
The library exports four React components — use one or all of them. They share state, so you can mix-and-match.
|
|
90
89
|
|
|
91
|
-
|
|
90
|
+
| Component | What it does | Live demo |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| [`<ShuffleMCP />`](#1-shufflemcp--inline-search) | Inline search box that merges Algolia's 3,000+ public app catalog with the current user's private apps | [/shuffle-mcp-demo §1](https://security.shuffler.io/shuffle-mcp-demo) |
|
|
93
|
+
| [`<AppSearchDrawer />`](#2-appsearchdrawer--full-search-drawer) | Right-side drawer wrapping `<ShuffleMCP />` plus a "Your apps" status row. This is the exact "Add Ingestion Source" drawer used in production | [/shuffle-mcp-demo §2](https://security.shuffler.io/shuffle-mcp-demo) |
|
|
94
|
+
| [`<AppDetailDrawer />`](#3-appdetaildrawer--single-app-config) | Right-side drawer for a single app: list/edit/test authentications and try the MCP `tools/call` endpoint inline | [/shuffle-mcp-demo §3](https://security.shuffler.io/shuffle-mcp-demo) |
|
|
95
|
+
| [`<AgentUI />`](#4-agentui--start--debug-agents) | Standalone "What do you want to do?" hero prompt + live decision-by-decision debugger for `/api/v1/agent` runs. Drop-in replacement for the legacy Shuffle Core agent page | [/agents](https://security.shuffler.io/agents) |
|
|
96
|
+
|
|
97
|
+
### 1. `<ShuffleMCP />` — inline search
|
|
98
|
+
|
|
99
|
+
Drop-in search component. By default, clicking a result opens `<AppDetailDrawer />` automatically.
|
|
92
100
|
|
|
93
101
|
```tsx
|
|
102
|
+
import { ShuffleMCP } from 'shuffle-mcps';
|
|
103
|
+
|
|
94
104
|
<ShuffleMCP
|
|
95
|
-
|
|
105
|
+
apiKey={user.apiKey}
|
|
106
|
+
inline
|
|
107
|
+
layout="grid"
|
|
108
|
+
gridColumns={3}
|
|
109
|
+
/>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Take over selection yourself (open your own modal, route to a detail page, kick off a custom auth flow):
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<ShuffleMCP
|
|
116
|
+
apiKey={user.apiKey}
|
|
96
117
|
inline
|
|
97
118
|
preventDefault
|
|
98
|
-
onAppSelected={(
|
|
119
|
+
onAppSelected={({ app, authUrl }) => {
|
|
120
|
+
navigate(`/integrations/${app.objectID}`);
|
|
121
|
+
}}
|
|
122
|
+
/>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 2. `<AppSearchDrawer />` — full search drawer
|
|
126
|
+
|
|
127
|
+
The complete "Add Ingestion Source" experience: header, your authenticated apps strip, search, and the Shuffle Pipelines banner. Clicking an app opens `<AppDetailDrawer />` for auth + MCP testing.
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { useState } from 'react';
|
|
131
|
+
import { AppSearchDrawer } from 'shuffle-mcps';
|
|
132
|
+
|
|
133
|
+
const [open, setOpen] = useState(false);
|
|
134
|
+
|
|
135
|
+
<>
|
|
136
|
+
<button onClick={() => setOpen(true)}>Add integration</button>
|
|
137
|
+
|
|
138
|
+
<AppSearchDrawer
|
|
139
|
+
open={open}
|
|
140
|
+
onClose={() => setOpen(false)}
|
|
141
|
+
title="Add Ingestion Source"
|
|
142
|
+
subtitle="Search and authenticate a tool to ingest incidents from"
|
|
143
|
+
/>
|
|
144
|
+
</>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Useful props:
|
|
148
|
+
|
|
149
|
+
| Prop | Type | Description |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `open` | `boolean` | Controlled open state |
|
|
152
|
+
| `onClose` | `() => void` | Called on close |
|
|
153
|
+
| `title` / `subtitle` | `string` | Header copy |
|
|
154
|
+
| `anchor` | `'left' \| 'right'` | Default `'right'` |
|
|
155
|
+
| `width` | `number` | Drawer width in px. Default `560` |
|
|
156
|
+
| `pinnedApps` | `Array<{ name, image_url, ... }>` | Apps pinned to the top of search results |
|
|
157
|
+
| `onQuickSelect` | `(app) => void` | Skip the detail drawer and call this directly |
|
|
158
|
+
| `onSelectOverride` | `(app) => boolean` | Intercept selection — return `true` to prevent default |
|
|
159
|
+
| `priorityCategory` | `string` | Sort matching apps to the top of the "Your apps" strip |
|
|
160
|
+
| `showPipelinesBanner` | `boolean` | Show the Shuffle Pipelines CTA |
|
|
161
|
+
|
|
162
|
+
### 3. `<AppDetailDrawer />` — single-app config
|
|
163
|
+
|
|
164
|
+
The right-side drawer that shows everything about one app: existing auth entries (with Configured / Tested / Inactive chips), an inline form to add or edit credentials, a **Test connection** button, an **Activate / Deactivate** toggle, and a built-in MCP `tools/call` chat to try it without leaving the page.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { useState } from 'react';
|
|
168
|
+
import { AppDetailDrawer } from 'shuffle-mcps';
|
|
169
|
+
|
|
170
|
+
const [appName, setAppName] = useState<string | null>(null);
|
|
171
|
+
|
|
172
|
+
<>
|
|
173
|
+
<button onClick={() => setAppName('Gmail')}>Configure Gmail</button>
|
|
174
|
+
|
|
175
|
+
<AppDetailDrawer
|
|
176
|
+
open={appName !== null}
|
|
177
|
+
onClose={() => setAppName(null)}
|
|
178
|
+
appName={appName}
|
|
179
|
+
/>
|
|
180
|
+
</>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Useful props:
|
|
184
|
+
|
|
185
|
+
| Prop | Type | Description |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `open` | `boolean` | Controlled open state |
|
|
188
|
+
| `onClose` | `() => void` | Called on close |
|
|
189
|
+
| `appName` | `string \| null` | The app to load. Resolved against Algolia by name |
|
|
190
|
+
| `anchor` | `'left' \| 'right'` | Default `'right'` |
|
|
191
|
+
| `width` | `number` | Drawer width in px. Default `520` |
|
|
192
|
+
| `onRefresh` | `() => void` | Called after a successful save/test, so the parent can re-fetch |
|
|
193
|
+
| `onAddToCanvas` | `(info) => void` | Replaces the **Activate** CTA with **+ Add** — used by the workflow editor |
|
|
194
|
+
| `isAuthenticated` | `boolean` | Whether the current user is signed in. Default `true` |
|
|
195
|
+
|
|
196
|
+
Reference implementation in production: [`src/Shuffle-MCPs/AppSearchDrawer.tsx`](./AppSearchDrawer.tsx) and [`AppDetailDrawer.tsx`](./AppDetailDrawer.tsx).
|
|
197
|
+
|
|
198
|
+
### 4. `<AgentUI />` — start + debug agents
|
|
199
|
+
|
|
200
|
+
A standalone "What do you want to do?" surface for the Shuffle agent. Two modes in one component:
|
|
201
|
+
|
|
202
|
+
1. **Starter** — large hero prompt with attached MCP/app chips. Submits to `/api/v1/agent`.
|
|
203
|
+
2. **Debugger** — compact header + live decision timeline driven by `/api/v1/streams/results`, with question/continuation forms, per-decision raw-JSON inspection, approvals, retries, and reruns.
|
|
204
|
+
|
|
205
|
+
It switches modes automatically when an execution starts, or when `?execution_id=...&authorization=...` is present in the URL.
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { AgentUI } from 'shuffle-mcps';
|
|
209
|
+
|
|
210
|
+
<AgentUI
|
|
211
|
+
apiKey={user.apiKey} // optional — falls back to session
|
|
212
|
+
apiBaseUrl="https://shuffler.io" // optional — falls back to default
|
|
213
|
+
orgId={user.orgId} // optional — sent as Org-Id header
|
|
214
|
+
defaultApps={[{ name: 'http' }, { name: 'shuffle_tools' }]}
|
|
215
|
+
onRun={({ input, success, executionId }) => {
|
|
216
|
+
console.log('agent run', { input, success, executionId });
|
|
217
|
+
}}
|
|
99
218
|
/>
|
|
100
219
|
```
|
|
101
220
|
|
|
102
|
-
|
|
221
|
+
Like `<ShuffleMCP />`, all three of `apiKey`, `apiBaseUrl`, and `orgId` are **optional**. When omitted, the component uses the shared browser session (cookie + `localStorage.shuffle_api_key`) — the same behavior as the rest of this library.
|
|
222
|
+
|
|
223
|
+
Useful props:
|
|
224
|
+
|
|
225
|
+
| Prop | Type | Description |
|
|
226
|
+
|---|---|---|
|
|
227
|
+
| `apiKey` | `string` | Bearer token used for every `/api/v1/*` call this instance makes |
|
|
228
|
+
| `apiBaseUrl` | `string` | Shuffle backend base URL (e.g. `https://shuffler.io`) |
|
|
229
|
+
| `orgId` | `string` | Sent as the `Org-Id` header on every call |
|
|
230
|
+
| `apps` | `AgentUIApp[]` | Controlled chip set. Disables auto-load |
|
|
231
|
+
| `defaultApps` | `AgentUIApp[]` | Initial chip set when `apps` is not provided |
|
|
232
|
+
| `autoLoadApps` | `boolean` | Auto-fetch the user's authenticated apps via `/api/v1/apps/authentication`. Default `true` |
|
|
233
|
+
| `executionId` / `authorization` | `string` | Attach to a known execution on mount and skip the starter |
|
|
234
|
+
| `readUrlParams` | `boolean` | Read `?execution_id` + `?authorization` from the URL. Default `true` |
|
|
235
|
+
| `title` / `subtitle` / `placeholder` | `string` | Hero copy |
|
|
236
|
+
| `defaultInput` / `autoSubmit` | `string` / `boolean` | Pre-fill and optionally submit on mount |
|
|
237
|
+
| `compact` / `hideHeroIcon` / `hideAppPicker` / `hideAttach` | `boolean` | Layout switches for embedding |
|
|
238
|
+
| `maxWidth` | `number` | Max width of the centered card. Default `900` |
|
|
239
|
+
| `onRun` | `(info) => void` | Fires whenever a run finishes (success or failure) |
|
|
240
|
+
|
|
241
|
+
Reference page in production: [`src/pages/dashboard/AgentsPage.tsx`](../pages/dashboard/AgentsPage.tsx) (mounted at [`/agents`](https://security.shuffler.io/agents)).
|
|
103
242
|
|
|
104
243
|
### Predefined search
|
|
105
244
|
|
|
106
245
|
```tsx
|
|
107
246
|
<ShuffleMCP
|
|
108
|
-
|
|
247
|
+
apiKey="..."
|
|
109
248
|
inline
|
|
110
249
|
initialFilterQuery="siem"
|
|
111
250
|
placeholder="Search SIEM tools..."
|
|
@@ -118,7 +257,7 @@ Full two-drawer reference implementation: [`src/components/shared/AppSearchDrawe
|
|
|
118
257
|
const [picked, setPicked] = useState<AlgoliaSearchApp[]>([]);
|
|
119
258
|
|
|
120
259
|
<ShuffleMCP
|
|
121
|
-
|
|
260
|
+
apiKey="..."
|
|
122
261
|
inline
|
|
123
262
|
multiSelect
|
|
124
263
|
showCheckbox
|
|
@@ -131,7 +270,6 @@ const [picked, setPicked] = useState<AlgoliaSearchApp[]>([]);
|
|
|
131
270
|
|
|
132
271
|
```tsx
|
|
133
272
|
<ShuffleMCP
|
|
134
|
-
authToken={user.token}
|
|
135
273
|
apiKey={user.apiKey}
|
|
136
274
|
apiBaseUrl="https://your-backend.example.com"
|
|
137
275
|
algoliaAppId="YOUR_APP_ID"
|
|
@@ -139,13 +277,16 @@ const [picked, setPicked] = useState<AlgoliaSearchApp[]>([]);
|
|
|
139
277
|
/>
|
|
140
278
|
```
|
|
141
279
|
|
|
280
|
+
> **About `apiKey`:** this is the **current user's personal Shuffle API key**, not an org-level or service token. Your backend should look up the signed-in user's API key (wherever you store it — DB, session, etc.) and pass it through to this component. The component sends it as `Authorization: Bearer` on every API request and forwards it into the auth handoff URL as `&auth=`, so all reads and authentications happen as that user.
|
|
281
|
+
|
|
142
282
|
When `apiKey` is set, the component also fetches the user's **private apps** from `/api/v1/apps` and merges them into the search results, with an **All / Public / Private** filter shown above the list. Status dots appear too: validated, configured, selected, inactive.
|
|
143
283
|
|
|
144
284
|
## Common props
|
|
145
285
|
|
|
146
286
|
| Prop | Type | Description |
|
|
147
287
|
|---|---|---|
|
|
148
|
-
| `
|
|
288
|
+
| `apiKey` | `string` | The **current user's** Shuffle API key. Your backend should fetch it from wherever you store the user's credentials and pass it in. Used as `Authorization: Bearer` on every request and forwarded into the auth handoff URL as `&auth=`. This is the canonical credential. |
|
|
289
|
+
| `~~authToken~~` | `string` | **Deprecated.** Use `apiKey`. Kept for back-compat — only used as the auth-URL token if `apiKey` is not set. |
|
|
149
290
|
| `orgId` | `string` | Optional Shuffle organization ID. When set, every API call (`/api/v1/apps/authentication`, `/api/v1/apps`) is sent with an `Org-Id: <orgId>` header, and the auth URL gets `&org_id=<orgId>` appended. Default: not sent. |
|
|
150
291
|
| `inline` | `boolean` | Inline results vs floating dropdown. |
|
|
151
292
|
| `layout` | `'list' \| 'grid'` | Result layout. Default `'list'`. |
|
|
@@ -153,7 +294,6 @@ When `apiKey` is set, the component also fetches the user's **private apps** fro
|
|
|
153
294
|
| `preventDefault` | `boolean` | Skip the default click behavior (built-in drawer or `window.open(authUrl)`) so you can handle selection yourself. |
|
|
154
295
|
| `onAppSelected` | `(detail) => void` | Fires on single-select pick. If provided, the built-in drawer is **not** opened — you take over. |
|
|
155
296
|
| `multiSelect` | `boolean` | Allow selecting multiple apps. |
|
|
156
|
-
| `apiKey` | `string` | Bearer token. Enables status dots and merges your private apps from `/api/v1/apps`. |
|
|
157
297
|
| `showSourceFilter` | `boolean` | Toggle the All / Public / Private filter. Default `true`. |
|
|
158
298
|
| `customStyles` | `CustomStyles` | Per-slot style overrides. |
|
|
159
299
|
|
|
@@ -165,20 +305,63 @@ When you do **not** pass `onAppSelected`, clicking an app opens a built-in right
|
|
|
165
305
|
|
|
166
306
|
```tsx
|
|
167
307
|
<ShuffleMCP
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
orgId={currentOrg.id} // every request now scoped to this org
|
|
308
|
+
apiKey={user.apiKey} // current user's Shuffle API key, fetched by your backend
|
|
309
|
+
orgId={currentOrg.id} // every request now scoped to this org
|
|
171
310
|
/>
|
|
172
311
|
```
|
|
173
312
|
|
|
174
313
|
|
|
314
|
+
## Theming (light + dark)
|
|
315
|
+
|
|
316
|
+
There is no `theme` prop — every component reads from your app's CSS variables and inherits whatever palette is active. Define these tokens once in your global stylesheet (e.g. `:root` for light, `.dark` for dark) and the components flip automatically:
|
|
317
|
+
|
|
318
|
+
```css
|
|
319
|
+
:root {
|
|
320
|
+
--background: 0 0% 100%;
|
|
321
|
+
--foreground: 222 47% 11%;
|
|
322
|
+
--card: 0 0% 100%;
|
|
323
|
+
--border: 220 13% 91%;
|
|
324
|
+
--muted: 220 14% 96%;
|
|
325
|
+
--muted-foreground: 220 9% 46%;
|
|
326
|
+
--primary: 24 100% 50%; /* Shuffle orange */
|
|
327
|
+
--primary-foreground: 0 0% 100%;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.dark {
|
|
331
|
+
--background: 222 47% 6%;
|
|
332
|
+
--foreground: 0 0% 98%;
|
|
333
|
+
--card: 222 47% 9%;
|
|
334
|
+
--border: 217 19% 18%;
|
|
335
|
+
--muted: 217 19% 14%;
|
|
336
|
+
--muted-foreground: 220 9% 65%;
|
|
337
|
+
--primary: 24 100% 55%;
|
|
338
|
+
--primary-foreground: 0 0% 100%;
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Values are raw HSL channels (no `hsl(...)` wrapper) because internally we use `hsl(var(--card))`, `hsl(var(--border) / 0.5)`, etc. Toggle dark mode by adding/removing the `.dark` class on `<html>` or `<body>` — same pattern as Tailwind / shadcn.
|
|
343
|
+
|
|
344
|
+
For finer-grained overrides on `<ShuffleMCP />`, pass `customStyles` — every slot (container, input, dropdown, gridItem, appName, appDescription, appCategory, appTags, emptyState, errorState, …) accepts a `React.CSSProperties` object. See [LIBRARY.md](./LIBRARY.md#styling) for the full slot list.
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
<ShuffleMCP
|
|
348
|
+
apiKey="..."
|
|
349
|
+
inline
|
|
350
|
+
customStyles={{
|
|
351
|
+
container: { borderRadius: 16 },
|
|
352
|
+
input: { fontFamily: 'Inter, sans-serif' },
|
|
353
|
+
gridItem: { background: 'hsl(var(--card))', border: '1px solid hsl(var(--border))' },
|
|
354
|
+
}}
|
|
355
|
+
/>
|
|
356
|
+
```
|
|
357
|
+
|
|
175
358
|
Full prop reference, framework setup (Next.js, Vue), styling slots, custom rendering, and publishing: [**LIBRARY.md**](./LIBRARY.md).
|
|
176
359
|
|
|
177
360
|
## Imperative handle
|
|
178
361
|
|
|
179
362
|
```tsx
|
|
180
363
|
const ref = useRef<ShuffleMCPHandle>(null);
|
|
181
|
-
<ShuffleMCP ref={ref}
|
|
364
|
+
<ShuffleMCP ref={ref} apiKey="..." inline />
|
|
182
365
|
|
|
183
366
|
ref.current?.search('slack');
|
|
184
367
|
ref.current?.clear();
|