mammoth-mcp-server 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 (32) hide show
  1. mammoth_mcp_server-0.1.0/.gitignore +72 -0
  2. mammoth_mcp_server-0.1.0/PKG-INFO +166 -0
  3. mammoth_mcp_server-0.1.0/README.md +148 -0
  4. mammoth_mcp_server-0.1.0/mammoth_mcp_server/__init__.py +3 -0
  5. mammoth_mcp_server-0.1.0/mammoth_mcp_server/app.py +55 -0
  6. mammoth_mcp_server-0.1.0/mammoth_mcp_server/config.py +53 -0
  7. mammoth_mcp_server-0.1.0/mammoth_mcp_server/consts.py +442 -0
  8. mammoth_mcp_server-0.1.0/mammoth_mcp_server/deadline.py +29 -0
  9. mammoth_mcp_server-0.1.0/mammoth_mcp_server/jobs.py +123 -0
  10. mammoth_mcp_server-0.1.0/mammoth_mcp_server/sdk.py +196 -0
  11. mammoth_mcp_server-0.1.0/mammoth_mcp_server/server.py +47 -0
  12. mammoth_mcp_server-0.1.0/mammoth_mcp_server/store.py +75 -0
  13. mammoth_mcp_server-0.1.0/mammoth_mcp_server/task_params.py +735 -0
  14. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tokens.py +49 -0
  15. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tool_kinds.py +15 -0
  16. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/__init__.py +27 -0
  17. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/automations.py +266 -0
  18. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/dashboards.py +328 -0
  19. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/data.py +59 -0
  20. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/discovery.py +173 -0
  21. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/exports.py +227 -0
  22. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/files.py +668 -0
  23. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/help_topics.py +69 -0
  24. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/pipeline.py +368 -0
  25. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/transformations.py +142 -0
  26. mammoth_mcp_server-0.1.0/mammoth_mcp_server/tools/views.py +90 -0
  27. mammoth_mcp_server-0.1.0/mammoth_mcp_server/transform_operations.py +1224 -0
  28. mammoth_mcp_server-0.1.0/mammoth_mcp_server/upload_app.py +266 -0
  29. mammoth_mcp_server-0.1.0/mammoth_mcp_server/upload_report.py +362 -0
  30. mammoth_mcp_server-0.1.0/mammoth_mcp_server/upload_routes.py +222 -0
  31. mammoth_mcp_server-0.1.0/mammoth_mcp_server/upload_tickets.py +122 -0
  32. mammoth_mcp_server-0.1.0/pyproject.toml +56 -0
@@ -0,0 +1,72 @@
1
+ # Bytecode
2
+ *.pyc
3
+ __pycache__/
4
+ *.py[cod]
5
+
6
+ # Environments
7
+ .venv/
8
+ venv/
9
+ env/
10
+
11
+ # Distribution / packaging
12
+ build/
13
+ dist/
14
+ *.egg-info/
15
+
16
+ # Testing / coverage
17
+ .coverage
18
+ htmlcov/
19
+ .tox/
20
+
21
+ # Jupyter
22
+ .ipynb_checkpoints/
23
+
24
+ # IDEs
25
+ .idea/
26
+ .vscode/
27
+
28
+ # Type checker
29
+ .mypy_cache/
30
+ .pytype/
31
+ .pyre/
32
+
33
+ # Linting / tools
34
+ .ruff_cache/
35
+ .pytest_cache/
36
+
37
+ # Build artifacts
38
+ *.whl
39
+ *.egg
40
+ *.egg-link
41
+
42
+ # MCP server local config
43
+ mammoth-mcp/.venv/
44
+ mammoth-mcp/poetry.toml
45
+
46
+ # Stale dev / scratch files
47
+ test_debug.py
48
+ test_live_api.py
49
+ stress.py
50
+ employee.csv
51
+ Store_Transactions.csv
52
+ openapi.json
53
+ # The CLI's pinned OpenAPI snapshot is a committed source artifact: contract
54
+ # tests read it and the built wheel ships it. Keep it tracked despite the
55
+ # broad rule above (which still ignores stray downloads elsewhere).
56
+ !mammoth-cli/spec/openapi/openapi.json
57
+
58
+ # Env files
59
+ .env
60
+ *.env
61
+ bridge/.env
62
+
63
+ # Local-only reference / build output (not part of the SDK)
64
+ mvc-service/
65
+ site/
66
+ bridge/
67
+ *.docx
68
+
69
+
70
+ # Transient STE text corpus
71
+ mammoth-cli/build/
72
+
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: mammoth-mcp-server
3
+ Version: 0.1.0
4
+ Summary: MCP server for Mammoth Analytics: an AI client explores, transforms and exports data as the signed-in user
5
+ Project-URL: homepage, https://mammoth.io
6
+ Project-URL: repository, https://github.com/EdgeMetric/mammothsdk
7
+ Author-email: Mammoth Analytics <support@mammoth.io>
8
+ License-Expression: LicenseRef-Proprietary
9
+ Requires-Python: <3.15,>=3.12
10
+ Requires-Dist: cryptography<50,>=48.0.1
11
+ Requires-Dist: mammoth-io<0.9,>=0.8.25
12
+ Requires-Dist: mcp<3,>=2.2.0
13
+ Requires-Dist: pydantic<3,>=2.12.5
14
+ Requires-Dist: python-multipart>=0.0.18
15
+ Requires-Dist: redis<8,>=5
16
+ Requires-Dist: uvicorn>=0.34
17
+ Description-Content-Type: text/markdown
18
+
19
+ # mammoth-mcp-server
20
+
21
+ The Mammoth MCP server. An AI client (Claude, ChatGPT) uses its tools to explore, clean, transform
22
+ and export data in Mammoth, as the signed-in user.
23
+
24
+ The server depends on one Mammoth package: the SDK (`mammoth-io`). Every tool reaches Mammoth
25
+ through the API, as the SDK does. The server imports no backend code.
26
+
27
+ ## Layout
28
+
29
+ | Path | What it holds |
30
+ |---|---|
31
+ | `mammoth_mcp_server/tools/` | The tools, one module per area (views, pipeline, files, dashboards, ...). |
32
+ | `mammoth_mcp_server/sdk.py` | The SDK client a tool call is given, and the API's errors in words a model can use. |
33
+ | `mammoth_mcp_server/jobs.py` | The wait for a job that an API route started. |
34
+ | `mammoth_mcp_server/task_params.py` | The map from a transformation to the pipeline task the API takes. |
35
+ | `mammoth_mcp_server/tokens.py` | The check of a caller's token: Mammoth says whether it is live and which server it is for. |
36
+ | `mammoth_mcp_server/store.py` | Short-lived records in Redis: upload tickets. |
37
+ | `mammoth_mcp_server/upload_routes.py` | The page a user drops a local file on. |
38
+ | `mammoth_mcp_server/app.py` | The ASGI app and `main()`. |
39
+ | `mammoth_mcp_server/config.py` | Every deployment setting, read from the environment. |
40
+
41
+ ## Settings
42
+
43
+ | Variable | Default | Meaning |
44
+ |---|---|---|
45
+ | `MCP_SERVER_URL` | `https://mcp.mammoth.io` | This server, as a client reaches it. |
46
+ | `MAMMOTH_APP_URL` | `https://app.mammoth.io` | The web app, for the links a tool gives the user. |
47
+ | `MAMMOTH_DASHBOARD_URL` | `MAMMOTH_APP_URL` | The host that serves a published dashboard. |
48
+ | `MAMMOTH_API_URL` | `MAMMOTH_APP_URL` + `/api/v2` | The API every tool call goes to. |
49
+ | `MAMMOTH_API_ROOT` | `/api/v2` | The path the API is served under. Set it empty for an API reached directly. |
50
+ | `MAMMOTH_OAUTH_URL` | `MAMMOTH_APP_URL` + `/api/v2` | Mammoth's authorization server (its issuer), where a client signs the user in. |
51
+ | `MCP_REDIS_URL` | `redis://localhost:6379/0` | Where upload tickets are kept. |
52
+ | `MCP_ENCRYPTION_KEY` | none | A Fernet key. The caller's token in an upload ticket is sealed with it. Required. |
53
+ | `MCP_STORE_PREFIX` | `mammoth_mcp` | Keeps this deployment's records apart in a shared Redis. |
54
+ | `MCP_HOST`, `MCP_PORT` | `127.0.0.1`, `8270` | Where the server listens. |
55
+
56
+ ## Sign-in
57
+
58
+ This server signs nobody in. Mammoth's own authorization server does: the OAuth server the Mammoth
59
+ API serves. This server is an OAuth resource server only.
60
+
61
+ 1. A client calls `/mcp` with no token. The server answers `401` and points to
62
+ `/.well-known/oauth-protected-resource/mcp`.
63
+ 2. That document names Mammoth's authorization server (`MAMMOTH_OAUTH_URL`). The client reads the
64
+ server's metadata there and sends the user to it.
65
+ 3. The user signs in to Mammoth, picks a workspace and a project, and approves. The client asks
66
+ for a token for this server's URI, `<MCP_SERVER_URL>/mcp` (the `resource` parameter, RFC 8707).
67
+ 4. The client calls `/mcp` with that token. On every request the server asks Mammoth what the token
68
+ is for (`GET /workspaces/current`). The server takes the token only when Mammoth says it is live
69
+ and was issued for this server's URI.
70
+ 5. Each tool call goes to the Mammoth API with the same token, so the user's permissions apply.
71
+
72
+ A token belongs to one workspace. A tool call for another workspace is refused. A Mammoth API token
73
+ made by hand in Workspace settings was issued for no server, so this server refuses it.
74
+
75
+ The server keeps no sign-in and no token, with one exception: an upload link holds the caller's
76
+ token, sealed, in Redis until the user uploads or 30 minutes pass.
77
+
78
+ ### What Mammoth needs
79
+
80
+ - The MCP server's URI, `<MCP_SERVER_URL>/mcp`, in mvc-service `const.ini`, section `[oauth]`,
81
+ key `MCP_RESOURCES`. Mammoth issues a token for no other resource.
82
+ - `GET /workspaces/current` must return the token's `resource` beside the workspace `id`. Without
83
+ it this server refuses every token.
84
+
85
+ **Before it works in an environment** below lists every step, in the order to check them.
86
+
87
+ ## Run
88
+
89
+ ### As a container, which needs nothing on the host but Docker
90
+
91
+ The image is built from this repository, so a deployment never waits for a
92
+ release of the SDK. Build it from the **repository root**, which is the build
93
+ context:
94
+
95
+ ```bash
96
+ cp mammoth-mcp-server/deploy/.dockerignore.example .dockerignore
97
+ docker build -f mammoth-mcp-server/deploy/Dockerfile -t mammoth-mcp-server .
98
+ docker run --env-file mammoth-mcp-server/deploy/.env -p 8270:8270 mammoth-mcp-server
99
+ ```
100
+
101
+ With a Redis of its own, as one stack:
102
+
103
+ ```bash
104
+ cp mammoth-mcp-server/.env.example mammoth-mcp-server/deploy/.env # then set the values
105
+ docker compose -f mammoth-mcp-server/deploy/compose.yaml up -d --build
106
+ ```
107
+
108
+ The server keeps nothing in memory between requests, so it scales by adding
109
+ containers. They share only Redis.
110
+
111
+ ### From a virtual environment
112
+
113
+ ```bash
114
+ pip install mammoth-mcp-server
115
+ cp .env.example .env # then set the values
116
+ mammoth-mcp-server # or: uvicorn mammoth_mcp_server.app:create_app --factory
117
+ ```
118
+
119
+ ### What `deploy/` holds
120
+
121
+ | File | What it is for |
122
+ |---|---|
123
+ | `Dockerfile` | The image, built from this repository |
124
+ | `compose.yaml` | The server and a Redis, as one stack |
125
+ | `.dockerignore.example` | Copy to the repository root before building |
126
+ | `mammoth-mcp-server-container.service.example` | systemd, running the container |
127
+ | `mammoth-mcp-server.service.example` | systemd, running a virtual environment |
128
+ | `nginx.example.conf` | Both server blocks a deployment needs |
129
+
130
+ ## Before it works in an environment
131
+
132
+ Each of these stops the sign-in on its own, so check them in order.
133
+
134
+ 1. **DNS and the front door** point `MCP_SERVER_URL` at the server's port.
135
+ `deploy/nginx.example.conf` holds the server block.
136
+ 2. **`MCP_ENCRYPTION_KEY` is set.** It is the only secret this server keeps.
137
+ Nothing else is required that has no default; see **Settings**.
138
+ 3. **Mammoth knows this server:** `MCP_RESOURCES = <MCP_SERVER_URL>/mcp`, in
139
+ mvc-service `const.ini`, section `[oauth]`. Mammoth issues a token for no
140
+ other resource.
141
+ 4. **The Mammoth host serves its OAuth metadata where a client looks for it.**
142
+ For an issuer with a path, RFC 8414 puts the document at
143
+ `/.well-known/oauth-authorization-server/api/v2`, and apiv2 serves it at its
144
+ own path, so the front door has to join the two. The second server block in
145
+ `deploy/nginx.example.conf` does it. Check it with:
146
+
147
+ ```bash
148
+ curl https://<mammoth host>/.well-known/oauth-authorization-server/api/v2
149
+ ```
150
+
151
+ It must answer the document, not `404`.
152
+ 5. **The web app's OAuth consent page is deployed.** Without it the user lands
153
+ on a page that does not exist, and approves nothing.
154
+
155
+ Then a client connects to `<MCP_SERVER_URL>/mcp`, and the user signs in to
156
+ Mammoth and picks a workspace and a project.
157
+
158
+ ## Tests
159
+
160
+ ```bash
161
+ cd mammoth-mcp-server
162
+ python -m pytest tests -q
163
+ ```
164
+
165
+ The tests reach no server. The SDK client and the tools are real; only the HTTP transport under
166
+ the client is replaced.
@@ -0,0 +1,148 @@
1
+ # mammoth-mcp-server
2
+
3
+ The Mammoth MCP server. An AI client (Claude, ChatGPT) uses its tools to explore, clean, transform
4
+ and export data in Mammoth, as the signed-in user.
5
+
6
+ The server depends on one Mammoth package: the SDK (`mammoth-io`). Every tool reaches Mammoth
7
+ through the API, as the SDK does. The server imports no backend code.
8
+
9
+ ## Layout
10
+
11
+ | Path | What it holds |
12
+ |---|---|
13
+ | `mammoth_mcp_server/tools/` | The tools, one module per area (views, pipeline, files, dashboards, ...). |
14
+ | `mammoth_mcp_server/sdk.py` | The SDK client a tool call is given, and the API's errors in words a model can use. |
15
+ | `mammoth_mcp_server/jobs.py` | The wait for a job that an API route started. |
16
+ | `mammoth_mcp_server/task_params.py` | The map from a transformation to the pipeline task the API takes. |
17
+ | `mammoth_mcp_server/tokens.py` | The check of a caller's token: Mammoth says whether it is live and which server it is for. |
18
+ | `mammoth_mcp_server/store.py` | Short-lived records in Redis: upload tickets. |
19
+ | `mammoth_mcp_server/upload_routes.py` | The page a user drops a local file on. |
20
+ | `mammoth_mcp_server/app.py` | The ASGI app and `main()`. |
21
+ | `mammoth_mcp_server/config.py` | Every deployment setting, read from the environment. |
22
+
23
+ ## Settings
24
+
25
+ | Variable | Default | Meaning |
26
+ |---|---|---|
27
+ | `MCP_SERVER_URL` | `https://mcp.mammoth.io` | This server, as a client reaches it. |
28
+ | `MAMMOTH_APP_URL` | `https://app.mammoth.io` | The web app, for the links a tool gives the user. |
29
+ | `MAMMOTH_DASHBOARD_URL` | `MAMMOTH_APP_URL` | The host that serves a published dashboard. |
30
+ | `MAMMOTH_API_URL` | `MAMMOTH_APP_URL` + `/api/v2` | The API every tool call goes to. |
31
+ | `MAMMOTH_API_ROOT` | `/api/v2` | The path the API is served under. Set it empty for an API reached directly. |
32
+ | `MAMMOTH_OAUTH_URL` | `MAMMOTH_APP_URL` + `/api/v2` | Mammoth's authorization server (its issuer), where a client signs the user in. |
33
+ | `MCP_REDIS_URL` | `redis://localhost:6379/0` | Where upload tickets are kept. |
34
+ | `MCP_ENCRYPTION_KEY` | none | A Fernet key. The caller's token in an upload ticket is sealed with it. Required. |
35
+ | `MCP_STORE_PREFIX` | `mammoth_mcp` | Keeps this deployment's records apart in a shared Redis. |
36
+ | `MCP_HOST`, `MCP_PORT` | `127.0.0.1`, `8270` | Where the server listens. |
37
+
38
+ ## Sign-in
39
+
40
+ This server signs nobody in. Mammoth's own authorization server does: the OAuth server the Mammoth
41
+ API serves. This server is an OAuth resource server only.
42
+
43
+ 1. A client calls `/mcp` with no token. The server answers `401` and points to
44
+ `/.well-known/oauth-protected-resource/mcp`.
45
+ 2. That document names Mammoth's authorization server (`MAMMOTH_OAUTH_URL`). The client reads the
46
+ server's metadata there and sends the user to it.
47
+ 3. The user signs in to Mammoth, picks a workspace and a project, and approves. The client asks
48
+ for a token for this server's URI, `<MCP_SERVER_URL>/mcp` (the `resource` parameter, RFC 8707).
49
+ 4. The client calls `/mcp` with that token. On every request the server asks Mammoth what the token
50
+ is for (`GET /workspaces/current`). The server takes the token only when Mammoth says it is live
51
+ and was issued for this server's URI.
52
+ 5. Each tool call goes to the Mammoth API with the same token, so the user's permissions apply.
53
+
54
+ A token belongs to one workspace. A tool call for another workspace is refused. A Mammoth API token
55
+ made by hand in Workspace settings was issued for no server, so this server refuses it.
56
+
57
+ The server keeps no sign-in and no token, with one exception: an upload link holds the caller's
58
+ token, sealed, in Redis until the user uploads or 30 minutes pass.
59
+
60
+ ### What Mammoth needs
61
+
62
+ - The MCP server's URI, `<MCP_SERVER_URL>/mcp`, in mvc-service `const.ini`, section `[oauth]`,
63
+ key `MCP_RESOURCES`. Mammoth issues a token for no other resource.
64
+ - `GET /workspaces/current` must return the token's `resource` beside the workspace `id`. Without
65
+ it this server refuses every token.
66
+
67
+ **Before it works in an environment** below lists every step, in the order to check them.
68
+
69
+ ## Run
70
+
71
+ ### As a container, which needs nothing on the host but Docker
72
+
73
+ The image is built from this repository, so a deployment never waits for a
74
+ release of the SDK. Build it from the **repository root**, which is the build
75
+ context:
76
+
77
+ ```bash
78
+ cp mammoth-mcp-server/deploy/.dockerignore.example .dockerignore
79
+ docker build -f mammoth-mcp-server/deploy/Dockerfile -t mammoth-mcp-server .
80
+ docker run --env-file mammoth-mcp-server/deploy/.env -p 8270:8270 mammoth-mcp-server
81
+ ```
82
+
83
+ With a Redis of its own, as one stack:
84
+
85
+ ```bash
86
+ cp mammoth-mcp-server/.env.example mammoth-mcp-server/deploy/.env # then set the values
87
+ docker compose -f mammoth-mcp-server/deploy/compose.yaml up -d --build
88
+ ```
89
+
90
+ The server keeps nothing in memory between requests, so it scales by adding
91
+ containers. They share only Redis.
92
+
93
+ ### From a virtual environment
94
+
95
+ ```bash
96
+ pip install mammoth-mcp-server
97
+ cp .env.example .env # then set the values
98
+ mammoth-mcp-server # or: uvicorn mammoth_mcp_server.app:create_app --factory
99
+ ```
100
+
101
+ ### What `deploy/` holds
102
+
103
+ | File | What it is for |
104
+ |---|---|
105
+ | `Dockerfile` | The image, built from this repository |
106
+ | `compose.yaml` | The server and a Redis, as one stack |
107
+ | `.dockerignore.example` | Copy to the repository root before building |
108
+ | `mammoth-mcp-server-container.service.example` | systemd, running the container |
109
+ | `mammoth-mcp-server.service.example` | systemd, running a virtual environment |
110
+ | `nginx.example.conf` | Both server blocks a deployment needs |
111
+
112
+ ## Before it works in an environment
113
+
114
+ Each of these stops the sign-in on its own, so check them in order.
115
+
116
+ 1. **DNS and the front door** point `MCP_SERVER_URL` at the server's port.
117
+ `deploy/nginx.example.conf` holds the server block.
118
+ 2. **`MCP_ENCRYPTION_KEY` is set.** It is the only secret this server keeps.
119
+ Nothing else is required that has no default; see **Settings**.
120
+ 3. **Mammoth knows this server:** `MCP_RESOURCES = <MCP_SERVER_URL>/mcp`, in
121
+ mvc-service `const.ini`, section `[oauth]`. Mammoth issues a token for no
122
+ other resource.
123
+ 4. **The Mammoth host serves its OAuth metadata where a client looks for it.**
124
+ For an issuer with a path, RFC 8414 puts the document at
125
+ `/.well-known/oauth-authorization-server/api/v2`, and apiv2 serves it at its
126
+ own path, so the front door has to join the two. The second server block in
127
+ `deploy/nginx.example.conf` does it. Check it with:
128
+
129
+ ```bash
130
+ curl https://<mammoth host>/.well-known/oauth-authorization-server/api/v2
131
+ ```
132
+
133
+ It must answer the document, not `404`.
134
+ 5. **The web app's OAuth consent page is deployed.** Without it the user lands
135
+ on a page that does not exist, and approves nothing.
136
+
137
+ Then a client connects to `<MCP_SERVER_URL>/mcp`, and the user signs in to
138
+ Mammoth and picks a workspace and a project.
139
+
140
+ ## Tests
141
+
142
+ ```bash
143
+ cd mammoth-mcp-server
144
+ python -m pytest tests -q
145
+ ```
146
+
147
+ The tests reach no server. The SDK client and the tools are real; only the HTTP transport under
148
+ the client is replaced.
@@ -0,0 +1,3 @@
1
+ """The Mammoth MCP server: tools an AI client drives Mammoth with, over the SDK alone."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,55 @@
1
+ """The MCP server as a process of its own: the ASGI app, and how to run it."""
2
+
3
+ import uvicorn
4
+ from mcp.server.transport_security import TransportSecuritySettings
5
+ from starlette.applications import Starlette
6
+
7
+ from .config import HOST, MCP_PATH, PORT
8
+ from .server import mcp_server, register_tools
9
+ from .store import cipher
10
+
11
+
12
+ def check_settings() -> None:
13
+ """Fail now, with the reason, rather than at the first upload.
14
+
15
+ A worker that dies at startup is restarted for ever by the process that
16
+ runs it, so the deployment looks alive and serves nothing. Run this before
17
+ the server starts, and the process stops with the reason instead.
18
+
19
+ Raises:
20
+ RuntimeError: If the deployment set no `MCP_ENCRYPTION_KEY`.
21
+ ValueError: If it set one that is not a Fernet key.
22
+ """
23
+ cipher()
24
+
25
+
26
+ def create_app() -> Starlette:
27
+ """Build the app a server runs: every tool, behind the token check.
28
+
29
+ Stateless JSON mode: any worker can serve any request, and no response is
30
+ a stream. So the server scales by adding workers, with nothing shared
31
+ between them but the store.
32
+
33
+ Raises:
34
+ RuntimeError: If the deployment set no `MCP_ENCRYPTION_KEY`.
35
+ ValueError: If it set one that is not a Fernet key.
36
+ """
37
+ check_settings()
38
+ register_tools()
39
+ return mcp_server.streamable_http_app(
40
+ streamable_http_path=MCP_PATH,
41
+ stateless_http=True,
42
+ json_response=True,
43
+ # DNS-rebinding protection is for servers on localhost. Here a browser on
44
+ # another origin cannot send the bearer token: CORS preflight stops it.
45
+ transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False),
46
+ )
47
+
48
+
49
+ def main() -> None:
50
+ """Run the server on the host and port the environment names."""
51
+ uvicorn.run(create_app(), host=HOST, port=PORT)
52
+
53
+
54
+ if __name__ == "__main__":
55
+ main()
@@ -0,0 +1,53 @@
1
+ """What this deployment is: its URLs, the Mammoth API it calls, and its store.
2
+
3
+ Every value comes from the environment, so one build runs anywhere. The server
4
+ names no Mammoth code but the SDK: it reaches Mammoth through the API alone.
5
+ """
6
+
7
+ import os
8
+ from urllib.parse import urlparse
9
+
10
+
11
+ def read(name: str, default: str) -> str:
12
+ """One setting from the environment, or its default when it is not set."""
13
+ return os.environ.get(name) or default
14
+
15
+
16
+ # The web app, as a user's browser reaches it.
17
+ APP_URL = read("MAMMOTH_APP_URL", "https://app.mammoth.io").rstrip("/")
18
+ # The host that serves a published dashboard.
19
+ DASHBOARD_DOMAIN = read("MAMMOTH_DASHBOARD_URL", APP_URL).rstrip("/")
20
+
21
+ # The Mammoth API every tool call is sent to, as the SDK takes it.
22
+ API_URL = read("MAMMOTH_API_URL", f"{APP_URL}/api/v2").rstrip("/")
23
+ # The path the API is served under. Empty for a server that is reached
24
+ # directly, which mounts its routes at their own paths.
25
+ API_ROOT: str | None = os.environ.get("MAMMOTH_API_ROOT", "/api/v2") or None
26
+
27
+ # This server, as a client reaches it. The canonical MCP server URI (RFC 8707)
28
+ # is this URL plus `/mcp`: a client asks for a token for it.
29
+ SERVER_URL = read("MCP_SERVER_URL", "https://mcp.mammoth.io").rstrip("/")
30
+ MCP_PATH = "/mcp"
31
+ MCP_RESOURCE_URL = f"{SERVER_URL}{MCP_PATH}"
32
+ # The authorization server that signs users in for this server: Mammoth's own,
33
+ # which the API serves. A client is sent there, and a token it issues for
34
+ # `MCP_RESOURCE_URL` is the one bearer this server takes.
35
+ MCP_OAUTH_URL = read("MAMMOTH_OAUTH_URL", f"{APP_URL}/api/v2").rstrip("/")
36
+ # The upload page, as the user's browser reaches it. A tool hands out this URL
37
+ # with a ticket.
38
+ MCP_UPLOAD_PATH = "/upload"
39
+ MCP_UPLOAD_URL = f"{SERVER_URL}{MCP_UPLOAD_PATH}"
40
+ # The origin of the upload page: the host lets the in-chat uploader's frame
41
+ # fetch only from the origins its resource names.
42
+ UPLOAD_ORIGIN = "{0.scheme}://{0.netloc}".format(urlparse(MCP_UPLOAD_URL))
43
+
44
+ # Where upload tickets are kept, and the key the caller's token in one is
45
+ # sealed with. Generate the key with
46
+ # `python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"`.
47
+ REDIS_URL = read("MCP_REDIS_URL", "redis://localhost:6379/0")
48
+ ENCRYPTION_KEY = read("MCP_ENCRYPTION_KEY", "")
49
+ # Keeps this server's records apart from another deployment's in one Redis.
50
+ STORE_PREFIX = read("MCP_STORE_PREFIX", "mammoth_mcp")
51
+
52
+ HOST = read("MCP_HOST", "127.0.0.1")
53
+ PORT = int(read("MCP_PORT", "8270"))