Skip to content

Commit 7ba8b3c

Browse files
authored
Merge pull request #61 "Update CLI" from universal-tool-calling-protocol/dev
Update CLI
2 parents d28c0af + 09a9bde commit 7ba8b3c

File tree

9 files changed

+731
-222
lines changed

9 files changed

+731
-222
lines changed

README.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,17 +437,35 @@ Note the name change from `http_stream` to `streamable_http`.
437437

438438
```json
439439
{
440-
"name": "my_cli_tool",
440+
"name": "multi_step_cli_tool",
441441
"call_template_type": "cli", // Required
442-
"command_name": "my-command --utcp", // Required
442+
"commands": [ // Required - sequential command execution
443+
{
444+
"command": "git clone UTCP_ARG_repo_url_UTCP_END temp_repo",
445+
"append_to_final_output": false
446+
},
447+
{
448+
"command": "cd temp_repo && find . -name '*.py' | wc -l"
449+
// Last command output returned by default
450+
}
451+
],
443452
"env_vars": { // Optional
444-
"MY_VAR": "my_value"
453+
"GIT_AUTHOR_NAME": "UTCP Bot",
454+
"API_KEY": "${MY_API_KEY}"
445455
},
446-
"working_dir": "/path/to/working/directory", // Optional
456+
"working_dir": "/tmp", // Optional
447457
"auth": null // Optional (always null for CLI)
448458
}
449459
```
450460

461+
**CLI Protocol Features:**
462+
- **Multi-command execution**: Commands run sequentially in single subprocess
463+
- **Cross-platform**: PowerShell on Windows, Bash on Unix/Linux/macOS
464+
- **State preservation**: Directory changes (`cd`) persist between commands
465+
- **Argument placeholders**: `UTCP_ARG_argname_UTCP_END` format
466+
- **Output referencing**: Access previous outputs with `$CMD_0_OUTPUT`, `$CMD_1_OUTPUT`
467+
- **Flexible output control**: Choose which command outputs to include in final result
468+
451469
### Text Call Template
452470

453471
```json

core/README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,16 @@ Note the name change from `http_stream` to `streamable_http`.
439439
{
440440
"name": "my_cli_tool",
441441
"call_template_type": "cli", // Required
442-
"command_name": "my-command --utcp", // Required
442+
"commands": [ // Required - array of commands to execute in sequence
443+
{
444+
"command": "cd UTCP_ARG_target_dir_UTCP_END",
445+
"append_to_final_output": false // Optional, default is false if not last command
446+
},
447+
{
448+
"command": "my-command --input UTCP_ARG_input_file_UTCP_END"
449+
// append_to_final_output defaults to true for last command
450+
}
451+
],
443452
"env_vars": { // Optional
444453
"MY_VAR": "my_value"
445454
},
@@ -448,6 +457,12 @@ Note the name change from `http_stream` to `streamable_http`.
448457
}
449458
```
450459

460+
**Notes:**
461+
- Commands execute in a single subprocess (PowerShell on Windows, Bash on Unix)
462+
- Use `UTCP_ARG_argname_UTCP_END` placeholders for arguments
463+
- Reference previous command output with `$CMD_0_OUTPUT`, `$CMD_1_OUTPUT`, etc.
464+
- Only the last command's output is returned by default
465+
451466
### Text Call Template
452467

453468
```json

core/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "utcp"
7-
version = "1.0.2"
7+
version = "1.0.4"
88
authors = [
99
{ name = "UTCP Contributors" },
1010
]

core/tests/client/test_utcp_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async def sample_tools():
147147

148148
cli_call_template = CliCallTemplate(
149149
name="test_cli_provider",
150-
command_name="echo",
150+
commands=[{"command": "echo UTCP_ARG_command_UTCP_END"}],
151151
call_template_type="cli"
152152
)
153153

@@ -496,7 +496,7 @@ async def test_load_manual_call_templates_from_file(self, isolated_communication
496496
{
497497
"name": "cli_template",
498498
"call_template_type": "cli",
499-
"command_name": "echo"
499+
"commands": [{"command": "echo UTCP_ARG_message_UTCP_END"}]
500500
}
501501
]
502502
}

plugins/communication_protocols/cli/README.md

Lines changed: 140 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ Command-line interface plugin for UTCP, enabling integration with command-line t
66

77
## Features
88

9-
- **Command Execution**: Run any command-line tool as a UTCP tool
9+
- **Multi-Command Execution**: Execute multiple commands sequentially in a single subprocess
10+
- **State Preservation**: Directory changes and environment persist between commands
11+
- **Cross-Platform Script Generation**: PowerShell on Windows, Bash on Unix/Linux/macOS
12+
- **Flexible Output Control**: Choose which command outputs to include in final result
13+
- **Argument Substitution**: `UTCP_ARG_argname_UTCP_END` placeholder system
14+
- **Output Referencing**: Access previous command outputs with `$CMD_0_OUTPUT`, `$CMD_1_OUTPUT`
1015
- **Environment Variables**: Secure credential and configuration passing
1116
- **Working Directory Control**: Execute commands in specific directories
12-
- **Input/Output Handling**: Support for stdin, stdout, stderr processing
13-
- **Cross-Platform**: Works on Windows, macOS, and Linux
1417
- **Timeout Management**: Configurable execution timeouts
15-
- **Argument Validation**: Optional input sanitization
18+
- **Error Handling**: Comprehensive subprocess error management
1619

1720
## Installation
1821

@@ -25,109 +28,184 @@ pip install utcp-cli
2528
```python
2629
from utcp.utcp_client import UtcpClient
2730

28-
# Basic CLI tool
31+
# Multi-step CLI tool
2932
client = await UtcpClient.create(config={
3033
"manual_call_templates": [{
31-
"name": "file_tools",
34+
"name": "file_analysis",
3235
"call_template_type": "cli",
33-
"command_name": "ls -la ${path}"
36+
"commands": [
37+
{
38+
"command": "cd UTCP_ARG_target_dir_UTCP_END",
39+
"append_to_final_output": false
40+
},
41+
{
42+
"command": "find . -type f -name '*.py' | wc -l"
43+
}
44+
]
3445
}]
3546
})
3647

37-
result = await client.call_tool("file_tools.list", {"path": "/home"})
48+
result = await client.call_tool("file_analysis.count_python_files", {"target_dir": "/project"})
3849
```
3950

4051
## Configuration Examples
4152

42-
### Basic Command
53+
### Basic Multi-Command Operation
4354
```json
4455
{
45-
"name": "file_ops",
56+
"name": "file_analysis",
4657
"call_template_type": "cli",
47-
"command_name": "ls -la ${path}",
58+
"commands": [
59+
{
60+
"command": "cd UTCP_ARG_target_dir_UTCP_END",
61+
"append_to_final_output": false
62+
},
63+
{
64+
"command": "ls -la"
65+
}
66+
],
4867
"working_dir": "/tmp"
4968
}
5069
```
5170

52-
### With Environment Variables
71+
### With Environment Variables and Output Control
5372
```json
5473
{
55-
"name": "python_script",
74+
"name": "python_pipeline",
5675
"call_template_type": "cli",
57-
"command_name": "python script.py ${input}",
76+
"commands": [
77+
{
78+
"command": "python setup.py install",
79+
"append_to_final_output": false
80+
},
81+
{
82+
"command": "python script.py --input UTCP_ARG_input_file_UTCP_END --result \"$CMD_0_OUTPUT\"",
83+
"append_to_final_output": true
84+
}
85+
],
5886
"env_vars": {
5987
"PYTHONPATH": "/custom/path",
6088
"API_KEY": "${API_KEY}"
6189
}
6290
}
6391
```
6492

65-
### Processing JSON with jq
93+
### Cross-Platform Git Operations
6694
```json
6795
{
68-
"name": "json_processor",
96+
"name": "git_analysis",
6997
"call_template_type": "cli",
70-
"command_name": "jq '.data'",
71-
"stdin": "${json_input}",
72-
"timeout": 10
98+
"commands": [
99+
{
100+
"command": "git clone UTCP_ARG_repo_url_UTCP_END temp_repo",
101+
"append_to_final_output": false
102+
},
103+
{
104+
"command": "cd temp_repo",
105+
"append_to_final_output": false
106+
},
107+
{
108+
"command": "git log --oneline -10",
109+
"append_to_final_output": true
110+
},
111+
{
112+
"command": "echo \"Repository has $(find . -name '*.py' | wc -l) Python files\"",
113+
"append_to_final_output": true
114+
}
115+
],
116+
"env_vars": {
117+
"GIT_AUTHOR_NAME": "UTCP Bot",
118+
"GIT_AUTHOR_EMAIL": "bot@utcp.dev"
119+
}
73120
}
74121
```
75122

76-
### Git Operations
123+
### Referencing Previous Command Output
77124
```json
78125
{
79-
"name": "git_tools",
126+
"name": "conditional_processor",
80127
"call_template_type": "cli",
81-
"command_name": "git ${operation} ${args}",
82-
"working_dir": "${repo_path}",
83-
"env_vars": {
84-
"GIT_AUTHOR_NAME": "${author_name}",
85-
"GIT_AUTHOR_EMAIL": "${author_email}"
86-
}
128+
"commands": [
129+
{
130+
"command": "git status --porcelain",
131+
"append_to_final_output": false
132+
},
133+
{
134+
"command": "echo \"Changes detected: $CMD_0_OUTPUT\"",
135+
"append_to_final_output": true
136+
}
137+
]
87138
}
88139
```
89140

90-
## Security Considerations
141+
## Cross-Platform Considerations
91142

92-
- Commands run in isolated subprocesses
93-
- Environment variables provide secure credential passing
94-
- Working directory restrictions limit file system access
95-
- Input validation prevents command injection
143+
### Command Syntax
144+
Commands should use appropriate syntax for the target platform:
96145

146+
**Windows (PowerShell):**
97147
```json
98148
{
99-
"name": "safe_grep",
100-
"call_template_type": "cli",
101-
"command_name": "grep ${pattern} ${file}",
102-
"working_dir": "/safe/directory",
103-
"allowed_args": {
104-
"pattern": "^[a-zA-Z0-9_-]+$",
105-
"file": "^[a-zA-Z0-9_./-]+\\.txt$"
106-
}
149+
"commands": [
150+
{"command": "Get-ChildItem UTCP_ARG_path_UTCP_END"},
151+
{"command": "Set-Location UTCP_ARG_dir_UTCP_END"}
152+
]
107153
}
108154
```
109155

156+
**Unix/Linux/macOS (Bash):**
157+
```json
158+
{
159+
"commands": [
160+
{"command": "ls -la UTCP_ARG_path_UTCP_END"},
161+
{"command": "cd UTCP_ARG_dir_UTCP_END"}
162+
]
163+
}
164+
```
165+
166+
### Universal Commands
167+
Some commands work across platforms:
168+
```json
169+
{
170+
"commands": [
171+
{"command": "git status"},
172+
{"command": "python --version"},
173+
{"command": "node -v"}
174+
]
175+
}
176+
```
177+
178+
## Security Considerations
179+
180+
- Commands execute in isolated subprocesses with controlled environment
181+
- Environment variables provide secure credential passing
182+
- Previous command outputs should be used carefully to avoid injection
183+
- Commands should use platform-appropriate syntax
184+
110185
## Error Handling
111186

112187
```python
113188
from utcp.exceptions import ToolCallError
114-
import subprocess
115189

116190
try:
117-
result = await client.call_tool("cli_tool.command", {"arg": "value"})
191+
result = await client.call_tool("cli_tool.multi_command", {
192+
"repo_url": "https://github.com/example/repo.git",
193+
"target_dir": "analysis_temp"
194+
})
118195
except ToolCallError as e:
119-
if isinstance(e.__cause__, subprocess.CalledProcessError):
120-
print(f"Command failed with exit code {e.__cause__.returncode}")
121-
print(f"stderr: {e.__cause__.stderr}")
196+
print(f"CLI tool execution failed: {e}")
197+
# Script execution failed - check individual command outputs
122198
```
123199

124200
## Common Use Cases
125201

126-
- **File Operations**: ls, find, grep, awk, sed
127-
- **Data Processing**: jq, sort, uniq, cut
128-
- **System Monitoring**: ps, top, df, netstat
129-
- **Development Tools**: git, npm, pip, docker
130-
- **Custom Scripts**: Python, bash, PowerShell scripts
202+
- **Multi-step Builds**: setup → compile → test → package
203+
- **Git Workflows**: clone → analyze → commit → push
204+
- **Data Pipelines**: fetch → transform → validate → output
205+
- **File Operations**: navigate → search → process → report
206+
- **Development Tools**: install dependencies → run tests → generate docs
207+
- **System Administration**: check status → backup → cleanup → verify
208+
- **Custom Workflows**: Any sequence of command-line operations
131209

132210
## Testing CLI Tools
133211

@@ -136,17 +214,25 @@ import pytest
136214
from utcp.utcp_client import UtcpClient
137215

138216
@pytest.mark.asyncio
139-
async def test_cli_tool():
217+
async def test_multi_command_cli_tool():
140218
client = await UtcpClient.create(config={
141219
"manual_call_templates": [{
142220
"name": "test_cli",
143221
"call_template_type": "cli",
144-
"command_name": "echo ${message}"
222+
"commands": [
223+
{
224+
"command": "echo UTCP_ARG_message_UTCP_END",
225+
"append_to_final_output": false
226+
},
227+
{
228+
"command": "echo \"Previous: $CMD_0_OUTPUT\""
229+
}
230+
]
145231
}]
146232
})
147233

148-
result = await client.call_tool("test_cli.echo", {"message": "hello"})
149-
assert "hello" in result["stdout"]
234+
result = await client.call_tool("test_cli.echo_chain", {"message": "hello"})
235+
assert "Previous: hello" in result
150236
```
151237

152238
## Related Documentation

plugins/communication_protocols/cli/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "utcp-cli"
7-
version = "1.0.2"
7+
version = "1.1.0"
88
authors = [
99
{ name = "UTCP Contributors" },
1010
]

0 commit comments

Comments
 (0)