Skip to content

Commit dd4af32

Browse files
committed
Add code mode
1 parent 1cc7699 commit dd4af32

File tree

8 files changed

+1626
-0
lines changed

8 files changed

+1626
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
dist/
3+
*.log

README.md

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
# @utcp/code-mode
2+
3+
[![npm](https://img.shields.io/npm/v/@utcp/code-mode)](https://www.npmjs.com/package/@utcp/code-mode)
4+
5+
A powerful extension for UTCP that enables executing TypeScript code with direct access to all registered tools as native TypeScript functions.
6+
7+
## Features
8+
9+
- **TypeScript Code Execution**: Execute TypeScript code snippets with full access to registered tools
10+
- **Hierarchical Tool Access**: Tools organized by manual namespace (e.g., `math_tools.add()`)
11+
- **Hierarchical Type Definitions**: TypeScript interfaces organized in namespaces matching tool structure
12+
- **Runtime Interface Access**: Access TypeScript interfaces at runtime for introspection
13+
- **Type Safety**: Generates proper TypeScript interfaces for all tool inputs and outputs
14+
- **Secure Execution**: Uses Node.js VM module for safe code execution with timeout support
15+
- **Chain Tool Calls**: Combine multiple tool calls in a single TypeScript code block
16+
17+
## Installation
18+
19+
```bash
20+
npm install @utcp/code-mode
21+
```
22+
23+
## Basic Usage
24+
25+
```typescript
26+
import { CodeModeUtcpClient } from '@utcp/code-mode';
27+
28+
const client = await CodeModeUtcpClient.create();
29+
30+
// Register some tools first (example)
31+
await client.registerManual({
32+
name: 'github',
33+
call_template_type: 'mcp',
34+
config: {
35+
"mcpServers": {
36+
"github": {
37+
"command": "docker",
38+
"args": [
39+
"run",
40+
"-i",
41+
"--rm",
42+
"-e",
43+
"GITHUB_PERSONAL_ACCESS_TOKEN",
44+
"mcp/github"
45+
],
46+
"env": {
47+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
48+
}
49+
}
50+
}
51+
}
52+
});
53+
54+
// Now execute TypeScript code that uses the tools
55+
const { result, logs } = await client.callToolChain(`
56+
// Get pull request details
57+
const prDetails = await github.get_pull_request({
58+
owner: 'microsoft',
59+
repo: 'vscode',
60+
pull_number: 1234
61+
});
62+
console.log('PR Title:', prDetails.title);
63+
console.log('PR State:', prDetails.state);
64+
65+
// Get pull request comments
66+
const prComments = await github.get_pull_request_comments({
67+
owner: 'microsoft',
68+
repo: 'vscode',
69+
pull_number: 1234
70+
});
71+
console.log('Found', prComments.length, 'review comments');
72+
73+
// Get pull request reviews
74+
const prReviews = await github.get_pull_request_reviews({
75+
owner: 'microsoft',
76+
repo: 'vscode',
77+
pull_number: 1234
78+
});
79+
console.log('Found', prReviews.length, 'reviews');
80+
81+
// Get files changed in the PR
82+
const prFiles = await github.get_pull_request_files({
83+
owner: 'microsoft',
84+
repo: 'vscode',
85+
pull_number: 1234
86+
});
87+
console.log('Files changed:', prFiles.length);
88+
89+
// Summarize the discussion
90+
const discussionSummary = {
91+
title: prDetails.title,
92+
description: prDetails.body || 'No description provided',
93+
state: prDetails.state,
94+
author: prDetails.user.login,
95+
filesChanged: prFiles.length,
96+
totalComments: prComments.length,
97+
totalReviews: prReviews.length,
98+
reviewSummary: prReviews.map(review => ({
99+
reviewer: review.user.login,
100+
state: review.state,
101+
commentCount: review.body ? 1 : 0
102+
})),
103+
keyDiscussionPoints: prComments.slice(0, 3).map(comment => ({
104+
author: comment.user.login,
105+
snippet: comment.body.substring(0, 100) + '...'
106+
}))
107+
};
108+
109+
console.log('Discussion Summary Generated');
110+
return discussionSummary;
111+
`);
112+
113+
console.log('PR Discussion Summary:', result);
114+
console.log('Console output:', logs);
115+
```
116+
117+
## Advanced Usage
118+
119+
### Console Output Capture
120+
121+
All console output is automatically captured and returned alongside execution results:
122+
123+
```typescript
124+
const { result, logs } = await client.callToolChain(`
125+
console.log('Starting PR analysis...');
126+
console.warn('Analyzing large PR with many changes');
127+
128+
const prDetails = await github.get_pull_request({
129+
owner: 'facebook',
130+
repo: 'react',
131+
pull_number: 5678
132+
});
133+
console.log('PR Title:', prDetails.title);
134+
135+
const prStatus = await github.get_pull_request_status({
136+
owner: 'facebook',
137+
repo: 'react',
138+
pull_number: 5678
139+
});
140+
console.log('Status checks passed:', prStatus.state === 'success');
141+
142+
return { title: prDetails.title, checksPass: prStatus.state === 'success' };
143+
`);
144+
145+
console.log('Result:', result); // { title: "Fix memory leak in hooks", checksPass: true }
146+
console.log('Captured logs:');
147+
logs.forEach((log, i) => console.log(`${i + 1}: ${log}`));
148+
// Output:
149+
// 1: Starting PR analysis...
150+
// 2: [WARN] Analyzing large PR with many changes
151+
// 3: PR Title: Fix memory leak in hooks
152+
// 4: Status checks passed: true
153+
```
154+
155+
### Getting TypeScript Interfaces
156+
157+
You can generate TypeScript interfaces for all your tools to get better IDE support:
158+
159+
```typescript
160+
const interfaces = await client.getAllToolsTypeScriptInterfaces();
161+
console.log(interfaces);
162+
```
163+
164+
This will output something like:
165+
166+
```typescript
167+
// Auto-generated TypeScript interfaces for UTCP tools
168+
169+
namespace math_tools {
170+
interface addInput {
171+
/** First number */
172+
a: number;
173+
/** Second number */
174+
b: number;
175+
}
176+
177+
interface addOutput {
178+
/** The sum result */
179+
result: number;
180+
}
181+
}
182+
183+
/**
184+
* Adds two numbers
185+
* Tags: math, arithmetic
186+
* Access as: math_tools.add(args)
187+
*/
188+
```
189+
190+
### Complex Tool Chains
191+
192+
Execute complex logic with multiple tools using hierarchical access:
193+
194+
```typescript
195+
const result = await client.callToolChain(`
196+
// Get user data (assuming 'user_service' manual)
197+
const user = await user_service.getUserData({ userId: "123" });
198+
199+
// Process the data (assuming 'data_processing' manual)
200+
const processedData = await data_processing.processUserData({
201+
userData: user,
202+
options: { normalize: true, validate: true }
203+
});
204+
205+
// Generate report (assuming 'reporting' manual)
206+
const report = await reporting.generateReport({
207+
data: processedData,
208+
format: "json",
209+
includeMetrics: true
210+
});
211+
212+
// Send notification (assuming 'notifications' manual)
213+
await notifications.sendNotification({
214+
recipient: user.email,
215+
subject: "Your report is ready",
216+
body: \`Report generated with \${report.metrics.totalItems} items\`
217+
});
218+
219+
return {
220+
reportId: report.id,
221+
itemCount: report.metrics.totalItems,
222+
notificationSent: true
223+
};
224+
`);
225+
```
226+
227+
### Error Handling
228+
229+
The code execution includes proper error handling:
230+
231+
```typescript
232+
try {
233+
const result = await client.callToolChain(`
234+
const result = await someToolThatMightFail({ input: "test" });
235+
return result;
236+
`);
237+
} catch (error) {
238+
console.error('Code execution failed:', error.message);
239+
}
240+
```
241+
242+
### Timeout Configuration
243+
244+
You can set custom timeouts for code execution:
245+
246+
```typescript
247+
const result = await client.callToolChain(`
248+
// Long running operation
249+
const result = await processLargeDataset({ data: largeArray });
250+
return result;
251+
`, 60000); // 60 second timeout
252+
```
253+
254+
### Runtime Interface Access
255+
256+
The code execution context provides access to TypeScript interfaces at runtime:
257+
258+
```typescript
259+
const result = await client.callToolChain(`
260+
// Access all interfaces
261+
console.log('All interfaces:', __interfaces);
262+
263+
// Get interface for a specific tool
264+
const addInterface = __getToolInterface('math_tools.add');
265+
console.log('Add tool interface:', addInterface);
266+
267+
// Parse interface information
268+
const hasNamespaces = __interfaces.includes('namespace math_tools');
269+
const availableNamespaces = __interfaces.match(/namespace \\w+/g) || [];
270+
271+
// Use this for dynamic validation, documentation, or debugging
272+
return {
273+
hasInterfaces: typeof __interfaces === 'string',
274+
namespaceCount: availableNamespaces.length,
275+
canIntrospect: typeof __getToolInterface === 'function',
276+
specificToolInterface: !!addInterface
277+
};
278+
`);
279+
```
280+
281+
#### Available Context Variables
282+
283+
- **`__interfaces`**: String containing all TypeScript interface definitions
284+
- **`__getToolInterface(toolName: string)`**: Function to get interface for a specific tool
285+
286+
## AI Agent Integration
287+
288+
For AI agents that will use CodeModeUtcpClient, include the built-in prompt template in your system prompt:
289+
290+
```typescript
291+
import { CodeModeUtcpClient } from '@utcp/code-mode';
292+
293+
// Add this to your AI agent's system prompt
294+
const systemPrompt = `
295+
You are an AI assistant with access to tools via UTCP CodeMode.
296+
297+
${CodeModeUtcpClient.AGENT_PROMPT_TEMPLATE}
298+
299+
Additional instructions...
300+
`;
301+
```
302+
303+
This template provides essential guidance on:
304+
- **Tool Discovery Workflow**: How to explore available tools before coding
305+
- **Hierarchical Access Patterns**: Using `manual.tool()` syntax correctly
306+
- **Interface Introspection**: Leveraging `__interfaces` and `__getToolInterface()`
307+
- **Best Practices**: Error handling, data flow, and proper code structure
308+
- **Runtime Context**: Available variables and functions in the execution environment
309+
310+
## API Reference
311+
312+
### CodeModeUtcpClient
313+
314+
Extends `UtcpClient` with additional code execution capabilities.
315+
316+
#### Methods
317+
318+
##### `callToolChain(code: string, timeout?: number): Promise<{result: any, logs: string[]}>`
319+
320+
Executes TypeScript code with access to all registered tools and captures console output.
321+
322+
- **code**: TypeScript code to execute
323+
- **timeout**: Optional timeout in milliseconds (default: 30000)
324+
- **Returns**: Object containing both the execution result and captured console logs (`console.log`, `console.error`, `console.warn`, `console.info`)
325+
326+
##### `toolToTypeScriptInterface(tool: Tool): string`
327+
328+
Converts a single tool to its TypeScript interface definition.
329+
330+
- **tool**: The Tool object to convert
331+
- **Returns**: TypeScript interface as a string
332+
333+
##### `getAllToolsTypeScriptInterfaces(): Promise<string>`
334+
335+
Generates TypeScript interfaces for all registered tools.
336+
337+
- **Returns**: Complete TypeScript interface definitions
338+
339+
### Static Properties
340+
341+
##### `CodeModeUtcpClient.AGENT_PROMPT_TEMPLATE: string`
342+
343+
A comprehensive prompt template designed for AI agents using CodeModeUtcpClient. Contains detailed guidance on tool discovery, hierarchical access patterns, interface introspection, and best practices for code execution.
344+
345+
### Static Methods
346+
347+
##### `CodeModeUtcpClient.create(root_dir?: string, config?: UtcpClientConfig): Promise<CodeModeUtcpClient>`
348+
349+
Creates a new CodeModeUtcpClient instance.
350+
351+
- **root_dir**: Root directory for relative path resolution
352+
- **config**: UTCP client configuration
353+
- **Returns**: New CodeModeUtcpClient instance
354+
355+
## Security Considerations
356+
357+
- Code execution happens in a secure Node.js VM context
358+
- No access to Node.js modules or filesystem by default
359+
- Timeout protection prevents infinite loops
360+
- Only registered tools are accessible in the execution context
361+
362+
## Type Safety
363+
364+
The code mode client generates hierarchical TypeScript interfaces for all tools, providing:
365+
366+
- **Namespace Organization**: Tools grouped by manual (e.g., `namespace math_tools`)
367+
- **Hierarchical Access**: Clean dot notation (`math_tools.add()`) prevents naming conflicts
368+
- **Compile-time Type Checking**: Full type safety for tool parameters and return values
369+
- **IntelliSense Support**: Enhanced IDE autocompletion with organized namespaces
370+
- **Runtime Introspection**: Access interface definitions during code execution
371+
- **Self-Documenting Code**: Generated interfaces include descriptions and access patterns
372+
373+
## Integration with IDEs
374+
375+
For the best development experience:
376+
377+
1. Generate TypeScript interfaces for your tools
378+
2. Save them to a `.d.ts` file in your project
379+
3. Reference the file in your TypeScript configuration
380+
4. Enjoy full IntelliSense support for tool functions
381+
382+
```typescript
383+
// Generate and save interfaces
384+
const interfaces = await client.getAllToolsTypeScriptInterfaces();
385+
await fs.writeFile('tools.d.ts', interfaces);
386+
```
387+
388+
## Actual up to date implementation in the [typescript repository](https://github.com/universal-tool-calling-protocol/typescript-utcp/tree/main/packages/code-mode)
389+
390+
## License
391+
392+
MPL-2.0

0 commit comments

Comments
 (0)