pi-soly 1.9.3 → 1.11.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.
Files changed (104) hide show
  1. package/ask/index.ts +12 -11
  2. package/ask/picker.ts +356 -76
  3. package/ask/prompt.ts +6 -2
  4. package/ask/tests/picker.test.ts +273 -82
  5. package/codemap.ts +276 -0
  6. package/hotreload.ts +239 -0
  7. package/init.ts +302 -0
  8. package/mcp/CHANGELOG.md +384 -0
  9. package/mcp/LICENSE +21 -0
  10. package/mcp/OAUTH.md +355 -0
  11. package/mcp/README.md +410 -0
  12. package/mcp/__tests__/agent-dir-paths.upstream-test.ts +80 -0
  13. package/mcp/__tests__/cli.upstream-test.ts +97 -0
  14. package/mcp/__tests__/commands-onboarding.upstream-test.ts +157 -0
  15. package/mcp/__tests__/config.upstream-test.ts +303 -0
  16. package/mcp/__tests__/consent-manager.upstream-test.ts +151 -0
  17. package/mcp/__tests__/direct-tools-auto-auth.upstream-test.ts +218 -0
  18. package/mcp/__tests__/direct-tools.upstream-test.ts +299 -0
  19. package/mcp/__tests__/elicitation-handler.upstream-test.ts +346 -0
  20. package/mcp/__tests__/elicitation-sdk-integration.upstream-test.ts +140 -0
  21. package/mcp/__tests__/errors.upstream-test.ts +218 -0
  22. package/mcp/__tests__/fixtures/elicitation-server.mjs +98 -0
  23. package/mcp/__tests__/host-html-template.upstream-test.ts +278 -0
  24. package/mcp/__tests__/index-lifecycle.upstream-test.ts +484 -0
  25. package/mcp/__tests__/init-elicitation.upstream-test.ts +86 -0
  26. package/mcp/__tests__/interactive-visualizer-server.upstream-test.ts +28 -0
  27. package/mcp/__tests__/logger.upstream-test.ts +175 -0
  28. package/mcp/__tests__/mcp-auth-flow-client-credentials.upstream-test.ts +612 -0
  29. package/mcp/__tests__/mcp-auth-storage.upstream-test.ts +47 -0
  30. package/mcp/__tests__/mcp-callback-server-unref.upstream-test.ts +264 -0
  31. package/mcp/__tests__/mcp-oauth-provider.upstream-test.ts +216 -0
  32. package/mcp/__tests__/mcp-panel-auth.upstream-test.ts +206 -0
  33. package/mcp/__tests__/mcp-panel-exclude-tools.upstream-test.ts +69 -0
  34. package/mcp/__tests__/mcp-panel-keybindings.upstream-test.ts +186 -0
  35. package/mcp/__tests__/npx-resolver.upstream-test.ts +54 -0
  36. package/mcp/__tests__/oauth-handler.upstream-test.ts +77 -0
  37. package/mcp/__tests__/onboarding-state.upstream-test.ts +52 -0
  38. package/mcp/__tests__/package-manifest.upstream-test.ts +22 -0
  39. package/mcp/__tests__/proxy-modes-auto-auth.upstream-test.ts +253 -0
  40. package/mcp/__tests__/proxy-modes-discovery.upstream-test.ts +84 -0
  41. package/mcp/__tests__/proxy-modes-manual-auth.upstream-test.ts +91 -0
  42. package/mcp/__tests__/proxy-modes-ui-messages.upstream-test.ts +72 -0
  43. package/mcp/__tests__/sampling-handler.upstream-test.ts +285 -0
  44. package/mcp/__tests__/server-manager-http-auth.upstream-test.ts +144 -0
  45. package/mcp/__tests__/server-manager-sampling.upstream-test.ts +236 -0
  46. package/mcp/__tests__/tool-metadata.upstream-test.ts +111 -0
  47. package/mcp/__tests__/tool-result-renderer.upstream-test.ts +147 -0
  48. package/mcp/__tests__/ui-integration.upstream-test.ts +551 -0
  49. package/mcp/__tests__/ui-resource-handler.upstream-test.ts +303 -0
  50. package/mcp/__tests__/ui-server.upstream-test.ts +967 -0
  51. package/mcp/__tests__/ui-session-messages.upstream-test.ts +72 -0
  52. package/mcp/__tests__/ui-streaming.upstream-test.ts +543 -0
  53. package/mcp/agent-dir.ts +20 -0
  54. package/mcp/app-bridge.bundle.js +67 -0
  55. package/mcp/cli.js +184 -0
  56. package/mcp/commands.ts +422 -0
  57. package/mcp/config.ts +666 -0
  58. package/mcp/consent-manager.ts +64 -0
  59. package/mcp/direct-tools.ts +439 -0
  60. package/mcp/elicitation-handler.ts +347 -0
  61. package/mcp/errors.ts +219 -0
  62. package/mcp/glimpse-ui.ts +80 -0
  63. package/mcp/host-html-template.ts +427 -0
  64. package/mcp/index.ts +362 -0
  65. package/mcp/init.ts +362 -0
  66. package/mcp/lifecycle.ts +93 -0
  67. package/mcp/logger.ts +169 -0
  68. package/mcp/mcp-auth-flow.ts +559 -0
  69. package/mcp/mcp-auth-flow.upstream-test.ts +259 -0
  70. package/mcp/mcp-auth.ts +302 -0
  71. package/mcp/mcp-auth.upstream-test.ts +373 -0
  72. package/mcp/mcp-callback-server.ts +372 -0
  73. package/mcp/mcp-callback-server.upstream-test.ts +416 -0
  74. package/mcp/mcp-oauth-provider.ts +369 -0
  75. package/mcp/mcp-oauth-provider.upstream-test.ts +518 -0
  76. package/mcp/mcp-panel.ts +829 -0
  77. package/mcp/mcp-setup-panel.ts +580 -0
  78. package/mcp/metadata-cache.ts +201 -0
  79. package/mcp/notify.ts +111 -0
  80. package/mcp/npx-resolver.ts +424 -0
  81. package/mcp/oauth-handler.ts +57 -0
  82. package/mcp/onboarding-state.ts +68 -0
  83. package/mcp/package.json +106 -0
  84. package/mcp/panel-keys.ts +37 -0
  85. package/mcp/proxy-modes.ts +949 -0
  86. package/mcp/resource-tools.ts +17 -0
  87. package/mcp/sampling-handler.ts +268 -0
  88. package/mcp/server-manager.ts +545 -0
  89. package/mcp/state.ts +41 -0
  90. package/mcp/tool-metadata.ts +216 -0
  91. package/mcp/tool-registrar.ts +46 -0
  92. package/mcp/tool-result-renderer.ts +161 -0
  93. package/mcp/types.ts +448 -0
  94. package/mcp/ui-resource-handler.ts +146 -0
  95. package/mcp/ui-server.ts +623 -0
  96. package/mcp/ui-session.ts +386 -0
  97. package/mcp/ui-stream-types.ts +89 -0
  98. package/mcp/utils.ts +129 -0
  99. package/mcp/vitest.config.ts +14 -0
  100. package/migrate.ts +258 -0
  101. package/notification.ts +218 -0
  102. package/notifications-log.ts +83 -0
  103. package/package.json +20 -3
  104. package/status.ts +140 -0
@@ -0,0 +1,303 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { UiResourceHandler } from "../ui-resource-handler.ts";
3
+ import { UrlElicitationRequiredError } from "@modelcontextprotocol/sdk/types.js";
4
+ import type { McpServerManager } from "../server-manager.ts";
5
+
6
+ // Mock the manager
7
+ function createMockManager(overrides: Partial<McpServerManager> = {}): McpServerManager {
8
+ return {
9
+ readResource: vi.fn(),
10
+ getConnection: vi.fn().mockReturnValue(null),
11
+ ...overrides,
12
+ } as unknown as McpServerManager;
13
+ }
14
+
15
+ describe("UiResourceHandler", () => {
16
+ describe("readUiResource", () => {
17
+ it("throws for non-ui:// URIs", async () => {
18
+ const manager = createMockManager();
19
+ const handler = new UiResourceHandler(manager);
20
+
21
+ await expect(handler.readUiResource("server", "https://example.com")).rejects.toThrow(
22
+ /URI must start with ui:\/\//
23
+ );
24
+ });
25
+
26
+ it("preserves URL-required errors for the outer tool adapter", async () => {
27
+ const error = new UrlElicitationRequiredError([{
28
+ mode: "url",
29
+ message: "Connect",
30
+ elicitationId: "connect-1",
31
+ url: "https://example.com/connect",
32
+ }]);
33
+ const manager = createMockManager({ readResource: vi.fn().mockRejectedValue(error) });
34
+ const handler = new UiResourceHandler(manager);
35
+
36
+ await expect(handler.readUiResource("server", "ui://test/widget")).rejects.toBe(error);
37
+ });
38
+
39
+ it("reads and returns HTML from text content", async () => {
40
+ const manager = createMockManager({
41
+ readResource: vi.fn().mockResolvedValue({
42
+ contents: [
43
+ {
44
+ uri: "ui://test/widget",
45
+ mimeType: "text/html",
46
+ text: "<h1>Hello</h1>",
47
+ },
48
+ ],
49
+ }),
50
+ });
51
+ const handler = new UiResourceHandler(manager);
52
+
53
+ const result = await handler.readUiResource("server", "ui://test/widget");
54
+
55
+ expect(result.uri).toBe("ui://test/widget");
56
+ expect(result.html).toBe("<h1>Hello</h1>");
57
+ expect(result.mimeType).toBe("text/html");
58
+ });
59
+
60
+ it("reads and decodes blob content", async () => {
61
+ const htmlContent = "<div>Blob content</div>";
62
+ const base64Content = Buffer.from(htmlContent).toString("base64");
63
+
64
+ const manager = createMockManager({
65
+ readResource: vi.fn().mockResolvedValue({
66
+ contents: [
67
+ {
68
+ uri: "ui://test/widget",
69
+ mimeType: "text/html",
70
+ blob: base64Content,
71
+ },
72
+ ],
73
+ }),
74
+ });
75
+ const handler = new UiResourceHandler(manager);
76
+
77
+ const result = await handler.readUiResource("server", "ui://test/widget");
78
+
79
+ expect(result.html).toBe(htmlContent);
80
+ });
81
+
82
+ it("throws for empty content", async () => {
83
+ const manager = createMockManager({
84
+ readResource: vi.fn().mockResolvedValue({
85
+ contents: [
86
+ {
87
+ uri: "ui://test/widget",
88
+ mimeType: "text/html",
89
+ text: " ",
90
+ },
91
+ ],
92
+ }),
93
+ });
94
+ const handler = new UiResourceHandler(manager);
95
+
96
+ await expect(handler.readUiResource("server", "ui://test/widget")).rejects.toThrow(
97
+ /content is empty/
98
+ );
99
+ });
100
+
101
+ it("throws for unsupported MIME type", async () => {
102
+ const manager = createMockManager({
103
+ readResource: vi.fn().mockResolvedValue({
104
+ contents: [
105
+ {
106
+ uri: "ui://test/widget",
107
+ mimeType: "application/json",
108
+ text: '{"key": "value"}',
109
+ },
110
+ ],
111
+ }),
112
+ });
113
+ const handler = new UiResourceHandler(manager);
114
+
115
+ await expect(handler.readUiResource("server", "ui://test/widget")).rejects.toThrow(
116
+ /unsupported MIME type/
117
+ );
118
+ });
119
+
120
+ it("accepts text/html;profile=mcp-app MIME type", async () => {
121
+ const manager = createMockManager({
122
+ readResource: vi.fn().mockResolvedValue({
123
+ contents: [
124
+ {
125
+ uri: "ui://test/widget",
126
+ mimeType: "text/html;profile=mcp-app",
127
+ text: "<app>content</app>",
128
+ },
129
+ ],
130
+ }),
131
+ });
132
+ const handler = new UiResourceHandler(manager);
133
+
134
+ const result = await handler.readUiResource("server", "ui://test/widget");
135
+
136
+ expect(result.html).toBe("<app>content</app>");
137
+ });
138
+
139
+ it("throws when no contents returned", async () => {
140
+ const manager = createMockManager({
141
+ readResource: vi.fn().mockResolvedValue({
142
+ contents: [],
143
+ }),
144
+ });
145
+ const handler = new UiResourceHandler(manager);
146
+
147
+ await expect(handler.readUiResource("server", "ui://test/widget")).rejects.toThrow(
148
+ "No contents returned for UI resource: ui://test/widget"
149
+ );
150
+ });
151
+
152
+ it("prefers content with matching URI", async () => {
153
+ const manager = createMockManager({
154
+ readResource: vi.fn().mockResolvedValue({
155
+ contents: [
156
+ {
157
+ uri: "ui://other/widget",
158
+ mimeType: "text/html",
159
+ text: "<h1>Wrong</h1>",
160
+ },
161
+ {
162
+ uri: "ui://test/widget",
163
+ mimeType: "text/html",
164
+ text: "<h1>Correct</h1>",
165
+ },
166
+ ],
167
+ }),
168
+ });
169
+ const handler = new UiResourceHandler(manager);
170
+
171
+ const result = await handler.readUiResource("server", "ui://test/widget");
172
+
173
+ expect(result.html).toBe("<h1>Correct</h1>");
174
+ });
175
+
176
+ it("falls back to first HTML content if no URI match", async () => {
177
+ const manager = createMockManager({
178
+ readResource: vi.fn().mockResolvedValue({
179
+ contents: [
180
+ {
181
+ mimeType: "application/json",
182
+ text: "{}",
183
+ },
184
+ {
185
+ mimeType: "text/html",
186
+ text: "<h1>HTML</h1>",
187
+ },
188
+ ],
189
+ }),
190
+ });
191
+ const handler = new UiResourceHandler(manager);
192
+
193
+ const result = await handler.readUiResource("server", "ui://test/widget");
194
+
195
+ expect(result.html).toBe("<h1>HTML</h1>");
196
+ });
197
+
198
+ it("extracts CSP meta from content _meta", async () => {
199
+ const manager = createMockManager({
200
+ readResource: vi.fn().mockResolvedValue({
201
+ contents: [
202
+ {
203
+ uri: "ui://test/widget",
204
+ mimeType: "text/html",
205
+ text: "<h1>Content</h1>",
206
+ _meta: {
207
+ ui: {
208
+ csp: {
209
+ scriptDomains: ["'self'", "cdn.example.com"],
210
+ styleDomains: ["'self'"],
211
+ },
212
+ },
213
+ },
214
+ },
215
+ ],
216
+ }),
217
+ });
218
+ const handler = new UiResourceHandler(manager);
219
+
220
+ const result = await handler.readUiResource("server", "ui://test/widget");
221
+
222
+ expect(result.meta.csp).toEqual({
223
+ scriptDomains: ["'self'", "cdn.example.com"],
224
+ styleDomains: ["'self'"],
225
+ });
226
+ });
227
+
228
+ it("extracts permissions meta", async () => {
229
+ const manager = createMockManager({
230
+ readResource: vi.fn().mockResolvedValue({
231
+ contents: [
232
+ {
233
+ uri: "ui://test/widget",
234
+ mimeType: "text/html",
235
+ text: "<h1>Content</h1>",
236
+ _meta: {
237
+ ui: {
238
+ permissions: {
239
+ camera: {},
240
+ microphone: {},
241
+ },
242
+ },
243
+ },
244
+ },
245
+ ],
246
+ }),
247
+ });
248
+ const handler = new UiResourceHandler(manager);
249
+
250
+ const result = await handler.readUiResource("server", "ui://test/widget");
251
+
252
+ expect(result.meta.permissions).toEqual({
253
+ camera: {},
254
+ microphone: {},
255
+ });
256
+ });
257
+
258
+ it("extracts domain and prefersBorder meta", async () => {
259
+ const manager = createMockManager({
260
+ readResource: vi.fn().mockResolvedValue({
261
+ contents: [
262
+ {
263
+ uri: "ui://test/widget",
264
+ mimeType: "text/html",
265
+ text: "<h1>Content</h1>",
266
+ _meta: {
267
+ ui: {
268
+ domain: "example.com",
269
+ prefersBorder: true,
270
+ },
271
+ },
272
+ },
273
+ ],
274
+ }),
275
+ });
276
+ const handler = new UiResourceHandler(manager);
277
+
278
+ const result = await handler.readUiResource("server", "ui://test/widget");
279
+
280
+ expect(result.meta.domain).toBe("example.com");
281
+ expect(result.meta.prefersBorder).toBe(true);
282
+ });
283
+
284
+ it("throws when content has no text or blob", async () => {
285
+ const manager = createMockManager({
286
+ readResource: vi.fn().mockResolvedValue({
287
+ contents: [
288
+ {
289
+ uri: "ui://test/widget",
290
+ mimeType: "text/html",
291
+ // No text or blob
292
+ },
293
+ ],
294
+ }),
295
+ });
296
+ const handler = new UiResourceHandler(manager);
297
+
298
+ await expect(handler.readUiResource("server", "ui://test/widget")).rejects.toThrow(
299
+ "did not include text or blob content"
300
+ );
301
+ });
302
+ });
303
+ });