Skip to main content
Glama

App Store Connect MCP Server

CLAUDE.md10.4 kB
# App Store Connect MCP Project Memory ## ⚠️ PUBLIC REPOSITORY RULES - **This is a PUBLIC GitHub repository** - **NEVER include specific revenue numbers in commits** - **Keep commit messages vague about financial data** - **Use terms like "improved accuracy" not specific percentages** - **Remove actual dollar amounts from documentation** ## 🚨 PROJECT CONTEXT **Created**: August 21, 2025 @ 2:24 PM **Why**: NPM package @joshuarileydev/app-store-connect-mcp-server returns 404. We build our own. **Timeline**: 3 hours to MVP, shipping TODAY **First User**: RenovateAI (90% of revenue is iOS - CRITICAL) ## 🎯 CURRENT STATUS (3:32 PM - Aug 21) - ✅ PRFAQ written - vision clear - ✅ RFC-001 drafted - architecture defined - ✅ Project structure created in ~/Code/te/appstore-connect-mcp - ✅ Authentication layer complete - JWT generation working - ✅ API client built - rate limiting, pagination, error handling - ✅ MCP server wrapper done - 8 tools exposed to Claude - ✅ TypeScript compiles successfully - ✅ Published to GitHub: https://github.com/TrialAndErrorAI/appstore-connect-mcp - ✅ First Trial and Error Inc open source contribution - 🔄 Ready to test with RenovateAI credentials - ⏳ Need App Store Connect API key to proceed ## 🚀 NEXT STEPS (Priority Order) 1. **Get App Store Connect Credentials** - Login to App Store Connect with RenovateAI account - Generate API Key (Admin or Finance role) - Download P8 file (ONLY downloadable once!) - Note KEY_ID and ISSUER_ID - Find VENDOR_NUMBER in Payments section 2. **Test Authentication** ```bash cd ~/Code/te/appstore-connect-mcp cp .env.example .env # Add credentials to .env npm run test:auth ``` 3. **Test API Connection** ```bash npm run test:api ``` 4. **Configure Claude Desktop** - Edit `~/Library/Application Support/Claude/claude_desktop_config.json` - Add MCP server configuration - Restart Claude Desktop 5. **Complete SID-157** - Pull real metrics using Claude - No more estimates! ## 🏗️ ARCHITECTURE DECISIONS ### Core Stack - **TypeScript** - Type safety for Apple's complex API - **MCP SDK** - @modelcontextprotocol/sdk for server - **jsonwebtoken** - JWT generation for Apple auth - **axios** - HTTP client (reliable, well-tested) - **No heavy frameworks** - Keep it simple ### Key Design Choices 1. **Local-first**: Runs on developer machine, not SaaS 2. **Generic package**: Not RenovateAI-specific, helps everyone 3. **Minimal dependencies**: Less to break 4. **AI-optimized responses**: Format for Claude consumption 5. **Automatic pagination**: Handle large datasets gracefully ### Three-Layer Architecture ``` 1. Auth Layer (src/auth/) - JWT generation from P8 key - Token caching (19-min validity) - Auto-refresh 2. API Client (src/api/) - Typed wrappers for Apple endpoints - Rate limiting (3600/hour) - Pagination with async generators 3. MCP Server (src/server/) - Tool definitions for Claude - Response formatting - Error handling ``` ## 📚 APPLE API REFERENCE ### Authentication - **Base URL**: `https://api.appstoreconnect.apple.com/v1` - **Auth Header**: `Authorization: Bearer [JWT_TOKEN]` - **Token Expiry**: 20 minutes (we refresh at 19) - **Algorithm**: ES256 with P8 private key ### Critical Endpoints We Need ```typescript // Financial GET /v1/salesReports GET /v1/financeReports // Analytics GET /v1/analyticsReportRequests GET /v1/apps/{id}/analyticsReportRequests // Apps GET /v1/apps GET /v1/apps/{id} // Beta GET /v1/betaTesters GET /v1/betaGroups // Reviews GET /v1/customerReviews GET /v1/apps/{id}/customerReviews ``` ### JWT Token Structure ```javascript { "iss": "ISSUER_ID", // Your issuer ID "iat": 1234567890, // Issued at (unix timestamp) "exp": 1234568890, // Expires (20 min from iat) "aud": "appstoreconnect-v1", // Always this value "scope": ["GET"] // Read-only by default } ``` ## 🧪 TESTING APPROACH ### Manual Testing Commands ```bash # Test JWT generation npm run test:auth # Test single API call npm run test:api -- --endpoint /v1/apps # Test MCP server locally npm run dev # Full integration test npm run test:integration ``` ### Test with Real Credentials 1. Create `.env.test` with: ``` APP_STORE_KEY_ID=your_key APP_STORE_ISSUER_ID=your_issuer APP_STORE_P8_PATH=/path/to/key.p8 ``` 2. Run: `npm run test:real` ## 🛠️ COMMON COMMANDS ```bash # Development npm run dev # Start with hot reload npm run build # Compile TypeScript npm run type-check # Check types only # Testing npm test # Run test suite npm run test:watch # Test with watch mode # Production npm start # Run compiled version npm run docker:build # Build Docker image npm run docker:run # Run in container ``` ## 🐛 KNOWN ISSUES & SOLUTIONS ### Issue: "Invalid JWT token" **Solution**: Check P8 key format - must include header/footer lines: ``` -----BEGIN PRIVATE KEY----- [key content] -----END PRIVATE KEY----- ``` ### Issue: Rate limit (429) **Solution**: Built-in exponential backoff. If persists, reduce request frequency. ### Issue: Empty responses **Solution**: Check app ID exists and you have access permissions. ## 📝 TODO IMPLEMENTATION ORDER 1. **Authentication (CURRENT)** - [x] Create directory structure - [ ] Implement JWTManager class - [ ] Test token generation - [ ] Add token caching 2. **API Client** - [ ] Create base client class - [ ] Add request method with auth - [ ] Implement pagination - [ ] Add rate limiting 3. **MCP Server** - [ ] Initialize MCP SDK server - [ ] Define tool schemas - [ ] Map tools to API calls - [ ] Format responses for AI 4. **Testing** - [ ] Test with RenovateAI credentials - [ ] Verify all tools work - [ ] Check Claude Desktop integration - [ ] Performance testing 5. **Polish** - [ ] Error messages - [ ] Documentation - [ ] npm publish - [ ] GitHub release ## 🔑 CREDENTIALS NEEDED From App Store Connect → Users and Access → Keys: 1. **KEY_ID**: 10-character string (e.g., ABC123DEF4) 2. **ISSUER_ID**: UUID format (e.g., 12345678-1234-1234-1234-123456789012) 3. **P8 File**: Private key (download once, keep secure) 4. **VENDOR_NUMBER**: (optional) For financial reports - find in Payments section ## 💡 KEY INSIGHTS 1. **Apple's API is well-designed** - Consistent patterns, good docs 2. **JWT auth is straightforward** - Just sign with P8 key 3. **Rate limits are generous** - 3600/hour is plenty 4. **Pagination is required** - Some endpoints return lots of data 5. **MCP SDK is minimal** - Most work is in API client ## ⚡ QUICK WINS - Start with `/v1/apps` endpoint - simplest, no parameters - Financial reports are most valuable for RenovateAI - TestFlight data is easy and impressive - Reviews/ratings are great for demos ## 🚀 SHIPPING CHECKLIST - [ ] All tests passing - [ ] Works in Claude Desktop - [ ] README has clear instructions - [ ] Credentials documented - [ ] npm package published - [ ] GitHub repo public - [ ] Tweet announcement - [ ] Update Linear ticket ## 🎯 SUCCESS METRICS - **Technical**: Response time < 2 seconds - **Adoption**: 100+ GitHub stars week 1 - **Quality**: Zero critical bugs - **Business**: SID-157 completed with real data --- ## SESSION NOTES ### Aug 21, 2:32 PM - Made architectural decision to build from here (not separate Claude session) - Maintaining flow and context in main secondbrain workspace - Thursday no-meeting day - perfect for deep work - Energy is high, momentum building ### Implementation Strategy - Write code directly, test immediately - No over-engineering - MVP first - Use RenovateAI as test case - Ship working code today ## 🔧 MCP SERVER DEVELOPMENT ### Critical Rules & Lessons Learned 1. **CRITICAL RULE**: MCP servers MUST NOT output ANYTHING to stdout except JSON-RPC messages - Any stdout output will break communication with Claude - This includes debug messages, library outputs, and dotenv warnings 2. **Console Suppression Pattern**: Suppress all console output at the very start of index.ts: ```typescript console.log = () => {}; console.error = process.stderr.write.bind(process.stderr); ``` 3. **dotenv Issue**: dotenv outputs debug tips to stdout - must be suppressed - Load dotenv after console suppression - Or use alternative environment loading methods 4. **Testing Pattern**: Test MCP output with: ```bash node dist/index.js 2>/dev/null < /dev/null | head -1 ``` - Should return empty (no stdout output) - Any output indicates a protocol violation 5. **Debugging**: Check Claude logs at: ``` /Users/sid/Library/Logs/Claude/mcp-server-*.log ``` - Use process.stderr for debug output - Never use console.log in production 6. **Apple JWT**: No 'scope' field in JWT payload for App Store Connect - Remove scope field from JWT generation - Apple's API uses different auth patterns than expected ## API Response Handling 1. **GZIPPED Responses**: Apple returns gzipped CSV data for reports - Detect gzip header: starts with `\u001f\u008b\u0008` - Use Node's zlib: `import { gunzipSync } from 'zlib'` - Pattern: `const decompressed = gunzipSync(Buffer.from(response))` 2. **Version Requirements**: Different Apple report types need different versions - Sales reports: version "1_1" - Subscription reports: version "1_3" - Financial reports: version "1_0" - Error message tells you the correct version: "The latest version for this report is X_X" 3. **Testing Financial APIs**: - Create dedicated test scripts (test-financial.ts) - Test each report type separately - Check response type and length before processing ## Pragmatic Debugging - MCP Specific - When API returns unexpected format, check first 10 bytes for magic headers - Binary/compressed data shows as unicode garbage in console - Use Buffer.from() and check for compression headers - Apple's API errors are actually helpful - they tell you the correct version ### Root Cause Philosophy - **Fix the problem, not the blame** - Focus on solutions - **Find root causes** - Understand why MCP communication breaks - **Test assumptions** - Verify protocol compliance early --- *"We build what doesn't exist. Today."* - Trial and Error Inc

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/TrialAndErrorAI/appstore-connect-mcp'

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