@tigerdata/mcp-boilerplate 0.3.0 → 0.4.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/dist/http/mcp.js +38 -15
- package/package.json +2 -1
package/dist/http/mcp.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
|
+
import { trace, context as otelContext, propagation, SpanKind, SpanStatusCode, } from '@opentelemetry/api';
|
|
4
|
+
import { ATTR_HTTP_RESPONSE_STATUS_CODE } from '@opentelemetry/semantic-conventions';
|
|
3
5
|
import { Router } from 'express';
|
|
4
6
|
import { randomUUID } from 'node:crypto';
|
|
5
7
|
import getRawBody from 'raw-body';
|
|
6
8
|
import { log } from '../logger.js';
|
|
9
|
+
const name = process.env.OTEL_SERVICE_NAME;
|
|
10
|
+
const tracer = trace.getTracer(name ? `${name}.router.mcp` : 'router.mcp');
|
|
7
11
|
export const mcpRouterFactory = (context, createServer, { name, stateful = true, } = {}) => {
|
|
8
12
|
const router = Router();
|
|
9
13
|
const transports = new Map();
|
|
@@ -75,24 +79,43 @@ export const mcpRouterFactory = (context, createServer, { name, stateful = true,
|
|
|
75
79
|
await transport.handleRequest(req, res, body);
|
|
76
80
|
};
|
|
77
81
|
router.post('/', async (req, res) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
let traceContext = otelContext.active();
|
|
83
|
+
if (req.headers.traceparent) {
|
|
84
|
+
// Some MCP clients (e.g. pydantic) pass the parent trace context
|
|
85
|
+
traceContext = propagation.extract(traceContext, {
|
|
86
|
+
traceparent: req.headers.traceparent,
|
|
87
|
+
});
|
|
82
88
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
await tracer.startActiveSpan('mcp.http.post', { kind: SpanKind.SERVER }, traceContext, async (span) => {
|
|
90
|
+
try {
|
|
91
|
+
await (stateful
|
|
92
|
+
? handleStatefulRequest(req, res)
|
|
93
|
+
: handleStatelessRequest(req, res));
|
|
94
|
+
span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, res.statusCode);
|
|
95
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
log.error('Error handling MCP request:', error);
|
|
99
|
+
span.recordException(error);
|
|
100
|
+
span.setStatus({
|
|
101
|
+
code: SpanStatusCode.ERROR,
|
|
102
|
+
message: error.message,
|
|
93
103
|
});
|
|
104
|
+
if (!res.headersSent) {
|
|
105
|
+
res.status(500).json({
|
|
106
|
+
jsonrpc: '2.0',
|
|
107
|
+
error: {
|
|
108
|
+
code: -32603,
|
|
109
|
+
message: 'Internal server error',
|
|
110
|
+
},
|
|
111
|
+
id: null,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
94
114
|
}
|
|
95
|
-
|
|
115
|
+
finally {
|
|
116
|
+
span.end();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
96
119
|
});
|
|
97
120
|
// Reusable handler for GET and DELETE requests
|
|
98
121
|
const handleSessionRequest = async (req, res) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tigerdata/mcp-boilerplate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "MCP boilerplate code for Node.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "TigerData",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@opentelemetry/sdk-metrics": "^2.2.0",
|
|
42
42
|
"@opentelemetry/sdk-node": "^0.207.0",
|
|
43
43
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
|
44
|
+
"@opentelemetry/semantic-conventions": "^1.37.0",
|
|
44
45
|
"express": "^5.1.0",
|
|
45
46
|
"raw-body": "^3.0.1",
|
|
46
47
|
"zod": "^3.23.8"
|