|
1 | | -import { FastifyInstance } from 'fastify'; |
| 1 | +import { FastifyInstance } from "fastify"; |
| 2 | +import cors from "@fastify/cors"; |
| 3 | +import axios from "axios"; |
2 | 4 |
|
3 | 5 | const name = process.env.MCP_NAME || "MCP Service"; |
4 | | -const description = process.env.MCP_DESCRIPTION || "MCP Server providing model context."; |
5 | | -const tags = (process.env.MCP_TAGS || "MCP").split(','); |
| 6 | +const description = |
| 7 | + process.env.MCP_DESCRIPTION || "MCP Server providing model context."; |
| 8 | +const tags = (process.env.MCP_TAGS || "MCP").split(","); |
6 | 9 | const email = process.env.MCP_CONTACT_EMAIL || "[email protected]"; |
7 | 10 | const website = process.env.MCP_CONTACT_WEBSITE || "https://ph7.me"; |
8 | | -const version = process.env.APP_VERSION || '0.1.0'; |
| 11 | +const version = process.env.APP_VERSION || "0.1.0"; |
9 | 12 |
|
10 | 13 | export async function registerContextRoutes(server: FastifyInstance) { |
| 14 | + await server.register(cors, { origin: true }); |
| 15 | + |
| 16 | + // Logging middleware |
| 17 | + server.addHook("onRequest", async (request) => { |
| 18 | + console.log(`[${new Date().toISOString()}] ${request.method} ${request.url}`); |
| 19 | + }); |
| 20 | + server.addHook("onError", async (request, reply, error) => { |
| 21 | + console.error(`[${new Date().toISOString()}] ERROR: ${error.message}`); |
| 22 | + }); |
| 23 | + |
11 | 24 | // MCP Discovery Endpoint |
12 | | - server.get('/.well-known/model-context', async () => { |
| 25 | + server.get("/.well-known/model-context", async () => { |
13 | 26 | return { |
14 | | - "@context": "/.well-known/v1.json", |
15 | | - "name": name, |
16 | | - "description": description, |
17 | | - "version": version, |
18 | | - "tags": tags, |
19 | | - "contact": { |
20 | | - "email": email, |
21 | | - "website": website |
| 27 | + "@context": "/.well-known/v1.json", // Local context reference |
| 28 | + name: name, |
| 29 | + description: description, |
| 30 | + version: version, |
| 31 | + tags, // ES6 shorthand property name |
| 32 | + contact: { |
| 33 | + email: email, |
| 34 | + website: website, |
22 | 35 | }, |
23 | | - "content_endpoint": "/v1/content" |
| 36 | + content_endpoint: "/v1/content", |
24 | 37 | }; |
25 | 38 | }); |
26 | 39 |
|
27 | 40 | // Available Models Content |
28 | | - server.get('/v1/content', async () => { |
| 41 | + server.get("/v1/content", async () => { |
29 | 42 | return [ |
30 | 43 | { |
31 | 44 | title: "Customer Churn Prediction", |
32 | | - content: "Predictive model using logistic regression and XGBoost to identify potential customer churn.", |
33 | | - tags: ["ML", "churn", "classification", "xgboost"] |
| 45 | + content: |
| 46 | + "Predictive model using logistic regression and XGBoost to identify potential customer churn.", |
| 47 | + tags: ["ML", "churn", "classification", "xgboost"], |
34 | 48 | }, |
35 | 49 | { |
36 | 50 | title: "Exploratory Data Analysis Report", |
37 | | - content: "Notebook-driven visualisation and summary statistics to uncover key trends and anomalies.", |
38 | | - tags: ["EDA", "data analysis", "notebooks"] |
| 51 | + content: |
| 52 | + "Notebook-driven visualisation and summary statistics to uncover key trends and anomalies.", |
| 53 | + tags: ["EDA", "data analysis", "notebooks"], |
39 | 54 | }, |
40 | 55 | { |
41 | 56 | title: "Statistical Hypothesis Testing", |
42 | | - content: "Support for t-tests, chi-square, ANOVA, and p-value interpretation for decision making.", |
43 | | - tags: ["statistics", "hypothesis testing", "inference"] |
| 57 | + content: |
| 58 | + "Support for t-tests, chi-square, ANOVA, and p-value interpretation for decision making.", |
| 59 | + tags: ["statistics", "hypothesis testing", "inference"], |
44 | 60 | }, |
45 | 61 | { |
46 | 62 | title: "Time Series Forecasting", |
47 | | - content: "Models using ARIMA and Prophet to forecast trends in business KPIs.", |
48 | | - tags: ["forecasting", "time series", "prophet"] |
49 | | - } |
| 63 | + content: |
| 64 | + "Models using ARIMA and Prophet to forecast trends in business KPIs.", |
| 65 | + tags: ["forecasting", "time series", "prophet"], |
| 66 | + }, |
50 | 67 | ]; |
51 | 68 | }); |
| 69 | + |
| 70 | + // Model Metadata Endpoint |
| 71 | + server.get("/v1/model/:modelId", async (request) => { |
| 72 | + const { modelId } = request.params as { modelId: string }; |
| 73 | + if (modelId === "churn") { |
| 74 | + return { |
| 75 | + id: "churn", |
| 76 | + title: "Customer Churn Prediction", |
| 77 | + input: { type: "json", example: { customerId: "123", features: [/* ... */] } }, |
| 78 | + output: { type: "json", example: { churnProbability: 0.87 } }, |
| 79 | + usage: "POST /v1/predict/churn", |
| 80 | + }; |
| 81 | + } |
| 82 | + if (modelId === "eda") { |
| 83 | + return { |
| 84 | + id: "eda", |
| 85 | + title: "Exploratory Data Analysis Report", |
| 86 | + input: { type: "csv", example: "data.csv" }, |
| 87 | + output: { type: "json", example: { summary: "..." } }, |
| 88 | + usage: "GET /v1/content/eda", |
| 89 | + }; |
| 90 | + } |
| 91 | + // ...other models... |
| 92 | + return { error: "Model not found" }; |
| 93 | + }); |
| 94 | + |
| 95 | + // Strava Activities Integration Endpoint |
| 96 | + server.post("/v1/strava/activities", async (request, reply) => { |
| 97 | + const { accessToken, endpoint = "https://www.strava.com/api/v3/athlete/activities", page = 1, per_page = 30 } = request.body as any; |
| 98 | + try { |
| 99 | + const response = await axios.get(endpoint, { |
| 100 | + headers: { Authorization: `Bearer ${accessToken}` }, |
| 101 | + params: { page, per_page } |
| 102 | + }); |
| 103 | + return response.data; |
| 104 | + } catch (error: any) { |
| 105 | + console.error(`[${new Date().toISOString()}] Strava API ERROR:`, error.message); |
| 106 | + reply.code(500).send({ error: error.message }); |
| 107 | + } |
| 108 | + }); |
52 | 109 | } |
0 commit comments