@extrovert.dev/mcp 0.1.0-pre.3
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.
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/dist/bin.d.ts +13 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +89 -0
- package/dist/bin.js.map +1 -0
- package/dist/client.d.ts +956 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1354 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +53 -0
- package/dist/config.js.map +1 -0
- package/dist/contract.d.ts +62 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +78 -0
- package/dist/contract.js.map +1 -0
- package/dist/extract.d.ts +25 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +131 -0
- package/dist/extract.js.map +1 -0
- package/dist/fixtures.d.ts +676 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +2685 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/http.d.ts +18 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +124 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +36 -0
- package/dist/server.js.map +1 -0
- package/dist/stdio.d.ts +8 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +22 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tools.d.ts +27 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +2752 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +1037 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +83 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Message Science
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
<!-- // SCOPED EMAIL FOR AGENTS -->
|
|
2
|
+
|
|
3
|
+
# Extrovert MCP server
|
|
4
|
+
|
|
5
|
+
**A real inbox for your agent, in one call.**
|
|
6
|
+
|
|
7
|
+
`@extrovert.dev/mcp` is the [Model Context Protocol](https://modelcontextprotocol.io) server for
|
|
8
|
+
[Extrovert](../README.md) — Message Science's agentic-email platform. It gives an AI agent a real,
|
|
9
|
+
persistent inbox on a domain Extrovert owns: created in one tool call, sends and receives, behind a
|
|
10
|
+
**scoped key that expires and revokes on its own**. The key is bound to a fixed org + project (call
|
|
11
|
+
`whoami` to see them) — there is no project selector to manage.
|
|
12
|
+
|
|
13
|
+
> **Prerelease status:** the package is published on npm under the `next` dist-tag. Extrovert does
|
|
14
|
+
> not currently operate a hosted MCP endpoint; use the packaged stdio server or an HTTP deployment
|
|
15
|
+
> you operate yourself.
|
|
16
|
+
|
|
17
|
+
The standout tool is **`wait_for_email`** — a blocking call that returns the next matching message
|
|
18
|
+
with the **OTP code and verification link already extracted**. Trigger a sign-in elsewhere, then act
|
|
19
|
+
on the code in the same turn. No polling loop, no losing the 5-minute window.
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
redeem an enrollment key -> create_inbox -> use it as a sign-up address -> wait_for_email -> { otp_code, verification_link }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Tools
|
|
28
|
+
|
|
29
|
+
| Tool | What it does |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `redeem_enrollment` | Exchange an enrollment token (`pk_enroll_…`) for a **scoped agent key** (`pk_agent_…`). |
|
|
32
|
+
| `create_inbox` | Provision an inbox. Omit username/domain for an instant `agent@smtp.extrovert.dev` address. Attach arbitrary metadata. |
|
|
33
|
+
| `list_inboxes` | List the inboxes this agent owns. Project keys need no args; an **org-tier** key must pick a breadth (`project:<id>` or `wildcard:true`). |
|
|
34
|
+
| `get_inbox` | Fetch one inbox by opaque `inbox_id` (`pmbx_…`) or address (with its metadata). |
|
|
35
|
+
| `update_inbox` | Update settings; `daily_send_limit` (1–10,000) sets the enforced rolling-24-hour recipient cap and requires opt-in `mailbox:quota`. |
|
|
36
|
+
| `delete_inbox` | Permanently delete an inbox, its messages, and sender identity. Requires `mailbox:delete`; cannot be undone. |
|
|
37
|
+
| `send_email` | Send a new email via the inbox's authenticated sender. |
|
|
38
|
+
| `reply_email` | Reply within an existing thread. |
|
|
39
|
+
| `read_messages` | List messages in an inbox (optionally unread-only). |
|
|
40
|
+
| `list_threads` | List conversation threads. |
|
|
41
|
+
| `search` | Full-text search across one or all inboxes. |
|
|
42
|
+
| **`wait_for_email`** | **Block until a matching message arrives; return it + extracted `otp_code` / `verification_link`.** |
|
|
43
|
+
|
|
44
|
+
Every tool is registered with a typed [zod](https://zod.dev) input schema and behavioural
|
|
45
|
+
annotations (`readOnlyHint`, `destructiveHint`, …) so hosts can present and gate them correctly.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## The security model (why a scoped key, not a master key)
|
|
50
|
+
|
|
51
|
+
> Don't hand an MCP host your master key. Hand it a scoped key.
|
|
52
|
+
|
|
53
|
+
An MCP host should never hold the keys to your whole account — it only ever needs a narrow, outbound
|
|
54
|
+
voice. With Extrovert it receives nothing more than a **scoped agent key**:
|
|
55
|
+
|
|
56
|
+
- **scoped** — carries only the granted capabilities (e.g. `mailbox:create`, `mailbox:read`,
|
|
57
|
+
`mailbox:send`); quota changes require opt-in `mailbox:quota`, deletion requires
|
|
58
|
+
`mailbox:delete`, and the key may additionally be restricted to fixed domains;
|
|
59
|
+
- **server-enforced** — the inbox counter and revocation live server-side; a cloned key can't
|
|
60
|
+
exceed its `max_mailboxes`;
|
|
61
|
+
- **revocable** — killing one agent's key never rotates anyone else's;
|
|
62
|
+
- **audited** — every action is attributed to the token + agent.
|
|
63
|
+
|
|
64
|
+
This is the deliberate containment of the Postmark-MCP supply-chain blast radius: no org-wide key
|
|
65
|
+
ever reaches the model host. Keep every model host on the narrowest scoped key it needs.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Install and run the prerelease
|
|
70
|
+
|
|
71
|
+
Use the explicit prerelease tag:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# stdio for an MCP host
|
|
75
|
+
npx -y @extrovert.dev/mcp@next
|
|
76
|
+
|
|
77
|
+
# inspect the packaged CLI
|
|
78
|
+
npx -y @extrovert.dev/mcp@next --help
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pin `@extrovert.dev/mcp@0.1.0-pre.3` for a reproducible dogfood environment. The package installs the
|
|
82
|
+
`extrovert-mcp` binary; this is the only Extrovert CLI currently shipped.
|
|
83
|
+
|
|
84
|
+
## Build and run from source
|
|
85
|
+
|
|
86
|
+
Requires Node ≥ 18.18. From this directory:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pnpm install --frozen-lockfile
|
|
90
|
+
pnpm run build # compiles to dist/ (excludes tests)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Two transports, one binary:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# stdio — for local hosts (Claude Desktop, Claude Code, Cursor)
|
|
97
|
+
node /absolute/path/to/extrovert/mcp/dist/bin.js
|
|
98
|
+
|
|
99
|
+
# self-hosted Streamable HTTP at POST /mcp (default :8787)
|
|
100
|
+
node /absolute/path/to/extrovert/mcp/dist/bin.js --http --port 8787
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Run it without building during development:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pnpm run dev # tsx watch, stdio
|
|
107
|
+
pnpm run dev -- --http # tsx watch, HTTP
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Configuration (environment)
|
|
111
|
+
|
|
112
|
+
| Variable | Default | Purpose |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `EXTROVERT_API_BASE_URL` | `https://api.extrovert.dev` | Base URL of the Extrovert REST API. |
|
|
115
|
+
| `EXTROVERT_API_KEY` | *(empty)* | Scoped agent key (`pk_agent_…`) or enrollment key (`pk_enroll_…`). |
|
|
116
|
+
| `EXTROVERT_MOCK` | *(off)* | Set `1` to force offline fixtures. |
|
|
117
|
+
| `EXTROVERT_REQUEST_TIMEOUT_MS` | `30000` | Per-request timeout for non-blocking calls. |
|
|
118
|
+
| `EXTROVERT_MAX_WAIT_MS` | `300000` | Upper bound the server allows `wait_for_email` to block. |
|
|
119
|
+
| `PORT` / `HOST` | `8787` / `0.0.0.0` | `--http` bind. |
|
|
120
|
+
|
|
121
|
+
> **Offline fixtures are opt-in.** With no `EXTROVERT_API_KEY`, the server still talks to the live
|
|
122
|
+
> API so an agent can start with `sign_up` and receive a limited key in-session. Set
|
|
123
|
+
> `EXTROVERT_MOCK=1` to use deterministic in-memory fixtures; `create_inbox`, `send_email`, and
|
|
124
|
+
> `wait_for_email` then operate on one coherent offline dataset.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Host configuration
|
|
129
|
+
|
|
130
|
+
Point any stdio-capable host at the prerelease package:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"extrovert": {
|
|
136
|
+
"command": "npx",
|
|
137
|
+
"args": ["-y", "@extrovert.dev/mcp@next"],
|
|
138
|
+
"env": {
|
|
139
|
+
"EXTROVERT_API_BASE_URL": "https://api.extrovert.dev",
|
|
140
|
+
"EXTROVERT_API_KEY": "pk_agent_…"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For Claude Code, register that same local entrypoint:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
claude mcp add extrovert \
|
|
151
|
+
--env EXTROVERT_API_BASE_URL=https://api.extrovert.dev \
|
|
152
|
+
--env EXTROVERT_API_KEY=pk_agent_… \
|
|
153
|
+
-- npx -y @extrovert.dev/mcp@next
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
> **Offline:** omit `EXTROVERT_API_KEY` and set `EXTROVERT_MOCK=1`; the packaged server uses
|
|
157
|
+
> deterministic fixtures with no network or mail.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Example agent flow
|
|
162
|
+
|
|
163
|
+
The canonical flow — **redeem → create_inbox → wait_for_email** — as an agent would run it.
|
|
164
|
+
|
|
165
|
+
**1. Redeem an enrollment key for a scoped agent key.** Skip this if the host already has a key in
|
|
166
|
+
`EXTROVERT_API_KEY`.
|
|
167
|
+
|
|
168
|
+
```jsonc
|
|
169
|
+
// tool: redeem_enrollment
|
|
170
|
+
{ "enrollment_token": "pk_enroll_42_aZ9…", "agent_handle": "signup-bot" }
|
|
171
|
+
// -> { agent_id, agent_key: "pk_agent_… (shown once)", scopes: ["mailbox:create", ...],
|
|
172
|
+
// org_id, project_id }
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**2. Mint an inbox.** Omit `username`/`domain` for an instant address on a pre-warmed, verified
|
|
176
|
+
shared subdomain. Optionally tag it with arbitrary `metadata` (string/number/boolean values).
|
|
177
|
+
|
|
178
|
+
```jsonc
|
|
179
|
+
// tool: create_inbox
|
|
180
|
+
{ "display_name": "Signup Bot", "metadata": { "team": "growth", "vip": true } }
|
|
181
|
+
// -> { object: "inbox", id: "pmbx_… (opaque inbox_id — treat as opaque)",
|
|
182
|
+
// org_id, project_id, address: "agent3@smtp.extrovert.dev", status: "live",
|
|
183
|
+
// sender_verified: true, metadata: { "team": "growth", "vip": true } }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
> **Addressing an inbox.** Every inbox-keyed tool's `inbox` argument takes the canonical opaque
|
|
187
|
+
> `inbox_id` (`pmbx_…`) **or** the inbox's email address as a within-project alias. The id is the
|
|
188
|
+
> stable key; treat it as opaque (do not parse the prefix). Each inbox carries its fixed
|
|
189
|
+
> `org_id`/`project_id` (a key only ever touches inboxes in its bound project).
|
|
190
|
+
|
|
191
|
+
> **Inbox metadata** is a shallow-merged map. On `update_inbox`, pass an object to merge keys in
|
|
192
|
+
> (a key whose value is `null` deletes it), pass a top-level `null` to clear all metadata, or omit
|
|
193
|
+
> `metadata` entirely to leave it untouched. Reads always return an object (`{}` when empty).
|
|
194
|
+
|
|
195
|
+
**3. Use the address to sign up somewhere** (the agent fills a form, hits an API, etc.), then block
|
|
196
|
+
for the verification email and read the code straight out of the result:
|
|
197
|
+
|
|
198
|
+
```jsonc
|
|
199
|
+
// tool: wait_for_email
|
|
200
|
+
{ "inbox": "agent3@smtp.extrovert.dev", "from": "stripe.com", "subject": "verify", "timeout_ms": 120000 }
|
|
201
|
+
// -> { matched: true,
|
|
202
|
+
// message: { from, subject, text, … },
|
|
203
|
+
// otp_code: "481920",
|
|
204
|
+
// verification_link: "https://dashboard.stripe.com/verify?code=481920&id=evt_9" }
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The agent now has the OTP and the link in the same turn — paste the code, or open the link, and
|
|
208
|
+
continue. No polling, no separate "check the inbox" round-trips.
|
|
209
|
+
|
|
210
|
+
**4. Keep working.** Send, reply in-thread, search, list:
|
|
211
|
+
|
|
212
|
+
```jsonc
|
|
213
|
+
// tool: send_email
|
|
214
|
+
{ "inbox": "agent3@smtp.extrovert.dev", "to": ["founder@acme.example"],
|
|
215
|
+
"subject": "intro", "text": "Hi — provisioned via Extrovert.",
|
|
216
|
+
"intent": { "summary": "Introduce the new agent inbox." }, "client_id": "send-intro-1" }
|
|
217
|
+
|
|
218
|
+
// tool: reply_email
|
|
219
|
+
{ "inbox": "agent3@smtp.extrovert.dev", "thread_id": "thr_…", "text": "Following up.",
|
|
220
|
+
"intent": { "summary": "Continue the existing conversation." }, "client_id": "reply-followup-1" }
|
|
221
|
+
|
|
222
|
+
// tool: search
|
|
223
|
+
{ "query": "invoice", "inbox": "agent3@smtp.extrovert.dev" }
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Self-hosted HTTP notes
|
|
229
|
+
|
|
230
|
+
`extrovert-mcp --http` speaks MCP Streamable HTTP:
|
|
231
|
+
|
|
232
|
+
- `POST /mcp` — client→server messages (the first must be `initialize`); the SDK assigns an
|
|
233
|
+
`mcp-session-id` returned on the response.
|
|
234
|
+
- `GET /mcp` — server→client SSE stream for an established session.
|
|
235
|
+
- `DELETE /mcp` — tear a session down.
|
|
236
|
+
- `GET /healthz` — liveness + mode (`mock`/`live`) + active session count.
|
|
237
|
+
|
|
238
|
+
Each session gets an isolated server + client. A **per-request scoped key** may be supplied via
|
|
239
|
+
`Authorization: Bearer …` (or `x-extrovert-api-key`), so one hosted deployment can serve many agents,
|
|
240
|
+
each with their own scoped key — falling back to `EXTROVERT_API_KEY` when no header is present. This
|
|
241
|
+
is how a single hosted endpoint stays multi-tenant without ever holding an org-wide key.
|
|
242
|
+
|
|
243
|
+
Extrovert does not operate this transport as a public hosted endpoint. These notes apply only to a
|
|
244
|
+
deployment you control.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Architecture
|
|
249
|
+
|
|
250
|
+
```text
|
|
251
|
+
src/
|
|
252
|
+
bin.ts CLI entrypoint (--http | stdio)
|
|
253
|
+
server.ts McpServer factory + instructions
|
|
254
|
+
stdio.ts stdio transport
|
|
255
|
+
http.ts Streamable HTTP transport (Express, per-session, bearer key)
|
|
256
|
+
tools.ts manifest-driven tools — zod schemas, annotations, handlers, registration
|
|
257
|
+
client.ts thin typed Extrovert REST client (one method per /v1 endpoint)
|
|
258
|
+
config.ts env-driven configuration
|
|
259
|
+
types.ts Extrovert resource types (the REST wire shapes)
|
|
260
|
+
extract.ts OTP code + verification-link extraction (ported from Go)
|
|
261
|
+
fixtures.ts offline fixture store for tests and demos
|
|
262
|
+
extract.test.ts unit tests for the extraction logic
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The **typed client** (`ExtrovertClient`) is the single network seam: tools never call `fetch`
|
|
266
|
+
directly. OTP/link extraction is shared by the MCP wait tool and its offline fixtures.
|
|
267
|
+
|
|
268
|
+
### Live API and fixtures
|
|
269
|
+
|
|
270
|
+
The MCP client talks to the Extrovert Go REST API by default. When `EXTROVERT_MOCK=1`,
|
|
271
|
+
`ExtrovertClient` returns fixture data instead so tests and offline demos can exercise the same
|
|
272
|
+
tool surface without network access. The endpoints the client targets:
|
|
273
|
+
|
|
274
|
+
| Tool | Method + path |
|
|
275
|
+
|---|---|
|
|
276
|
+
| `redeem_enrollment` | `POST /v1/enroll` |
|
|
277
|
+
| `create_inbox` | `POST /v1/inboxes` (project-tier sugar) / `POST /v1/projects/{project_id}/inboxes` |
|
|
278
|
+
| `list_inboxes` | `GET /v1/inboxes` (project sugar) · `GET /v1/projects/{project_id}/inboxes` · `GET /v1/projects/-/inboxes` (org wildcard) |
|
|
279
|
+
| `get_inbox` | `GET /v1/inboxes/{inbox_id}` |
|
|
280
|
+
| `update_inbox` | `PATCH /v1/inboxes/{inbox_id}` (set `daily_send_limit` with `mailbox:quota`) |
|
|
281
|
+
| `delete_inbox` | `DELETE /v1/inboxes/{inbox_id}` |
|
|
282
|
+
| `send_email` | `POST /v1/inboxes/{inbox_id}/send` |
|
|
283
|
+
| `reply_email` | `POST /v1/inboxes/{inbox_id}/reply` |
|
|
284
|
+
| `read_messages` | `GET /v1/inboxes/{inbox_id}/messages` |
|
|
285
|
+
| `list_threads` | `GET /v1/inboxes/{inbox_id}/threads` |
|
|
286
|
+
| `search` | `GET /v1/inboxes/{inbox_id}/messages/search` (fans out across inboxes when none given) |
|
|
287
|
+
| `wait_for_email` | `POST /v1/inboxes/{inbox_id}/wait` (server holds the connection via IMAP IDLE) |
|
|
288
|
+
|
|
289
|
+
The path key is the canonical opaque **`inbox_id`** (`pmbx_…`); the inbox's email address is accepted
|
|
290
|
+
as a within-project alias. **Scope is in the KEY** (no scope headers): a `pk_agent_proj_…` key's
|
|
291
|
+
project is implicit; a `pk_agent_org_…` key reaches its org subtree and must pick a list breadth
|
|
292
|
+
(`project`/`wildcard`) — a bare org-key list is a `400 breadth_required`. Errors are RFC-9457
|
|
293
|
+
**problem+json** (`application/problem+json`) with a closed machine `code`
|
|
294
|
+
(`forbidden_scope`, `breadth_required`, `not_found`, `idempotency_conflict`, …); the client surfaces
|
|
295
|
+
that `code` (and any `request_id`) on every tool error, in both live and `EXTROVERT_MOCK=1` modes.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Scripts
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
pnpm run build # tsc -> dist/ (production build, tests excluded)
|
|
303
|
+
pnpm run typecheck # tsc --noEmit over the whole project (incl. tests)
|
|
304
|
+
pnpm run test # node:test via tsx
|
|
305
|
+
pnpm run dev # tsx watch (stdio); add -- --http for HTTP
|
|
306
|
+
pnpm run start # node dist/bin.js
|
|
307
|
+
pnpm run start:http # node dist/bin.js --http
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
MIT © Message Science. Extrovert is *steel-at-dusk*: a dark, technical developer brand whose single
|
|
313
|
+
warm signal is the amber seam of a side-gate.
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Extrovert MCP server CLI entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* extrovert-mcp # stdio transport (default; for local MCP hosts)
|
|
6
|
+
* extrovert-mcp --http # hosted Streamable HTTP transport
|
|
7
|
+
* extrovert-mcp --http --port 9000
|
|
8
|
+
*
|
|
9
|
+
* Configuration is read from the environment (see config.ts):
|
|
10
|
+
* EXTROVERT_API_BASE_URL, EXTROVERT_API_KEY, EXTROVERT_MOCK, ...
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=bin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Extrovert MCP server CLI entrypoint.
|
|
4
|
+
*
|
|
5
|
+
* extrovert-mcp # stdio transport (default; for local MCP hosts)
|
|
6
|
+
* extrovert-mcp --http # hosted Streamable HTTP transport
|
|
7
|
+
* extrovert-mcp --http --port 9000
|
|
8
|
+
*
|
|
9
|
+
* Configuration is read from the environment (see config.ts):
|
|
10
|
+
* EXTROVERT_API_BASE_URL, EXTROVERT_API_KEY, EXTROVERT_MOCK, ...
|
|
11
|
+
*/
|
|
12
|
+
import { runHttp } from "./http.js";
|
|
13
|
+
import { runStdio } from "./stdio.js";
|
|
14
|
+
function parseArgs(argv) {
|
|
15
|
+
const args = { http: false, help: false };
|
|
16
|
+
for (let i = 0; i < argv.length; i++) {
|
|
17
|
+
const arg = argv[i];
|
|
18
|
+
switch (arg) {
|
|
19
|
+
case "--http":
|
|
20
|
+
case "--hosted":
|
|
21
|
+
args.http = true;
|
|
22
|
+
break;
|
|
23
|
+
case "--stdio":
|
|
24
|
+
args.http = false;
|
|
25
|
+
break;
|
|
26
|
+
case "--port": {
|
|
27
|
+
const next = argv[++i];
|
|
28
|
+
if (next)
|
|
29
|
+
args.port = Number.parseInt(next, 10);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case "--host": {
|
|
33
|
+
const next = argv[++i];
|
|
34
|
+
if (next)
|
|
35
|
+
args.host = next;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case "-h":
|
|
39
|
+
case "--help":
|
|
40
|
+
args.help = true;
|
|
41
|
+
break;
|
|
42
|
+
default:
|
|
43
|
+
// Ignore unknown flags so MCP hosts can pass extras harmlessly.
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return args;
|
|
48
|
+
}
|
|
49
|
+
const HELP = `extrovert-mcp — MCP server for Extrovert (a real mailbox for your agent, in one call)
|
|
50
|
+
|
|
51
|
+
Usage:
|
|
52
|
+
extrovert-mcp [--http] [--port <n>] [--host <addr>]
|
|
53
|
+
|
|
54
|
+
Transports:
|
|
55
|
+
(default) stdio — for Claude Desktop, Claude Code, Cursor, and other local hosts
|
|
56
|
+
--http hosted Streamable HTTP transport at POST /mcp (default port 8787)
|
|
57
|
+
|
|
58
|
+
Environment:
|
|
59
|
+
EXTROVERT_API_BASE_URL Base URL of the Extrovert REST API (default https://api.extrovert.dev)
|
|
60
|
+
EXTROVERT_API_KEY Scoped agent key (pk_agent_…) or enrollment key (pk_enroll_…)
|
|
61
|
+
EXTROVERT_MOCK Set to 1 to force offline fixture mode
|
|
62
|
+
PORT / HOST Override --http bind (also honored by hosting platforms)
|
|
63
|
+
|
|
64
|
+
Tools: redeem_enrollment, create_inbox, list_inboxes, get_inbox, update_inbox, delete_inbox,
|
|
65
|
+
send_email, reply_email, read_messages, list_threads, search, wait_for_email
|
|
66
|
+
`;
|
|
67
|
+
async function main() {
|
|
68
|
+
const args = parseArgs(process.argv.slice(2));
|
|
69
|
+
if (args.help) {
|
|
70
|
+
process.stdout.write(HELP);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (args.http) {
|
|
74
|
+
const opts = {};
|
|
75
|
+
if (args.port !== undefined)
|
|
76
|
+
opts.port = args.port;
|
|
77
|
+
if (args.host !== undefined)
|
|
78
|
+
opts.host = args.host;
|
|
79
|
+
await runHttp(opts);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
await runStdio();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
main().catch((err) => {
|
|
86
|
+
process.stderr.write(`extrovert-mcp: fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAStC,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,IAAI,GAAY,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,QAAQ,CAAC;YACd,KAAK,UAAU;gBACb,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,MAAM;YACR,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvB,IAAI,IAAI;oBAAE,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAChD,MAAM;YACR,CAAC;YACD,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvB,IAAI,IAAI;oBAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBAC3B,MAAM;YACR,CAAC;YACD,KAAK,IAAI,CAAC;YACV,KAAK,QAAQ;gBACX,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR;gBACE,gEAAgE;gBAChE,MAAM;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;CAiBZ,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,GAAqC,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACnD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACnD,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,MAAM,QAAQ,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|