@novorb/mcp-core 2.1.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/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/http-client.d.ts +69 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +228 -0
- package/dist/http-client.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +80 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +236 -0
- package/dist/logger.js.map +1 -0
- package/dist/pagination.d.ts +45 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +46 -0
- package/dist/pagination.js.map +1 -0
- package/dist/retry.d.ts +51 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +94 -0
- package/dist/retry.js.map +1 -0
- package/dist/server.d.ts +80 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +73 -0
- package/dist/server.js.map +1 -0
- package/dist/transport.d.ts +102 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +213 -0
- package/dist/transport.js.map +1 -0
- package/package.json +86 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Novorb
|
|
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,179 @@
|
|
|
1
|
+
# NOVORB - MCP Core
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@novorb/mcp-core)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[]()
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
Shared TypeScript utilities for building MCP (Model Context Protocol) servers — logging, HTTP clients, retry logic, pagination, and transport — so every MCP server gets a consistent, tested foundation without reimplementing common patterns.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Structured logging** — file output with rotation, stderr output, text or JSON format, secret redaction
|
|
15
|
+
- **HTTP client** — axios wrapper with automatic retry on 429/5xx, request logging, pagination
|
|
16
|
+
- **Retry with backoff** — exponential backoff with jitter, configurable retries and delays
|
|
17
|
+
- **Pagination helpers** — normalize params, compute metadata, paginate arrays in memory
|
|
18
|
+
- **Unified transport** — stdio and stateless streamable-HTTP with built-in auth and DNS-rebinding protection
|
|
19
|
+
- **Server builder** — `McpServer` factory, annotation presets, and SDK re-exports from one import point
|
|
20
|
+
- **ESM native** — built as ES modules with full TypeScript declarations
|
|
21
|
+
|
|
22
|
+
## Documentation
|
|
23
|
+
|
|
24
|
+
- **[CLAUDE.md](CLAUDE.md)** — agent/contributor context and working conventions
|
|
25
|
+
- **[docs/TESTING.md](docs/TESTING.md)** — test runner commands, conventions, and guidelines
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @novorb/mcp-core
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Modules
|
|
34
|
+
|
|
35
|
+
### Logger
|
|
36
|
+
|
|
37
|
+
Unified logger supporting file output (with rotation) and stderr output, in text or structured JSON format. MCP servers use stdio for protocol communication, so logging to stdout/stderr directly isn't always possible — file-based logging solves this. Sensitive-keyed fields in `meta` are redacted before serialisation.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { initLogger, LogLevel } from '@novorb/mcp-core';
|
|
41
|
+
|
|
42
|
+
// File-based (writes to logs/mcp.log with rotation)
|
|
43
|
+
const logger = initLogger({
|
|
44
|
+
output: 'file',
|
|
45
|
+
logDir: './logs',
|
|
46
|
+
level: LogLevel.INFO,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Stderr with structured JSON
|
|
50
|
+
const logger = initLogger({
|
|
51
|
+
output: 'stderr',
|
|
52
|
+
format: 'json',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
logger.info('Server started', 'Server');
|
|
56
|
+
logger.error('Request failed', 'HttpClient', { status: 500, path: '/api/items' });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### HTTP Client
|
|
60
|
+
|
|
61
|
+
Pre-configured axios wrapper with automatic retry on transient failures (429, 5xx, network errors) for idempotent methods, request/response logging, and pagination support via `X-Total-Count` headers.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { createHttpClient } from '@novorb/mcp-core';
|
|
65
|
+
|
|
66
|
+
const client = createHttpClient({
|
|
67
|
+
baseURL: 'https://api.example.com',
|
|
68
|
+
headers: { 'Authorization': 'Bearer token' },
|
|
69
|
+
retries: 3,
|
|
70
|
+
logContext: 'MyApiClient',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const item = await client.get('/items/1');
|
|
74
|
+
const { data, totalCount } = await client.getPaginated('/items', { page: 1, per_page: 50 });
|
|
75
|
+
await client.post('/items', { name: 'new item' });
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Retry
|
|
79
|
+
|
|
80
|
+
Exponential backoff with jitter for retrying failed operations. Used internally by the HTTP client, but also available standalone.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { retryWithBackoff, isRetryableError } from '@novorb/mcp-core';
|
|
84
|
+
|
|
85
|
+
const result = await retryWithBackoff(async (ctx) => {
|
|
86
|
+
console.log(`Attempt ${ctx.attempt + 1}/${ctx.maxRetries + 1}`);
|
|
87
|
+
return await someUnreliableOperation();
|
|
88
|
+
}, {
|
|
89
|
+
maxRetries: 3,
|
|
90
|
+
initialDelayMs: 1000,
|
|
91
|
+
backoffMultiplier: 2,
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Pagination
|
|
96
|
+
|
|
97
|
+
Normalize pagination parameters, compute metadata, and paginate arrays in memory.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { normalizePagination, createPaginationMetadata, paginateArray } from '@novorb/mcp-core';
|
|
101
|
+
|
|
102
|
+
const params = normalizePagination({ page: 3, per_page: 200 });
|
|
103
|
+
// { page: 3, per_page: 100 } (capped at max)
|
|
104
|
+
|
|
105
|
+
const metadata = createPaginationMetadata(500, params);
|
|
106
|
+
// { totalCount: 500, page: 3, perPage: 100, totalPages: 5 }
|
|
107
|
+
|
|
108
|
+
const { data, metadata } = paginateArray(allItems, { page: 2, per_page: 20 });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Transport
|
|
112
|
+
|
|
113
|
+
Unified transport that selects between stdio and stateless streamable HTTP based on the `MCP_TRANSPORT` environment variable. HTTP mode is fail-closed (refuses to start without auth unless explicitly disabled) and supports Bearer-token auth, body-size caps, and opt-in DNS-rebinding protection.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { startMcpServer } from '@novorb/mcp-core/transport';
|
|
117
|
+
|
|
118
|
+
// Factory (recommended for HTTP — fresh server instance per request):
|
|
119
|
+
await startMcpServer(() => createServer(services));
|
|
120
|
+
|
|
121
|
+
// Server instance (fine for stdio):
|
|
122
|
+
await startMcpServer(server);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Variable | Default | Description |
|
|
126
|
+
|----------|---------|-------------|
|
|
127
|
+
| `MCP_TRANSPORT` | `stdio` | Transport mode: `stdio` or `http` |
|
|
128
|
+
| `PORT` | `8080` | HTTP listen port |
|
|
129
|
+
| `MCP_HTTP_HOST` | `0.0.0.0` | Bind address (`127.0.0.1` for local-only) |
|
|
130
|
+
| `MCP_API_KEY` | none | Bearer token for HTTP auth (required in HTTP mode unless disabled) |
|
|
131
|
+
| `MCP_AUTH_DISABLED` | `false` | Set `true` to run HTTP without auth (only behind a fronting gateway) |
|
|
132
|
+
| `MCP_MAX_BODY_BYTES` | 4 MiB | Max accepted POST body size |
|
|
133
|
+
| `MCP_ALLOWED_HOSTS` | none | Comma-separated Host allowlist (enables DNS-rebinding protection) |
|
|
134
|
+
| `MCP_ALLOWED_ORIGINS` | none | Comma-separated Origin allowlist (enables DNS-rebinding protection) |
|
|
135
|
+
|
|
136
|
+
### Server
|
|
137
|
+
|
|
138
|
+
Import MCP server-building primitives from here instead of the SDK directly, to centralise the MCP dependency.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { createMcpServer, TOOL_ANNOTATIONS, completable } from '@novorb/mcp-core/server';
|
|
142
|
+
import type { McpToolAnnotations, McpCallToolResult } from '@novorb/mcp-core/server';
|
|
143
|
+
|
|
144
|
+
const server = createMcpServer('my-mcp', '1.0.0');
|
|
145
|
+
|
|
146
|
+
server.registerTool('lookup', {
|
|
147
|
+
description: 'Look up a value',
|
|
148
|
+
inputSchema: { key: z.string() },
|
|
149
|
+
outputSchema: { value: z.string(), found: z.boolean() },
|
|
150
|
+
annotations: TOOL_ANNOTATIONS.readOnly,
|
|
151
|
+
}, async ({ key }) => ({
|
|
152
|
+
content: [{ type: 'text', text: value }],
|
|
153
|
+
structuredContent: { value, found: true },
|
|
154
|
+
}));
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**`TOOL_ANNOTATIONS` presets:** `readOnly` (lookups/searches), `idempotentWrite` (upserts), `mutableWrite` (non-idempotent writes), `destructive` (deletes/overwrites).
|
|
158
|
+
|
|
159
|
+
## Testing
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npm test # 67 tests
|
|
163
|
+
npm run test:watch # watch mode
|
|
164
|
+
npm run test:coverage
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
> **Windows note:** the test scripts set `NODE_OPTIONS=` inline, which is Unix shell syntax. On Windows, invoke jest directly via a bash shell. See [docs/TESTING.md](docs/TESTING.md).
|
|
168
|
+
|
|
169
|
+
## Building
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm run build # tsc → dist/
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This is an ESM package (`"type": "module"`). `tsconfig.json` is self-contained so it builds standalone.
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core — HTTP Client Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates pre-configured axios instances with retry logic and logging.
|
|
5
|
+
*/
|
|
6
|
+
import { AxiosInstance } from 'axios';
|
|
7
|
+
import { Logger } from './logger.js';
|
|
8
|
+
export interface HttpClientConfig {
|
|
9
|
+
/** Base URL for all requests */
|
|
10
|
+
baseURL: string;
|
|
11
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
12
|
+
timeout?: number;
|
|
13
|
+
/** Default headers to include on every request */
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
/** Number of retries on retryable failures (default: 3) */
|
|
16
|
+
retries?: number;
|
|
17
|
+
/** Logger instance (default: singleton from getLogger) */
|
|
18
|
+
logger?: Logger;
|
|
19
|
+
/** Component name for log messages (default: 'HttpClient') */
|
|
20
|
+
logContext?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PaginatedResponse<T> {
|
|
23
|
+
data: T[];
|
|
24
|
+
totalCount: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* MCP HTTP client wrapping axios with retry, logging, and pagination support.
|
|
28
|
+
*/
|
|
29
|
+
export declare class McpHttpClient {
|
|
30
|
+
private client;
|
|
31
|
+
private logger;
|
|
32
|
+
private logContext;
|
|
33
|
+
constructor(config: HttpClientConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Perform a GET request.
|
|
36
|
+
*/
|
|
37
|
+
get<T>(path: string, params?: Record<string, unknown>): Promise<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Perform a GET request with pagination.
|
|
40
|
+
* Reads total count from the X-Total-Count response header.
|
|
41
|
+
*/
|
|
42
|
+
getPaginated<T>(path: string, params?: Record<string, unknown>): Promise<PaginatedResponse<T>>;
|
|
43
|
+
/**
|
|
44
|
+
* Perform a POST request.
|
|
45
|
+
*/
|
|
46
|
+
post<T>(path: string, data?: unknown, params?: Record<string, unknown>): Promise<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Perform a PUT request.
|
|
49
|
+
*/
|
|
50
|
+
put<T>(path: string, data?: unknown, params?: Record<string, unknown>): Promise<T>;
|
|
51
|
+
/**
|
|
52
|
+
* Perform a PATCH request.
|
|
53
|
+
*/
|
|
54
|
+
patch<T>(path: string, data?: unknown, params?: Record<string, unknown>): Promise<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Perform a DELETE request.
|
|
57
|
+
*/
|
|
58
|
+
delete<T>(path: string, params?: Record<string, unknown>): Promise<T>;
|
|
59
|
+
/**
|
|
60
|
+
* Get the underlying axios instance for advanced use cases.
|
|
61
|
+
*/
|
|
62
|
+
getAxiosInstance(): AxiosInstance;
|
|
63
|
+
private normalizeError;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a configured HTTP client.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createHttpClient(config: HttpClientConfig): McpHttpClient;
|
|
69
|
+
//# sourceMappingURL=http-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAc,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAa,MAAM,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,gBAAgB;IAmCpC;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsBxE;;;OAGG;IACG,YAAY,CAAC,CAAC,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAyBhC;;OAEG;IACG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsBzF;;OAEG;IACG,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsBxF;;OAEG;IACG,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsB1F;;OAEG;IACG,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsB3E;;OAEG;IACH,gBAAgB,IAAI,aAAa;IAIjC,OAAO,CAAC,cAAc;CAkBvB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,CAExE"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core — HTTP Client Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates pre-configured axios instances with retry logic and logging.
|
|
5
|
+
*/
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import axiosRetry from 'axios-retry';
|
|
8
|
+
import { getLogger } from './logger.js';
|
|
9
|
+
/**
|
|
10
|
+
* MCP HTTP client wrapping axios with retry, logging, and pagination support.
|
|
11
|
+
*/
|
|
12
|
+
export class McpHttpClient {
|
|
13
|
+
client;
|
|
14
|
+
logger;
|
|
15
|
+
logContext;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.logger = config.logger || getLogger();
|
|
18
|
+
this.logContext = config.logContext || 'HttpClient';
|
|
19
|
+
this.client = axios.create({
|
|
20
|
+
baseURL: config.baseURL,
|
|
21
|
+
timeout: config.timeout || 30000,
|
|
22
|
+
// Never follow redirects: the instance carries a static Authorization header, and axios re-sends
|
|
23
|
+
// request data to a 3xx Location — a malicious/compromised upstream could harvest the credential.
|
|
24
|
+
maxRedirects: 0,
|
|
25
|
+
// Cap response/request size (OOM/DoS guard).
|
|
26
|
+
maxContentLength: 25 * 1024 * 1024,
|
|
27
|
+
maxBodyLength: 25 * 1024 * 1024,
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
...config.headers,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
axiosRetry(this.client, {
|
|
34
|
+
retries: config.retries ?? 3,
|
|
35
|
+
retryDelay: axiosRetry.exponentialDelay,
|
|
36
|
+
retryCondition: (error) => {
|
|
37
|
+
// Network/idempotent-request errors are already method-safe. Only ALSO retry 429/5xx for
|
|
38
|
+
// idempotent methods — never replay a non-idempotent POST/PATCH (duplicate side effects).
|
|
39
|
+
if (axiosRetry.isNetworkOrIdempotentRequestError(error))
|
|
40
|
+
return true;
|
|
41
|
+
const method = (error.config?.method ?? 'get').toLowerCase();
|
|
42
|
+
const idempotent = ['get', 'head', 'options', 'put', 'delete'].includes(method);
|
|
43
|
+
const status = error.response?.status;
|
|
44
|
+
const retryableStatus = status === 429 || (status !== undefined && status >= 500);
|
|
45
|
+
return idempotent && retryableStatus;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Perform a GET request.
|
|
51
|
+
*/
|
|
52
|
+
async get(path, params) {
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
this.logger.debug(`GET ${path}`, this.logContext, { params });
|
|
55
|
+
try {
|
|
56
|
+
const response = await this.client.get(path, { params });
|
|
57
|
+
const duration = Date.now() - startTime;
|
|
58
|
+
this.logger.debug(`GET ${path} completed`, this.logContext, {
|
|
59
|
+
status: response.status,
|
|
60
|
+
duration: `${duration}ms`,
|
|
61
|
+
});
|
|
62
|
+
return response.data;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const duration = Date.now() - startTime;
|
|
66
|
+
this.logger.error(`GET ${path} failed`, this.logContext, {
|
|
67
|
+
duration: `${duration}ms`,
|
|
68
|
+
error: error instanceof Error ? error.message : String(error),
|
|
69
|
+
});
|
|
70
|
+
throw this.normalizeError(error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Perform a GET request with pagination.
|
|
75
|
+
* Reads total count from the X-Total-Count response header.
|
|
76
|
+
*/
|
|
77
|
+
async getPaginated(path, params) {
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
this.logger.debug(`GET (paginated) ${path}`, this.logContext, { params });
|
|
80
|
+
try {
|
|
81
|
+
const response = await this.client.get(path, { params });
|
|
82
|
+
const totalCount = parseInt(response.headers['x-total-count'] || '0', 10);
|
|
83
|
+
const duration = Date.now() - startTime;
|
|
84
|
+
this.logger.debug(`GET (paginated) ${path} completed`, this.logContext, {
|
|
85
|
+
status: response.status,
|
|
86
|
+
totalCount,
|
|
87
|
+
resultCount: response.data.length,
|
|
88
|
+
duration: `${duration}ms`,
|
|
89
|
+
});
|
|
90
|
+
return { data: response.data, totalCount };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const duration = Date.now() - startTime;
|
|
94
|
+
this.logger.error(`GET (paginated) ${path} failed`, this.logContext, {
|
|
95
|
+
duration: `${duration}ms`,
|
|
96
|
+
error: error instanceof Error ? error.message : String(error),
|
|
97
|
+
});
|
|
98
|
+
throw this.normalizeError(error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Perform a POST request.
|
|
103
|
+
*/
|
|
104
|
+
async post(path, data, params) {
|
|
105
|
+
const startTime = Date.now();
|
|
106
|
+
this.logger.debug(`POST ${path}`, this.logContext);
|
|
107
|
+
try {
|
|
108
|
+
const response = await this.client.post(path, data, { params });
|
|
109
|
+
const duration = Date.now() - startTime;
|
|
110
|
+
this.logger.debug(`POST ${path} completed`, this.logContext, {
|
|
111
|
+
status: response.status,
|
|
112
|
+
duration: `${duration}ms`,
|
|
113
|
+
});
|
|
114
|
+
return response.data;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const duration = Date.now() - startTime;
|
|
118
|
+
this.logger.error(`POST ${path} failed`, this.logContext, {
|
|
119
|
+
duration: `${duration}ms`,
|
|
120
|
+
error: error instanceof Error ? error.message : String(error),
|
|
121
|
+
});
|
|
122
|
+
throw this.normalizeError(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Perform a PUT request.
|
|
127
|
+
*/
|
|
128
|
+
async put(path, data, params) {
|
|
129
|
+
const startTime = Date.now();
|
|
130
|
+
this.logger.debug(`PUT ${path}`, this.logContext);
|
|
131
|
+
try {
|
|
132
|
+
const response = await this.client.put(path, data, { params });
|
|
133
|
+
const duration = Date.now() - startTime;
|
|
134
|
+
this.logger.debug(`PUT ${path} completed`, this.logContext, {
|
|
135
|
+
status: response.status,
|
|
136
|
+
duration: `${duration}ms`,
|
|
137
|
+
});
|
|
138
|
+
return response.data;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
const duration = Date.now() - startTime;
|
|
142
|
+
this.logger.error(`PUT ${path} failed`, this.logContext, {
|
|
143
|
+
duration: `${duration}ms`,
|
|
144
|
+
error: error instanceof Error ? error.message : String(error),
|
|
145
|
+
});
|
|
146
|
+
throw this.normalizeError(error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Perform a PATCH request.
|
|
151
|
+
*/
|
|
152
|
+
async patch(path, data, params) {
|
|
153
|
+
const startTime = Date.now();
|
|
154
|
+
this.logger.debug(`PATCH ${path}`, this.logContext);
|
|
155
|
+
try {
|
|
156
|
+
const response = await this.client.patch(path, data, { params });
|
|
157
|
+
const duration = Date.now() - startTime;
|
|
158
|
+
this.logger.debug(`PATCH ${path} completed`, this.logContext, {
|
|
159
|
+
status: response.status,
|
|
160
|
+
duration: `${duration}ms`,
|
|
161
|
+
});
|
|
162
|
+
return response.data;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
const duration = Date.now() - startTime;
|
|
166
|
+
this.logger.error(`PATCH ${path} failed`, this.logContext, {
|
|
167
|
+
duration: `${duration}ms`,
|
|
168
|
+
error: error instanceof Error ? error.message : String(error),
|
|
169
|
+
});
|
|
170
|
+
throw this.normalizeError(error);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Perform a DELETE request.
|
|
175
|
+
*/
|
|
176
|
+
async delete(path, params) {
|
|
177
|
+
const startTime = Date.now();
|
|
178
|
+
this.logger.debug(`DELETE ${path}`, this.logContext);
|
|
179
|
+
try {
|
|
180
|
+
const response = await this.client.delete(path, { params });
|
|
181
|
+
const duration = Date.now() - startTime;
|
|
182
|
+
this.logger.debug(`DELETE ${path} completed`, this.logContext, {
|
|
183
|
+
status: response.status,
|
|
184
|
+
duration: `${duration}ms`,
|
|
185
|
+
});
|
|
186
|
+
return response.data;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
const duration = Date.now() - startTime;
|
|
190
|
+
this.logger.error(`DELETE ${path} failed`, this.logContext, {
|
|
191
|
+
duration: `${duration}ms`,
|
|
192
|
+
error: error instanceof Error ? error.message : String(error),
|
|
193
|
+
});
|
|
194
|
+
throw this.normalizeError(error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get the underlying axios instance for advanced use cases.
|
|
199
|
+
*/
|
|
200
|
+
getAxiosInstance() {
|
|
201
|
+
return this.client;
|
|
202
|
+
}
|
|
203
|
+
normalizeError(error) {
|
|
204
|
+
if (axios.isAxiosError(error)) {
|
|
205
|
+
const status = error.response?.status;
|
|
206
|
+
const statusText = error.response?.statusText;
|
|
207
|
+
const data = error.response?.data;
|
|
208
|
+
const message = typeof data === 'object' && data?.message
|
|
209
|
+
? data.message
|
|
210
|
+
: `HTTP ${status} ${statusText}`;
|
|
211
|
+
const normalized = new Error(message);
|
|
212
|
+
// Attach only the status — NOT the raw axios `response`. Its `config` carries the request's
|
|
213
|
+
// Authorization header/token, and the upstream response headers may carry set-cookie/auth
|
|
214
|
+
// echoes; a consumer logging the thrown error would otherwise leak them.
|
|
215
|
+
normalized.status = status;
|
|
216
|
+
normalized.statusText = statusText;
|
|
217
|
+
return normalized;
|
|
218
|
+
}
|
|
219
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Create a configured HTTP client.
|
|
224
|
+
*/
|
|
225
|
+
export function createHttpClient(config) {
|
|
226
|
+
return new McpHttpClient(config);
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=http-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.js","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for MCP servers.
|
|
5
|
+
*/
|
|
6
|
+
export { Logger, LogLevel, initLogger, getLogger, resetLogger, type LoggerConfig, } from './logger.js';
|
|
7
|
+
export { retryWithBackoff, calculateRetryDelay, isRetryableError, sleep, parseRetryAfter, type RetryOptions, type RetryContext, } from './retry.js';
|
|
8
|
+
export { normalizePagination, createPaginationMetadata, pageToOffset, paginateArray, type PaginationParams, type NormalizedPagination, type PaginationMetadata, } from './pagination.js';
|
|
9
|
+
export { McpHttpClient, createHttpClient, type HttpClientConfig, type PaginatedResponse, } from './http-client.js';
|
|
10
|
+
export { startMcpServer, type TransportMode, type TransportOptions, } from './transport.js';
|
|
11
|
+
export { McpServer, completable, createMcpServer, TOOL_ANNOTATIONS, type McpToolAnnotations, type McpCallToolResult, } from './server.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,EACL,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,GACxB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,SAAS,EACT,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for MCP servers.
|
|
5
|
+
*/
|
|
6
|
+
// Logger
|
|
7
|
+
export { Logger, LogLevel, initLogger, getLogger, resetLogger, } from './logger.js';
|
|
8
|
+
// Retry
|
|
9
|
+
export { retryWithBackoff, calculateRetryDelay, isRetryableError, sleep, parseRetryAfter, } from './retry.js';
|
|
10
|
+
// Pagination
|
|
11
|
+
export { normalizePagination, createPaginationMetadata, pageToOffset, paginateArray, } from './pagination.js';
|
|
12
|
+
// HTTP Client
|
|
13
|
+
export { McpHttpClient, createHttpClient, } from './http-client.js';
|
|
14
|
+
// Transport
|
|
15
|
+
export { startMcpServer, } from './transport.js';
|
|
16
|
+
// Server builder (McpServer patterns, re-exports, annotation presets)
|
|
17
|
+
export { McpServer, completable, createMcpServer, TOOL_ANNOTATIONS, } from './server.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,SAAS;AACT,OAAO,EACL,MAAM,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,GAEZ,MAAM,aAAa,CAAC;AAErB,QAAQ;AACR,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,EACL,eAAe,GAGhB,MAAM,YAAY,CAAC;AAEpB,aAAa;AACb,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACZ,aAAa,GAId,MAAM,iBAAiB,CAAC;AAEzB,cAAc;AACd,OAAO,EACL,aAAa,EACb,gBAAgB,GAGjB,MAAM,kBAAkB,CAAC;AAE1B,YAAY;AACZ,OAAO,EACL,cAAc,GAGf,MAAM,gBAAgB,CAAC;AAExB,sEAAsE;AACtE,OAAO,EACL,SAAS,EACT,WAAW,EACX,eAAe,EACf,gBAAgB,GAGjB,MAAM,aAAa,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @novorb/mcp-core — Logger
|
|
3
|
+
*
|
|
4
|
+
* Unified logger for MCP servers. Supports two output modes:
|
|
5
|
+
* - file: writes to rotating log files (for stdio-based MCP servers)
|
|
6
|
+
* - stderr: writes to stderr (for Docker / HTTP transport)
|
|
7
|
+
*
|
|
8
|
+
* Log format (text): 2026-03-22T13:27:58.513Z | INFO | Server | Starting...
|
|
9
|
+
* Log format (json): {"level":"INFO","timestamp":"...","component":"Server","message":"Starting..."}
|
|
10
|
+
*
|
|
11
|
+
* Environment variables (read once at first logger creation):
|
|
12
|
+
* - LOG_LEVEL: DEBUG, INFO, WARN, ERROR (default: INFO)
|
|
13
|
+
* - LOG_PATH: log directory (default: ./logs)
|
|
14
|
+
* - LOG_FILE: log file name (default: mcp.log)
|
|
15
|
+
*
|
|
16
|
+
* Security: `meta` is passed through `redactSecrets()` before serialisation — sensitive-keyed fields
|
|
17
|
+
* (authorization/token/api_key/secret/password/cookie/...) are replaced with `[REDACTED]` so a
|
|
18
|
+
* consumer that logs an error object or request params never writes a credential in cleartext. In
|
|
19
|
+
* text mode the message + component are also stripped of CR/LF so a logged value can't forge log lines.
|
|
20
|
+
*/
|
|
21
|
+
export declare enum LogLevel {
|
|
22
|
+
DEBUG = 0,
|
|
23
|
+
INFO = 1,
|
|
24
|
+
WARN = 2,
|
|
25
|
+
ERROR = 3
|
|
26
|
+
}
|
|
27
|
+
export interface LoggerConfig {
|
|
28
|
+
/** Minimum log level (default: INFO) */
|
|
29
|
+
level: LogLevel;
|
|
30
|
+
/** Output mode: 'file' writes to disk, 'stderr' writes to process.stderr */
|
|
31
|
+
output: 'file' | 'stderr';
|
|
32
|
+
/** Format: 'text' for human-readable, 'json' for structured (default: text) */
|
|
33
|
+
format: 'text' | 'json';
|
|
34
|
+
/** Directory for log files (file mode only) */
|
|
35
|
+
logDir: string;
|
|
36
|
+
/** Log file name (file mode only, default: mcp.log) */
|
|
37
|
+
logFile: string;
|
|
38
|
+
/** Max log file size in bytes before rotation (default: 10MB) */
|
|
39
|
+
maxLogSize: number;
|
|
40
|
+
/** Max number of rotated log files to keep (default: 5) */
|
|
41
|
+
maxLogFiles: number;
|
|
42
|
+
}
|
|
43
|
+
/** Recursively copy a value, replacing any sensitive-keyed field with `[REDACTED]`. */
|
|
44
|
+
export declare function redactSecrets(value: unknown, depth?: number): unknown;
|
|
45
|
+
export declare class Logger {
|
|
46
|
+
private config;
|
|
47
|
+
private logPath;
|
|
48
|
+
constructor(config?: Partial<LoggerConfig>);
|
|
49
|
+
private ensureLogDirectory;
|
|
50
|
+
private shouldLog;
|
|
51
|
+
private formatText;
|
|
52
|
+
private formatJson;
|
|
53
|
+
private format;
|
|
54
|
+
private write;
|
|
55
|
+
private writeToFile;
|
|
56
|
+
private rotateLog;
|
|
57
|
+
debug(message: string, context?: string, meta?: unknown): void;
|
|
58
|
+
info(message: string, context?: string, meta?: unknown): void;
|
|
59
|
+
warn(message: string, context?: string, meta?: unknown): void;
|
|
60
|
+
error(message: string, context?: string, meta?: unknown): void;
|
|
61
|
+
setLevel(level: LogLevel): void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Initialize logger with custom config.
|
|
65
|
+
* Should be called once at application startup.
|
|
66
|
+
* Reads LOG_LEVEL, LOG_PATH, LOG_FILE from env (overrides take precedence).
|
|
67
|
+
*/
|
|
68
|
+
export declare function initLogger(config?: Partial<LoggerConfig>): Logger;
|
|
69
|
+
/**
|
|
70
|
+
* Get the logger instance.
|
|
71
|
+
* Creates a default stderr/text instance if initLogger was not called.
|
|
72
|
+
* Reads LOG_LEVEL, LOG_PATH, LOG_FILE from env on first creation.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getLogger(config?: Partial<LoggerConfig>): Logger;
|
|
75
|
+
/**
|
|
76
|
+
* Reset the singleton (for testing).
|
|
77
|
+
*/
|
|
78
|
+
export declare function resetLogger(): void;
|
|
79
|
+
export default getLogger;
|
|
80
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAYD,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,KAAK,EAAE,QAAQ,CAAC;IAChB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,+EAA+E;IAC/E,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;CACrB;AAiCD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAQhE;AAoBD,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAS1C,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,KAAK;IAYb,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,SAAS;IAqBjB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI7D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI9D,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAGhC;AAKD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,CAGjE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,CAKhE;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED,eAAe,SAAS,CAAC"}
|