@norbix.ai/mcp 0.0.0-stage → 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/README.md +283 -2
- package/dist/client.js +70 -0
- package/dist/http.js +62 -0
- package/dist/index.js +70 -0
- package/dist/manifest/dtos.api.json +4624 -0
- package/dist/manifest/dtos.hub.json +28142 -0
- package/dist/manifest/endpoints.json +7783 -0
- package/dist/manifest/manifest/dtos.api.json +4624 -0
- package/dist/manifest/manifest/dtos.hub.json +28142 -0
- package/dist/manifest/manifest/endpoints.json +7783 -0
- package/dist/manifest.js +94 -0
- package/dist/server.js +281 -0
- package/package.json +65 -4
package/README.md
CHANGED
|
@@ -1,3 +1,284 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Norbix MCP Server (`@norbix.ai/mcp`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Work with your Norbix backend from any AI tool that speaks MCP — Claude
|
|
4
|
+
Desktop, Claude Code, Cursor, VS Code, Windsurf, and others. Create projects,
|
|
5
|
+
manage integrations, query databases, manage users, read logs — everything
|
|
6
|
+
your API key is allowed to do.
|
|
7
|
+
|
|
8
|
+
Built on the official [`@norbix.ai/ts`](../norbix-js) SDK. Covers **every**
|
|
9
|
+
Norbix Hub and API endpoint (640+) through a small tool set.
|
|
10
|
+
|
|
11
|
+
## What you need
|
|
12
|
+
|
|
13
|
+
1. **A Norbix API key (service user).**
|
|
14
|
+
In the Norbix dashboard, open your project → *Membership* → *Service
|
|
15
|
+
users*. Create a service user, give it a role (for example *Project
|
|
16
|
+
Administrator* for full project access), and issue an API key.
|
|
17
|
+
The key's permissions decide what the MCP server can do.
|
|
18
|
+
2. **Your Norbix Hub endpoint** (for example `https://hub.norbix.ai`, or
|
|
19
|
+
your own host on a self-hosted install). The server calls `/echo` on
|
|
20
|
+
startup to discover the rest (API URL, versions, regions).
|
|
21
|
+
|
|
22
|
+
## Configuration (environment variables)
|
|
23
|
+
|
|
24
|
+
| Variable | Required | Meaning |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `NORBIX_API_KEY` | yes | Service-user API key (sent as `Authorization: Bearer`). |
|
|
27
|
+
| `NORBIX_HUB_URL` | yes | Norbix Hub endpoint. |
|
|
28
|
+
| `NORBIX_PROJECT_ID` | no | Default project to work in. You can also switch at any time with the `norbix_set_project` tool. |
|
|
29
|
+
| `NORBIX_API_URL` | no | API endpoint. Discovered from `/echo` when not set. |
|
|
30
|
+
| `NORBIX_ENV` | no | Project environment (e.g. `DEV`). Default is `PROD`. |
|
|
31
|
+
| `NORBIX_REGION` | no | Region code (e.g. `nb-eu-germany`). |
|
|
32
|
+
|
|
33
|
+
These are the same variables the `@norbix.ai/ts` SDK uses.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
The server is published on npm as
|
|
38
|
+
[`@norbix.ai/mcp`](https://www.npmjs.com/package/@norbix.ai/mcp). Nothing to
|
|
39
|
+
install by hand — MCP clients start it with `npx` (Node.js 18 or newer):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx -y @norbix.ai/mcp --help # usage and env variables
|
|
43
|
+
npx -y @norbix.ai/mcp --version
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Or install it globally and use the `norbix-mcp` command:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install -g @norbix.ai/mcp
|
|
50
|
+
norbix-mcp --help
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Build from source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone git@github.com:norbix-code/mcp.git && cd mcp
|
|
57
|
+
npm ci
|
|
58
|
+
npm run build
|
|
59
|
+
node dist/index.js --help
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## IDE / client setup
|
|
63
|
+
|
|
64
|
+
### Claude Desktop
|
|
65
|
+
|
|
66
|
+
`claude_desktop_config.json` → `mcpServers`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"norbix": {
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["-y", "@norbix.ai/mcp"],
|
|
74
|
+
"env": {
|
|
75
|
+
"NORBIX_API_KEY": "your-api-key",
|
|
76
|
+
"NORBIX_HUB_URL": "https://hub.norbix.ai",
|
|
77
|
+
"NORBIX_PROJECT_ID": "your-project-id"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Claude Code
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
claude mcp add norbix \
|
|
88
|
+
-e NORBIX_API_KEY=your-api-key \
|
|
89
|
+
-e NORBIX_HUB_URL=https://hub.norbix.ai \
|
|
90
|
+
-e NORBIX_PROJECT_ID=your-project-id \
|
|
91
|
+
-- npx -y @norbix.ai/mcp
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Cursor
|
|
95
|
+
|
|
96
|
+
`.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"norbix": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": ["-y", "@norbix.ai/mcp"],
|
|
104
|
+
"env": {
|
|
105
|
+
"NORBIX_API_KEY": "your-api-key",
|
|
106
|
+
"NORBIX_HUB_URL": "https://hub.norbix.ai",
|
|
107
|
+
"NORBIX_PROJECT_ID": "your-project-id"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### VS Code (GitHub Copilot agent mode)
|
|
115
|
+
|
|
116
|
+
`.vscode/mcp.json`:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"servers": {
|
|
121
|
+
"norbix": {
|
|
122
|
+
"type": "stdio",
|
|
123
|
+
"command": "npx",
|
|
124
|
+
"args": ["-y", "@norbix.ai/mcp"],
|
|
125
|
+
"env": {
|
|
126
|
+
"NORBIX_API_KEY": "your-api-key",
|
|
127
|
+
"NORBIX_HUB_URL": "https://hub.norbix.ai"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Windsurf
|
|
135
|
+
|
|
136
|
+
`~/.codeium/windsurf/mcp_config.json` — same JSON shape as Cursor.
|
|
137
|
+
|
|
138
|
+
> Until the package is published, replace `"command": "npx", "args": [...]`
|
|
139
|
+
> with `"command": "node", "args": ["/path/to/sdks/norbix-mcp/dist/index.js"]`.
|
|
140
|
+
|
|
141
|
+
## Remote mode (Streamable HTTP)
|
|
142
|
+
|
|
143
|
+
For a hosted, shared MCP endpoint:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
NORBIX_HUB_URL=https://hub.norbix.ai norbix-mcp --http --port 3010
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- Endpoint: `POST http://host:3010/mcp` (stateless Streamable HTTP).
|
|
150
|
+
- Each request may carry its own key: `Authorization: Bearer <norbix-api-key>`.
|
|
151
|
+
This overrides `NORBIX_API_KEY`, so one instance can serve many users.
|
|
152
|
+
- Health check: `GET /healthz`.
|
|
153
|
+
|
|
154
|
+
## Tools
|
|
155
|
+
|
|
156
|
+
**Discovery + generic call — reaches every endpoint:**
|
|
157
|
+
|
|
158
|
+
- `norbix_search_endpoints` — find endpoints by keyword (`"push template"`, `"invite user"`, …)
|
|
159
|
+
- `norbix_describe_endpoint` — full request/response field shapes, nested types, enums, and the known subtypes of a polymorphic field
|
|
160
|
+
- `norbix_call_endpoint` — execute any endpoint by name with a JSON body
|
|
161
|
+
- `norbix_set_project` — set/switch the active project
|
|
162
|
+
|
|
163
|
+
**Curated shortcuts:** `norbix_echo`, `norbix_get_projects`, `norbix_get_project`,
|
|
164
|
+
`norbix_create_project`, `norbix_get_project_environments`,
|
|
165
|
+
`norbix_get_integrations`, `norbix_get_integration`, `norbix_save_integration`,
|
|
166
|
+
`norbix_test_integration`, `norbix_get_marketplace_integrations`,
|
|
167
|
+
`norbix_save_marketplace_integration`, `norbix_get_database_schemas`,
|
|
168
|
+
`norbix_find_documents`, `norbix_insert_document`, `norbix_update_document`,
|
|
169
|
+
`norbix_get_taxonomy_terms`, `norbix_get_roles`, `norbix_get_policies`,
|
|
170
|
+
`norbix_get_logs`, `norbix_list_service_api_keys`.
|
|
171
|
+
|
|
172
|
+
Typical flow the model follows for anything not curated:
|
|
173
|
+
`norbix_search_endpoints("archive email template")` →
|
|
174
|
+
`norbix_describe_endpoint("archiveEmailTemplate")` →
|
|
175
|
+
`norbix_call_endpoint({ name: "archiveEmailTemplate", body: { id: "…" } })`.
|
|
176
|
+
|
|
177
|
+
A polymorphic body lists its subtypes. Example — a scheduled task (only
|
|
178
|
+
`EmailCampaign` runs today; `cron` is 5 fields in UTC; `initiatorUserId` is
|
|
179
|
+
you or a project service user): `norbix_describe_endpoint("saveSchedulerTask")`
|
|
180
|
+
shows `types.SchedulerTaskRequest.subtypes.EmailCampaignSchedulerTaskRequest`
|
|
181
|
+
(`type`, `campaign?`, `databaseIntegrationId?`), then
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{ "name": "saveSchedulerTask", "body": {
|
|
185
|
+
"name": "Weekly digest", "cron": "0 9 * * 1", "initiatorUserId": "usr_…",
|
|
186
|
+
"isEnabled": true, "stopOnError": false,
|
|
187
|
+
"task": { "type": "EmailCampaign",
|
|
188
|
+
"campaign": { "source": "AllUsers", "templateId": "tmpl_…" } } } }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The scheduler module switch (`enableScheduler` / `disableScheduler`) is `PUT`.
|
|
192
|
+
|
|
193
|
+
## Database tools
|
|
194
|
+
|
|
195
|
+
Five shortcuts cover the everyday database work. Everything else in the
|
|
196
|
+
Database module (95 endpoints: schemas and drafts, records, taxonomies and
|
|
197
|
+
terms, triggers, aggregates, database integrations) is reached with the
|
|
198
|
+
generic trio.
|
|
199
|
+
|
|
200
|
+
| Tool | SDK method | Route | Input |
|
|
201
|
+
| --- | --- | --- | --- |
|
|
202
|
+
| `norbix_get_database_schemas` | `hub.database.getDatabaseSchemas` | `GET /{version}/database/schemas` | `body` (paging) |
|
|
203
|
+
| `norbix_find_documents` | `api.database.find` | `GET /{version}/database/collections/{collectionName}` | `body`: `collectionName`, `filter` (JSON string), `sortBy`, `pageSize`, … |
|
|
204
|
+
| `norbix_insert_document` | `api.database.insertOne` | `POST /{version}/database/collections/{collectionName}` | `collectionName`, `document`, `databaseIntegrationId?` |
|
|
205
|
+
| `norbix_update_document` | `api.database.updateOne` | `PUT /{version}/database/collections/{collectionName}/{id}` | `collectionName`, `id`, `update`, `databaseIntegrationId?` |
|
|
206
|
+
| `norbix_get_taxonomy_terms` | `api.database.findTerms` | `GET /{version}/database/taxonomies/{taxonomyName}/terms` | `taxonomyName`, `filter?`, `pageSize?`, `sortDescending?`, `databaseIntegrationId?` |
|
|
207
|
+
|
|
208
|
+
The Api record endpoints expect the document, the update and the filter as a
|
|
209
|
+
JSON **string**. The three newer shortcuts accept an object or a string and
|
|
210
|
+
send the string, so this works in one call:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{ "name": "norbix_insert_document", "arguments": {
|
|
214
|
+
"collectionName": "products", "document": { "title": "Lamp", "price": 12 } } }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{ "name": "norbix_update_document", "arguments": {
|
|
219
|
+
"collectionName": "products", "id": "…", "update": { "price": 10 } } }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`update` is the plain field document — the gateway applies it with `$set`.
|
|
223
|
+
A `$` operator in it (`$set`, `$inc`, …) is refused with
|
|
224
|
+
`CM-ERRORS-DATABASE-035`. An update-many or delete-many call with an empty
|
|
225
|
+
filter `{}` is refused (`CM-ERRORS-DATABASE-037`) unless the body also has
|
|
226
|
+
`allRecords: true`.
|
|
227
|
+
|
|
228
|
+
For anything else, search the `database` group:
|
|
229
|
+
|
|
230
|
+
- `norbix_search_endpoints({ query: "taxonomy term", group: "database" })`
|
|
231
|
+
- `norbix_describe_endpoint({ name: "saveSchemaTrigger" })`
|
|
232
|
+
- `norbix_call_endpoint({ name: "getDatabaseSchemas", surface: "api" })` —
|
|
233
|
+
a few names exist on both the Hub (admin plane) and the Api (data plane);
|
|
234
|
+
pass `surface` for those.
|
|
235
|
+
|
|
236
|
+
Which tools work depends on the API key's role: the Hub tools need a
|
|
237
|
+
project role with database permissions, the Api tools need permissions on the
|
|
238
|
+
collection. An error answer carries `status`, `code`, `errors` (per field)
|
|
239
|
+
and `payload` (the gateway's body).
|
|
240
|
+
|
|
241
|
+
## Keeping the endpoint manifest in sync
|
|
242
|
+
|
|
243
|
+
The tool catalog is generated from the `norbix-js` SDK source and committed
|
|
244
|
+
under `src/manifest/`. After endpoints change (backend regenerates DTOs →
|
|
245
|
+
`norbix-js` runs `npm run generate-endpoints`):
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
npm run generate-manifest && npm run build && npm test
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`generate-manifest` reads `../norbix-js`; from a worktree, point it at a
|
|
252
|
+
checkout of the released SDK: `NORBIX_JS_DIR=/path/to/norbix-js npm run generate-manifest`.
|
|
253
|
+
Keep `@norbix.ai/ts` in `package.json` at the version the manifest was made
|
|
254
|
+
from (a test checks every curated tool is in the manifest).
|
|
255
|
+
|
|
256
|
+
## Tests
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
npm test
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Vitest, no network: the server runs over the MCP SDK's in-memory transport
|
|
263
|
+
with a fake Norbix client (or the real `@norbix.ai/ts` client with a fake
|
|
264
|
+
`fetch`).
|
|
265
|
+
|
|
266
|
+
## Versioning and releases
|
|
267
|
+
|
|
268
|
+
Every merge to `main` is released to npm by
|
|
269
|
+
[semantic-release](https://semantic-release.gitbook.io/) from the
|
|
270
|
+
conventional-commit messages: `feat:` → minor, `fix:` / `perf:` /
|
|
271
|
+
`refactor:` → patch; `ci:`, `build:`, `test:`, `chore:` do not release.
|
|
272
|
+
**The major version is frozen until the public launch**: a breaking change
|
|
273
|
+
is written as `feat(...)` with a `Breaking: <what>` line in the body (a
|
|
274
|
+
`!:` title or a `BREAKING CHANGE:` footer fails the PR check). The git tag
|
|
275
|
+
is the source of truth for the version; release notes are on the GitHub
|
|
276
|
+
Releases page.
|
|
277
|
+
|
|
278
|
+
## Security notes
|
|
279
|
+
|
|
280
|
+
- The server can do exactly what the API key can do — nothing more. Scope the
|
|
281
|
+
service user's role to what you need (e.g. read-only roles for analysis).
|
|
282
|
+
- Never commit API keys. Use env vars in the MCP client config.
|
|
283
|
+
- In `--http` mode, put the server behind TLS and network controls; the
|
|
284
|
+
`Authorization` header carries the Norbix API key.
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Norbix client factory. Configuration comes from NORBIX_* env vars
|
|
3
|
+
* (NORBIX_API_KEY, NORBIX_HUB_URL, NORBIX_PROJECT_ID, ...) — the same
|
|
4
|
+
* variables the @norbix.ai/ts SDK reads. On startup we call the Hub /echo
|
|
5
|
+
* endpoint to discover the rest of the deployment (API URL, regions,
|
|
6
|
+
* versions) so users only need an API key and the Hub endpoint.
|
|
7
|
+
*/
|
|
8
|
+
import { Norbix } from '@norbix.ai/ts';
|
|
9
|
+
export function requireConfig() {
|
|
10
|
+
const missing = ['NORBIX_API_KEY', 'NORBIX_HUB_URL'].filter((k) => !process.env[k]);
|
|
11
|
+
if (missing.length > 0) {
|
|
12
|
+
throw new Error(`Missing required environment variable(s): ${missing.join(', ')}. ` +
|
|
13
|
+
'Create a service-user API key in the Norbix dashboard ' +
|
|
14
|
+
'(Membership → Service users) and set NORBIX_HUB_URL to your Hub endpoint.');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The SDK constructor requires `projectId`, but account-level work (e.g.
|
|
19
|
+
* creating the very first project) has none. The transport only sends the
|
|
20
|
+
* project headers when `cfg.projectId` is set, so when no NORBIX_PROJECT_ID
|
|
21
|
+
* is configured we construct with a placeholder and clear it. Use the
|
|
22
|
+
* `norbix_set_project` tool (client.setScope) to pick a project later.
|
|
23
|
+
*/
|
|
24
|
+
function newNorbix(config = {}) {
|
|
25
|
+
const hasProject = Boolean(process.env.NORBIX_PROJECT_ID);
|
|
26
|
+
const client = new Norbix({
|
|
27
|
+
...config,
|
|
28
|
+
...(hasProject ? {} : { projectId: 'unscoped' }),
|
|
29
|
+
});
|
|
30
|
+
if (!hasProject) {
|
|
31
|
+
client.cfg.projectId =
|
|
32
|
+
undefined;
|
|
33
|
+
}
|
|
34
|
+
return client;
|
|
35
|
+
}
|
|
36
|
+
export async function createClient(overrides = {}) {
|
|
37
|
+
const base = newNorbix(overrides.apiKey ? { apiKey: overrides.apiKey } : {});
|
|
38
|
+
let echo;
|
|
39
|
+
try {
|
|
40
|
+
echo = (await base.hub.echo.echo({}));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// /echo is best-effort discovery; tools still work against the Hub.
|
|
44
|
+
}
|
|
45
|
+
// If the API base URL was not configured explicitly, adopt the one the
|
|
46
|
+
// deployment reports. A user-supplied NORBIX_API_URL always wins.
|
|
47
|
+
const discoveredApiUrl = !process.env.NORBIX_API_URL && typeof echo?.apiUrl === 'string'
|
|
48
|
+
? echo.apiUrl
|
|
49
|
+
: undefined;
|
|
50
|
+
const client = discoveredApiUrl
|
|
51
|
+
? newNorbix({
|
|
52
|
+
...(overrides.apiKey ? { apiKey: overrides.apiKey } : {}),
|
|
53
|
+
apiUrl: discoveredApiUrl,
|
|
54
|
+
})
|
|
55
|
+
: base;
|
|
56
|
+
return { client, echo };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Dispatch a call to a generated SDK endpoint method:
|
|
60
|
+
* client.hub.<group>.<method>(body) / client.api.<group>.<method>(body).
|
|
61
|
+
*/
|
|
62
|
+
export async function invoke(client, surface, group, method, body) {
|
|
63
|
+
const ns = client[surface];
|
|
64
|
+
const mod = ns?.[group];
|
|
65
|
+
const fn = mod?.[method];
|
|
66
|
+
if (typeof fn !== 'function') {
|
|
67
|
+
throw new Error(`Unknown endpoint: ${surface}.${group}.${method}`);
|
|
68
|
+
}
|
|
69
|
+
return fn(body ?? {});
|
|
70
|
+
}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streamable HTTP transport (remote MCP). Stateless: every POST /mcp gets a
|
|
3
|
+
* fresh server + transport, so the endpoint scales horizontally and needs no
|
|
4
|
+
* session store.
|
|
5
|
+
*
|
|
6
|
+
* Auth: `Authorization: Bearer <norbix-api-key>` on each request wins over
|
|
7
|
+
* the NORBIX_API_KEY env var. This lets one hosted instance serve many users,
|
|
8
|
+
* each with their own service-user key.
|
|
9
|
+
*/
|
|
10
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
11
|
+
import express from 'express';
|
|
12
|
+
import { createClient } from './client.js';
|
|
13
|
+
import { buildServer } from './server.js';
|
|
14
|
+
export async function startHttp(port) {
|
|
15
|
+
const app = express();
|
|
16
|
+
app.use(express.json({ limit: '4mb' }));
|
|
17
|
+
app.get('/healthz', (_req, res) => {
|
|
18
|
+
res.json({ ok: true, name: 'norbix-mcp' });
|
|
19
|
+
});
|
|
20
|
+
app.post('/mcp', async (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const auth = req.headers.authorization;
|
|
23
|
+
const apiKey = auth?.replace(/^Bearer\s+/i, '').trim() || undefined;
|
|
24
|
+
const handle = await createClient(apiKey ? { apiKey } : {});
|
|
25
|
+
const server = buildServer(handle);
|
|
26
|
+
const transport = new StreamableHTTPServerTransport({
|
|
27
|
+
sessionIdGenerator: undefined,
|
|
28
|
+
});
|
|
29
|
+
res.on('close', () => {
|
|
30
|
+
void transport.close();
|
|
31
|
+
void server.close();
|
|
32
|
+
});
|
|
33
|
+
await server.connect(transport);
|
|
34
|
+
await transport.handleRequest(req, res, req.body);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
if (!res.headersSent) {
|
|
38
|
+
res.status(500).json({
|
|
39
|
+
jsonrpc: '2.0',
|
|
40
|
+
error: { code: -32603, message: e.message },
|
|
41
|
+
id: null,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// Stateless mode: no server-initiated streams, no sessions to delete.
|
|
47
|
+
const reject = (_req, res) => {
|
|
48
|
+
res.status(405).json({
|
|
49
|
+
jsonrpc: '2.0',
|
|
50
|
+
error: { code: -32000, message: 'Method not allowed (stateless server).' },
|
|
51
|
+
id: null,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
app.get('/mcp', reject);
|
|
55
|
+
app.delete('/mcp', reject);
|
|
56
|
+
await new Promise((resolve) => {
|
|
57
|
+
app.listen(port, () => {
|
|
58
|
+
console.error(`norbix-mcp listening on http://localhost:${port}/mcp`);
|
|
59
|
+
resolve();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Norbix MCP server.
|
|
4
|
+
*
|
|
5
|
+
* norbix-mcp stdio transport (Claude, Cursor, VS Code, …)
|
|
6
|
+
* norbix-mcp --http Streamable HTTP on port 3010 (remote MCP)
|
|
7
|
+
* norbix-mcp --http --port N Streamable HTTP on port N
|
|
8
|
+
*
|
|
9
|
+
* Required env: NORBIX_API_KEY, NORBIX_HUB_URL.
|
|
10
|
+
* Optional env: NORBIX_PROJECT_ID, NORBIX_ACCOUNT_ID, NORBIX_API_URL,
|
|
11
|
+
* NORBIX_ENV, NORBIX_REGION (same vars as the @norbix.ai/ts SDK).
|
|
12
|
+
*/
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
15
|
+
import { createClient, requireConfig } from './client.js';
|
|
16
|
+
import { startHttp } from './http.js';
|
|
17
|
+
import { buildServer } from './server.js';
|
|
18
|
+
const HELP = `Norbix MCP server
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
norbix-mcp stdio transport (Claude, Cursor, VS Code, ...)
|
|
22
|
+
norbix-mcp --http [--port N] Streamable HTTP (default port 3010)
|
|
23
|
+
norbix-mcp --help show this text
|
|
24
|
+
norbix-mcp --version show the version
|
|
25
|
+
|
|
26
|
+
Required env: NORBIX_API_KEY, NORBIX_HUB_URL
|
|
27
|
+
Optional env: NORBIX_PROJECT_ID, NORBIX_ACCOUNT_ID, NORBIX_API_URL,
|
|
28
|
+
NORBIX_ENV, NORBIX_REGION
|
|
29
|
+
|
|
30
|
+
Docs: https://github.com/norbix-code/mcp#readme`;
|
|
31
|
+
function packageVersion() {
|
|
32
|
+
// dist/index.js -> ../package.json (npm always ships package.json).
|
|
33
|
+
const pkg = createRequire(import.meta.url)('../package.json');
|
|
34
|
+
return pkg.version ?? 'unknown';
|
|
35
|
+
}
|
|
36
|
+
async function main() {
|
|
37
|
+
const args = process.argv.slice(2);
|
|
38
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
39
|
+
console.log(HELP);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
43
|
+
console.log(packageVersion());
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const http = args.includes('--http');
|
|
47
|
+
const portIdx = args.indexOf('--port');
|
|
48
|
+
const port = portIdx >= 0 ? Number(args[portIdx + 1]) : 3010;
|
|
49
|
+
if (http) {
|
|
50
|
+
// In HTTP mode the key may arrive per request; env config is a fallback.
|
|
51
|
+
await startHttp(Number.isFinite(port) ? port : 3010);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
requireConfig();
|
|
55
|
+
const handle = await createClient();
|
|
56
|
+
if (handle.echo) {
|
|
57
|
+
console.error(`norbix-mcp: connected to ${process.env.NORBIX_HUB_URL} ` +
|
|
58
|
+
`(hub ${String(handle.echo.hubVersion ?? '?')}, api ${String(handle.echo.apiVersion ?? '?')})`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.error('norbix-mcp: warning — /echo discovery failed; check NORBIX_HUB_URL and network. Tools will still try to call the Hub.');
|
|
62
|
+
}
|
|
63
|
+
const server = buildServer(handle);
|
|
64
|
+
await server.connect(new StdioServerTransport());
|
|
65
|
+
console.error('norbix-mcp: ready (stdio)');
|
|
66
|
+
}
|
|
67
|
+
main().catch((e) => {
|
|
68
|
+
console.error(`norbix-mcp: fatal — ${e.message}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|