bitscale-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.
@@ -0,0 +1,28 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write # required for PyPI trusted publishing
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.11"
20
+
21
+ - name: Install build tools
22
+ run: pip install hatchling build
23
+
24
+ - name: Build package
25
+ run: python -m build
26
+
27
+ - name: Publish to PyPI
28
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: bitscale-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for BitScale — connect your BitScale workspace to Claude
5
+ Project-URL: Homepage, https://github.com/featherflow/bitscale-mcp
6
+ Project-URL: Repository, https://github.com/featherflow/bitscale-mcp
7
+ License: MIT
8
+ Keywords: ai,bitscale,claude,mcp,model-context-protocol
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.11
13
+ Requires-Dist: httpx>=0.27.0
14
+ Requires-Dist: mcp>=1.0.0
15
+ Description-Content-Type: text/markdown
16
+
17
+ # BitScale MCP Server
18
+
19
+ Connect your [BitScale](https://bitscale.ai) workspace to Claude via the [Model Context Protocol](https://modelcontextprotocol.io) (MCP).
20
+
21
+ ## Setup
22
+
23
+ ### 1. Install `uv` (one-time)
24
+
25
+ **macOS / Linux**
26
+ ```bash
27
+ curl -LsSf https://astral.sh/uv/install.sh | sh
28
+ ```
29
+ **Windows**
30
+ ```powershell
31
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
32
+ ```
33
+
34
+ ### 2. Add to Claude Desktop config
35
+
36
+ Open `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) and add:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "bitscale": {
42
+ "command": "uvx",
43
+ "args": ["bitscale-mcp"],
44
+ "env": {
45
+ "BITSCALE_API_KEY": "your_api_key_here"
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### 3. Restart Claude Desktop
53
+
54
+ That's it. No cloning, no pip install — `uvx` pulls and runs the package automatically.
55
+
56
+ ---
57
+
58
+ ## Tools
59
+
60
+ | Tool | Description |
61
+ |------|-------------|
62
+ | `get_workspace_details` | Get workspace name, plan, and member info |
63
+ | `list_grids` | List all Grids with optional search & pagination |
64
+ | `get_grid_details` | Fetch a Grid's column schema and row data |
65
+ | `run_grid` | Trigger a Grid run (full or selected rows) |
66
+ | `get_run_status` | Poll the status of a Grid run |
67
+ | `rotate_api_key` | Rotate the workspace API key |
68
+
69
+ ---
70
+
71
+ ## Usage Examples
72
+
73
+ > *"List all my BitScale grids"*
74
+
75
+ > *"Run the Leads Enrichment grid"*
76
+
77
+ > *"What columns does my Outbound grid have?"*
78
+
79
+ > *"Check the status of run xyz456 on grid abc123"*
80
+
81
+ ---
82
+
83
+ ## Claude Code
84
+
85
+ ```bash
86
+ claude mcp add bitscale \
87
+ --command uvx \
88
+ --args bitscale-mcp \
89
+ --env BITSCALE_API_KEY=your_api_key_here
90
+ ```
91
+
92
+ ---
93
+
94
+ ## API Reference
95
+
96
+ Requests hit `https://api.bitscale.ai/api/v1`, authenticated via `X-API-KEY`. Default rate limit: **5 req/s** per workspace.
97
+
98
+ | Endpoint | Method | Tool |
99
+ |----------|--------|------|
100
+ | `/workspace` | GET | `get_workspace_details` |
101
+ | `/grids` | GET | `list_grids` |
102
+ | `/grids/:gridId` | GET | `get_grid_details` |
103
+ | `/grids/:gridId/run` | POST | `run_grid` |
104
+ | `/grids/:gridId/runs/:runId` | GET | `get_run_status` |
105
+ | `/rotate-api-key` | POST | `rotate_api_key` |
106
+
107
+ ---
108
+
109
+ ## ⚠️ API Key Rotation
110
+
111
+ Calling `rotate_api_key` immediately invalidates the current key. Update `BITSCALE_API_KEY` in your config and restart Claude Desktop after rotating.
112
+
113
+ ---
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,101 @@
1
+ # BitScale MCP Server
2
+
3
+ Connect your [BitScale](https://bitscale.ai) workspace to Claude via the [Model Context Protocol](https://modelcontextprotocol.io) (MCP).
4
+
5
+ ## Setup
6
+
7
+ ### 1. Install `uv` (one-time)
8
+
9
+ **macOS / Linux**
10
+ ```bash
11
+ curl -LsSf https://astral.sh/uv/install.sh | sh
12
+ ```
13
+ **Windows**
14
+ ```powershell
15
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
16
+ ```
17
+
18
+ ### 2. Add to Claude Desktop config
19
+
20
+ Open `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) and add:
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "bitscale": {
26
+ "command": "uvx",
27
+ "args": ["bitscale-mcp"],
28
+ "env": {
29
+ "BITSCALE_API_KEY": "your_api_key_here"
30
+ }
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### 3. Restart Claude Desktop
37
+
38
+ That's it. No cloning, no pip install — `uvx` pulls and runs the package automatically.
39
+
40
+ ---
41
+
42
+ ## Tools
43
+
44
+ | Tool | Description |
45
+ |------|-------------|
46
+ | `get_workspace_details` | Get workspace name, plan, and member info |
47
+ | `list_grids` | List all Grids with optional search & pagination |
48
+ | `get_grid_details` | Fetch a Grid's column schema and row data |
49
+ | `run_grid` | Trigger a Grid run (full or selected rows) |
50
+ | `get_run_status` | Poll the status of a Grid run |
51
+ | `rotate_api_key` | Rotate the workspace API key |
52
+
53
+ ---
54
+
55
+ ## Usage Examples
56
+
57
+ > *"List all my BitScale grids"*
58
+
59
+ > *"Run the Leads Enrichment grid"*
60
+
61
+ > *"What columns does my Outbound grid have?"*
62
+
63
+ > *"Check the status of run xyz456 on grid abc123"*
64
+
65
+ ---
66
+
67
+ ## Claude Code
68
+
69
+ ```bash
70
+ claude mcp add bitscale \
71
+ --command uvx \
72
+ --args bitscale-mcp \
73
+ --env BITSCALE_API_KEY=your_api_key_here
74
+ ```
75
+
76
+ ---
77
+
78
+ ## API Reference
79
+
80
+ Requests hit `https://api.bitscale.ai/api/v1`, authenticated via `X-API-KEY`. Default rate limit: **5 req/s** per workspace.
81
+
82
+ | Endpoint | Method | Tool |
83
+ |----------|--------|------|
84
+ | `/workspace` | GET | `get_workspace_details` |
85
+ | `/grids` | GET | `list_grids` |
86
+ | `/grids/:gridId` | GET | `get_grid_details` |
87
+ | `/grids/:gridId/run` | POST | `run_grid` |
88
+ | `/grids/:gridId/runs/:runId` | GET | `get_run_status` |
89
+ | `/rotate-api-key` | POST | `rotate_api_key` |
90
+
91
+ ---
92
+
93
+ ## ⚠️ API Key Rotation
94
+
95
+ Calling `rotate_api_key` immediately invalidates the current key. Update `BITSCALE_API_KEY` in your config and restart Claude Desktop after rotating.
96
+
97
+ ---
98
+
99
+ ## License
100
+
101
+ MIT
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ BitScale MCP Server
4
+ Exposes BitScale API endpoints as tools for Claude via the Model Context Protocol.
5
+
6
+ Quickstart (no cloning needed):
7
+ Add to your claude_desktop_config.json:
8
+ {
9
+ "mcpServers": {
10
+ "bitscale": {
11
+ "command": "uvx",
12
+ "args": ["--from", "git+https://github.com/featherflow/bitscale-mcp", "bitscale-mcp"],
13
+ "env": { "BITSCALE_API_KEY": "your_key_here" }
14
+ }
15
+ }
16
+ }
17
+ """
18
+
19
+ import os
20
+ import json
21
+ import httpx
22
+ from mcp.server.fastmcp import FastMCP
23
+
24
+ # ── Configuration ────────────────────────────────────────────────────────────
25
+
26
+ BITSCALE_API_BASE = "https://api.bitscale.ai/api/v1"
27
+ API_KEY = os.environ.get("BITSCALE_API_KEY", "")
28
+
29
+ # ── MCP Server ───────────────────────────────────────────────────────────────
30
+
31
+ mcp = FastMCP("BitScale")
32
+
33
+
34
+ def _headers() -> dict:
35
+ """Return the auth headers required by the BitScale API."""
36
+ if not API_KEY:
37
+ raise RuntimeError(
38
+ "BITSCALE_API_KEY environment variable is not set. "
39
+ "Set it before starting the server."
40
+ )
41
+ return {
42
+ "X-API-KEY": API_KEY,
43
+ "Content-Type": "application/json",
44
+ }
45
+
46
+
47
+ def _get(path: str, params: dict | None = None) -> dict:
48
+ """Perform an authenticated GET request against the BitScale API."""
49
+ url = f"{BITSCALE_API_BASE}{path}"
50
+ with httpx.Client(timeout=30) as client:
51
+ response = client.get(url, headers=_headers(), params=params)
52
+ response.raise_for_status()
53
+ return response.json()
54
+
55
+
56
+ def _post(path: str, body: dict | None = None) -> dict:
57
+ """Perform an authenticated POST request against the BitScale API."""
58
+ url = f"{BITSCALE_API_BASE}{path}"
59
+ with httpx.Client(timeout=60) as client:
60
+ response = client.post(url, headers=_headers(), json=body or {})
61
+ response.raise_for_status()
62
+ return response.json()
63
+
64
+
65
+ # ── Tools ────────────────────────────────────────────────────────────────────
66
+
67
+
68
+ @mcp.tool()
69
+ def get_workspace_details() -> str:
70
+ """
71
+ Retrieve details about the current BitScale workspace associated with
72
+ the API key, including workspace ID, name, plan, and member count.
73
+
74
+ Returns workspace metadata such as id, name, plan tier, and members.
75
+ """
76
+ data = _get("/workspace")
77
+ return json.dumps(data, indent=2)
78
+
79
+
80
+ @mcp.tool()
81
+ def list_grids(
82
+ search: str = "",
83
+ page: int = 1,
84
+ limit: int = 20,
85
+ ) -> str:
86
+ """
87
+ List all Grids (spreadsheet-like tables) in the current workspace.
88
+
89
+ Args:
90
+ search: Optional keyword to filter grids by name.
91
+ page: Page number for pagination (default: 1).
92
+ limit: Number of grids per page (default: 20, max: 100).
93
+
94
+ Returns a paginated list of grids with their IDs, names, column
95
+ definitions, and row counts.
96
+ """
97
+ params: dict = {"page": page, "limit": limit}
98
+ if search:
99
+ params["search"] = search
100
+ data = _get("/grids", params=params)
101
+ return json.dumps(data, indent=2)
102
+
103
+
104
+ @mcp.tool()
105
+ def get_grid_details(grid_id: str) -> str:
106
+ """
107
+ Retrieve detailed information about a specific Grid, including its
108
+ column schema and current data rows.
109
+
110
+ Args:
111
+ grid_id: The unique identifier of the grid (e.g. "abc123").
112
+
113
+ Returns full grid metadata plus column definitions and row data.
114
+ """
115
+ if not grid_id:
116
+ raise ValueError("grid_id must not be empty")
117
+ data = _get(f"/grids/{grid_id}")
118
+ return json.dumps(data, indent=2)
119
+
120
+
121
+ @mcp.tool()
122
+ def run_grid(
123
+ grid_id: str,
124
+ row_ids: list[str] | None = None,
125
+ ) -> str:
126
+ """
127
+ Trigger a run (execution) of a specific Grid. Optionally run only
128
+ a subset of rows by providing their IDs.
129
+
130
+ Args:
131
+ grid_id: The unique identifier of the grid to run.
132
+ row_ids: Optional list of row IDs to run. If omitted, all rows
133
+ are processed.
134
+
135
+ Returns a run object containing the run_id and initial status.
136
+ Use get_run_status to poll for completion.
137
+ """
138
+ if not grid_id:
139
+ raise ValueError("grid_id must not be empty")
140
+ body: dict = {}
141
+ if row_ids:
142
+ body["rowIds"] = row_ids
143
+ data = _post(f"/grids/{grid_id}/run", body)
144
+ return json.dumps(data, indent=2)
145
+
146
+
147
+ @mcp.tool()
148
+ def get_run_status(grid_id: str, run_id: str) -> str:
149
+ """
150
+ Check the status of a previously triggered Grid run.
151
+
152
+ Args:
153
+ grid_id: The unique identifier of the grid.
154
+ run_id: The unique identifier of the run (returned by run_grid).
155
+
156
+ Returns the run object with status ("pending", "running", "completed",
157
+ or "failed"), progress information, and any error details.
158
+ """
159
+ if not grid_id:
160
+ raise ValueError("grid_id must not be empty")
161
+ if not run_id:
162
+ raise ValueError("run_id must not be empty")
163
+ data = _get(f"/grids/{grid_id}/runs/{run_id}")
164
+ return json.dumps(data, indent=2)
165
+
166
+
167
+ @mcp.tool()
168
+ def rotate_api_key() -> str:
169
+ """
170
+ Rotate the current workspace API key. The existing key is immediately
171
+ invalidated and a new key is returned.
172
+
173
+ WARNING: After calling this tool the BITSCALE_API_KEY environment
174
+ variable must be updated with the returned key, and the MCP server
175
+ must be restarted, otherwise subsequent tool calls will fail with 401.
176
+
177
+ Returns the new API key string.
178
+ """
179
+ data = _post("/rotate-api-key")
180
+ return json.dumps(data, indent=2)
181
+
182
+
183
+ # ── Entry Point ──────────────────────────────────────────────────────────────
184
+
185
+ def run():
186
+ """Entry point used by the pyproject.toml console script."""
187
+ mcp.run()
188
+
189
+
190
+ if __name__ == "__main__":
191
+ run()
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bitscale-mcp"
7
+ version = "0.1.0"
8
+ description = "MCP server for BitScale — connect your BitScale workspace to Claude"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.11"
12
+ keywords = ["mcp", "bitscale", "claude", "ai", "model-context-protocol"]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
17
+ ]
18
+ dependencies = [
19
+ "mcp>=1.0.0",
20
+ "httpx>=0.27.0",
21
+ ]
22
+
23
+ [project.scripts]
24
+ bitscale-mcp = "main:run"
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/featherflow/bitscale-mcp"
28
+ Repository = "https://github.com/featherflow/bitscale-mcp"
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ include = ["main.py"]