Imported from kostejnv/mapy-com-mcp (
AGENTS.md). Install upstream withnpx skills add kostejnv/mapy-com-mcp. Copyright stays with the author.
AGENTS.md
Vendor-neutral instructions for AI coding agents working in this repo. If
you're a Claude Code session, CLAUDE.md is a symlink to this file —
everything below applies.
Project at a glance
mapy-com-mcp is a Model Context Protocol (MCP) server that gives LLM clients
first-class access to mapy.com (Czech mapping service). Built in Python ≥ 3.12
with FastMCP. Distributed on PyPI and invoked via uvx mapy-com-mcp.
For the public pitch and quickstart, read README.md.
Repo layout
src/mapy_com_mcp/
__init__.py
server.py # FastMCP entry, main(), tool auto-registration
mapy_client.py # the only place that talks to mapy.com over HTTP
tools/ # one file per MCP tool
tests/ # pytest, mocked HTTP via respx
Conventions tied to this layout:
src/layout, not flat. Tests must import the installed package, not the working directory.- One file per tool in
tools/. Auto-registered inserver.py. mapy_client.pyis the only HTTP boundary. Tools never make rawhttpxcalls — auth, retries, and error mapping live in one place.tests/lives outsidesrc/. Coverage targetssrc/mapy_com_mcp.
Standards
- Lint + format:
ruff(config inpyproject.toml). - Type checking:
pyrightin strict mode. Type hints everywhere. - Pre-commit: ruff (lint+format) and basic hygiene hooks. Pyright runs in CI, not in pre-commit.
- Tests:
pytestwithrespxfor HTTP mocking. No real network in CI. - Conventional Commits. See "Git workflow" below. Enforced locally via a
commit-msgpre-commit hook. - CI / Release. GitHub Actions runs ruff, pyright, and pytest on Python
3.12 + 3.13 (
ci.yml). Releases are automated:release-pleaseopens a release PR from Conventional Commit history, and merging it triggersrelease.yml, which publishes to PyPI via Trusted Publishing (no API tokens). - No AI attribution in commits or PRs. No
Co-Authored-By: Claudetrailers, no "Generated with Claude Code" footers, no other AI metadata. This is a hard rule — see.context/decisions.md. - Dependencies kept minimal.
fastmcp,httpx,pydantic(transitive via fastmcp). Anything beyond these needs an entry in.context/decisions.md.
Working principles
How to make changes — separate from what tools to use.
- No over-engineering. The smallest thing that solves the task. No premature abstractions, no helper for a one-shot call site, no error handling for cases that can't happen, no design for hypothetical future requirements. Three similar lines beat a premature abstraction.
- Avoid special-case ladders. Don't solve every edge case by adding
another
if/else— that's how the code rots.if/elseis the right answer only when avoiding it would itself be over-engineering (e.g. a class hierarchy or strategy pattern for two cases). Otherwise push the special case into the data, the type, or the boundary so the main path stays straight. - No workarounds for blockers — fix the root cause. When something fails,
don't bypass it. No
--no-verify, no broad# type: ignore, noexcept: passto swallow exceptions, no commenting out a failing test, no hardcoding values to "make the test pass". Investigate the actual cause and fix that. - Pushback on tooling rules is welcome — talk to the user first. If a
ruff lint or pyright check seems wrong for this codebase, don't sprinkle
# noqa/# type: ignoreto silence it. Surface it: "this rule fires on X, here's why I think it's not useful here — should we disable it project-wide?" The output is a real decision recorded inpyproject.toml(and.context/decisions.mdif the rationale is non-obvious), not inline suppressions scattered through the code.
How to run things
uv sync # install deps + create .venv
uv run pre-commit install # install pre-commit (one-time)
uv run pre-commit install --hook-type commit-msg # commit-msg hook (one-time)
uv run mapy-com-mcp # run the server (stdio by default)
make test # run pytest
uv run pytest --cov=src/mapy_com_mcp # tests with coverage
make lint # pre-commit run --all-files
make inspect # MCP Inspector, local dev server
make inspect-remote # MCP Inspector, published PyPI build
The make inspect* targets shell out to npx @modelcontextprotocol/inspector,
so they require Node.js / npx in addition to the Python toolchain. Everything
else above only needs uv.
Git workflow
Explicit rules so agent-driven work doesn't pollute main or muddy history.
- Branching. Never commit directly to
main. Every change goes on a feature branch offmain. Naming:<type>/<kebab-description>where<type>is one offeat,fix,chore,docs,refactor,test,ci. Examples:feat/ping-tool,chore/scaffold-pyproject,docs/agents-and-subagents. - Commits. Conventional Commits:
<type>(<scope>)?: <subject>, imperative mood, lowercase subject, no trailing period, ≤72 chars on the subject line. Body wrapped at 72, used for the why, not the what. Examples:feat(tools): add ping tool,chore: scaffold pyproject and src layout. - No AI attribution in commit messages or PR descriptions. (See Standards above.)
- PRs. Target
main. Title in Conventional Commits format. Description has a short why, a bulleted summary of changes, and a test-plan checklist. Reference the related issue if any. - No force-push to
main. No--no-verify,--no-gpg-sign, or hook-skipping unless the user explicitly asks. Pre-commit failures get fixed, not bypassed. - One logical change per PR. If a branch grows two unrelated changes, split it.
- Agent commit policy. Agents only create commits when the user asks. Never auto-commit, never auto-push. PRs are opened only on explicit request.
Multi-agent workflow
This repo runs with two narrow Claude Code sub-agents alongside the main
agent. They are defined in .claude/agents/:
test-agent— the only agent allowed to edit files intests/or runpytest. Evaluates failures and decides whether they're real bugs (→ feedback to main agent) or stale tests (→ updates the test).review-agent— read-only. Independent reviewer that checks the main agent's work for shortcuts, inconsistencies, standards violations, docs alignment, and accidentally-committed secrets.
When the main agent finishes a unit of work that touches code
Before declaring "done", the main agent must:
-
Invoke
test-agent(Agent tool,subagent_type: test-agent) with a brief that includes (a) what changed and (b) testing hints — your suggestions for what's worth testing in the new code (happy path, edge cases, failure modes, the boundary most likely to break). test-agent takes these as suggestions, exercises its own judgment, runs the suite, adds tests for new features, evaluates failures, and updatestests/README.mdas part of its work. It returns either "green" or actionable feedback. -
Invoke
review-agent(subagent_type: review-agent) with the same brief. review-agent returns text feedback only. -
Review-agent's output is a checklist, not a gate. Each
must_fix/should_fixitem is a suggestion of something the main agent may have overlooked. The main agent reads each item, decides yes/no using its own judgment (anchored by the Working principles above — smallest viable thing, no over-engineering, root cause not bypass), and:- Yes → apply directly, regardless of whether the change is docstring-level or behavior-level. The diff is the audit trail.
- No → don't apply. Save for the end-of-turn rejection brief with a one-line reason.
There is no "uncertain" bucket. The main agent commits to yes or no per item. If a suggestion genuinely depends on context the main agent doesn't have (e.g. a product call only the user can make), classify it as no and flag it in the brief as an open question — never silently apply something the main agent isn't sure about.
Exception — policy violations. Three
must_fixcategories are not judgment calls and are auto-yes regardless of context: leaked secrets, AI attribution in commits/PRs, and tests modified by the main agent (when test-agent ownstests/). New hard-rule exceptions are added to this list explicitly — never inferred from ablockverdict alone. -
Re-invoke test-agent if any applied yes-item changed runtime behavior. Re-invoke review-agent only if a follow-up round of work (e.g. user-requested rejections that the main agent then applies) introduced substantive new code worth a second pass.
-
End-of-turn rejection brief. Report to the user only the items the main agent didn't apply, with one line of reasoning each. Applied items live in the diff — the user reads it if they want to audit. If the user reads the brief and disagrees with a rejection ("you should have applied that one"), the main agent applies it.
Items the main agent skipped or dismissed without surfacing them in this brief still count as silent dismissal and are not allowed.
Boundaries
- Only
test-agentedits files undertests/or runspytest. review-agentis read-only — it never edits files, never runspytest, never runs commands that mutate state.- The main agent does not modify tests directly, even to "make CI green". When a test fails, the main agent asks test-agent to evaluate it first.
Skip rule
Doc-only changes (README.md, .context/, AGENTS.md, CLAUDE.md) don't
require test-agent. review-agent is still invoked for consistency/standards
review on substantive doc changes, and its feedback is still discussed with
the user.