create_folder
Create a folder under a project root or knowledge base. Supports nested paths like 'notes/inbox' by creating intermediate directories. Idempotent: succeeds even if folder exists.
Instructions
Create a folder under a project root or the KB. Idempotent — creating an existing folder succeeds. Nested paths like notes/inbox create intermediates. REJECTS: empty names, null bytes, leading path separators, and any segment equal to .. (the call returns isError, no folder is touched). Side effect: a directory is mkdir'd on disk; no DB rows are written until a file lands inside. No external auth or rate limits. Returns {path, base_path}.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | No | Project ID. Pass null or omit to create the folder under the KB. | |
| name | Yes | Folder name (relative; supports nested paths via '/') |
Implementation Reference
- packages/core/src/files/index.ts:336-340 (handler)Core handler: creates a folder by resolving the path safely with assertPathInside, then calling mkdirSync with recursive: true. Returns the full path.
export function createFolder(projectPath: string, folderName: string): string { const fullPath = assertPathInside(projectPath, folderName); mkdirSync(fullPath, { recursive: true }); return fullPath; } - apps/mcp/src/index.ts:1115-1137 (registration)MCP tool registration using server.tool() with name 'create_folder', schema (project_id optional nullable int, name string), and handler that validates input, resolves base path, and delegates to core createFolder.
server.tool( "create_folder", "Create a folder under a project root or the KB. Idempotent — creating an existing folder succeeds. Nested paths like `notes/inbox` create intermediates. REJECTS: empty names, null bytes, leading path separators, and any segment equal to `..` (the call returns isError, no folder is touched). Side effect: a directory is mkdir'd on disk; no DB rows are written until a file lands inside. No external auth or rate limits. Returns `{path, base_path}`.", { project_id: z.number().nullable().optional().describe("Project ID. Pass null or omit to create the folder under the KB."), name: z.string().describe("Folder name (relative; supports nested paths via '/')"), }, async ({ project_id, name }) => { try { validateFolderName(name); const base = resolveFolderBase(project_id); const path = createFolder(base, name); return { content: [{ type: "text", text: JSON.stringify({ path, base_path: base }, null, 2) }], }; } catch (e: any) { return { isError: true, content: [{ type: "text", text: JSON.stringify({ error: e?.message ?? String(e) }, null, 2) }], }; } } ); - apps/mcp/src/index.ts:1118-1121 (schema)Zod schema for create_folder: project_id (nullable optional number) and name (string) input parameters.
{ project_id: z.number().nullable().optional().describe("Project ID. Pass null or omit to create the folder under the KB."), name: z.string().describe("Folder name (relative; supports nested paths via '/')"), }, - apps/mcp/src/index.ts:1080-1091 (helper)Validates folder name before calling createFolder: rejects empty/null, null bytes, leading separators, and '..' traversal segments.
function validateFolderName(name: string): void { if (typeof name !== "string" || name.length === 0) { throw new Error("name must be a non-empty string"); } if (name.includes("\0")) throw new Error("name contains null byte"); if (name.startsWith("/") || name.startsWith("\\")) { throw new Error("name must not start with a path separator"); } if (name.split(/[/\\]/).some((seg) => seg === "..")) { throw new Error("name must not contain '..' segments"); } } - apps/mcp/src/index.ts:1067-1078 (helper)Resolves the base directory path for folder operations: KB data dir if project_id is null/undefined, or the project's registered path from the database.
function resolveFolderBase(projectId: number | null | undefined): string { if (projectId === undefined || projectId === null) { return join(dataDir, "knowledge"); } const project = getDatabase() .prepare("SELECT path FROM projects WHERE id = ?") .get(projectId) as { path: string | null } | undefined; if (!project?.path) { throw new Error(`Project not found: ${projectId}`); } return project.path; }