Skip to main content
Glama
start-project.ts7.89 kB
/** * Start Project Tool * * Retrieves task prompt from the first task of a project */ import { z } from 'zod'; import { BaseTool, MCPToolDefinition, ToolAnnotations, AgentInstructions } from '../utils/base-tool.js'; import { SecureApiClient, StartProjectApiResponse } from '../utils/secure-api-client.js'; import { logger } from '../utils/logger.js'; // Removed unused local interfaces TaskData, GetTasksResponse, ProjectData, GetProjectResponse /** * Schema for the start-project tool input */ const StartProjectSchema = z.object({ // Project slug (URL-friendly identifier) slug: z.string({ required_error: "Project slug is required" }) .regex(/^[A-Za-z]{3}$/, { message: "Project slug must be three letters (e.g., CRD or crd). Case insensitive." }) .describe("Project slug identifier (e.g., 'CRD' or 'crd'). Case insensitive"), }).strict(); /** * Type for the start-project tool input */ type StartProjectInput = z.infer<typeof StartProjectSchema>; /** * Start Project Tool Implementation */ export class StartProjectTool extends BaseTool<typeof StartProjectSchema> { readonly name = 'start_project'; readonly description = "Retrieves the project details and the prompt for the very first task of a specified project using the project's unique slug (e.g., 'CRD'). This is useful for initiating work on a new project or understanding its starting point."; readonly zodSchema = StartProjectSchema; // Renamed from schema readonly annotations: ToolAnnotations = { title: "Start Project", readOnlyHint: true, openWorldHint: true, // Interacts with an external API }; /** * Constructor with dependency injection */ constructor(apiClient?: SecureApiClient) { super(apiClient); } /** * Returns the full tool definition conforming to MCP. */ getMCPToolDefinition(): MCPToolDefinition { return { name: this.name, description: this.description, annotations: this.annotations, inputSchema: { type: "object", properties: { slug: { type: "string", pattern: "^[A-Za-z]{3}$", description: "The unique three-letter identifier for the project (e.g., 'CRD' or 'crd'). Case insensitive - will be converted to uppercase." } }, required: ["slug"], additionalProperties: false } }; } /** * Generate agent instructions for start_project tool */ protected generateAgentInstructions(input: StartProjectInput, result: any): AgentInstructions { return { immediateActions: [ "Check if git repository exists for this project", "Initialize git repository if not present (git init, initial commit)", "Proceed to get_project for comprehensive project context", "Review the first task prompt for project initialization requirements" ], nextRecommendedTools: ["get_project"], workflowPhase: 'context', gitSetupRequired: true, criticalReminders: [ "This is project initialization - establish git repository first", "Always get full project context before starting first task", "Follow the structured workflow: get_project → get_task → get_prompt" ], automationHints: { projectInitialization: "This tool starts a new project workflow", gitSetup: "Ensure version control is established before proceeding", workflowGuidance: "Use get_project next to understand project architecture and standards" } }; } /** * Execute the start-project tool */ async execute(input: StartProjectInput): Promise<unknown> { logger.info('Executing start-project tool', input); try { // Use the injected API client to get first task if (!this.apiClient) { throw new Error('API client not available - tool not properly initialized'); } // Get the first task of the project using the new endpoint const url = `/project/slug/${input.slug.toUpperCase()}/first-task`; logger.debug(`Making GET request to: ${url}`); const responseData = await this.apiClient.get<StartProjectApiResponse>(url) as unknown as StartProjectApiResponse; // const responseData: StartProjectApiResponse = axiosResponse.data; // This was the previous incorrect line if (!responseData || responseData.error) { const errorMessage = responseData?.error || `Failed to get first task for project '${input.slug}' (no data or error in response).`; logger.warn(`Error in start-project tool: ${errorMessage}`, { responseData }); return { isError: true, content: [{ type: "text", text: errorMessage }] }; } // Git integration: Set up repository if needed const gitSetupResult = await this.setupGitRepository(responseData.project); // Return data structured according to the new schema with git setup status return { project: { slug: responseData.project?.slug || '', name: responseData.project?.name || '' }, task: { number: responseData.task?.number || '', title: responseData.task?.title || '', prompt: responseData.task?.prompt || '' // Access 'prompt' and output as 'prompt' }, gitSetup: gitSetupResult }; } catch (error) { const errorMessage = (error instanceof Error) ? error.message : 'An unknown error occurred'; logger.error(`Error in start-project tool: ${errorMessage}`, error instanceof Error ? error : undefined); return { isError: true, content: [{ type: "text", text: errorMessage }] }; } } /** * Set up git repository for the project */ private async setupGitRepository(projectData: any): Promise<{ status: string; message: string; actions: string[] }> { try { const projectSlug = projectData?.slug; const projectName = projectData?.name || projectSlug; // Check if we're in a git repository const { execSync } = await import('child_process'); try { execSync('git rev-parse --git-dir', { stdio: 'ignore' }); // Already in a git repository return { status: 'existing', message: 'Git repository already exists', actions: [ 'Verified existing git repository', 'Ready for project development', 'Consider creating feature branch for project work' ] }; } catch { // Not in a git repository, initialize one try { execSync('git init', { stdio: 'ignore' }); execSync('git add .', { stdio: 'ignore' }); execSync(`git commit -m "feat: initialize ${projectName} project (${projectSlug})"`, { stdio: 'ignore' }); return { status: 'initialized', message: 'Git repository initialized successfully', actions: [ 'Initialized new git repository', 'Created initial commit for project', 'Ready for feature branch creation', 'Consider setting up remote repository' ] }; } catch (gitError) { return { status: 'failed', message: `Git initialization failed: ${gitError}`, actions: [ 'Manual git setup required', 'Run: git init', 'Run: git add .', 'Run: git commit -m "feat: initialize project"' ] }; } } } catch (error) { return { status: 'error', message: `Git setup error: ${error}`, actions: [ 'Git not available or accessible', 'Install git if needed', 'Verify git is in PATH' ] }; } } }

Implementation Reference

Latest Blog Posts

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/PixdataOrg/coderide-mcp'

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