@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 +213 -0
- package/dist/index.d.ts +16 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -18
- package/package.json +4 -3
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
|
-
* @
|
|
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
|
|
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;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
* @
|
|
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
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
147
|
+
lines.push(`--- PREVIEW (first 500 chars) ---`);
|
|
148
|
+
lines.push(response.preview);
|
|
149
|
+
lines.push(`--- END PREVIEW ---`);
|
|
150
|
+
lines.push(``);
|
|
130
151
|
}
|
|
131
|
-
|
|
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
|
|
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
|
|
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",
|