11import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
22import { McpAgent } from 'agents/mcp'
3- import { z } from 'zod'
43
54import { OPEN_CONTAINER_PORT } from '../shared/consts'
65import { ExecParams , FilePathParam , FilesWrite } from '../shared/schema'
76import { MAX_CONTAINERS , proxyFetch , startAndWaitForPort } from './containerHelpers'
87import { getContainerManager } from './containerManager'
98import { BASE_INSTRUCTIONS } from './prompts'
10- import { fileToBase64 } from './utils'
9+ import { fileToBase64 , stripProtocolFromFilePath } from './utils'
1110
12- import type { FileList } from '../shared/schema'
1311import type { Env , Props } from '.'
12+ import type { FileList } from '../shared/schema'
1413
1514export class ContainerMcpAgent extends McpAgent < Env , Props > {
1615 server = new McpServer (
@@ -77,7 +76,8 @@ export class ContainerMcpAgent extends McpAgent<Env, Props> {
7776 'Delete file and its contents' ,
7877 { args : FilePathParam } ,
7978 async ( { args } ) => {
80- const deleted = await this . container_file_delete ( args )
79+ const path = await stripProtocolFromFilePath ( args . path )
80+ const deleted = await this . container_file_delete ( path )
8181 return {
8282 content : [ { type : 'text' , text : `File deleted: ${ deleted } .` } ] ,
8383 }
@@ -88,6 +88,7 @@ export class ContainerMcpAgent extends McpAgent<Env, Props> {
8888 'Write file contents' ,
8989 { args : FilesWrite } ,
9090 async ( { args } ) => {
91+ args . path = await stripProtocolFromFilePath ( args . path )
9192 return {
9293 content : [ { type : 'text' , text : await this . container_files_write ( args ) } ] ,
9394 }
@@ -117,19 +118,13 @@ export class ContainerMcpAgent extends McpAgent<Env, Props> {
117118 } )
118119 this . server . tool (
119120 'container_file_read' ,
120- 'Read a specific file' ,
121- { path : z . string ( ) } ,
122- async ( { path } ) => {
123- // normalize
124- path = path . startsWith ( 'file://' ) ? path . replace ( 'file://' , '' ) : path
125- let { blob, mimeType } = await this . container_files_read ( path )
126-
127- if ( mimeType && ( mimeType . startsWith ( 'text' ) || mimeType === 'inode/directory' ) ) {
128- // this is because there isn't a "real" directory mime type, so we're reusing the "text/directory" mime type
129- // so claude doesn't give an error
130- mimeType = mimeType === 'inode/directory' ? 'text/directory' : mimeType
121+ 'Read a specific file or list of files within a specific directory' ,
122+ { args : FilePathParam } ,
123+ async ( { args } ) => {
124+ const path = await stripProtocolFromFilePath ( args . path )
125+ const { blob, mimeType } = await this . container_file_read ( path )
131126
132- // maybe "inode/directory" should list out multiple files in the contents list?
127+ if ( mimeType && ( mimeType . startsWith ( 'text' ) ) ) {
133128 return {
134129 content : [
135130 {
@@ -253,7 +248,7 @@ export class ContainerMcpAgent extends McpAgent<Env, Props> {
253248 )
254249 return res . ok
255250 }
256- async container_files_read (
251+ async container_file_read (
257252 filePath : string
258253 ) : Promise < { blob : Blob ; mimeType : string | undefined } > {
259254 const res = await proxyFetch (
@@ -267,15 +262,11 @@ export class ContainerMcpAgent extends McpAgent<Env, Props> {
267262 }
268263 return {
269264 blob : await res . blob ( ) ,
270- mimeType : res . headers . get ( 'content-type ' ) ?? undefined ,
265+ mimeType : res . headers . get ( 'Content-Type ' ) ?? undefined ,
271266 }
272267 }
273268
274269 async container_files_write ( file : FilesWrite ) : Promise < string > {
275- if ( file . path . startsWith ( 'file://' ) ) {
276- // normalize just in case the LLM sends the full resource URI
277- file . path = file . path . replace ( 'file://' , '' )
278- }
279270 const res = await proxyFetch (
280271 this . env . ENVIRONMENT ,
281272 this . ctx . container ,
0 commit comments