tiny-stdio-mcp-server 0.1.14 → 0.1.16

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/README.md CHANGED
@@ -58,7 +58,7 @@ server.tool("search", "Search for things", schema, async ({ query, limit }) => {
58
58
  });
59
59
  ```
60
60
 
61
- For structured-data tools, pass a root-object output schema. The server advertises it as MCP `Tool.outputSchema`, validates the handler result, returns it as `CallToolResult.structuredContent`, and also includes a JSON text backstop in `content[]` for older clients.
61
+ For structured-data tools, pass a root-object output schema. The server advertises it as MCP `Tool.outputSchema`, validates successful handler results, and returns them as `CallToolResult.structuredContent`. Plain object returns receive a JSON text backstop in `content[]` for older clients. Explicit `CallToolResult` values keep their handler-supplied content blocks; the JSON text backstop is added only when `content` is empty.
62
62
 
63
63
  ```ts
64
64
  const input = defineSchema({
@@ -89,7 +89,11 @@ server.tool(
89
89
  );
90
90
  ```
91
91
 
92
- Output schemas must have `type: "object"` at the root. Tools whose natural result is prose, images, audio, files, or other content blocks should omit `outputSchema` and keep returning content.
92
+ Output schemas must have `type: "object"` at the root because MCP structured content is an object. Otherwise, any schema accepted by Ajv is supported, including composition keywords, nullable type unions, and local `$defs`/`$ref` references. Input and output schemas compile synchronously during `.tool()` or `.registerTool()` registration, so malformed schemas throw immediately instead of failing on the first tool call.
93
+
94
+ Successful structured results must satisfy `outputSchema`. Validation failures use JSON-RPC `-32602` for inputs and `-32603` for outputs, with Ajv's formatted error text in the message and the raw Ajv error array in `error.data`. Explicit handler results with `isError: true` are passed through unchanged and are exempt from structured-content and output-schema validation.
95
+
96
+ Tools whose natural result is prose, images, audio, files, or other content blocks should omit `outputSchema` and keep returning content.
93
97
 
94
98
  ### `.listen()`
95
99
 
package/dist/jsonrpc.js CHANGED
@@ -37,7 +37,8 @@ export function parseMessage(line) {
37
37
  : null;
38
38
  if ("params" in obj
39
39
  && (typeof obj.params !== "object"
40
- || obj.params === null)) {
40
+ || obj.params === null
41
+ || Array.isArray(obj.params))) {
41
42
  return {
42
43
  success: false,
43
44
  error: {
package/dist/server.js CHANGED
@@ -47,7 +47,8 @@ export function createServer(options) {
47
47
  ? params.protocolVersion
48
48
  : undefined;
49
49
  const result = {
50
- protocolVersion: requestedProtocol !== undefined && SUPPORTED_PROTOCOL_VERSIONS.has(requestedProtocol)
50
+ protocolVersion: requestedProtocol !== undefined &&
51
+ SUPPORTED_PROTOCOL_VERSIONS.has(requestedProtocol)
51
52
  ? requestedProtocol
52
53
  : PROTOCOL_VERSION,
53
54
  capabilities: {
@@ -95,6 +96,9 @@ export function createServer(options) {
95
96
  for (const tool of tools.values()) {
96
97
  const descriptor = { ...tool };
97
98
  delete descriptor.handler;
99
+ delete descriptor.inputValidator;
100
+ delete descriptor
101
+ .outputValidator;
98
102
  toolList.push({
99
103
  ...descriptor,
100
104
  });
@@ -121,19 +125,25 @@ export function createServer(options) {
121
125
  };
122
126
  }
123
127
  const toolArgs = (params?.arguments ?? {});
124
- if (options.validateToolArguments !== false && !jsonSchemaValidator.validate(tool.inputSchema, toolArgs)) {
128
+ if (options.validateToolArguments !== false &&
129
+ !tool.inputValidator(toolArgs)) {
130
+ const errors = tool.inputValidator.errors ?? [];
125
131
  return {
126
132
  error: {
127
133
  code: JSON_RPC_ERROR_CODES.INVALID_PARAMS,
128
- message: "Invalid tool arguments",
134
+ message: `Invalid tool arguments: ${jsonSchemaValidator.errorsText(errors)}`,
135
+ data: errors,
129
136
  },
130
137
  };
131
138
  }
132
139
  try {
133
140
  const handlerResult = await tool.handler(toolArgs);
134
141
  const result = normalizeToolResult(handlerResult, tool.outputSchema);
135
- if (tool.outputSchema !== undefined && !jsonSchemaValidator.validate(tool.outputSchema, result.structuredContent)) {
136
- throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, "Invalid structured tool result");
142
+ if (result.isError !== true &&
143
+ tool.outputValidator !== undefined &&
144
+ !tool.outputValidator(result.structuredContent)) {
145
+ const errors = tool.outputValidator.errors ?? [];
146
+ throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, `Invalid structured tool result: ${jsonSchemaValidator.errorsText(errors)}`, errors);
137
147
  }
138
148
  return { result };
139
149
  }
@@ -220,7 +230,8 @@ export function createServer(options) {
220
230
  return internalError(toErrorMessage(error));
221
231
  }
222
232
  }
223
- if (method === "resources/subscribe" || method === "resources/unsubscribe") {
233
+ if (method === "resources/subscribe" ||
234
+ method === "resources/unsubscribe") {
224
235
  if (!supportResourceSubscriptions) {
225
236
  return {
226
237
  error: {
@@ -233,8 +244,8 @@ export function createServer(options) {
233
244
  if (uri === undefined || !isValidUri(uri)) {
234
245
  return invalidParams("Resource URI required");
235
246
  }
236
- if (method === "resources/subscribe"
237
- && findReadableResource(uri, resources, resourceTemplates) === undefined) {
247
+ if (method === "resources/subscribe" &&
248
+ findReadableResource(uri, resources, resourceTemplates) === undefined) {
238
249
  return resourceNotFound(uri);
239
250
  }
240
251
  if (method === "resources/subscribe") {
@@ -292,7 +303,21 @@ export function createServer(options) {
292
303
  }) + "\n");
293
304
  return;
294
305
  }
295
- const { result, error } = await messageHandler(request.method, request.params);
306
+ let handled;
307
+ try {
308
+ handled = await messageHandler(request.method, request.params);
309
+ }
310
+ catch {
311
+ if (!isNotification) {
312
+ const requestWithId = request;
313
+ write(formatErrorResponse(requestWithId.id, {
314
+ code: JSON_RPC_ERROR_CODES.INTERNAL_ERROR,
315
+ message: "Internal error",
316
+ }) + "\n");
317
+ }
318
+ return;
319
+ }
320
+ const { result, error } = handled;
296
321
  if (isNotification) {
297
322
  return;
298
323
  }
@@ -322,26 +347,38 @@ export function createServer(options) {
322
347
  const server = {
323
348
  tool(name, description, inputSchema, handler, outputSchema) {
324
349
  assertNonEmptyName(name, "Tool name required");
350
+ const inputValidator = jsonSchemaValidator.compile(inputSchema);
351
+ let outputValidator;
325
352
  if (outputSchema !== undefined) {
326
- assertSupportedOutputSchema(outputSchema);
353
+ assertObjectRootSchema(outputSchema, "outputSchema");
354
+ outputValidator = jsonSchemaValidator.compile(outputSchema);
327
355
  }
328
356
  tools.set(name, {
329
357
  name,
330
358
  description,
331
359
  inputSchema: inputSchema,
332
- ...(outputSchema === undefined ? {} : { outputSchema: outputSchema }),
360
+ ...(outputSchema === undefined
361
+ ? {}
362
+ : { outputSchema: outputSchema }),
333
363
  handler: handler,
364
+ inputValidator,
365
+ ...(outputValidator === undefined ? {} : { outputValidator }),
334
366
  });
335
367
  return server;
336
368
  },
337
369
  registerTool(definition, handler) {
338
370
  assertNonEmptyName(definition.name, "Tool name required");
371
+ const inputValidator = jsonSchemaValidator.compile(definition.inputSchema);
372
+ let outputValidator;
339
373
  if (definition.outputSchema !== undefined) {
340
- assertSupportedOutputSchema(definition.outputSchema);
374
+ assertObjectRootSchema(definition.outputSchema, "outputSchema");
375
+ outputValidator = jsonSchemaValidator.compile(definition.outputSchema);
341
376
  }
342
377
  tools.set(definition.name, {
343
378
  ...definition,
344
379
  handler: handler,
380
+ inputValidator,
381
+ ...(outputValidator === undefined ? {} : { outputValidator }),
345
382
  });
346
383
  return server;
347
384
  },
@@ -382,17 +419,20 @@ export function createServer(options) {
382
419
  return resourceTemplates.delete(uriTemplate);
383
420
  },
384
421
  async notifyToolsChanged() {
385
- if (supportNotifications && [...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
422
+ if (supportNotifications &&
423
+ [...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
386
424
  await broadcastNotification("notifications/tools/list_changed");
387
425
  }
388
426
  },
389
427
  async notifyPromptsChanged() {
390
- if (supportNotifications && [...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
428
+ if (supportNotifications &&
429
+ [...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
391
430
  await broadcastNotification("notifications/prompts/list_changed");
392
431
  }
393
432
  },
394
433
  async notifyResourcesChanged() {
395
- if (supportNotifications && [...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
434
+ if (supportNotifications &&
435
+ [...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
396
436
  await broadcastNotification("notifications/resources/list_changed");
397
437
  }
398
438
  },
@@ -412,20 +452,17 @@ export function createServer(options) {
412
452
  },
413
453
  async connect(transport) {
414
454
  return new Promise((resolve) => {
415
- const lifecycle = { initialized: false, initializeAccepted: false, notificationReady: false, resourceSubscriptions: new Set() };
416
- const messageHandler = (method, params) => handleMessageWithLifecycle(method, lifecycle, params);
417
- messageLifecycles.add(lifecycle);
418
455
  const listener = (notification) => {
419
456
  transport.writable.write(`${JSON.stringify(notification)}\n`);
420
457
  };
421
- connectionNotificationListeners.set(listener, lifecycle);
458
+ const session = server.createMessageSession(listener);
422
459
  const rl = readline.createInterface({
423
460
  input: transport.readable,
424
461
  crlfDelay: Infinity,
425
462
  });
426
463
  const pendingMessages = new Set();
427
464
  rl.on("line", (line) => {
428
- const message = processLine(line, (data) => transport.writable.write(data), messageHandler);
465
+ const message = processLine(line, (data) => transport.writable.write(data), session.handleMessage);
429
466
  pendingMessages.add(message);
430
467
  void message.finally(() => {
431
468
  pendingMessages.delete(message);
@@ -433,19 +470,15 @@ export function createServer(options) {
433
470
  });
434
471
  rl.on("close", async () => {
435
472
  await Promise.all([...pendingMessages]);
436
- connectionNotificationListeners.delete(listener);
437
- messageLifecycles.delete(lifecycle);
473
+ session.close();
438
474
  resolve();
439
475
  });
440
476
  });
441
477
  },
442
478
  async connectSDK(transport) {
443
479
  return new Promise((resolve, reject) => {
444
- const lifecycle = { initialized: false, initializeAccepted: false, notificationReady: false, resourceSubscriptions: new Set() };
445
- const messageHandler = (method, params) => handleMessageWithLifecycle(method, lifecycle, params);
446
- messageLifecycles.add(lifecycle);
447
480
  const listener = (notification) => transport.send(notification);
448
- connectionNotificationListeners.set(listener, lifecycle);
481
+ const session = server.createMessageSession(listener);
449
482
  transport.onmessage = async (message) => {
450
483
  // Ignore responses (we only handle requests/notifications)
451
484
  if (!("method" in message)) {
@@ -456,7 +489,12 @@ export function createServer(options) {
456
489
  if (message.method === "initialize") {
457
490
  return;
458
491
  }
459
- await messageHandler(message.method, message.params);
492
+ try {
493
+ await session.handleMessage(message.method, message.params);
494
+ }
495
+ catch {
496
+ return;
497
+ }
460
498
  return;
461
499
  }
462
500
  if (message.method === "notifications/initialized") {
@@ -471,7 +509,22 @@ export function createServer(options) {
471
509
  return;
472
510
  }
473
511
  const request = message;
474
- const { result, error } = await messageHandler(request.method, request.params);
512
+ let handled;
513
+ try {
514
+ handled = await session.handleMessage(request.method, request.params);
515
+ }
516
+ catch {
517
+ await transport.send({
518
+ jsonrpc: "2.0",
519
+ id: request.id,
520
+ error: {
521
+ code: JSON_RPC_ERROR_CODES.INTERNAL_ERROR,
522
+ message: "Internal error",
523
+ },
524
+ });
525
+ return;
526
+ }
527
+ const { result, error } = handled;
475
528
  if (error) {
476
529
  const response = {
477
530
  jsonrpc: "2.0",
@@ -490,13 +543,11 @@ export function createServer(options) {
490
543
  }
491
544
  };
492
545
  transport.onclose = () => {
493
- connectionNotificationListeners.delete(listener);
494
- messageLifecycles.delete(lifecycle);
546
+ session.close();
495
547
  resolve();
496
548
  };
497
549
  void transport.start().catch((error) => {
498
- connectionNotificationListeners.delete(listener);
499
- messageLifecycles.delete(lifecycle);
550
+ session.close();
500
551
  reject(error);
501
552
  });
502
553
  });
@@ -592,14 +643,14 @@ function isCallToolResult(value) {
592
643
  if (!hasContentArray(value) || !value.content.every(isContentItem)) {
593
644
  return false;
594
645
  }
595
- if (hasOwnProperty(value, "structuredContent")
596
- && value.structuredContent !== undefined
597
- && !isJsonObject(value.structuredContent)) {
646
+ if (hasOwnProperty(value, "structuredContent") &&
647
+ value.structuredContent !== undefined &&
648
+ !isJsonObject(value.structuredContent)) {
598
649
  return false;
599
650
  }
600
- return !(hasOwnProperty(value, "isError")
601
- && value.isError !== undefined
602
- && typeof value.isError !== "boolean");
651
+ return !(hasOwnProperty(value, "isError") &&
652
+ value.isError !== undefined &&
653
+ typeof value.isError !== "boolean");
603
654
  }
604
655
  function normalizeToolResult(handlerResult, outputSchema) {
605
656
  if (hasContentArray(handlerResult) && !isCallToolResult(handlerResult)) {
@@ -614,131 +665,71 @@ function normalizeToolResult(handlerResult, outputSchema) {
614
665
  }
615
666
  return result;
616
667
  }
617
- const structuredContent = isCallToolResult(handlerResult)
618
- ? handlerResult.structuredContent
668
+ if (isCallToolResult(handlerResult) && handlerResult.isError === true) {
669
+ return handlerResult;
670
+ }
671
+ const callToolResult = isCallToolResult(handlerResult)
672
+ ? handlerResult
673
+ : undefined;
674
+ const structuredContent = callToolResult
675
+ ? callToolResult.structuredContent
619
676
  : handlerResult;
620
677
  if (!isJsonObject(structuredContent)) {
621
678
  throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, "Structured tool result must be an object");
622
679
  }
623
680
  return {
624
- content: [{ type: "text", text: JSON.stringify(structuredContent) }],
625
- ...(isCallToolResult(handlerResult) && handlerResult.isError !== undefined
626
- ? { isError: handlerResult.isError }
681
+ content: callToolResult !== undefined && callToolResult.content.length > 0
682
+ ? callToolResult.content
683
+ : [{ type: "text", text: JSON.stringify(structuredContent) }],
684
+ ...(callToolResult?.isError !== undefined
685
+ ? { isError: callToolResult.isError }
627
686
  : {}),
628
687
  structuredContent,
629
688
  };
630
689
  }
631
- function assertSupportedOutputSchema(schema) {
632
- assertObjectRootSchema(schema, "outputSchema");
633
- assertSupportedJsonSchema(schema, "outputSchema");
634
- }
635
690
  function assertObjectRootSchema(schema, path) {
636
691
  if (schema.type !== "object") {
637
692
  throw new Error(`${path} root type must be "object"`);
638
693
  }
639
694
  }
640
- function assertSupportedJsonSchema(schema, path) {
641
- for (const keyword of ["anyOf", "allOf", "not", "if", "then", "else", "contains", "prefixItems"]) {
642
- if (schema[keyword] !== undefined) {
643
- throw new Error(`${path} uses unsupported keyword "${keyword}"`);
644
- }
645
- }
646
- const type = schema.type;
647
- if (Array.isArray(type)) {
648
- const supported = new Set(["string", "number", "integer", "boolean", "object", "array", "null"]);
649
- for (const item of type) {
650
- if (!supported.has(item)) {
651
- throw new Error(`${path} uses unsupported type "${item}"`);
652
- }
653
- }
654
- }
655
- else if (type !== undefined &&
656
- type !== "string" &&
657
- type !== "number" &&
658
- type !== "integer" &&
659
- type !== "boolean" &&
660
- type !== "object" &&
661
- type !== "array") {
662
- throw new Error(`${path} uses unsupported type "${type}"`);
663
- }
664
- if (isJsonObject(schema.properties)) {
665
- for (const [key, child] of Object.entries(schema.properties)) {
666
- if (isJsonObject(child)) {
667
- assertSupportedJsonSchema(child, `${path}.properties.${key}`);
668
- }
669
- else {
670
- throw new Error(`${path}.properties.${key} must be an object schema`);
671
- }
672
- }
673
- }
674
- else if (schema.properties !== undefined) {
675
- throw new Error(`${path}.properties must be an object`);
676
- }
677
- const additionalProperties = schema.additionalProperties;
678
- if (typeof additionalProperties === "object" && additionalProperties !== null) {
679
- if (Array.isArray(additionalProperties)) {
680
- throw new Error(`${path}.additionalProperties must be an object schema or boolean`);
681
- }
682
- assertSupportedJsonSchema(additionalProperties, `${path}.additionalProperties`);
683
- }
684
- else if (additionalProperties !== undefined
685
- && typeof additionalProperties !== "boolean") {
686
- throw new Error(`${path}.additionalProperties must be an object schema or boolean`);
687
- }
688
- const items = schema.items;
689
- if (Array.isArray(items)) {
690
- throw new Error(`${path}.items uses unsupported tuple array schemas`);
691
- }
692
- if (typeof items === "object" && items !== null && !Array.isArray(items)) {
693
- assertSupportedJsonSchema(items, `${path}.items`);
694
- }
695
- else if (items !== undefined && typeof items !== "boolean") {
696
- throw new Error(`${path}.items must be an object schema or boolean`);
697
- }
698
- const oneOf = schema.oneOf;
699
- if (Array.isArray(oneOf)) {
700
- for (const [index, child] of oneOf.entries()) {
701
- if (typeof child === "object" && child !== null && !Array.isArray(child)) {
702
- assertSupportedJsonSchema(child, `${path}.oneOf[${index}]`);
703
- }
704
- else {
705
- throw new Error(`${path}.oneOf[${index}] must be an object schema`);
706
- }
707
- }
708
- }
709
- else if (oneOf !== undefined) {
710
- throw new Error(`${path}.oneOf must be an array`);
711
- }
712
- }
713
695
  function isJsonObject(value) {
714
696
  return typeof value === "object" && value !== null && !Array.isArray(value);
715
697
  }
716
698
  function isGetPromptResult(value) {
717
- if (typeof value !== "object" || value === null || !hasOwnProperty(value, "messages")) {
699
+ if (typeof value !== "object" ||
700
+ value === null ||
701
+ !hasOwnProperty(value, "messages")) {
718
702
  return false;
719
703
  }
720
- return (!hasOwnProperty(value, "description") || value.description === undefined || typeof value.description === "string")
721
- && Array.isArray(value.messages)
722
- && value.messages.every((message) => typeof message === "object"
723
- && message !== null
724
- && hasOwnProperty(message, "role")
725
- && (message.role === "user" || message.role === "assistant")
726
- && hasOwnProperty(message, "content")
727
- && isPromptContentItem(message.content));
704
+ return ((!hasOwnProperty(value, "description") ||
705
+ value.description === undefined ||
706
+ typeof value.description === "string") &&
707
+ Array.isArray(value.messages) &&
708
+ value.messages.every((message) => typeof message === "object" &&
709
+ message !== null &&
710
+ hasOwnProperty(message, "role") &&
711
+ (message.role === "user" || message.role === "assistant") &&
712
+ hasOwnProperty(message, "content") &&
713
+ isPromptContentItem(message.content)));
728
714
  }
729
715
  function isReadResourceResult(value) {
730
- if (typeof value !== "object" || value === null || !hasOwnProperty(value, "contents")) {
716
+ if (typeof value !== "object" ||
717
+ value === null ||
718
+ !hasOwnProperty(value, "contents")) {
731
719
  return false;
732
720
  }
733
- return Array.isArray(value.contents)
734
- && value.contents.every(isResourceContents);
721
+ return (Array.isArray(value.contents) && value.contents.every(isResourceContents));
735
722
  }
736
723
  function hasContentArray(value) {
737
- return typeof value === "object" && value !== null && hasOwnProperty(value, "content")
738
- && Array.isArray(value.content);
724
+ return (typeof value === "object" &&
725
+ value !== null &&
726
+ hasOwnProperty(value, "content") &&
727
+ Array.isArray(value.content));
739
728
  }
740
729
  function isContentItem(value) {
741
- if (typeof value !== "object" || value === null || !hasOwnProperty(value, "type")) {
730
+ if (typeof value !== "object" ||
731
+ value === null ||
732
+ !hasOwnProperty(value, "type")) {
742
733
  return false;
743
734
  }
744
735
  const block = value;
@@ -749,56 +740,71 @@ function isContentItem(value) {
749
740
  return hasOwnProperty(block, "text") && typeof block.text === "string";
750
741
  }
751
742
  if (block.type === "image" || block.type === "audio") {
752
- return hasOwnProperty(block, "data")
753
- && typeof block.data === "string"
754
- && isBase64(block.data)
755
- && hasOwnProperty(block, "mimeType")
756
- && typeof block.mimeType === "string";
743
+ return (hasOwnProperty(block, "data") &&
744
+ typeof block.data === "string" &&
745
+ isBase64(block.data) &&
746
+ hasOwnProperty(block, "mimeType") &&
747
+ typeof block.mimeType === "string");
757
748
  }
758
749
  if (block.type === "resource_link") {
759
- return hasOwnProperty(block, "uri")
760
- && typeof block.uri === "string"
761
- && isValidUri(block.uri)
762
- && hasOwnProperty(block, "name")
763
- && typeof block.name === "string"
764
- && (!hasOwnProperty(block, "title") || block.title === undefined || typeof block.title === "string")
765
- && (!hasOwnProperty(block, "description") || block.description === undefined || typeof block.description === "string")
766
- && (!hasOwnProperty(block, "mimeType") || block.mimeType === undefined || typeof block.mimeType === "string")
767
- && (!hasOwnProperty(block, "size") || block.size === undefined || typeof block.size === "number");
768
- }
769
- if (block.type !== "resource"
770
- || !hasOwnProperty(block, "resource")
771
- || typeof block.resource !== "object"
772
- || block.resource === null) {
750
+ return (hasOwnProperty(block, "uri") &&
751
+ typeof block.uri === "string" &&
752
+ isValidUri(block.uri) &&
753
+ hasOwnProperty(block, "name") &&
754
+ typeof block.name === "string" &&
755
+ (!hasOwnProperty(block, "title") ||
756
+ block.title === undefined ||
757
+ typeof block.title === "string") &&
758
+ (!hasOwnProperty(block, "description") ||
759
+ block.description === undefined ||
760
+ typeof block.description === "string") &&
761
+ (!hasOwnProperty(block, "mimeType") ||
762
+ block.mimeType === undefined ||
763
+ typeof block.mimeType === "string") &&
764
+ (!hasOwnProperty(block, "size") ||
765
+ block.size === undefined ||
766
+ typeof block.size === "number"));
767
+ }
768
+ if (block.type !== "resource" ||
769
+ !hasOwnProperty(block, "resource") ||
770
+ typeof block.resource !== "object" ||
771
+ block.resource === null) {
773
772
  return false;
774
773
  }
775
774
  return isResourceContents(block.resource);
776
775
  }
777
776
  function isResourceContents(value) {
778
- if (typeof value !== "object"
779
- || value === null
780
- || !hasOwnProperty(value, "uri")
781
- || typeof value.uri !== "string"
782
- || !isValidUri(value.uri)) {
777
+ if (typeof value !== "object" ||
778
+ value === null ||
779
+ !hasOwnProperty(value, "uri") ||
780
+ typeof value.uri !== "string" ||
781
+ !isValidUri(value.uri)) {
783
782
  return false;
784
783
  }
785
- if (hasOwnProperty(value, "mimeType") && value.mimeType !== undefined && typeof value.mimeType !== "string") {
784
+ if (hasOwnProperty(value, "mimeType") &&
785
+ value.mimeType !== undefined &&
786
+ typeof value.mimeType !== "string") {
786
787
  return false;
787
788
  }
788
- return (hasOwnProperty(value, "text") && typeof value.text === "string")
789
- || (hasOwnProperty(value, "blob") && typeof value.blob === "string" && isBase64(value.blob));
789
+ return ((hasOwnProperty(value, "text") && typeof value.text === "string") ||
790
+ (hasOwnProperty(value, "blob") &&
791
+ typeof value.blob === "string" &&
792
+ isBase64(value.blob)));
790
793
  }
791
794
  function hasValidContentAnnotations(value) {
792
- if (!hasOwnProperty(value, "annotations") || value.annotations === undefined) {
795
+ if (!hasOwnProperty(value, "annotations") ||
796
+ value.annotations === undefined) {
793
797
  return true;
794
798
  }
795
799
  if (!isJsonObject(value.annotations)) {
796
800
  return false;
797
801
  }
798
802
  const { audience, priority, lastModified } = value.annotations;
799
- return (audience === undefined || (Array.isArray(audience) && audience.every((item) => item === "user" || item === "assistant")))
800
- && (priority === undefined || typeof priority === "number")
801
- && (lastModified === undefined || typeof lastModified === "string");
803
+ return ((audience === undefined ||
804
+ (Array.isArray(audience) &&
805
+ audience.every((item) => item === "user" || item === "assistant"))) &&
806
+ (priority === undefined || typeof priority === "number") &&
807
+ (lastModified === undefined || typeof lastModified === "string"));
802
808
  }
803
809
  function isBase64(value) {
804
810
  if (value.length === 0) {
@@ -811,7 +817,8 @@ function isBase64(value) {
811
817
  const paddingStart = value.indexOf("=");
812
818
  const encoded = paddingStart === -1 ? value : value.slice(0, paddingStart);
813
819
  const padding = paddingStart === -1 ? "" : value.slice(paddingStart);
814
- if (padding.length > 2 || [...padding].some((character) => character !== "=")) {
820
+ if (padding.length > 2 ||
821
+ [...padding].some((character) => character !== "=")) {
815
822
  return false;
816
823
  }
817
824
  if ([...encoded].some((character) => !alphabet.includes(character))) {
@@ -823,7 +830,10 @@ function isPromptContentItem(value) {
823
830
  if (!isContentItem(value)) {
824
831
  return false;
825
832
  }
826
- return !(typeof value === "object" && value !== null && hasOwnProperty(value, "type") && value.type === "resource_link");
833
+ return !(typeof value === "object" &&
834
+ value !== null &&
835
+ hasOwnProperty(value, "type") &&
836
+ value.type === "resource_link");
827
837
  }
828
838
  function hasOwnProperty(value, name) {
829
839
  return Object.prototype.hasOwnProperty.call(value, name);
package/dist/types.d.ts CHANGED
@@ -164,10 +164,7 @@ export interface ResourceTemplateDefinition extends ResourceTemplate {
164
164
  }
165
165
  export interface HandleResult {
166
166
  result?: unknown;
167
- error?: {
168
- code: number;
169
- message: string;
170
- };
167
+ error?: JSONRPCError;
171
168
  }
172
169
  export type PromptContentItem = {
173
170
  type: "text";
package/dist/types.js CHANGED
@@ -5,7 +5,7 @@ export const JSON_RPC_ERROR_CODES = Object.freeze({
5
5
  METHOD_NOT_FOUND: -32601,
6
6
  INVALID_PARAMS: -32602,
7
7
  INTERNAL_ERROR: -32603,
8
- RESOURCE_NOT_FOUND: -32002
8
+ RESOURCE_NOT_FOUND: -32002,
9
9
  });
10
10
  export class ToolError extends Error {
11
11
  code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-stdio-mcp-server",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "bugs": {
5
5
  "url": "https://github.com/poe-platform/poe-code/issues"
6
6
  },