asic-mcp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. asic_mcp-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
  2. asic_mcp-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
  3. asic_mcp-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
  4. asic_mcp-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +26 -0
  5. asic_mcp-0.1.0/.github/dependabot.yml +39 -0
  6. asic_mcp-0.1.0/.github/workflows/codeql.yml +40 -0
  7. asic_mcp-0.1.0/.github/workflows/release.yml +57 -0
  8. asic_mcp-0.1.0/.github/workflows/test.yml +60 -0
  9. asic_mcp-0.1.0/.gitignore +18 -0
  10. asic_mcp-0.1.0/CHANGELOG.md +79 -0
  11. asic_mcp-0.1.0/CODE_OF_CONDUCT.md +33 -0
  12. asic_mcp-0.1.0/CONTRIBUTING.md +86 -0
  13. asic_mcp-0.1.0/LICENSE +21 -0
  14. asic_mcp-0.1.0/PKG-INFO +197 -0
  15. asic_mcp-0.1.0/README.md +162 -0
  16. asic_mcp-0.1.0/RESEARCH.md +363 -0
  17. asic_mcp-0.1.0/SECURITY.md +41 -0
  18. asic_mcp-0.1.0/examples/claude_desktop_config_all_three.json +33 -0
  19. asic_mcp-0.1.0/examples/claude_desktop_config_local.json +14 -0
  20. asic_mcp-0.1.0/examples/demo_prompts.md +104 -0
  21. asic_mcp-0.1.0/glama.json +4 -0
  22. asic_mcp-0.1.0/llms.txt +90 -0
  23. asic_mcp-0.1.0/pyproject.toml +77 -0
  24. asic_mcp-0.1.0/src/asic_mcp/__init__.py +8 -0
  25. asic_mcp-0.1.0/src/asic_mcp/cache.py +134 -0
  26. asic_mcp-0.1.0/src/asic_mcp/catalog.py +57 -0
  27. asic_mcp-0.1.0/src/asic_mcp/client.py +124 -0
  28. asic_mcp-0.1.0/src/asic_mcp/curated.py +317 -0
  29. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_AFS_AUTH_REP.yaml +131 -0
  30. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_AFS_LICENSEE.yaml +120 -0
  31. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_BANNED_ORGS.yaml +73 -0
  32. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_BANNED_PERSONS.yaml +109 -0
  33. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_CREDIT_LICENSEE.yaml +138 -0
  34. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_FINANCIAL_ADVISERS.yaml +159 -0
  35. asic_mcp-0.1.0/src/asic_mcp/data/curated/ASIC_LIQUIDATOR.yaml +128 -0
  36. asic_mcp-0.1.0/src/asic_mcp/discovery.py +241 -0
  37. asic_mcp-0.1.0/src/asic_mcp/models.py +105 -0
  38. asic_mcp-0.1.0/src/asic_mcp/parsing.py +166 -0
  39. asic_mcp-0.1.0/src/asic_mcp/py.typed +0 -0
  40. asic_mcp-0.1.0/src/asic_mcp/server.py +564 -0
  41. asic_mcp-0.1.0/src/asic_mcp/shaping.py +542 -0
  42. asic_mcp-0.1.0/tests/__init__.py +0 -0
  43. asic_mcp-0.1.0/tests/conftest.py +71 -0
  44. asic_mcp-0.1.0/tests/fixtures/asic_afs_auth_rep.csv +51 -0
  45. asic_mcp-0.1.0/tests/fixtures/asic_afs_licensee.csv +51 -0
  46. asic_mcp-0.1.0/tests/fixtures/asic_banned_orgs.csv +16 -0
  47. asic_mcp-0.1.0/tests/fixtures/asic_banned_persons.csv +51 -0
  48. asic_mcp-0.1.0/tests/fixtures/asic_credit_licensee.csv +51 -0
  49. asic_mcp-0.1.0/tests/fixtures/asic_financial_advisers.csv +51 -0
  50. asic_mcp-0.1.0/tests/fixtures/asic_liquidator.csv +51 -0
  51. asic_mcp-0.1.0/tests/test_cache.py +162 -0
  52. asic_mcp-0.1.0/tests/test_concurrency.py +112 -0
  53. asic_mcp-0.1.0/tests/test_curated.py +222 -0
  54. asic_mcp-0.1.0/tests/test_dataset_specific.py +253 -0
  55. asic_mcp-0.1.0/tests/test_df_cache.py +192 -0
  56. asic_mcp-0.1.0/tests/test_discovery.py +298 -0
  57. asic_mcp-0.1.0/tests/test_edge_inputs.py +316 -0
  58. asic_mcp-0.1.0/tests/test_live.py +125 -0
  59. asic_mcp-0.1.0/tests/test_parsing_asic.py +138 -0
  60. asic_mcp-0.1.0/tests/test_register_shape.py +234 -0
  61. asic_mcp-0.1.0/tests/test_resilience.py +147 -0
  62. asic_mcp-0.1.0/tests/test_server_validation.py +107 -0
  63. asic_mcp-0.1.0/uv.lock +1775 -0
@@ -0,0 +1,72 @@
1
+ name: Bug report
2
+ description: Something works wrong, returns wrong data, or crashes.
3
+ title: "[bug] "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for filing! Please give us enough to reproduce. The fastest path to a fix is a minimal reproducer + the version you're running.
10
+
11
+ - type: input
12
+ id: version
13
+ attributes:
14
+ label: asic-mcp version
15
+ description: Run `python -c "import asic_mcp; print(asic_mcp.__version__)"`
16
+ placeholder: 0.1.1
17
+ validations:
18
+ required: true
19
+
20
+ - type: dropdown
21
+ id: client
22
+ attributes:
23
+ label: MCP client
24
+ options:
25
+ - Claude Desktop
26
+ - Cursor
27
+ - Windsurf
28
+ - Cline
29
+ - Direct Python (no MCP client)
30
+ - Other
31
+ validations:
32
+ required: true
33
+
34
+ - type: textarea
35
+ id: reproducer
36
+ attributes:
37
+ label: Reproducer
38
+ description: |
39
+ The exact tool call(s) that trigger the bug. Either the MCP request payload or the equivalent Python:
40
+ ```python
41
+ from asic_mcp import server
42
+ await server.latest("F1.1", series="cash_rate_target")
43
+ ```
44
+ render: python
45
+ validations:
46
+ required: true
47
+
48
+ - type: textarea
49
+ id: actual
50
+ attributes:
51
+ label: What actually happened
52
+ description: Error message, wrong value, surprising response shape, etc.
53
+ validations:
54
+ required: true
55
+
56
+ - type: textarea
57
+ id: expected
58
+ attributes:
59
+ label: What you expected
60
+ validations:
61
+ required: true
62
+
63
+ - type: textarea
64
+ id: env
65
+ attributes:
66
+ label: Environment
67
+ description: |
68
+ - Python version (`python --version`)
69
+ - OS (macOS / Linux / Windows + version)
70
+ - How you installed (`uvx asic-mcp`, `uv pip install -e .`, etc.)
71
+ placeholder: |
72
+ Python 3.12.3, macOS 15, installed via uvx
@@ -0,0 +1,11 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Question or how-to
4
+ url: https://github.com/Bigred97/asic-mcp/discussions/new?category=q-a
5
+ about: For "how do I" or "is it possible to" — start a Discussion instead of an issue.
6
+ - name: Show & tell — share what you built
7
+ url: https://github.com/Bigred97/asic-mcp/discussions/new?category=show-and-tell
8
+ about: Built something cool with asic-mcp? We'd love to see it.
9
+ - name: Security vulnerability
10
+ url: https://github.com/Bigred97/asic-mcp/security/advisories/new
11
+ about: Use the private security advisory flow — do NOT open a public issue.
@@ -0,0 +1,46 @@
1
+ name: Feature request
2
+ description: Suggest a new tool, F-table, or behaviour.
3
+ title: "[feat] "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for the suggestion. Higher chance of merge if you frame it around a real use case ("I want to ask Claude X and currently I have to do Y").
10
+
11
+ - type: textarea
12
+ id: use_case
13
+ attributes:
14
+ label: What are you trying to do?
15
+ placeholder: I want to ask Claude about ATO government bond yields and currently asic-mcp doesn't curate F2.
16
+ validations:
17
+ required: true
18
+
19
+ - type: textarea
20
+ id: proposal
21
+ attributes:
22
+ label: Proposed change
23
+ description: |
24
+ Concrete: which file would change, what would the new tool/curated YAML look like, what does the user-visible API become?
25
+ validations:
26
+ required: true
27
+
28
+ - type: textarea
29
+ id: alternatives
30
+ attributes:
31
+ label: Alternatives considered
32
+ description: e.g. "I could just pass raw ATO series IDs to get_data, but..."
33
+
34
+ - type: dropdown
35
+ id: scope
36
+ attributes:
37
+ label: Scope
38
+ options:
39
+ - Add a curated F-table YAML
40
+ - New MCP tool
41
+ - Change to existing tool's signature
42
+ - Change to default behaviour
43
+ - Documentation
44
+ - Other
45
+ validations:
46
+ required: true
@@ -0,0 +1,26 @@
1
+ ## What & why
2
+
3
+ <!-- One paragraph: what does this PR do, and what's the motivating use case or bug? -->
4
+
5
+ ## How
6
+
7
+ <!-- Brief: which files changed and why? -->
8
+
9
+ ## Tests
10
+
11
+ - [ ] All existing tests still pass (`uv run pytest -m "not live"`)
12
+ - [ ] New behaviour has a test
13
+ - [ ] If this touches the live-API surface, `uv run pytest -m live` is green
14
+
15
+ ## Compatibility
16
+
17
+ - [ ] No breaking changes to the public MCP tool signatures
18
+ - [ ] No breaking changes to the `DataResponse` / `TableDetail` JSON shape
19
+ - [ ] CC-BY 4.0 attribution still surfaces in `DataResponse.attribution`
20
+ - [ ] If any of the above is broken, this is justified in the PR body
21
+
22
+ ## Other
23
+
24
+ - [ ] CHANGELOG.md updated
25
+ - [ ] README updated if user-visible behaviour changed
26
+ - [ ] No new dependencies (or new dep is justified above)
@@ -0,0 +1,39 @@
1
+ version: 2
2
+
3
+ updates:
4
+ # Python dependencies (pyproject.toml + uv.lock)
5
+ - package-ecosystem: pip
6
+ directory: "/"
7
+ schedule:
8
+ interval: weekly
9
+ day: monday
10
+ time: "10:00"
11
+ timezone: Australia/Sydney
12
+ open-pull-requests-limit: 5
13
+ groups:
14
+ # Group all minor + patch updates into one PR per week.
15
+ python-runtime-minor-patch:
16
+ applies-to: version-updates
17
+ update-types: [minor, patch]
18
+ labels: [dependencies]
19
+ commit-message:
20
+ prefix: deps
21
+ include: scope
22
+
23
+ # GitHub Actions used in workflows
24
+ - package-ecosystem: github-actions
25
+ directory: "/"
26
+ schedule:
27
+ interval: weekly
28
+ day: monday
29
+ time: "10:00"
30
+ timezone: Australia/Sydney
31
+ open-pull-requests-limit: 3
32
+ groups:
33
+ gha-minor-patch:
34
+ applies-to: version-updates
35
+ update-types: [minor, patch]
36
+ labels: [dependencies, github-actions]
37
+ commit-message:
38
+ prefix: ci
39
+ include: scope
@@ -0,0 +1,40 @@
1
+ name: codeql
2
+
3
+ # GitHub's free SAST scanning. Runs on push to main, on PRs, and weekly.
4
+ # Findings appear under the repo's Security tab.
5
+
6
+ on:
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+ branches: [main]
11
+ schedule:
12
+ - cron: "47 6 * * 1" # Mondays 06:47 UTC — off-peak, offset 10min from rba-mcp
13
+
14
+ jobs:
15
+ analyze:
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ actions: read
19
+ contents: read
20
+ security-events: write
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ language: [python]
25
+ steps:
26
+ - uses: actions/checkout@v6
27
+
28
+ - name: Initialise CodeQL
29
+ uses: github/codeql-action/init@v4
30
+ with:
31
+ languages: ${{ matrix.language }}
32
+ queries: security-extended
33
+
34
+ - name: Autobuild
35
+ uses: github/codeql-action/autobuild@v4
36
+
37
+ - name: Analyze
38
+ uses: github/codeql-action/analyze@v4
39
+ with:
40
+ category: "/language:${{ matrix.language }}"
@@ -0,0 +1,57 @@
1
+ name: release
2
+
3
+ # Push a tag like v0.3.0 → wheel is built and published to PyPI via
4
+ # Trusted Publishing (OIDC, no API token in repo secrets). One-time setup:
5
+ # 1. https://pypi.org/manage/account/publishing/
6
+ # 2. Add a "pending publisher":
7
+ # PyPI project = asic-mcp
8
+ # owner = Bigred97
9
+ # repository = asic-mcp
10
+ # workflow = release.yml
11
+ # environment = pypi
12
+ # 3. Create a `pypi` environment in repo Settings → Environments
13
+ # (no secrets needed; the environment scopes the OIDC token).
14
+ # Optionally add a required-reviewers rule so a tag push waits for
15
+ # your manual approval before publishing.
16
+ # Then any `git push origin vX.Y.Z` triggers this job.
17
+
18
+ on:
19
+ push:
20
+ tags:
21
+ - "v*"
22
+
23
+ jobs:
24
+ release:
25
+ runs-on: ubuntu-latest
26
+ environment:
27
+ name: pypi
28
+ url: https://pypi.org/project/asic-mcp/
29
+ permissions:
30
+ id-token: write # required for Trusted Publishing OIDC
31
+ contents: read
32
+ steps:
33
+ - uses: actions/checkout@v6
34
+
35
+ - name: Install uv
36
+ uses: astral-sh/setup-uv@v7
37
+
38
+ - name: Sanity-check the tag matches pyproject.toml
39
+ run: |
40
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
41
+ PYPROJECT_VERSION=$(uv run python -c "import tomllib,sys; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
42
+ if [ "$TAG_VERSION" != "$PYPROJECT_VERSION" ]; then
43
+ echo "::error::Tag $GITHUB_REF_NAME does not match pyproject version $PYPROJECT_VERSION"
44
+ exit 1
45
+ fi
46
+ echo "Releasing version $PYPROJECT_VERSION"
47
+
48
+ - name: Build wheel + sdist
49
+ run: uv build
50
+
51
+ - name: Publish to PyPI (Trusted Publishing)
52
+ uses: pypa/gh-action-pypi-publish@release/v1
53
+
54
+ - uses: actions/upload-artifact@v4
55
+ with:
56
+ name: dist
57
+ path: dist/
@@ -0,0 +1,60 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v6
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v7
16
+ - name: Run ruff check
17
+ # `uvx` runs ruff in an isolated managed env; avoids PEP 668
18
+ # "externally-managed environment" failure on Ubuntu 23.04+ runners
19
+ # that the previous `uv pip install --system` hit.
20
+ run: uvx "ruff>=0.5" check src/ tests/
21
+
22
+ test:
23
+ runs-on: ubuntu-latest
24
+ needs: lint
25
+ strategy:
26
+ fail-fast: false
27
+ matrix:
28
+ python-version: ["3.11", "3.12", "3.13"]
29
+ steps:
30
+ - uses: actions/checkout@v6
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@v7
33
+ with:
34
+ enable-cache: true
35
+ - name: Set up Python ${{ matrix.python-version }}
36
+ run: uv python install ${{ matrix.python-version }}
37
+ - name: Sync dependencies
38
+ run: uv sync --extra dev
39
+ - name: Install package
40
+ run: uv pip install -e .
41
+ - name: Run unit tests
42
+ run: uv run pytest -q
43
+
44
+ build:
45
+ runs-on: ubuntu-latest
46
+ needs: test
47
+ steps:
48
+ - uses: actions/checkout@v6
49
+ - name: Install uv
50
+ uses: astral-sh/setup-uv@v7
51
+ - name: Build wheel + sdist
52
+ run: uv build
53
+ - name: Verify wheel installs cleanly
54
+ run: |
55
+ uv run --isolated --with ./dist/*.whl python -c \
56
+ "import asic_mcp.server as s; n = len(s.list_curated()); assert n >= 8, f'expected >=8 curated, got {n}'; print(f'OK ({n} curated datasets)')"
57
+ - uses: actions/upload-artifact@v4
58
+ with:
59
+ name: dist
60
+ path: dist/
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .venv/
6
+ venv/
7
+ dist/
8
+ build/
9
+ .coverage
10
+ .coverage.*
11
+ htmlcov/
12
+ .DS_Store
13
+ *.swp
14
+ *.swo
15
+ .idea/
16
+ .vscode/
17
+ .asic-mcp-cache/
18
+ .asic-mcp/
@@ -0,0 +1,79 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-05-13
9
+
10
+ Initial release. ASIC registers via data.gov.au, plain-English access.
11
+
12
+ ### Added
13
+
14
+ - **Seven curated ASIC register datasets**, all served from CKAN-discovered
15
+ resource URLs on data.gov.au under CC BY 3.0 AU:
16
+ - `ASIC_FINANCIAL_ADVISERS` — Financial Advisers Register (~21,000 records, weekly).
17
+ - `ASIC_AFS_LICENSEE` — Australian Financial Services Licensees (~6,500 entities, weekly).
18
+ - `ASIC_AFS_AUTH_REP` — AFS Authorised Representatives (~50 MB CSV, weekly).
19
+ - `ASIC_CREDIT_LICENSEE` — Australian Credit Licensees (NCCP-regulated lenders/brokers, weekly).
20
+ - `ASIC_BANNED_PERSONS` — Banned and Disqualified Persons (weekly).
21
+ - `ASIC_BANNED_ORGS` — Banned and Disqualified Organisations (weekly).
22
+ - `ASIC_LIQUIDATOR` — Registered & Official Liquidators (~700 practitioners, weekly).
23
+ - **Five MCP tools** matching the abs-mcp / rba-mcp / ato-mcp / apra-mcp envelope:
24
+ `search_datasets`, `describe_dataset`, `get_data`, `latest`, `list_curated`.
25
+ - **Trust contract on every `DataResponse`**: `source`, `source_url`,
26
+ `attribution`, `retrieved_at`, `server_version`, `stale`. Attribution is
27
+ the exact CC BY 3.0 AU statement from data.gov.au, naming ASIC as the
28
+ source and pointing at the canonical CC licence URL.
29
+ - **CKAN auto-discovery** — each YAML declares a `discovery:` block
30
+ (`package_id` + `resource_name`); the server resolves the freshest
31
+ resource URL at fetch time. Hard-coded YAML URLs are the safe fallback.
32
+ - **CSV delimiter sniffing** — ASIC labels every file `.csv` on data.gov.au,
33
+ but the actual delimiter is tab for some datasets (Financial Advisers, AFS
34
+ Authorised Representative, Banned Orgs) and comma for others (AFS
35
+ Licensee, Credit Licensee, Banned Persons, Liquidator). `read_csv`
36
+ detects from the first line.
37
+ - **Dimension-only register shaping** — register data has no measure
38
+ columns, so `shape_wide` emits one `Observation` per row carrying every
39
+ dimension on `Observation.dimensions` with `value`/`measure` left `None`.
40
+ - **SQLite byte cache** with per-kind TTLs (24h for register data, 1h for
41
+ CKAN catalogue) and mid-session corruption recovery.
42
+ - **Parsed-DataFrame in-process LRU cache** keyed by (URL, parse-spec,
43
+ body content hash) — warm hits avoid pandas re-parse.
44
+ - **In-flight dedup** — a burst of identical `latest()` calls fans into
45
+ one HTTP request.
46
+ - **State alias maps** on every register that exposes `state` — pass
47
+ `"nsw"`, `"NSW"`, or canonical `"NSW"`; all resolve identically.
48
+ - **Status alias maps** on Credit Licensee, Liquidator, and Financial
49
+ Adviser registers — pass `"approved"` and asic-mcp resolves to ASIC's
50
+ `"APPR"` code.
51
+ - **Polite User-Agent** on every outbound request. data.gov.au's CDN
52
+ blocks the default httpx UA (returns 302 to HTML); asic-mcp identifies
53
+ itself with `asic-mcp/0.1 (+https://github.com/Bigred97/asic-mcp)`.
54
+
55
+ ### Tests
56
+
57
+ - **229 unit tests + 9 live tests = 238 total**, zero flake across the
58
+ 10× gauntlet.
59
+ - Live tests assert a known-stable AFSL number (234945 — Commonwealth
60
+ Bank of Australia) is present in the live snapshot.
61
+ - Adversarial / fuzz inputs: ~80 parametrised cases probing Unicode, path
62
+ traversal, URL injection, type confusion, huge strings, and emoji on
63
+ every tool parameter.
64
+ - Discovery tests cover happy paths, off-host URL rejection (defense-in-
65
+ depth), no-match raises `DiscoveryError`, malformed CKAN responses,
66
+ and the fallback-to-YAML invariant when the network is down.
67
+
68
+ ### Known limitations (deferred to v0.2+)
69
+
70
+ - `ASIC_COMPANIES` (373 MB CSV) and `ASIC_BUSINESS_NAMES` (234 MB CSV)
71
+ are excluded from v0.1 — they need a streaming lookup-by-ACN/ABN tool
72
+ rather than bulk table reads.
73
+ - Enforcement / insolvency statistics (Series 3.1 / 3.2 / 3.3 XLSX on
74
+ asic.gov.au) are not in v0.1 — they live outside data.gov.au and need
75
+ apra-style landing-page scraping.
76
+ - Short-position daily CSVs are not in v0.1 — they have a different
77
+ cadence and shape (one CSV per reporting day).
78
+ - The dimension-only register model gives `Observation.value = None` for
79
+ every row. Agents should read register data from `Observation.dimensions`.
@@ -0,0 +1,33 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behaviour that contributes to a positive environment:
12
+
13
+ - Demonstrating empathy and kindness toward other people
14
+ - Being respectful of differing opinions, viewpoints, and experiences
15
+ - Giving and gracefully accepting constructive feedback
16
+ - Accepting responsibility and apologising to those affected by mistakes, and learning from the experience
17
+ - Focusing on what is best for the overall community
18
+
19
+ Examples of unacceptable behaviour:
20
+
21
+ - The use of sexualised language or imagery, and sexual attention or advances of any kind
22
+ - Trolling, insulting or derogatory comments, and personal or political attacks
23
+ - Public or private harassment
24
+ - Publishing others' private information, such as a physical or email address, without their explicit permission
25
+ - Other conduct which could reasonably be considered inappropriate in a professional setting
26
+
27
+ ## Enforcement
28
+
29
+ Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported to the project maintainer at hvass97@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
30
+
31
+ ## Attribution
32
+
33
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.
@@ -0,0 +1,86 @@
1
+ # Contributing to asic-mcp
2
+
3
+ Thanks for considering a contribution. This is an indie open-source project — every PR is read.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ git clone https://github.com/Bigred97/asic-mcp.git
9
+ cd asic-mcp
10
+ uv sync --extra dev
11
+ uv pip install -e .
12
+
13
+ # Unit tests (no network)
14
+ uv run pytest
15
+
16
+ # Live integration tests (hits data.gov.au)
17
+ uv run pytest -m live
18
+ ```
19
+
20
+ ## What kind of contribution helps?
21
+
22
+ | Most welcome | Be cautious |
23
+ |---|---|
24
+ | Bug fixes (with a regression test) | Adding new tools to the MCP surface |
25
+ | New curated datasets (one YAML per dataset in `src/asic_mcp/data/curated/`) | Refactors that touch >3 modules |
26
+ | Better error messages with actionable hints | Changes that break the public response shape |
27
+ | Docs / README improvements | Pulling in new dependencies |
28
+ | Performance fixes (with a benchmark) | Changes to the YAML schema |
29
+
30
+ ## Adding a curated dataset
31
+
32
+ 1. Find the dataset on [data.gov.au](https://data.gov.au/data/organization/australiantaxationoffice). Note the dataset slug (e.g. `taxation-statistics-2022-23`) and the specific resource (e.g. `Individuals - Table 6`).
33
+ 2. Fetch the resource metadata via CKAN: `curl https://data.gov.au/data/api/3/action/package_show?id={slug} | jq` and find the resource's `url` field.
34
+ 3. Download a copy and inspect headers:
35
+ ```python
36
+ import openpyxl
37
+ wb = openpyxl.load_workbook("file.xlsx", read_only=True, data_only=True)
38
+ for sn in wb.sheetnames:
39
+ ws = wb[sn]
40
+ for row in ws.iter_rows(max_row=5, values_only=True):
41
+ print(row)
42
+ ```
43
+ Identify the data sheet, the 1-indexed header row, and the canonical column names. Note: ATO column headers often have embedded newlines (`Individuals\nno.`) — `parsing._normalize_header` strips padding around the newline so canonical YAML form is `"Individuals\nno."`.
44
+ 4. Hand-write the YAML under `src/asic_mcp/data/curated/{ID}.yaml`. `CORP_TRANSPARENCY.yaml` is the simplest reference (header_row 1, flat tabular); `IND_POSTCODE.yaml` covers the multi-dimension case.
45
+ 5. Run the smoke test to confirm column mappings match:
46
+ ```bash
47
+ PYTHONPATH=src uv run python -c "
48
+ from pathlib import Path
49
+ from asic_mcp import curated, parsing, shaping
50
+ cd = curated.get('YOUR_ID')
51
+ df = parsing.read_xlsx(Path('/path/to/file.xlsx').read_bytes(), sheet=cd.sheet, header_row=cd.header_row)
52
+ missing = [c.source_column for c in cd.columns.values() if c.source_column not in df.columns]
53
+ print('missing:' if missing else 'all columns match', missing)
54
+ "
55
+ ```
56
+ 6. Add a test fixture in `tests/fixtures/` and write a test in `tests/test_shaping.py`.
57
+ 7. Run `uv run pytest -m live` and confirm green.
58
+
59
+ ## PR checklist
60
+
61
+ - [ ] All tests pass (`uv run pytest -m "not live"` minimum; `uv run pytest -m live` if you touched curation or the network path)
62
+ - [ ] New code has tests
63
+ - [ ] No new dependencies (or they're justified in the PR body)
64
+ - [ ] CHANGELOG.md updated under the Unreleased section
65
+ - [ ] If you changed default behaviour, the README "Example queries" still produces the documented values
66
+ - [ ] CC-BY 3.0 AU attribution still surfaces in `DataResponse.attribution`
67
+
68
+ ## Style
69
+
70
+ - Python 3.11+, `from __future__ import annotations` at file top
71
+ - Pydantic v2 models — use `Field(default_factory=...)` for mutable defaults
72
+ - Docstrings in module-level summary; functions only when non-obvious
73
+ - No comments restating the code; comments explain *why*
74
+
75
+ ## Filing bugs
76
+
77
+ Use the bug-report issue template. Bugs filed via the template get triaged within a week; freeform issues may sit longer.
78
+
79
+ ## Discussions vs Issues
80
+
81
+ - **Issue**: bug, feature request, security report
82
+ - **Discussion**: question, idea you're not sure about, sharing how you're using the package
83
+
84
+ ## Code of conduct
85
+
86
+ This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md). Be kind.
asic_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harry Vass
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.