@puredesktop/puredesktop-ui-bridge 2.1.7 → 2.1.9
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/package.json +89 -5
- package/src/agents/mcpServerPresets.test.ts +105 -0
- package/src/agents/mcpServerPresets.ts +380 -0
- package/src/agents/runtime/index.ts +7 -0
- package/src/agents/runtime/mapAgentSnapshot.ts +11 -8
- package/src/agents/runtime/pendingAsks.test.ts +117 -0
- package/src/agents/runtime/pendingAsks.ts +110 -0
- package/src/bridge/agentScopeDefaults.ts +65 -0
- package/src/bridge/agentTypes.ts +57 -3
- package/src/bridge/agents.d.mts +6 -0
- package/src/bridge/agents.mjs +10 -0
- package/src/bridge/agents.ts +4 -0
- package/src/bridge/collectionImagePaste.ts +19 -1
- package/src/bridge/context.mjs +32 -0
- package/src/bridge/credentials.d.mts +62 -0
- package/src/bridge/credentials.mjs +53 -0
- package/src/bridge/documents.d.mts +76 -0
- package/src/bridge/documents.mjs +40 -0
- package/src/bridge/events.d.mts +1 -0
- package/src/bridge/events.mjs +1 -0
- package/src/bridge/googleAuth.ts +7 -152
- package/src/bridge/mcpClient.d.mts +74 -0
- package/src/bridge/mcpClient.mjs +79 -0
- package/src/bridge/methods.d.mts +45 -0
- package/src/bridge/methods.mjs +46 -0
- package/src/bridge/notifications.d.mts +12 -0
- package/src/bridge/notifications.mjs +36 -0
- package/src/bridge/operations.d.mts +44 -0
- package/src/bridge/operations.mjs +40 -0
- package/src/bridge/pureRender/base.css +44 -0
- package/src/bridge/pureRender/document.ts +29 -2
- package/src/bridge/pureRender/index.ts +3 -0
- package/src/bridge/pureRender/normalize.ts +167 -0
- package/src/bridge/pureRender/sanitizeExportBody.test.ts +51 -0
- package/src/bridge/pureRender/sanitizeExportBody.ts +61 -0
- package/src/bridge/pureRender/styles.ts +125 -5
- package/src/bridge/pureRender/theme.test.ts +259 -0
- package/src/bridge/pureRender/theme.ts +19 -1
- package/src/bridge/pureRender/types.ts +33 -0
- package/src/bridge/pureRender/visualLayoutInspection.test.ts +26 -0
- package/src/bridge/pureRender/visualLayoutInspection.ts +75 -0
- package/src/bridge/react/useDocumentHotkeys.ts +41 -0
- package/src/bridge/react/useDocumentLifecycle.tsx +359 -0
- package/src/bridge/react/usePlatformAgentSessionDrive.test.tsx +29 -10
- package/src/bridge/react/usePlatformAgentSessionDrive.tsx +10 -81
- package/src/bridge/react/usePlatformAppSettings.test.tsx +105 -0
- package/src/bridge/react/usePlatformAppSettings.tsx +104 -0
- package/src/bridge/react/usePlatformJsonStore.test.tsx +152 -0
- package/src/bridge/react/usePlatformJsonStore.tsx +149 -0
- package/src/bridge/react/usePlatformOperations.tsx +75 -0
- package/src/bridge/storage.d.mts +23 -10
- package/src/bridge/storage.mjs +6 -3
- package/src/bridge/storage.test.ts +6 -6
- package/src/bridge/types.ts +7 -0
- package/src/components/agents/AgentComposer.tsx +10 -6
- package/src/components/agents/AgentDrawerPanel.test.tsx +2 -205
- package/src/components/agents/AgentDrawerPanel.tsx +23 -62
- package/src/components/agents/AgentMessageBubble.tsx +59 -33
- package/src/components/agents/AgentMessageList.tsx +4 -4
- package/src/components/agents/AgentQuestionPromptList.tsx +34 -212
- package/src/components/agents/AgentToolApprovalActions.tsx +13 -11
- package/src/components/agents/AgentToolCallCard.tsx +20 -20
- package/src/components/agents/AgentToolPendingList.tsx +6 -6
- package/src/components/agents/AgentToolPendingReview.tsx +6 -6
- package/src/components/agents/ContextPicker.tsx +239 -0
- package/src/components/agents/QuestionRequestList.tsx +278 -0
- package/src/components/agents/agentPanelStyles.ts +8 -31
- package/src/components/agents/agentTypes.ts +25 -41
- package/src/components/agents/index.ts +4 -0
- package/src/components/assets/asset-library/sidebar/AssetLibraryList.tsx +10 -4
- package/src/components/chrome/WorkspaceTabStrip.tsx +22 -4
- package/src/components/chrome/workspaceTabTypes.ts +2 -0
- package/src/components/common/chat/ChatBox.tsx +9 -9
- package/src/components/common/chat/ChatModelMenu.tsx +34 -4
- package/src/components/common/chat/ChatThread.tsx +23 -7
- package/src/components/common/connections/ProviderConnection.test.tsx +208 -0
- package/src/components/common/connections/ProviderConnection.tsx +277 -0
- package/src/components/common/connections/index.ts +5 -0
- package/src/components/common/containers/AppFrame.test.tsx +4 -4
- package/src/components/common/containers/AppFrame.tsx +26 -10
- package/src/components/common/containers/AppHeader.tsx +37 -15
- package/src/components/common/documents/DocumentHeaderActions.tsx +206 -0
- package/src/components/common/documents/DocumentSwitcher.tsx +1181 -0
- package/src/components/common/documents/index.ts +8 -0
- package/src/components/common/dropdown/MenuDropdownItem.tsx +14 -2
- package/src/components/common/dropdown/MenuPortal.tsx +3 -1
- package/src/components/common/dropdown/menuViewportPosition.test.ts +20 -10
- package/src/components/common/dropdown/menuViewportPosition.ts +35 -27
- package/src/components/common/overlays/Modal.tsx +13 -2
- package/src/components/common/research/EvidenceDossier.tsx +1232 -150
- package/src/components/common/research/index.ts +2 -0
- package/src/components/editor/EditorLinkPromptDialog.tsx +1 -1
- package/src/components/print-design/PrintDesignPanel.test.tsx +430 -40
- package/src/components/print-design/PrintDesignPanel.tsx +1770 -207
- package/src/components/print-design/index.ts +2 -0
- package/src/components/print-design/previewContent.test.ts +32 -0
- package/src/components/print-design/previewContent.ts +31 -0
- package/src/context/buildContextDocument.test.ts +240 -0
- package/src/context/buildContextDocument.ts +309 -0
- package/src/context/contextAccess.ts +66 -0
- package/src/context/contextConfig.ts +72 -0
- package/src/context/contextDocument.test.ts +155 -0
- package/src/context/contextDocument.ts +300 -0
- package/src/context/contextSections.test.ts +88 -0
- package/src/context/contextSections.ts +84 -0
- package/src/context/htmlToContextMarkdown.test.ts +134 -0
- package/src/context/htmlToContextMarkdown.ts +369 -0
- package/src/ics/generateIcs.test.ts +153 -0
- package/src/ics/generateIcs.ts +197 -0
- package/src/index.ts +3 -0
- package/src/net/httpRetry.test.ts +117 -0
- package/src/net/httpRetry.ts +111 -0
- package/src/theme/appAccents.test.ts +36 -35
- package/src/theme/appAccents.ts +93 -104
- package/src/theme/appIdentityCss.mjs +178 -229
- package/src/theme/appIdentityCss.ts +222 -231
- package/src/theme/themes/dark.ts +3 -1
- package/src/theme/themes/light.ts +3 -1
- package/src/bridge/googleProviderConfig.ts +0 -73
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated MCP servers preloaded with PureDesktop — one click to add from the
|
|
3
|
+
* assistant Tools library (nothing is enabled until the user confirms).
|
|
4
|
+
* Selection favors keyless servers; entries that need a key say so and the
|
|
5
|
+
* UI asks for the bearer token after the first failing test.
|
|
6
|
+
*
|
|
7
|
+
* `stdio` presets run locally and need their runner on PATH (`npx` → Node,
|
|
8
|
+
* `uvx` → uv); remote presets need nothing. The Test button verifies either
|
|
9
|
+
* way, so a missing runner or a renamed package fails loudly, not silently.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type McpServerPresetTransport = 'http' | 'sse' | 'stdio'
|
|
13
|
+
|
|
14
|
+
export type McpServerPresetCategory =
|
|
15
|
+
| 'academic'
|
|
16
|
+
| 'knowledge'
|
|
17
|
+
| 'documentation'
|
|
18
|
+
| 'developer'
|
|
19
|
+
|
|
20
|
+
export type McpServerPresetAuth = 'none' | 'bearer' | 'apiKey'
|
|
21
|
+
|
|
22
|
+
export interface McpServerPreset {
|
|
23
|
+
/** Registry id — `[a-z0-9-]+`, stable across releases. */
|
|
24
|
+
id: string
|
|
25
|
+
label: string
|
|
26
|
+
description: string
|
|
27
|
+
category: McpServerPresetCategory
|
|
28
|
+
transport: McpServerPresetTransport
|
|
29
|
+
url?: string
|
|
30
|
+
command?: string
|
|
31
|
+
args?: string[]
|
|
32
|
+
/**
|
|
33
|
+
* How the server authenticates: `bearer` (Authorization header on a remote
|
|
34
|
+
* server) or `apiKey` (an env var, named by `apiKeyEnvVar`, on a local stdio
|
|
35
|
+
* server). Omitted → no auth.
|
|
36
|
+
*/
|
|
37
|
+
auth?: McpServerPresetAuth
|
|
38
|
+
/** Env var the key is passed as, for `auth: 'apiKey'` servers. */
|
|
39
|
+
apiKeyEnvVar?: string
|
|
40
|
+
/** Preset needs a secret (bearer token or API key) before it will connect. */
|
|
41
|
+
requiresToken?: boolean
|
|
42
|
+
/** Short caveat shown with the entry (runner needed, rate limits, …). */
|
|
43
|
+
note?: string
|
|
44
|
+
/**
|
|
45
|
+
* Site to pull a favicon from for the row logo. Auto-derived from `url` for
|
|
46
|
+
* remote presets; set explicitly for local (stdio) presets that still have a
|
|
47
|
+
* recognizable home page (arXiv, PubMed, …) so they don't fall back to plain
|
|
48
|
+
* initials.
|
|
49
|
+
*/
|
|
50
|
+
iconDomain?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const MCP_SERVER_PRESETS: readonly McpServerPreset[] = [
|
|
54
|
+
// ---- Academic research ----------------------------------------------------
|
|
55
|
+
{
|
|
56
|
+
id: 'arxiv',
|
|
57
|
+
label: 'arXiv',
|
|
58
|
+
description:
|
|
59
|
+
'Search arXiv preprints, fetch abstracts, and download papers for analysis.',
|
|
60
|
+
category: 'academic',
|
|
61
|
+
transport: 'stdio',
|
|
62
|
+
command: 'uvx',
|
|
63
|
+
args: ['arxiv-mcp-server'],
|
|
64
|
+
note: 'Python server — needs uv (one-time install).',
|
|
65
|
+
iconDomain: 'arxiv.org',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'pubmed',
|
|
69
|
+
label: 'PubMed',
|
|
70
|
+
description:
|
|
71
|
+
'Search PubMed biomedical literature and fetch article metadata and abstracts.',
|
|
72
|
+
category: 'academic',
|
|
73
|
+
transport: 'stdio',
|
|
74
|
+
command: 'uvx',
|
|
75
|
+
args: ['pubmedmcp'],
|
|
76
|
+
note: 'Python server — needs uv (one-time install).',
|
|
77
|
+
iconDomain: 'pubmed.ncbi.nlm.nih.gov',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'wikipedia',
|
|
81
|
+
label: 'Wikipedia',
|
|
82
|
+
description:
|
|
83
|
+
'Look up and summarize Wikipedia articles with full-text search.',
|
|
84
|
+
category: 'academic',
|
|
85
|
+
transport: 'stdio',
|
|
86
|
+
command: 'uvx',
|
|
87
|
+
args: ['wikipedia-mcp'],
|
|
88
|
+
note: 'Python server — needs uv (one-time install).',
|
|
89
|
+
iconDomain: 'wikipedia.org',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'huggingface',
|
|
93
|
+
label: 'Hugging Face',
|
|
94
|
+
description:
|
|
95
|
+
'Search models, datasets, Spaces, and daily papers on the Hugging Face Hub.',
|
|
96
|
+
category: 'academic',
|
|
97
|
+
transport: 'http',
|
|
98
|
+
url: 'https://huggingface.co/mcp',
|
|
99
|
+
note: 'Remote server; anonymous access is rate-limited.',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'exa',
|
|
103
|
+
label: 'Exa research',
|
|
104
|
+
description:
|
|
105
|
+
'Neural web + research-paper search with full-text retrieval — strong for academic literature.',
|
|
106
|
+
category: 'academic',
|
|
107
|
+
transport: 'stdio',
|
|
108
|
+
command: 'npx',
|
|
109
|
+
args: ['-y', 'exa-mcp-server'],
|
|
110
|
+
auth: 'apiKey',
|
|
111
|
+
apiKeyEnvVar: 'EXA_API_KEY',
|
|
112
|
+
requiresToken: true,
|
|
113
|
+
note: 'Runs via npx (Node). Needs a free Exa API key (asked for after adding).',
|
|
114
|
+
iconDomain: 'exa.ai',
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// ---- General knowledge work ----------------------------------------------
|
|
118
|
+
{
|
|
119
|
+
id: 'fetch',
|
|
120
|
+
label: 'Web fetch',
|
|
121
|
+
description:
|
|
122
|
+
'Fetch a web page and convert it to markdown for reading and citation.',
|
|
123
|
+
category: 'knowledge',
|
|
124
|
+
transport: 'stdio',
|
|
125
|
+
command: 'uvx',
|
|
126
|
+
args: ['mcp-server-fetch'],
|
|
127
|
+
note: 'Python reference server — needs uv (one-time install).',
|
|
128
|
+
iconDomain: 'developer.mozilla.org',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'duckduckgo',
|
|
132
|
+
label: 'DuckDuckGo search',
|
|
133
|
+
description: 'Key-free web search with content extraction.',
|
|
134
|
+
category: 'knowledge',
|
|
135
|
+
transport: 'stdio',
|
|
136
|
+
command: 'uvx',
|
|
137
|
+
args: ['duckduckgo-mcp-server'],
|
|
138
|
+
note: 'Python server — needs uv (one-time install).',
|
|
139
|
+
iconDomain: 'duckduckgo.com',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'memory',
|
|
143
|
+
label: 'Memory (knowledge graph)',
|
|
144
|
+
description:
|
|
145
|
+
'Persistent knowledge-graph memory agents can read and extend across sessions.',
|
|
146
|
+
category: 'knowledge',
|
|
147
|
+
transport: 'stdio',
|
|
148
|
+
command: 'npx',
|
|
149
|
+
args: ['-y', '@modelcontextprotocol/server-memory'],
|
|
150
|
+
note: 'Reference server; runs locally via npx.',
|
|
151
|
+
iconDomain: 'modelcontextprotocol.io',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'sequential-thinking',
|
|
155
|
+
label: 'Sequential thinking',
|
|
156
|
+
description:
|
|
157
|
+
'Structured step-by-step reasoning scratchpad for complex problems.',
|
|
158
|
+
category: 'knowledge',
|
|
159
|
+
transport: 'stdio',
|
|
160
|
+
command: 'npx',
|
|
161
|
+
args: ['-y', '@modelcontextprotocol/server-sequential-thinking'],
|
|
162
|
+
note: 'Reference server; runs locally via npx.',
|
|
163
|
+
iconDomain: 'modelcontextprotocol.io',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'time',
|
|
167
|
+
label: 'Time & timezones',
|
|
168
|
+
description: 'Current time and timezone conversion.',
|
|
169
|
+
category: 'knowledge',
|
|
170
|
+
transport: 'stdio',
|
|
171
|
+
command: 'uvx',
|
|
172
|
+
args: ['mcp-server-time'],
|
|
173
|
+
note: 'Python reference server — needs uv (one-time install).',
|
|
174
|
+
iconDomain: 'modelcontextprotocol.io',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: 'tavily',
|
|
178
|
+
label: 'Tavily search',
|
|
179
|
+
description:
|
|
180
|
+
'Web search, extract, and crawl tuned for LLM answers with citations.',
|
|
181
|
+
category: 'knowledge',
|
|
182
|
+
transport: 'stdio',
|
|
183
|
+
command: 'npx',
|
|
184
|
+
args: ['-y', 'tavily-mcp'],
|
|
185
|
+
auth: 'apiKey',
|
|
186
|
+
apiKeyEnvVar: 'TAVILY_API_KEY',
|
|
187
|
+
requiresToken: true,
|
|
188
|
+
note: 'Runs via npx (Node). Needs a Tavily API key (asked for after adding).',
|
|
189
|
+
iconDomain: 'tavily.com',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: 'brave-search',
|
|
193
|
+
label: 'Brave Search',
|
|
194
|
+
description: 'Independent web and local search index.',
|
|
195
|
+
category: 'knowledge',
|
|
196
|
+
transport: 'stdio',
|
|
197
|
+
command: 'npx',
|
|
198
|
+
args: ['-y', '@modelcontextprotocol/server-brave-search'],
|
|
199
|
+
auth: 'apiKey',
|
|
200
|
+
apiKeyEnvVar: 'BRAVE_API_KEY',
|
|
201
|
+
requiresToken: true,
|
|
202
|
+
note: 'Runs via npx (Node). Needs a Brave Search API key (asked for after adding).',
|
|
203
|
+
iconDomain: 'search.brave.com',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'firecrawl',
|
|
207
|
+
label: 'Firecrawl',
|
|
208
|
+
description:
|
|
209
|
+
'Scrape, crawl, and structure whole websites into clean markdown or JSON.',
|
|
210
|
+
category: 'knowledge',
|
|
211
|
+
transport: 'stdio',
|
|
212
|
+
command: 'npx',
|
|
213
|
+
args: ['-y', 'firecrawl-mcp'],
|
|
214
|
+
auth: 'apiKey',
|
|
215
|
+
apiKeyEnvVar: 'FIRECRAWL_API_KEY',
|
|
216
|
+
requiresToken: true,
|
|
217
|
+
note: 'Runs via npx (Node). Needs a Firecrawl API key (asked for after adding).',
|
|
218
|
+
iconDomain: 'firecrawl.dev',
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
// ---- Documentation --------------------------------------------------------
|
|
222
|
+
{
|
|
223
|
+
id: 'context7',
|
|
224
|
+
label: 'Context7 docs',
|
|
225
|
+
description:
|
|
226
|
+
'Version-accurate library and framework documentation for grounded answers.',
|
|
227
|
+
category: 'documentation',
|
|
228
|
+
transport: 'http',
|
|
229
|
+
url: 'https://mcp.context7.com/mcp',
|
|
230
|
+
note: 'Remote server; anonymous tier is rate-limited.',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'deepwiki',
|
|
234
|
+
label: 'DeepWiki',
|
|
235
|
+
description:
|
|
236
|
+
'Ask questions about public GitHub repositories via generated wikis.',
|
|
237
|
+
category: 'documentation',
|
|
238
|
+
transport: 'http',
|
|
239
|
+
url: 'https://mcp.deepwiki.com/mcp',
|
|
240
|
+
note: 'Remote server, key-free.',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: 'microsoft-learn',
|
|
244
|
+
label: 'Microsoft Learn',
|
|
245
|
+
description:
|
|
246
|
+
'Search and read Microsoft/Azure documentation grounded to current pages.',
|
|
247
|
+
category: 'documentation',
|
|
248
|
+
transport: 'http',
|
|
249
|
+
url: 'https://learn.microsoft.com/api/mcp',
|
|
250
|
+
note: 'Remote server, key-free.',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'cloudflare-docs',
|
|
254
|
+
label: 'Cloudflare Docs',
|
|
255
|
+
description:
|
|
256
|
+
'Search and read Cloudflare developer documentation, grounded to current pages.',
|
|
257
|
+
category: 'documentation',
|
|
258
|
+
transport: 'sse',
|
|
259
|
+
url: 'https://docs.mcp.cloudflare.com/sse',
|
|
260
|
+
note: 'Remote server, key-free.',
|
|
261
|
+
iconDomain: 'cloudflare.com',
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// ---- Developer ------------------------------------------------------------
|
|
265
|
+
{
|
|
266
|
+
id: 'git',
|
|
267
|
+
label: 'Git (local repos)',
|
|
268
|
+
description:
|
|
269
|
+
'Inspect and search local git repositories: log, diff, blame, status.',
|
|
270
|
+
category: 'developer',
|
|
271
|
+
transport: 'stdio',
|
|
272
|
+
command: 'uvx',
|
|
273
|
+
args: ['mcp-server-git'],
|
|
274
|
+
note: 'Python reference server — needs uv (one-time install).',
|
|
275
|
+
iconDomain: 'git-scm.com',
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: 'github',
|
|
279
|
+
label: 'GitHub',
|
|
280
|
+
description:
|
|
281
|
+
'Repositories, issues, and pull requests through the official GitHub MCP.',
|
|
282
|
+
category: 'developer',
|
|
283
|
+
transport: 'http',
|
|
284
|
+
url: 'https://api.githubcopilot.com/mcp/',
|
|
285
|
+
auth: 'bearer',
|
|
286
|
+
requiresToken: true,
|
|
287
|
+
note: 'Needs a GitHub personal access token (asked for after adding).',
|
|
288
|
+
iconDomain: 'github.com',
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: 'playwright',
|
|
292
|
+
label: 'Playwright browser',
|
|
293
|
+
description:
|
|
294
|
+
'Drive a real browser: navigate, click, fill, and read pages for the agent.',
|
|
295
|
+
category: 'developer',
|
|
296
|
+
transport: 'stdio',
|
|
297
|
+
command: 'npx',
|
|
298
|
+
args: ['-y', '@playwright/mcp@latest'],
|
|
299
|
+
note: 'Runs via npx (Node); downloads a browser on first use.',
|
|
300
|
+
iconDomain: 'playwright.dev',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: 'everything',
|
|
304
|
+
label: 'Everything (demo)',
|
|
305
|
+
description:
|
|
306
|
+
'Reference server exercising every MCP feature — handy for trying tools out.',
|
|
307
|
+
category: 'developer',
|
|
308
|
+
transport: 'stdio',
|
|
309
|
+
command: 'npx',
|
|
310
|
+
args: ['-y', '@modelcontextprotocol/server-everything'],
|
|
311
|
+
note: 'Reference server; runs locally via npx.',
|
|
312
|
+
iconDomain: 'modelcontextprotocol.io',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: 'chart',
|
|
316
|
+
label: 'Charts (AntV)',
|
|
317
|
+
description:
|
|
318
|
+
'Generate 25+ chart types (line, bar, pie, maps, …) as images from data.',
|
|
319
|
+
category: 'developer',
|
|
320
|
+
transport: 'stdio',
|
|
321
|
+
command: 'npx',
|
|
322
|
+
args: ['-y', '@antv/mcp-server-chart'],
|
|
323
|
+
note: 'Runs via npx (Node).',
|
|
324
|
+
iconDomain: 'antv.antgroup.com',
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: 'stripe',
|
|
328
|
+
label: 'Stripe',
|
|
329
|
+
description:
|
|
330
|
+
'Query and manage Stripe: customers, payments, invoices, and products.',
|
|
331
|
+
category: 'developer',
|
|
332
|
+
transport: 'http',
|
|
333
|
+
url: 'https://mcp.stripe.com',
|
|
334
|
+
auth: 'bearer',
|
|
335
|
+
requiresToken: true,
|
|
336
|
+
note: 'Needs a Stripe restricted API key as the bearer token (asked for after adding).',
|
|
337
|
+
iconDomain: 'stripe.com',
|
|
338
|
+
},
|
|
339
|
+
]
|
|
340
|
+
|
|
341
|
+
export const MCP_SERVER_PRESET_CATEGORY_LABELS: Record<
|
|
342
|
+
McpServerPresetCategory,
|
|
343
|
+
string
|
|
344
|
+
> = {
|
|
345
|
+
academic: 'Academic research',
|
|
346
|
+
knowledge: 'Knowledge work',
|
|
347
|
+
documentation: 'Documentation',
|
|
348
|
+
developer: 'Developer',
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export function findMcpServerPreset(id: string): McpServerPreset | undefined {
|
|
352
|
+
return MCP_SERVER_PRESETS.find(preset => preset.id === id)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* What a preset needs on the user's machine to run:
|
|
357
|
+
* - `remote` — nothing (an HTTP/SSE endpoint the shell just calls);
|
|
358
|
+
* - `node` — a system `npx` (Node.js), usually already present;
|
|
359
|
+
* - `python` — `uvx` (uv), the least commonly installed.
|
|
360
|
+
*
|
|
361
|
+
* The Tools UI privileges lower-friction runtimes and clearly flags the ones
|
|
362
|
+
* that need an extra install, rather than requiring uv across the board.
|
|
363
|
+
*/
|
|
364
|
+
export type McpPresetRuntime = 'remote' | 'node' | 'python'
|
|
365
|
+
|
|
366
|
+
const NODE_RUNNERS = new Set(['npx', 'node', 'bunx'])
|
|
367
|
+
|
|
368
|
+
export function mcpPresetRuntime(
|
|
369
|
+
preset: Pick<McpServerPreset, 'transport' | 'command'>,
|
|
370
|
+
): McpPresetRuntime {
|
|
371
|
+
if (preset.transport !== 'stdio') return 'remote'
|
|
372
|
+
return preset.command && NODE_RUNNERS.has(preset.command) ? 'node' : 'python'
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** Sort rank so no-install servers come first, then Node, then Python. */
|
|
376
|
+
export const MCP_PRESET_RUNTIME_RANK: Record<McpPresetRuntime, number> = {
|
|
377
|
+
remote: 0,
|
|
378
|
+
node: 1,
|
|
379
|
+
python: 2,
|
|
380
|
+
}
|
|
@@ -8,6 +8,13 @@ export {
|
|
|
8
8
|
export { createIdleLiveTurn, reduceAgentLiveTurn } from './agentLiveReducer.js'
|
|
9
9
|
export { unresolvedToolCalls } from './unresolvedToolCalls.js'
|
|
10
10
|
export { derivePendingToolCalls } from './pendingToolCalls.js'
|
|
11
|
+
export {
|
|
12
|
+
derivePendingAsks,
|
|
13
|
+
sessionPendingAskCalls,
|
|
14
|
+
type PendingAsk,
|
|
15
|
+
type PendingAskDelegation,
|
|
16
|
+
type PendingAskSession,
|
|
17
|
+
} from './pendingAsks.js'
|
|
11
18
|
export { toolRequiresApproval } from './toolRequiresApproval.js'
|
|
12
19
|
export { mergeUsageTurn } from './mergeUsageTurn.js'
|
|
13
20
|
export {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type AgentUiToolCall,
|
|
9
9
|
} from '../../components/agents/agentTypes.js'
|
|
10
10
|
import { derivePendingToolCalls } from './pendingToolCalls.js'
|
|
11
|
-
import {
|
|
11
|
+
import { sessionPendingAskCalls } from './pendingAsks.js'
|
|
12
12
|
|
|
13
13
|
const VISIBLE_ROLES = new Set(['user', 'assistant', 'tool'])
|
|
14
14
|
|
|
@@ -76,19 +76,22 @@ export function mapPendingToolCalls(
|
|
|
76
76
|
)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Pending tools that still need explicit user approval — the shared
|
|
81
|
+
* pending-asks selector's per-session slice (docs/09 §Card states).
|
|
82
|
+
*/
|
|
80
83
|
export function mapApprovalPendingToolCalls(
|
|
81
84
|
snapshot: PlatformAgentRunSnapshot | null,
|
|
82
85
|
policy: PlatformAgentSessionToolPolicy | undefined,
|
|
83
86
|
acceptedPendingToolCallIds: readonly string[] = [],
|
|
84
87
|
): AgentUiToolCall[] {
|
|
85
88
|
if (!snapshot || !policy) return []
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
return sessionPendingAskCalls({
|
|
90
|
+
state: snapshot.state,
|
|
91
|
+
messages: snapshot.messages,
|
|
92
|
+
acceptedPendingToolCallIds,
|
|
93
|
+
toolPolicy: policy,
|
|
94
|
+
})
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
function mapToolCall(call: AgentUiToolCall): AgentUiToolCall {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { PlatformAgentMessage } from '../../bridge/agentTypes.js'
|
|
3
|
+
import {
|
|
4
|
+
derivePendingAsks,
|
|
5
|
+
sessionPendingAskCalls,
|
|
6
|
+
type PendingAskSession,
|
|
7
|
+
} from './pendingAsks.js'
|
|
8
|
+
|
|
9
|
+
function pendingMessages(
|
|
10
|
+
calls: Array<{ id: string; name: string }>,
|
|
11
|
+
): PlatformAgentMessage[] {
|
|
12
|
+
return [
|
|
13
|
+
{ role: 'user', content: 'do the thing' },
|
|
14
|
+
{
|
|
15
|
+
role: 'assistant',
|
|
16
|
+
content: '',
|
|
17
|
+
toolCalls: calls.map(call => ({ ...call, arguments: {} })),
|
|
18
|
+
},
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function session(
|
|
23
|
+
overrides: Partial<PendingAskSession> = {},
|
|
24
|
+
): PendingAskSession {
|
|
25
|
+
return {
|
|
26
|
+
scope: 'global',
|
|
27
|
+
sessionId: 'parent-1',
|
|
28
|
+
state: 'pending_tools',
|
|
29
|
+
messages: pendingMessages([{ id: 'call-1', name: 'harness.invoke_agent' }]),
|
|
30
|
+
...overrides,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('sessionPendingAskCalls', () => {
|
|
35
|
+
it('lists unresolved pending calls', () => {
|
|
36
|
+
expect(sessionPendingAskCalls(session())).toEqual([
|
|
37
|
+
{ id: 'call-1', name: 'harness.invoke_agent', arguments: {} },
|
|
38
|
+
])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('an accepted call is in flight, not an ask', () => {
|
|
42
|
+
expect(
|
|
43
|
+
sessionPendingAskCalls(
|
|
44
|
+
session({ acceptedPendingToolCallIds: ['call-1'] }),
|
|
45
|
+
),
|
|
46
|
+
).toEqual([])
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('a settled session has no asks', () => {
|
|
50
|
+
expect(
|
|
51
|
+
sessionPendingAskCalls(session({ state: 'awaiting_user' })),
|
|
52
|
+
).toEqual([])
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('policy-approved calls are not asks when a policy is supplied', () => {
|
|
56
|
+
expect(
|
|
57
|
+
sessionPendingAskCalls(
|
|
58
|
+
session({
|
|
59
|
+
toolPolicy: {
|
|
60
|
+
allowedTargetApps: [],
|
|
61
|
+
routeReadinessMs: 1_000,
|
|
62
|
+
handlerResponseMs: 1_000,
|
|
63
|
+
approvalOverrides: { 'harness.invoke_agent': false },
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
67
|
+
).toEqual([])
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('derivePendingAsks', () => {
|
|
72
|
+
it('an active child delegation ask outranks the parent ask', () => {
|
|
73
|
+
const asks = derivePendingAsks(session(), [
|
|
74
|
+
{
|
|
75
|
+
status: 'running',
|
|
76
|
+
childAppSlug: 'writer',
|
|
77
|
+
child: session({
|
|
78
|
+
scope: 'app',
|
|
79
|
+
sessionId: 'child-1',
|
|
80
|
+
appSlug: 'writer',
|
|
81
|
+
messages: pendingMessages([
|
|
82
|
+
{ id: 'call-2', name: 'writer.createDocument' },
|
|
83
|
+
]),
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
])
|
|
87
|
+
|
|
88
|
+
expect(asks).toHaveLength(2)
|
|
89
|
+
expect(asks[0]).toEqual({
|
|
90
|
+
session: { scope: 'app', sessionId: 'child-1', appSlug: 'writer' },
|
|
91
|
+
origin: 'child',
|
|
92
|
+
childAppSlug: 'writer',
|
|
93
|
+
calls: [{ id: 'call-2', name: 'writer.createDocument', arguments: {} }],
|
|
94
|
+
})
|
|
95
|
+
expect(asks[1]).toEqual(
|
|
96
|
+
expect.objectContaining({ origin: 'parent' }),
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('settled delegations and unreadable children are ignored', () => {
|
|
101
|
+
const asks = derivePendingAsks(session(), [
|
|
102
|
+
{
|
|
103
|
+
status: 'completed',
|
|
104
|
+
childAppSlug: 'writer',
|
|
105
|
+
child: session({ sessionId: 'child-done' }),
|
|
106
|
+
},
|
|
107
|
+
{ status: 'running', childAppSlug: 'book', child: null },
|
|
108
|
+
])
|
|
109
|
+
expect(asks).toEqual([expect.objectContaining({ origin: 'parent' })])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('no asks anywhere yields an empty list', () => {
|
|
113
|
+
expect(
|
|
114
|
+
derivePendingAsks(session({ state: 'awaiting_user' })),
|
|
115
|
+
).toEqual([])
|
|
116
|
+
})
|
|
117
|
+
})
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PlatformAgentMessage,
|
|
3
|
+
PlatformAgentSessionScope,
|
|
4
|
+
PlatformAgentSessionToolPolicy,
|
|
5
|
+
} from '../../bridge/agentTypes.js'
|
|
6
|
+
import type { AgentUiToolCall } from '../../components/agents/agentTypes.js'
|
|
7
|
+
import { derivePendingToolCalls } from './pendingToolCalls.js'
|
|
8
|
+
import { toolRequiresApproval } from './toolRequiresApproval.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The one pending-asks selector (docs/09-missions.md §Card states): both the
|
|
12
|
+
* agent drawer and the mission card derive "what is this run waiting on the
|
|
13
|
+
* human for?" from here — manual pending calls minus accepted, with active
|
|
14
|
+
* child-delegation asks taking precedence over the parent's. No surface
|
|
15
|
+
* keeps its own derivation that can disagree. Approving from either surface
|
|
16
|
+
* is the same operation against the same session.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export interface PendingAskSession {
|
|
20
|
+
scope: PlatformAgentSessionScope
|
|
21
|
+
sessionId: string
|
|
22
|
+
appSlug?: string
|
|
23
|
+
state: string
|
|
24
|
+
messages: readonly PlatformAgentMessage[]
|
|
25
|
+
acceptedPendingToolCallIds?: readonly string[]
|
|
26
|
+
/** When supplied, calls the policy auto-approves are not asks. */
|
|
27
|
+
toolPolicy?: PlatformAgentSessionToolPolicy
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PendingAskDelegation {
|
|
31
|
+
status: string
|
|
32
|
+
childAppSlug: string
|
|
33
|
+
/** The delegated child's session, when the caller could read it. */
|
|
34
|
+
child?: PendingAskSession | null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PendingAsk {
|
|
38
|
+
session: {
|
|
39
|
+
scope: PlatformAgentSessionScope
|
|
40
|
+
sessionId: string
|
|
41
|
+
appSlug?: string
|
|
42
|
+
}
|
|
43
|
+
/** Child asks come from an active delegated app agent. */
|
|
44
|
+
origin: 'child' | 'parent'
|
|
45
|
+
childAppSlug?: string
|
|
46
|
+
calls: AgentUiToolCall[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* One session's slice of the contract: unresolved manual calls, minus the
|
|
51
|
+
* accepted-but-still-settling ones (an accepted call is IN FLIGHT — a
|
|
52
|
+
* delegation or app tool holds it open — not waiting on the human).
|
|
53
|
+
*/
|
|
54
|
+
export function sessionPendingAskCalls(
|
|
55
|
+
session: Pick<
|
|
56
|
+
PendingAskSession,
|
|
57
|
+
'state' | 'messages' | 'acceptedPendingToolCallIds' | 'toolPolicy'
|
|
58
|
+
>,
|
|
59
|
+
): AgentUiToolCall[] {
|
|
60
|
+
const accepted = new Set(session.acceptedPendingToolCallIds ?? [])
|
|
61
|
+
return derivePendingToolCalls(session.messages, session.state)
|
|
62
|
+
.filter(call => !accepted.has(call.id))
|
|
63
|
+
.filter(call =>
|
|
64
|
+
session.toolPolicy
|
|
65
|
+
? toolRequiresApproval(call.name, session.toolPolicy)
|
|
66
|
+
: true,
|
|
67
|
+
)
|
|
68
|
+
.map(call => ({ id: call.id, name: call.name, arguments: call.arguments }))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sessionRef(session: PendingAskSession): PendingAsk['session'] {
|
|
72
|
+
return {
|
|
73
|
+
scope: session.scope,
|
|
74
|
+
sessionId: session.sessionId,
|
|
75
|
+
...(session.appSlug ? { appSlug: session.appSlug } : {}),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function delegationIsActive(status: string): boolean {
|
|
80
|
+
return status === 'pending' || status === 'running'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* All open asks for one run, child-first: while a delegated app agent is
|
|
85
|
+
* asking, that ask IS the run's current question — a parent-level
|
|
86
|
+
* `harness.invoke_agent` ask shown instead would be stale.
|
|
87
|
+
*/
|
|
88
|
+
export function derivePendingAsks(
|
|
89
|
+
parent: PendingAskSession,
|
|
90
|
+
delegations: readonly PendingAskDelegation[] = [],
|
|
91
|
+
): PendingAsk[] {
|
|
92
|
+
const asks: PendingAsk[] = []
|
|
93
|
+
for (const delegation of delegations) {
|
|
94
|
+
if (!delegationIsActive(delegation.status)) continue
|
|
95
|
+
if (!delegation.child) continue
|
|
96
|
+
const calls = sessionPendingAskCalls(delegation.child)
|
|
97
|
+
if (calls.length === 0) continue
|
|
98
|
+
asks.push({
|
|
99
|
+
session: sessionRef(delegation.child),
|
|
100
|
+
origin: 'child',
|
|
101
|
+
childAppSlug: delegation.childAppSlug,
|
|
102
|
+
calls,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
const parentCalls = sessionPendingAskCalls(parent)
|
|
106
|
+
if (parentCalls.length > 0) {
|
|
107
|
+
asks.push({ session: sessionRef(parent), origin: 'parent', calls: parentCalls })
|
|
108
|
+
}
|
|
109
|
+
return asks
|
|
110
|
+
}
|