Skip to main content
Glama
AGENTS.md7.94 kB
# Agent Instructions for Unsplash MCP Server ## Project Context You are working on an **Unsplash MCP Server** - a Model Context Protocol server that integrates with the Unsplash API. This server allows LLMs to search for photos, collections, and users on Unsplash through a standardized MCP interface. **Tech Stack:** - TypeScript with ES modules - Node.js LTS (currently 20.x) - MCP SDK (@modelcontextprotocol/sdk) - Zod for validation - Node's built-in test runner ## Architecture Overview ### File Structure ``` src/ ├── index.ts # MCP server entry point (stdio/HTTP) ├── services/ │ ├── unsplash-client.ts # Unsplash API client │ ├── unsplash-client.test.ts # API client tests │ ├── formatters.ts # Response formatters │ └── formatters.test.ts # Formatter tests ├── tools/ │ ├── photos.ts # Photo-related MCP tools │ └── collections-users.ts # Collection/user MCP tools ├── schemas/ │ └── index.ts # Zod validation schemas ├── types.ts # TypeScript type definitions └── constants.ts # Constants (API URL, limits) ``` ### Key Components **UnsplashClient** (`src/services/unsplash-client.ts`) - Handles all HTTP communication with Unsplash API - Implements error handling with `UnsplashAPIError` class - Provides `makeRequest<T>()` and `trackDownload()` methods **Formatters** (`src/services/formatters.ts`) - Convert API responses to markdown or JSON - Include: `formatPhotoMarkdown()`, `formatCollectionMarkdown()`, `formatUserMarkdown()`, etc. - Use `truncateIfNeeded()` for content length management **MCP Tools** (`src/tools/`) - Register tools with the MCP server - Use Zod schemas for input validation - Return formatted responses (markdown or JSON based on `format` parameter) ## Development Guidelines ### When Adding New Features **Step-by-step process:** 1. Add types to `src/types.ts` 2. Create Zod schema in `src/schemas/index.ts` 3. Add API method to `UnsplashClient` if needed 4. Create formatter function in `src/services/formatters.ts` 5. Register MCP tool in appropriate file under `src/tools/` 6. Write comprehensive tests 7. Update AGENTS.md if new patterns are introduced ### Testing Requirements **Critical:** All test fixtures MUST include every required field from `src/types.ts`. The types use strict TypeScript interfaces. **Example: Creating a test UnsplashUser** ```typescript const user: UnsplashUser = { id: "user-1", username: "photographer", name: "John Doe", first_name: "John", // Required! last_name: "Doe", // Required! total_likes: 100, total_photos: 50, total_collections: 5, profile_image: { // Required! small: "https://...", medium: "https://...", large: "https://..." }, links: { // All links required self: "https://...", html: "https://...", photos: "https://...", likes: "https://...", portfolio: "https://..." } // Optional fields can be omitted or set to undefined }; ``` **Test commands:** ```bash npm test # Build and run all tests npm run build # Compile TypeScript only ``` ### Code Patterns **Error Handling:** ```typescript try { const data = await client.makeRequest<ResponseType>('/endpoint', params); return formatResponse(data); } catch (error) { if (error instanceof UnsplashAPIError) { throw new Error(`Unsplash API error: ${error.message}`); } throw error; } ``` **Adding an MCP Tool:** ```typescript server.addTool({ name: "tool_name", description: "Clear description of what this tool does", inputSchema: zodToJsonSchema(ToolInputSchema), handler: async (input) => { const validated = ToolInputSchema.parse(input); const response = await client.makeRequest<DataType>('/endpoint', { query: validated.query, per_page: validated.per_page }); if (validated.format === 'json') { return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] }; } const markdown = formatDataMarkdown(response); return { content: [{ type: "text", text: truncateIfNeeded(markdown) }] }; } }); ``` **Unsplash API Requirements:** - Always track downloads: `await client.trackDownload(photo.links.download_location)` - Handle rate limits (50 requests/hour for demo keys) - Use proper attribution in responses ### Important Conventions 1. **ES Modules:** All imports must end with `.js` extension 2. **Character Limits:** Use `truncateIfNeeded()` for large responses (default: 100,000 chars) 3. **Response Format:** Support both `json` and `markdown` output formats 4. **Type Safety:** Use TypeScript strict mode, no `any` unless absolutely necessary 5. **No Emojis:** Avoid emojis in code (only in commits when specified) ## CI/CD Pipeline ### Workflows **CI Pipeline** (`.github/workflows/ci.yml`) - Triggers: Push to main, all PRs - Node Version: Latest LTS (automatic) - Steps: Install → Test → Build → TypeScript check **Dependabot Auto-Merge** (`.github/workflows/dependabot-auto-merge.yml`) - Auto-merges Dependabot PRs when all tests pass - Uses squash merge strategy - Waits for CI completion before merging **Dependabot Config** (`.github/dependabot.yml`) - Weekly updates every Monday - Max 10 open PRs - Labels: "dependencies", "automated" ### Pre-commit Checklist Before committing: - [ ] All tests pass (`npm test`) - [ ] Build succeeds (`npm run build`) - [ ] New code has tests - [ ] Test fixtures include all required type fields - [ ] Updated AGENTS.md if patterns changed ## Environment Configuration ```bash # Required UNSPLASH_ACCESS_KEY=your_key_here # Optional TRANSPORT=stdio # or "http" PORT=3000 # for HTTP transport ``` ## Common Issues & Solutions ### Issue: Tests fail with "missing properties" **Solution:** Check `src/types.ts` for all required fields. Test fixtures must include every non-optional field. ### Issue: "Module not found" errors **Solution:** Ensure import paths end with `.js` (ESM requirement) ### Issue: API returns 401 Unauthorized **Solution:** Verify `UNSPLASH_ACCESS_KEY` is set correctly ### Issue: Build fails after adding new imports **Solution:** Run `npm install` to ensure dependencies are installed ## Debugging Tips **Enable API response logging:** ```typescript console.error("API Response:", JSON.stringify(response, null, 2)); ``` **Check test output:** ```bash node --test dist/**/*.test.js --test-reporter=spec ``` ## Resources - [MCP SDK Documentation](https://modelcontextprotocol.io/) - [Unsplash API Reference](https://unsplash.com/documentation) - [Zod Schema Validation](https://zod.dev/) - [Node.js Test Runner](https://nodejs.org/api/test.html) ## Decision Framework **When to add a new file:** - New tool category → New file in `src/tools/` - New service layer → New file in `src/services/` - Always colocate tests with source (`.test.ts`) **When to modify existing files:** - Adding similar functionality → Extend existing tool file - New response format → Add to `formatters.ts` - New types → Add to `types.ts` **When to write tests:** - Always! Every new function needs tests - Test both success and error cases - Mock `fetch` for API client tests ## Best Practices Summary 1. **Read before writing:** Always read `src/types.ts` before creating test fixtures 2. **Test thoroughly:** Cover success cases, error cases, and edge cases 3. **Follow patterns:** Match existing code style and structure 4. **Document decisions:** Update this file when introducing new patterns 5. **CI-first:** Let workflows complete before merging 6. **Type safety:** Leverage TypeScript, avoid type assertions when possible 7. **User-focused:** Return helpful error messages and well-formatted responses

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/mjordi/unsplash-mcp-server'

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