dine-mcp 0.2.3__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.
- dine_mcp-0.2.3/.dockerignore +18 -0
- dine_mcp-0.2.3/.github/ISSUE_TEMPLATE/bug_report.yml +30 -0
- dine_mcp-0.2.3/.github/ISSUE_TEMPLATE/config.yml +1 -0
- dine_mcp-0.2.3/.github/ISSUE_TEMPLATE/provider_request.yml +17 -0
- dine_mcp-0.2.3/.github/dependabot.yml +12 -0
- dine_mcp-0.2.3/.github/workflows/ci.yml +26 -0
- dine_mcp-0.2.3/.github/workflows/pypi.yml +93 -0
- dine_mcp-0.2.3/.github/workflows/release.yml +106 -0
- dine_mcp-0.2.3/.gitignore +25 -0
- dine_mcp-0.2.3/AGENTS.md +26 -0
- dine_mcp-0.2.3/CHANGELOG.md +88 -0
- dine_mcp-0.2.3/CONTRIBUTING.md +53 -0
- dine_mcp-0.2.3/Dockerfile +37 -0
- dine_mcp-0.2.3/Dockerfile.deps +26 -0
- dine_mcp-0.2.3/LICENSE +202 -0
- dine_mcp-0.2.3/Makefile +21 -0
- dine_mcp-0.2.3/NOTICE +24 -0
- dine_mcp-0.2.3/PKG-INFO +229 -0
- dine_mcp-0.2.3/README.md +210 -0
- dine_mcp-0.2.3/SECURITY.md +13 -0
- dine_mcp-0.2.3/compose.yml +22 -0
- dine_mcp-0.2.3/docs/ARCHITECTURE.md +147 -0
- dine_mcp-0.2.3/docs/CONNECTIONS.md +42 -0
- dine_mcp-0.2.3/docs/DESIGN.md +80 -0
- dine_mcp-0.2.3/docs/PERMISSIONS.md +102 -0
- dine_mcp-0.2.3/docs/PROVIDER-REVIEW.md +24 -0
- dine_mcp-0.2.3/docs/PROVIDERS.md +78 -0
- dine_mcp-0.2.3/docs/ROADMAP.md +99 -0
- dine_mcp-0.2.3/docs/VERIFICATION.md +149 -0
- dine_mcp-0.2.3/evals/catalog.py +211 -0
- dine_mcp-0.2.3/evals/fixture_server.py +45 -0
- dine_mcp-0.2.3/evals/run.py +170 -0
- dine_mcp-0.2.3/evals/tasks.toml +202 -0
- dine_mcp-0.2.3/pyproject.toml +77 -0
- dine_mcp-0.2.3/requirements-runtime.lock +1014 -0
- dine_mcp-0.2.3/runtime-installer.py +39 -0
- dine_mcp-0.2.3/scripts/bootstrap.sh +5 -0
- dine_mcp-0.2.3/scripts/drift-report.py +33 -0
- dine_mcp-0.2.3/scripts/provider-matrix.py +42 -0
- dine_mcp-0.2.3/scripts/qualify-container.sh +85 -0
- dine_mcp-0.2.3/scripts/qualify-protocol.py +94 -0
- dine_mcp-0.2.3/scripts/source-identity.py +39 -0
- dine_mcp-0.2.3/scripts/test.sh +43 -0
- dine_mcp-0.2.3/server.json +29 -0
- dine_mcp-0.2.3/src/dine_mcp/__init__.py +3 -0
- dine_mcp-0.2.3/src/dine_mcp/__main__.py +3 -0
- dine_mcp-0.2.3/src/dine_mcp/auth.py +166 -0
- dine_mcp-0.2.3/src/dine_mcp/availability.py +566 -0
- dine_mcp-0.2.3/src/dine_mcp/cli.py +172 -0
- dine_mcp-0.2.3/src/dine_mcp/composition.py +141 -0
- dine_mcp-0.2.3/src/dine_mcp/controls.py +73 -0
- dine_mcp-0.2.3/src/dine_mcp/definitions.py +269 -0
- dine_mcp-0.2.3/src/dine_mcp/errors.py +85 -0
- dine_mcp-0.2.3/src/dine_mcp/hours.py +45 -0
- dine_mcp-0.2.3/src/dine_mcp/http_boundary.py +124 -0
- dine_mcp-0.2.3/src/dine_mcp/links.py +82 -0
- dine_mcp-0.2.3/src/dine_mcp/logs.py +177 -0
- dine_mcp-0.2.3/src/dine_mcp/models.py +214 -0
- dine_mcp-0.2.3/src/dine_mcp/places.py +113 -0
- dine_mcp-0.2.3/src/dine_mcp/places_google.py +55 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/catchtable.toml +15 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/formitable.toml +15 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/resdiary.toml +27 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/sevenrooms.toml +26 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/tabelog.toml +15 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/tablecheck.toml +26 -0
- dine_mcp-0.2.3/src/dine_mcp/provider_definitions/zenchef.toml +15 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/__init__.py +21 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/base.py +84 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/catchtable.py +115 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/common.py +91 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/formitable.py +135 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/resdiary.py +249 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/sevenrooms.py +212 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/tabelog.py +164 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/tablecheck.py +256 -0
- dine_mcp-0.2.3/src/dine_mcp/providers/zenchef.py +150 -0
- dine_mcp-0.2.3/src/dine_mcp/regions/au.toml +9 -0
- dine_mcp-0.2.3/src/dine_mcp/regions/gb.toml +10 -0
- dine_mcp-0.2.3/src/dine_mcp/regions/jp.toml +11 -0
- dine_mcp-0.2.3/src/dine_mcp/regions/kr.toml +9 -0
- dine_mcp-0.2.3/src/dine_mcp/regions/sg.toml +9 -0
- dine_mcp-0.2.3/src/dine_mcp/resolver.py +316 -0
- dine_mcp-0.2.3/src/dine_mcp/server.py +371 -0
- dine_mcp-0.2.3/src/dine_mcp/service.py +502 -0
- dine_mcp-0.2.3/src/dine_mcp/state.py +283 -0
- dine_mcp-0.2.3/src/dine_mcp/text.py +103 -0
- dine_mcp-0.2.3/src/dine_mcp/transport.py +471 -0
- dine_mcp-0.2.3/src/dine_mcp/ui/THIRD_PARTY_LICENSES.html +780 -0
- dine_mcp-0.2.3/src/dine_mcp/ui/availability.html +115 -0
- dine_mcp-0.2.3/src/dine_mcp/venues/README.md +18 -0
- dine_mcp-0.2.3/src/dine_mcp/venues/curated.toml +2 -0
- dine_mcp-0.2.3/src/dine_mcp/widgets.py +120 -0
- dine_mcp-0.2.3/tests/conftest.py +144 -0
- dine_mcp-0.2.3/tests/fixtures/README.md +8 -0
- dine_mcp-0.2.3/tests/fixtures/catchtable/search.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/catchtable/venue.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/formitable/products.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/formitable/slots.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/formitable/status.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/resdiary/error_500.json +4 -0
- dine_mcp-0.2.3/tests/fixtures/resdiary/range.json +40 -0
- dine_mcp-0.2.3/tests/fixtures/resdiary/range_empty.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/resdiary/setup.json +32 -0
- dine_mcp-0.2.3/tests/fixtures/resdiary/setup_no_services.json +11 -0
- dine_mcp-0.2.3/tests/fixtures/sevenrooms/range.json +95 -0
- dine_mcp-0.2.3/tests/fixtures/sevenrooms/range_error.json +4 -0
- dine_mcp-0.2.3/tests/fixtures/sevenrooms/venue_info.json +31 -0
- dine_mcp-0.2.3/tests/fixtures/sevenrooms/venue_info_unknown.json +5 -0
- dine_mcp-0.2.3/tests/fixtures/tabelog/suggestions.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/tabelog/venue.html +2 -0
- dine_mcp-0.2.3/tests/fixtures/tablecheck/calendar.json +186 -0
- dine_mcp-0.2.3/tests/fixtures/tablecheck/calendar_wrong_zone.json +9 -0
- dine_mcp-0.2.3/tests/fixtures/tablecheck/not_found.html +1 -0
- dine_mcp-0.2.3/tests/fixtures/tablecheck/reserve_page.html +5 -0
- dine_mcp-0.2.3/tests/fixtures/tool_surface.json +379 -0
- dine_mcp-0.2.3/tests/fixtures/website_widgets.html +10 -0
- dine_mcp-0.2.3/tests/fixtures/zenchef/calendar.json +1 -0
- dine_mcp-0.2.3/tests/fixtures/zenchef/widget.json +1 -0
- dine_mcp-0.2.3/tests/protocol_server.py +48 -0
- dine_mcp-0.2.3/tests/test_audit_regressions.py +406 -0
- dine_mcp-0.2.3/tests/test_core.py +259 -0
- dine_mcp-0.2.3/tests/test_documentation.py +43 -0
- dine_mcp-0.2.3/tests/test_evaluations.py +159 -0
- dine_mcp-0.2.3/tests/test_experimental_providers.py +234 -0
- dine_mcp-0.2.3/tests/test_hardening.py +431 -0
- dine_mcp-0.2.3/tests/test_hours.py +59 -0
- dine_mcp-0.2.3/tests/test_operations.py +135 -0
- dine_mcp-0.2.3/tests/test_provider_parsing.py +185 -0
- dine_mcp-0.2.3/tests/test_providers.py +325 -0
- dine_mcp-0.2.3/tests/test_roadmap.py +91 -0
- dine_mcp-0.2.3/tests/test_safety.py +90 -0
- dine_mcp-0.2.3/tests/test_server.py +114 -0
- dine_mcp-0.2.3/tests/test_service.py +226 -0
- dine_mcp-0.2.3/tests/test_widgets.py +472 -0
- dine_mcp-0.2.3/tests/test_wire.py +84 -0
- dine_mcp-0.2.3/tools/qualification/expected-failures.yaml +4 -0
- dine_mcp-0.2.3/tools/qualification/package-lock.json +3826 -0
- dine_mcp-0.2.3/tools/qualification/package.json +9 -0
- dine_mcp-0.2.3/tools/ui/app.js +23 -0
- dine_mcp-0.2.3/tools/ui/build.mjs +19 -0
- dine_mcp-0.2.3/tools/ui/licenses.mjs +19 -0
- dine_mcp-0.2.3/tools/ui/package-lock.json +1347 -0
- dine_mcp-0.2.3/tools/ui/package.json +8 -0
- dine_mcp-0.2.3/tools/ui/render.js +80 -0
- dine_mcp-0.2.3/tools/ui/template.html +7 -0
- dine_mcp-0.2.3/tools/ui/test/bridge.test.mjs +61 -0
- dine_mcp-0.2.3/tools/ui/test/render.test.mjs +26 -0
- dine_mcp-0.2.3/uv.lock +1933 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.github
|
|
3
|
+
.venv
|
|
4
|
+
.deps
|
|
5
|
+
.artifacts
|
|
6
|
+
**/__pycache__
|
|
7
|
+
**/.pytest_cache
|
|
8
|
+
**/.ruff_cache
|
|
9
|
+
**/*.pyc
|
|
10
|
+
tests
|
|
11
|
+
scripts
|
|
12
|
+
docs
|
|
13
|
+
private
|
|
14
|
+
publish
|
|
15
|
+
.env
|
|
16
|
+
|
|
17
|
+
# The runtime uses the packaged static App; development Node dependencies stay outside the context.
|
|
18
|
+
tools
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a failure or incorrect availability result.
|
|
3
|
+
title: "[Bug] "
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Do not include tokens, state files, personal details or real booking records.
|
|
9
|
+
Report security issues privately through the Security tab.
|
|
10
|
+
- type: input
|
|
11
|
+
id: version
|
|
12
|
+
attributes:
|
|
13
|
+
label: Version and installation method
|
|
14
|
+
placeholder: "0.1.0, uvx, Python 3.12, Linux"
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: steps
|
|
19
|
+
attributes:
|
|
20
|
+
label: Steps to reproduce
|
|
21
|
+
description: Use a public venue link or a synthetic example, with the provider and venue time zone.
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
- type: textarea
|
|
25
|
+
id: result
|
|
26
|
+
attributes:
|
|
27
|
+
label: Expected and actual result
|
|
28
|
+
description: Include the status or error code, observation time and a redacted log if useful.
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Provider request
|
|
2
|
+
description: Suggest another booking system or improve provider coverage.
|
|
3
|
+
title: "[Provider] "
|
|
4
|
+
body:
|
|
5
|
+
- type: input
|
|
6
|
+
id: provider
|
|
7
|
+
attributes:
|
|
8
|
+
label: Booking system and countries
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: example
|
|
13
|
+
attributes:
|
|
14
|
+
label: Public guest widget and availability
|
|
15
|
+
description: Link a public booking page and describe whether availability needs an account. Do not include credentials or reservations.
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ci-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
|
|
14
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 20
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
22
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
23
|
+
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
|
24
|
+
with:
|
|
25
|
+
node-version: '22'
|
|
26
|
+
- run: make check
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: PyPI
|
|
2
|
+
# A vX.Y.Z tag builds the wheel and sdist from the tagged tree and uploads them to PyPI as
|
|
3
|
+
# `dine-mcp` with trusted publishing (OIDC through the `pypi` environment; no token is
|
|
4
|
+
# stored). Tag pushes upload once the repository variable PYPI_PUBLISH is `true`; until then,
|
|
5
|
+
# or to retry, run it by hand on the tag: `gh workflow run pypi.yml --ref vX.Y.Z`. Files that
|
|
6
|
+
# are already on PyPI are skipped, so a repeated run is harmless.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ['v*.*.*']
|
|
10
|
+
workflow_dispatch: {}
|
|
11
|
+
|
|
12
|
+
permissions: {}
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: pypi-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
test:
|
|
20
|
+
if: github.ref_type == 'tag' && (github.event_name == 'workflow_dispatch' || vars.PYPI_PUBLISH == 'true')
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 20
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
30
|
+
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
|
|
31
|
+
with:
|
|
32
|
+
node-version: '22'
|
|
33
|
+
- run: make check
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
needs: test
|
|
37
|
+
if: github.ref_type == 'tag' && (github.event_name == 'workflow_dispatch' || vars.PYPI_PUBLISH == 'true')
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
timeout-minutes: 10
|
|
40
|
+
environment: pypi
|
|
41
|
+
permissions:
|
|
42
|
+
contents: read
|
|
43
|
+
id-token: write
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
46
|
+
with:
|
|
47
|
+
persist-credentials: false
|
|
48
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
49
|
+
- name: Build
|
|
50
|
+
run: |
|
|
51
|
+
version="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
52
|
+
[ "${GITHUB_REF_NAME#v}" = "$version" ] || { echo "tag $GITHUB_REF_NAME is not version $version" >&2; exit 1; }
|
|
53
|
+
uv build
|
|
54
|
+
- name: Upload
|
|
55
|
+
run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/
|
|
56
|
+
|
|
57
|
+
registry:
|
|
58
|
+
needs: publish
|
|
59
|
+
runs-on: ubuntu-latest
|
|
60
|
+
timeout-minutes: 10
|
|
61
|
+
permissions:
|
|
62
|
+
contents: read
|
|
63
|
+
id-token: write
|
|
64
|
+
env:
|
|
65
|
+
MCP_PUBLISHER_VERSION: v1.8.1
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
68
|
+
with:
|
|
69
|
+
persist-credentials: false
|
|
70
|
+
- uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
|
71
|
+
- name: Download and verify the registry publisher
|
|
72
|
+
shell: bash
|
|
73
|
+
run: |
|
|
74
|
+
set -euo pipefail
|
|
75
|
+
archive=mcp-publisher_linux_amd64.tar.gz
|
|
76
|
+
base_url="https://github.com/modelcontextprotocol/registry/releases/download/$MCP_PUBLISHER_VERSION"
|
|
77
|
+
curl --fail --location --silent --show-error -o "$archive" "$base_url/$archive"
|
|
78
|
+
curl --fail --location --silent --show-error -o "$archive.sigstore.json" "$base_url/$archive.sigstore.json"
|
|
79
|
+
cosign verify-blob --bundle "$archive.sigstore.json" \
|
|
80
|
+
--certificate-identity "https://github.com/modelcontextprotocol/registry/.github/workflows/release.yml@refs/tags/$MCP_PUBLISHER_VERSION" \
|
|
81
|
+
--certificate-oidc-issuer https://token.actions.githubusercontent.com "$archive"
|
|
82
|
+
tar xzf "$archive" mcp-publisher
|
|
83
|
+
- name: Prepare the release metadata
|
|
84
|
+
env:
|
|
85
|
+
TAG: ${{ github.ref_name }}
|
|
86
|
+
run: |
|
|
87
|
+
version="${TAG#v}"
|
|
88
|
+
jq --arg version "$version" '.version = $version | .packages[].version = $version' server.json > server-release.json
|
|
89
|
+
mv server-release.json server.json
|
|
90
|
+
- name: Publish the PyPI package to the MCP Registry
|
|
91
|
+
run: |
|
|
92
|
+
./mcp-publisher login github-oidc
|
|
93
|
+
./mcp-publisher publish
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# On a vX.Y.Z tag: test the tagged tree, build the image for linux/amd64 and linux/arm64
|
|
4
|
+
# (dependency stage first, keyed on its inputs and reused), smoke-test it, then tag it
|
|
5
|
+
# X.Y.Z and latest in ghcr.io/<owner>/dine-mcp-server, and publish a GitHub release.
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags: ['v*.*.*']
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
# GitHub can deliver one tag push twice: the runs of a ref queue one behind the other, and a
|
|
14
|
+
# run whose tag already has a GitHub release (the last job) stops at `released`.
|
|
15
|
+
concurrency:
|
|
16
|
+
group: release-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
released:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 5
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
outputs:
|
|
26
|
+
done: ${{ steps.check.outputs.done }}
|
|
27
|
+
steps:
|
|
28
|
+
- id: check
|
|
29
|
+
env:
|
|
30
|
+
GH_TOKEN: ${{ github.token }}
|
|
31
|
+
TAG: ${{ github.ref_name }}
|
|
32
|
+
run: |
|
|
33
|
+
done=false
|
|
34
|
+
if [ "$GITHUB_REF_TYPE" = tag ] && gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
|
|
35
|
+
done=true
|
|
36
|
+
echo "$TAG is already released: nothing to do"
|
|
37
|
+
fi
|
|
38
|
+
echo "done=$done" >> "$GITHUB_OUTPUT"
|
|
39
|
+
|
|
40
|
+
test:
|
|
41
|
+
needs: released
|
|
42
|
+
if: needs.released.outputs.done != 'true'
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
timeout-minutes: 20
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
47
|
+
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
48
|
+
- run: make check
|
|
49
|
+
|
|
50
|
+
image:
|
|
51
|
+
needs: test
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
timeout-minutes: 45
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
packages: write
|
|
57
|
+
env:
|
|
58
|
+
IMAGE: ghcr.io/${{ github.repository_owner }}/dine-mcp-server
|
|
59
|
+
PLATFORMS: linux/amd64,linux/arm64
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
|
|
62
|
+
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
|
|
63
|
+
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
|
|
64
|
+
- uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
|
|
65
|
+
with:
|
|
66
|
+
registry: ghcr.io
|
|
67
|
+
username: ${{ github.actor }}
|
|
68
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
69
|
+
- name: Build, smoke-test, tag
|
|
70
|
+
env:
|
|
71
|
+
VERSION: ${{ github.ref_name }}
|
|
72
|
+
run: |
|
|
73
|
+
set -euo pipefail
|
|
74
|
+
version="${VERSION#v}"
|
|
75
|
+
deps="$IMAGE:deps-$(sha256sum Dockerfile.deps requirements-runtime.lock | sha256sum | cut -c1-16)"
|
|
76
|
+
if ! docker buildx imagetools inspect "$deps" >/dev/null 2>&1; then
|
|
77
|
+
docker buildx build --platform "$PLATFORMS" -f Dockerfile.deps -t "$deps" --push .
|
|
78
|
+
fi
|
|
79
|
+
candidate="$IMAGE:sha-$(git rev-parse --short=12 HEAD)"
|
|
80
|
+
docker buildx build --platform "$PLATFORMS" -f Dockerfile --push -t "$candidate" \
|
|
81
|
+
--build-arg DINE_MCP_DEPS_IMAGE="$deps" \
|
|
82
|
+
--build-arg VCS_REF="$GITHUB_SHA" \
|
|
83
|
+
--build-arg VERSION="$version" \
|
|
84
|
+
--build-arg BUILD_DATE="$(git show -s --format=%cI HEAD)" \
|
|
85
|
+
--build-arg SOURCE_URL="https://github.com/$GITHUB_REPOSITORY" .
|
|
86
|
+
# Smoke: HTTP refuses to start without a bearer; stdio answers initialize.
|
|
87
|
+
rc=0
|
|
88
|
+
docker run --rm --network none --tmpfs /state:uid=1000 "$candidate" || rc=$?
|
|
89
|
+
[ "$rc" = 1 ] || { echo "expected a refusal without MCP_BEARER_TOKEN, got exit $rc" >&2; exit 1; }
|
|
90
|
+
reply="$(printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' \
|
|
91
|
+
| timeout 60 docker run --rm -i --network none --read-only --tmpfs /state:uid=1000 "$candidate" --transport stdio)"
|
|
92
|
+
grep -q '"serverInfo"' <<<"$reply" || { echo "stdio initialize failed" >&2; exit 1; }
|
|
93
|
+
docker buildx imagetools create -t "$IMAGE:$version" -t "$IMAGE:latest" "$candidate"
|
|
94
|
+
|
|
95
|
+
github-release:
|
|
96
|
+
needs: image
|
|
97
|
+
if: github.event_name == 'push' && github.ref_type == 'tag'
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
timeout-minutes: 5
|
|
100
|
+
permissions:
|
|
101
|
+
contents: write
|
|
102
|
+
steps:
|
|
103
|
+
- env:
|
|
104
|
+
GH_TOKEN: ${{ github.token }}
|
|
105
|
+
TAG: ${{ github.ref_name }}
|
|
106
|
+
run: gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --verify-tag --title "$TAG" --generate-notes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# secrets and local state
|
|
2
|
+
.env
|
|
3
|
+
|
|
4
|
+
# python
|
|
5
|
+
.venv/
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
|
|
15
|
+
# gate
|
|
16
|
+
.deps/
|
|
17
|
+
.artifacts/
|
|
18
|
+
|
|
19
|
+
# local overrides and users files
|
|
20
|
+
users.toml
|
|
21
|
+
overrides.toml
|
|
22
|
+
|
|
23
|
+
# protocol qualification downloads and reports
|
|
24
|
+
node_modules/
|
|
25
|
+
results/
|
dine_mcp-0.2.3/AGENTS.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Working on dine-mcp
|
|
2
|
+
|
|
3
|
+
Run `make bootstrap`, then `make check` (ruff, the format check, mypy, the lock-consistency check
|
|
4
|
+
and pytest). After a dependency change run `make lock`: `requirements-runtime.lock` must equal
|
|
5
|
+
`uv export` of `uv.lock`, because the image installs exactly that file.
|
|
6
|
+
|
|
7
|
+
dine-mcp checks availability and never books. Keep it that way by construction:
|
|
8
|
+
`src/dine_mcp/transport.py` is the only module that reaches a booking provider, every request names
|
|
9
|
+
one of its provider's pinned `OPERATIONS` (reads only), and `tests/test_safety.py` is the boundary.
|
|
10
|
+
A new host, a write operation, a network import elsewhere, or a tool argument that reaches a fetch
|
|
11
|
+
is a deliberate change there, never a side effect. Opt-in website discovery is the explicit
|
|
12
|
+
exception: `WebsiteTransport` pins GET to the website in a current place-source cache row,
|
|
13
|
+
uses public-only DNS and exact-host redirects, excludes known provider hosts, and accepts no
|
|
14
|
+
model URL. Keep its credential-free client, budgets, response cap, expiry and no-permanent-alias
|
|
15
|
+
tests intact. Venue identity comes from Google Maps only through the `google-maps-keyless-mcp` library (`places_google.py`).
|
|
16
|
+
|
|
17
|
+
Tests never reach a provider or Google: providers run against the synthetic fixtures in
|
|
18
|
+
`tests/fixtures/`, Google against a fake place source. The live tier (`pytest -m live`) is for an
|
|
19
|
+
operator, a handful of paced requests, never in a loop.
|
|
20
|
+
|
|
21
|
+
Results are observations, not holds: keep `observed_at`, `served_from` and the per-venue statuses
|
|
22
|
+
honest (`not_yet_open` is not `none_available`). Provider and venue text is data, never
|
|
23
|
+
instructions. Errors use the envelope in `errors.py`; exception text never reaches a tool result.
|
|
24
|
+
|
|
25
|
+
Keep code, comments, docs and fixtures free of personal details (names, phone numbers, e-mail
|
|
26
|
+
addresses, reservations, hosts): this repository is public. `private/` is export-ignored.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) over the tool surface.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.3]
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Compressed provider responses are decoded once, preserving decoded body limits, retry headers
|
|
14
|
+
and refusal classification instead of returning unavailable for valid gzip responses.
|
|
15
|
+
|
|
16
|
+
## [0.2.2]
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Region documentation uses ISO country codes consistently with the provider definitions.
|
|
21
|
+
|
|
22
|
+
## [0.2.1]
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Default provider adapters reject ambiguous/nonexistent local DST times and malformed calendar
|
|
27
|
+
data without reporting false inventory or complete negative coverage; valid partial slots survive.
|
|
28
|
+
- TableCheck requires boolean availability states and keeps timestamps within their calendar day.
|
|
29
|
+
- Provider clock parsing and CLI/Python query validation no longer silently truncate unsupported times.
|
|
30
|
+
- Invalid or ambiguous booking release clocks remain unknown instead of failing the venue check.
|
|
31
|
+
- Signed-in provider guidance no longer suggests an unimplemented connection tool.
|
|
32
|
+
- PyPI and MCP Registry publication require successful checks of the same tagged source.
|
|
33
|
+
- The standalone Compose default selects the current version.
|
|
34
|
+
- File locking uses the patched Filelock 4.2.1 security floor.
|
|
35
|
+
- The partial-batch deadline regression expires the service timeout after the later dispatch starts,
|
|
36
|
+
avoiding a race with worker scheduling.
|
|
37
|
+
|
|
38
|
+
## [0.2.0]
|
|
39
|
+
|
|
40
|
+
Initial availability-only release. Public distribution is pending.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- Tools `dine_find_venue`, `dine_get_venue`, `dine_check_availability` and `dine_status`.
|
|
45
|
+
- Providers SevenRooms, TableCheck and ResDiary (public guest widgets, no account).
|
|
46
|
+
- Experimental Zenchef and Formitable guest reads, and listing-only Tabelog/Korean CatchTable
|
|
47
|
+
discovery. All four additions default off and have no live acceptance claim.
|
|
48
|
+
- Minimum-party preflight and `party_too_small`/`min_party` output with contact and booking link.
|
|
49
|
+
- Optional MCP Apps grid, local filters, bounded CSP, bundled dependency notices and offline SDK tests.
|
|
50
|
+
- Bounded provider-search fallback for empty and unavailable place-source results, plus Korea configuration.
|
|
51
|
+
- Venue identity and booking-provider detection from Google Maps through
|
|
52
|
+
[google-maps-keyless-mcp](https://pypi.org/project/google-maps-keyless-mcp/).
|
|
53
|
+
- Regions SG, JP, GB and AU; curated venue facts; operator overrides.
|
|
54
|
+
- Gateway, standalone multi-user and single-user HTTP modes; stdio.
|
|
55
|
+
- MCP Registry metadata and package ownership marker; public issue forms and dependency updates.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- Explicit empty slot-kind filters are refused at the MCP boundary.
|
|
60
|
+
- Evaluation dates follow each venue's time zone across the Singapore/Tokyo midnight boundary.
|
|
61
|
+
|
|
62
|
+
- Coverage-aware TableCheck caching, honest missing dates and retained partial batches/slots.
|
|
63
|
+
- Per-dispatch budgets and redirect pacing; queued breaker checks and bounded Retry-After.
|
|
64
|
+
- End-to-end deadlines, complete venue disable guards and expiring Google-derived channels.
|
|
65
|
+
- Strict configurable HTTP authority/body protection and streamed provider response limits.
|
|
66
|
+
- First-contact gateway records and provider DNS address filtering.
|
|
67
|
+
- Clarification before selecting ambiguous venues across countries; caller location is insufficient.
|
|
68
|
+
- Single-time TableCheck queries use one anchor; unreleased-date caveats survive live/cache results.
|
|
69
|
+
- Cached booking rules avoid false budget refusals; missing-coverage warnings retain provider notes.
|
|
70
|
+
- Expired dispatch budgets report `skipped_deadline` while retaining completed observations.
|
|
71
|
+
- Verified provider channels survive Google expiry/refresh; sparse search rows preserve place details.
|
|
72
|
+
- Bounded metadata caching, isolated adapter failures, strict booking-link parsing and malformed-hours fallback.
|
|
73
|
+
- Venue-local timestamp parsing and refusal of ambiguous slots when no time zone is known.
|
|
74
|
+
- Website discovery rechecks source ownership, preserves the original expiry and bounds lock waits.
|
|
75
|
+
- Standalone token writes are serialized and atomic; duplicate token identities and excess users are refused.
|
|
76
|
+
|
|
77
|
+
### Added — hardening
|
|
78
|
+
|
|
79
|
+
- Optional `dine_status(check=false)` canaries, CLI `status --check`, persistent cooldown and
|
|
80
|
+
operator drift probes; default status and health remain offline.
|
|
81
|
+
- Fair scheduling, joined completion logs, trace metadata and stdio progress notifications.
|
|
82
|
+
- Explicit closed-day shortcuts, generated provider matrix, hardened Compose example and OCI version.
|
|
83
|
+
- Offline real transport regressions and pinned Inspector/generic conformance qualification.
|
|
84
|
+
- Model scenarios for non-Latin names, cross-country ambiguity, messaging contact, partial
|
|
85
|
+
pushback and overnight time zones; explicit model selection and failing evaluation exit codes.
|
|
86
|
+
- Packaged-container qualification using temporary images and isolated stdio/HTTP runtimes.
|
|
87
|
+
- Opt-in source-owned website widget discovery, with expiring provisional channels, bounded
|
|
88
|
+
credential-free reads, SSRF protections and directory fallback; no JavaScript or crawling.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Issues and pull requests are welcome, especially a provider that changed (`unavailable` or
|
|
4
|
+
`blocked` where it used to work), a booking system you would like supported, or a region file.
|
|
5
|
+
|
|
6
|
+
## Before you open a pull request
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
make bootstrap # uv sync --locked
|
|
10
|
+
make check # ruff, format, mypy, lock consistency, App build/tests, pytest (offline)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Development requires Python 3.12+, uv and Node 22+ (for the bundled optional App).
|
|
14
|
+
`make ui` rebuilds the App; the Python runtime needs no Node.
|
|
15
|
+
|
|
16
|
+
[AGENTS.md](AGENTS.md) has the rules: tests never reach a provider, `src/dine_mcp/transport.py` is the
|
|
17
|
+
only network module, every provider request is a pinned read operation, and fixtures are synthetic
|
|
18
|
+
or scrubbed. Keep personal details (names, phone numbers, e-mail addresses, reservations) out of
|
|
19
|
+
code, docs and fixtures.
|
|
20
|
+
|
|
21
|
+
### Add a provider
|
|
22
|
+
|
|
23
|
+
1. `src/dine_mcp/provider_definitions/<key>.toml`: access mode, maturity (`experimental` first),
|
|
24
|
+
countries, hosts, URL patterns, pacing, budget, cache lifetime, a terms note and a credit line,
|
|
25
|
+
with dated evidence comments.
|
|
26
|
+
2. `src/dine_mcp/providers/<key>.py`: the `Provider` protocol; its `OPERATIONS` are GET reads (a POST
|
|
27
|
+
only for a read endpoint that takes its query as a body, with the reason).
|
|
28
|
+
3. Synthetic fixtures under `tests/fixtures/<key>/` for: available, none available, not yet open,
|
|
29
|
+
unknown venue, closed day, party above the maximum, and a challenge or refusal.
|
|
30
|
+
4. Register it in `src/dine_mcp/providers/__init__.py`; the conformance suite
|
|
31
|
+
(`tests/test_providers.py`) then runs it.
|
|
32
|
+
5. A row in [docs/PROVIDERS.md](docs/PROVIDERS.md) stating limits, not promises.
|
|
33
|
+
|
|
34
|
+
### Add a region
|
|
35
|
+
|
|
36
|
+
`src/dine_mcp/regions/<cc>.toml` (ISO 3166-1 alpha-2, lower case): the providers that matter there
|
|
37
|
+
in order, locales, currency, time zones and dated notes. The code never branches on country.
|
|
38
|
+
|
|
39
|
+
### Curated venues
|
|
40
|
+
|
|
41
|
+
`src/dine_mcp/venues/*.toml` holds facts Google Maps and the providers lack (WhatsApp-only booking,
|
|
42
|
+
release times), keyed by Google place id, from primary sources only (the venue or the provider),
|
|
43
|
+
each with its `source`. Never copy Google Maps or other databases into it.
|
|
44
|
+
|
|
45
|
+
Security problems: see [SECURITY.md](SECURITY.md) instead of opening an issue.
|
|
46
|
+
|
|
47
|
+
## How changes land
|
|
48
|
+
|
|
49
|
+
Each release of this repository is exported from a private working repository as one
|
|
50
|
+
`Release X.Y.Z` commit, so pull requests are not merged here: the next release would replace
|
|
51
|
+
them. When a change fits, the maintainer applies it there, credits you in the release notes
|
|
52
|
+
and closes the pull request with a link to the release that contains it. Issues are welcome;
|
|
53
|
+
answers are best effort.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
# dine-mcp: source on top of dine-mcp-deps (Dockerfile.deps). The release workflow builds
|
|
3
|
+
# both stages as one chain.
|
|
4
|
+
ARG DINE_MCP_DEPS_IMAGE=dine-mcp-deps:latest
|
|
5
|
+
FROM ${DINE_MCP_DEPS_IMAGE}
|
|
6
|
+
|
|
7
|
+
WORKDIR /app
|
|
8
|
+
COPY pyproject.toml README.md LICENSE NOTICE ./
|
|
9
|
+
COPY src ./src
|
|
10
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
11
|
+
uv pip install --system --compile-bytecode --no-deps . \
|
|
12
|
+
&& rm -rf /app/src
|
|
13
|
+
|
|
14
|
+
# /state (venues, budgets, breakers, the roster, Google's consent cookies) is the only writable path; run the
|
|
15
|
+
# container with a read-only root and a /state volume or tmpfs owned by uid 1000.
|
|
16
|
+
RUN mkdir -p /state && chown 1000:1000 /state
|
|
17
|
+
|
|
18
|
+
ARG VERSION=0.2.3
|
|
19
|
+
ARG VCS_REF=unknown
|
|
20
|
+
ARG BUILD_DATE=unknown
|
|
21
|
+
ARG SOURCE_URL=unknown
|
|
22
|
+
LABEL org.opencontainers.image.title="dine-mcp" \
|
|
23
|
+
org.opencontainers.image.description="Restaurant table availability across booking systems, as an MCP server" \
|
|
24
|
+
org.opencontainers.image.version="${VERSION}" \
|
|
25
|
+
org.opencontainers.image.licenses="Apache-2.0" \
|
|
26
|
+
org.opencontainers.image.source="${SOURCE_URL}" \
|
|
27
|
+
org.opencontainers.image.revision="${VCS_REF}" \
|
|
28
|
+
org.opencontainers.image.created="${BUILD_DATE}"
|
|
29
|
+
|
|
30
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
31
|
+
DINE_HOST=0.0.0.0 \
|
|
32
|
+
DINE_STATE_DIR=/state
|
|
33
|
+
USER 1000:1000
|
|
34
|
+
EXPOSE 8000
|
|
35
|
+
HEALTHCHECK --interval=60s --timeout=5s --start-period=60s CMD ["dine-mcp", "health"]
|
|
36
|
+
ENTRYPOINT ["dine-mcp"]
|
|
37
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
# Runtime dependencies for dine-mcp on a digest-pinned Python base. Kept apart from the
|
|
3
|
+
# source so code-only releases reuse this layer: release.yml keys the image on this file and
|
|
4
|
+
# requirements-runtime.lock. Rebuild it when dependencies change (make lock rewrites
|
|
5
|
+
# requirements-runtime.lock) or when these pins are upgraded (deliberately, never a moving tag).
|
|
6
|
+
FROM ghcr.io/astral-sh/uv:0.9.26@sha256:9a23023be68b2ed09750ae636228e903a54a05ea56ed03a934d00fe9fbeded4b AS uv
|
|
7
|
+
|
|
8
|
+
FROM python:3.12.14-slim-trixie@sha256:f77ac9e44ae96ef2c90b8053ea08c31f8be030f824196b0ae4db6d462c84e51f
|
|
9
|
+
|
|
10
|
+
COPY runtime-installer.py /tmp/runtime-installer.py
|
|
11
|
+
RUN python /tmp/runtime-installer.py && rm /tmp/runtime-installer.py
|
|
12
|
+
|
|
13
|
+
COPY --from=uv /uv /usr/local/bin/uv
|
|
14
|
+
|
|
15
|
+
# The committed hash lock makes rebuilds independent of the package index's state.
|
|
16
|
+
COPY requirements-runtime.lock /tmp/requirements-runtime.lock
|
|
17
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
18
|
+
uv pip install --system --compile-bytecode --require-hashes -r /tmp/requirements-runtime.lock \
|
|
19
|
+
&& rm -f /tmp/requirements-runtime.lock \
|
|
20
|
+
&& python -c 'import httpx, fastmcp, tzdata, maps_mcp; assert fastmcp.__version__ == "4.0.10"'
|
|
21
|
+
|
|
22
|
+
ARG DEPS_REF=unknown
|
|
23
|
+
LABEL org.opencontainers.image.title="dine-mcp-deps" \
|
|
24
|
+
org.opencontainers.image.revision="${DEPS_REF}"
|
|
25
|
+
|
|
26
|
+
CMD ["true"]
|