Skip to content

Commit e22f982

Browse files
authored
Change how Logging is implemented (#5)
1 parent 9e20af0 commit e22f982

12 files changed

Lines changed: 236 additions & 405 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ OPENAI_API_KEY=your-openai-api-key
1313
# MAX_RETRIES=3
1414
# RETRY_DELAY=1000
1515
# MAX_CONNECTIONS=100
16-
# LOG_LEVEL=INFO
16+
# LOG_LEVEL=info
1717

1818
# MCP Transport configuration
1919
# MCP_TRANSPORT=stdio

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ We welcome contributions to the Nutrient Document Engine MCP Server! This docume
4646
4747
# Optional configuration
4848
MCP_TRANSPORT=stdio
49-
LOG_LEVEL=INFO
49+
LOG_LEVEL=info
5050
```
5151

5252
### Starting Document Engine

docs/configuration.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@ This guide covers configuration options, environment variables, transport modes,
1313

1414
### Optional Configuration
1515

16-
| Variable | Description | Default | Example |
17-
|----------------------|---------------------------------------------------------------------------|---------------------------------|------------|
18-
| `MCP_TRANSPORT` | Transport type - "stdio" or "http" | `stdio` | `http` |
19-
| `MCP_HOST` | The host as IP address for the Dashboard and the MCP server and Dashboard | `localhost` | `0.0.0.0` |
20-
| `PORT` | HTTP server port (HTTP transport only) | `5100` | `8080` |
21-
| `MAX_RETRIES` | Number of API request retries | `3` | `5` |
22-
| `CONNECTION_TIMEOUT` | Request timeout in milliseconds | `30000` | `60000` |
23-
| `RETRY_DELAY` | Delay between retries in milliseconds | `1000` | `2000` |
24-
| `MAX_CONNECTIONS` | Maximum concurrent connections | `100` | `200` |
25-
| `LOG_LEVEL` | Logging level | `INFO` | `DEBUG` |
26-
| `DASHBOARD_USERNAME` | Dashboard authentication username (required to enable dashboard) | `undefined` | `admin` |
27-
| `DASHBOARD_PASSWORD` | Dashboard authentication password (required to enable dashboard) | `undefined` | `password` |
16+
| Variable | Description | Default | Example |
17+
|----------------------|-------------------------------------------------------------------------------------------------|-------------|------------|
18+
| `MCP_TRANSPORT` | Transport type - "stdio" or "http" | `stdio` | `http` |
19+
| `MCP_HOST` | The host as IP address for the Dashboard and the MCP server and Dashboard | `localhost` | `0.0.0.0` |
20+
| `PORT` | HTTP server port (HTTP transport only) | `5100` | `8080` |
21+
| `MAX_RETRIES` | Number of API request retries | `3` | `5` |
22+
| `CONNECTION_TIMEOUT` | Request timeout in milliseconds | `30000` | `60000` |
23+
| `RETRY_DELAY` | Delay between retries in milliseconds | `1000` | `2000` |
24+
| `MAX_CONNECTIONS` | Maximum concurrent connections | `100` | `200` |
25+
| `LOG_LEVEL` | Logging level ("debug", "info", "notice", "warning", "error", "critical", "alert", "emergency") | `info` | `error` |
26+
| `DASHBOARD_USERNAME` | Dashboard authentication username (required to enable dashboard) | `undefined` | `admin` |
27+
| `DASHBOARD_PASSWORD` | Dashboard authentication password (required to enable dashboard) | `undefined` | `password` |
2828

2929
> MCP_HOST information
3030
> Setting host to `127.0.0.1` (localhost) ensure that the Dashboard and the MCP server is only accessible locally
@@ -275,5 +275,5 @@ echo $DOCUMENT_ENGINE_API_AUTH_TOKEN
275275

276276
### Debug Logging
277277
```bash
278-
LOG_LEVEL=DEBUG npx @nutrient-sdk/document-engine-mcp-server
278+
LOG_LEVEL=debug npx @nutrient-sdk/document-engine-mcp-server
279279
```

src/api/Client.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export async function createDocumentEngineClient(config: ClientConfig): Promise<
6969
function setupInterceptors(client: Client): void {
7070
client.interceptors.request.use(
7171
config => {
72-
logger.request(config.method || 'unknown', config.url || 'unknown', {
72+
logger.request(null, config.method || 'unknown', config.url || 'unknown', {
7373
params: config.params,
7474
body: config.data,
7575
});
@@ -82,7 +82,7 @@ function setupInterceptors(client: Client): void {
8282

8383
client.interceptors.response.use(
8484
response => {
85-
logger.response(response.status, response.config?.url || 'unknown', {
85+
logger.response(null, response.status, response.config?.url || 'unknown', {
8686
data: response.data,
8787
});
8888
return response;
@@ -93,15 +93,15 @@ function setupInterceptors(client: Client): void {
9393
const method = error.config?.method?.toUpperCase();
9494
const url = error.config?.url || 'unknown';
9595

96-
logger.error('API Request Failed', {
96+
logger.error(null, 'API Request Failed', {
9797
method,
9898
url,
9999
status,
100100
message: error.message,
101101
code: error.code,
102102
});
103103
} else {
104-
logger.error('Non-Axios Error in Response Interceptor', {
104+
logger.error(null, 'Non-Axios Error in Response Interceptor', {
105105
message: error instanceof Error ? error.message : String(error),
106106
});
107107
}
@@ -139,7 +139,7 @@ function addRetryFunctionality(client: Client, maxRetries: number, retryDelay: n
139139
const currentAttempt = retryCount + 1;
140140
const delay = calculateDelay(retryDelay, retryCount);
141141

142-
logger.retry(currentAttempt, maxRetries, delay);
142+
logger.retry(null, currentAttempt, maxRetries, delay);
143143
await new Promise(resolve => setTimeout(resolve, delay));
144144
return attempt(retryCount + 1);
145145
}

src/dashboard/routes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export async function dashboardHandler(client: DocumentEngineClient, req: Reques
5252
// Render HTML dashboard
5353
res.send(dashboardTemplate(documents, formatFileSize, message));
5454
} catch (error) {
55-
logger.error('Dashboard error', { error });
55+
logger.error(null, 'Dashboard error', { error });
5656
res
5757
.status(500)
5858
.send(
@@ -128,7 +128,7 @@ export async function uploadHandler(client: DocumentEngineClient, req: Request,
128128
// Render upload results
129129
res.send(uploadResultsTemplate(templateResults));
130130
} catch (error) {
131-
logger.error('File upload error', { error });
131+
logger.error(null, 'File upload error', { error });
132132
res
133133
.status(500)
134134
.send(
@@ -173,7 +173,7 @@ export async function downloadHandler(client: DocumentEngineClient, req: Request
173173

174174
res.send(Buffer.from(response.data));
175175
} catch (error) {
176-
logger.error('Document download error', { error });
176+
logger.error(null, 'Document download error', { error });
177177
res
178178
.status(500)
179179
.send(
@@ -201,7 +201,7 @@ export async function deleteHandler(client: DocumentEngineClient, req: Request,
201201
// Redirect back to dashboard with success message
202202
res.redirect('/dashboard?message=Document+deleted+successfully');
203203
} catch (error) {
204-
logger.error('Document delete error', { error });
204+
logger.error(null, 'Document delete error', { error });
205205
res
206206
.status(500)
207207
.send(

src/index.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ if (process.env.NODE_ENV !== 'test') {
2323
try {
2424
validateEnvironment();
2525
} catch (error) {
26-
console.error('Environment validation failed:', error instanceof Error ? error.message : error);
26+
logger.error(
27+
null,
28+
`Environment validation failed: ${error instanceof Error ? error.message : error}`
29+
);
2730
process.exit(1);
2831
}
2932
}
@@ -41,29 +44,32 @@ async function waitForDocumentEngine(): Promise<DocumentEngineClient> {
4144
while (attempts < maxRetries) {
4245
try {
4346
logger.info(
47+
null,
4448
`Attempting to connect to Document Engine (attempt ${attempts + 1}/${maxRetries})`
4549
);
4650
const client = await getDocumentEngineClient();
4751

4852
// Test the connection with a health check
4953
await client.get('/healthcheck');
5054

51-
logger.info('Document Engine is ready! Connection established successfully.');
55+
logger.info(null, 'Document Engine is ready! Connection established successfully.');
5256
return client;
5357
} catch (error) {
5458
attempts++;
5559
const errorMessage = error instanceof Error ? error.message : String(error);
5660

5761
if (attempts >= maxRetries) {
5862
logger.error(
63+
null,
5964
`Failed to connect to Document Engine after ${maxRetries} attempts. Last error: ${errorMessage}`
6065
);
6166
throw new Error(
6267
`Document Engine connection failed after ${maxRetries} attempts: ${errorMessage}`
6368
);
6469
}
6570

66-
logger.warn(
71+
logger.warning(
72+
null,
6773
`Document Engine not ready yet (attempt ${attempts}/${maxRetries}): ${errorMessage}. Retrying in ${retryDelay}ms...`
6874
);
6975
await new Promise(resolve => setTimeout(resolve, retryDelay));
@@ -97,7 +103,7 @@ function createMCPServer(): McpServer {
97103

98104
function configureMCPServerTools(server: McpServer): void {
99105
for (const tool of mcpToolsToRegister) {
100-
server.tool(tool.name, tool.schema, (args, extra) => tool.handler(client, args, extra));
106+
server.tool(tool.name, tool.schema, (args, extra) => tool.handler(server, client, args, extra));
101107
}
102108
}
103109

@@ -127,7 +133,7 @@ function createExpressApp(enableDashboard: boolean = false): express.Application
127133
});
128134
}
129135
} catch (error) {
130-
logger.error('Health check endpoint error', { error });
136+
logger.error(null, 'Health check endpoint error', { error });
131137
res.status(500).json({
132138
status: 'error',
133139
message: error instanceof Error ? error.message : 'Unknown error',
@@ -153,7 +159,10 @@ async function startStdioServer() {
153159

154160
// Start HTTP server for dashboard
155161
app.listen(env.PORT, env.MCP_HOST, () => {
156-
logger.info(`Dashboard server running on HTTP at ${env.MCP_HOST}:${env.PORT}/dashboard`);
162+
logger.info(
163+
null,
164+
`Dashboard server running on HTTP at ${env.MCP_HOST}:${env.PORT}/dashboard`
165+
);
157166
});
158167
}
159168

@@ -168,8 +177,7 @@ async function startStdioServer() {
168177
});
169178

170179
await server.connect(transport);
171-
logger.setMCPServer(server);
172-
logger.info(`Nutrient Document Engine MCP server ${getVersion()} running on stdio`);
180+
logger.info(null, `Nutrient Document Engine MCP server ${getVersion()} running on stdio`);
173181
}
174182

175183
async function startHttpServer() {
@@ -204,12 +212,14 @@ async function startHttpServer() {
204212
transport.onclose = () => {
205213
if (transport.sessionId) {
206214
delete transports[transport.sessionId];
215+
logger.info(null, `Close Session : ${transport.sessionId}`);
207216
}
208217
};
209218

210219
const server = createMCPServer();
211220
await server.connect(transport);
212-
logger.setMCPServer(server);
221+
222+
logger.info(null, `Open Session : ${transport.sessionId}`);
213223
} else {
214224
// Invalid request
215225
res.status(400).json({
@@ -226,7 +236,7 @@ async function startHttpServer() {
226236
// Handle the request
227237
await transport.handleRequest(req, res, req.body);
228238
} catch (error) {
229-
logger.error('Error handling MCP request:', error);
239+
logger.error(null, 'Error handling MCP request:', error);
230240
if (!res.headersSent) {
231241
res.status(500).json({
232242
jsonrpc: '2.0',
@@ -265,10 +275,14 @@ async function startHttpServer() {
265275

266276
app.listen(env.PORT, env.MCP_HOST, () => {
267277
logger.info(
278+
null,
268279
`Nutrient Document Engine MCP server ${getVersion()} running on HTTP at ${env.MCP_HOST}:${env.PORT}/mcp`
269280
);
270281
if (dashboardEnabled) {
271-
logger.info(`Dashboard server running on HTTP at ${env.MCP_HOST}:${env.PORT}/dashboard`);
282+
logger.info(
283+
null,
284+
`Dashboard server running on HTTP at ${env.MCP_HOST}:${env.PORT}/dashboard`
285+
);
272286
}
273287
});
274288
}
@@ -284,6 +298,6 @@ async function main() {
284298
}
285299

286300
main().catch(error => {
287-
logger.error('Failed to start server', { error: error.message, stack: error.stack });
301+
logger.error(null, 'Failed to start server', { error: error.message, stack: error.stack });
288302
process.exit(1);
289303
});

src/mcpTools.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
MergeDocumentPagesSchema,
4141
} from './tools/document-editing/mergeDocumentPages.js';
4242
import { rotatePages, RotatePagesSchema } from './tools/document-editing/rotatePages.js';
43+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4344

4445
export type MCPToolOutput = {
4546
markdown: string;
@@ -51,6 +52,7 @@ export type MCPToolOutput = {
5152
};
5253

5354
type MCPToolCallback = (
55+
server: McpServer,
5456
client: DocumentEngineClient,
5557
args: Record<string, unknown>,
5658
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
@@ -72,22 +74,23 @@ async function withLogging<T, R>(
7274
toolName: string,
7375
extras: RequestHandlerExtra<ServerRequest, ServerNotification>,
7476
toolFn: (client: DocumentEngineClient, params: T) => Promise<R>,
77+
server: McpServer,
7578
client: DocumentEngineClient,
7679
params: T
7780
): Promise<R> {
7881
let messagePrefix = `[${toolName}]`;
7982
if (extras.sessionId) messagePrefix += `[s=${extras.sessionId}]`;
8083
if (extras.requestId) messagePrefix += `[r=${extras.requestId}]`;
8184

82-
logger.info(`${messagePrefix} START`, params);
85+
logger.info(server, `${messagePrefix} START`, params);
8386

8487
try {
8588
const result = await toolFn(client, params);
86-
logger.debug(`${messagePrefix} END`, result);
87-
logger.info(`${messagePrefix} END`);
89+
logger.debug(server, `${messagePrefix} END`, result);
90+
logger.info(server, `${messagePrefix} END`);
8891
return result;
8992
} catch (error) {
90-
logger.error(`${messagePrefix} END`, {
93+
logger.error(server, `${messagePrefix} END`, {
9194
error: error instanceof Error ? error.message : String(error),
9295
});
9396
throw error;
@@ -100,8 +103,8 @@ async function withLogging<T, R>(
100103
function createToolHandler<TParams>(
101104
toolFn: (client: DocumentEngineClient, params: TParams) => Promise<MCPToolOutput>
102105
): (toolName: string) => MCPToolCallback {
103-
return (toolName: string) => async (client, params, extras) => {
104-
const result = await withLogging(toolName, extras, toolFn, client, params as TParams);
106+
return (toolName: string) => async (server, client, params, extras) => {
107+
const result = await withLogging(toolName, extras, toolFn, server, client, params as TParams);
105108

106109
// Create the content array with the markdown text
107110
const textContent = {

src/utils/Environment.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ const environmentSchema = z.object({
1717
MAX_RETRIES: z.coerce.number().int().min(0).default(3),
1818
RETRY_DELAY: z.coerce.number().int().min(0).default(1000),
1919
MAX_CONNECTIONS: z.coerce.number().int().positive().default(100),
20-
LOG_LEVEL: z.enum(['ERROR', 'WARN', 'WARNING', 'INFO', 'DEBUG']).default('INFO'),
20+
LOG_LEVEL: z
21+
.enum(['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency'])
22+
.default('info'),
2123

2224
// MCP Transport configuration
2325
MCP_TRANSPORT: z.enum(['stdio', 'http']).default('stdio'),

0 commit comments

Comments
 (0)