{"_id":"@abd3lraouf/app-store-connect-mcp","_rev":"4-ddabba99a98971c624f3c30563df0bcb","name":"@abd3lraouf/app-store-connect-mcp","dist-tags":{"latest":"1.3.0"},"versions":{"1.0.0":{"name":"@abd3lraouf/app-store-connect-mcp","version":"1.0.0","keywords":["mcp","mcp-server","model-context-protocol","claude","claude-code","anthropic","app-store-connect","app-store-connect-api","appstoreconnect","app-store-server-api","storekit","storekit2","testflight","app-review","ios","macos","apple","subscriptions","in-app-purchase","aso"],"author":{"name":"Abdelraouf Sabri"},"license":"BUSL-1.1","_id":"@abd3lraouf/app-store-connect-mcp@1.0.0","maintainers":[{"name":"abd3lraouf","email":"abdelraoufsabri@gmail.com"}],"homepage":"https://github.com/abd3lraouf-studios/app-store-connect-mcp","bugs":{"url":"https://github.com/abd3lraouf-studios/app-store-connect-mcp/issues"},"bin":{"asc-mcp":"dist/index.js"},"dist":{"shasum":"81c55223f76fd1071318f86e494ea800bbb4f8fa","tarball":"https://registry.npmjs.org/@abd3lraouf/app-store-connect-mcp/-/app-store-connect-mcp-1.0.0.tgz","fileCount":83,"integrity":"sha512-gc1qnpAHz+KBUIwEUHz8wtN1f+XLpBg6o22AwSmXpQNCupHCapsQpScCVhzq4mozk0tjYF0DSiiqL6qAHDCCLQ==","signatures":[{"sig":"MEUCIBk5rYPSJh6Uqfm5QpAEOuMSsXP+zVKwHGFEzvgcKettAiEAqMzQRiEYWoxww+lA/44BThxjW1KvZYTMvQRuTsEgYeU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4144008},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"46ffa9249e1db653c7080799975a55e47fedf447","mcpName":"io.github.abd3lraouf-studios/app-store-connect-mcp","scripts":{"lint":"eslint src test scripts","test":"vitest run","build":"npm run build:index && tsc -p tsconfig.build.json","start":"node dist/index.js","verify":"node scripts/verify-apis.mjs","coverage":"vitest run --coverage","preflight":"node scripts/preflight-release.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","build:index":"node scripts/build-index.mjs","build:types":"npx -y openapi-typescript@7 spec/apple-openapi.json -o spec/apple-api.d.ts","fetch:specs":"node scripts/fetch-specs.mjs"},"_npmUser":{"name":"abd3lraouf","email":"abdelraoufsabri@gmail.com"},"repository":{"url":"git+https://github.com/abd3lraouf-studios/app-store-connect-mcp.git","type":"git"},"_npmVersion":"11.19.0","description":"App Store Connect MCP server for Claude Code, Claude Desktop and Cursor. 1,293 App Store Connect and StoreKit 2 (App Store Server API) operations behind 11 tools, with macOS Keychain credentials, Apple JWS signature verification and risk-tiered write conf","directories":{},"_nodeVersion":"26.7.0","dependencies":{"express":"^4.21.2","jsonwebtoken":"^9.0.2","@modelcontextprotocol/sdk":"^1.0.0","@apple/app-store-server-library":"^3.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.15.0","eslint":"^9.39.5","vitest":"^4.1.11","typescript":"~5.9.3","@types/node":"^22.10.0","@types/express":"^5.0.0","typescript-eslint":"^8.67.0","@types/jsonwebtoken":"^9.0.7","@vitest/coverage-v8":"^4.1.11"},"_npmOperationalInternal":{"tmp":"tmp/app-store-connect-mcp_1.0.0_1787092754728_0.005790273448019301","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@abd3lraouf/app-store-connect-mcp","version":"1.1.0","keywords":["mcp","mcp-server","model-context-protocol","claude","claude-code","anthropic","app-store-connect","app-store-connect-api","appstoreconnect","app-store-server-api","storekit","storekit2","testflight","app-review","ios","macos","apple","subscriptions","in-app-purchase","aso"],"author":{"name":"Abdelraouf Sabri"},"license":"BUSL-1.1","_id":"@abd3lraouf/app-store-connect-mcp@1.1.0","maintainers":[{"name":"abd3lraouf","email":"abdelraoufsabri@gmail.com"}],"homepage":"https://github.com/abd3lraouf-studios/app-store-connect-mcp","bugs":{"url":"https://github.com/abd3lraouf-studios/app-store-connect-mcp/issues"},"bin":{"asc-mcp":"dist/index.js"},"dist":{"shasum":"e63ca5957daa111f58a80d813b744b7144becd34","tarball":"https://registry.npmjs.org/@abd3lraouf/app-store-connect-mcp/-/app-store-connect-mcp-1.1.0.tgz","fileCount":86,"integrity":"sha512-5JpLEIxqm2yXIXROBsl2BXbfbBh62oOL4fxVP1JrHhdGhO2N0NMSjGesqfl9GsyzYj28JS3IqbSs3wbYqVF57g==","signatures":[{"sig":"MEUCIBZgecKUzvVio1V/NMXNez/3WOiKIYNKmNskq0GygW0aAiEAvCxUyFwcrt5njVdcntf9vbjtLnp9B651b1KmNrgtOZc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@abd3lraouf%2fapp-store-connect-mcp@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":4181020},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"1f89baffe8244402325e4e789e7867fa068bd8cb","mcpName":"io.github.abd3lraouf-studios/app-store-connect-mcp","scripts":{"lint":"eslint src test scripts","test":"vitest run","build":"npm run build:index && tsc -p tsconfig.build.json","start":"node dist/index.js","verify":"node scripts/verify-apis.mjs","coverage":"vitest run --coverage","preflight":"node scripts/preflight-release.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","build:index":"node scripts/build-index.mjs","build:types":"npx -y openapi-typescript@7 spec/apple-openapi.json -o spec/apple-api.d.ts","fetch:specs":"node scripts/fetch-specs.mjs","release:version":"node scripts/bump-version.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0b31f2bc-970b-4e03-b158-8f3c6ac5564e"}},"repository":{"url":"git+https://github.com/abd3lraouf-studios/app-store-connect-mcp.git","type":"git"},"_npmVersion":"12.0.2","description":"App Store Connect MCP server for Claude Code, Claude Desktop and Cursor. 1,293 App Store Connect and StoreKit 2 (App Store Server API) operations behind 13 tools, with macOS Keychain credentials, Apple JWS signature verification and risk-tiered write conf","directories":{},"_nodeVersion":"24.19.0","dependencies":{"express":"^4.21.2","jsonwebtoken":"^9.0.2","@modelcontextprotocol/sdk":"^1.0.0","@apple/app-store-server-library":"^3.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.15.0","eslint":"^9.39.5","vitest":"^4.1.11","typescript":"~5.9.3","@types/node":"^22.10.0","@types/express":"^5.0.0","typescript-eslint":"^8.67.0","@types/jsonwebtoken":"^9.0.7","@vitest/coverage-v8":"^4.1.11"},"_npmOperationalInternal":{"tmp":"tmp/app-store-connect-mcp_1.1.0_1787110099853_0.980408038326728","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@abd3lraouf/app-store-connect-mcp","version":"1.2.0","keywords":["mcp","mcp-server","model-context-protocol","claude","claude-code","anthropic","app-store-connect","app-store-connect-api","appstoreconnect","app-store-server-api","storekit","storekit2","testflight","app-review","ios","macos","apple","subscriptions","in-app-purchase","aso"],"author":{"name":"Abdelraouf Sabri"},"license":"BUSL-1.1","_id":"@abd3lraouf/app-store-connect-mcp@1.2.0","maintainers":[{"name":"abd3lraouf","email":"abdelraoufsabri@gmail.com"}],"homepage":"https://github.com/abd3lraouf-studios/app-store-connect-mcp","bugs":{"url":"https://github.com/abd3lraouf-studios/app-store-connect-mcp/issues"},"bin":{"asc-mcp":"dist/index.js","asc-mcp-skill":"scripts/install-skill.mjs"},"dist":{"shasum":"0d02147f2dc6764c3f9fe1b3816cf291cf04984c","tarball":"https://registry.npmjs.org/@abd3lraouf/app-store-connect-mcp/-/app-store-connect-mcp-1.2.0.tgz","fileCount":95,"integrity":"sha512-twpf+ilhOQG6itsUM8c+fnsR2GzQNQfSoIHndcUVc74JCLYwi89U3WSd7mFjhpVOL4pGxlSCUDvi0dTNJUAaPQ==","signatures":[{"sig":"MEUCIQDd5sM5z0ctj+agddtkOCus5uToPcT9sjMKU6b7gktd4AIgfe5lv0RTjA5U24ufRiz6WxJb8NNUKstpgbcRixqJusI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@abd3lraouf%2fapp-store-connect-mcp@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":4270082},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.19.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./failure-log":{"types":"./dist/failure-log.d.ts","default":"./dist/failure-log.js"},"./package.json":"./package.json"},"gitHead":"3861bc56d3a790ebb52bf663f6eaec89cc715e60","mcpName":"io.github.abd3lraouf-studios/app-store-connect-mcp","scripts":{"lint":"eslint src test scripts skills","test":"vitest run","build":"npm run build:index && tsc -p tsconfig.build.json","start":"node dist/index.js","triage":"node scripts/triage-failures.mjs","verify":"node scripts/verify-apis.mjs","coverage":"vitest run --coverage","preflight":"node scripts/preflight-release.mjs","typecheck":"tsc --noEmit","test:watch":"vitest","build:index":"node scripts/build-index.mjs","build:types":"npx -y openapi-typescript@7 spec/apple-openapi.json -o spec/apple-api.d.ts","fetch:specs":"node scripts/fetch-specs.mjs","install:skill":"node scripts/install-skill.mjs","release:version":"node scripts/bump-version.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0b31f2bc-970b-4e03-b158-8f3c6ac5564e"}},"repository":{"url":"git+https://github.com/abd3lraouf-studios/app-store-connect-mcp.git","type":"git"},"_npmVersion":"12.0.2","description":"App Store Connect MCP server for Claude Code, Claude Desktop and Cursor. 1,293 App Store Connect and StoreKit 2 (App Store Server API) operations behind 13 tools, with macOS Keychain credentials, Apple JWS signature verification and risk-tiered write conf","directories":{},"_nodeVersion":"24.19.0","dependencies":{"express":"^4.21.2","jsonwebtoken":"^9.0.2","@modelcontextprotocol/sdk":"^1.0.0","@apple/app-store-server-library":"^3.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"msw":"^2.15.0","eslint":"^9.39.5","vitest":"^4.1.11","typescript":"~5.9.3","@types/node":"^22.10.0","@types/express":"^5.0.0","typescript-eslint":"^8.67.0","@types/jsonwebtoken":"^9.0.7","@vitest/coverage-v8":"^4.1.11"},"_npmOperationalInternal":{"tmp":"tmp/app-store-connect-mcp_1.2.0_1787386176969_0.7870262537846868","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@abd3lraouf/app-store-connect-mcp","version":"1.3.0","description":"App Store Connect MCP server for Claude Code, Claude Desktop and Cursor. 1,293 App Store Connect and StoreKit 2 (App Store Server API) operations behind 13 tools, with macOS Keychain credentials, Apple JWS signature verification and risk-tiered write conf","type":"module","bin":{"asc-mcp":"dist/index.js","asc-mcp-skill":"scripts/install-skill.mjs"},"main":"dist/index.js","scripts":{"build:index":"node scripts/build-index.mjs","build":"npm run build:index && tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","start":"node dist/index.js","fetch:specs":"node scripts/fetch-specs.mjs","verify":"node scripts/verify-apis.mjs","build:types":"npx -y openapi-typescript@7 spec/apple-openapi.json -o spec/apple-api.d.ts","test":"vitest run","test:watch":"vitest","coverage":"vitest run --coverage","preflight":"node scripts/preflight-release.mjs","lint":"eslint src test scripts skills","release:version":"node scripts/bump-version.mjs","install:skill":"node scripts/install-skill.mjs","triage":"node scripts/triage-failures.mjs"},"engines":{"node":">=22.19.0"},"license":"BUSL-1.1","dependencies":{"@apple/app-store-server-library":"^3.1.0","@modelcontextprotocol/sdk":"^1.0.0","express":"^4.21.2","jsonwebtoken":"^9.0.2"},"devDependencies":{"@types/express":"^5.0.0","@types/jsonwebtoken":"^9.0.7","@types/node":"^22.10.0","@vitest/coverage-v8":"^4.1.11","eslint":"^9.39.5","msw":"^2.15.0","typescript":"~5.9.3","typescript-eslint":"^8.67.0","vitest":"^4.1.11"},"repository":{"type":"git","url":"git+https://github.com/abd3lraouf-studios/app-store-connect-mcp.git"},"keywords":["mcp","mcp-server","model-context-protocol","claude","claude-code","anthropic","app-store-connect","app-store-connect-api","appstoreconnect","app-store-server-api","storekit","storekit2","testflight","app-review","ios","macos","apple","subscriptions","in-app-purchase","aso"],"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./failure-log":{"types":"./dist/failure-log.d.ts","default":"./dist/failure-log.js"},"./package.json":"./package.json"},"mcpName":"io.github.abd3lraouf-studios/app-store-connect-mcp","homepage":"https://github.com/abd3lraouf-studios/app-store-connect-mcp","bugs":{"url":"https://github.com/abd3lraouf-studios/app-store-connect-mcp/issues"},"author":{"name":"Abdelraouf Sabri"},"publishConfig":{"access":"public"},"gitHead":"9f89f96c9ea84c882078adab3efb4e7515f067e6","types":"./dist/index.d.ts","_id":"@abd3lraouf/app-store-connect-mcp@1.3.0","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-klJjWTjWadT8PfMmJqxwSDb63huzBWzocG64YZWmWfT5/vkrYLiMD5p5+jjbDiKvmjegwpGWUMC5M6ch6VYKGQ==","shasum":"3eb16c7cbdf050cbf90e0e9ad59c370f6fa697cd","tarball":"https://registry.npmjs.org/@abd3lraouf/app-store-connect-mcp/-/app-store-connect-mcp-1.3.0.tgz","fileCount":95,"unpackedSize":4272828,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@abd3lraouf%2fapp-store-connect-mcp@1.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICijlTeMMsGIXJwvWo+fWMEXC8tZ36kpG6AYtW2SNIVVAiA2Lg1A2fsQJRUxGvz/ZQeYgKI/Lo8GqGqfBkjLOYg3HA=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0b31f2bc-970b-4e03-b158-8f3c6ac5564e"}},"directories":{},"maintainers":[{"name":"abd3lraouf","email":"abdelraoufsabri@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/app-store-connect-mcp_1.3.0_1788181220620_0.7158888139006636"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T22:39:14.567Z","modified":"2026-08-31T13:00:21.057Z","1.0.0":"2026-08-18T22:39:14.900Z","1.1.0":"2026-08-19T03:28:20.013Z","1.2.0":"2026-08-22T08:09:37.150Z","1.3.0":"2026-08-31T13:00:20.790Z"},"bugs":{"url":"https://github.com/abd3lraouf-studios/app-store-connect-mcp/issues"},"author":{"name":"Abdelraouf Sabri"},"license":"BUSL-1.1","homepage":"https://github.com/abd3lraouf-studios/app-store-connect-mcp","keywords":["mcp","mcp-server","model-context-protocol","claude","claude-code","anthropic","app-store-connect","app-store-connect-api","appstoreconnect","app-store-server-api","storekit","storekit2","testflight","app-review","ios","macos","apple","subscriptions","in-app-purchase","aso"],"repository":{"type":"git","url":"git+https://github.com/abd3lraouf-studios/app-store-connect-mcp.git"},"description":"App Store Connect MCP server for Claude Code, Claude Desktop and Cursor. 1,293 App Store Connect and StoreKit 2 (App Store Server API) operations behind 13 tools, with macOS Keychain credentials, Apple JWS signature verification and risk-tiered write conf","maintainers":[{"name":"abd3lraouf","email":"abdelraoufsabri@gmail.com"}],"readme":"# App Store Connect MCP Server\n\n**Give Claude your App Store Connect account without giving it the keys to your pricing.**\nAn MCP server covering the App Store Connect API *and* the App Store Server API\n(StoreKit 2) — 1,293 operations behind 13 tools, for Claude Code, Claude Desktop,\nCursor and anything else that speaks [Model Context Protocol](https://modelcontextprotocol.io).\n\n[![npm](https://img.shields.io/npm/v/@abd3lraouf/app-store-connect-mcp?logo=npm&color=cb3837)](https://www.npmjs.com/package/@abd3lraouf/app-store-connect-mcp)\n[![CI](https://github.com/abd3lraouf-studios/app-store-connect-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/abd3lraouf-studios/app-store-connect-mcp/actions/workflows/ci.yml)\n[![Tests](https://img.shields.io/badge/tests-300%20passing-brightgreen?logo=vitest&logoColor=white)](#receipts)\n[![Coverage](https://img.shields.io/badge/line%20coverage-93%25-brightgreen)](#receipts)\n[![Licence](https://img.shields.io/badge/licence-BUSL--1.1-orange)](LICENSE)\n\n[![Operations](https://img.shields.io/badge/Apple%20API%20operations-1%2C293-0b5fff)](#keeping-current-with-apple)\n[![Tools](https://img.shields.io/badge/MCP%20tools-11-0b5fff)](#the-eleven-tools)\n[![MCP](https://img.shields.io/badge/protocol-MCP%202025--11--25-8a3ffc?logo=anthropic&logoColor=white)](#receipts)\n[![Node](https://img.shields.io/badge/node-%E2%89%A522.19-339933?logo=nodedotjs&logoColor=white)](package.json)\n[![Platform](https://img.shields.io/badge/macOS%20%C2%B7%20Linux%20%C2%B7%20Windows-lightgrey?logo=apple&logoColor=white)](#known-limits)\n[![Install size](https://img.shields.io/badge/tarball-276%20kB-lightgrey)](#install)\n\n```\n1,293 operations · 13 tools · key never on disk · Apple signatures verified\n```\n\n```mermaid\nflowchart LR\n    A[\"Claude<br/>Cursor · any MCP client\"] -->|\"search · call · write\"| B[\"app-store-connect-mcp<br/>13 tools\"]\n    B --> C{\"risk tier\"}\n    C -->|\"READ · 811 ops\"| D[\"Apple<br/>App Store Connect API\"]\n    C -->|\"WRITE · 482 ops\"| E[\"ask a human first\"]\n    E -->|approved| D\n    E -->|declined| F[\"nothing is sent\"]\n    B --> G[\"App Store Server API<br/>StoreKit 2 · signatures verified\"]\n\n    style E fill:#ffe8b3,stroke:#c98a00,color:#000\n    style F fill:#ffd6d6,stroke:#c00,color:#000\n    style D fill:#d6f5d6,stroke:#2a2,color:#000\n    style G fill:#d6f5d6,stroke:#2a2,color:#000\n```\n\n---\n\n## Don't install this\n\nGenuinely. There are cheaper ways to spend your afternoon, and several kinds of\nperson should close the tab now:\n\n**You want an agent that just does things.** This one stops and asks before it\nchanges a price, deletes anything, or touches who can access your account — and\nit asks *you*, not itself. If that sounds like friction, it is. That is the\nproduct.\n\n**You want every endpoint as its own tool.** Some servers register 890. Yours\nwould spend six figures of context on tool definitions before answering a single\nquestion. This registers 11 and finds the rest by searching.\n\n```text\ntool definitions loaded into context, before you ask anything\n\n  one tool per endpoint   ███████████████████████████████████   >100k tokens\n  this server             ▌                                       ~1k tokens\n```\n\n**You're on Windows or Linux and wanted Keychain.** Keychain storage is macOS\nonly. You can use a file path elsewhere, but the best part of this is\nmacOS-shaped.\n\n**You want it to write your App Store copy.** It will fetch your reviews and\nyour localisations. It will not invent marketing prose and push it live, and\nthere is no flag to make it.\n\n**You're evaluating this for a product you sell.** Read [the licence](LICENSE)\nfirst. Internal use is free; reselling it isn't.\n\nStill here? Then the rest is probably for you.\n\n---\n\n## What it refuses to do\n\nMost of the engineering here went into restraint, so it is the honest place to\nstart.\n\n**It won't run generated code.** The elegant way to cover a huge API is to let\nthe model write JavaScript and `eval` it in a sandbox. Node's `vm` is not a\nsandbox — its own documentation says so — and any host object handed in leaks\nthe whole realm back through its prototype chain:\n\n```js\nspec.constructor.constructor('return process.env.HOME')()   // → /Users/you\n```\n\nThat is a reproduction of a real shipping MCP server's sandbox, and it returns\nyour home directory. Its 15-second timeout doesn't help either: it bounds only\n*synchronous* code, so an `async` loop runs forever. This server dispatches\n**parameters**, not code. Same coverage, same token cost, nothing to escape.\n\n**It won't let a write pretend to be a read.** Reads and writes are separate\ntools. `asc_write` carries `_meta[\"anthropic/requiresUserInteraction\"]`, which\nClaude Code honours **even under `bypassPermissions`**. The model cannot talk\nits way past it; only the operator can, by starting the server with\n`--no-confirm` — which drops the flag, the prompt and the token together, for\nruns where nobody is present to answer.\n\n**It won't decide your pricing intent for you.** `preserve_current_price` is a\nrequired parameter with no default. Apple defaults it to `false` — meaning your\nexisting subscribers get moved to the new price. Making it required forces that\ndecision into the open, where a person can see it.\n\n**It won't create ongoing commitments to answer a question.** Fetching analytics\nneeds a report request, and `accessType: ONGOING` is a standing obligation on\nyour account, not a query. The tool reads reports; it will not create one\nsilently.\n\n**It won't pretend it sanitised your reviews.** Customer review text is written\nby strangers and lands in your model's context verbatim. Results carrying it\n*lead* with a note saying it is data to report on, not instructions to follow.\nIt is deliberately not filtered for injection phrases — that is a game attackers\niterate against, and passing such a filter would imply a safety it cannot\ndeliver.\n\n**It won't tell you a signature is fine when it hasn't checked.** See below.\n\n---\n\n## Install\n\n**Claude Code, one line:**\n\n```bash\nclaude mcp add --scope user app-store-connect \\\n  --env ASC_KEY=keychain:my-asc-key \\\n  --env ASC_BUNDLE_ID=com.example.app \\\n  --env ASC_APP_APPLE_ID=1234567890 \\\n  -- npx -y @abd3lraouf/app-store-connect-mcp\n```\n\nNothing to clone or build. Or from source, if you'd rather read it first:\n\n```bash\ngit clone https://github.com/abd3lraouf-studios/app-store-connect-mcp\ncd app-store-connect-mcp && npm install && npm run build\n```\n\nOr, for Claude Desktop, Cursor and friends:\n\n```json\n{\n  \"mcpServers\": {\n    \"app-store-connect\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@abd3lraouf/app-store-connect-mcp\"],\n      \"env\": {\n        \"ASC_KEY\": \"keychain:my-asc-key\",\n        \"ASC_BUNDLE_ID\": \"com.example.app\",\n        \"ASC_APP_APPLE_ID\": \"1234567890\"\n      }\n    }\n  }\n}\n```\n\nThen ask it *\"check the App Store Connect connection\"* — that runs `asc_status`,\nwhich verifies your credentials with one lightweight request and tells you\nexactly what is missing if anything is.\n\n### Your key belongs in the Keychain\n\nApple lets you download a `.p8` **exactly once**. A plaintext copy on disk is a\ncopy that can leak.\n\n```bash\nASC_KEY=keychain:my-asc-key          # recommended\nASC_KEY=/path/to/AuthKey.p8          # works, but plaintext\nASC_PRIVATE_KEY='-----BEGIN…'        # discouraged: ps -E exposes it\n```\n\nStore it as base64 JSON so the identifiers travel *with* the key material —\n`ASC_KEY_ID` then cannot drift out of sync with the key it names, a mismatch\nthat surfaces only as an opaque 401:\n\n```bash\nsecurity add-generic-password -s my-asc-key -a api -w \"$(\n  jq -nc --arg i \"$ISSUER\" --arg k \"$KEYID\" --arg p \"$(cat AuthKey.p8)\" \\\n    '{issuerID:$i,keyID:$k,privateKeyPEM:$p}' | base64\n)\"\n```\n\n---\n\n## The thirteen tools\n\n**Five core**, covering everything:\n\n| Tool | |\n|---|---|\n| `asc_status` | Credentials, reachability, remaining rate-limit budget. Run this first when anything fails — it separates a bad key from a bad request. |\n| `asc_search_endpoints` | Search 1,293 operations across both APIs by keyword, method, tag or risk tier. |\n| `asc_describe_endpoint` | Parameters, request-body schema with real field names, risk tier. |\n| `asc_call` | **Reads.** Path and query parameters, pagination, both APIs. |\n| `asc_write` | **Everything that changes data.** Confirmation, `dry_run`. |\n\n**Eight composite**, for chains the raw API cannot express in a single call. A\ntool that merely saved one request was left out — it would need keeping in step\nwith Apple forever and buys nothing `asc_call` doesn't already do:\n\n| Tool | What it collapses |\n|---|---|\n| `asc_pricing_get` | ~175 lookups → a handful, for subscriptions **and** one-time purchases. The **currency lives on the territory**, not the price row, so reading prices by hand gives ambiguous numbers. |\n| `asc_pricing_set` | The same chain plus the write, with the subscriber decision forced into the open. |\n| `asc_preflight_version` | Six resources → **GO / NO-GO**, each gap naming the operation that fixes it. |\n| `asc_listing_screenshots` | A request per locale → four, via `included`. |\n| `asc_upload_screenshot` | Apple's reserve → PUT-at-offsets → commit-with-MD5 sequence, across two hosts. |\n| `asc_upload_iap_screenshot` | The same sequence for an in-app purchase's review screenshot — the field that keeps an IAP in `MISSING_METADATA`. |\n| `asc_availability_set` | One PATCH per territory (up to 175; Apple has no bulk endpoint), then **re-reads every one** and reports what did not take. |\n| `asc_analytics_report` | Five hops → signed URL → gunzip → rows, with **every segment stitched**. |\n\n<details>\n<summary><b>Why <code>asc_upload_screenshot</code> cannot be one API call</b></summary>\n\nApple's asset flow spans two hosts and ends in a checksum that fails *silently*\nif you get it wrong — the upload simply sits in `AWAITING_UPLOAD` looking like\nnothing happened. `uploadOperations` appears in Apple's OpenAPI document only as\na value in a `fields[]` enum, so an agent reading the spec can see the field\nexists and still have no idea it must act on it.\n\n```mermaid\nsequenceDiagram\n    participant M as Claude\n    participant S as this server\n    participant A as App Store Connect\n    participant U as Apple asset host\n\n    M->>S: asc_upload_screenshot(set, file)\n    S->>A: POST /v1/appScreenshots (fileName, fileSize)\n    A-->>S: id + uploadOperations[]\n    loop each byte range\n        S->>U: PUT bytes at offset, Apple's headers\n        Note over S,U: pre-signed URL — no bearer token sent\n    end\n    S->>A: PATCH uploaded=true + MD5 checksum\n    A-->>S: assetDeliveryState\n    S-->>M: state, and how to clean up if it failed\n```\n\n</details>\n\nPlus **four resources** (`@asc:cookbook`, `@asc:enums`, `@asc:risk`,\n`@asc:sources`) and **four workflows** as slash commands:\n`/mcp__asc__release-readiness`, `pricing-audit`, `review-triage`,\n`testflight-status`.\n\n---\n\n## Write safety\n\nAn HTTP method is a poor proxy for consequence. `PATCH /v1/subscriptionPrices`\nand `PATCH /v1/appInfos/{id}` are both writes; only one changes what customers\nare charged, and neither is undone by repeating it. So every operation carries a\ntier (counts are App Store Connect; StoreKit 2 adds 14 reads and 16 writes):\n\n| Tier | Count | |\n|---|---|---|\n| `READ` | 797 | No change. |\n| `WRITE` | 238 | Changes data. |\n| `REVENUE` | 61 | Pricing, subscriptions, entitlements. |\n| `DESTRUCTIVE` | 132 | Deletes. |\n| `RELEASE` | 12 | Builds, submissions, what ships. |\n| `ACCESS` | 12 | Who can reach the account. |\n| `INFRASTRUCTURE` | 11 | Certificates, identifiers, callback URLs. |\n\n```mermaid\nflowchart TD\n    A[\"asc_write called\"] --> B{\"--read-only?\"}\n    B -->|yes| Z[\"blocked\"]\n    B -->|no| N{\"--no-confirm?\"}\n    N -->|yes| S[\"send it\"]\n    N -->|no| C{\"risk tier\"}\n    C -->|\"WRITE\"| S[\"send it\"]\n    C -->|\"REVENUE · DESTRUCTIVE<br/>INFRASTRUCTURE · ACCESS · RELEASE\"| D{\"client supports<br/>elicitation?\"}\n    D -->|yes| E[\"ask the person<br/>method · path · body · tier\"]\n    D -->|no| F[\"issue a token<br/>hash-bound to this exact request\"]\n    E -->|accepted| S\n    E -->|declined| Z\n    F --> G[\"caller repeats the call<br/>with the token\"]\n    G --> S\n\n    style Z fill:#ffd6d6,stroke:#c00,color:#000\n    style E fill:#ffe8b3,stroke:#c98a00,color:#000\n    style F fill:#ffe8b3,stroke:#c98a00,color:#000\n    style S fill:#d6f5d6,stroke:#2a2,color:#000\n```\n\nThe bottom five ask before running. Where your client supports **elicitation**,\nit asks *you* directly, showing the method, path, body and tier. Otherwise it\nissues a confirmation token bound by hash to the exact operation, path, query\nand body — so one obtained for a cheap call cannot be spent on an expensive one.\nA client that claims elicitation but fails to deliver it falls back rather than\nsailing through.\n\n```\n--read-only    block every write       --confirm     confirm every write\n--no-confirm   never confirm           (default)     confirm the five tiers above\ndry_run: true  report the exact request without sending it (a parameter of asc_write)\n```\n\n`--no-confirm` is the unattended mode, and it is total: no elicitation prompt,\nno confirmation token, and the write tools stop declaring\n`requiresUserInteraction`, so Claude Code does not stop for them either. Anything\nshort of that would hand an unattended run a confirmation it cannot answer.\n\n---\n\n## Signature verification\n\nDecoding a JWS tells you what the bytes say. **Verifying** it tells you Apple\nsaid it. That distinction matters here more than most places, because these\npayloads are the evidence behind *\"is this person a paying subscriber?\"* — and a\ndecoded but unverified transaction is exactly the shape a forged one takes.\n\nEvery signed field is checked against **Apple Root CA - G3**, vendored in\n`certs/` so verification cannot be switched off by a network failure. Chain\nvalidation, expiry, revocation and the bundle/environment checks go through\nApple's own library, because those are precisely where a plausible-looking\nimplementation accepts bad input.\n\nOutcomes are **per field**, not per response — one bad signature in a history of\ntwo hundred is the case that matters:\n\n```json\n\"signedTransactionInfo_decoded\":      { \"productId\": \"premium.monthly\" },\n\"signedTransactionInfo_verification\": { \"verified\": true }\n```\n\nWhere it cannot run, payloads are still decoded and **every field says so**.\nSilence would be the dangerous outcome.\n\n---\n\n## How it compares\n\n| | Approach | Kept | Rejected |\n|---|---|---|---|\n| Hand-wrapped tools | One tool per endpoint | Typed, discoverable arguments | 70–900 tool definitions, >100k tokens, stale the moment Apple ships a version |\n| Code Mode | Model writes JS, server `eval`s it | Two tools, ~1k tokens, full coverage | Runs generated code in a process holding a signing key |\n| **Meta-tools** | `search` → `call` with **parameters** | Same context win, no code execution | — |\n\nCredit where due: several ideas here were adapted from reading\n[erayendes/app-store-connect-mcp](https://github.com/erayendes/app-store-connect-mcp)\nand [TrialAndErrorAI/appstore-connect-mcp](https://github.com/TrialAndErrorAI/appstore-connect-mcp).\nNo code was copied. Heimdall in particular is the most developed server in this\nspace, and if you want 890 typed tools organised into profiles, use it instead —\nit is good, and it is a different trade to this one.\n\n---\n\n## Receipts\n\nClaims are cheap. These are checkable:\n\n```\n378 tests · 90% line coverage · offline, no credentials, runs on every PR\n```\n\n- **Both directions of signature verification.** A verifier that rejected\n  everything would look just as healthy as a correct one, so a generated chain\n  carrying the two Apple OIDs the verifier insists on proves the *accept* path,\n  while forged, tampered, wrong-bundle and wrong-environment payloads are all\n  rejected.\n- **The protocol version is measured, not assumed.** Claude Code 2.1.235\n  negotiates MCP `2025-11-25` and declares `elicitation` — recorded by having it\n  connect to a probe server. That is why this stays on SDK v1: the v2 packages\n  implement 2026-07-28, which replaces elicitation with MRTR, and elicitation is\n  what makes `asc_write` ask a human.\n- **MSW, not nock,** for HTTP mocking — `nock` patches `node:http`, and Node's\n  global `fetch` is undici, which bypasses it entirely. It would have\n  intercepted nothing, silently.\n- **One test spawns the real binary** and asserts every stdout line parses as\n  JSON, because a single stray `console.log` corrupts the JSON-RPC channel and\n  no in-process harness can catch it.\n\n```bash\nnpm test              # 378 tests, offline\nnpm run verify        # 14 read-only checks against the live APIs\nnpm run fetch:specs   # re-download both API descriptions from Apple\n```\n\n---\n\n## Keeping current with Apple\n\nCoverage is a property of Apple's spec, not of how many endpoints somebody got\naround to wrapping.\n\n- **App Store Connect** — 1,263 operations, spec v4.4.1. Apple publishes an\n  OpenAPI document; it is downloaded and compiled into a slim index (360KB\n  against a 3.3MB spec).\n- **App Store Server** — 30 operations. Apple publishes **no** OpenAPI document\n  for this one, so the endpoint set is parsed out of Apple's own client library\n  at a pinned release tag, and diffed against this repo's catalogue on every\n  `verify` run.\n- **Enum tables are generated**, all 90 of them. A widely-copied cookbook\n  elsewhere lists an `eventState` value Apple does not have and omits two it\n  does; generating them makes that impossible here.\n- A weekly job re-fetches both and opens a branch if Apple moved.\n\n---\n\n## Transports\n\n```bash\nnode dist/index.js                       # stdio (default)\nnode dist/index.js --transport http --http-token \"$(openssl rand -hex 32)\"\n```\n\nHTTP binds to loopback and **refuses to start without a bearer token**. It\nvalidates `Host` and `Origin` too: a browser page can otherwise reach a\nloopback-bound server as same-origin, where a token alone is no defence.\n\nDocker builds distroless and runs as non-root. Note that stdio needs\n`docker run -i` and must **not** get `-t` — a TTY mangles the JSON-RPC stream.\n\n---\n\n## The skill\n\nThe server answers questions. It cannot stop a model drawing the wrong\nconclusion from a correct answer — an empty list because a territory was written\n`US` instead of `USA`, one page of thirty read as the whole account, \"no\nsubscriptions\" for an app that sells a one-time purchase. There is no error to\ncatch in any of those; the response was a 200.\n\nThat judgement ships as a Claude Code skill:\n\n```bash\nnpx -p @abd3lraouf/app-store-connect-mcp asc-mcp-skill\n```\n\nIt installs into `~/.claude/skills/app-store-connect` (`--scope project` for\n`./.claude/skills`, `--link` to symlink a checkout so edits are live). It adds\nnothing to `settings.json`.\n\nThe skill is a list of checks that fire *before* a conclusion, not a tour of the\nAPI. It hand-copies nothing that can be derived — no enum tables, no endpoint\nlists — and a test asserts that every tool name, resource URI, prompt and\nenum-shaped value in it still traces back to `src/` or to Apple's spec.\n\n### Failures are recorded\n\nToday a failure exists only as a tool result in a transcript. When the\nconversation ends, so does Apple's `x-request-id` — the one value Apple support\nasks for — along with any evidence of which operations a model tried and could\nnot find.\n\nBoth halves now write to `failures.jsonl`: the server records its own HTTP,\nnetwork and swallowed failures, and the skill records the ones the server cannot\nsee, the interpretation failures that arrived as a 200.\n\n```\nnpm run triage        # what keeps failing, and what the cookbook never warned about\n```\n\nWhere it writes, in order: `ASC_FAILURE_LOG_DIR` if set (and then nowhere else —\nan explicit destination that silently becomes a different destination is worse\nthan none); otherwise a **working checkout of this package**, identified by\ncarrying `src/` and `.git/`, which `npm pack` publishes neither of; otherwise the\ninstalled skill's own directory; otherwise `~/.claude`.\n\nOn a machine with no checkout and no installed skill, it writes **nothing**\nunless `ASC_FAILURE_LOG=1` says otherwise. `ASC_FAILURE_LOG=0` disables it\neverywhere.\n\nWhat it will not write: request headers or request bodies, ever — only body\n*keys*, because `appStoreReviewDetails` carries a live `demoAccountPassword` in\na plain body. Response payloads are reduced to Apple's error object. Pre-signed\nasset URLs are recorded as a hostname, since the signature is in the path. JWTs,\nbearer tokens, PEM blocks and email local-parts are scrubbed as a second line of\ndefence.\n\nRecords are appended as single bounded writes to an `O_APPEND` descriptor, so\nthe server and the skill can share one file without a lock. That guarantee is\nPOSIX's and does not hold on NFS — point `ASC_FAILURE_LOG_DIR` at local disk.\n\n---\n\n## Known limits\n\nStated plainly, because you will find them anyway:\n\n- **Risk tiers are pattern-matched** from method and path. Deliberately\n  cautious, but heuristics. Read `asc_describe_endpoint` before a write.\n- **Keychain storage is macOS-only.** Elsewhere, use a file path with\n  restrictive permissions.\n- **`--no-confirm` disables every gate** — token, elicitation and the\n  `requiresUserInteraction` flag. It exists for CI and unattended agents, and is\n  a poor default anywhere a person is present.\n- **`--no-online-checks` skips OCSP**, which means accepting a revoked\n  certificate.\n- **The failure log's lock-free append relies on `O_APPEND` atomicity**, which\n  network filesystems do not honour. `npm run triage` skips torn lines rather\n  than failing, but a network mount can lose records.\n- **The accept path for signatures is proven against a substituted trust\n  anchor**, not Apple's — getting a genuinely Apple-signed payload needs a real\n  customer transaction. The chain logic is Apple's own library.\n\n## Releasing\n\nBump, merge, done. Both registries authenticate with the workflow's own OIDC\nidentity, so there is no token to rotate and no tag to remember:\n\n```bash\nnpm run release:version patch     # or minor, major, or an exact version\ngit commit -am \"Release v1.0.1\" && git push\n```\n\nCI notices a version that is not yet on npm, runs the full gate, then publishes\nto npm and the MCP Registry, tags the commit and opens a GitHub Release. The\ntrigger is the version rather than a tag, which makes the job idempotent —\nre-running it publishes nothing.\n\n## Licence\n\n[Business Source License 1.1](LICENSE). Free for internal use, evaluation and\ndevelopment; offering it to third parties as a hosted or embedded service, or\nredistributing it inside a commercial product, needs a licence. Converts to\nApache-2.0 on 2030-08-19.\n\nApple's OpenAPI description and root certificate are vendored here under\nApple's terms, not this licence — see [NOTICE](NOTICE).\n\nApple, App Store, App Store Connect, StoreKit and TestFlight are trademarks of\nApple Inc. This project is not affiliated with Apple.\n","readmeFilename":"README.md"}