Skip to content

Commit d77672a

Browse files
authored
fix: call command outputs raw text content instead of MCP envelope (#30)
Fixes #25 The call command was outputting the full MCP protocol envelope: { "content": [{ "type": "text", "text": "..." }] } Now outputs raw text content directly, making it easier to pipe to grep, head, jq, and other CLI tools without needing .content[0].text. Changes: - Use existing formatToolResult helper in call.ts - Update SKILL.md examples (remove jq extraction patterns) - Update CHANGELOG.md to reflect correct behavior - Add e2e test to prevent regression
1 parent 5cc5a62 commit d77672a

4 files changed

Lines changed: 35 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
- **3-Subcommand Architecture** - `info`, `grep`, `call`
2424
- Flexible format support: `server tool` and `server/tool`
25-
- `call` always outputs raw JSON (for piping/scripting)
26-
- `info`/`grep` always output human-readable format
25+
- `call` outputs raw text content (CLI-friendly, pipe to grep/head/etc.)
26+
- `info`/`grep` output human-readable format
2727

2828
- **Improved Error Messages for LLMs**
2929
- AMBIGUOUS_COMMAND: Shows both `call` and `info` options

SKILL.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,40 +52,38 @@ cat args.json | mcp-cli call filesystem read_file
5252
# Search for tools
5353
mcp-cli grep "*file*"
5454

55-
# Extract text from result
56-
mcp-cli call filesystem read_file '{"path": "./file"}' | jq -r '.content[0].text'
55+
# Output is raw text (pipe-friendly)
56+
mcp-cli call filesystem read_file '{"path": "./file"}' | head -10
5757
```
5858

5959
## Advanced Chaining
6060

6161
```bash
6262
# Chain: search files → read first match
6363
mcp-cli call filesystem search_files '{"path": ".", "pattern": "*.md"}' \
64-
| jq -r '.content[0].text | split("\n")[0]' \
64+
| head -1 \
6565
| xargs -I {} mcp-cli call filesystem read_file '{"path": "{}"}'
6666

6767
# Loop: process multiple files
6868
mcp-cli call filesystem list_directory '{"path": "./src"}' \
69-
| jq -r '.content[0].text | split("\n")[]' \
7069
| while read f; do mcp-cli call filesystem read_file "{\"path\": \"$f\"}"; done
7170

7271
# Conditional: check before reading
7372
mcp-cli call filesystem list_directory '{"path": "."}' \
74-
| jq -e '.content[0].text | contains("README")' \
73+
| grep -q "README" \
7574
&& mcp-cli call filesystem read_file '{"path": "./README.md"}'
7675

7776
# Multi-server aggregation
7877
{
7978
mcp-cli call github search_repositories '{"query": "mcp", "per_page": 3}'
8079
mcp-cli call filesystem list_directory '{"path": "."}'
81-
} | jq -s '.'
80+
}
8281

8382
# Save to file
84-
mcp-cli call github get_file_contents '{"owner": "x", "repo": "y", "path": "z"}' \
85-
| jq -r '.content[0].text' > output.txt
83+
mcp-cli call github get_file_contents '{"owner": "x", "repo": "y", "path": "z"}' > output.txt
8684
```
8785

88-
**jq tips:** `-r` raw output, `-e` exit 1 if false, `-s` slurp multiple inputs
86+
**Note:** `call` outputs raw text content directly (no jq needed for text extraction)
8987

9088
## Options
9189

src/commands/call.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
toolExecutionError,
3030
toolNotFoundError,
3131
} from '../errors.js';
32-
import { formatJson } from '../output.js';
32+
import { formatJson, formatToolResult } from '../output.js';
3333

3434
export interface CallOptions {
3535
target: string; // "server/tool"
@@ -161,8 +161,9 @@ export async function callCommand(options: CallOptions): Promise<void> {
161161
try {
162162
const result = await connection.callTool(toolName, args);
163163

164-
// Always output raw JSON for programmatic use
165-
console.log(formatJson(result));
164+
// Extract text content from MCP response for CLI-friendly output
165+
// Uses formatToolResult which extracts text from MCP content array
166+
console.log(formatToolResult(result));
166167
} catch (error) {
167168
// Try to get available tools for better error message
168169
let availableTools: string[] | undefined;

tests/integration/cli.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,28 @@ describe('CLI Integration Tests', () => {
241241
// We just verify it doesn't crash
242242
expect(typeof result.exitCode).toBe('number');
243243
});
244+
245+
test('outputs raw text content, not MCP envelope (issue #25)', async () => {
246+
// This test ensures the call command outputs raw text content
247+
// instead of the full MCP protocol envelope like:
248+
// { "content": [{ "type": "text", "text": "..." }] }
249+
const result = await runCli([
250+
'call',
251+
'filesystem',
252+
'read_file',
253+
JSON.stringify({ path: testFilePath }),
254+
]);
255+
256+
expect(result.exitCode).toBe(0);
257+
258+
// Output should be the raw file content
259+
expect(result.stdout).toContain('Hello from test file!');
260+
261+
// Output should NOT contain MCP envelope structure
262+
expect(result.stdout).not.toContain('"content"');
263+
expect(result.stdout).not.toContain('"type"');
264+
expect(result.stdout).not.toContain('"text"');
265+
});
244266
});
245267

246268
describe('error handling', () => {

0 commit comments

Comments
 (0)