Skip to main content
Glama

Task Trellis MCP

directorySetup.e2e.test.ts10.2 kB
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js"; import { mkdir, readdir, stat, writeFile } from "fs/promises"; import path from "path"; import { TestEnvironment, pathExists } from "../utils"; describe("E2E Configuration - Directory Setup", () => { let testEnv: TestEnvironment; let client: Client | null = null; let transport: StdioClientTransport | null = null; beforeEach(async () => { testEnv = new TestEnvironment(); testEnv.setup(); transport = new StdioClientTransport({ command: "node", args: ["dist/server.js", "--mode", "local"], }); client = new Client( { name: "test-client", version: "1.0.0" }, { capabilities: {} }, ); await client.connect(transport); }, 30000); afterEach(async () => { if (client) { await client.close(); client = null; } if (transport) { await transport.close(); transport = null; } testEnv?.cleanup(); }); async function callTool(name: string, args: any = {}): Promise<any> { if (!client) { throw new Error("Client not connected"); } return client.request( { method: "tools/call", params: { name, arguments: args }, }, CallToolResultSchema, ); } describe(".trellis Directory Creation", () => { it("should create .trellis directory on first object creation", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); const trellisPath = path.join(testEnv.projectRoot, ".trellis"); // Directory should not exist yet expect(await pathExists(trellisPath)).toBe(false); // Create first object await callTool("create_issue", { type: "project", title: "Test Project", }); // Now directory should exist expect(await pathExists(trellisPath)).toBe(true); // Verify it's a directory const stats = await stat(trellisPath); expect(stats.isDirectory()).toBe(true); }); it("should create subdirectory structure for projects", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); await callTool("create_issue", { type: "project", title: "Test Project", }); const projectDir = path.join( testEnv.projectRoot, ".trellis", "p", "P-test-project", ); expect(await pathExists(projectDir)).toBe(true); const files = await readdir(projectDir); expect(files).toContain("P-test-project.md"); }); it("should create nested structure for epics", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Create project first await callTool("create_issue", { type: "project", title: "Parent Project", }); // Create epic await callTool("create_issue", { type: "epic", title: "Test Epic", parent: "P-parent-project", }); const epicDir = path.join( testEnv.projectRoot, ".trellis", "p", "P-parent-project", "e", "E-test-epic", ); expect(await pathExists(epicDir)).toBe(true); }); it("should create nested structure for features", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Create hierarchy await callTool("create_issue", { type: "project", title: "Project", }); await callTool("create_issue", { type: "epic", title: "Epic", parent: "P-project", }); await callTool("create_issue", { type: "feature", title: "Feature", parent: "E-epic", }); const featureDir = path.join( testEnv.projectRoot, ".trellis", "p", "P-project", "e", "E-epic", "f", "F-feature", ); expect(await pathExists(featureDir)).toBe(true); }); it("should create task structure within features", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Create feature await callTool("create_issue", { type: "feature", title: "Feature", }); // Create task await callTool("create_issue", { type: "task", title: "Open Task", parent: "F-feature", status: "open", }); // Check task is in the correct location const taskPath = path.join( testEnv.projectRoot, ".trellis", "f", "F-feature", "t", ); expect(await pathExists(taskPath)).toBe(true); // Check task is created in status subdirectory const files = await readdir(taskPath); expect(files).toContain("open"); // Check task file exists in the open subdirectory const openTaskFiles = await readdir(path.join(taskPath, "open")); expect( openTaskFiles.some( (f) => f.includes("open-task") || f.startsWith("T-"), ), ).toBe(true); }); it("should handle standalone tasks", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Create standalone task await callTool("create_issue", { type: "task", title: "Standalone Task", status: "open", }); // Check task is in standalone location const taskPath = path.join(testEnv.projectRoot, ".trellis", "t"); expect(await pathExists(taskPath)).toBe(true); const files = await readdir(taskPath); expect(files).toContain("open"); // Check task file exists in the open subdirectory const openTaskFiles = await readdir(path.join(taskPath, "open")); expect( openTaskFiles.some( (f) => f.includes("standalone-task") || f.startsWith("T-"), ), ).toBe(true); }); }); describe("Directory Permissions", () => { it("should create directories with proper permissions", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); await callTool("create_issue", { type: "project", title: "Test", }); const trellisPath = path.join(testEnv.projectRoot, ".trellis"); const stats = await stat(trellisPath); // Check directory is readable and writable expect(stats.mode & 0o400).toBeTruthy(); // Readable expect(stats.mode & 0o200).toBeTruthy(); // Writable }); }); describe("Existing Files Handling", () => { it("should handle existing .trellis directory", async () => { const trellisPath = path.join(testEnv.projectRoot, ".trellis"); await mkdir(trellisPath, { recursive: true }); await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Should work with existing directory await callTool("create_issue", { type: "project", title: "Test", }); expect(await pathExists(path.join(trellisPath, "p", "P-test"))).toBe( true, ); }); it("should handle existing subdirectories and files", async () => { const projectPath = path.join( testEnv.projectRoot, ".trellis", "p", "P-existing", ); await mkdir(projectPath, { recursive: true }); await writeFile( path.join(projectPath, "P-existing.md"), "---\nkind: project\nid: P-existing\ntitle: Existing\nstatus: open\npriority: normal\ncreated: 2025-01-01T00:00:00.000Z\nupdated: 2025-01-01T00:00:00.000Z\nschema_version: 1.1\n---\nExisting project\n", ); await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Should be able to read existing objects const response = await callTool("get_issue", { id: "P-existing", }); expect(response.content[0].text).toContain("P-existing"); // And create new ones await callTool("create_issue", { type: "project", title: "New", }); const newProjectPath = path.join( testEnv.projectRoot, ".trellis", "p", "P-new", ); expect(await pathExists(newProjectPath)).toBe(true); }); }); describe("Complex Hierarchy Creation", () => { it("should create full project hierarchy structure", async () => { await callTool("activate", { mode: "local", projectRoot: testEnv.projectRoot, }); // Create complete hierarchy await callTool("create_issue", { type: "project", title: "Full Project", }); await callTool("create_issue", { type: "epic", title: "Epic One", parent: "P-full-project", }); await callTool("create_issue", { type: "feature", title: "Feature Alpha", parent: "E-epic-one", }); await callTool("create_issue", { type: "task", title: "Task Beta", parent: "F-feature-alpha", }); // Verify complete path exists const fullPath = path.join( testEnv.projectRoot, ".trellis", "p", "P-full-project", "e", "E-epic-one", "f", "F-feature-alpha", "t", ); expect(await pathExists(fullPath)).toBe(true); // Verify task directory structure exists const files = await readdir(fullPath); expect(files).toContain("open"); // Check task file exists in the open subdirectory const openTaskFiles = await readdir(path.join(fullPath, "open")); expect( openTaskFiles.some( (f) => f.includes("task-beta") || f.startsWith("T-"), ), ).toBe(true); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/langadventurellc/task-trellis-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server