Skip to content

Commit 20fd5cf

Browse files
authored
feat: add local storage and resource uri support for large spreadsheets (#3)
1 parent d329a70 commit 20fd5cf

File tree

10 files changed

+1602
-46
lines changed

10 files changed

+1602
-46
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ jobs:
1111
runs-on: ubuntu-latest
1212
env:
1313
QUIP_TOKEN: ${{ secrets.QUIP_TOKEN }}
14-
QUIP_BASE_URL: ${{ secrets.QUIP_BASE_URL }}
14+
QUIP_BASE_URL: ${{ secrets.QUIP_BASE_URL || 'https://platform.quip.com'}}
1515
TEST_THREAD_ID: ${{ secrets.TEST_THREAD_ID }}
16-
TEST_SHEET_NAME: ${{ secrets.TEST_SHEET_NAME }}
16+
TEST_SHEET_NAME: ${{ secrets.TEST_SHEET_NAME || 'Sheet1' }}
17+
TEST_LARGE_SHEET_NAME: ${{ secrets.TEST_LARGE_SHEET_NAME || 'Large Table' }}
1718
steps:
1819
- name: Checkout code
1920
uses: actions/checkout@v4
@@ -38,7 +39,7 @@ jobs:
3839
pip list
3940
4041
- name: Run unit tests
41-
run: pytest tests
42+
run: pytest tests --ignore=tests/e2e
4243

4344
- name: Run e2e tests
4445
run: pytest tests/e2e

README.md

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ A Model Context Protocol (MCP) server for interacting with Quip spreadsheets. Th
66

77
- Retrieve spreadsheet content from Quip documents
88
- Support for selecting specific sheets by name
9-
- Returns data in CSV format
9+
- Returns data in CSV format with metadata
1010
- Handles authentication via Quip API token
1111
- Provides appropriate error messages for non-spreadsheet documents
12+
- Automatically handles large spreadsheets by truncating content when necessary
13+
- Stores spreadsheet content locally for efficient access
14+
- Provides resource URIs for accessing complete spreadsheet content
1215

1316
## Installation
1417

@@ -41,16 +44,18 @@ python -m src.server
4144
### Set up environment variables
4245

4346
Set up the required environment variables:
44-
4547
```bash
4648
export QUIP_TOKEN=your_quip_api_token
4749
export QUIP_BASE_URL=https://platform.quip.com # Optional, defaults to this value
50+
export QUIP_STORAGE_PATH=/path/to/storage # Optional, defaults to ~/.quip-mcp-server/storage
51+
```
4852
```
4953
5054
Alternatively, create a `.env` file in the root directory:
5155
```
5256
QUIP_TOKEN=your_quip_api_token
5357
QUIP_BASE_URL=https://platform.quip.com
58+
QUIP_STORAGE_PATH=/path/to/storage
5459
```
5560
5661
## Usage
@@ -63,7 +68,21 @@ Add to your Claude settings:
6368
"mcpServers": {
6469
"quip": {
6570
"command": "uvx",
66-
"args": ["quip-mcp-server"],
71+
"args": ["quip-mcp-server", "--storage-path", "/path/to/storage"],
72+
"env": {
73+
"QUIP_TOKEN": "your_quip_api_token"
74+
}
75+
}
76+
}
77+
```
78+
79+
If you want to use the file protocol for resource URIs:
80+
81+
```json
82+
"mcpServers": {
83+
"quip": {
84+
"command": "uvx",
85+
"args": ["quip-mcp-server", "--storage-path", "/path/to/storage", "--file-protocol"],
6786
"env": {
6887
"QUIP_TOKEN": "your_quip_api_token"
6988
}
@@ -77,10 +96,16 @@ Run the server directly:
7796

7897
```bash
7998
# Using uvx (recommended)
80-
uvx quip-mcp-server
99+
uvx quip-mcp-server --storage-path /path/to/storage
81100

82101
# Using python (if installed via pip)
83-
python -m src.server
102+
python -m src.server --storage-path /path/to/storage
103+
104+
# With file protocol for resource URIs
105+
uvx quip-mcp-server --storage-path /path/to/storage --file-protocol
106+
107+
# With debug logging enabled
108+
uvx quip-mcp-server --storage-path /path/to/storage --debug
84109
```
85110

86111
### Available Tools
@@ -102,19 +127,101 @@ Retrieves the content of a Quip spreadsheet as CSV.
102127
```
103128

104129
**Response:**
105-
The tool returns the spreadsheet content in CSV format.
130+
The tool returns a JSON object containing:
131+
- `csv_content`: The spreadsheet content in CSV format (truncated if too large)
132+
- `metadata`: Additional information about the spreadsheet:
133+
- `total_rows`: Total number of rows in the spreadsheet
134+
- `total_size`: Total size of the CSV content in bytes
135+
- `is_truncated`: Boolean indicating if the content was truncated
136+
- `resource_uri`: URI to access the complete spreadsheet content
137+
138+
**Example Response (default protocol):**
139+
```json
140+
{
141+
"csv_content": "header1,header2\nvalue1,value2\n...",
142+
"metadata": {
143+
"total_rows": 1000,
144+
"total_size": 52840,
145+
"is_truncated": true,
146+
"resource_uri": "quip://AbCdEfGhIjKl?sheet=Sheet1"
147+
}
148+
}
149+
```
150+
151+
**Example Response (with --file-protocol):**
152+
```json
153+
{
154+
"csv_content": "header1,header2\nvalue1,value2\n...",
155+
"metadata": {
156+
"total_rows": 1000,
157+
"total_size": 52840,
158+
"is_truncated": true,
159+
"resource_uri": "file:///path/to/storage/AbCdEfGhIjKl-Sheet1.csv"
160+
}
161+
}
162+
```
106163

107164
**Error Handling:**
108165
- If the thread is not a spreadsheet, an error will be returned.
109166
- If the specified sheet is not found, an error will be returned.
110167

168+
### Resource URIs
169+
170+
The server provides resource URIs for accessing complete spreadsheet content. These URIs can be used with the MCP resource access mechanism.
171+
172+
By default, the server uses the `quip://` protocol for resource URIs. However, you can use the `--file-protocol` option to use the `file://` protocol instead, which points directly to the local CSV files.
173+
174+
#### Default Protocol (quip://)
175+
176+
**URI Format:**
177+
```
178+
quip://{threadId}?sheet={sheetName}
179+
```
180+
181+
**Example:**
182+
```
183+
quip://AbCdEfGhIjKl?sheet=Sheet1
184+
```
185+
186+
#### File Protocol (with --file-protocol option)
187+
188+
**URI Format:**
189+
```
190+
file://{storage_path}/{threadId}-{sheetName}.csv
191+
```
192+
193+
**Example:**
194+
```
195+
file:///home/user/.quip-mcp-server/storage/AbCdEfGhIjKl-Sheet1.csv
196+
```
197+
198+
When accessed, the resource returns the complete CSV content of the spreadsheet, regardless of size.
199+
111200
## How It Works
112201

113202
The server uses two methods to extract spreadsheet data:
114203

115204
1. **Primary Method**: Exports the spreadsheet to XLSX format using the Quip API, then converts it to CSV.
116205
2. **Fallback Method**: If the primary method fails, it parses the HTML content of the document to extract the table data.
117206

207+
For large spreadsheets, the server:
208+
1. Saves the complete CSV content to local storage
209+
2. Returns a truncated version (up to 10KB) with metadata
210+
3. Provides a resource URI for accessing the complete content
211+
212+
### Command Line Arguments
213+
214+
The server supports the following command line arguments:
215+
216+
- `--storage-path`: Path to store CSV files (defaults to QUIP_STORAGE_PATH environment variable or ~/.quip-mcp-server/storage)
217+
- `--file-protocol`: Use file protocol for resource URIs (instead of quip:// protocol)
218+
- `--debug`: Enable debug logging
219+
220+
**Example:**
221+
```bash
222+
uvx quip-mcp-server --storage-path /path/to/storage
223+
```
224+
118225
## Development
119226

120227
### Project Structure
@@ -125,10 +232,12 @@ quip-mcp-server/
125232
│ ├── __init__.py
126233
│ ├── server.py # Main MCP server implementation
127234
│ ├── quip_client.py # Quip API client
128-
│ └── tools.py # Tool definitions and handlers
235+
│ ├── tools.py # Tool definitions and handlers
236+
│ └── storage.py # Storage abstraction and implementations
129237
├── tests/
130238
│ ├── __init__.py
131239
│ ├── test_server.py # Unit tests for the server
240+
│ ├── test_storage.py # Unit tests for the storage module
132241
│ └── e2e/ # End-to-end tests
133242
│ ├── __init__.py
134243
│ ├── conftest.py # Test fixtures for e2e tests

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ python_functions = "test_*"
5151
addopts = "-v"
5252
markers = [
5353
"e2e: marks tests as end-to-end tests that require external resources",
54+
"asyncio: marks tests as asyncio tests",
5455
]
56+
asyncio_default_fixture_loop_scope = "function"
5557

5658
[tool.coverage.run]
5759
source = ["src"]

0 commit comments

Comments
 (0)