Skip to main content
Glama

Recommend Stack

recommend_stack
Read-only

Get technology stack recommendations for your project based on type, scale, and priorities using real-time scoring with context adjustments.

Instructions

Recommends the best tech stack for a project using real-time scoring with context adjustments. Requires API key.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
projectTypeYesType of project
scaleNoProject scale
prioritiesNoTop priorities (max 3)
constraintsNoProject constraints

Implementation Reference

  • Main handler function for the recommend_stack tool. Calls the scoring API with project inputs, formats the response as a markdown table with JSON, and handles errors.
    export async function executeRecommendStack(
    	input: RecommendStackInput
    ): Promise<{ text: string; isError?: boolean }> {
    	const { projectType, scale = 'mvp', priorities = [], constraints = [] } = input;
    
    	// Deduplicate priorities
    	const uniquePriorities = [...new Set(priorities)].slice(0, 3);
    
    	debug('Calling score API', { projectType, scale, priorities: uniquePriorities, constraints });
    
    	try {
    		const response = await scoreRequest<ScoreApiResponse>({
    			projectType,
    			scale,
    			priorities: uniquePriorities,
    			constraints
    		});
    
    		const text = formatResponse(response, projectType, scale);
    		return { text };
    	} catch (err) {
    		if (err instanceof McpError) {
    			return { text: err.toResponseText(), isError: true };
    		}
    
    		const error = new McpError(
    			ErrorCode.API_ERROR,
    			err instanceof Error ? err.message : 'Failed to get recommendations'
    		);
    		return { text: error.toResponseText(), isError: true };
    	}
    }
  • Zod input schema for validating recommend_stack tool arguments.
    export const RecommendStackInputSchema = z.object({
    	projectType: z.enum(PROJECT_TYPES).describe('Type of project'),
    	scale: z.enum(SCALES).optional().default('mvp').describe('Project scale'),
    	priorities: z
    		.array(z.enum(PRIORITIES))
    		.max(3)
    		.optional()
    		.default([])
    		.describe('Top priorities (max 3)'),
    	constraints: z.array(z.string()).optional().default([]).describe('Project constraints')
    });
  • src/server.ts:163-216 (registration)
    MCP server registration of the recommend_stack tool, including input schema override and handler wrapper.
    // Register recommend_stack tool (API-based, requires API key)
    server.registerTool(
    	recommendStackToolDefinition.name,
    	{
    		title: 'Recommend Stack',
    		description: recommendStackToolDefinition.description,
    		inputSchema: {
    			projectType: z
    				.enum([
    					'web-app',
    					'mobile-app',
    					'api',
    					'desktop',
    					'cli',
    					'library',
    					'e-commerce',
    					'saas',
    					'marketplace'
    				])
    				.describe('Type of project'),
    			scale: z.enum(['mvp', 'startup', 'growth', 'enterprise']).optional().describe('Project scale'),
    			priorities: z
    				.array(
    					z.enum([
    						'time-to-market',
    						'scalability',
    						'developer-experience',
    						'cost-efficiency',
    						'performance',
    						'security',
    						'maintainability'
    					])
    				)
    				.max(3)
    				.optional()
    				.describe('Top priorities (max 3)'),
    			constraints: z.array(z.string()).optional().describe('Project constraints')
    		},
    		annotations: {
    			readOnlyHint: true,
    			destructiveHint: false,
    			openWorldHint: false
    		}
    	},
    	async (args) => {
    		debug('recommend_stack called', args);
    		const input = RecommendStackInputSchema.parse(args);
    		const { text, isError } = await executeRecommendStack(input);
    		return {
    			content: [{ type: 'text', text }],
    			isError
    		};
    	}
    );
  • Tool definition exported for MCP registration, confirming the tool name 'recommend_stack' and providing the input schema structure.
    export const recommendStackToolDefinition = {
    	name: 'recommend_stack',
    	description:
    		'Recommends the best tech stack for a project using real-time scoring with context adjustments. Requires API key.',
    	inputSchema: {
    		type: 'object' as const,
    		properties: {
    			projectType: {
    				type: 'string',
    				enum: PROJECT_TYPES,
    				description: 'Type of project (e.g., saas, web-app, api)'
    			},
    			scale: {
    				type: 'string',
    				enum: SCALES,
    				description: 'Project scale (mvp, startup, growth, enterprise)'
    			},
    			priorities: {
    				type: 'array',
    				items: { type: 'string', enum: PRIORITIES },
    				maxItems: 3,
    				description: 'Top priorities (max 3)'
    			},
    			constraints: {
    				type: 'array',
    				items: { type: 'string' },
    				description: 'Project constraints (e.g., real-time, multi-tenant)'
    			}
    		},
    		required: ['projectType']
    	}
    };
  • Helper function to format the API response into a markdown table with embedded JSON for the tool output.
    function formatResponse(response: ScoreApiResponse, projectType: string, scale: string): string {
    	let text = `## Recommended Stack for ${projectType.replace('-', ' ').replace(/\b\w/g, (c) => c.toUpperCase())} (${scale})
    
    | Category | Technology | Score | Grade |
    |----------|------------|-------|-------|
    `;
    
    	for (const stack of response.stacks) {
    		text += `| ${stack.category} | ${stack.technology} | ${stack.score} | ${stack.grade} |\n`;
    	}
    
    	text += `
    **Confidence**: ${response.confidence}`;
    
    	if (response.requestId) {
    		text += `
    **Request ID**: ${response.requestId}`;
    	}
    
    	// Include raw JSON for programmatic access
    	text += `
    
    <json>
    ${JSON.stringify({ stacks: response.stacks, confidence: response.confidence, inputsNormalized: response.inputsNormalized })}
    </json>`;
    
    	return text;
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, destructiveHint=false, and openWorldHint=false, indicating a safe, non-destructive read operation with limited scope. The description adds value by specifying 'real-time scoring with context adjustments,' which suggests dynamic behavior beyond a simple lookup, and notes the API key requirement, addressing authentication needs. However, it doesn't detail rate limits, output format, or other behavioral traits, so it partially compensates for the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise and front-loaded: it states the core purpose in the first clause and adds a critical prerequisite in the second. Every sentence earns its place with no wasted words, making it efficient and easy to parse for an agent.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (4 parameters, real-time scoring) and lack of output schema, the description is somewhat incomplete. It covers the basic purpose and authentication need but doesn't explain the return values, scoring methodology, or how context adjustments work. With annotations providing safety context, it's minimally adequate but leaves gaps for a recommendation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with all parameters well-documented in the schema (e.g., projectType, scale, priorities, constraints). The description doesn't add any parameter-specific semantics beyond what's in the schema, such as explaining how 'constraints' affect recommendations. Given the high coverage, a baseline score of 3 is appropriate, as the schema handles the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Recommends the best tech stack for a project using real-time scoring with context adjustments.' It specifies the verb ('recommends'), resource ('tech stack'), and method ('real-time scoring with context adjustments'), but doesn't explicitly differentiate from siblings like 'analyze_tech' or 'compare_techs', which likely serve different purposes. This makes it clear but not fully sibling-aware.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides minimal usage guidance: it only states 'Requires API key' as a prerequisite. There's no explicit guidance on when to use this tool versus alternatives like 'recommend_stack_demo' or other siblings, nor any context on exclusions or specific scenarios. This leaves the agent with little direction on tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/hoklims/stacksfinder-mcp'

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