@isolo/trinity 1.0.3 → 1.0.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isolo/trinity",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A Bun-based MCP server framework with dynamic tool loading, hot reload, and stdio/HTTP modes",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/Server.ts CHANGED
@@ -13,6 +13,7 @@ import { Config } from "./Config.js";
13
13
  import { Logger } from "./Logger.js";
14
14
  import { Tools } from "./Tools.js";
15
15
  import safeJsonStringify from "json-stringify-safe";
16
+ import { ZodType } from "zod";
16
17
 
17
18
  export class Server {
18
19
  private static instance: Server | null = null;
@@ -51,6 +52,8 @@ export class Server {
51
52
  port: this.config.port,
52
53
  });
53
54
 
55
+ await this.tools.loadTools();
56
+
54
57
  this.tools.startWatching();
55
58
 
56
59
  const transport = await this.createTransport();
@@ -88,32 +91,53 @@ export class Server {
88
91
  );
89
92
  }
90
93
 
94
+ safeZodToJson(schema: ZodType) {
95
+ try {
96
+ // Validate it's a proper Zod schema
97
+ if (!schema || typeof schema !== "object" || !("_zod" in schema)) {
98
+ this.logger.error("Invalid Zod schema: missing _zod property", {
99
+ schemaType: typeof schema,
100
+ hasZodProp: schema ? "_zod" in schema : false,
101
+ });
102
+ return {
103
+ type: "object",
104
+ properties: {},
105
+ description: "Invalid Zod schema: not a valid Zod schema object",
106
+ };
107
+ }
108
+ return schema.toJSONSchema();
109
+ } catch (err) {
110
+ this.logger.error("Failed to convert Zod schema to JSON Schema", {
111
+ error: (err as Error).message,
112
+ stack: (err as Error).stack,
113
+ });
114
+ return {
115
+ type: "object",
116
+ properties: {},
117
+ description:
118
+ "Failed to convert zod schema to JSON Schema, pls check your zod schema maybe it have some problems",
119
+ };
120
+ }
121
+ }
122
+
91
123
  private async handleListTools() {
92
124
  this.logger.debug("Handling list_tools request");
93
125
 
94
126
  const toolDefs = await this.tools.loadTools();
95
127
 
96
128
  const tools = toolDefs.map((tool) => {
97
- // Convert Zod schema to JSON Schema for the MCP protocol
98
- const jsonSchema = tool.inputSchema.toJSONSchema();
99
-
100
129
  this.logger.debug(`Generated JSON schema for tool`, {
101
130
  name: tool.name,
102
- jsonSchema,
103
131
  });
104
132
 
105
133
  return {
106
134
  name: tool.name,
107
135
  description: tool.description,
108
- inputSchema: jsonSchema as {
109
- type: "object";
110
- properties?: Record<string, unknown>;
111
- required?: string[];
112
- },
136
+ inputSchema: this.safeZodToJson(tool.inputSchema),
113
137
  };
114
138
  });
115
139
 
116
- this.logger.debug(`Returning ${tools.length} tools`);
140
+ this.logger.debug("Returning tools", { count: tools.length });
117
141
  return { tools };
118
142
  }
119
143
 
@@ -121,16 +145,16 @@ export class Server {
121
145
  params: { name: string; arguments?: Record<string, unknown> };
122
146
  }) {
123
147
  const { name, arguments: args } = request.params;
124
- this.logger.info(`Calling tool: ${name}`);
148
+ this.logger.info("Calling tool", { name });
125
149
 
126
150
  try {
127
151
  const tool = await this.tools.loadTool(name);
128
152
  const raw = await tool.execute(args || {});
129
- this.logger.debug(`Tool ${name} executed successfully`, { raw });
153
+ this.logger.debug("Tool executed successfully", { name, raw });
130
154
 
131
155
  return this.formatToolResult(raw);
132
156
  } catch (err) {
133
- this.logger.error(`Tool ${name} execution failed`, err as Error);
157
+ this.logger.error("Tool execution failed", { name, error: err as Error });
134
158
  return {
135
159
  content: [
136
160
  {
@@ -185,7 +209,7 @@ export class Server {
185
209
  return new StdioServerTransport();
186
210
  }
187
211
 
188
- this.logger.info(`Starting HTTP server on port ${this.config.port}`);
212
+ this.logger.info("Starting HTTP server", { port: this.config.port });
189
213
  return this.createHttpTransport();
190
214
  }
191
215
 
@@ -217,7 +241,7 @@ export class Server {
217
241
  },
218
242
  });
219
243
 
220
- this.logger.info(`HTTP server listening on port ${this.config.port}`);
244
+ this.logger.info("HTTP server listening", { port: this.config.port });
221
245
  return transport;
222
246
  }
223
247
 
@@ -234,13 +258,13 @@ export class Server {
234
258
  },
235
259
  ];
236
260
 
237
- this.logger.debug(`Returning ${resources.length} resources`);
261
+ this.logger.debug("Returning resources", { count: resources.length });
238
262
  return { resources };
239
263
  }
240
264
 
241
265
  private async handleReadResource(request: { params: { uri: string } }) {
242
266
  const { uri } = request.params;
243
- this.logger.info(`Reading resource: ${uri}`);
267
+ this.logger.info("Reading resource", { uri });
244
268
 
245
269
  if (uri === "trinity://docs/tools") {
246
270
  try {
@@ -257,7 +281,10 @@ export class Server {
257
281
  ],
258
282
  };
259
283
  } catch (err) {
260
- this.logger.error(`Failed to read resource ${uri}`, err as Error);
284
+ this.logger.error("Failed to read resource", {
285
+ uri,
286
+ error: err as Error,
287
+ });
261
288
  throw new Error(`Failed to read resource: ${(err as Error).message}`);
262
289
  }
263
290
  }
package/src/Tools.ts CHANGED
@@ -32,20 +32,26 @@ export class Tools {
32
32
 
33
33
  async loadTools(): Promise<ToolDefinition[]> {
34
34
  const files = await this.resolveToolFiles();
35
- this.logger.debug(`Resolved ${files.length} tool files`);
35
+ this.logger.debug("Resolved tool files", { count: files.length });
36
+
37
+ // Load all tools in parallel
38
+ const toolPromises = files.map((filePath) =>
39
+ this.loadToolFromFile(filePath),
40
+ );
41
+ const results = await Promise.allSettled(toolPromises);
36
42
 
37
43
  const tools: ToolDefinition[] = [];
38
44
 
39
- for (const filePath of files) {
40
- try {
41
- const tool = await this.loadToolFromFile(filePath);
42
- tools.push(tool);
43
- } catch (err: any) {
44
- this.logger.error(`Failed to load tool from ${filePath}`, {
45
- err: err.message,
45
+ results.forEach((result, index) => {
46
+ if (result.status === "fulfilled") {
47
+ tools.push(result.value);
48
+ } else {
49
+ this.logger.error(`Failed to load tool`, {
50
+ file: files[index],
51
+ err: result.reason?.message || result.reason,
46
52
  });
47
53
  }
48
- }
54
+ });
49
55
 
50
56
  return tools;
51
57
  }
@@ -69,7 +75,7 @@ export class Tools {
69
75
 
70
76
  startWatching(): void {
71
77
  const targetFolder = this.config.targetFolder;
72
- this.logger.info(`Watching for changes in ${targetFolder}`);
78
+ this.logger.info("Watching for changes", { targetFolder });
73
79
 
74
80
  this.watcher = fs.watch(
75
81
  targetFolder,
@@ -169,7 +175,7 @@ export class Tools {
169
175
  return;
170
176
  }
171
177
 
172
- this.logger.info(`File ${event}: ${filename}`);
178
+ this.logger.info("File changed", { event, filename });
173
179
 
174
180
  if (this.onChangeCallback) {
175
181
  this.onChangeCallback();
package/src/index.ts CHANGED
@@ -9,8 +9,10 @@ const logger = new Logger("main");
9
9
  try {
10
10
  Config.getInstance().parse(process.argv.slice(2));
11
11
  } catch (err) {
12
- console.error((err as Error).message);
13
- console.error("Usage: bun --hot src/index.ts --target <path> [--glob <pattern>] [--port <number>]");
12
+ logger.error((err as Error).message);
13
+ logger.error(
14
+ "Usage: bun --hot src/index.ts --target <path> [--glob <pattern>] [--port <number>]",
15
+ );
14
16
  process.exit(1);
15
17
  }
16
18