Skip to content

Commit b9b9b67

Browse files
jefcodercforge42
authored andcommitted
append_in_code_file tool for enabling writing up longer code files with continue
1 parent 861fd03 commit b9b9b67

File tree

2 files changed

+582
-6
lines changed

2 files changed

+582
-6
lines changed

build/index.js

Lines changed: 270 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
44
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
55
import { randomBytes } from 'crypto';
66
import { join } from 'path';
7-
import { mkdir, writeFile } from 'fs/promises';
7+
import { mkdir, writeFile, appendFile, readFile, access } from 'fs/promises';
88
import { exec } from 'child_process';
99
import { promisify } from 'util';
1010
import { platform } from 'os';
@@ -129,6 +129,150 @@ async function executeCode(code, filePath) {
129129
};
130130
}
131131
}
132+
/**
133+
* Execute Python code from an existing file and return the result
134+
*/
135+
async function executeCodeFromFile(filePath) {
136+
try {
137+
// Ensure file exists
138+
await access(filePath);
139+
// Get platform-specific command
140+
const pythonCmd = platform() === 'win32' ? `python "${filePath}"` : `python3 "${filePath}"`;
141+
const { command, options } = getPlatformSpecificCommand(pythonCmd);
142+
// Execute code
143+
const { stdout, stderr } = await execAsync(command, {
144+
cwd: CODE_STORAGE_DIR,
145+
env: { ...process.env },
146+
...options
147+
});
148+
const response = {
149+
status: stderr ? 'error' : 'success',
150+
output: stderr || stdout,
151+
file_path: filePath
152+
};
153+
return {
154+
type: 'text',
155+
text: JSON.stringify(response),
156+
isError: !!stderr
157+
};
158+
}
159+
catch (error) {
160+
const response = {
161+
status: 'error',
162+
error: error instanceof Error ? error.message : String(error),
163+
file_path: filePath
164+
};
165+
return {
166+
type: 'text',
167+
text: JSON.stringify(response),
168+
isError: true
169+
};
170+
}
171+
}
172+
/**
173+
* Create or initialize a new file with content
174+
*/
175+
async function initializeCodeFile(content, filename) {
176+
try {
177+
// Generate a filename if not provided
178+
let actualFilename;
179+
if (filename && typeof filename === 'string') {
180+
// Extract base name without extension
181+
const baseName = filename.replace(/\.py$/, '');
182+
// Add a random suffix to ensure uniqueness
183+
actualFilename = `${baseName}_${randomBytes(4).toString('hex')}.py`;
184+
}
185+
else {
186+
// Default filename if none provided
187+
actualFilename = `code_${randomBytes(4).toString('hex')}.py`;
188+
}
189+
const filePath = join(CODE_STORAGE_DIR, actualFilename);
190+
// Write initial content to file
191+
await writeFile(filePath, content, 'utf-8');
192+
return {
193+
type: 'text',
194+
text: JSON.stringify({
195+
status: 'success',
196+
message: 'File initialized successfully',
197+
file_path: filePath,
198+
filename: actualFilename
199+
}),
200+
isError: false
201+
};
202+
}
203+
catch (error) {
204+
return {
205+
type: 'text',
206+
text: JSON.stringify({
207+
status: 'error',
208+
error: error instanceof Error ? error.message : String(error)
209+
}),
210+
isError: true
211+
};
212+
}
213+
}
214+
/**
215+
* Append content to an existing file
216+
*/
217+
async function appendToCodeFile(filePath, content) {
218+
try {
219+
// Ensure file exists
220+
await access(filePath);
221+
// Append content to file
222+
await appendFile(filePath, content, 'utf-8');
223+
return {
224+
type: 'text',
225+
text: JSON.stringify({
226+
status: 'success',
227+
message: 'Content appended successfully',
228+
file_path: filePath
229+
}),
230+
isError: false
231+
};
232+
}
233+
catch (error) {
234+
return {
235+
type: 'text',
236+
text: JSON.stringify({
237+
status: 'error',
238+
error: error instanceof Error ? error.message : String(error),
239+
file_path: filePath
240+
}),
241+
isError: true
242+
};
243+
}
244+
}
245+
/**
246+
* Read the content of a code file
247+
*/
248+
async function readCodeFile(filePath) {
249+
try {
250+
// Ensure file exists
251+
await access(filePath);
252+
// Read file content
253+
const content = await readFile(filePath, 'utf-8');
254+
return {
255+
type: 'text',
256+
text: JSON.stringify({
257+
status: 'success',
258+
content: content,
259+
file_path: filePath
260+
}),
261+
isError: false
262+
};
263+
}
264+
catch (error) {
265+
return {
266+
type: 'text',
267+
text: JSON.stringify({
268+
status: 'error',
269+
error: error instanceof Error ? error.message : String(error),
270+
file_path: filePath
271+
}),
272+
isError: true
273+
};
274+
}
275+
}
132276
/**
133277
* Install dependencies using the appropriate package manager
134278
*/
@@ -317,7 +461,7 @@ print(json.dumps(results))
317461
*/
318462
const server = new Server({
319463
name: "code-executor",
320-
version: "0.2.0",
464+
version: "0.3.0",
321465
}, {
322466
capabilities: {
323467
tools: {},
@@ -331,7 +475,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
331475
tools: [
332476
{
333477
name: "execute_code",
334-
description: `Execute Python code in the ${ENV_CONFIG.type} environment`,
478+
description: `Execute Python code in the ${ENV_CONFIG.type} environment. For short code snippets only. For longer code, use initialize_code_file and append_to_code_file instead.`,
335479
inputSchema: {
336480
type: "object",
337481
properties: {
@@ -347,6 +491,70 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
347491
required: ["code"]
348492
}
349493
},
494+
{
495+
name: "initialize_code_file",
496+
description: "Create a new Python file with initial content. Use this as the first step for longer code that may exceed token limits. Follow with append_to_code_file for additional code.",
497+
inputSchema: {
498+
type: "object",
499+
properties: {
500+
content: {
501+
type: "string",
502+
description: "Initial content to write to the file"
503+
},
504+
filename: {
505+
type: "string",
506+
description: "Optional: Name of the file (default: generated UUID)"
507+
}
508+
},
509+
required: ["content"]
510+
}
511+
},
512+
{
513+
name: "append_to_code_file",
514+
description: "Append content to an existing Python code file. Use this to add more code to a file created with initialize_code_file, allowing you to build up larger code bases in parts.",
515+
inputSchema: {
516+
type: "object",
517+
properties: {
518+
file_path: {
519+
type: "string",
520+
description: "Full path to the file"
521+
},
522+
content: {
523+
type: "string",
524+
description: "Content to append to the file"
525+
}
526+
},
527+
required: ["file_path", "content"]
528+
}
529+
},
530+
{
531+
name: "execute_code_file",
532+
description: "Execute an existing Python file. Use this as the final step after building up code with initialize_code_file and append_to_code_file.",
533+
inputSchema: {
534+
type: "object",
535+
properties: {
536+
file_path: {
537+
type: "string",
538+
description: "Full path to the Python file to execute"
539+
}
540+
},
541+
required: ["file_path"]
542+
}
543+
},
544+
{
545+
name: "read_code_file",
546+
description: "Read the content of an existing Python code file. Use this to verify the current state of a file before appending more content or executing it.",
547+
inputSchema: {
548+
type: "object",
549+
properties: {
550+
file_path: {
551+
type: "string",
552+
description: "Full path to the file to read"
553+
}
554+
},
555+
required: ["file_path"]
556+
}
557+
},
350558
{
351559
name: "install_dependencies",
352560
description: `Install Python dependencies in the ${ENV_CONFIG.type} environment`,
@@ -477,6 +685,65 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
477685
}]
478686
};
479687
}
688+
case "initialize_code_file": {
689+
const args = request.params.arguments;
690+
if (!args?.content) {
691+
throw new Error("Content is required");
692+
}
693+
const result = await initializeCodeFile(args.content, args.filename);
694+
return {
695+
content: [{
696+
type: "text",
697+
text: result.text,
698+
isError: result.isError
699+
}]
700+
};
701+
}
702+
case "append_to_code_file": {
703+
const args = request.params.arguments;
704+
if (!args?.file_path) {
705+
throw new Error("File path is required");
706+
}
707+
if (!args?.content) {
708+
throw new Error("Content is required");
709+
}
710+
const result = await appendToCodeFile(args.file_path, args.content);
711+
return {
712+
content: [{
713+
type: "text",
714+
text: result.text,
715+
isError: result.isError
716+
}]
717+
};
718+
}
719+
case "execute_code_file": {
720+
const args = request.params.arguments;
721+
if (!args?.file_path) {
722+
throw new Error("File path is required");
723+
}
724+
const result = await executeCodeFromFile(args.file_path);
725+
return {
726+
content: [{
727+
type: "text",
728+
text: result.text,
729+
isError: result.isError
730+
}]
731+
};
732+
}
733+
case "read_code_file": {
734+
const args = request.params.arguments;
735+
if (!args?.file_path) {
736+
throw new Error("File path is required");
737+
}
738+
const result = await readCodeFile(args.file_path);
739+
return {
740+
content: [{
741+
type: "text",
742+
text: result.text,
743+
isError: result.isError
744+
}]
745+
};
746+
}
480747
case "install_dependencies": {
481748
const args = request.params.arguments;
482749
if (!args?.packages || !Array.isArray(args.packages)) {

0 commit comments

Comments
 (0)