peil-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,3 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
peil_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peil
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.
@@ -0,0 +1,288 @@
1
+ Metadata-Version: 2.4
2
+ Name: peil-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for Peil — log hours, draft invoices and check where you stand from an AI assistant
5
+ Project-URL: Homepage, https://peil.app
6
+ Project-URL: Documentation, https://peil.app/docs/mcp
7
+ Author-email: Peil <studio@jeroenkortekaas.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai,claude,freelance,invoicing,mcp,peil,zzp
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Office/Business :: Financial
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: httpx>=0.27
22
+ Requires-Dist: mcp>=1.2
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Requires-Dist: respx>=0.21; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Peil MCP server
29
+
30
+ Connect Peil to Claude (or any MCP client): log hours, draft invoices from
31
+ unbilled hours, and check where you stand — from a prompt.
32
+
33
+ The server is a pure client of Peil's public API. It authenticates with a
34
+ **scoped API key** you create in Peil under **Settings → Developer** (Pro).
35
+
36
+ ## Draft-by-default
37
+
38
+ `draft_invoice` only ever creates a **draft** — nothing is sent to your
39
+ clients. Sending is a separate tool (`send_invoice`) that also requires the
40
+ separate **invoices:send** permission on your key. A key without that
41
+ permission can never email anything on your behalf.
42
+
43
+ ## Tools
44
+
45
+ **Reads** (`read`)
46
+
47
+ | Tool | What it does |
48
+ |---|---|
49
+ | `list_clients` | List your clients |
50
+ | `get_client_details` | One client's details, incl. whether a default rate is set |
51
+ | `list_unbilled` | Unbilled hours per client for a period |
52
+ | `list_invoices` | List invoices, filterable by status / client |
53
+ | `orientation_snapshot` | Outstanding / overdue / drafts / YTD position |
54
+ | `get_reminder_copy` | Your custom reminder email copy + schedule |
55
+
56
+ **Hours** (`timesheet:write`)
57
+
58
+ | Tool | What it does |
59
+ |---|---|
60
+ | `log_hours` | Add a timesheet entry (client default rate unless given) |
61
+ | `edit_hours` | Edit an entry (only the fields you pass change) |
62
+ | `delete_hours` | Delete an entry (blocked if on a sent/paid invoice) |
63
+
64
+ **Clients** (`clients:write`)
65
+
66
+ | Tool | What it does |
67
+ |---|---|
68
+ | `create_client` / `update_client` / `delete_client` | Client CRUD (delete blocked if it has projects/sent invoices) |
69
+
70
+ **Invoices** (`invoices:write`)
71
+
72
+ | Tool | What it does |
73
+ |---|---|
74
+ | `draft_invoice` | Draft an invoice from unbilled hours (summary / by_project / per_day) |
75
+ | `set_invoice_status` | Change status (e.g. mark paid) — does **not** email anyone |
76
+ | `update_invoice` | Edit safe fields (due date, payment date, notes) |
77
+ | `delete_invoice` / `archive_invoice` | Delete (paid ones blocked) / archive |
78
+ | `set_reminder_copy` | Write custom reminder email copy for one tone/language |
79
+
80
+ **Client-facing email** (`invoices:send` — irreversible, always confirm first)
81
+
82
+ | Tool | What it does |
83
+ |---|---|
84
+ | `send_invoice` | Email an invoice to the client now |
85
+ | `schedule_send` | Schedule a draft to be emailed at a future time |
86
+ | `cancel_scheduled_send` | Cancel a scheduled send |
87
+ | `send_reminder` | Email a payment reminder for a sent/overdue invoice |
88
+
89
+ ---
90
+
91
+ ## 1. Create your key
92
+
93
+ In Peil: **Settings → Developer** → create a key with the permissions you want.
94
+ Start with **read + Log hours + Draft invoices**; leave **Send invoices** off
95
+ unless you truly want an assistant emailing clients. You'll paste this key into
96
+ your assistant's config as `PEIL_API_KEY` below.
97
+
98
+ ## 2. Install the server
99
+
100
+ **The easy way — no clone, no install** (once `peil-mcp` is published to PyPI):
101
+
102
+ ```sh
103
+ uvx peil-mcp # runs the latest release on demand
104
+ # or, with pipx:
105
+ pipx run peil-mcp
106
+ ```
107
+
108
+ → Your launch command is `uvx peil-mcp` (or `pipx run peil-mcp`). Skip to
109
+ [step 3](#3-connect-your-assistant). Everything below is only needed if you're
110
+ running **from source** (e.g. before the first release, or to hack on it).
111
+
112
+ ### From source
113
+
114
+ You need **Python 3.11 or newer** and a local copy of this `mcp-server/` folder.
115
+ Check your Python with `python3 --version`.
116
+
117
+ > **Which method?** Run `which uv pipx` first. If you already have `uv`, use A —
118
+ > it's the least work. If not, `pipx` (B) gives you a clean global command. If
119
+ > you have neither and don't want to install tooling, the plain-`venv` path (C)
120
+ > works with nothing but the Python that's already on your machine.
121
+
122
+ Everywhere below, replace `/ABS/PATH/TO/mcp-server` with the real absolute path
123
+ to this folder (run `pwd` inside it to get it).
124
+
125
+ ### A. With `uv` (no install step)
126
+
127
+ `uv` builds and runs on demand — nothing to install first:
128
+
129
+ ```sh
130
+ uv run --directory /ABS/PATH/TO/mcp-server peil-mcp
131
+ ```
132
+
133
+ → Your launch command is: `uv run --directory /ABS/PATH/TO/mcp-server peil-mcp`
134
+
135
+ Don't have `uv`? `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS/Linux)
136
+ or `pip install uv`.
137
+
138
+ ### B. With `pipx` (isolated global command)
139
+
140
+ `pipx` installs the server into its own isolated environment and puts a
141
+ `peil-mcp` command on your PATH:
142
+
143
+ ```sh
144
+ pipx install /ABS/PATH/TO/mcp-server
145
+ ```
146
+
147
+ → Your launch command is simply: `peil-mcp`
148
+
149
+ Don't have `pipx`? `python3 -m pip install --user pipx && python3 -m pipx ensurepath`.
150
+
151
+ ### C. Plain `venv` + `pip` (works with only stock Python)
152
+
153
+ No extra tooling — just the `python3` you already have:
154
+
155
+ ```sh
156
+ cd /ABS/PATH/TO/mcp-server
157
+ python3 -m venv .venv
158
+ .venv/bin/pip install .
159
+ ```
160
+
161
+ → Your launch command is: `/ABS/PATH/TO/mcp-server/.venv/bin/peil-mcp`
162
+ (equivalently `/ABS/PATH/TO/mcp-server/.venv/bin/python -m peil_mcp`).
163
+
164
+ > Use a **plain** `pip install .` (not `-e`/editable) for running. An editable
165
+ > install relies on a `.pth` path hook that can silently fail to load on some
166
+ > setups, giving `ModuleNotFoundError: No module named 'peil_mcp'`. Editable is
167
+ > only needed if you're modifying the server itself — see
168
+ > [Local development](#local-development).
169
+
170
+ ## 3. Connect your assistant
171
+
172
+ **The only thing that changes between assistants is where the config lives.**
173
+ Every MCP client needs the same three things:
174
+
175
+ - **command** — your launch command from step 2
176
+ - **env** — `PEIL_API_KEY` set to the key from step 1
177
+ - (optional) **`PEIL_API_URL`** — only if you're pointing at a non-production
178
+ Peil (see [Local development](#local-development)); defaults to
179
+ `https://api.peil.app/api/v1`.
180
+
181
+ The canonical config block (used by Claude Desktop, Cursor, Windsurf, Cline, and
182
+ most others) looks like this — `command` + `args` are just your launch command
183
+ split on spaces:
184
+
185
+ ```jsonc
186
+ {
187
+ "mcpServers": {
188
+ "peil": {
189
+ "command": "peil-mcp", // or "uv", or the venv's python path
190
+ "args": [], // e.g. ["run","--directory","/ABS/PATH/TO/mcp-server","peil-mcp"] for uv
191
+ "env": { "PEIL_API_KEY": "your-key" }
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Claude Desktop
198
+
199
+ Edit `claude_desktop_config.json`
200
+ (macOS: `~/Library/Application Support/Claude/`,
201
+ Windows: `%APPDATA%\Claude\`), add the block above, and restart Claude Desktop.
202
+
203
+ ### Claude Code (CLI)
204
+
205
+ ```sh
206
+ # pipx / venv (single-command launcher):
207
+ claude mcp add peil -e PEIL_API_KEY=your-key -- peil-mcp
208
+
209
+ # uv:
210
+ claude mcp add peil -e PEIL_API_KEY=your-key -- uv run --directory /ABS/PATH/TO/mcp-server peil-mcp
211
+ ```
212
+
213
+ Anything after `--` is the launch command. Verify with `claude mcp get peil`
214
+ (look for `Status: ✔ Connected`) and use `/mcp` in a session to reconnect.
215
+
216
+ ### Cursor
217
+
218
+ Add the canonical block to `.cursor/mcp.json` (this project) or
219
+ `~/.cursor/mcp.json` (all projects), then enable **peil** in
220
+ **Settings → MCP**.
221
+
222
+ ### Windsurf
223
+
224
+ Add the canonical block to `~/.codeium/windsurf/mcp_config.json`, then hit
225
+ **Refresh** in the Cascade MCP panel.
226
+
227
+ ### Cline / Roo (VS Code)
228
+
229
+ Open the extension's **MCP Servers → Configure** panel and add the canonical
230
+ block to `cline_mcp_settings.json`.
231
+
232
+ ### VS Code (native Copilot agent mode)
233
+
234
+ VS Code uses a slightly different shape — `servers` (not `mcpServers`) and an
235
+ explicit `type` — in `.vscode/mcp.json`:
236
+
237
+ ```jsonc
238
+ {
239
+ "servers": {
240
+ "peil": {
241
+ "type": "stdio",
242
+ "command": "peil-mcp",
243
+ "args": [],
244
+ "env": { "PEIL_API_KEY": "your-key" }
245
+ }
246
+ }
247
+ }
248
+ ```
249
+
250
+ ### Any other MCP client
251
+
252
+ Give it the same **command + args + `PEIL_API_KEY` env**. The server speaks MCP
253
+ over stdio; if a client can launch a stdio command, it can run Peil.
254
+
255
+ ## First prompts
256
+
257
+ Once connected, try:
258
+
259
+ - *"Where do I stand?"* → `orientation_snapshot`
260
+ - *"Log 6 hours to De Correspondent today for editing work."* → `log_hours`
261
+ - *"Draft an invoice from my unbilled hours for De Correspondent."* → `draft_invoice`
262
+
263
+ With **Send invoices** left off your key, an assistant can prepare everything
264
+ but physically cannot email a client — you send from Peil yourself.
265
+
266
+ ---
267
+
268
+ ## Local development
269
+
270
+ Point the server at a local backend with `PEIL_API_URL`:
271
+
272
+ ```sh
273
+ PEIL_API_URL=http://localhost:8000/api/v1 PEIL_API_KEY=your-local-key peil-mcp
274
+ ```
275
+
276
+ If you're modifying the server, an editable install picks up your changes
277
+ without reinstalling:
278
+
279
+ ```sh
280
+ .venv/bin/pip install -e ".[dev]"
281
+ ```
282
+
283
+ Tests (mocked HTTP, no backend needed — `pythonpath = ["src"]` in
284
+ `pyproject.toml` makes them independent of the install mechanism):
285
+
286
+ ```sh
287
+ .venv/bin/pytest
288
+ ```
@@ -0,0 +1,261 @@
1
+ # Peil MCP server
2
+
3
+ Connect Peil to Claude (or any MCP client): log hours, draft invoices from
4
+ unbilled hours, and check where you stand — from a prompt.
5
+
6
+ The server is a pure client of Peil's public API. It authenticates with a
7
+ **scoped API key** you create in Peil under **Settings → Developer** (Pro).
8
+
9
+ ## Draft-by-default
10
+
11
+ `draft_invoice` only ever creates a **draft** — nothing is sent to your
12
+ clients. Sending is a separate tool (`send_invoice`) that also requires the
13
+ separate **invoices:send** permission on your key. A key without that
14
+ permission can never email anything on your behalf.
15
+
16
+ ## Tools
17
+
18
+ **Reads** (`read`)
19
+
20
+ | Tool | What it does |
21
+ |---|---|
22
+ | `list_clients` | List your clients |
23
+ | `get_client_details` | One client's details, incl. whether a default rate is set |
24
+ | `list_unbilled` | Unbilled hours per client for a period |
25
+ | `list_invoices` | List invoices, filterable by status / client |
26
+ | `orientation_snapshot` | Outstanding / overdue / drafts / YTD position |
27
+ | `get_reminder_copy` | Your custom reminder email copy + schedule |
28
+
29
+ **Hours** (`timesheet:write`)
30
+
31
+ | Tool | What it does |
32
+ |---|---|
33
+ | `log_hours` | Add a timesheet entry (client default rate unless given) |
34
+ | `edit_hours` | Edit an entry (only the fields you pass change) |
35
+ | `delete_hours` | Delete an entry (blocked if on a sent/paid invoice) |
36
+
37
+ **Clients** (`clients:write`)
38
+
39
+ | Tool | What it does |
40
+ |---|---|
41
+ | `create_client` / `update_client` / `delete_client` | Client CRUD (delete blocked if it has projects/sent invoices) |
42
+
43
+ **Invoices** (`invoices:write`)
44
+
45
+ | Tool | What it does |
46
+ |---|---|
47
+ | `draft_invoice` | Draft an invoice from unbilled hours (summary / by_project / per_day) |
48
+ | `set_invoice_status` | Change status (e.g. mark paid) — does **not** email anyone |
49
+ | `update_invoice` | Edit safe fields (due date, payment date, notes) |
50
+ | `delete_invoice` / `archive_invoice` | Delete (paid ones blocked) / archive |
51
+ | `set_reminder_copy` | Write custom reminder email copy for one tone/language |
52
+
53
+ **Client-facing email** (`invoices:send` — irreversible, always confirm first)
54
+
55
+ | Tool | What it does |
56
+ |---|---|
57
+ | `send_invoice` | Email an invoice to the client now |
58
+ | `schedule_send` | Schedule a draft to be emailed at a future time |
59
+ | `cancel_scheduled_send` | Cancel a scheduled send |
60
+ | `send_reminder` | Email a payment reminder for a sent/overdue invoice |
61
+
62
+ ---
63
+
64
+ ## 1. Create your key
65
+
66
+ In Peil: **Settings → Developer** → create a key with the permissions you want.
67
+ Start with **read + Log hours + Draft invoices**; leave **Send invoices** off
68
+ unless you truly want an assistant emailing clients. You'll paste this key into
69
+ your assistant's config as `PEIL_API_KEY` below.
70
+
71
+ ## 2. Install the server
72
+
73
+ **The easy way — no clone, no install** (once `peil-mcp` is published to PyPI):
74
+
75
+ ```sh
76
+ uvx peil-mcp # runs the latest release on demand
77
+ # or, with pipx:
78
+ pipx run peil-mcp
79
+ ```
80
+
81
+ → Your launch command is `uvx peil-mcp` (or `pipx run peil-mcp`). Skip to
82
+ [step 3](#3-connect-your-assistant). Everything below is only needed if you're
83
+ running **from source** (e.g. before the first release, or to hack on it).
84
+
85
+ ### From source
86
+
87
+ You need **Python 3.11 or newer** and a local copy of this `mcp-server/` folder.
88
+ Check your Python with `python3 --version`.
89
+
90
+ > **Which method?** Run `which uv pipx` first. If you already have `uv`, use A —
91
+ > it's the least work. If not, `pipx` (B) gives you a clean global command. If
92
+ > you have neither and don't want to install tooling, the plain-`venv` path (C)
93
+ > works with nothing but the Python that's already on your machine.
94
+
95
+ Everywhere below, replace `/ABS/PATH/TO/mcp-server` with the real absolute path
96
+ to this folder (run `pwd` inside it to get it).
97
+
98
+ ### A. With `uv` (no install step)
99
+
100
+ `uv` builds and runs on demand — nothing to install first:
101
+
102
+ ```sh
103
+ uv run --directory /ABS/PATH/TO/mcp-server peil-mcp
104
+ ```
105
+
106
+ → Your launch command is: `uv run --directory /ABS/PATH/TO/mcp-server peil-mcp`
107
+
108
+ Don't have `uv`? `curl -LsSf https://astral.sh/uv/install.sh | sh` (macOS/Linux)
109
+ or `pip install uv`.
110
+
111
+ ### B. With `pipx` (isolated global command)
112
+
113
+ `pipx` installs the server into its own isolated environment and puts a
114
+ `peil-mcp` command on your PATH:
115
+
116
+ ```sh
117
+ pipx install /ABS/PATH/TO/mcp-server
118
+ ```
119
+
120
+ → Your launch command is simply: `peil-mcp`
121
+
122
+ Don't have `pipx`? `python3 -m pip install --user pipx && python3 -m pipx ensurepath`.
123
+
124
+ ### C. Plain `venv` + `pip` (works with only stock Python)
125
+
126
+ No extra tooling — just the `python3` you already have:
127
+
128
+ ```sh
129
+ cd /ABS/PATH/TO/mcp-server
130
+ python3 -m venv .venv
131
+ .venv/bin/pip install .
132
+ ```
133
+
134
+ → Your launch command is: `/ABS/PATH/TO/mcp-server/.venv/bin/peil-mcp`
135
+ (equivalently `/ABS/PATH/TO/mcp-server/.venv/bin/python -m peil_mcp`).
136
+
137
+ > Use a **plain** `pip install .` (not `-e`/editable) for running. An editable
138
+ > install relies on a `.pth` path hook that can silently fail to load on some
139
+ > setups, giving `ModuleNotFoundError: No module named 'peil_mcp'`. Editable is
140
+ > only needed if you're modifying the server itself — see
141
+ > [Local development](#local-development).
142
+
143
+ ## 3. Connect your assistant
144
+
145
+ **The only thing that changes between assistants is where the config lives.**
146
+ Every MCP client needs the same three things:
147
+
148
+ - **command** — your launch command from step 2
149
+ - **env** — `PEIL_API_KEY` set to the key from step 1
150
+ - (optional) **`PEIL_API_URL`** — only if you're pointing at a non-production
151
+ Peil (see [Local development](#local-development)); defaults to
152
+ `https://api.peil.app/api/v1`.
153
+
154
+ The canonical config block (used by Claude Desktop, Cursor, Windsurf, Cline, and
155
+ most others) looks like this — `command` + `args` are just your launch command
156
+ split on spaces:
157
+
158
+ ```jsonc
159
+ {
160
+ "mcpServers": {
161
+ "peil": {
162
+ "command": "peil-mcp", // or "uv", or the venv's python path
163
+ "args": [], // e.g. ["run","--directory","/ABS/PATH/TO/mcp-server","peil-mcp"] for uv
164
+ "env": { "PEIL_API_KEY": "your-key" }
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ ### Claude Desktop
171
+
172
+ Edit `claude_desktop_config.json`
173
+ (macOS: `~/Library/Application Support/Claude/`,
174
+ Windows: `%APPDATA%\Claude\`), add the block above, and restart Claude Desktop.
175
+
176
+ ### Claude Code (CLI)
177
+
178
+ ```sh
179
+ # pipx / venv (single-command launcher):
180
+ claude mcp add peil -e PEIL_API_KEY=your-key -- peil-mcp
181
+
182
+ # uv:
183
+ claude mcp add peil -e PEIL_API_KEY=your-key -- uv run --directory /ABS/PATH/TO/mcp-server peil-mcp
184
+ ```
185
+
186
+ Anything after `--` is the launch command. Verify with `claude mcp get peil`
187
+ (look for `Status: ✔ Connected`) and use `/mcp` in a session to reconnect.
188
+
189
+ ### Cursor
190
+
191
+ Add the canonical block to `.cursor/mcp.json` (this project) or
192
+ `~/.cursor/mcp.json` (all projects), then enable **peil** in
193
+ **Settings → MCP**.
194
+
195
+ ### Windsurf
196
+
197
+ Add the canonical block to `~/.codeium/windsurf/mcp_config.json`, then hit
198
+ **Refresh** in the Cascade MCP panel.
199
+
200
+ ### Cline / Roo (VS Code)
201
+
202
+ Open the extension's **MCP Servers → Configure** panel and add the canonical
203
+ block to `cline_mcp_settings.json`.
204
+
205
+ ### VS Code (native Copilot agent mode)
206
+
207
+ VS Code uses a slightly different shape — `servers` (not `mcpServers`) and an
208
+ explicit `type` — in `.vscode/mcp.json`:
209
+
210
+ ```jsonc
211
+ {
212
+ "servers": {
213
+ "peil": {
214
+ "type": "stdio",
215
+ "command": "peil-mcp",
216
+ "args": [],
217
+ "env": { "PEIL_API_KEY": "your-key" }
218
+ }
219
+ }
220
+ }
221
+ ```
222
+
223
+ ### Any other MCP client
224
+
225
+ Give it the same **command + args + `PEIL_API_KEY` env**. The server speaks MCP
226
+ over stdio; if a client can launch a stdio command, it can run Peil.
227
+
228
+ ## First prompts
229
+
230
+ Once connected, try:
231
+
232
+ - *"Where do I stand?"* → `orientation_snapshot`
233
+ - *"Log 6 hours to De Correspondent today for editing work."* → `log_hours`
234
+ - *"Draft an invoice from my unbilled hours for De Correspondent."* → `draft_invoice`
235
+
236
+ With **Send invoices** left off your key, an assistant can prepare everything
237
+ but physically cannot email a client — you send from Peil yourself.
238
+
239
+ ---
240
+
241
+ ## Local development
242
+
243
+ Point the server at a local backend with `PEIL_API_URL`:
244
+
245
+ ```sh
246
+ PEIL_API_URL=http://localhost:8000/api/v1 PEIL_API_KEY=your-local-key peil-mcp
247
+ ```
248
+
249
+ If you're modifying the server, an editable install picks up your changes
250
+ without reinstalling:
251
+
252
+ ```sh
253
+ .venv/bin/pip install -e ".[dev]"
254
+ ```
255
+
256
+ Tests (mocked HTTP, no backend needed — `pythonpath = ["src"]` in
257
+ `pyproject.toml` makes them independent of the install mechanism):
258
+
259
+ ```sh
260
+ .venv/bin/pytest
261
+ ```
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "peil-mcp"
3
+ version = "0.1.0"
4
+ description = "MCP server for Peil — log hours, draft invoices and check where you stand from an AI assistant"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Peil", email = "studio@jeroenkortekaas.com" }]
9
+ keywords = ["mcp", "peil", "invoicing", "zzp", "freelance", "claude", "ai"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Office/Business :: Financial",
20
+ ]
21
+ dependencies = [
22
+ "mcp>=1.2",
23
+ "httpx>=0.27",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://peil.app"
28
+ Documentation = "https://peil.app/docs/mcp"
29
+
30
+ [project.scripts]
31
+ peil-mcp = "peil_mcp.server:main"
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "pytest>=8",
36
+ "respx>=0.21",
37
+ ]
38
+
39
+ [build-system]
40
+ requires = ["hatchling"]
41
+ build-backend = "hatchling.build"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/peil_mcp"]
45
+
46
+ [tool.pytest.ini_options]
47
+ # Make tests independent of the editable-install mechanism (src layout).
48
+ pythonpath = ["src"]
@@ -0,0 +1,3 @@
1
+ """Peil MCP server — a pure client of the Peil public API."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,3 @@
1
+ from peil_mcp.server import main
2
+
3
+ main()