@ebowwa/large-output 1.1.0 → 1.2.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 +88 -134
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/package.json +21 -4
- package/rust/Cargo.lock +690 -0
- package/rust/Cargo.toml +29 -0
- package/rust/README.md +185 -0
- package/rust/src/lib.rs +596 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Shared utility for handling large MCP outputs with automatic file fallback and **actionable LLM prompts**.
|
|
4
4
|
|
|
5
|
+
**Available in TypeScript and Rust with full API parity.**
|
|
6
|
+
|
|
5
7
|
## Problem Solved
|
|
6
8
|
|
|
7
9
|
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.
|
|
@@ -9,55 +11,26 @@ When MCP tools return large outputs, they typically hit Claude's ~20,000 charact
|
|
|
9
11
|
**This library solves it by:**
|
|
10
12
|
- Returning content inline if under the threshold
|
|
11
13
|
- Automatically writing to a temp file if over the threshold
|
|
12
|
-
- **Returning actionable text that prompts the LLM to read the file**
|
|
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:
|
|
14
|
+
- **Returning actionable text that prompts the LLM to read the file**
|
|
28
15
|
|
|
29
|
-
|
|
16
|
+
---
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
[preview content...]
|
|
33
|
-
--- END PREVIEW ---
|
|
34
|
-
|
|
35
|
-
✅ File contains the complete data. Read it to proceed.
|
|
36
|
-
```
|
|
18
|
+
## TypeScript
|
|
37
19
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
## Installation
|
|
20
|
+
### Installation
|
|
41
21
|
|
|
42
22
|
```bash
|
|
43
|
-
# From npm
|
|
44
23
|
bun add @ebowwa/large-output
|
|
45
|
-
|
|
46
|
-
# Or from within the monorepo
|
|
47
|
-
bun add ../../packages/src/large-output
|
|
48
24
|
```
|
|
49
25
|
|
|
50
|
-
|
|
26
|
+
### Quick Start
|
|
51
27
|
|
|
52
28
|
```typescript
|
|
53
29
|
import { handleMCPOutput } from "@ebowwa/large-output";
|
|
54
30
|
|
|
55
31
|
// In your MCP tool handler:
|
|
56
32
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
// Fetch your data
|
|
60
|
-
const results = await fetchData(query);
|
|
33
|
+
const results = await fetchData();
|
|
61
34
|
const content = JSON.stringify(results, null, 2);
|
|
62
35
|
|
|
63
36
|
// Return with automatic file fallback (actionable format by default)
|
|
@@ -67,147 +40,128 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
67
40
|
});
|
|
68
41
|
```
|
|
69
42
|
|
|
70
|
-
|
|
43
|
+
### API
|
|
71
44
|
|
|
72
|
-
|
|
45
|
+
| Function | Description |
|
|
46
|
+
|----------|-------------|
|
|
47
|
+
| `handleOutput(content, options?)` | Handle output, returns `OutputResponse` |
|
|
48
|
+
| `handleMCPOutput(content, options?)` | Convenience: handle + return MCP string |
|
|
49
|
+
| `toMCPResponse(response, format)` | Convert `OutputResponse` to string |
|
|
50
|
+
| `handleBatch(contents, options?)` | Batch handler for multiple outputs |
|
|
73
51
|
|
|
74
|
-
|
|
52
|
+
### Options
|
|
75
53
|
|
|
76
54
|
| Option | Type | Default | Description |
|
|
77
55
|
|--------|------|---------|-------------|
|
|
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
|
|
85
|
-
|
|
86
|
-
### Response Formats
|
|
56
|
+
| `threshold` | `number` | `15000` | Character threshold for file fallback |
|
|
57
|
+
| `previewLength` | `number` | `500` | Preview chars when written to file |
|
|
58
|
+
| `tempDir` | `string` | `os.tmpdir()` | Custom temp directory |
|
|
59
|
+
| `filenamePrefix` | `string` | `"mcp_output"` | Filename prefix |
|
|
60
|
+
| `includeSize` | `boolean` | `true` | Include size in file response |
|
|
61
|
+
| `includePreview` | `boolean` | `true` | Include preview in file response |
|
|
62
|
+
| `responseFormat` | `"actionable"` \| `"json"` | `"actionable"` | Response format |
|
|
87
63
|
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
<raw content returned directly>
|
|
91
|
-
```
|
|
64
|
+
---
|
|
92
65
|
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
⚠️ Large output (126.5 KB) saved to file.
|
|
66
|
+
## Rust
|
|
96
67
|
|
|
97
|
-
|
|
68
|
+
Located in `./rust/` directory.
|
|
98
69
|
|
|
99
|
-
|
|
70
|
+
### Installation
|
|
100
71
|
|
|
101
|
-
|
|
102
|
-
{...preview...}
|
|
103
|
-
--- END PREVIEW ---
|
|
104
|
-
|
|
105
|
-
✅ File contains the complete data. Read it to proceed.
|
|
106
|
-
```
|
|
72
|
+
Add to your `Cargo.toml`:
|
|
107
73
|
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
}
|
|
74
|
+
```toml
|
|
75
|
+
[dependencies]
|
|
76
|
+
large-output = "1.2.0"
|
|
117
77
|
```
|
|
118
78
|
|
|
119
|
-
|
|
79
|
+
### Quick Start
|
|
120
80
|
|
|
121
|
-
|
|
81
|
+
```rust
|
|
82
|
+
use large_output::{handle_output, handle_mcp_output, OutputResponse};
|
|
122
83
|
|
|
123
|
-
|
|
84
|
+
let content = "very large content...".repeat(1000);
|
|
85
|
+
let response = handle_output(&content, None);
|
|
124
86
|
|
|
125
|
-
|
|
126
|
-
|
|
87
|
+
match response {
|
|
88
|
+
OutputResponse::Inline { content, .. } => println!("{}", content),
|
|
89
|
+
OutputResponse::File { path, size, .. } => {
|
|
90
|
+
println!("Written {} bytes to {:?}", size, path);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
127
93
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
94
|
+
// MCP convenience function
|
|
95
|
+
let mcp_text = handle_mcp_output(&content, None);
|
|
96
|
+
println!("{}", mcp_text);
|
|
131
97
|
```
|
|
132
98
|
|
|
133
|
-
###
|
|
99
|
+
### API
|
|
134
100
|
|
|
135
|
-
|
|
136
|
-
|
|
101
|
+
| Function | Description |
|
|
102
|
+
|----------|-------------|
|
|
103
|
+
| `handle_output(content, options)` | Handle output, returns `OutputResponse` |
|
|
104
|
+
| `handle_mcp_output(content, options)` | Convenience: handle + return MCP string |
|
|
105
|
+
| `to_mcp_response(response, format)` | Convert `OutputResponse` to string |
|
|
106
|
+
| `handle_batch(contents, options)` | Batch handler for multiple outputs |
|
|
107
|
+
| `OptionsBuilder::new()...build()` | Builder for `LargeOutputOptions` |
|
|
137
108
|
|
|
138
|
-
|
|
139
|
-
threshold: 20000,
|
|
140
|
-
filenamePrefix: "github_search",
|
|
141
|
-
});
|
|
109
|
+
### Options
|
|
142
110
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Convert to MCP return format (actionable by default)
|
|
148
|
-
return toMCPResponse(response);
|
|
111
|
+
```rust
|
|
112
|
+
use large_output::{OptionsBuilder, ResponseFormat};
|
|
149
113
|
|
|
150
|
-
|
|
151
|
-
|
|
114
|
+
let options = OptionsBuilder::new()
|
|
115
|
+
.threshold(20000)
|
|
116
|
+
.preview_length(1000)
|
|
117
|
+
.filename_prefix("github_search")
|
|
118
|
+
.response_format(ResponseFormat::Json)
|
|
119
|
+
.build();
|
|
152
120
|
```
|
|
153
121
|
|
|
154
|
-
###
|
|
122
|
+
### Feature Flags
|
|
155
123
|
|
|
156
|
-
|
|
157
|
-
|
|
124
|
+
- `default` - Synchronous file operations
|
|
125
|
+
- `async` - Async file operations with tokio
|
|
158
126
|
|
|
159
|
-
|
|
160
|
-
// Each output handled independently
|
|
161
|
-
```
|
|
127
|
+
---
|
|
162
128
|
|
|
163
|
-
##
|
|
129
|
+
## Response Formats
|
|
164
130
|
|
|
165
|
-
###
|
|
166
|
-
```typescript
|
|
167
|
-
import { handleMCPOutput } from "@ebowwa/large-output";
|
|
131
|
+
### Inline (under threshold)
|
|
168
132
|
|
|
169
|
-
|
|
170
|
-
// Fetch all pages, accumulate results
|
|
171
|
-
return handleMCPOutput(JSON.stringify(results, null, 2));
|
|
172
|
-
```
|
|
133
|
+
Content returned directly.
|
|
173
134
|
|
|
174
|
-
###
|
|
175
|
-
```typescript
|
|
176
|
-
import { handleMCPOutput } from "@ebowwa/large-output";
|
|
135
|
+
### File - Actionable format (default)
|
|
177
136
|
|
|
178
|
-
const conversations = await getConversations({ limit: 1000 });
|
|
179
|
-
return handleMCPOutput(JSON.stringify(conversations, null, 2));
|
|
180
137
|
```
|
|
138
|
+
Large output (126.5 KB) saved to file.
|
|
181
139
|
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
import { handleMCPOutput } from "@ebowwa/large-output";
|
|
140
|
+
ACTION REQUIRED: Use the Read tool to read this file:
|
|
185
141
|
|
|
186
|
-
|
|
187
|
-
return handleMCPOutput(JSON.stringify(commits, null, 2));
|
|
188
|
-
```
|
|
142
|
+
/tmp/mcp_output_2025-02-19T12-38-00_abc123.txt
|
|
189
143
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
144
|
+
--- PREVIEW (first 500 chars) ---
|
|
145
|
+
{...preview...}
|
|
146
|
+
--- END PREVIEW ---
|
|
193
147
|
|
|
194
|
-
|
|
195
|
-
return handleMCPOutput(JSON.stringify(packages, null, 2));
|
|
148
|
+
File contains the complete data. Read it to proceed.
|
|
196
149
|
```
|
|
197
150
|
|
|
198
|
-
|
|
151
|
+
### File - JSON format
|
|
199
152
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
handleMCPOutput(content, { responseFormat: "json" });
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"type": "file",
|
|
156
|
+
"path": "/tmp/mcp_output_2025-02-19T12-38-00_abc123.txt",
|
|
157
|
+
"size": 126507,
|
|
158
|
+
"sizeFormatted": "126.5 KB",
|
|
159
|
+
"preview": "..."
|
|
160
|
+
}
|
|
209
161
|
```
|
|
210
162
|
|
|
163
|
+
---
|
|
164
|
+
|
|
211
165
|
## License
|
|
212
166
|
|
|
213
167
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -92,7 +92,7 @@ export type OutputResponse = InlineOutputResponse | FileOutputResponse;
|
|
|
92
92
|
*/
|
|
93
93
|
export declare function handleOutput(content: string, options?: LargeOutputOptions): OutputResponse;
|
|
94
94
|
/**
|
|
95
|
-
* Convert an OutputResponse to a
|
|
95
|
+
* Convert an OutputResponse to a string for MCP tool return
|
|
96
96
|
*
|
|
97
97
|
* @param response - The response from handleOutput()
|
|
98
98
|
* @param format - Response format: "actionable" (default) or "json"
|
|
@@ -101,7 +101,7 @@ export declare function handleOutput(content: string, options?: LargeOutputOptio
|
|
|
101
101
|
* @example
|
|
102
102
|
* ```ts
|
|
103
103
|
* const response = handleOutput(largeContent);
|
|
104
|
-
* return
|
|
104
|
+
* return toMCPResponse(response);
|
|
105
105
|
* ```
|
|
106
106
|
*/
|
|
107
107
|
export declare function toMCPResponse(response: OutputResponse, format?: "actionable" | "json"): string;
|
package/dist/index.js
CHANGED
|
@@ -100,7 +100,7 @@ export function handleOutput(content, options = {}) {
|
|
|
100
100
|
return response;
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
|
-
* Convert an OutputResponse to a
|
|
103
|
+
* Convert an OutputResponse to a string for MCP tool return
|
|
104
104
|
*
|
|
105
105
|
* @param response - The response from handleOutput()
|
|
106
106
|
* @param format - Response format: "actionable" (default) or "json"
|
|
@@ -109,7 +109,7 @@ export function handleOutput(content, options = {}) {
|
|
|
109
109
|
* @example
|
|
110
110
|
* ```ts
|
|
111
111
|
* const response = handleOutput(largeContent);
|
|
112
|
-
* return
|
|
112
|
+
* return toMCPResponse(response);
|
|
113
113
|
* ```
|
|
114
114
|
*/
|
|
115
115
|
export function toMCPResponse(response, format = "actionable") {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebowwa/large-output",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Shared utility for handling large MCP outputs with automatic file fallback and actionable LLM prompts",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Shared utility for handling large MCP outputs with automatic file fallback and actionable LLM prompts. Includes TypeScript and Rust implementations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -12,10 +12,13 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"dist"
|
|
15
|
+
"dist",
|
|
16
|
+
"rust"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
18
19
|
"build": "tsc",
|
|
20
|
+
"build:rust": "cd rust && cargo build --release",
|
|
21
|
+
"test": "tsc --noEmit && cd rust && cargo test",
|
|
19
22
|
"prepublishOnly": "bun run build"
|
|
20
23
|
},
|
|
21
24
|
"keywords": [
|
|
@@ -23,12 +26,26 @@
|
|
|
23
26
|
"output",
|
|
24
27
|
"pagination",
|
|
25
28
|
"file-fallback",
|
|
26
|
-
"llm-actionable"
|
|
29
|
+
"llm-actionable",
|
|
30
|
+
"rust",
|
|
31
|
+
"typescript"
|
|
27
32
|
],
|
|
28
33
|
"author": "ebowwa",
|
|
29
34
|
"license": "MIT",
|
|
30
35
|
"devDependencies": {
|
|
31
36
|
"@types/node": "^22.10.2",
|
|
32
37
|
"typescript": "^5.7.2"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/ebowwa/codespaces",
|
|
42
|
+
"directory": "packages/src/ai/large-output"
|
|
43
|
+
},
|
|
44
|
+
"ownership": {
|
|
45
|
+
"domain": "tooling",
|
|
46
|
+
"responsibilities": [
|
|
47
|
+
"output-handling",
|
|
48
|
+
"large-response-management"
|
|
49
|
+
]
|
|
33
50
|
}
|
|
34
51
|
}
|