Skip to content

feat(mcp): expose ASO analyzers as an MCP server - #16

Merged
alirezarezvani merged 1 commit into
devfrom
claude/mcp-server
May 19, 2026
Merged

alirezarezvani merged 1 commit into
devfrom
claude/mcp-server

Conversation

@alirezarezvani

Copy link
Copy Markdown
Owner

Summary

Expose the ASO analyzers as an MCP server so non-Claude-Code clients (Claude Desktop, Cursor, Continue, custom agents) can use the skill without depending on the Claude Code CLI. This finishes Theme C — the "reach beyond Claude Code" theme is now complete (plugin marketplace #14 + ASO Watcher #15 + MCP server #16).

What changed

  • aso_skill/mcp_server.py — FastMCP-based server (high-level decorator API in the MCP Python SDK). Seven tools, all thin wrappers around existing aso_skill convenience functions:

    Tool Wraps
    aso_score calculate_aso_score
    aso_optimize optimize_app_metadata
    aso_validate MetadataOptimizer.validate_character_limits
    aso_analyze_keywords analyze_keyword_set
    aso_plan_ab_test plan_ab_test
    aso_itunes_search iTunesAPI.search_apps (cached)
    aso_itunes_app iTunesAPI.get_app_by_id (cached)

    JSON schemas + tool descriptions are auto-generated from Python type hints and docstrings — no hand-rolled schemas to drift out of sync.

  • pyproject.toml — new [project.optional-dependencies] mcp = ["mcp>=1.0"] and new console script aso-mcp = "aso_skill.mcp_server:main". Core install stays zero-dependency.

  • documentation/mcp/INSTALL.md — install + per-client setup. Concrete Claude Desktop claude_desktop_config.json snippet, troubleshooting section, table of all seven tools.

  • tests/test_mcp_server.py — 12 tests. Each tool handler called directly (no event loop, no Client, no subprocess); one smoke test confirms all seven tools register on the FastMCP server. Uses pytest.importorskip("mcp") so the suite still passes for users on stdlib-only installs.

  • CHANGELOG.md — [1.1.4] - Unreleased entry.

How to install

pip install 'aso-skill[mcp]'

Then add to Claude Desktop's claude_desktop_config.json:

{
  "mcpServers": {
    "aso-skill": {
      "command": "aso-mcp"
    }
  }
}

Restart Claude Desktop — the seven tools appear in the picker.

Test plan

  • python3 -m pytest tests/ -q → 84 passed in 3.08s (72 prior + 12 new MCP tests)
  • ruff check aso_skill scripts tests → All checks passed
  • ruff format --check aso_skill scripts tests → 32 files already formatted
  • pip install -e ".[mcp]" installs cleanly
  • which aso-mcp → console script on PATH after install
  • aso-mcp boots and waits on stdio (server starts without error)
  • All seven tools register on the FastMCP server (verified by test_all_tools_registered_on_server)
  • Post-merge: live test against a real Claude Desktop install

Risks

  • MCP SDK version churn — the SDK API has shifted in past minor versions. Pin floor at mcp>=1.0 (current stable line). If a breaking change lands, the optional-dep gate means only [mcp] installs break, not core.
  • Python 3.10+ for the SDK — mcp itself may require Python ≥ 3.10. On Python 3.9, pip install '.[mcp]' will refuse to resolve. Core aso-skill install on 3.9 still works (this PR doesn't change requires-python).
  • Schema verbosity — dict[str, Any] annotations on tools like aso_score produce loose JSON schemas. Acceptable trade-off for not maintaining Pydantic mirrors of the existing dict shapes; LLM callers get the structure from the rich docstring.

https://claude.ai/code/session_01CbA3Mp4rD4vW9Yjd2aPEXw


Generated by Claude Code

New optional MCP server lets non-Claude-Code clients (Claude Desktop,
Cursor, Continue, custom agents) call into the skill's analyzers
over MCP / stdio.

Seven tools, all thin wrappers around existing aso_skill convenience
functions:
- aso_score
- aso_optimize
- aso_validate
- aso_analyze_keywords
- aso_plan_ab_test
- aso_itunes_search   (uses the existing 24h iTunes cache)
- aso_itunes_app

Built on FastMCP (the high-level decorator API in the MCP Python SDK),
so JSON schemas and tool descriptions are auto-generated from Python
type hints + docstrings -- no hand-rolled schemas to drift.

Install path:

    pip install 'aso-skill[mcp]'

New console script `aso-mcp` runs the server over stdio. Claude
Desktop config example and per-client setup notes live at
documentation/mcp/INSTALL.md.

Tests: 12 new tests exercise each tool handler directly plus a smoke
check that all seven tools register on the FastMCP server. Uses
`pytest.importorskip` so the suite still passes on stdlib-only
installs without the optional `mcp` dependency.

Core install stays zero-dependency -- mcp is pulled in only via the
new [mcp] extra.

Full suite: 84 passed in 3.08s.

https://claude.ai/code/session_01CbA3Mp4rD4vW9Yjd2aPEXw
@github-actions github-actions Bot added the size: M Medium PR (200-499 changes) label May 19, 2026
@claude

claude Bot commented May 19, 2026

Copy link
Copy Markdown

Code Review — PR #16: MCP server for ASO analyzers

Overall: Clean, well-scoped addition. The optional-dependency gate, pytest.importorskip, and the thin-wrapper design are all right calls. Three issues need attention before this leaves DRAFT.


Blockers

1. iTunesAPI per-call instantiation likely breaks the 24-hour cache

# mcp_server.py:149, 160
api = iTunesAPI(country=country)
return api.search_apps(term, limit=limit)

If iTunesAPI's cache is held on the instance (a dict attribute), every MCP tool call creates a fresh instance with an empty cache. The docstring says "repeated calls within 24h hit a local cache" — that claim is only true if the cache is class-level or file-backed. Make the instance module-level or use functools.lru_cache / a file-based cache, and document which it is.

2. No input validation on platform — errors will be opaque to MCP callers

def aso_optimize(platform: str, ...) -> Dict[str, Any]:
def aso_validate(platform: str, ...) -> Dict[str, Any]:

If a caller passes "android" or "ios", the raw exception from MetadataOptimizer.__init__ propagates. MCP clients surface this as an unhandled tool error. Add an explicit guard:

if platform not in {"apple", "google"}:
    return {"error": f"platform must be 'apple' or 'google', got {platform!r}"}

Same applies to the field parameter in aso_validate (unknown field → silent empty dict or crash).


ASO-specific

3. aso_validate Apple keywords limit is 100 chars total (comma-separated, no spaces)

The CLAUDE.md constraint is unusual — the 100-char keyword field must be comma-separated with no spaces. Confirm MetadataOptimizer.validate_character_limits enforces the no-spaces rule, not just the char count, when field is "keywords". The MCP docstring should mention this constraint explicitly so callers don't construct space-padded keyword strings.


Code quality

4. Dict/List from typing — unnecessary with from __future__ import annotations

The file already has from __future__ import annotations on line 24. Use lowercase dict and list directly (PEP 585):

# before
def aso_score(metadata: Dict[str, Any], ...) -> Dict[str, Any]:
# after
def aso_score(metadata: dict[str, Any], ...) -> dict[str, Any]:

Tests

5. test_aso_plan_ab_test_returns_design — weak assertion

assert "test_design" in result or "sample_size_requirements" in result

An or here means the test passes even if half the expected output is missing. Assert both keys, or at least assert the key the function is documented to return.

6. test_all_tools_registered_on_server — import asyncio buried inside the test

Move import asyncio to the top of the file with the other imports. It reads as an afterthought and ruff will flag it (E402 if it were after pytest.importorskip, though it's inside a function here).

7. Missing boundary test for Apple title exactly at 30 chars

test_aso_validate_valid_apple_title only tests "TaskFlow" (8 chars). Add a test for a 30-char string (should pass) and a 31-char string (should fail) to pin the boundary behaviour.


Documentation

8. CHANGELOG section header doesn't follow Keep a Changelog

### MCP server   ← non-standard

Keep a Changelog uses ### Added, ### Changed, ### Fixed. This should be:

### Added
- `aso_skill.mcp_server` — MCP server exposing seven tools …

9. Troubleshooting references aso itunes --clear-cache

clear it via `aso itunes --clear-cache`

Verify this subcommand exists in the CLI. If not, either add it or remove the reference.


Summary

Blockers 2 (caching correctness, input validation)
ASO-specific 1 (keyword no-spaces constraint)
Code quality 1 (typing imports)
Tests 3 (weak assertion, import style, missing boundary)
Docs 2 (CHANGELOG format, unverified CLI flag)

Happy to look again after the caching and validation items are addressed.

@alirezarezvani
alirezarezvani marked this pull request as ready for review May 19, 2026 04:38
@alirezarezvani
alirezarezvani merged commit fb26543 into dev May 19, 2026
13 checks passed
@github-actions

Copy link
Copy Markdown

Branch claude/mcp-server was automatically deleted after merge.

@github-actions
github-actions Bot deleted the claude/mcp-server branch May 19, 2026 04:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: M Medium PR (200-499 changes)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants