@liendev/lien 0.11.0 → 0.12.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.
@@ -114,6 +114,73 @@ list_functions({
114
114
 
115
115
  ---
116
116
 
117
+ ## Input Validation & Error Handling
118
+
119
+ Lien uses Zod schemas for runtime type-safe validation of all tool inputs. This provides:
120
+ - **Automatic validation** of all parameters before tool execution
121
+ - **Rich error messages** with field-level feedback
122
+ - **Type safety** with full TypeScript inference
123
+ - **Consistent error structure** across all tools
124
+
125
+ ### Understanding Validation Errors
126
+
127
+ When you provide invalid parameters, you'll receive a structured error response:
128
+
129
+ ```json
130
+ {
131
+ "error": "Invalid parameters",
132
+ "code": "INVALID_INPUT",
133
+ "details": [
134
+ {
135
+ "field": "query",
136
+ "message": "Query must be at least 3 characters"
137
+ },
138
+ {
139
+ "field": "limit",
140
+ "message": "Limit cannot exceed 50"
141
+ }
142
+ ]
143
+ }
144
+ ```
145
+
146
+ ### Common Validation Rules
147
+
148
+ **semantic_search:**
149
+ - `query`: 3-500 characters (required)
150
+ - `limit`: 1-50 (default: 5)
151
+
152
+ **find_similar:**
153
+ - `code`: minimum 10 characters (required)
154
+ - `limit`: 1-20 (default: 5)
155
+
156
+ **get_file_context:**
157
+ - `filepath`: cannot be empty (required)
158
+ - `includeRelated`: boolean (default: true)
159
+
160
+ **list_functions:**
161
+ - `pattern`: optional regex string
162
+ - `language`: optional language filter
163
+
164
+ ### Error Codes
165
+
166
+ Lien uses structured error codes for programmatic error handling:
167
+
168
+ - `INVALID_INPUT` - Parameter validation failed
169
+ - `FILE_NOT_FOUND` - Requested file doesn't exist in index
170
+ - `INDEX_NOT_FOUND` - No index found (run `lien index`)
171
+ - `INDEX_CORRUPTED` - Index is corrupted (run `lien reindex`)
172
+ - `EMBEDDING_GENERATION_FAILED` - Embedding model failed (retryable)
173
+ - `INTERNAL_ERROR` - Unexpected internal error
174
+
175
+ ### Best Practices
176
+
177
+ 1. **Always provide required fields**: Check tool schemas for required parameters
178
+ 2. **Respect validation limits**: Don't exceed max values for `limit` parameters
179
+ 3. **Use descriptive queries**: Avoid very short or vague queries
180
+ 4. **Handle validation errors gracefully**: Parse error details to understand what went wrong
181
+
182
+ ---
183
+
117
184
  ## Workflow Patterns (FOLLOW THESE)
118
185
 
119
186
  ### Pattern 1: User Asks "Where is X?"
package/dist/index.js CHANGED
@@ -210,13 +210,20 @@ var init_migration = __esm({
210
210
  }
211
211
  });
212
212
 
213
+ // src/errors/codes.ts
214
+ var init_codes = __esm({
215
+ "src/errors/codes.ts"() {
216
+ "use strict";
217
+ }
218
+ });
219
+
213
220
  // src/errors/index.ts
214
221
  function wrapError(error, context, additionalContext) {
215
222
  const message = error instanceof Error ? error.message : String(error);
216
223
  const stack = error instanceof Error ? error.stack : void 0;
217
224
  const wrappedError = new LienError(
218
225
  `${context}: ${message}`,
219
- "WRAPPED_ERROR",
226
+ "INTERNAL_ERROR" /* INTERNAL_ERROR */,
220
227
  additionalContext
221
228
  );
222
229
  if (stack) {
@@ -231,32 +238,61 @@ var LienError, ConfigError, EmbeddingError, DatabaseError;
231
238
  var init_errors = __esm({
232
239
  "src/errors/index.ts"() {
233
240
  "use strict";
241
+ init_codes();
242
+ init_codes();
234
243
  LienError = class extends Error {
235
- constructor(message, code, context) {
244
+ constructor(message, code, context, severity = "medium", recoverable = true, retryable = false) {
236
245
  super(message);
237
246
  this.code = code;
238
247
  this.context = context;
248
+ this.severity = severity;
249
+ this.recoverable = recoverable;
250
+ this.retryable = retryable;
239
251
  this.name = "LienError";
240
252
  if (Error.captureStackTrace) {
241
253
  Error.captureStackTrace(this, this.constructor);
242
254
  }
243
255
  }
256
+ /**
257
+ * Serialize error to JSON for MCP responses
258
+ */
259
+ toJSON() {
260
+ return {
261
+ error: this.message,
262
+ code: this.code,
263
+ severity: this.severity,
264
+ recoverable: this.recoverable,
265
+ context: this.context
266
+ };
267
+ }
268
+ /**
269
+ * Check if this error is retryable
270
+ */
271
+ isRetryable() {
272
+ return this.retryable;
273
+ }
274
+ /**
275
+ * Check if this error is recoverable
276
+ */
277
+ isRecoverable() {
278
+ return this.recoverable;
279
+ }
244
280
  };
245
281
  ConfigError = class extends LienError {
246
282
  constructor(message, context) {
247
- super(message, "CONFIG_ERROR", context);
283
+ super(message, "CONFIG_INVALID" /* CONFIG_INVALID */, context, "medium", true, false);
248
284
  this.name = "ConfigError";
249
285
  }
250
286
  };
251
287
  EmbeddingError = class extends LienError {
252
288
  constructor(message, context) {
253
- super(message, "EMBEDDING_ERROR", context);
289
+ super(message, "EMBEDDING_GENERATION_FAILED" /* EMBEDDING_GENERATION_FAILED */, context, "high", true, true);
254
290
  this.name = "EmbeddingError";
255
291
  }
256
292
  };
257
293
  DatabaseError = class extends LienError {
258
294
  constructor(message, context) {
259
- super(message, "DATABASE_ERROR", context);
295
+ super(message, "INTERNAL_ERROR" /* INTERNAL_ERROR */, context, "high", true, true);
260
296
  this.name = "DatabaseError";
261
297
  }
262
298
  };
@@ -4082,82 +4118,85 @@ import { createRequire as createRequire3 } from "module";
4082
4118
  import { fileURLToPath as fileURLToPath4 } from "url";
4083
4119
  import { dirname as dirname3, join as join3 } from "path";
4084
4120
 
4121
+ // src/mcp/utils/zod-to-json-schema.ts
4122
+ import { zodToJsonSchema } from "zod-to-json-schema";
4123
+ function toMCPToolSchema(zodSchema, name, description) {
4124
+ return {
4125
+ name,
4126
+ description,
4127
+ inputSchema: zodToJsonSchema(zodSchema, {
4128
+ target: "jsonSchema7",
4129
+ $refStrategy: "none"
4130
+ })
4131
+ };
4132
+ }
4133
+
4134
+ // src/mcp/schemas/search.schema.ts
4135
+ import { z } from "zod";
4136
+ var SemanticSearchSchema = z.object({
4137
+ query: z.string().min(3, "Query must be at least 3 characters").max(500, "Query too long (max 500 characters)").describe(
4138
+ "Natural language description of what you're looking for.\n\nUse full sentences describing functionality, not exact names.\n\nGood examples:\n - 'handles user authentication'\n - 'validates email format'\n - 'processes payment transactions'\n\nBad examples:\n - 'auth' (too vague)\n - 'validateEmail' (use grep for exact names)"
4139
+ ),
4140
+ limit: z.number().int().min(1, "Limit must be at least 1").max(50, "Limit cannot exceed 50").default(5).describe(
4141
+ "Number of results to return.\n\nDefault: 5\nIncrease to 10-15 for broad exploration."
4142
+ )
4143
+ });
4144
+
4145
+ // src/mcp/schemas/similarity.schema.ts
4146
+ import { z as z2 } from "zod";
4147
+ var FindSimilarSchema = z2.object({
4148
+ code: z2.string().min(10, "Code snippet must be at least 10 characters").describe(
4149
+ "Code snippet to find similar implementations for.\n\nProvide a representative code sample that demonstrates the pattern you want to find similar examples of in the codebase."
4150
+ ),
4151
+ limit: z2.number().int().min(1, "Limit must be at least 1").max(20, "Limit cannot exceed 20").default(5).describe(
4152
+ "Number of similar code blocks to return.\n\nDefault: 5"
4153
+ )
4154
+ });
4155
+
4156
+ // src/mcp/schemas/file.schema.ts
4157
+ import { z as z3 } from "zod";
4158
+ var GetFileContextSchema = z3.object({
4159
+ filepath: z3.string().min(1, "Filepath cannot be empty").describe(
4160
+ "Relative path to file from workspace root.\n\nExample: 'src/components/Button.tsx'"
4161
+ ),
4162
+ includeRelated: z3.boolean().default(true).describe(
4163
+ "Include semantically related chunks from nearby code.\n\nDefault: true\n\nWhen enabled, also returns related code from other files that are semantically similar to the target file's contents."
4164
+ )
4165
+ });
4166
+
4167
+ // src/mcp/schemas/symbols.schema.ts
4168
+ import { z as z4 } from "zod";
4169
+ var ListFunctionsSchema = z4.object({
4170
+ pattern: z4.string().optional().describe(
4171
+ "Regex pattern to match symbol names.\n\nExamples:\n - '.*Controller.*' to find all Controllers\n - 'handle.*' to find handlers\n - '.*Service$' to find Services\n\nIf omitted, returns all symbols."
4172
+ ),
4173
+ language: z4.string().optional().describe(
4174
+ "Filter by programming language.\n\nExamples: 'typescript', 'python', 'javascript', 'php'\n\nIf omitted, searches all languages."
4175
+ )
4176
+ });
4177
+
4085
4178
  // src/mcp/tools.ts
4086
4179
  var tools = [
4087
- {
4088
- name: "semantic_search",
4089
- description: "Search the codebase semantically for relevant code using natural language. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity.",
4090
- inputSchema: {
4091
- type: "object",
4092
- properties: {
4093
- query: {
4094
- type: "string",
4095
- description: 'Natural language search query (e.g., "authentication logic", "database connection handling")'
4096
- },
4097
- limit: {
4098
- type: "number",
4099
- description: "Maximum number of results to return",
4100
- default: 5
4101
- }
4102
- },
4103
- required: ["query"]
4104
- }
4105
- },
4106
- {
4107
- name: "find_similar",
4108
- description: "Find code similar to a given code snippet. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity.",
4109
- inputSchema: {
4110
- type: "object",
4111
- properties: {
4112
- code: {
4113
- type: "string",
4114
- description: "Code snippet to find similar implementations"
4115
- },
4116
- limit: {
4117
- type: "number",
4118
- description: "Maximum number of results to return",
4119
- default: 5
4120
- }
4121
- },
4122
- required: ["code"]
4123
- }
4124
- },
4125
- {
4126
- name: "get_file_context",
4127
- description: "Get all chunks and related context for a specific file. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity.",
4128
- inputSchema: {
4129
- type: "object",
4130
- properties: {
4131
- filepath: {
4132
- type: "string",
4133
- description: "Path to the file (relative to project root)"
4134
- },
4135
- includeRelated: {
4136
- type: "boolean",
4137
- description: "Include semantically related chunks from other files",
4138
- default: true
4139
- }
4140
- },
4141
- required: ["filepath"]
4142
- }
4143
- },
4144
- {
4145
- name: "list_functions",
4146
- description: "List functions, classes, and interfaces by name pattern and language",
4147
- inputSchema: {
4148
- type: "object",
4149
- properties: {
4150
- pattern: {
4151
- type: "string",
4152
- description: 'Regex pattern to match symbol names (e.g., ".*Service$", "handle.*")'
4153
- },
4154
- language: {
4155
- type: "string",
4156
- description: 'Language filter (e.g., "typescript", "python", "php")'
4157
- }
4158
- }
4159
- }
4160
- }
4180
+ toMCPToolSchema(
4181
+ SemanticSearchSchema,
4182
+ "semantic_search",
4183
+ "Search the codebase semantically for relevant code using natural language. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity."
4184
+ ),
4185
+ toMCPToolSchema(
4186
+ FindSimilarSchema,
4187
+ "find_similar",
4188
+ "Find code similar to a given code snippet. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity."
4189
+ ),
4190
+ toMCPToolSchema(
4191
+ GetFileContextSchema,
4192
+ "get_file_context",
4193
+ "Get all chunks and related context for a specific file. Results include a relevance category (highly_relevant, relevant, loosely_related, not_relevant) based on semantic similarity."
4194
+ ),
4195
+ toMCPToolSchema(
4196
+ ListFunctionsSchema,
4197
+ "list_functions",
4198
+ "List functions, classes, and interfaces by name pattern and language"
4199
+ )
4161
4200
  ];
4162
4201
 
4163
4202
  // src/mcp/server.ts
@@ -4300,6 +4339,64 @@ var FileWatcher = class {
4300
4339
 
4301
4340
  // src/mcp/server.ts
4302
4341
  init_constants();
4342
+
4343
+ // src/mcp/utils/tool-wrapper.ts
4344
+ init_errors();
4345
+ import { ZodError } from "zod";
4346
+ function wrapToolHandler(schema, handler) {
4347
+ return async (args) => {
4348
+ try {
4349
+ const validated = schema.parse(args);
4350
+ const result = await handler(validated);
4351
+ return {
4352
+ content: [{
4353
+ type: "text",
4354
+ text: JSON.stringify(result, null, 2)
4355
+ }]
4356
+ };
4357
+ } catch (error) {
4358
+ if (error instanceof ZodError) {
4359
+ return {
4360
+ isError: true,
4361
+ content: [{
4362
+ type: "text",
4363
+ text: JSON.stringify({
4364
+ error: "Invalid parameters",
4365
+ code: "INVALID_INPUT" /* INVALID_INPUT */,
4366
+ details: error.errors.map((e) => ({
4367
+ field: e.path.join("."),
4368
+ message: e.message
4369
+ }))
4370
+ }, null, 2)
4371
+ }]
4372
+ };
4373
+ }
4374
+ if (error instanceof LienError) {
4375
+ return {
4376
+ isError: true,
4377
+ content: [{
4378
+ type: "text",
4379
+ text: JSON.stringify(error.toJSON(), null, 2)
4380
+ }]
4381
+ };
4382
+ }
4383
+ console.error("Unexpected error in tool handler:", error);
4384
+ return {
4385
+ isError: true,
4386
+ content: [{
4387
+ type: "text",
4388
+ text: JSON.stringify({
4389
+ error: error instanceof Error ? error.message : "Unknown error",
4390
+ code: "INTERNAL_ERROR" /* INTERNAL_ERROR */
4391
+ }, null, 2)
4392
+ }]
4393
+ };
4394
+ }
4395
+ };
4396
+ }
4397
+
4398
+ // src/mcp/server.ts
4399
+ init_errors();
4303
4400
  var __filename4 = fileURLToPath4(import.meta.url);
4304
4401
  var __dirname4 = dirname3(__filename4);
4305
4402
  var require4 = createRequire3(import.meta.url);
@@ -4364,148 +4461,139 @@ async function startMCPServer(options) {
4364
4461
  }, VERSION_CHECK_INTERVAL_MS);
4365
4462
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
4366
4463
  const { name, arguments: args } = request.params;
4464
+ log(`Handling tool call: ${name}`);
4367
4465
  try {
4368
- log(`Handling tool call: ${name}`);
4369
4466
  switch (name) {
4370
- case "semantic_search": {
4371
- const query = args?.query;
4372
- const limit = args?.limit || 5;
4373
- log(`Searching for: "${query}"`);
4374
- await checkAndReconnect();
4375
- const queryEmbedding = await embeddings.embed(query);
4376
- const results = await vectorDB.search(queryEmbedding, limit, query);
4377
- log(`Found ${results.length} results`);
4378
- const response = {
4379
- indexInfo: getIndexMetadata(),
4380
- results
4381
- };
4382
- return {
4383
- content: [
4384
- {
4385
- type: "text",
4386
- text: JSON.stringify(response, null, 2)
4387
- }
4388
- ]
4389
- };
4390
- }
4391
- case "find_similar": {
4392
- const code = args?.code;
4393
- const limit = args?.limit || 5;
4394
- log(`Finding similar code...`);
4395
- await checkAndReconnect();
4396
- const codeEmbedding = await embeddings.embed(code);
4397
- const results = await vectorDB.search(codeEmbedding, limit, code);
4398
- log(`Found ${results.length} similar chunks`);
4399
- const response = {
4400
- indexInfo: getIndexMetadata(),
4401
- results
4402
- };
4403
- return {
4404
- content: [
4405
- {
4406
- type: "text",
4407
- text: JSON.stringify(response, null, 2)
4408
- }
4409
- ]
4410
- };
4411
- }
4412
- case "get_file_context": {
4413
- const filepath = args?.filepath;
4414
- const includeRelated = args?.includeRelated ?? true;
4415
- log(`Getting context for: ${filepath}`);
4416
- await checkAndReconnect();
4417
- const fileEmbedding = await embeddings.embed(filepath);
4418
- const allResults = await vectorDB.search(fileEmbedding, 50, filepath);
4419
- const fileChunks = allResults.filter(
4420
- (r) => r.metadata.file.includes(filepath) || filepath.includes(r.metadata.file)
4421
- );
4422
- let results = fileChunks;
4423
- if (includeRelated && fileChunks.length > 0) {
4424
- const relatedEmbedding = await embeddings.embed(fileChunks[0].content);
4425
- const related = await vectorDB.search(relatedEmbedding, 5, fileChunks[0].content);
4426
- const relatedOtherFiles = related.filter(
4427
- (r) => !r.metadata.file.includes(filepath) && !filepath.includes(r.metadata.file)
4428
- );
4429
- results = [...fileChunks, ...relatedOtherFiles];
4430
- }
4431
- log(`Found ${results.length} chunks`);
4432
- const response = {
4433
- indexInfo: getIndexMetadata(),
4434
- file: filepath,
4435
- chunks: results
4436
- };
4437
- return {
4438
- content: [
4439
- {
4440
- type: "text",
4441
- text: JSON.stringify(response, null, 2)
4467
+ case "semantic_search":
4468
+ return await wrapToolHandler(
4469
+ SemanticSearchSchema,
4470
+ async (validatedArgs) => {
4471
+ log(`Searching for: "${validatedArgs.query}"`);
4472
+ await checkAndReconnect();
4473
+ const queryEmbedding = await embeddings.embed(validatedArgs.query);
4474
+ const results = await vectorDB.search(queryEmbedding, validatedArgs.limit, validatedArgs.query);
4475
+ log(`Found ${results.length} results`);
4476
+ return {
4477
+ indexInfo: getIndexMetadata(),
4478
+ results
4479
+ };
4480
+ }
4481
+ )(args);
4482
+ case "find_similar":
4483
+ return await wrapToolHandler(
4484
+ FindSimilarSchema,
4485
+ async (validatedArgs) => {
4486
+ log(`Finding similar code...`);
4487
+ await checkAndReconnect();
4488
+ const codeEmbedding = await embeddings.embed(validatedArgs.code);
4489
+ const results = await vectorDB.search(codeEmbedding, validatedArgs.limit, validatedArgs.code);
4490
+ log(`Found ${results.length} similar chunks`);
4491
+ return {
4492
+ indexInfo: getIndexMetadata(),
4493
+ results
4494
+ };
4495
+ }
4496
+ )(args);
4497
+ case "get_file_context":
4498
+ return await wrapToolHandler(
4499
+ GetFileContextSchema,
4500
+ async (validatedArgs) => {
4501
+ log(`Getting context for: ${validatedArgs.filepath}`);
4502
+ await checkAndReconnect();
4503
+ const fileEmbedding = await embeddings.embed(validatedArgs.filepath);
4504
+ const allResults = await vectorDB.search(fileEmbedding, 50, validatedArgs.filepath);
4505
+ const fileChunks = allResults.filter(
4506
+ (r) => r.metadata.file.includes(validatedArgs.filepath) || validatedArgs.filepath.includes(r.metadata.file)
4507
+ );
4508
+ let results = fileChunks;
4509
+ if (validatedArgs.includeRelated && fileChunks.length > 0) {
4510
+ const relatedEmbedding = await embeddings.embed(fileChunks[0].content);
4511
+ const related = await vectorDB.search(relatedEmbedding, 5, fileChunks[0].content);
4512
+ const relatedOtherFiles = related.filter(
4513
+ (r) => !r.metadata.file.includes(validatedArgs.filepath) && !validatedArgs.filepath.includes(r.metadata.file)
4514
+ );
4515
+ results = [...fileChunks, ...relatedOtherFiles];
4442
4516
  }
4443
- ]
4444
- };
4445
- }
4446
- case "list_functions": {
4447
- const pattern = args?.pattern;
4448
- const language = args?.language;
4449
- log("Listing functions with symbol metadata...");
4450
- await checkAndReconnect();
4451
- let results;
4452
- let usedMethod = "symbols";
4453
- try {
4454
- results = await vectorDB.querySymbols({
4455
- language,
4456
- pattern,
4457
- limit: 50
4458
- });
4459
- if (results.length === 0 && (language || pattern)) {
4460
- log("No symbol results, falling back to content scan...");
4461
- results = await vectorDB.scanWithFilter({
4462
- language,
4463
- pattern,
4464
- limit: 50
4465
- });
4466
- usedMethod = "content";
4517
+ log(`Found ${results.length} chunks`);
4518
+ return {
4519
+ indexInfo: getIndexMetadata(),
4520
+ file: validatedArgs.filepath,
4521
+ chunks: results
4522
+ };
4467
4523
  }
4468
- } catch (error) {
4469
- log(`Symbol query failed, falling back to content scan: ${error}`);
4470
- results = await vectorDB.scanWithFilter({
4471
- language,
4472
- pattern,
4473
- limit: 50
4474
- });
4475
- usedMethod = "content";
4476
- }
4477
- log(`Found ${results.length} matches using ${usedMethod} method`);
4478
- const response = {
4479
- indexInfo: getIndexMetadata(),
4480
- method: usedMethod,
4481
- results,
4482
- note: usedMethod === "content" ? 'Using content search. Run "lien reindex" to enable faster symbol-based queries.' : void 0
4483
- };
4484
- return {
4485
- content: [
4486
- {
4487
- type: "text",
4488
- text: JSON.stringify(response, null, 2)
4524
+ )(args);
4525
+ case "list_functions":
4526
+ return await wrapToolHandler(
4527
+ ListFunctionsSchema,
4528
+ async (validatedArgs) => {
4529
+ log("Listing functions with symbol metadata...");
4530
+ await checkAndReconnect();
4531
+ let results;
4532
+ let usedMethod = "symbols";
4533
+ try {
4534
+ results = await vectorDB.querySymbols({
4535
+ language: validatedArgs.language,
4536
+ pattern: validatedArgs.pattern,
4537
+ limit: 50
4538
+ });
4539
+ if (results.length === 0 && (validatedArgs.language || validatedArgs.pattern)) {
4540
+ log("No symbol results, falling back to content scan...");
4541
+ results = await vectorDB.scanWithFilter({
4542
+ language: validatedArgs.language,
4543
+ pattern: validatedArgs.pattern,
4544
+ limit: 50
4545
+ });
4546
+ usedMethod = "content";
4547
+ }
4548
+ } catch (error) {
4549
+ log(`Symbol query failed, falling back to content scan: ${error}`);
4550
+ results = await vectorDB.scanWithFilter({
4551
+ language: validatedArgs.language,
4552
+ pattern: validatedArgs.pattern,
4553
+ limit: 50
4554
+ });
4555
+ usedMethod = "content";
4489
4556
  }
4490
- ]
4491
- };
4492
- }
4557
+ log(`Found ${results.length} matches using ${usedMethod} method`);
4558
+ return {
4559
+ indexInfo: getIndexMetadata(),
4560
+ method: usedMethod,
4561
+ results,
4562
+ note: usedMethod === "content" ? 'Using content search. Run "lien reindex" to enable faster symbol-based queries.' : void 0
4563
+ };
4564
+ }
4565
+ )(args);
4493
4566
  default:
4494
- throw new Error(`Unknown tool: ${name}`);
4567
+ throw new LienError(
4568
+ `Unknown tool: ${name}`,
4569
+ "INVALID_INPUT" /* INVALID_INPUT */,
4570
+ { requestedTool: name, availableTools: tools.map((t) => t.name) },
4571
+ "medium",
4572
+ false,
4573
+ false
4574
+ );
4495
4575
  }
4496
4576
  } catch (error) {
4497
- console.error(`Error handling tool call ${name}:`, error);
4498
- return {
4499
- content: [
4500
- {
4577
+ if (error instanceof LienError) {
4578
+ return {
4579
+ isError: true,
4580
+ content: [{
4501
4581
  type: "text",
4502
- text: JSON.stringify({
4503
- error: String(error),
4504
- tool: name
4505
- })
4506
- }
4507
- ],
4508
- isError: true
4582
+ text: JSON.stringify(error.toJSON(), null, 2)
4583
+ }]
4584
+ };
4585
+ }
4586
+ console.error(`Unexpected error handling tool call ${name}:`, error);
4587
+ return {
4588
+ isError: true,
4589
+ content: [{
4590
+ type: "text",
4591
+ text: JSON.stringify({
4592
+ error: error instanceof Error ? error.message : "Unknown error",
4593
+ code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
4594
+ tool: name
4595
+ }, null, 2)
4596
+ }]
4509
4597
  };
4510
4598
  }
4511
4599
  });