MCP Terminal Server
by dillip285
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TraceData } from '@genkit-ai/tools-common';
import express from 'express';
import * as http from 'http';
import { TraceStore } from './types';
export { FirestoreTraceStore } from './firestoreTraceStore.js';
export { LocalFileTraceStore } from './localFileTraceStore.js';
export { TraceStore } from './types';
let server: http.Server;
/**
* Starts the telemetry server with the provided params
*/
export function startTelemetryServer(params: {
port: number;
traceStore: TraceStore;
/**
* Controls the maximum request body size. If this is a number,
* then the value specifies the number of bytes; if it is a string,
* the value is passed to the bytes library for parsing.
*
* Defaults to '5mb'.
*/
maxRequestBodySize?: string | number;
}) {
const api = express();
api.use(express.json({ limit: params.maxRequestBodySize ?? '30mb' }));
api.get('/api/__health', async (_, response) => {
response.status(200).send('OK');
});
api.get('/api/traces/:traceId', async (request, response) => {
const { traceId } = request.params;
response.json(await params.traceStore.load(traceId));
});
api.post('/api/traces', async (request, response) => {
const traceData = request.body as TraceData;
await params.traceStore.save(traceData.traceId, traceData);
response.status(200).send('OK');
});
api.get('/api/traces', async (request, response) => {
const { limit, continuationToken } = request.query;
response.json(
await params.traceStore.list({
limit: limit ? parseInt(limit.toString()) : undefined,
continuationToken: continuationToken
? continuationToken.toString()
: undefined,
})
);
});
api.use((err: any, req: any, res: any, next: any) => {
console.error(err.stack);
const error = err as Error;
const { message, stack } = error;
const errorResponse = {
code: 13, // StatusCodes.INTERNAL,
message,
details: {
stack,
traceId: err.traceId,
},
};
res.status(500).json(errorResponse);
});
server = api.listen(params.port, () => {
console.log(`Telemetry API running on http://localhost:${params.port}`);
});
server.on('error', (error) => {
console.error(error);
});
process.on('SIGTERM', async () => await stopTelemetryApi());
}
/**
* Stops Telemetry API and any running dependencies.
*/
async function stopTelemetryApi() {
await Promise.all([
new Promise<void>((resolve) => {
if (server) {
server.close(() => {
console.info('Telemetry API has succesfully shut down.');
resolve();
});
} else {
resolve();
}
}),
]);
}