agentleFS
r3dz4rr3dz4r/datapulse-my/mcp/AGENTS.md

datapulse-my / mcp

AGENTS.md3 starsChanged 3 months agoMITView on GitHub

Working agreement for AI agents editing or extending the read-only FastMCP server in this directory. server.py exposes a read-only FastMCP server over the published DataPulse MY manifest (datapulse.json) and health snapshot (health/latest.json). 19 tools (canonical count in mcp.json, generated from server.py AST — never hardcode in docs). No writes to the DataPulse MY data layer. The M8ven tool trust score (currently D, 41/100) audits this server. Tool annotation completeness, test coverage, and handler isolation are tracked findings — changes here…

  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository6 other files it gives its agents.

The file

We split this AGENTS.md into the rules it gives an agent

9 rules. About 1 is common, 2 are distinctive, and 6 are very specific to this project.

  • ≈1

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    Read-only by design: No new tool may write, mutate, or delete upstream state.

    line 13
  2. Very specific:

    Tool annotations are mandatory: Every new tool (and every existing tool touched) MUST declare readOnlyHint, destructiveHint, …

    line 14
  3. Very specific:

    Handler isolation via FastMCP decorators: Use @mcp.tool(name=..., description=..., annotations={...}) — do NOT use the lower-level …

    line 15
  4. Very specific:

    Pydantic or Annotated input schemas: All tool inputs must declare a typed schema.

    line 16
  5. Very specific:

    Tests live in mcp/tests/: Every new tool needs at least one integration test using FastMCP's in-memory client.

    line 17
  6. Common:

    Secrets never flow to network: Do not add code that reads env vars and exfiltrates them, even for "debug logging." M8ven's secret-flow …

    line 18
  7. Very specific:

    Return type: always a dict (FastMCP serialises).

    line 26
  8. Very specific:

    When writing a codex brief that touches mcp/server.py:

    line 79
  9. Distinctive:

    What test is added?: test name + fixture

    line 83

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

AGENTS.md — mcp/ (DataPulse MY MCP server)

Working agreement for AI agents editing or extending the read-only FastMCP server in this directory.

What this is

server.py exposes a read-only FastMCP server over the published DataPulse MY manifest (datapulse.json) and health snapshot (health/latest.json). 19 tools (canonical count in mcp.json, generated from server.py AST — never hardcode in docs). No writes to the DataPulse MY data layer.

The M8ven tool trust score (currently D, 41/100) audits this server. Tool annotation completeness, test coverage, and handler isolation are tracked findings — changes here directly affect that score.

Hard rules

  1. Read-only by design. No new tool may write, mutate, or delete upstream state. If a request requires writing, return an error explaining the read-only contract.
  2. Tool annotations are mandatory. Every new tool (and every existing tool touched) MUST declare readOnlyHint, destructiveHint, idempotentHint, openWorldHint. OpenAI's directory rejects tools missing any of the four hints; M8ven scoring deducts heavily for the same.
  3. Handler isolation via FastMCP decorators. Use @mcp.tool(name=..., description=..., annotations={...}) — do NOT use the lower-level setRequestHandler pattern. The lower-level pattern skips handler-content checks and reduces M8ven's handler-isolation coverage.
  4. Pydantic or Annotated input schemas. All tool inputs must declare a typed schema. dict[str, Any] inputs are forbidden — they fail M8ven's "Tool inputs are validated" check.
  5. Tests live in mcp/tests/. Every new tool needs at least one integration test using FastMCP's in-memory client. Run uv run --with fastmcp,httpx pytest mcp/tests/ -v before pushing.
  6. Secrets never flow to network. Do not add code that reads env vars and exfiltrates them, even for "debug logging." M8ven's secret-flow analysis is positive for this server today; preserve it.
  7. Commit and push atomically — never leave an unpushed commit on main. scripts/datapulse-pipeline.sh (assert_managed_commits, ~line 121) walks every commit above origin/main and fails the run unless each one has a pipeline-managed subject and touches only artifact paths (health/*, badges/*, docs/*, deltas/*, data/passports/*, catalog-*.json, attestations/latest/scores.json, and a few more). mcp/* is not on that allowlist, so a code commit left unpushed here fails the health pipeline on its next run (~10 minute cadence) with control-plane degraded: local-only commit is not pipeline-managed. Commit and push as one operation. If the push fails, revert the commit (git reset --soft HEAD~1) rather than leaving it local — an uncommitted working tree is safe, an unpushed commit is not.

    A push may cost one deferred publish — it is timing-dependent, not guaranteed. Health cycles fire every ~5 minutes and commonly take ~8 or more (they probe all 447 datasets), and the deploy stage runs after the publish stage (run_mcp_sync at ~line 737, push_head at ~713/732). If a push moves origin/main inside a cycle's probe-to-publish window, that cycle logs publish deferred: origin/main moved during probe, exits 1, and never reaches the deploy, so the change waits one more cycle. Measured on three pushes: two deferred one cycle each, the third landed outside the window and cost nothing. It is self-healing either way — the pipeline re-probes on its next cycle, health publishing does not regress, and nothing is lost. Do not wait for an idle gap: an 11-minute hand-checked wait found the pipeline busy throughout, so blocking on a gap can stall indefinitely. Commit and push together, and accept a possible one-cycle delay; what is mandatory is never leaving an unpushed commit, because that is the thing that actually breaks the pipeline.

Style conventions

  • Tool registration: @mcp.tool(name="search_datasets", description="...", annotations=ToolAnnotations(readOnlyHint=True, ...))
  • Return type: always a dict (FastMCP serialises). Use explicit keys, not naked lists.
  • Errors: raise ToolError or return a structured error dict. Do not return string error messages to clients.
  • Logging: stdlib logging with module-level logger. No print statements.
  • Imports: absolute imports preferred (matches Honcho convention). from mcp.server import ..., not from .server import ....
  • Type hints: required on all public functions. mypy --strict clean for new code.

What is NOT in this directory

  • Public deployment config — docs/mcp-deploy.md, ~/datapulse-my/.github/workflows/mcp-*.yml
  • Tool catalogue & schemas — mcp.json at repo root (generated, do not hand-edit)
  • Telemetry sinks — record-evidence/ and the per-caller JSONL sinks at repo root
  • Deployment attestations — .attestations/chain_head.json (generated by gen_attestation_chain.py in scripts/)

Out of scope

  • Anything that writes to data.gov.my, BNM, DOSM, or any upstream — that's outside our threat model
  • New tools that require per-user authentication or rate-limit keys — the public endpoint is intentionally no-auth; per-user enforcement belongs to the caller's own deployment, not this public server
  • New dependencies beyond FastMCP, httpx, pydantic — keep the dep tree small; M8ven scores CVE surface

Verification before push

# 1. Run the test suite
uv run --isolated --prerelease=allow --with-requirements mcp/requirements.txt --with pytest python -m pytest mcp/tests/ -v

# 2. Re-render the discovery artefacts (writes BOTH /mcp.json and /agent.json at repo root)
#    Without injection, the generator uses the same newest-mcp-revision derivation
#    as scripts/verify_mcp_deployment.py, derived only from mcp/server.py and
#    its two imported scripts/gen_per_dataset_receipt.py and
#    scripts/verify_per_dataset_receipt.py. Tests and documentation do not count.
#    Deployment may inject a source identity
#    explicitly; that injection wins. The literals in mcp/server.py are only runtime
#    fallbacks and are not a generation authority.
python3 scripts/gen_mcp_reference.py
# Expected diffs on regeneration:
#   mcp.json   - unicode escaping only (the committed file escapes em-dashes as \u2014,
#                the generator emits them literally). Any other change is content.
#   agent.json - its commit_sha must equal the deployed MCP code revision. A single-line
#                change here is real provenance movement, not formatting.
# Revert any artefact you did not intend to change rather than leaving it dirty.

# 3. Confirm the live tool count matches mcp.json (or update intentionally)
jq '.tools | length' mcp.json

# 4. Check annotations are present on every tool
jq '.tools[] | select(.annotations == null) | .name' mcp.json
# should return empty

Then commit and push in the same operation (see hard rule 7). A commit that sits above origin/main fails the next health-pipeline run, because assert_managed_commits rejects any commit whose paths are outside the artifact allowlist.

Pre-flight checklist (for dispatch briefs)

When writing a codex brief that touches mcp/server.py:

  • What tool(s) change? name + line range
  • What annotations are added? copy the 4-hint block into the brief
  • What test is added? test name + fixture
  • Why is this read-only? brief must explicitly state why no write happens
  • Workdir: absolute path to this repo (most common cross-repo bug — see dotfiles AGENTS.md rule 7)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.