tiny-stdio-mcp-server 0.1.8 → 0.1.9

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.
@@ -1,3 +1,4 @@
1
+ import { type FromUrlOptions } from "./remote.js";
1
2
  export interface AudioContent {
2
3
  type: "audio";
3
4
  data: string;
@@ -7,7 +8,7 @@ export declare class Audio {
7
8
  private readonly base64Data;
8
9
  private readonly mimeType;
9
10
  private constructor();
10
- static fromUrl(url: string): Promise<Audio>;
11
+ static fromUrl(url: string, options?: FromUrlOptions): Promise<Audio>;
11
12
  static fromBytes(data: Uint8Array, format?: string): Audio;
12
13
  static fromBase64(base64: string, mimeType: string): Audio;
13
14
  toContentBlock(): AudioContent;
@@ -1,4 +1,5 @@
1
1
  import { assertBase64, fileTypeFromBuffer, parseContentType, safeRemoteLabel } from "./mime.js";
2
+ import { readRemoteBytes } from "./remote.js";
2
3
  const SUPPORTED_AUDIO_MIMES = new Set([
3
4
  "audio/mpeg",
4
5
  "audio/wav",
@@ -19,13 +20,12 @@ export class Audio {
19
20
  this.base64Data = base64Data;
20
21
  this.mimeType = mimeType;
21
22
  }
22
- static async fromUrl(url) {
23
+ static async fromUrl(url, options) {
23
24
  const response = await fetch(url);
24
25
  if (!response.ok) {
25
26
  throw new Error(`Failed to fetch audio from ${safeRemoteLabel(url)}: ${response.status} ${response.statusText}`);
26
27
  }
27
- const arrayBuffer = await response.arrayBuffer();
28
- const data = new Uint8Array(arrayBuffer);
28
+ const data = await readRemoteBytes(response, "audio", url, options);
29
29
  const detected = fileTypeFromBuffer(data);
30
30
  let mimeType;
31
31
  if (detected && SUPPORTED_AUDIO_MIMES.has(detected.mime)) {
@@ -1,3 +1,4 @@
1
+ import { type FromUrlOptions } from "./remote.js";
1
2
  export interface TextResourceContents {
2
3
  uri: string;
3
4
  mimeType: string;
@@ -19,7 +20,7 @@ export declare class File {
19
20
  private readonly name?;
20
21
  private readonly charset;
21
22
  private constructor();
22
- static fromUrl(url: string): Promise<File>;
23
+ static fromUrl(url: string, options?: FromUrlOptions): Promise<File>;
23
24
  static fromBytes(data: Uint8Array, mimeType: string): File;
24
25
  static fromText(text: string, mimeType?: string): File;
25
26
  static fromBase64(base64: string, mimeType: string): File;
@@ -1,4 +1,5 @@
1
1
  import { assertBase64, fileTypeFromBuffer, parseContentType, safeRemoteLabel } from "./mime.js";
2
+ import { readRemoteBytes } from "./remote.js";
2
3
  function isTextMimeType(mimeType) {
3
4
  const normalizedMimeType = mimeType.toLowerCase();
4
5
  return (normalizedMimeType.startsWith("text/") ||
@@ -22,13 +23,12 @@ export class File {
22
23
  this.name = name;
23
24
  this.charset = charset;
24
25
  }
25
- static async fromUrl(url) {
26
+ static async fromUrl(url, options) {
26
27
  const response = await fetch(url);
27
28
  if (!response.ok) {
28
29
  throw new Error(`Failed to fetch file from ${safeRemoteLabel(url)}: ${response.status} ${response.statusText}`);
29
30
  }
30
- const arrayBuffer = await response.arrayBuffer();
31
- const data = new Uint8Array(arrayBuffer);
31
+ const data = await readRemoteBytes(response, "file", url, options);
32
32
  const detected = fileTypeFromBuffer(data);
33
33
  let mimeType;
34
34
  if (detected) {
@@ -1,3 +1,4 @@
1
+ import { type FromUrlOptions } from "./remote.js";
1
2
  export interface ImageContent {
2
3
  type: "image";
3
4
  data: string;
@@ -7,7 +8,7 @@ export declare class Image {
7
8
  private readonly base64Data;
8
9
  private readonly mimeType;
9
10
  private constructor();
10
- static fromUrl(url: string): Promise<Image>;
11
+ static fromUrl(url: string, options?: FromUrlOptions): Promise<Image>;
11
12
  static fromBytes(data: Uint8Array, format?: string): Image;
12
13
  static fromBase64(base64: string, mimeType: string): Image;
13
14
  toContentBlock(): ImageContent;
@@ -1,4 +1,5 @@
1
1
  import { assertBase64, fileTypeFromBuffer, parseContentType, safeRemoteLabel } from "./mime.js";
2
+ import { readRemoteBytes } from "./remote.js";
2
3
  const SUPPORTED_IMAGE_MIMES = new Set([
3
4
  "image/png",
4
5
  "image/jpeg",
@@ -12,13 +13,12 @@ export class Image {
12
13
  this.base64Data = base64Data;
13
14
  this.mimeType = mimeType;
14
15
  }
15
- static async fromUrl(url) {
16
+ static async fromUrl(url, options) {
16
17
  const response = await fetch(url);
17
18
  if (!response.ok) {
18
19
  throw new Error(`Failed to fetch image from ${safeRemoteLabel(url)}: ${response.status} ${response.statusText}`);
19
20
  }
20
- const arrayBuffer = await response.arrayBuffer();
21
- const data = new Uint8Array(arrayBuffer);
21
+ const data = await readRemoteBytes(response, "image", url, options);
22
22
  const detected = fileTypeFromBuffer(data);
23
23
  let mimeType;
24
24
  if (detected && SUPPORTED_IMAGE_MIMES.has(detected.mime)) {
@@ -1,4 +1,6 @@
1
1
  export { fileTypeFromBuffer, type FileTypeResult } from "./mime.js";
2
+ export { DEFAULT_FROM_URL_MAX_BYTES } from "./remote.js";
3
+ export type { FromUrlOptions } from "./remote.js";
2
4
  export { Image, type ImageContent } from "./image.js";
3
5
  export { Audio, type AudioContent } from "./audio.js";
4
6
  export { File, type EmbeddedResource, type TextResourceContents, type BlobResourceContents, } from "./file.js";
@@ -1,5 +1,6 @@
1
1
  // MIME detection
2
2
  export { fileTypeFromBuffer } from "./mime.js";
3
+ export { DEFAULT_FROM_URL_MAX_BYTES } from "./remote.js";
3
4
  // Content helpers
4
5
  export { Image } from "./image.js";
5
6
  export { Audio } from "./audio.js";
@@ -0,0 +1,5 @@
1
+ export declare const DEFAULT_FROM_URL_MAX_BYTES: number;
2
+ export interface FromUrlOptions {
3
+ maxBytes?: number;
4
+ }
5
+ export declare function readRemoteBytes(response: Response, kind: string, url: string, options?: FromUrlOptions): Promise<Uint8Array>;
@@ -0,0 +1,69 @@
1
+ import { safeRemoteLabel } from "./mime.js";
2
+ export const DEFAULT_FROM_URL_MAX_BYTES = 5 * 1024 * 1024;
3
+ function resolveMaxBytes(options) {
4
+ const maxBytes = options?.maxBytes ?? DEFAULT_FROM_URL_MAX_BYTES;
5
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) {
6
+ throw new Error("maxBytes must be a positive integer");
7
+ }
8
+ return maxBytes;
9
+ }
10
+ function createSizeError(kind, url, maxBytes) {
11
+ return new Error(`Remote ${kind} from ${safeRemoteLabel(url)} exceeds maximum size of ${maxBytes} bytes`);
12
+ }
13
+ function readContentLength(response) {
14
+ const rawContentLength = response.headers.get("content-length");
15
+ if (typeof rawContentLength !== "string") {
16
+ return undefined;
17
+ }
18
+ const contentLength = Number(rawContentLength.trim());
19
+ if (!Number.isSafeInteger(contentLength) || contentLength < 0) {
20
+ return undefined;
21
+ }
22
+ return contentLength;
23
+ }
24
+ function combineChunks(chunks, totalBytes) {
25
+ const combined = new Uint8Array(totalBytes);
26
+ let offset = 0;
27
+ for (const chunk of chunks) {
28
+ combined.set(chunk, offset);
29
+ offset += chunk.byteLength;
30
+ }
31
+ return combined;
32
+ }
33
+ async function readStreamBytes(body, kind, url, maxBytes) {
34
+ const reader = body.getReader();
35
+ const chunks = [];
36
+ let totalBytes = 0;
37
+ try {
38
+ while (true) {
39
+ const { done, value } = await reader.read();
40
+ if (done) {
41
+ return combineChunks(chunks, totalBytes);
42
+ }
43
+ totalBytes += value.byteLength;
44
+ if (totalBytes > maxBytes) {
45
+ await reader.cancel().catch(() => undefined);
46
+ throw createSizeError(kind, url, maxBytes);
47
+ }
48
+ chunks.push(value);
49
+ }
50
+ }
51
+ finally {
52
+ reader.releaseLock();
53
+ }
54
+ }
55
+ export async function readRemoteBytes(response, kind, url, options) {
56
+ const maxBytes = resolveMaxBytes(options);
57
+ const contentLength = readContentLength(response);
58
+ if (contentLength !== undefined && contentLength > maxBytes) {
59
+ throw createSizeError(kind, url, maxBytes);
60
+ }
61
+ if (response.body) {
62
+ return readStreamBytes(response.body, kind, url, maxBytes);
63
+ }
64
+ const arrayBuffer = await response.arrayBuffer();
65
+ if (arrayBuffer.byteLength > maxBytes) {
66
+ throw createSizeError(kind, url, maxBytes);
67
+ }
68
+ return new Uint8Array(arrayBuffer);
69
+ }
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@ export { createServer } from "./server.js";
2
2
  export type { MessageHandler, MessageSession, Server } from "./server.js";
3
3
  export { defineSchema } from "./schema.js";
4
4
  export type { TypedSchema } from "./schema.js";
5
- export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, } from "./content/index.js";
6
- export type { ImageContent, AudioContent, EmbeddedResource, TextResourceContents, BlobResourceContents, ContentBlock, TextContent, FileTypeResult, } from "./content/index.js";
5
+ export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, DEFAULT_FROM_URL_MAX_BYTES, } from "./content/index.js";
6
+ export type { ImageContent, AudioContent, EmbeddedResource, TextResourceContents, BlobResourceContents, ContentBlock, TextContent, FileTypeResult, FromUrlOptions, } from "./content/index.js";
7
7
  export type { ToolReturn } from "./content/index.js";
8
8
  export type { ServerOptions, ToolHandler, ToolDefinition, Tool, ToolAnnotations, ToolExecution, Icon, ContentAnnotations, ResourceLink, CallToolResult, PromptContentItem, PromptArgument, Prompt, PromptMessage, GetPromptResult, PromptHandler, PromptDefinition, Resource, ResourceTemplate, ResourceContents, ReadResourceResult, ResourceHandler, ResourceDefinition, ResourceTemplateDefinition, HandleResult, ContentItem, JSONSchema, JSONSchemaProperty, Transport, SDKTransport, JSONRPCRequest, JSONRPCResponse, JSONRPCError, JSONRPCMessage, JSONRPCNotification, InitializeResult, } from "./types.js";
9
9
  export { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
package/dist/index.js CHANGED
@@ -3,5 +3,5 @@ export { createServer } from "./server.js";
3
3
  // Schema
4
4
  export { defineSchema } from "./schema.js";
5
5
  // Content helpers
6
- export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, } from "./content/index.js";
6
+ export { Image, Audio, File, toContentBlocks, fileTypeFromBuffer, DEFAULT_FROM_URL_MAX_BYTES, } from "./content/index.js";
7
7
  export { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-stdio-mcp-server",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "bugs": {
5
5
  "url": "https://github.com/poe-platform/poe-code/issues"
6
6
  },