my-first-valindex.tsx1 match
10<link rel="stylesheet" href="https://esm.town/v/Onujules/my-first-val@main/frontend/style.css" />
11<style>
12@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;600;700&display=swap');
1314body {
Glanceriframe.tsx3 matches
19src="https://glance--90537b2ecab54268bf831875fe1d0158.web.val.run"
20// src="https://lightweight--ef4179e03fc011f0bc0c76b3cceeab13.web.val.run"
21// src="/api/iframe"
22// src={`/api/${content}`}
23// src={`/api/${content}?user=${userId}`}
24// src={contentURL}
25title={`Glance content: ${content}`}
fotmobindex.html6 matches
469
470// Send to backend for analysis
471const response = await fetch('/api/analyze', {
472method: 'POST',
473headers: {
563
564try {
565const response = await fetch('/api/analyze', {
566method: 'POST',
567headers: {
674
675try {
676const response = await fetch('/api/export/full', {
677method: 'POST',
678headers: { 'Content-Type': 'application/json' },
701
702try {
703const response = await fetch('/api/export/iddaa', {
704method: 'POST',
705headers: { 'Content-Type': 'application/json' },
728
729try {
730const response = await fetch('/api/export/poisson', {
731method: 'POST',
732headers: { 'Content-Type': 'application/json' },
755
756try {
757const response = await fetch('/api/export/accuracy', {
758method: 'POST',
759headers: { 'Content-Type': 'application/json' },
54} | null = null;
5556// Analysis API endpoint
57app.post("/api/analyze", async c => {
58try {
59const request: AnalysisRequest = await c.req.json();
9091// Excel export endpoints
92app.post("/api/export/full", async c => {
93try {
94const { targetWeek } = await c.req.json();
122});
123124app.post("/api/export/iddaa", async c => {
125try {
126const { targetWeek } = await c.req.json();
154});
155156app.post("/api/export/poisson", async c => {
157try {
158const { targetWeek } = await c.req.json();
186});
187188app.post("/api/export/accuracy", async c => {
189try {
190if (!globalAnalysisData) {
18โ โโโ index.ts # Main HTTP server
19โ โโโ routes/
20โ โโโ analysis.ts # Analysis API endpoints
21โโโ frontend/
22โ โโโ index.html # Main application interface
LiveStormMCPREADME.md10 matches
1# Livestorm API MCP Server
23This project creates a Model Context Protocol (MCP) server that wraps the
4Livestorm API, exposing:
56- GET endpoints as Resources
9## How it works
10111. The server fetches and parses the Livestorm API's OpenAPI definition
122. It dynamically creates MCP Resources and Tools based on the API endpoints
133. When a client requests a Resource or Tool, the server proxies the request to
14the Livestorm API
1516## MCP Definition
24{
25"mcpServers": {
26"livestorm-api": {
27"type": "streamable-http",
28"url": "https://supagroova--7fab7ae4322911f080e9569c3dd06744.web.val.run/mcp",
38{
39"mcpServers": {
40"livestorm-api": {
41"type": "sse",
42"serverUrl": "https://supagroova--7fab7ae4322911f080e9569c3dd06744.web.val.run/sse"
4950- `index.ts`: Main entry point with HTTP trigger
51- `livestorm.ts`: Functions to fetch and parse the OpenAPI definition
52- `mcp.ts`: MCP server setup and configuration
5367RUN_LOCAL=1
68PORT=8787
69LIVESTORM_API_TOKEN=your-livestorm-api-token-here
70```
7172This is useful for API tokens and local configuration.
7374You can run this MCP server locally using the [Deno](https://deno.land/)
10"security": [
11{
12"api_key": []
13},
14{
21"description": "Created",
22"content": {
23"application/vnd.api+json": {
24"schema": {
25"type": "object",
207},
208"examples": {
209"application/vnd.api+json": {
210"value": {
211"data": {
343"requestBody": {
344"content": {
345"application/vnd.api+json": {
346"schema": {
347"type": "object",
LiveStormMCPtypes.ts3 matches
12// Types for OpenAPI schema
3export interface OpenApiSchema {
4paths: Record<string, PathItem>;
5// Other OpenAPI properties we might need
6}
7
LiveStormMCPmcp.ts16 matches
6extractGetEndpoints,
7extractMutationEndpoints,
8fetchOpenApiSpec,
9proxyRequest,
10} from "../src/livestorm.ts";
2829/**
30* Sets up the MCP server with resources and tools based on the Livestorm API
31* Uses a cached instance if available
32*/
39// Create a new MCP server
40const server = new McpServer({
41name: "livestorm-api-server",
42version: "1.0.0",
43description: "MCP server that provides access to Livestorm API endpoints",
44}, { capabilities: { logging: {} } });
4546try {
47console.log("Fetching Livestorm API OpenAPI spec...");
48// Fetch the OpenAPI spec
49const openApiSpec = await fetchOpenApiSpec();
5051// Extract GET endpoints (Resources)
52const getEndpoints = extractGetEndpoints(openApiSpec);
5354// Extract mutation endpoints (Tools)
55const mutationEndpoints = extractMutationEndpoints(openApiSpec);
5657// Register Resources
127}
128129// Make the request to the Livestorm API
130const response = await proxyRequest(
131path,
137const errorText = await response.text();
138throw new Error(
139`Livestorm API error: ${response.status} ${response.statusText} - ${errorText}`,
140);
141}
236}
237238// Make the request to the Livestorm API
239const response = await proxyRequest(
240path,
247const errorText = await response.text();
248throw new Error(
249`Livestorm API error: ${response.status} ${response.statusText} - ${errorText}`,
250);
251}
283284/**
285* Creates a tool schema from an OpenAPI operation
286* Returns an object with input and output schemas using Zod
287*
314315const requestSchema = operation.requestBody
316?.content["application/vnd.api+json"]?.schema;
317const responseSchema = operation.responses["201"]
318?.content["application/vnd.api+json"]?.schema?.properties?.data.allOf[1];
319320// Iterate through the schema elements and add those to the inputSchema as Zod types
LiveStormMCPlivestorm.ts34 matches
1import "https://deno.land/std@0.224.0/dotenv/load.ts";
2import { parse as parseYaml } from "https://esm.sh/yaml@2.3.1";
3import { OpenApiSchema, OperationObject } from "./types.ts";
4import { blob } from "https://esm.town/v/std/blob";
5import { ValTownBlobNotFoundError } from "https://esm.town/v/std/ValTownBlobNotFoundError";
6import { ValTownBlobError } from "https://esm.town/v/std/ValTownBlobError";
78// URL of the Livestorm API OpenAPI definition
9const LIVESTORM_API_SPEC_URL =
10"https://api.livestorm.co/api-docs/v1/swagger.yaml";
11const LIVESTORM_API_BASE_URL = "https://api.livestorm.co/v1";
1213// Cache key for storing the OpenAPI spec in Blob storage
14const OPENAPI_SPEC_CACHE_KEY = "livestorm-openapi-spec";
1516/**
17* Fetches the Livestorm API OpenAPI definition
18*/
19export async function fetchOpenApiSpec(): Promise<OpenApiSchema> {
20try {
21// Check if the OpenAPI spec is cached in Blob storage
22let response;
23try {
24response = await blob.get(OPENAPI_SPEC_CACHE_KEY);
25console.log(`Using cached OpenAPI spec from Blob storage`);
26} catch (e) {
27if (
28e instanceof ValTownBlobNotFoundError || e instanceof ValTownBlobError
29) {
30console.log(`Fetching OpenAPI spec from ${LIVESTORM_API_SPEC_URL}...`);
31response = await fetch(LIVESTORM_API_SPEC_URL);
32if (!response.ok) {
33const errorText = await response.text();
34console.error(
35`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`,
36);
37console.error(`Response body: ${errorText}`);
38throw new Error(
39`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`,
40);
41}
4647const yamlText = await response.text();
48let parsedSpec: OpenApiSchema;
49try {
50parsedSpec = parseYaml(yamlText);
51// Validate the parsed spec has the expected structure
52if (!parsedSpec || !parsedSpec.paths) {
53throw new Error("Invalid OpenAPI spec: missing paths property");
54}
55} catch (parseError) {
56console.error("Error parsing OpenAPI spec YAML:", parseError);
57throw new Error(
58`Failed to parse OpenAPI spec: ${
59parseError instanceof Error ? parseError.message : String(parseError)
60}`,
62}
6364// Cache the OpenAPI spec in Blob storage
65try {
66await blob.set(OPENAPI_SPEC_CACHE_KEY, yamlText);
67} catch (e) {
68console.error("Error caching OpenAPI spec:", e);
69}
7071return parsedSpec;
72} catch (error) {
73console.error("Error fetching OpenAPI spec:", error);
74throw error;
75}
7778/**
79* Extracts GET endpoints from the OpenAPI spec
80*/
81export function extractGetEndpoints(spec: OpenApiSchema) {
82const getEndpoints: Record<
83string,
99100/**
101* Extracts POST, PUT, DELETE endpoints from the OpenAPI spec
102*/
103export function extractMutationEndpoints(spec: OpenApiSchema) {
104const mutationEndpoints: Record<
105string,
148149/**
150* Proxies a request to the Livestorm API
151*/
152export async function proxyRequest(
157): Promise<Response> {
158// Replace path parameters in the URL
159let url = `${LIVESTORM_API_BASE_URL}${path}`;
160161// Extract path parameters and replace them in the URL
193194// Forward the Authorization header
195const authHeader = Deno.env.get("LIVESTORM_API_TOKEN");
196if (!authHeader) {
197throw new Error(
198"Authorization header is required for Livestorm API requests",
199);
200}
222223try {
224// Make the request to Livestorm API
225const response = await fetch(url, requestOptions);
226230console.error(`Error proxying request to ${url}:`, error);
231throw new Error(
232`Failed to proxy request to Livestorm API: ${
233error instanceof Error ? error.message : String(error)
234}`,