@stacksjs/ai 0.70.55 → 0.70.57

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 (46) hide show
  1. package/package.json +10 -7
  2. package/src/agents/claude/index.ts +0 -310
  3. package/src/agents/index.ts +0 -7
  4. package/src/buddy.ts +0 -619
  5. package/src/drivers/anthropic/index.ts +0 -430
  6. package/src/drivers/claude-agent-sdk/index.ts +0 -370
  7. package/src/drivers/index.ts +0 -14
  8. package/src/drivers/ollama/index.ts +0 -514
  9. package/src/drivers/openai/index.ts +0 -529
  10. package/src/image.ts +0 -607
  11. package/src/index.ts +0 -53
  12. package/src/mcp.ts +0 -658
  13. package/src/personalization.ts +0 -490
  14. package/src/search.ts +0 -555
  15. package/src/text.ts +0 -79
  16. package/src/types.ts +0 -229
  17. package/src/utils/client-bedrock-runtime.ts +0 -51
  18. package/src/utils/client-bedrock.ts +0 -75
  19. package/src/utils/model-access.ts +0 -32
  20. package/src/utils/retry.ts +0 -124
  21. package/src/utils/tokens.ts +0 -159
  22. package/src/utils/usage.ts +0 -98
  23. package/src/utils/vision.ts +0 -119
  24. /package/dist/{src/agents → agents}/claude/index.d.ts +0 -0
  25. /package/dist/{src/agents → agents}/index.d.ts +0 -0
  26. /package/dist/{src/buddy.d.ts → buddy.d.ts} +0 -0
  27. /package/dist/{src/drivers → drivers}/anthropic/index.d.ts +0 -0
  28. /package/dist/{src/drivers → drivers}/claude-agent-sdk/index.d.ts +0 -0
  29. /package/dist/{src/drivers → drivers}/index.d.ts +0 -0
  30. /package/dist/{src/drivers → drivers}/ollama/index.d.ts +0 -0
  31. /package/dist/{src/drivers → drivers}/openai/index.d.ts +0 -0
  32. /package/dist/{src/image.d.ts → image.d.ts} +0 -0
  33. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  34. /package/dist/{src/index.js → index.js} +0 -0
  35. /package/dist/{src/mcp.d.ts → mcp.d.ts} +0 -0
  36. /package/dist/{src/personalization.d.ts → personalization.d.ts} +0 -0
  37. /package/dist/{src/search.d.ts → search.d.ts} +0 -0
  38. /package/dist/{src/text.d.ts → text.d.ts} +0 -0
  39. /package/dist/{src/types.d.ts → types.d.ts} +0 -0
  40. /package/dist/{src/utils → utils}/client-bedrock-runtime.d.ts +0 -0
  41. /package/dist/{src/utils → utils}/client-bedrock.d.ts +0 -0
  42. /package/dist/{src/utils → utils}/model-access.d.ts +0 -0
  43. /package/dist/{src/utils → utils}/retry.d.ts +0 -0
  44. /package/dist/{src/utils → utils}/tokens.d.ts +0 -0
  45. /package/dist/{src/utils → utils}/usage.d.ts +0 -0
  46. /package/dist/{src/utils → utils}/vision.d.ts +0 -0
package/src/mcp.ts DELETED
@@ -1,658 +0,0 @@
1
- /**
2
- * MCP (Model Context Protocol) Module
3
- *
4
- * Implements an MCP client for connecting to MCP servers, discovering tools,
5
- * and invoking them through AI providers. This enables AI models to interact
6
- * with external services via a standardized protocol.
7
- *
8
- * @see https://modelcontextprotocol.io
9
- */
10
-
11
- // ============================================================================
12
- // Types
13
- // ============================================================================
14
-
15
- export interface MCPServerConfig {
16
- name: string
17
- transport: MCPTransport
18
- capabilities?: MCPCapabilities
19
- }
20
-
21
- export type MCPTransport =
22
- | { type: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }
23
- | { type: 'sse'; url: string; headers?: Record<string, string> }
24
- | { type: 'streamable-http'; url: string; headers?: Record<string, string> }
25
-
26
- export interface MCPCapabilities {
27
- tools?: boolean
28
- resources?: boolean
29
- prompts?: boolean
30
- }
31
-
32
- export interface MCPTool {
33
- name: string
34
- description: string
35
- inputSchema: Record<string, unknown>
36
- }
37
-
38
- export interface MCPResource {
39
- uri: string
40
- name: string
41
- description?: string
42
- mimeType?: string
43
- }
44
-
45
- export interface MCPPrompt {
46
- name: string
47
- description?: string
48
- arguments?: Array<{
49
- name: string
50
- description?: string
51
- required?: boolean
52
- }>
53
- }
54
-
55
- export interface MCPToolCallResult {
56
- content: Array<{
57
- type: 'text' | 'image' | 'resource'
58
- text?: string
59
- data?: string
60
- mimeType?: string
61
- }>
62
- isError?: boolean
63
- }
64
-
65
- export interface MCPResourceContent {
66
- uri: string
67
- mimeType?: string
68
- text?: string
69
- blob?: string
70
- }
71
-
72
- // ============================================================================
73
- // MCP Client
74
- // ============================================================================
75
-
76
- /**
77
- * MCP Client for connecting to Model Context Protocol servers.
78
- *
79
- * Supports stdio and SSE/streamable-http transports.
80
- * Provides tool discovery, invocation, resource reading, and prompt listing.
81
- */
82
- export class MCPClient {
83
- private config: MCPServerConfig
84
- private process: ReturnType<typeof import('bun').spawn> | null = null
85
- private requestId = 0
86
- private pendingRequests = new Map<number, { resolve: (value: any) => void; reject: (error: Error) => void }>()
87
- private tools: MCPTool[] = []
88
- private resources: MCPResource[] = []
89
- private prompts: MCPPrompt[] = []
90
- private initialized = false
91
- private buffer = ''
92
-
93
- constructor(config: MCPServerConfig) {
94
- this.config = config
95
- }
96
-
97
- /**
98
- * Connect to the MCP server and initialize the session.
99
- */
100
- async connect(): Promise<void> {
101
- if (this.config.transport.type === 'stdio') {
102
- await this.connectStdio()
103
- }
104
- else if (this.config.transport.type === 'sse' || this.config.transport.type === 'streamable-http') {
105
- await this.connectHTTP()
106
- }
107
- else {
108
- throw new Error(`Unsupported transport type: ${(this.config.transport as any).type}`)
109
- }
110
-
111
- this.initialized = true
112
- }
113
-
114
- private async connectStdio(): Promise<void> {
115
- const transport = this.config.transport as { type: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }
116
- const { spawn } = await import('bun')
117
-
118
- this.process = spawn([transport.command, ...(transport.args || [])], {
119
- stdin: 'pipe',
120
- stdout: 'pipe',
121
- stderr: 'pipe',
122
- env: { ...process.env, ...transport.env },
123
- }) as any
124
-
125
- // Start reading stdout for responses
126
- this.readStdout()
127
-
128
- // Send initialize request
129
- await this.sendRequest('initialize', {
130
- protocolVersion: '2024-11-05',
131
- capabilities: {},
132
- clientInfo: { name: 'stacks-ai', version: '1.0.0' },
133
- })
134
-
135
- // Send initialized notification
136
- await this.sendNotification('notifications/initialized', {})
137
-
138
- // Discover capabilities
139
- await this.discoverCapabilities()
140
- }
141
-
142
- private async connectHTTP(): Promise<void> {
143
- const transport = this.config.transport as { type: 'sse' | 'streamable-http'; url: string; headers?: Record<string, string> }
144
-
145
- // Send initialize via HTTP
146
- const response = await fetch(transport.url, {
147
- method: 'POST',
148
- headers: {
149
- 'Content-Type': 'application/json',
150
- ...transport.headers,
151
- },
152
- body: JSON.stringify({
153
- jsonrpc: '2.0',
154
- id: this.nextId(),
155
- method: 'initialize',
156
- params: {
157
- protocolVersion: '2024-11-05',
158
- capabilities: {},
159
- clientInfo: { name: 'stacks-ai', version: '1.0.0' },
160
- },
161
- }),
162
- })
163
-
164
- if (!response.ok) {
165
- const error = await response.text()
166
- throw new Error(`MCP HTTP connection error: ${error}`)
167
- }
168
-
169
- await this.discoverCapabilities()
170
- }
171
-
172
- private async readStdout(): Promise<void> {
173
- if (!this.process) return
174
-
175
- const reader = (this.process as any).stdout?.getReader()
176
- if (!reader) return
177
-
178
- const decoder = new TextDecoder()
179
-
180
- try {
181
- while (true) {
182
- const { done, value } = await reader.read()
183
- if (done) break
184
-
185
- this.buffer += decoder.decode(value, { stream: true })
186
- this.processBuffer()
187
- }
188
- }
189
- catch {
190
- // Process exited
191
- }
192
- }
193
-
194
- private processBuffer(): void {
195
- const lines = this.buffer.split('\n')
196
- this.buffer = lines.pop() || ''
197
-
198
- for (const line of lines) {
199
- if (!line.trim()) continue
200
-
201
- try {
202
- const message = JSON.parse(line)
203
- if (message.id !== undefined && this.pendingRequests.has(message.id)) {
204
- const pending = this.pendingRequests.get(message.id)!
205
- this.pendingRequests.delete(message.id)
206
-
207
- if (message.error) {
208
- pending.reject(new Error(message.error.message || 'MCP error'))
209
- }
210
- else {
211
- pending.resolve(message.result)
212
- }
213
- }
214
- }
215
- catch {
216
- // Skip invalid JSON
217
- }
218
- }
219
- }
220
-
221
- private nextId(): number {
222
- return ++this.requestId
223
- }
224
-
225
- private async sendRequest(method: string, params: Record<string, unknown> = {}): Promise<any> {
226
- const id = this.nextId()
227
-
228
- const message = JSON.stringify({
229
- jsonrpc: '2.0',
230
- id,
231
- method,
232
- params,
233
- }) + '\n'
234
-
235
- if (this.config.transport.type === 'stdio' && this.process) {
236
- const writer = (this.process as any).stdin?.getWriter()
237
- if (writer) {
238
- await writer.write(new TextEncoder().encode(message))
239
- writer.releaseLock()
240
- }
241
-
242
- return new Promise((resolve, reject) => {
243
- const timeout = setTimeout(() => {
244
- this.pendingRequests.delete(id)
245
- reject(new Error(`MCP request timeout: ${method}`))
246
- }, 30000)
247
-
248
- this.pendingRequests.set(id, {
249
- resolve: (value: any) => {
250
- clearTimeout(timeout)
251
- resolve(value)
252
- },
253
- reject: (error: Error) => {
254
- clearTimeout(timeout)
255
- reject(error)
256
- },
257
- })
258
- })
259
- }
260
-
261
- if (this.config.transport.type === 'sse' || this.config.transport.type === 'streamable-http') {
262
- const transport = this.config.transport as { url: string; headers?: Record<string, string> }
263
-
264
- const response = await fetch(transport.url, {
265
- method: 'POST',
266
- headers: {
267
- 'Content-Type': 'application/json',
268
- ...transport.headers,
269
- },
270
- body: message,
271
- })
272
-
273
- if (!response.ok) {
274
- const error = await response.text()
275
- throw new Error(`MCP request error: ${error}`)
276
- }
277
-
278
- const result = (await response.json()) as any
279
- if (result.error) {
280
- throw new Error(result.error.message || 'MCP error')
281
- }
282
- return result.result
283
- }
284
-
285
- throw new Error('No transport available')
286
- }
287
-
288
- private async sendNotification(method: string, params: Record<string, unknown> = {}): Promise<void> {
289
- const message = JSON.stringify({
290
- jsonrpc: '2.0',
291
- method,
292
- params,
293
- }) + '\n'
294
-
295
- if (this.config.transport.type === 'stdio' && this.process) {
296
- const writer = (this.process as any).stdin?.getWriter()
297
- if (writer) {
298
- await writer.write(new TextEncoder().encode(message))
299
- writer.releaseLock()
300
- }
301
- }
302
- }
303
-
304
- private async discoverCapabilities(): Promise<void> {
305
- const capabilities = this.config.capabilities || { tools: true, resources: true, prompts: true }
306
-
307
- if (capabilities.tools !== false) {
308
- try {
309
- const result = await this.sendRequest('tools/list')
310
- this.tools = result?.tools || []
311
- }
312
- catch {
313
- this.tools = []
314
- }
315
- }
316
-
317
- if (capabilities.resources !== false) {
318
- try {
319
- const result = await this.sendRequest('resources/list')
320
- this.resources = result?.resources || []
321
- }
322
- catch {
323
- this.resources = []
324
- }
325
- }
326
-
327
- if (capabilities.prompts !== false) {
328
- try {
329
- const result = await this.sendRequest('prompts/list')
330
- this.prompts = result?.prompts || []
331
- }
332
- catch {
333
- this.prompts = []
334
- }
335
- }
336
- }
337
-
338
- // ========================================================================
339
- // Public API
340
- // ========================================================================
341
-
342
- /**
343
- * List available tools from the MCP server.
344
- */
345
- listTools(): MCPTool[] {
346
- return this.tools
347
- }
348
-
349
- /**
350
- * List available resources from the MCP server.
351
- */
352
- listResources(): MCPResource[] {
353
- return this.resources
354
- }
355
-
356
- /**
357
- * List available prompts from the MCP server.
358
- */
359
- listPrompts(): MCPPrompt[] {
360
- return this.prompts
361
- }
362
-
363
- /**
364
- * Call a tool on the MCP server.
365
- */
366
- async callTool(name: string, args: Record<string, unknown> = {}): Promise<MCPToolCallResult> {
367
- if (!this.initialized) {
368
- throw new Error('MCP client not connected. Call connect() first.')
369
- }
370
-
371
- const result = await this.sendRequest('tools/call', { name, arguments: args })
372
- return result as MCPToolCallResult
373
- }
374
-
375
- /**
376
- * Read a resource from the MCP server.
377
- */
378
- async readResource(uri: string): Promise<MCPResourceContent> {
379
- if (!this.initialized) {
380
- throw new Error('MCP client not connected. Call connect() first.')
381
- }
382
-
383
- const result = await this.sendRequest('resources/read', { uri })
384
- const contents = result?.contents?.[0]
385
- return {
386
- uri: contents?.uri || uri,
387
- mimeType: contents?.mimeType,
388
- text: contents?.text,
389
- blob: contents?.blob,
390
- }
391
- }
392
-
393
- /**
394
- * Get a prompt from the MCP server with arguments filled in.
395
- */
396
- async getPrompt(name: string, args: Record<string, string> = {}): Promise<{
397
- description?: string
398
- messages: Array<{ role: string; content: { type: string; text: string } }>
399
- }> {
400
- if (!this.initialized) {
401
- throw new Error('MCP client not connected. Call connect() first.')
402
- }
403
-
404
- return this.sendRequest('prompts/get', { name, arguments: args })
405
- }
406
-
407
- /**
408
- * Convert MCP tools to the format expected by AI providers (e.g., Anthropic tool_use).
409
- */
410
- toAnthropicTools(): Array<{ name: string; description: string; input_schema: Record<string, unknown> }> {
411
- return this.tools.map(tool => ({
412
- name: tool.name,
413
- description: tool.description,
414
- input_schema: tool.inputSchema,
415
- }))
416
- }
417
-
418
- /**
419
- * Convert MCP tools to OpenAI function calling format.
420
- */
421
- toOpenAITools(): Array<{ type: 'function'; function: { name: string; description: string; parameters: Record<string, unknown> } }> {
422
- return this.tools.map(tool => ({
423
- type: 'function' as const,
424
- function: {
425
- name: tool.name,
426
- description: tool.description,
427
- parameters: tool.inputSchema,
428
- },
429
- }))
430
- }
431
-
432
- /**
433
- * Disconnect from the MCP server.
434
- *
435
- * Reject every still-pending request before clearing them — otherwise
436
- * callers that `await client.callTool(...)` past a disconnect hang
437
- * forever waiting for a response that will never arrive (the timeout
438
- * inside sendRequest would have fired, but only if disconnect was
439
- * called less than 30s into a request).
440
- */
441
- async disconnect(): Promise<void> {
442
- if (this.process) {
443
- (this.process as any).kill()
444
- this.process = null
445
- }
446
-
447
- for (const pending of this.pendingRequests.values()) {
448
- try { pending.reject(new Error('MCP client disconnected')) } catch { /* ignore */ }
449
- }
450
- this.pendingRequests.clear()
451
-
452
- this.initialized = false
453
- this.tools = []
454
- this.resources = []
455
- this.prompts = []
456
- }
457
-
458
- /**
459
- * Check if the client is connected.
460
- */
461
- get isConnected(): boolean {
462
- return this.initialized
463
- }
464
-
465
- /**
466
- * Get the server name.
467
- */
468
- get serverName(): string {
469
- return this.config.name
470
- }
471
- }
472
-
473
- // ============================================================================
474
- // MCP Manager (Multi-Server)
475
- // ============================================================================
476
-
477
- /**
478
- * Manages connections to multiple MCP servers.
479
- */
480
- export class MCPManager {
481
- private clients = new Map<string, MCPClient>()
482
-
483
- /**
484
- * Add and connect to an MCP server.
485
- */
486
- async addServer(config: MCPServerConfig): Promise<MCPClient> {
487
- if (this.clients.has(config.name)) {
488
- throw new Error(`MCP server already registered: ${config.name}`)
489
- }
490
-
491
- const client = new MCPClient(config)
492
- await client.connect()
493
- this.clients.set(config.name, client)
494
- return client
495
- }
496
-
497
- /**
498
- * Remove and disconnect from an MCP server.
499
- */
500
- async removeServer(name: string): Promise<void> {
501
- const client = this.clients.get(name)
502
- if (client) {
503
- await client.disconnect()
504
- this.clients.delete(name)
505
- }
506
- }
507
-
508
- /**
509
- * Get a connected MCP client by server name.
510
- */
511
- getServer(name: string): MCPClient | undefined {
512
- return this.clients.get(name)
513
- }
514
-
515
- /**
516
- * List all connected server names.
517
- */
518
- listServers(): string[] {
519
- return Array.from(this.clients.keys())
520
- }
521
-
522
- /**
523
- * Get all tools from all connected servers.
524
- * Tool names are prefixed with the server name to avoid conflicts.
525
- */
526
- getAllTools(): Array<MCPTool & { serverName: string }> {
527
- const allTools: Array<MCPTool & { serverName: string }> = []
528
-
529
- for (const [name, client] of this.clients) {
530
- for (const tool of client.listTools()) {
531
- allTools.push({ ...tool, serverName: name })
532
- }
533
- }
534
-
535
- return allTools
536
- }
537
-
538
- /**
539
- * Call a tool, routing to the correct server.
540
- * Tool name format: "serverName/toolName" or just "toolName" if unique.
541
- */
542
- async callTool(toolPath: string, args: Record<string, unknown> = {}): Promise<MCPToolCallResult> {
543
- if (toolPath.includes('/')) {
544
- const [serverName, toolName] = toolPath.split('/', 2)
545
- const client = this.clients.get(serverName)
546
- if (!client) throw new Error(`MCP server not found: ${serverName}`)
547
- return client.callTool(toolName, args)
548
- }
549
-
550
- // Search all servers for the tool
551
- for (const [, client] of this.clients) {
552
- const tools = client.listTools()
553
- if (tools.some(t => t.name === toolPath)) {
554
- return client.callTool(toolPath, args)
555
- }
556
- }
557
-
558
- throw new Error(`MCP tool not found: ${toolPath}`)
559
- }
560
-
561
- /**
562
- * Convert all tools to Anthropic format with server-prefixed names.
563
- */
564
- toAnthropicTools(): Array<{ name: string; description: string; input_schema: Record<string, unknown> }> {
565
- const tools: Array<{ name: string; description: string; input_schema: Record<string, unknown> }> = []
566
-
567
- for (const [serverName, client] of this.clients) {
568
- for (const tool of client.listTools()) {
569
- tools.push({
570
- name: `${serverName}__${tool.name}`,
571
- description: `[${serverName}] ${tool.description}`,
572
- input_schema: tool.inputSchema,
573
- })
574
- }
575
- }
576
-
577
- return tools
578
- }
579
-
580
- /**
581
- * Convert all tools to OpenAI format with server-prefixed names.
582
- */
583
- toOpenAITools(): Array<{ type: 'function'; function: { name: string; description: string; parameters: Record<string, unknown> } }> {
584
- const tools: Array<{ type: 'function'; function: { name: string; description: string; parameters: Record<string, unknown> } }> = []
585
-
586
- for (const [serverName, client] of this.clients) {
587
- for (const tool of client.listTools()) {
588
- tools.push({
589
- type: 'function',
590
- function: {
591
- name: `${serverName}__${tool.name}`,
592
- description: `[${serverName}] ${tool.description}`,
593
- parameters: tool.inputSchema,
594
- },
595
- })
596
- }
597
- }
598
-
599
- return tools
600
- }
601
-
602
- /**
603
- * Disconnect from all servers.
604
- */
605
- async disconnectAll(): Promise<void> {
606
- const disconnects = Array.from(this.clients.values()).map(c => c.disconnect())
607
- await Promise.all(disconnects)
608
- this.clients.clear()
609
- }
610
- }
611
-
612
- // ============================================================================
613
- // Convenience Functions
614
- // ============================================================================
615
-
616
- /**
617
- * Create and connect to an MCP server using stdio transport.
618
- */
619
- export async function connectStdio(
620
- name: string,
621
- command: string,
622
- args?: string[],
623
- env?: Record<string, string>,
624
- ): Promise<MCPClient> {
625
- const client = new MCPClient({
626
- name,
627
- transport: { type: 'stdio', command, args, env },
628
- })
629
- await client.connect()
630
- return client
631
- }
632
-
633
- /**
634
- * Create and connect to an MCP server using HTTP transport.
635
- */
636
- export async function connectHTTP(
637
- name: string,
638
- url: string,
639
- headers?: Record<string, string>,
640
- ): Promise<MCPClient> {
641
- const client = new MCPClient({
642
- name,
643
- transport: { type: 'streamable-http', url, headers },
644
- })
645
- await client.connect()
646
- return client
647
- }
648
-
649
- // ============================================================================
650
- // Exports
651
- // ============================================================================
652
-
653
- export const mcp = {
654
- MCPClient,
655
- MCPManager,
656
- connectStdio,
657
- connectHTTP,
658
- }