Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
to = "/.netlify/functions/snapshots?key=:key"
status = 200

[[redirects]]
from = "/api/mcp-context"
to = "/.netlify/functions/mcp-context"
status = 200

# Swagger UI
[[redirects]]
from = "/docs"
Expand Down
12 changes: 11 additions & 1 deletion netlify/functions/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,14 @@ async function getLatestSnapshot() {
return { key: latestKey, data };
}

module.exports = { STORE_NAME, json, error, options, getLatestSnapshot, listBlobs, getBlob };
async function putBlob(store, key, value, contentType = "application/json") {
const body = contentType === "application/json" ? JSON.stringify(value) : value;
const resp = await fetch(blobUrl(store, key), {
method: "PUT",
headers: { ...blobHeaders(), "Content-Type": contentType },
body,
});
if (!resp.ok) throw new Error(`Put blob failed: ${resp.status}`);
}

module.exports = { STORE_NAME, json, error, options, getLatestSnapshot, listBlobs, getBlob, putBlob, blobUrl, blobHeaders, BLOB_SITE_ID };
49 changes: 49 additions & 0 deletions netlify/functions/mcp-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { json, error, options, listBlobs, blobUrl, blobHeaders } = require("./helpers");

const ONCALL_STORE = "oncall";

exports.handler = async (event) => {
if (event.httpMethod === "OPTIONS") return options();

try {
const service = event.queryStringParameters?.service;

if (!service) {
const blobs = await listBlobs(ONCALL_STORE);
const entries = blobs.map((b) => {
const parts = b.key.split("/");
return { key: b.key, service: parts[0], timestamp: parts.slice(1).join("/") };
});
return json({ count: entries.length, entries });
}

const blobs = await listBlobs(ONCALL_STORE);
const serviceBlobs = blobs.filter((b) => b.key.startsWith(`${service}/`));

if (!serviceBlobs.length) {
return error(`No oncall logs found for service: ${service}`, 404);
}

const keys = serviceBlobs.map((b) => b.key).sort();
const latestKey = keys[keys.length - 1];

const resp = await fetch(blobUrl(ONCALL_STORE, latestKey), {
headers: blobHeaders(),
});
if (!resp.ok) return error(`Failed to fetch oncall log: ${resp.status}`, 500);

const ct = resp.headers.get("content-type") || "";
const content = ct.includes("application/json")
? await resp.json()
: await resp.text();

return json({
service,
key: latestKey,
available_logs: keys,
latest_log: content,
});
} catch (e) {
return error(`Failed to fetch oncall context: ${e.message}`);
}
};
62 changes: 62 additions & 0 deletions public/swagger/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,36 @@ paths:
schema:
$ref: "#/components/schemas/Error"

/mcp-context:
get:
summary: Oncall service context
description: |
Returns oncall chat logs for a given service. Without a service parameter,
lists all available oncall entries. With a service name, returns the latest
log and all available timestamps.
operationId: getMcpContext
tags: [Oncall]
parameters:
- name: service
in: query
required: false
description: "Service name (e.g. allocation-engine-2.0)"
schema:
type: string
responses:
"200":
description: Oncall context logs
content:
application/json:
schema:
$ref: "#/components/schemas/McpContextResponse"
"404":
description: No oncall logs found for service
content:
application/json:
schema:
$ref: "#/components/schemas/Error"

components:
schemas:
Error:
Expand Down Expand Up @@ -433,6 +463,38 @@ components:
type: string
description: Snapshot keys (most recent 50), newest first

McpContextResponse:
type: object
properties:
service:
type: string
example: allocation-engine-2.0
key:
type: string
description: Blob key for the latest oncall log
available_logs:
type: array
items:
type: string
description: All available log keys for this service
latest_log:
description: Content of the latest oncall chat log (text or JSON)
count:
type: integer
description: Total number of oncall entries (when listing all)
entries:
type: array
items:
type: object
properties:
key:
type: string
service:
type: string
timestamp:
type: string
description: All oncall entries across services (when listing all)

SnapshotResponse:
type: object
properties:
Expand Down
Loading