@ebowwa/large-output 1.0.3 → 1.1.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.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # @ebowwa/large-output
2
+
3
+ Shared utility for handling large MCP outputs with automatic file fallback and **actionable LLM prompts**.
4
+
5
+ ## Problem Solved
6
+
7
+ When MCP tools return large outputs, they typically hit Claude's ~20,000 character tool output limit. Common solutions like pagination fragment the context and require multiple round-trips.
8
+
9
+ **This library solves it by:**
10
+ - Returning content inline if under the threshold
11
+ - Automatically writing to a temp file if over the threshold
12
+ - **Returning actionable text that prompts the LLM to read the file** (NEW in v1.1.0)
13
+
14
+ ## What's New in v1.1.0
15
+
16
+ **Actionable Response Format (Default):**
17
+
18
+ Instead of returning JSON that LLMs often ignore:
19
+ ```json
20
+ {"type": "file", "path": "...", "size": 126507}
21
+ ```
22
+
23
+ Now returns actionable text:
24
+ ```
25
+ ⚠️ Large output (126.5 KB) saved to file.
26
+
27
+ 📖 **ACTION REQUIRED**: Use the Read tool to read this file:
28
+
29
+ /tmp/mcp_output_2025-02-19T12-38-00_abc123.txt
30
+
31
+ --- PREVIEW (first 500 chars) ---
32
+ [preview content...]
33
+ --- END PREVIEW ---
34
+
35
+ ✅ File contains the complete data. Read it to proceed.
36
+ ```
37
+
38
+ This format is designed to **prompt the LLM to take action** and read the file.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ # From npm
44
+ bun add @ebowwa/large-output
45
+
46
+ # Or from within the monorepo
47
+ bun add ../../packages/src/large-output
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```typescript
53
+ import { handleMCPOutput } from "@ebowwa/large-output";
54
+
55
+ // In your MCP tool handler:
56
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
57
+ const { query } = request.params.arguments as { query: string };
58
+
59
+ // Fetch your data
60
+ const results = await fetchData(query);
61
+ const content = JSON.stringify(results, null, 2);
62
+
63
+ // Return with automatic file fallback (actionable format by default)
64
+ return {
65
+ content: [{ type: "text", text: handleMCPOutput(content) }],
66
+ };
67
+ });
68
+ ```
69
+
70
+ ## API
71
+
72
+ ### `handleMCPOutput(content, options?)`
73
+
74
+ Convenience function that handles output and returns MCP-formatted string.
75
+
76
+ | Option | Type | Default | Description |
77
+ |--------|------|---------|-------------|
78
+ | `threshold` | number | `15000` | Character threshold for file fallback |
79
+ | `previewLength` | number | `500` | Preview chars when written to file |
80
+ | `tempDir` | string | `os.tmpdir()` | Custom temp directory |
81
+ | `filenamePrefix` | string | `"mcp_output"` | Filename prefix |
82
+ | `includeSize` | boolean | `true` | Include size in file response |
83
+ | `includePreview` | boolean | `true` | Include preview in file response |
84
+ | `responseFormat` | `"actionable"` \| `"json"` | `"actionable"` | Response format for file outputs |
85
+
86
+ ### Response Formats
87
+
88
+ **Inline (under threshold):**
89
+ ```
90
+ <raw content returned directly>
91
+ ```
92
+
93
+ **File - Actionable format (default):**
94
+ ```
95
+ ⚠️ Large output (126.5 KB) saved to file.
96
+
97
+ 📖 **ACTION REQUIRED**: Use the Read tool to read this file:
98
+
99
+ /tmp/mcp_output_2025-02-19T12-38-00_abc123.txt
100
+
101
+ --- PREVIEW (first 500 chars) ---
102
+ {...preview...}
103
+ --- END PREVIEW ---
104
+
105
+ ✅ File contains the complete data. Read it to proceed.
106
+ ```
107
+
108
+ **File - JSON format (for programmatic consumers):**
109
+ ```json
110
+ {
111
+ "type": "file",
112
+ "path": "/tmp/mcp_output_2025-02-19T12-38-00_abc123.txt",
113
+ "size": 126507,
114
+ "sizeFormatted": "126.5 KB",
115
+ "preview": "..."
116
+ }
117
+ ```
118
+
119
+ ## Advanced Usage
120
+
121
+ ### Using JSON format
122
+
123
+ If you need JSON for programmatic consumers:
124
+
125
+ ```typescript
126
+ import { handleMCPOutput } from "@ebowwa/large-output";
127
+
128
+ return handleMCPOutput(JSON.stringify(results), {
129
+ responseFormat: "json",
130
+ });
131
+ ```
132
+
133
+ ### Direct response handling
134
+
135
+ ```typescript
136
+ import { handleOutput, toMCPResponse } from "@ebowwa/large-output";
137
+
138
+ const response = handleOutput(largeContent, {
139
+ threshold: 20000,
140
+ filenamePrefix: "github_search",
141
+ });
142
+
143
+ if (response.type === "file") {
144
+ console.log(`Written ${response.sizeFormatted} to ${response.path}`);
145
+ }
146
+
147
+ // Convert to MCP return format (actionable by default)
148
+ return toMCPResponse(response);
149
+
150
+ // Or explicitly use JSON format
151
+ return toMCPResponse(response, "json");
152
+ ```
153
+
154
+ ### Batch handling
155
+
156
+ ```typescript
157
+ import { handleBatch } from "@ebowwa/large-output";
158
+
159
+ const outputs = handleBatch([data1, data2, data3]);
160
+ // Each output handled independently
161
+ ```
162
+
163
+ ## Examples by MCP Server
164
+
165
+ ### github-search
166
+ ```typescript
167
+ import { handleMCPOutput } from "@ebowwa/large-output";
168
+
169
+ const results = await githubApi.search(query, { per_page: 100 });
170
+ // Fetch all pages, accumulate results
171
+ return handleMCPOutput(JSON.stringify(results, null, 2));
172
+ ```
173
+
174
+ ### claude-code-history
175
+ ```typescript
176
+ import { handleMCPOutput } from "@ebowwa/large-output";
177
+
178
+ const conversations = await getConversations({ limit: 1000 });
179
+ return handleMCPOutput(JSON.stringify(conversations, null, 2));
180
+ ```
181
+
182
+ ### git
183
+ ```typescript
184
+ import { handleMCPOutput } from "@ebowwa/large-output";
185
+
186
+ const commits = await gitLog({ maxCount: 500 });
187
+ return handleMCPOutput(JSON.stringify(commits, null, 2));
188
+ ```
189
+
190
+ ### npm-publish
191
+ ```typescript
192
+ import { handleMCPOutput } from "@ebowwa/large-output";
193
+
194
+ const packages = await searchPackages({ limit: 500 });
195
+ return handleMCPOutput(JSON.stringify(packages, null, 2));
196
+ ```
197
+
198
+ ## Migration from v1.0.x
199
+
200
+ No code changes needed! The default behavior now uses actionable format.
201
+
202
+ If you relied on JSON format:
203
+ ```typescript
204
+ // Old (v1.0.x) - JSON was default
205
+ handleMCPOutput(content);
206
+
207
+ // New (v1.1.0) - Explicitly request JSON
208
+ handleMCPOutput(content, { responseFormat: "json" });
209
+ ```
210
+
211
+ ## License
212
+
213
+ MIT
package/dist/index.d.ts CHANGED
@@ -40,6 +40,13 @@ export interface LargeOutputOptions {
40
40
  * @default true
41
41
  */
42
42
  includePreview?: boolean;
43
+ /**
44
+ * Response format for file outputs
45
+ * - "actionable" (default): Returns actionable text prompting LLM to read the file
46
+ * - "json": Returns structured JSON object
47
+ * @default "actionable"
48
+ */
49
+ responseFormat?: "actionable" | "json";
43
50
  }
44
51
  /**
45
52
  * Response when output is returned inline (under threshold)
@@ -88,7 +95,8 @@ export declare function handleOutput(content: string, options?: LargeOutputOptio
88
95
  * Convert an OutputResponse to a JSON string for MCP tool return
89
96
  *
90
97
  * @param response - The response from handleOutput()
91
- * @returns JSON string suitable for MCP tool return value
98
+ * @param format - Response format: "actionable" (default) or "json"
99
+ * @returns Text or JSON string suitable for MCP tool return value
92
100
  *
93
101
  * @example
94
102
  * ```ts
@@ -96,20 +104,23 @@ export declare function handleOutput(content: string, options?: LargeOutputOptio
96
104
  * return JSON.stringify(toMCPResponse(response));
97
105
  * ```
98
106
  */
99
- export declare function toMCPResponse(response: OutputResponse): string;
107
+ export declare function toMCPResponse(response: OutputResponse, format?: "actionable" | "json"): string;
100
108
  /**
101
109
  * Convenience function: handle output and return MCP-formatted string
102
110
  *
103
111
  * @param content - The content to return
104
- * @param options - Configuration options
105
- * @returns JSON string or content directly
112
+ * @param options - Configuration options (including responseFormat)
113
+ * @returns Text string with actionable instructions (default) or JSON
106
114
  *
107
115
  * @example
108
116
  * ```ts
109
117
  * import { handleMCPOutput } from "@ebowwa/large-output";
110
118
  *
111
- * // In your MCP tool handler:
119
+ * // In your MCP tool handler (default: actionable format):
112
120
  * return handleMCPOutput(JSON.stringify(results));
121
+ *
122
+ * // For JSON format:
123
+ * return handleMCPOutput(JSON.stringify(results), { responseFormat: "json" });
113
124
  * ```
114
125
  */
115
126
  export declare function handleMCPOutput(content: string, options?: LargeOutputOptions): string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;AAuCvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,cAAc,CAwChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAwB9D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAGR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE,kBAAuB,GAC/B,cAAc,EAAE,CAElB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;AAwCvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,cAAc,CAwChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,cAAc,EACxB,MAAM,GAAE,YAAY,GAAG,MAAqB,GAC3C,MAAM,CAgDR;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAGR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,GAAE,kBAAuB,GAC/B,cAAc,EAAE,CAElB"}
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ const DEFAULT_OPTIONS = {
17
17
  previewLength: 500,
18
18
  includeSize: true,
19
19
  includePreview: true,
20
+ responseFormat: "actionable",
20
21
  };
21
22
  /**
22
23
  * Format bytes to human-readable string
@@ -102,7 +103,8 @@ export function handleOutput(content, options = {}) {
102
103
  * Convert an OutputResponse to a JSON string for MCP tool return
103
104
  *
104
105
  * @param response - The response from handleOutput()
105
- * @returns JSON string suitable for MCP tool return value
106
+ * @param format - Response format: "actionable" (default) or "json"
107
+ * @returns Text or JSON string suitable for MCP tool return value
106
108
  *
107
109
  * @example
108
110
  * ```ts
@@ -110,44 +112,67 @@ export function handleOutput(content, options = {}) {
110
112
  * return JSON.stringify(toMCPResponse(response));
111
113
  * ```
112
114
  */
113
- export function toMCPResponse(response) {
115
+ export function toMCPResponse(response, format = "actionable") {
114
116
  if (response.type === "inline") {
115
117
  return response.content;
116
118
  }
117
- // For file type, return structured JSON
118
- const result = {
119
- type: "file",
120
- path: response.path,
121
- };
122
- if (response.size !== undefined) {
123
- result.size = response.size;
124
- }
125
- if (response.sizeFormatted !== undefined) {
126
- result.sizeFormatted = response.sizeFormatted;
119
+ // JSON format for programmatic consumers
120
+ if (format === "json") {
121
+ const result = {
122
+ type: "file",
123
+ path: response.path,
124
+ };
125
+ if (response.size !== undefined) {
126
+ result.size = response.size;
127
+ }
128
+ if (response.sizeFormatted !== undefined) {
129
+ result.sizeFormatted = response.sizeFormatted;
130
+ }
131
+ if (response.preview !== undefined) {
132
+ result.preview = response.preview;
133
+ }
134
+ return JSON.stringify(result, null, 2);
127
135
  }
136
+ // Actionable format - prompts LLM to read the file (DEFAULT)
137
+ // This is critical - JSON alone gets ignored by LLMs
138
+ const lines = [
139
+ `⚠️ Large output (${response.sizeFormatted || response.size} characters) saved to file.`,
140
+ ``,
141
+ `📖 **ACTION REQUIRED**: Use the Read tool to read this file:`,
142
+ ``,
143
+ ` ${response.path}`,
144
+ ``,
145
+ ];
128
146
  if (response.preview !== undefined) {
129
- result.preview = response.preview;
147
+ lines.push(`--- PREVIEW (first 500 chars) ---`);
148
+ lines.push(response.preview);
149
+ lines.push(`--- END PREVIEW ---`);
150
+ lines.push(``);
130
151
  }
131
- return JSON.stringify(result, null, 2);
152
+ lines.push(`✅ File contains the complete data. Read it to proceed.`);
153
+ return lines.join("\n");
132
154
  }
133
155
  /**
134
156
  * Convenience function: handle output and return MCP-formatted string
135
157
  *
136
158
  * @param content - The content to return
137
- * @param options - Configuration options
138
- * @returns JSON string or content directly
159
+ * @param options - Configuration options (including responseFormat)
160
+ * @returns Text string with actionable instructions (default) or JSON
139
161
  *
140
162
  * @example
141
163
  * ```ts
142
164
  * import { handleMCPOutput } from "@ebowwa/large-output";
143
165
  *
144
- * // In your MCP tool handler:
166
+ * // In your MCP tool handler (default: actionable format):
145
167
  * return handleMCPOutput(JSON.stringify(results));
168
+ *
169
+ * // For JSON format:
170
+ * return handleMCPOutput(JSON.stringify(results), { responseFormat: "json" });
146
171
  * ```
147
172
  */
148
173
  export function handleMCPOutput(content, options = {}) {
149
174
  const response = handleOutput(content, options);
150
- return toMCPResponse(response);
175
+ return toMCPResponse(response, options.responseFormat);
151
176
  }
152
177
  /**
153
178
  * Batch handler for multiple outputs
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ebowwa/large-output",
3
- "version": "1.0.3",
4
- "description": "Shared utility for handling large MCP outputs with automatic file fallback",
3
+ "version": "1.1.0",
4
+ "description": "Shared utility for handling large MCP outputs with automatic file fallback and actionable LLM prompts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -22,7 +22,8 @@
22
22
  "mcp",
23
23
  "output",
24
24
  "pagination",
25
- "file-fallback"
25
+ "file-fallback",
26
+ "llm-actionable"
26
27
  ],
27
28
  "author": "ebowwa",
28
29
  "license": "MIT",