@opentiny/next-sdk 0.1.5 → 0.1.7

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/WebMcpClient.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js'
2
2
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
3
3
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
4
+ import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js'
4
5
  import { z, ZodObject, ZodLiteral, ZodType } from 'zod'
5
6
  import {
6
7
  ElicitRequestSchema,
@@ -18,8 +19,9 @@ import {
18
19
  sseOptions,
19
20
  streamOptions,
20
21
  attemptConnection,
21
- createStreamProxy,
22
22
  createSseProxy,
23
+ createStreamProxy,
24
+ createSocketProxy,
23
25
  AuthClientProvider
24
26
  } from '@opentiny/next'
25
27
  import type {
@@ -60,7 +62,7 @@ export interface ClientConnectOptions {
60
62
  token?: string
61
63
  sessionId?: string
62
64
  authProvider?: AuthClientProvider
63
- type?: 'channel' | 'sse'
65
+ type?: 'channel' | 'sse' | 'stream' | 'socket'
64
66
  agent?: boolean
65
67
  onError?: (error: Error) => void
66
68
  onUnauthorized?: (connect: () => Promise<void>) => Promise<void>
@@ -126,7 +128,11 @@ export class WebMcpClient {
126
128
 
127
129
  const connectProxy = async () => {
128
130
  const { transport, sessionId } =
129
- type === 'sse' ? await createSseProxy(proxyOptions) : await createStreamProxy(proxyOptions)
131
+ type === 'sse'
132
+ ? await createSseProxy(proxyOptions)
133
+ : type === 'socket'
134
+ ? await createSocketProxy(proxyOptions)
135
+ : await createStreamProxy(proxyOptions)
130
136
 
131
137
  transport.onerror = async (error: Error) => {
132
138
  onError?.(error)
@@ -169,6 +175,12 @@ export class WebMcpClient {
169
175
  }
170
176
  }
171
177
 
178
+ if (type === 'socket') {
179
+ transport = new WebSocketClientTransport(new URL(`${url}?sessionId=${sessionId}&token=${token}`))
180
+ transport.sessionId = sessionId
181
+ await this.client.connect(transport)
182
+ }
183
+
172
184
  if (typeof transport === 'undefined') {
173
185
  if (authProvider) {
174
186
  const createTransport = () => new StreamableHTTPClientTransport(endpoint, { authProvider })
@@ -1,4 +1,4 @@
1
- import { streamText, stepCountIs, generateText } from 'ai'
1
+ import { streamText, stepCountIs, generateText, StreamTextResult } from 'ai'
2
2
  import { experimental_createMCPClient as createMCPClient, experimental_MCPClientConfig as MCPClientConfig } from 'ai'
3
3
  import type { ToolSet } from 'ai'
4
4
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
@@ -36,6 +36,10 @@ export class AgentModelProvider {
36
36
 
37
37
  /** chat 时,自动更新 所有的tools 后的事件 */
38
38
  onUpdatedTools: (() => void) | undefined
39
+ /** 内部报错时,抛出错误事件 */
40
+ onError: ((msg: string, err?: any) => void) | undefined
41
+
42
+ messages: any[] = []
39
43
 
40
44
  constructor({ llmConfig, mcpServers, llm }: IAgentModelProviderOption) {
41
45
  // 1、保存 mcpServer
@@ -73,7 +77,10 @@ export class AgentModelProvider {
73
77
  }
74
78
 
75
79
  return await createMCPClient({ transport: transport as MCPClientConfig['transport'] })
76
- } catch (error) {
80
+ } catch (error: unknown) {
81
+ if (this.onError) {
82
+ this.onError((error as Error)?.message || `Failed to create MCP client`, error)
83
+ }
77
84
  console.error(`Failed to create MCP client`, serverConfig, error)
78
85
  return null
79
86
  }
@@ -93,7 +100,11 @@ export class AgentModelProvider {
93
100
  this.mcpClients.map(async (client) => {
94
101
  try {
95
102
  return client ? await client?.tools?.() : null
96
- } catch (error) {
103
+ } catch (error: unknown) {
104
+ if (this.onError) {
105
+ this.onError((error as Error)?.message || `Failed to query tools`, error)
106
+ }
107
+ console.error(`Failed to query tools`, error)
97
108
  return null
98
109
  }
99
110
  })
@@ -104,8 +115,13 @@ export class AgentModelProvider {
104
115
  await Promise.all(
105
116
  this.mcpClients.map(async (client) => {
106
117
  try {
107
- client.close()
108
- } catch (error) {}
118
+ await client.close()
119
+ } catch (error: unknown) {
120
+ if (this.onError) {
121
+ this.onError((error as Error)?.message || `Failed to close client`, error)
122
+ }
123
+ console.error(`Failed to close client`, error)
124
+ }
109
125
  })
110
126
  )
111
127
  }
@@ -122,7 +138,7 @@ export class AgentModelProvider {
122
138
  }
123
139
 
124
140
  async insertMcpServer(mcpServer: McpServerConfig) {
125
- const find = this.mcpServers.find((item: any) => item.url === mcpServer.url)
141
+ const find = this.mcpServers.find((item: any) => 'url' in item && 'url' in mcpServer && item.url === mcpServer.url)
126
142
 
127
143
  if (!find) {
128
144
  this.mcpServers = [...this.mcpServers, mcpServer]
@@ -172,7 +188,7 @@ export class AgentModelProvider {
172
188
 
173
189
  async _chat(
174
190
  chatMethod: ChatMethodFn,
175
- { model, maxSteps = 5, ...options }: Parameters<typeof generateText>[0] & { maxSteps?: number }
191
+ { model, maxSteps = 5, ...options }: Parameters<typeof generateText>[0] & { maxSteps?: number; message?: string }
176
192
  ): Promise<any> {
177
193
  if (!this.llm) {
178
194
  throw new Error('LLM is not initialized')
@@ -183,20 +199,33 @@ export class AgentModelProvider {
183
199
  this.onUpdatedTools?.()
184
200
  }
185
201
 
186
- return chatMethod({
202
+ const chatOptions = {
187
203
  // @ts-ignore ProviderV2 是所有llm的父类, 在每一个具体的llm 类都有一个选择model的函数用法
188
204
  model: this.llm(model),
189
205
  stopWhen: stepCountIs(maxSteps),
190
206
  ...options,
191
207
  tools: this.tempMergeTools(options.tools) as ToolSet
208
+ }
209
+
210
+ if (options.message && !options.messages) {
211
+ this.messages.push({ role: 'user', content: options.message })
212
+ chatOptions.messages = [...this.messages]
213
+ }
214
+
215
+ const result = chatMethod(chatOptions)
216
+
217
+ ;(result as StreamTextResult<ToolSet, unknown>)?.response?.then((res: any) => {
218
+ this.messages.push(...res.messages)
192
219
  })
220
+
221
+ return result
193
222
  }
194
223
 
195
- async chat(options: Parameters<typeof generateText>[0] & { maxSteps?: number }): Promise<any> {
224
+ async chat(options: Parameters<typeof generateText>[0] & { maxSteps?: number; message?: string }): Promise<any> {
196
225
  return this._chat(generateText, options)
197
226
  }
198
227
 
199
- async chatStream(options: Parameters<typeof streamText>[0] & { maxSteps?: number }): Promise<any> {
228
+ async chatStream(options: Parameters<typeof streamText>[0] & { maxSteps?: number; message?: string }): Promise<any> {
200
229
  return this._chat(streamText, options as any)
201
230
  }
202
231
  }
package/agent/type.ts CHANGED
@@ -14,7 +14,7 @@ export interface IAgentModelProviderLlmConfig {
14
14
  }
15
15
 
16
16
  /** Mcp Server的配置对象 */
17
- export type McpServerConfig = { type: 'streamableHttp' | 'sse'; url: string } & { transport: MCPTransport }
17
+ export type McpServerConfig = { type: 'streamableHttp' | 'sse'; url: string } | { transport: MCPTransport }
18
18
 
19
19
  /** */
20
20
  export interface IAgentModelProviderOption {
@@ -18,7 +18,7 @@ export interface ClientConnectOptions {
18
18
  token?: string;
19
19
  sessionId?: string;
20
20
  authProvider?: AuthClientProvider;
21
- type?: 'channel' | 'sse';
21
+ type?: 'channel' | 'sse' | 'stream' | 'socket';
22
22
  agent?: boolean;
23
23
  onError?: (error: Error) => void;
24
24
  onUnauthorized?: (connect: () => Promise<void>) => Promise<void>;
@@ -10,8 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
11
11
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
12
12
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
13
+ import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
13
14
  import { ElicitRequestSchema, CallToolResultSchema, ListRootsRequestSchema, CreateMessageRequestSchema, LoggingMessageNotificationSchema, ToolListChangedNotificationSchema, ResourceUpdatedNotificationSchema, PromptListChangedNotificationSchema, ResourceListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
14
- import { MessageChannelClientTransport, sseOptions, streamOptions, attemptConnection, createStreamProxy, createSseProxy } from '@opentiny/next';
15
+ import { MessageChannelClientTransport, sseOptions, streamOptions, attemptConnection, createSseProxy, createStreamProxy, createSocketProxy } from '@opentiny/next';
15
16
  /**
16
17
  * An MCP client on top of a pluggable transport.
17
18
  * The client will automatically begin the initialization flow with the server when connect() is called.
@@ -56,7 +57,11 @@ export class WebMcpClient {
56
57
  let reconnect = false;
57
58
  let response;
58
59
  const connectProxy = () => __awaiter(this, void 0, void 0, function* () {
59
- const { transport, sessionId } = type === 'sse' ? yield createSseProxy(proxyOptions) : yield createStreamProxy(proxyOptions);
60
+ const { transport, sessionId } = type === 'sse'
61
+ ? yield createSseProxy(proxyOptions)
62
+ : type === 'socket'
63
+ ? yield createSocketProxy(proxyOptions)
64
+ : yield createStreamProxy(proxyOptions);
60
65
  transport.onerror = (error) => __awaiter(this, void 0, void 0, function* () {
61
66
  onError === null || onError === void 0 ? void 0 : onError(error);
62
67
  if (error.message === 'Unauthorized' && !reconnect) {
@@ -93,6 +98,11 @@ export class WebMcpClient {
93
98
  yield this.client.connect(transport);
94
99
  }
95
100
  }
101
+ if (type === 'socket') {
102
+ transport = new WebSocketClientTransport(new URL(`${url}?sessionId=${sessionId}&token=${token}`));
103
+ transport.sessionId = sessionId;
104
+ yield this.client.connect(transport);
105
+ }
96
106
  if (typeof transport === 'undefined') {
97
107
  if (authProvider) {
98
108
  const createTransport = () => new StreamableHTTPClientTransport(endpoint, { authProvider });
@@ -28,6 +28,9 @@ export declare class AgentModelProvider {
28
28
  autoUpdateTools: boolean;
29
29
  /** chat 时,自动更新 所有的tools 后的事件 */
30
30
  onUpdatedTools: (() => void) | undefined;
31
+ /** 内部报错时,抛出错误事件 */
32
+ onError: ((msg: string, err?: any) => void) | undefined;
33
+ messages: any[];
31
34
  constructor({ llmConfig, mcpServers, llm }: IAgentModelProviderOption);
32
35
  /** 创建一个 ai-sdk的 mcpClient, 创建失败则返回 Null */
33
36
  private _createOneClient;
@@ -46,12 +49,15 @@ export declare class AgentModelProvider {
46
49
  tempMergeTools(extraTool?: {}): Record<string, any>;
47
50
  _chat(chatMethod: ChatMethodFn, { model, maxSteps, ...options }: Parameters<typeof generateText>[0] & {
48
51
  maxSteps?: number;
52
+ message?: string;
49
53
  }): Promise<any>;
50
54
  chat(options: Parameters<typeof generateText>[0] & {
51
55
  maxSteps?: number;
56
+ message?: string;
52
57
  }): Promise<any>;
53
58
  chatStream(options: Parameters<typeof streamText>[0] & {
54
59
  maxSteps?: number;
60
+ message?: string;
55
61
  }): Promise<any>;
56
62
  }
57
63
  export {};
@@ -44,6 +44,7 @@ export class AgentModelProvider {
44
44
  this.ignoreToolnames = [];
45
45
  /** 在 chat 之前,自动更新 所有的tools */
46
46
  this.autoUpdateTools = true;
47
+ this.messages = [];
47
48
  // 1、保存 mcpServer
48
49
  this.mcpServers = mcpServers || [];
49
50
  // 2、保存 llm
@@ -82,6 +83,9 @@ export class AgentModelProvider {
82
83
  return yield createMCPClient({ transport: transport });
83
84
  }
84
85
  catch (error) {
86
+ if (this.onError) {
87
+ this.onError((error === null || error === void 0 ? void 0 : error.message) || `Failed to create MCP client`, error);
88
+ }
85
89
  console.error(`Failed to create MCP client`, serverConfig, error);
86
90
  return null;
87
91
  }
@@ -105,6 +109,10 @@ export class AgentModelProvider {
105
109
  return client ? yield ((_a = client === null || client === void 0 ? void 0 : client.tools) === null || _a === void 0 ? void 0 : _a.call(client)) : null;
106
110
  }
107
111
  catch (error) {
112
+ if (this.onError) {
113
+ this.onError((error === null || error === void 0 ? void 0 : error.message) || `Failed to query tools`, error);
114
+ }
115
+ console.error(`Failed to query tools`, error);
108
116
  return null;
109
117
  }
110
118
  })));
@@ -115,9 +123,14 @@ export class AgentModelProvider {
115
123
  return __awaiter(this, void 0, void 0, function* () {
116
124
  yield Promise.all(this.mcpClients.map((client) => __awaiter(this, void 0, void 0, function* () {
117
125
  try {
118
- client.close();
126
+ yield client.close();
127
+ }
128
+ catch (error) {
129
+ if (this.onError) {
130
+ this.onError((error === null || error === void 0 ? void 0 : error.message) || `Failed to close client`, error);
131
+ }
132
+ console.error(`Failed to close client`, error);
119
133
  }
120
- catch (error) { }
121
134
  })));
122
135
  });
123
136
  }
@@ -137,7 +150,7 @@ export class AgentModelProvider {
137
150
  insertMcpServer(mcpServer) {
138
151
  return __awaiter(this, void 0, void 0, function* () {
139
152
  var _a;
140
- const find = this.mcpServers.find((item) => item.url === mcpServer.url);
153
+ const find = this.mcpServers.find((item) => 'url' in item && 'url' in mcpServer && item.url === mcpServer.url);
141
154
  if (!find) {
142
155
  this.mcpServers = [...this.mcpServers, mcpServer];
143
156
  const client = yield this._createOneClient(mcpServer);
@@ -181,7 +194,7 @@ export class AgentModelProvider {
181
194
  }
182
195
  _chat(chatMethod, _a) {
183
196
  return __awaiter(this, void 0, void 0, function* () {
184
- var _b;
197
+ var _b, _c;
185
198
  var { model, maxSteps = 5 } = _a, options = __rest(_a, ["model", "maxSteps"]);
186
199
  if (!this.llm) {
187
200
  throw new Error('LLM is not initialized');
@@ -190,9 +203,18 @@ export class AgentModelProvider {
190
203
  yield this._createMpcTools();
191
204
  (_b = this.onUpdatedTools) === null || _b === void 0 ? void 0 : _b.call(this);
192
205
  }
193
- return chatMethod(Object.assign(Object.assign({
206
+ const chatOptions = Object.assign(Object.assign({
194
207
  // @ts-ignore ProviderV2 是所有llm的父类, 在每一个具体的llm 类都有一个选择model的函数用法
195
- model: this.llm(model), stopWhen: stepCountIs(maxSteps) }, options), { tools: this.tempMergeTools(options.tools) }));
208
+ model: this.llm(model), stopWhen: stepCountIs(maxSteps) }, options), { tools: this.tempMergeTools(options.tools) });
209
+ if (options.message && !options.messages) {
210
+ this.messages.push({ role: 'user', content: options.message });
211
+ chatOptions.messages = [...this.messages];
212
+ }
213
+ const result = chatMethod(chatOptions);
214
+ (_c = result === null || result === void 0 ? void 0 : result.response) === null || _c === void 0 ? void 0 : _c.then((res) => {
215
+ this.messages.push(...res.messages);
216
+ });
217
+ return result;
196
218
  });
197
219
  }
198
220
  chat(options) {
@@ -15,7 +15,7 @@ export interface IAgentModelProviderLlmConfig {
15
15
  export type McpServerConfig = {
16
16
  type: 'streamableHttp' | 'sse';
17
17
  url: string;
18
- } & {
18
+ } | {
19
19
  transport: MCPTransport;
20
20
  };
21
21
  /** */