Add API/schema version metadata to REST and MCP responses
This commit is contained in:
parent
80f46a70c9
commit
3cb6feeb15
@ -23,6 +23,11 @@ Open:
|
|||||||
- Health: `http://localhost:8787/health`
|
- Health: `http://localhost:8787/health`
|
||||||
- MCP UI bundle descriptor: `http://localhost:8787/mcp/ui-bundle`
|
- MCP UI bundle descriptor: `http://localhost:8787/mcp/ui-bundle`
|
||||||
|
|
||||||
|
Version metadata:
|
||||||
|
- REST and MCP tool responses include `api_version` and `schema_version`.
|
||||||
|
- Current values: `api_version=0.3.0`, `schema_version=1.0.0`.
|
||||||
|
- Compatibility policy (current): additive, backward-compatible fields may be introduced in the same API minor version.
|
||||||
|
|
||||||
## REST API
|
## REST API
|
||||||
|
|
||||||
### `POST /compile`
|
### `POST /compile`
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { compile, analyze } from "./compile.js";
|
import { compile, analyze } from "./compile.js";
|
||||||
|
const API_VERSION = "0.3.0";
|
||||||
|
const SCHEMA_VERSION = "1.0.0";
|
||||||
|
|
||||||
const SERVER_INFO = {
|
const SERVER_INFO = {
|
||||||
name: "schemeta-mcp",
|
name: "schemeta-mcp",
|
||||||
@ -87,10 +89,20 @@ function toolListResult() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withEnvelopeMeta(payload) {
|
||||||
|
return {
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function uiBundleDescriptor() {
|
function uiBundleDescriptor() {
|
||||||
return {
|
return {
|
||||||
name: "schemeta-workspace",
|
name: "schemeta-workspace",
|
||||||
version: "0.2.0",
|
version: "0.2.0",
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
entry: "/",
|
entry: "/",
|
||||||
title: "Schemeta Workspace",
|
title: "Schemeta Workspace",
|
||||||
transport: "iframe"
|
transport: "iframe"
|
||||||
@ -104,7 +116,7 @@ function handleToolCall(name, args) {
|
|||||||
|
|
||||||
if (name === "schemeta_compile") {
|
if (name === "schemeta_compile") {
|
||||||
const payload = decodePayload(args.payload);
|
const payload = decodePayload(args.payload);
|
||||||
const result = compile(payload, args.options ?? {});
|
const result = withEnvelopeMeta(compile(payload, args.options ?? {}));
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text", text: JSON.stringify(result) }],
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
||||||
structuredContent: result,
|
structuredContent: result,
|
||||||
@ -114,7 +126,7 @@ function handleToolCall(name, args) {
|
|||||||
|
|
||||||
if (name === "schemeta_analyze") {
|
if (name === "schemeta_analyze") {
|
||||||
const payload = decodePayload(args.payload);
|
const payload = decodePayload(args.payload);
|
||||||
const result = analyze(payload, args.options ?? {});
|
const result = withEnvelopeMeta(analyze(payload, args.options ?? {}));
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text", text: JSON.stringify(result) }],
|
content: [{ type: "text", text: JSON.stringify(result) }],
|
||||||
structuredContent: result,
|
structuredContent: result,
|
||||||
|
|||||||
@ -9,6 +9,8 @@ const PORT = Number(process.env.PORT ?? "8787");
|
|||||||
const MAX_BODY_BYTES = Number(process.env.MAX_BODY_BYTES ?? 2 * 1024 * 1024);
|
const MAX_BODY_BYTES = Number(process.env.MAX_BODY_BYTES ?? 2 * 1024 * 1024);
|
||||||
const CORS_ORIGIN = process.env.CORS_ORIGIN ?? "*";
|
const CORS_ORIGIN = process.env.CORS_ORIGIN ?? "*";
|
||||||
const FRONTEND_ROOT = join(process.cwd(), "frontend");
|
const FRONTEND_ROOT = join(process.cwd(), "frontend");
|
||||||
|
const API_VERSION = "0.3.0";
|
||||||
|
const SCHEMA_VERSION = "1.0.0";
|
||||||
|
|
||||||
const MIME_TYPES = {
|
const MIME_TYPES = {
|
||||||
".html": "text/html; charset=utf-8",
|
".html": "text/html; charset=utf-8",
|
||||||
@ -123,6 +125,14 @@ function parsePayloadOptions(body) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withEnvelopeMeta(payload) {
|
||||||
|
return {
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const server = createServer(async (req, res) => {
|
const server = createServer(async (req, res) => {
|
||||||
if (!req.url || !req.method) {
|
if (!req.url || !req.method) {
|
||||||
return json(res, 400, errorEnvelope("invalid_request", "Invalid request."));
|
return json(res, 400, errorEnvelope("invalid_request", "Invalid request."));
|
||||||
@ -141,6 +151,8 @@ const server = createServer(async (req, res) => {
|
|||||||
return json(res, 200, {
|
return json(res, 200, {
|
||||||
ok: true,
|
ok: true,
|
||||||
service: "schemeta",
|
service: "schemeta",
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
status: "ok",
|
status: "ok",
|
||||||
date: new Date().toISOString()
|
date: new Date().toISOString()
|
||||||
});
|
});
|
||||||
@ -151,6 +163,8 @@ const server = createServer(async (req, res) => {
|
|||||||
ok: true,
|
ok: true,
|
||||||
name: "schemeta-workspace",
|
name: "schemeta-workspace",
|
||||||
version: "0.2.0",
|
version: "0.2.0",
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
entry: "/",
|
entry: "/",
|
||||||
title: "Schemeta Workspace",
|
title: "Schemeta Workspace",
|
||||||
transport: "iframe"
|
transport: "iframe"
|
||||||
@ -161,7 +175,7 @@ const server = createServer(async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const body = await readBody(req);
|
const body = await readBody(req);
|
||||||
const parsed = parsePayloadOptions(body);
|
const parsed = parsePayloadOptions(body);
|
||||||
return json(res, 200, analyze(parsed.payload, parsed.options));
|
return json(res, 200, withEnvelopeMeta(analyze(parsed.payload, parsed.options)));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
||||||
return json(res, 413, errorEnvelope("payload_too_large", `Request body exceeds ${MAX_BODY_BYTES} bytes.`));
|
return json(res, 413, errorEnvelope("payload_too_large", `Request body exceeds ${MAX_BODY_BYTES} bytes.`));
|
||||||
@ -177,7 +191,7 @@ const server = createServer(async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const body = await readBody(req);
|
const body = await readBody(req);
|
||||||
const parsed = parsePayloadOptions(body);
|
const parsed = parsePayloadOptions(body);
|
||||||
return json(res, 200, compile(parsed.payload, parsed.options));
|
return json(res, 200, withEnvelopeMeta(compile(parsed.payload, parsed.options)));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
||||||
return json(res, 413, errorEnvelope("payload_too_large", `Request body exceeds ${MAX_BODY_BYTES} bytes.`));
|
return json(res, 413, errorEnvelope("payload_too_large", `Request body exceeds ${MAX_BODY_BYTES} bytes.`));
|
||||||
@ -198,8 +212,10 @@ const server = createServer(async (req, res) => {
|
|||||||
const laidOut = applyLayoutToModel(model, { respectLocks: false });
|
const laidOut = applyLayoutToModel(model, { respectLocks: false });
|
||||||
return json(res, 200, {
|
return json(res, 200, {
|
||||||
ok: true,
|
ok: true,
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
model: laidOut,
|
model: laidOut,
|
||||||
compile: compile(laidOut, parsed.options)
|
compile: withEnvelopeMeta(compile(laidOut, parsed.options))
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
||||||
@ -221,8 +237,10 @@ const server = createServer(async (req, res) => {
|
|||||||
const laidOut = applyLayoutToModel(model, { respectLocks: true });
|
const laidOut = applyLayoutToModel(model, { respectLocks: true });
|
||||||
return json(res, 200, {
|
return json(res, 200, {
|
||||||
ok: true,
|
ok: true,
|
||||||
|
api_version: API_VERSION,
|
||||||
|
schema_version: SCHEMA_VERSION,
|
||||||
model: laidOut,
|
model: laidOut,
|
||||||
compile: compile(laidOut, parsed.options)
|
compile: withEnvelopeMeta(compile(laidOut, parsed.options))
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
if (err?.code === "PAYLOAD_TOO_LARGE") {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user