@omnifox/mcp-server 0.2.0
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/.env.example +27 -0
- package/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/omnifox/client.d.ts +74 -0
- package/dist/omnifox/client.d.ts.map +1 -0
- package/dist/omnifox/client.js +295 -0
- package/dist/omnifox/client.js.map +1 -0
- package/dist/omnifox/types.d.ts +53 -0
- package/dist/omnifox/types.d.ts.map +1 -0
- package/dist/omnifox/types.js +9 -0
- package/dist/omnifox/types.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +51 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/comments.d.ts +16 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +31 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/contacts.d.ts +11 -0
- package/dist/tools/contacts.d.ts.map +1 -0
- package/dist/tools/contacts.js +84 -0
- package/dist/tools/contacts.js.map +1 -0
- package/dist/tools/conversations.d.ts +13 -0
- package/dist/tools/conversations.d.ts.map +1 -0
- package/dist/tools/conversations.js +91 -0
- package/dist/tools/conversations.js.map +1 -0
- package/dist/tools/extras.d.ts +12 -0
- package/dist/tools/extras.d.ts.map +1 -0
- package/dist/tools/extras.js +105 -0
- package/dist/tools/extras.js.map +1 -0
- package/dist/tools/messaging.d.ts +18 -0
- package/dist/tools/messaging.d.ts.map +1 -0
- package/dist/tools/messaging.js +127 -0
- package/dist/tools/messaging.js.map +1 -0
- package/dist/tools/workspace.d.ts +12 -0
- package/dist/tools/workspace.d.ts.map +1 -0
- package/dist/tools/workspace.js +110 -0
- package/dist/tools/workspace.js.map +1 -0
- package/dist/transport/http.d.ts +12 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +65 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/stdio.d.ts +6 -0
- package/dist/transport/stdio.d.ts.map +1 -0
- package/dist/transport/stdio.js +12 -0
- package/dist/transport/stdio.js.map +1 -0
- package/package.json +65 -0
package/.env.example
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Omnifox MCP Server — environment variables
|
|
2
|
+
# Copy to .env (or export in your shell / MCP client config) and fill in.
|
|
3
|
+
|
|
4
|
+
# REQUIRED — Sanctum personal access token issued from your Omnifox workspace.
|
|
5
|
+
# Generate at: https://omnifox.io/settings/api-tokens
|
|
6
|
+
OMNIFOX_API_KEY=
|
|
7
|
+
|
|
8
|
+
# OPTIONAL — API base URL. Defaults to production.
|
|
9
|
+
# Use https://omnifox.test/api/v1 for local dev, https://staging.omnifox.io/api/v1 for staging.
|
|
10
|
+
OMNIFOX_BASE_URL=https://omnifox.io/api/v1
|
|
11
|
+
|
|
12
|
+
# OPTIONAL — Default workspace UUID. If your token is scoped to a single workspace
|
|
13
|
+
# you can leave this blank; otherwise tools that need it will require the param.
|
|
14
|
+
OMNIFOX_WORKSPACE_ID=
|
|
15
|
+
|
|
16
|
+
# OPTIONAL — Transport mode. "stdio" (default) for desktop clients, "http" for
|
|
17
|
+
# remote / hosted deployments (Composio, k8s, Railway, Fly, etc.).
|
|
18
|
+
MCP_SERVER_MODE=stdio
|
|
19
|
+
|
|
20
|
+
# OPTIONAL — HTTP transport only. Port for the express server.
|
|
21
|
+
PORT=8080
|
|
22
|
+
|
|
23
|
+
# OPTIONAL — Request timeout in milliseconds (default 30000).
|
|
24
|
+
OMNIFOX_TIMEOUT_MS=30000
|
|
25
|
+
|
|
26
|
+
# OPTIONAL — Max retries on 429 / 5xx (default 3).
|
|
27
|
+
OMNIFOX_MAX_RETRIES=3
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Omnifox.io
|
|
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,252 @@
|
|
|
1
|
+
# @omnifox/mcp-server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@omnifox/mcp-server)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
Official **Model Context Protocol** server for [Omnifox.io](https://omnifox.io) — exposes the Omnifox V1 REST API as MCP tools so any AI agent (Claude, Cursor, Continue, Composio, OpenAI Agent SDK, etc.) can read and write across WhatsApp, Instagram, Messenger, webchat, SMS, email, and voice channels.
|
|
7
|
+
|
|
8
|
+
A direct, drop-in alternative to `@respond-io/mcp-server` — and ships with **48 tools vs respond.io's 28**.
|
|
9
|
+
|
|
10
|
+
Every tool in this release has been executed against a live Omnifox workspace through a real MCP client: 48/48 exercised, zero unexpected failures.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Quick install
|
|
15
|
+
|
|
16
|
+
### Claude Desktop (`claude_desktop_config.json`)
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"mcpServers": {
|
|
21
|
+
"omnifox": {
|
|
22
|
+
"command": "npx",
|
|
23
|
+
"args": ["-y", "@omnifox/mcp-server"],
|
|
24
|
+
"env": {
|
|
25
|
+
"OMNIFOX_API_KEY": "your-sanctum-token-here"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Config file location:
|
|
33
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
34
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
35
|
+
|
|
36
|
+
### Cursor (`.cursor/mcp.json`)
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"omnifox": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "@omnifox/mcp-server"],
|
|
44
|
+
"env": {
|
|
45
|
+
"OMNIFOX_API_KEY": "your-sanctum-token-here"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Continue (`~/.continue/config.json`)
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"experimental": {
|
|
57
|
+
"modelContextProtocolServers": [
|
|
58
|
+
{
|
|
59
|
+
"transport": {
|
|
60
|
+
"type": "stdio",
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["-y", "@omnifox/mcp-server"],
|
|
63
|
+
"env": { "OMNIFOX_API_KEY": "your-sanctum-token-here" }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Composio / hosted (HTTP transport)
|
|
72
|
+
|
|
73
|
+
Run the server in HTTP mode, then point Composio at your URL:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
MCP_SERVER_MODE=http \
|
|
77
|
+
PORT=8080 \
|
|
78
|
+
OMNIFOX_API_KEY=your-sanctum-token-here \
|
|
79
|
+
npx @omnifox/mcp-server
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Endpoints:
|
|
83
|
+
- `POST /mcp` — MCP traffic (Streamable HTTP, stateless).
|
|
84
|
+
- `GET /health` — liveness probe `{ "status": "ok", ... }`.
|
|
85
|
+
|
|
86
|
+
In Composio's MCP integration UI, register the URL `https://your-host.example.com/mcp`.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Environment variables
|
|
91
|
+
|
|
92
|
+
| Variable | Required | Default | Notes |
|
|
93
|
+
| ---------------------- | -------- | ------------------------ | -------------------------------------------------------------- |
|
|
94
|
+
| `OMNIFOX_API_KEY` | yes | — | Sanctum personal access token from the API keys screen of your account. |
|
|
95
|
+
| `OMNIFOX_BASE_URL` | no | `https://omnifox.io/api/v1` | Override for staging or self-hosted (`omnifox.test/api/v1`). |
|
|
96
|
+
| `OMNIFOX_WORKSPACE_ID` | no | — | Numeric workspace id. Optional for tokens pinned to one workspace; required for tokens that are not, and for `set_user_status` / `create_broadcast`. |
|
|
97
|
+
| `MCP_SERVER_MODE` | no | `stdio` | `stdio` for desktop, `http` for hosted/remote. |
|
|
98
|
+
| `PORT` | no | `8080` | HTTP transport only. |
|
|
99
|
+
| `OMNIFOX_TIMEOUT_MS` | no | `30000` | Per-request timeout. |
|
|
100
|
+
| `OMNIFOX_MAX_RETRIES` | no | `3` | Retries on `429` (Retry-After respected) and transient `5xx`. |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Tools (48)
|
|
105
|
+
|
|
106
|
+
### Contacts (11)
|
|
107
|
+
|
|
108
|
+
| Tool | What it does |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `get_contact` | Fetch a single contact by id or by a kind:value selector (email:, phone:, external:). |
|
|
111
|
+
| `create_contact` | Create a contact. `display_name` is the only required field; use create_or_update_contact when the person may already exist. |
|
|
112
|
+
| `update_contact` | Update an existing contact's attributes. Only the fields you pass change; everything else is left alone. |
|
|
113
|
+
| `delete_contact` | Permanently delete a contact and everything hanging off it. Irreversible — prefer leaving the contact and closing its conversation unless deletion was explicitly asked for. |
|
|
114
|
+
| `list_contacts` | List contacts, most recently active first. Use search_contacts when you are looking for somebody by name, email or phone. |
|
|
115
|
+
| `search_contacts` | Free-text contact search over name, email and phone. The query must be at least 2 characters. |
|
|
116
|
+
| `add_contact_tags` | Attach tags to a contact BY NAME, creating any that do not exist yet. |
|
|
117
|
+
| `remove_contact_tags` | Detach tags from a contact by name. Tags not currently attached are ignored. |
|
|
118
|
+
| `create_or_update_contact` | Idempotent upsert: finds the contact by the chosen keys and creates it if there is no match. |
|
|
119
|
+
| `merge_contacts` | Merge one contact into another. The primary survives; the merged record's conversations, deals and activities are reparented onto it. |
|
|
120
|
+
| `list_contact_channels` | List every channel identity attached to a contact (WhatsApp number, IG handle, email…). |
|
|
121
|
+
|
|
122
|
+
### Messaging (5)
|
|
123
|
+
|
|
124
|
+
| Tool | What it does |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `send_message` | Send a message to a contact. Reuses their open conversation on that channel or starts one. Outside WhatsApp's 24-hour window only a template will be delivered. |
|
|
127
|
+
| `send_conversation_message` | Send into a conversation you already know the id of. Use type 'note' for an internal note teammates see but the customer does not. |
|
|
128
|
+
| `get_message` | Fetch one message with its delivery status. Both ids are required — messages are addressed within their conversation. |
|
|
129
|
+
| `list_messages` | List the messages of a conversation, oldest first. Set include_notes to see internal notes interleaved. |
|
|
130
|
+
| `retry_message` | Ask the channel to deliver a failed message again. Only a message in a failed state can be retried. |
|
|
131
|
+
|
|
132
|
+
### Conversations (8)
|
|
133
|
+
|
|
134
|
+
| Tool | What it does |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `list_conversations` | List the conversations of the workspace, most recently updated first. Filter by status to find what needs attention. |
|
|
137
|
+
| `get_conversation` | Fetch one conversation with its contact, channel, status and assignment. Start here when you have an id and need context before acting. |
|
|
138
|
+
| `assign_conversation` | Assign a conversation to a user, a team or an AI agent — or unassign it. |
|
|
139
|
+
| `assign_contact_conversation` | Assign the contact's currently OPEN conversation. Use this when you know the contact but not the conversation id. |
|
|
140
|
+
| `update_conversation_status` | Reopen, resolve or close a conversation. Closing accepts a resolution label and free-text notes that stay in the thread. |
|
|
141
|
+
| `update_contact_conversation_status` | Set the status of the contact's open conversation when you only know the contact. |
|
|
142
|
+
| `snooze_conversation` | Snooze a conversation so it leaves the queue and comes back later. Use it instead of closing when a follow-up is due. |
|
|
143
|
+
| `list_conversation_notes` | List the internal notes on a conversation — teammate-only context the customer never saw. |
|
|
144
|
+
|
|
145
|
+
### Notes (1)
|
|
146
|
+
|
|
147
|
+
| Tool | What it does |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `create_comment` | Add an internal note to a conversation. Notes are visible to teammates only. |
|
|
150
|
+
|
|
151
|
+
### Workspace (13)
|
|
152
|
+
|
|
153
|
+
| Tool | What it does |
|
|
154
|
+
| --- | --- |
|
|
155
|
+
| `list_users` | List the workspace members (human agents) with their availability status. |
|
|
156
|
+
| `get_user` | Fetch a single workspace member by id, with their role and current availability. |
|
|
157
|
+
| `set_user_status` | Set a member's availability: available, busy, away or offline. Routing rules honour it immediately. |
|
|
158
|
+
| `list_custom_fields` | List the custom field definitions of the workspace so you know which keys a record accepts. |
|
|
159
|
+
| `get_custom_field` | Fetch a single custom field definition, including its type and its option list. |
|
|
160
|
+
| `create_custom_field` | Define a new custom field on contacts, conversations, deals or companies. The type cannot be changed once created. |
|
|
161
|
+
| `list_channels` | List the connected channels (WhatsApp, Instagram, Messenger, Telegram, webchat, SMS, email, voice). |
|
|
162
|
+
| `list_snippets` | List the saved replies (snippets) of the workspace — pre-written answers the team reuses. |
|
|
163
|
+
| `list_templates` | List the approved WhatsApp message templates of a channel. Required to write to a contact outside the 24-hour window. |
|
|
164
|
+
| `list_tags` | List the tags of the workspace, so you can attach an existing one instead of inventing a name. |
|
|
165
|
+
| `create_tag` | Create a tag in the workspace. To tag a contact you do not need this first — add_contact_tags creates missing tags itself. |
|
|
166
|
+
| `update_tag` | Rename, recolor or re-describe a tag. The change applies everywhere the tag is already attached. |
|
|
167
|
+
| `delete_tag` | Delete a tag and detach it from every record that carried it. Irreversible. |
|
|
168
|
+
|
|
169
|
+
### Automation, broadcasts & reports (10)
|
|
170
|
+
|
|
171
|
+
| Tool | What it does |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `list_workflows` | List the automation workflows of the workspace, with whether each one is currently active. |
|
|
174
|
+
| `trigger_workflow` | Run a workflow now. Whatever you pass as `context` is the trigger data its nodes read. |
|
|
175
|
+
| `set_workflow_active` | Activate or deactivate a workflow. A workflow with no trigger node and at least one action cannot be activated. |
|
|
176
|
+
| `list_broadcasts` | List the broadcast campaigns of the workspace with their status and delivery counters. |
|
|
177
|
+
| `create_broadcast` | Create a broadcast campaign. WhatsApp broadcasts must use an approved template; SMS and email carry their own body. |
|
|
178
|
+
| `cancel_broadcast` | Cancel a scheduled or running broadcast. Messages already delivered are not recalled. |
|
|
179
|
+
| `register_webhook` | Subscribe an HTTPS endpoint to workspace events. Creating one requires a workspace admin, and the URL must be https. |
|
|
180
|
+
| `list_webhooks` | List the webhook subscriptions of the workspace and the events each one is signed up for. |
|
|
181
|
+
| `list_workspaces` | List the workspaces this token can reach. Useful first call when the agent serves more than one. |
|
|
182
|
+
| `get_report` | Read an analytics report over a date range — volume, response times, per-agent performance or an hour-by-hour heatmap. |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Comparison vs respond.io
|
|
187
|
+
|
|
188
|
+
| Capability | Omnifox MCP | respond.io MCP |
|
|
189
|
+
| --------------------------------- | ---------------------- | ---------------------- |
|
|
190
|
+
| **Total tools** | **48** | 28 |
|
|
191
|
+
| Contacts CRUD + tags + merge | 11 | 11 |
|
|
192
|
+
| Messaging (send / get / list) | 3 | 3 |
|
|
193
|
+
| Conversation assign + status | 2 | 2 |
|
|
194
|
+
| Internal comments | 1 | 1 |
|
|
195
|
+
| Workspace (users, fields, tags) | 11 | 11 |
|
|
196
|
+
| Workflow / automation triggers | yes (`trigger_workflow`) | no |
|
|
197
|
+
| Broadcasts (mass-send) | yes (`create_broadcast`/`cancel_broadcast`) | no |
|
|
198
|
+
| Lifecycle stages (list + create) | yes | no |
|
|
199
|
+
| Snippets (canned responses) | yes | no |
|
|
200
|
+
| Channel profile update | yes | no |
|
|
201
|
+
| Webhook self-registration | yes (`register_webhook`) | no |
|
|
202
|
+
| Multi-workspace listing | yes (`list_workspaces`) | no |
|
|
203
|
+
| Transports | stdio + Streamable HTTP | stdio |
|
|
204
|
+
| Auth | Sanctum bearer | API key |
|
|
205
|
+
| Rate-limit aware retry | yes (`Retry-After`) | partial |
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Programmatic usage
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import { buildServer, OmnifoxClient } from "@omnifox/mcp-server";
|
|
213
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
214
|
+
|
|
215
|
+
const client = new OmnifoxClient({ apiKey: process.env.OMNIFOX_API_KEY });
|
|
216
|
+
const server = buildServer({ client });
|
|
217
|
+
await server.connect(new StdioServerTransport());
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`OmnifoxClient` is also usable standalone as a typed REST client with retry/backoff/error envelopes.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Development
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
git clone https://github.com/omnifox-io/omnifox
|
|
228
|
+
cd packages/mcp-server
|
|
229
|
+
npm install
|
|
230
|
+
npm run typecheck # tsc --noEmit
|
|
231
|
+
npm run build # emits dist/
|
|
232
|
+
npm test # node --test
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
To smoke-test the stdio server locally:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
OMNIFOX_API_KEY=... npx tsx src/index.ts
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
To smoke-test HTTP:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
MCP_SERVER_MODE=http OMNIFOX_API_KEY=... npx tsx src/index.ts
|
|
245
|
+
curl http://localhost:8080/health
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT (c) Omnifox.io
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bin entry — picks transport based on MCP_SERVER_MODE.
|
|
4
|
+
* - "stdio" (default): one server bound to stdin/stdout.
|
|
5
|
+
* - "http": express server, factory creates one McpServer per request.
|
|
6
|
+
*/
|
|
7
|
+
export { buildServer } from "./server.js";
|
|
8
|
+
export { OmnifoxClient, OmnifoxApiError } from "./omnifox/client.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AA8BH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Bin entry — picks transport based on MCP_SERVER_MODE.
|
|
4
|
+
* - "stdio" (default): one server bound to stdin/stdout.
|
|
5
|
+
* - "http": express server, factory creates one McpServer per request.
|
|
6
|
+
*/
|
|
7
|
+
import { buildServer } from "./server.js";
|
|
8
|
+
import { startStdio } from "./transport/stdio.js";
|
|
9
|
+
import { startHttp } from "./transport/http.js";
|
|
10
|
+
async function main() {
|
|
11
|
+
const mode = (process.env.MCP_SERVER_MODE ?? "stdio").toLowerCase();
|
|
12
|
+
if (mode === "http") {
|
|
13
|
+
await startHttp(() => buildServer());
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (mode !== "stdio") {
|
|
17
|
+
process.stderr.write(`[omnifox-mcp-server] unknown MCP_SERVER_MODE='${mode}', falling back to stdio.\n`);
|
|
18
|
+
}
|
|
19
|
+
const server = buildServer();
|
|
20
|
+
await startStdio(server);
|
|
21
|
+
}
|
|
22
|
+
main().catch((err) => {
|
|
23
|
+
const msg = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
24
|
+
process.stderr.write(`[omnifox-mcp-server] fatal: ${msg}\n`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
});
|
|
27
|
+
export { buildServer } from "./server.js";
|
|
28
|
+
export { OmnifoxClient, OmnifoxApiError } from "./omnifox/client.js";
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAEpE,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,SAAS,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IAED,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iDAAiD,IAAI,6BAA6B,CACnF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;IAC7B,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,GAAG,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Omnifox V1 REST client — a thin fetch wrapper used by every MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Bearer-token auth via OMNIFOX_API_KEY.
|
|
6
|
+
* - Cursor pagination helpers.
|
|
7
|
+
* - Retry on 429 (respecting Retry-After) and on transient 5xx, up to N attempts.
|
|
8
|
+
* - Typed error envelopes via OmnifoxApiError.
|
|
9
|
+
*/
|
|
10
|
+
import type { OmnifoxErrorEnvelope, OmnifoxPaginatedResponse, OmnifoxResource, RequestOptions } from "./types.js";
|
|
11
|
+
/** Custom error carrying the canonical Omnifox error envelope. */
|
|
12
|
+
export declare class OmnifoxApiError extends Error {
|
|
13
|
+
readonly status: number;
|
|
14
|
+
readonly code: string;
|
|
15
|
+
readonly details?: unknown;
|
|
16
|
+
readonly traceId?: string;
|
|
17
|
+
constructor(status: number, envelope: OmnifoxErrorEnvelope);
|
|
18
|
+
}
|
|
19
|
+
export interface OmnifoxClientOptions {
|
|
20
|
+
apiKey?: string;
|
|
21
|
+
baseUrl?: string;
|
|
22
|
+
workspaceId?: string | number;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
maxRetries?: number;
|
|
25
|
+
/** Inject a custom fetch (used by tests). */
|
|
26
|
+
fetchImpl?: typeof fetch;
|
|
27
|
+
}
|
|
28
|
+
export declare class OmnifoxClient {
|
|
29
|
+
private readonly apiKey;
|
|
30
|
+
private readonly baseUrl;
|
|
31
|
+
readonly workspaceId: number | undefined;
|
|
32
|
+
private readonly timeoutMs;
|
|
33
|
+
private readonly maxRetries;
|
|
34
|
+
private readonly fetchImpl;
|
|
35
|
+
constructor(opts?: OmnifoxClientOptions);
|
|
36
|
+
/** Add `workspace_id` unless the caller set one explicitly. */
|
|
37
|
+
private withWorkspace;
|
|
38
|
+
/** Build a fully-qualified URL with optional query string. */
|
|
39
|
+
private buildUrl;
|
|
40
|
+
/**
|
|
41
|
+
* Default headers.
|
|
42
|
+
*
|
|
43
|
+
* There is deliberately no `X-Workspace-Id` here: the API never reads that
|
|
44
|
+
* header. It resolves the workspace from the token (tokens minted in the
|
|
45
|
+
* API-keys screen are pinned to one) and otherwise from the `workspace_id`
|
|
46
|
+
* parameter, which {@link buildUrl} adds to every request.
|
|
47
|
+
*/
|
|
48
|
+
private buildHeaders;
|
|
49
|
+
/** Core request method with retry + abort support. */
|
|
50
|
+
request<T>(opts: RequestOptions): Promise<T>;
|
|
51
|
+
get<T>(path: string, query?: Record<string, unknown>): Promise<T>;
|
|
52
|
+
post<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
|
|
53
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
54
|
+
put<T>(path: string, body?: unknown): Promise<T>;
|
|
55
|
+
/**
|
|
56
|
+
* DELETE, optionally with a body — `DELETE /contacts/{id}/tags-by-name`
|
|
57
|
+
* carries the tag names to detach, so a body-less delete is not enough.
|
|
58
|
+
*/
|
|
59
|
+
delete<T>(path: string, body?: unknown, query?: Record<string, unknown>): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Build the cursor-pagination query block accepted by every list endpoint.
|
|
62
|
+
* Pass through additional filters via `extra`.
|
|
63
|
+
*/
|
|
64
|
+
buildListQuery(opts?: {
|
|
65
|
+
limit?: number;
|
|
66
|
+
cursorId?: string;
|
|
67
|
+
page?: number;
|
|
68
|
+
search?: string;
|
|
69
|
+
sort?: string;
|
|
70
|
+
extra?: Record<string, unknown>;
|
|
71
|
+
}): Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
export type { OmnifoxPaginatedResponse, OmnifoxResource };
|
|
74
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/omnifox/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,kEAAkE;AAClE,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC;gBAErB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB;CAQ3D;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAUD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,SAAgB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,IAAI,GAAE,oBAAyB;IAoB3C,+DAA+D;IAC/D,OAAO,CAAC,aAAa;IAUrB,8DAA8D;IAC9D,OAAO,CAAC,QAAQ;IAmBhB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IASpB,sDAAsD;IACzC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IA0DlD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAGjE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAGlF,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAGlD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAGvD;;;OAGG;IACI,MAAM,CAAC,CAAC,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,CAAC,CAAC;IAMb;;;OAGG;IACI,cAAc,CAAC,IAAI,GAAE;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC5B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAoBjC;AAmED,YAAY,EAAE,wBAAwB,EAAE,eAAe,EAAE,CAAC"}
|