@ebowwa/large-output 1.1.0 → 1.3.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 CHANGED
@@ -1,100 +1,97 @@
1
- # @ebowwa/large-output
1
+ # large-output
2
2
 
3
- Shared utility for handling large MCP outputs with automatic file fallback and **actionable LLM prompts**.
3
+ Utility for handling large outputs with automatic file fallback and **actionable LLM prompts**.
4
+
5
+ **Primary distribution: Rust crate** (TypeScript source preserved in `typescript/` for reference).
4
6
 
5
7
  ## Problem Solved
6
8
 
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.
9
+ When tools return large outputs, they typically hit context limits. Common solutions like pagination fragment the context and require multiple round-trips.
8
10
 
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** (NEW in v1.1.0)
14
+ - **Returning actionable text that prompts the LLM to read the file**
13
15
 
14
- ## What's New in v1.1.0
16
+ ---
15
17
 
16
- **Actionable Response Format (Default):**
18
+ ## Rust (Primary)
17
19
 
18
- Instead of returning JSON that LLMs often ignore:
19
- ```json
20
- {"type": "file", "path": "...", "size": 126507}
21
- ```
20
+ ### Installation
22
21
 
23
- Now returns actionable text:
22
+ ```bash
23
+ cargo add large-output
24
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
25
 
31
- --- PREVIEW (first 500 chars) ---
32
- [preview content...]
33
- --- END PREVIEW ---
26
+ Or add to `Cargo.toml`:
34
27
 
35
- ✅ File contains the complete data. Read it to proceed.
28
+ ```toml
29
+ [dependencies]
30
+ large-output = "1.3"
36
31
  ```
37
32
 
38
- This format is designed to **prompt the LLM to take action** and read the file.
33
+ ### Quick Start
39
34
 
40
- ## Installation
35
+ ```rust
36
+ use large_output::{handle_output, handle_mcp_output, OutputResponse};
41
37
 
42
- ```bash
43
- # From npm
44
- bun add @ebowwa/large-output
38
+ let content = "very large content...".repeat(1000);
39
+ let response = handle_output(&content, None);
40
+
41
+ match response {
42
+ OutputResponse::Inline { content, .. } => println!("{}", content),
43
+ OutputResponse::File { path, size, .. } => {
44
+ println!("Written {} bytes to {:?}", size, path);
45
+ }
46
+ }
45
47
 
46
- # Or from within the monorepo
47
- bun add ../../packages/src/large-output
48
+ // MCP convenience function
49
+ let mcp_text = handle_mcp_output(&content, None);
50
+ println!("{}", mcp_text);
48
51
  ```
49
52
 
50
- ## Quick Start
53
+ ### API
51
54
 
52
- ```typescript
53
- import { handleMCPOutput } from "@ebowwa/large-output";
55
+ | Function | Description |
56
+ |----------|-------------|
57
+ | `handle_output(content, options)` | Handle output, returns `OutputResponse` |
58
+ | `handle_mcp_output(content, options)` | Convenience: handle + return MCP string |
59
+ | `to_mcp_response(response, format)` | Convert `OutputResponse` to string |
60
+ | `handle_batch(contents, options)` | Batch handler for multiple outputs |
61
+ | `OptionsBuilder::new()...build()` | Builder for `LargeOutputOptions` |
54
62
 
55
- // In your MCP tool handler:
56
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
57
- const { query } = request.params.arguments as { query: string };
63
+ ### Options
58
64
 
59
- // Fetch your data
60
- const results = await fetchData(query);
61
- const content = JSON.stringify(results, null, 2);
65
+ ```rust
66
+ use large_output::{OptionsBuilder, ResponseFormat};
62
67
 
63
- // Return with automatic file fallback (actionable format by default)
64
- return {
65
- content: [{ type: "text", text: handleMCPOutput(content) }],
66
- };
67
- });
68
+ let options = OptionsBuilder::new()
69
+ .threshold(20000)
70
+ .preview_length(1000)
71
+ .filename_prefix("github_search")
72
+ .response_format(ResponseFormat::Json)
73
+ .build();
68
74
  ```
69
75
 
70
- ## API
76
+ ### Feature Flags
71
77
 
72
- ### `handleMCPOutput(content, options?)`
78
+ - `default` - Synchronous file operations
79
+ - `async` - Async file operations with tokio
73
80
 
74
- Convenience function that handles output and returns MCP-formatted string.
81
+ ---
75
82
 
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 |
83
+ ## Response Formats
85
84
 
86
- ### Response Formats
85
+ ### Inline (under threshold)
87
86
 
88
- **Inline (under threshold):**
89
- ```
90
- <raw content returned directly>
91
- ```
87
+ Content returned directly.
88
+
89
+ ### File - Actionable format (default)
92
90
 
93
- **File - Actionable format (default):**
94
91
  ```
95
- ⚠️ Large output (126.5 KB) saved to file.
92
+ Large output (126.5 KB) saved to file.
96
93
 
97
- 📖 **ACTION REQUIRED**: Use the Read tool to read this file:
94
+ ACTION REQUIRED: Use the Read tool to read this file:
98
95
 
99
96
  /tmp/mcp_output_2025-02-19T12-38-00_abc123.txt
100
97
 
@@ -102,10 +99,11 @@ Convenience function that handles output and returns MCP-formatted string.
102
99
  {...preview...}
103
100
  --- END PREVIEW ---
104
101
 
105
- File contains the complete data. Read it to proceed.
102
+ File contains the complete data. Read it to proceed.
106
103
  ```
107
104
 
108
- **File - JSON format (for programmatic consumers):**
105
+ ### File - JSON format
106
+
109
107
  ```json
110
108
  {
111
109
  "type": "file",
@@ -116,97 +114,17 @@ Convenience function that handles output and returns MCP-formatted string.
116
114
  }
117
115
  ```
118
116
 
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";
117
+ ---
137
118
 
138
- const response = handleOutput(largeContent, {
139
- threshold: 20000,
140
- filenamePrefix: "github_search",
141
- });
119
+ ## TypeScript (Legacy/Reference)
142
120
 
143
- if (response.type === "file") {
144
- console.log(`Written ${response.sizeFormatted} to ${response.path}`);
145
- }
121
+ TypeScript implementation preserved in `typescript/` directory for reference. Not distributed via npm.
146
122
 
147
- // Convert to MCP return format (actionable by default)
148
- return toMCPResponse(response);
123
+ If you need TypeScript support, consider:
124
+ 1. Using the Rust crate via FFI (like napi-rs)
125
+ 2. Copying the TypeScript source from `typescript/` into your project
149
126
 
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
- ```
127
+ ---
210
128
 
211
129
  ## License
212
130
 
package/package.json CHANGED
@@ -1,34 +1,45 @@
1
1
  {
2
2
  "name": "@ebowwa/large-output",
3
- "version": "1.1.0",
4
- "description": "Shared utility for handling large MCP outputs with automatic file fallback and actionable LLM prompts",
3
+ "version": "1.3.0",
4
+ "description": "Utility for handling large outputs with automatic file fallback and actionable LLM prompts. Rust implementation (crate: large-output). TypeScript source preserved in typescript/ for reference.",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
12
- }
13
- },
6
+ "main": "./rust/src/lib.rs",
14
7
  "files": [
15
- "dist"
8
+ "rust/src",
9
+ "rust/Cargo.toml",
10
+ "rust/README.md"
16
11
  ],
17
12
  "scripts": {
18
- "build": "tsc",
19
- "prepublishOnly": "bun run build"
13
+ "build": "cd rust && cargo build --release",
14
+ "test": "cd rust && cargo test",
15
+ "publish:crate": "cd rust && cargo publish",
16
+ "build:ts": "cd typescript && tsc",
17
+ "test:ts": "cd typescript && tsc --noEmit"
20
18
  },
21
19
  "keywords": [
22
20
  "mcp",
23
21
  "output",
24
22
  "pagination",
25
23
  "file-fallback",
26
- "llm-actionable"
24
+ "llm-actionable",
25
+ "rust"
27
26
  ],
28
27
  "author": "ebowwa",
29
28
  "license": "MIT",
30
- "devDependencies": {
31
- "@types/node": "^22.10.2",
32
- "typescript": "^5.7.2"
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/ebowwa/codespaces",
32
+ "directory": "packages/src/ai/large-output"
33
+ },
34
+ "ownership": {
35
+ "domain": "tooling",
36
+ "responsibilities": [
37
+ "output-handling",
38
+ "large-response-management"
39
+ ]
40
+ },
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "registry": "https://registry.npmjs.org"
33
44
  }
34
45
  }
@@ -0,0 +1,29 @@
1
+ [package]
2
+ name = "large-output"
3
+ version = "1.3.0"
4
+ edition = "2021"
5
+ authors = ["ebowwa"]
6
+ description = "Utility for handling large outputs with automatic file fallback and actionable LLM prompts"
7
+ license = "MIT"
8
+ keywords = ["mcp", "output", "pagination", "file-fallback", "llm"]
9
+ categories = ["filesystem", "text-processing"]
10
+ repository = "https://github.com/ebowwa/codespaces"
11
+ readme = "README.md"
12
+
13
+ [dependencies]
14
+ serde = { version = "1.0", features = ["derive"] }
15
+ serde_json = "1.0"
16
+ chrono = "0.4"
17
+ uuid = { version = "1.0", features = ["v4"] }
18
+
19
+ [dev-dependencies]
20
+ tempfile = "3.10"
21
+
22
+ [features]
23
+ default = []
24
+ async = ["tokio", "tokio/fs"]
25
+
26
+ [dependencies.tokio]
27
+ version = "1.0"
28
+ features = ["fs", "io-util"]
29
+ optional = true
package/rust/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # large-output (Rust)
2
+
3
+ Shared utility for handling large outputs with automatic file fallback and **actionable LLM prompts**.
4
+
5
+ Rust port of [`@ebowwa/large-output`](../large-output/).
6
+
7
+ ## Problem Solved
8
+
9
+ When tools return large outputs, they typically hit character limits. Common solutions like pagination fragment the context and require multiple round-trips.
10
+
11
+ **This library solves it by:**
12
+ - Returning content inline if under the threshold
13
+ - Automatically writing to a temp file if over the threshold
14
+ - **Returning actionable text that prompts the LLM to read the file**
15
+
16
+ ## Installation
17
+
18
+ Add to your `Cargo.toml`:
19
+
20
+ ```toml
21
+ [dependencies]
22
+ large-output = "1.1.0"
23
+ ```
24
+
25
+ Or use cargo:
26
+
27
+ ```bash
28
+ cargo add large-output
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ```rust
34
+ use large_output::{handle_output, handle_mcp_output, OutputResponse};
35
+
36
+ // Basic usage
37
+ let content = "very large content...".repeat(1000);
38
+ let response = handle_output(&content, None);
39
+
40
+ match response {
41
+ OutputResponse::Inline { content, .. } => println!("{}", content),
42
+ OutputResponse::File { path, size, .. } => {
43
+ println!("Written {} bytes to {:?}", size, path);
44
+ }
45
+ }
46
+
47
+ // MCP convenience function (returns actionable text by default)
48
+ let mcp_text = handle_mcp_output(&content, None);
49
+ println!("{}", mcp_text);
50
+ ```
51
+
52
+ ## API
53
+
54
+ ### `handle_output(content, options) -> OutputResponse`
55
+
56
+ Handle output with automatic file fallback.
57
+
58
+ ```rust
59
+ use large_output::{handle_output, OutputResponse, OptionsBuilder};
60
+
61
+ let options = OptionsBuilder::new()
62
+ .threshold(20000)
63
+ .preview_length(1000)
64
+ .filename_prefix("my_tool")
65
+ .build();
66
+
67
+ let response = handle_output("content", Some(options));
68
+ ```
69
+
70
+ ### `handle_mcp_output(content, options) -> String`
71
+
72
+ Convenience function that handles output and returns MCP-formatted string.
73
+
74
+ ```rust
75
+ use large_output::handle_mcp_output;
76
+
77
+ // Default: actionable format
78
+ let text = handle_mcp_output(&large_content, None);
79
+
80
+ // JSON format for programmatic consumers
81
+ use large_output::{OptionsBuilder, ResponseFormat};
82
+ let options = OptionsBuilder::new()
83
+ .response_format(ResponseFormat::Json)
84
+ .build();
85
+ let json = handle_mcp_output(&large_content, Some(options));
86
+ ```
87
+
88
+ ### `to_mcp_response(response, format) -> String`
89
+
90
+ Convert an OutputResponse to a string for MCP tool return.
91
+
92
+ ```rust
93
+ use large_output::{handle_output, to_mcp_response, ResponseFormat};
94
+
95
+ let response = handle_output(&content, None);
96
+ let text = to_mcp_response(&response, ResponseFormat::Actionable);
97
+ ```
98
+
99
+ ### `handle_batch(contents, options) -> Vec<OutputResponse>`
100
+
101
+ Batch handler for multiple outputs.
102
+
103
+ ```rust
104
+ use large_output::handle_batch;
105
+
106
+ let contents = vec!["data1", "data2", "data3"];
107
+ let outputs = handle_batch(&contents.iter().map(|s| *s).collect::<Vec<_>>(), None);
108
+ ```
109
+
110
+ ## Options
111
+
112
+ | Option | Type | Default | Description |
113
+ |--------|------|---------|-------------|
114
+ | `threshold` | `usize` | `15000` | Character threshold for file fallback |
115
+ | `preview_length` | `usize` | `500` | Preview chars when written to file |
116
+ | `temp_dir` | `Option<PathBuf>` | `None` (OS temp) | Custom temp directory |
117
+ | `filename_prefix` | `String` | `"mcp_output"` | Filename prefix |
118
+ | `include_size` | `bool` | `true` | Include size in file response |
119
+ | `include_preview` | `bool` | `true` | Include preview in file response |
120
+ | `response_format` | `ResponseFormat` | `Actionable` | Response format for file outputs |
121
+
122
+ ### Using OptionsBuilder
123
+
124
+ ```rust
125
+ use large_output::{OptionsBuilder, ResponseFormat};
126
+
127
+ let options = OptionsBuilder::new()
128
+ .threshold(20000)
129
+ .preview_length(1000)
130
+ .filename_prefix("github_search")
131
+ .response_format(ResponseFormat::Json)
132
+ .build();
133
+ ```
134
+
135
+ ## Response Formats
136
+
137
+ ### Inline (under threshold)
138
+
139
+ Content returned directly.
140
+
141
+ ### File - Actionable format (default)
142
+
143
+ ```
144
+ Large output (126.5 KB) saved to file.
145
+
146
+ ACTION REQUIRED: Use the Read tool to read this file:
147
+
148
+ /tmp/mcp_output_2025-02-19T12-38-00_abc123.txt
149
+
150
+ --- PREVIEW (first 500 chars) ---
151
+ {...preview...}
152
+ --- END PREVIEW ---
153
+
154
+ File contains the complete data. Read it to proceed.
155
+ ```
156
+
157
+ ### File - JSON format (for programmatic consumers)
158
+
159
+ ```json
160
+ {
161
+ "type": "file",
162
+ "path": "/tmp/mcp_output_2025-02-19T12-38-00_abc123.txt",
163
+ "size": 126507,
164
+ "sizeFormatted": "126.5 KB",
165
+ "preview": "..."
166
+ }
167
+ ```
168
+
169
+ ## Feature Flags
170
+
171
+ - `default` - Synchronous file operations
172
+ - `async` - Async file operations with tokio (requires `tokio` feature)
173
+
174
+ ### Async Usage
175
+
176
+ Enable the `async` feature:
177
+
178
+ ```toml
179
+ [dependencies]
180
+ large-output = { version = "1.1.0", features = ["async"] }
181
+ ```
182
+
183
+ ## License
184
+
185
+ MIT