@ampless/backend 1.0.0-beta.88 → 1.0.0-beta.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/functions/mcp-handler.js +237 -93
- package/package.json +3 -3
|
@@ -3,7 +3,7 @@ import { ConditionalCheckFailedException, DynamoDBClient } from "@aws-sdk/client
|
|
|
3
3
|
import { DynamoDBDocumentClient, GetCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb";
|
|
4
4
|
import { createHash } from "crypto";
|
|
5
5
|
|
|
6
|
-
// ../mcp-server/dist/index.js
|
|
6
|
+
// ../mcp-server/dist/tools/index.js
|
|
7
7
|
import { filterSortPostSummaries } from "ampless";
|
|
8
8
|
import {
|
|
9
9
|
decodeAwsJson
|
|
@@ -1683,50 +1683,66 @@ var tools = [
|
|
|
1683
1683
|
name: "list_posts",
|
|
1684
1684
|
description: "Returns lightweight post summaries (no body \u2014 use get_post for content) with search / sort / filters. `total` reflects the filtered count. Supports query (substring over title/slug/tags), tag (exact), status, sort, limit (1\u2013100, default 20), offset (default 0).",
|
|
1685
1685
|
inputSchema: listPostsSchema,
|
|
1686
|
-
handler: (args, ctx) => listPosts(ctx.graphql, args)
|
|
1686
|
+
handler: (args, ctx) => listPosts(ctx.graphql, args),
|
|
1687
|
+
readOnly: true,
|
|
1688
|
+
destructive: false
|
|
1687
1689
|
},
|
|
1688
1690
|
{
|
|
1689
1691
|
name: "get_post",
|
|
1690
1692
|
description: "Fetch a single post by slug or postId. Returns null if not found.",
|
|
1691
1693
|
inputSchema: getPostSchema,
|
|
1692
|
-
handler: (args, ctx) => getPost(ctx.graphql, args)
|
|
1694
|
+
handler: (args, ctx) => getPost(ctx.graphql, args),
|
|
1695
|
+
readOnly: true,
|
|
1696
|
+
destructive: false
|
|
1693
1697
|
},
|
|
1694
1698
|
{
|
|
1695
1699
|
name: "create_post",
|
|
1696
1700
|
description: 'Create a new post. Title and slug are required. Body shape depends on format: tiptap=JSON node tree, markdown=source string, html=raw HTML string. Defaults to status=draft. Pass `metadata: { no_layout: true }` alongside format=html to publish the body as a bare HTML page with no theme chrome (middleware rewrites the /<slug> request to the internal bare-HTML handler). Pass `metadata: { cache: "deep" | "hot" }` to override the default cooldown-based cache strategy \u2014 see get_schema.notes.cacheStrategy for details.',
|
|
1697
1701
|
inputSchema: createPostSchema,
|
|
1698
|
-
handler: (args, ctx) => createPost(ctx.graphql, args)
|
|
1702
|
+
handler: (args, ctx) => createPost(ctx.graphql, args),
|
|
1703
|
+
readOnly: false,
|
|
1704
|
+
destructive: false
|
|
1699
1705
|
},
|
|
1700
1706
|
{
|
|
1701
1707
|
name: "update_post",
|
|
1702
1708
|
description: "Update an existing post by postId. Only the fields you pass are changed. Tag list / publishedAt changes also update the PostTag denormalized index. Passing `metadata` REPLACES the existing object \u2014 call get_post first if you only want to add or change one key.",
|
|
1703
1709
|
inputSchema: updatePostSchema,
|
|
1704
|
-
handler: (args, ctx) => updatePost(ctx.graphql, args)
|
|
1710
|
+
handler: (args, ctx) => updatePost(ctx.graphql, args),
|
|
1711
|
+
readOnly: false,
|
|
1712
|
+
// Overwrites an existing post's fields — not a pure additive write.
|
|
1713
|
+
destructive: true
|
|
1705
1714
|
},
|
|
1706
1715
|
{
|
|
1707
1716
|
name: "delete_post",
|
|
1708
1717
|
description: "Delete a post by postId. Also drops associated PostTag index entries.",
|
|
1709
1718
|
inputSchema: deletePostSchema,
|
|
1710
1719
|
handler: (args, ctx) => deletePost(ctx.graphql, args),
|
|
1720
|
+
readOnly: false,
|
|
1711
1721
|
destructive: true
|
|
1712
1722
|
},
|
|
1713
1723
|
{
|
|
1714
1724
|
name: "upload_media",
|
|
1715
1725
|
description: "Upload a file to the site's media S3 bucket. Pass base64-encoded bytes; the server stores them verbatim under public/media/YYYY/MM/. Returns the public URL and Media record. The server does not transcode \u2014 pre-process (e.g. resize/webp) on the client.",
|
|
1716
1726
|
inputSchema: uploadMediaSchema,
|
|
1717
|
-
handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(), args)
|
|
1727
|
+
handler: (args, ctx) => uploadMedia(ctx.graphql, ctx.storage(), args),
|
|
1728
|
+
readOnly: false,
|
|
1729
|
+
destructive: false
|
|
1718
1730
|
},
|
|
1719
1731
|
{
|
|
1720
1732
|
name: "list_media",
|
|
1721
1733
|
description: 'List media files in the CMS. Optional filters: `mimeType` (prefix match \u2014 "image/" matches all images, "image/png" only PNG), `prefix` (S3 key prefix on `src`, e.g. "public/media/2024/"), `createdAfter` / `createdBefore` (ISO 8601 date range). Returns up to `limit` rows (default 20) \u2014 each `{ mediaId, src, url, mimeType, size, createdAt, updatedAt }` \u2014 plus a `nextToken` cursor. Note: filters apply after the page read, so a page may return fewer than `limit` rows while a `nextToken` remains \u2014 follow the cursor. Use this to find media to delete without remembering `upload_media` responses.',
|
|
1722
1734
|
inputSchema: listMediaSchema,
|
|
1723
|
-
handler: (args, ctx) => listMedia(ctx.graphql, ctx.storage(), args)
|
|
1735
|
+
handler: (args, ctx) => listMedia(ctx.graphql, ctx.storage(), args),
|
|
1736
|
+
readOnly: true,
|
|
1737
|
+
destructive: false
|
|
1724
1738
|
},
|
|
1725
1739
|
{
|
|
1726
1740
|
name: "search_media",
|
|
1727
1741
|
description: 'Search media by substring (case-sensitive `contains`) across `src` (which includes the filename) and `mimeType`. Pass `query` (e.g. "logo", ".png", "image/png"). Walks DynamoDB pages internally until it collects at least `limit` matches (default 20), exhausts the table, or hits its page cap \u2014 so `limit` is a soft target and the result may run slightly past it. Returns the same row shape as `list_media` plus `nextToken`; `truncated: true` means the page cap was hit with more to scan (pass `nextToken` back to continue).',
|
|
1728
1742
|
inputSchema: searchMediaSchema,
|
|
1729
|
-
handler: (args, ctx) => searchMedia(ctx.graphql, ctx.storage(), args)
|
|
1743
|
+
handler: (args, ctx) => searchMedia(ctx.graphql, ctx.storage(), args),
|
|
1744
|
+
readOnly: true,
|
|
1745
|
+
destructive: false
|
|
1730
1746
|
},
|
|
1731
1747
|
{
|
|
1732
1748
|
name: "delete_media",
|
|
@@ -1737,13 +1753,16 @@ var tools = [
|
|
|
1737
1753
|
ctx.storage(),
|
|
1738
1754
|
args
|
|
1739
1755
|
),
|
|
1756
|
+
readOnly: false,
|
|
1740
1757
|
destructive: true
|
|
1741
1758
|
},
|
|
1742
1759
|
{
|
|
1743
1760
|
name: "get_schema",
|
|
1744
1761
|
description: "Returns the CMS content schema (Post/Page/Media field shapes, format enum, notes). Useful as the first call to understand what fields are available.",
|
|
1745
1762
|
inputSchema: getSchemaSchema,
|
|
1746
|
-
handler: async () => getSchema()
|
|
1763
|
+
handler: async () => getSchema(),
|
|
1764
|
+
readOnly: true,
|
|
1765
|
+
destructive: false
|
|
1747
1766
|
},
|
|
1748
1767
|
{
|
|
1749
1768
|
name: "upload_static_bundle",
|
|
@@ -1754,6 +1773,7 @@ var tools = [
|
|
|
1754
1773
|
ctx.storage(),
|
|
1755
1774
|
args
|
|
1756
1775
|
),
|
|
1776
|
+
readOnly: false,
|
|
1757
1777
|
destructive: true
|
|
1758
1778
|
},
|
|
1759
1779
|
{
|
|
@@ -1763,7 +1783,10 @@ var tools = [
|
|
|
1763
1783
|
handler: (args, ctx) => uploadStaticFile(
|
|
1764
1784
|
ctx.storage(),
|
|
1765
1785
|
args
|
|
1766
|
-
)
|
|
1786
|
+
),
|
|
1787
|
+
readOnly: false,
|
|
1788
|
+
// Overwrites whatever file currently sits at that S3 key.
|
|
1789
|
+
destructive: true
|
|
1767
1790
|
},
|
|
1768
1791
|
{
|
|
1769
1792
|
name: "delete_static_file",
|
|
@@ -1773,6 +1796,7 @@ var tools = [
|
|
|
1773
1796
|
ctx.storage(),
|
|
1774
1797
|
args
|
|
1775
1798
|
),
|
|
1799
|
+
readOnly: false,
|
|
1776
1800
|
destructive: true
|
|
1777
1801
|
},
|
|
1778
1802
|
{
|
|
@@ -1783,13 +1807,191 @@ var tools = [
|
|
|
1783
1807
|
ctx.graphql,
|
|
1784
1808
|
ctx.storage(),
|
|
1785
1809
|
args
|
|
1786
|
-
)
|
|
1810
|
+
),
|
|
1811
|
+
readOnly: false,
|
|
1812
|
+
// Rebuilds (overwrites) the existing Post row's manifest.
|
|
1813
|
+
destructive: true
|
|
1787
1814
|
}
|
|
1788
1815
|
];
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1816
|
+
|
|
1817
|
+
// ../mcp-server/dist/jsonrpc/index.js
|
|
1818
|
+
var JSON_RPC_PARSE_ERROR = -32700;
|
|
1819
|
+
var JSON_RPC_INVALID_REQUEST = -32600;
|
|
1820
|
+
var JSON_RPC_METHOD_NOT_FOUND = -32601;
|
|
1821
|
+
var JSON_RPC_INVALID_PARAMS = -32602;
|
|
1822
|
+
var JSON_RPC_INTERNAL_ERROR = -32603;
|
|
1823
|
+
var SUPPORTED_PROTOCOL_VERSIONS = ["2025-03-26", "2024-11-05"];
|
|
1824
|
+
var LATEST_SUPPORTED_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[0];
|
|
1825
|
+
function jsonRpcResult(id, result) {
|
|
1826
|
+
return { jsonrpc: "2.0", id, result };
|
|
1827
|
+
}
|
|
1828
|
+
function jsonRpcError(id, code, message, data) {
|
|
1829
|
+
const error = { code, message };
|
|
1830
|
+
if (data !== void 0) error.data = data;
|
|
1831
|
+
return { jsonrpc: "2.0", id, error };
|
|
1832
|
+
}
|
|
1833
|
+
function isNotification(req) {
|
|
1834
|
+
return req.id === void 0;
|
|
1835
|
+
}
|
|
1836
|
+
function hasValidJsonRpcId(req) {
|
|
1837
|
+
const id = req.id;
|
|
1838
|
+
if (id === void 0) return true;
|
|
1839
|
+
return typeof id === "string" || typeof id === "number" && Number.isInteger(id);
|
|
1840
|
+
}
|
|
1841
|
+
function isPlainObject(v) {
|
|
1842
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1843
|
+
}
|
|
1844
|
+
var MAX_BATCH = 50;
|
|
1845
|
+
function idForError(input) {
|
|
1846
|
+
if (isPlainObject(input) && hasValidJsonRpcId(input)) {
|
|
1847
|
+
return input.id ?? null;
|
|
1848
|
+
}
|
|
1849
|
+
return null;
|
|
1850
|
+
}
|
|
1851
|
+
function validateEnvelope(input, inBatch) {
|
|
1852
|
+
if (!isPlainObject(input)) {
|
|
1853
|
+
return { ok: false, error: jsonRpcError(null, JSON_RPC_INVALID_REQUEST, "Invalid Request") };
|
|
1854
|
+
}
|
|
1855
|
+
const jsonrpc = input.jsonrpc;
|
|
1856
|
+
const method = input.method;
|
|
1857
|
+
if (jsonrpc !== "2.0" || typeof method !== "string" || !hasValidJsonRpcId(input)) {
|
|
1858
|
+
return {
|
|
1859
|
+
ok: false,
|
|
1860
|
+
error: jsonRpcError(idForError(input), JSON_RPC_INVALID_REQUEST, "Invalid Request")
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
if (inBatch && method === "initialize") {
|
|
1864
|
+
return {
|
|
1865
|
+
ok: false,
|
|
1866
|
+
error: jsonRpcError(
|
|
1867
|
+
idForError(input),
|
|
1868
|
+
JSON_RPC_INVALID_REQUEST,
|
|
1869
|
+
"Invalid Request: `initialize` is not allowed in a batch"
|
|
1870
|
+
)
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
return { ok: true, req: input };
|
|
1874
|
+
}
|
|
1875
|
+
async function dispatchJsonRpcMessage(input, opts) {
|
|
1876
|
+
const maxBatch = opts.maxBatch ?? MAX_BATCH;
|
|
1877
|
+
if (Array.isArray(input)) {
|
|
1878
|
+
if (input.length === 0 || input.length > maxBatch) {
|
|
1879
|
+
return {
|
|
1880
|
+
status: "invalid",
|
|
1881
|
+
body: jsonRpcError(
|
|
1882
|
+
null,
|
|
1883
|
+
JSON_RPC_INVALID_REQUEST,
|
|
1884
|
+
input.length === 0 ? "Invalid Request: empty batch" : `Invalid Request: batch exceeds the maximum of ${maxBatch} elements`
|
|
1885
|
+
)
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
const responses = [];
|
|
1889
|
+
for (const element of input) {
|
|
1890
|
+
const validated2 = validateEnvelope(element, true);
|
|
1891
|
+
if (!validated2.ok) {
|
|
1892
|
+
responses.push(validated2.error);
|
|
1893
|
+
continue;
|
|
1894
|
+
}
|
|
1895
|
+
const res2 = await dispatchJsonRpc(validated2.req, opts);
|
|
1896
|
+
if (res2 !== null) responses.push(res2);
|
|
1897
|
+
}
|
|
1898
|
+
return responses.length === 0 ? { status: "no-content" } : { status: "ok", body: responses };
|
|
1899
|
+
}
|
|
1900
|
+
const validated = validateEnvelope(input, false);
|
|
1901
|
+
if (!validated.ok) {
|
|
1902
|
+
return { status: "invalid", body: validated.error };
|
|
1903
|
+
}
|
|
1904
|
+
const res = await dispatchJsonRpc(validated.req, opts);
|
|
1905
|
+
return res === null ? { status: "no-content" } : { status: "ok", body: res };
|
|
1906
|
+
}
|
|
1907
|
+
function toolAnnotations(t) {
|
|
1908
|
+
const annotations = {};
|
|
1909
|
+
if (typeof t.readOnly === "boolean") annotations.readOnlyHint = t.readOnly;
|
|
1910
|
+
if (typeof t.destructive === "boolean") annotations.destructiveHint = t.destructive;
|
|
1911
|
+
return annotations;
|
|
1912
|
+
}
|
|
1913
|
+
async function dispatchJsonRpc(req, opts) {
|
|
1914
|
+
if (!hasValidJsonRpcId(req)) {
|
|
1915
|
+
return jsonRpcError(
|
|
1916
|
+
null,
|
|
1917
|
+
JSON_RPC_INVALID_REQUEST,
|
|
1918
|
+
"Invalid Request: `id` must be a string or an integer"
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
const response = await dispatchMethod(req, opts);
|
|
1922
|
+
return isNotification(req) ? null : response;
|
|
1923
|
+
}
|
|
1924
|
+
async function dispatchMethod(req, opts) {
|
|
1925
|
+
const id = req.id ?? null;
|
|
1926
|
+
switch (req.method) {
|
|
1927
|
+
case "initialize": {
|
|
1928
|
+
const requested = req.params?.protocolVersion;
|
|
1929
|
+
if (typeof requested !== "string") {
|
|
1930
|
+
return jsonRpcError(
|
|
1931
|
+
id,
|
|
1932
|
+
JSON_RPC_INVALID_PARAMS,
|
|
1933
|
+
"initialize requires a string `protocolVersion` parameter"
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(
|
|
1937
|
+
requested
|
|
1938
|
+
) ? requested : LATEST_SUPPORTED_PROTOCOL_VERSION;
|
|
1939
|
+
return jsonRpcResult(id, {
|
|
1940
|
+
protocolVersion,
|
|
1941
|
+
capabilities: { tools: {} },
|
|
1942
|
+
serverInfo: opts.serverInfo
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
case "notifications/initialized":
|
|
1946
|
+
return jsonRpcResult(id, {});
|
|
1947
|
+
case "tools/list":
|
|
1948
|
+
return jsonRpcResult(id, {
|
|
1949
|
+
tools: opts.tools.map((t) => ({
|
|
1950
|
+
name: t.name,
|
|
1951
|
+
description: t.description,
|
|
1952
|
+
inputSchema: t.inputSchema,
|
|
1953
|
+
annotations: toolAnnotations(t)
|
|
1954
|
+
}))
|
|
1955
|
+
});
|
|
1956
|
+
case "tools/call": {
|
|
1957
|
+
const params = req.params;
|
|
1958
|
+
if (!params || typeof params.name !== "string") {
|
|
1959
|
+
return jsonRpcError(id, JSON_RPC_INVALID_PARAMS, "tools/call requires a `name` parameter");
|
|
1960
|
+
}
|
|
1961
|
+
const rawArgs = params.arguments;
|
|
1962
|
+
if (rawArgs !== void 0 && !isPlainObject(rawArgs)) {
|
|
1963
|
+
return jsonRpcError(
|
|
1964
|
+
id,
|
|
1965
|
+
JSON_RPC_INVALID_PARAMS,
|
|
1966
|
+
"tools/call `arguments` must be an object"
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1969
|
+
const tool = opts.tools.find((t) => t.name === params.name);
|
|
1970
|
+
if (!tool) {
|
|
1971
|
+
return jsonRpcError(id, JSON_RPC_METHOD_NOT_FOUND, `unknown tool: ${params.name}`);
|
|
1972
|
+
}
|
|
1973
|
+
const ctx = opts.getContext();
|
|
1974
|
+
try {
|
|
1975
|
+
const result = await tool.handler(rawArgs ?? {}, ctx);
|
|
1976
|
+
return jsonRpcResult(id, {
|
|
1977
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1978
|
+
});
|
|
1979
|
+
} catch (err2) {
|
|
1980
|
+
const rawMessage = err2 instanceof Error ? err2.message : String(err2);
|
|
1981
|
+
console.error("[mcp-jsonrpc] tool dispatch failed", {
|
|
1982
|
+
tool: params.name,
|
|
1983
|
+
message: rawMessage
|
|
1984
|
+
});
|
|
1985
|
+
const message = opts.formatToolError ? opts.formatToolError(err2) : rawMessage;
|
|
1986
|
+
return jsonRpcResult(id, {
|
|
1987
|
+
isError: true,
|
|
1988
|
+
content: [{ type: "text", text: message }]
|
|
1989
|
+
});
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
default:
|
|
1993
|
+
return jsonRpcError(id, JSON_RPC_METHOD_NOT_FOUND, `Method not found: ${req.method}`);
|
|
1994
|
+
}
|
|
1793
1995
|
}
|
|
1794
1996
|
|
|
1795
1997
|
// src/functions/mcp-graphql-client.ts
|
|
@@ -1909,12 +2111,6 @@ function createMcpStorageClient(opts) {
|
|
|
1909
2111
|
}
|
|
1910
2112
|
|
|
1911
2113
|
// src/functions/mcp-handler.ts
|
|
1912
|
-
var JSON_RPC_PARSE_ERROR = -32700;
|
|
1913
|
-
var JSON_RPC_INVALID_REQUEST = -32600;
|
|
1914
|
-
var JSON_RPC_METHOD_NOT_FOUND = -32601;
|
|
1915
|
-
var JSON_RPC_INVALID_PARAMS = -32602;
|
|
1916
|
-
var JSON_RPC_INTERNAL_ERROR = -32603;
|
|
1917
|
-
var MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
1918
2114
|
var LAST_USED_THROTTLE_MS = 6e4;
|
|
1919
2115
|
function requireEnv(name) {
|
|
1920
2116
|
const v = process.env[name];
|
|
@@ -1937,14 +2133,6 @@ function jsonResponse(statusCode, body) {
|
|
|
1937
2133
|
body: JSON.stringify(body)
|
|
1938
2134
|
};
|
|
1939
2135
|
}
|
|
1940
|
-
function jsonRpcResult(id, result) {
|
|
1941
|
-
return { jsonrpc: "2.0", id, result };
|
|
1942
|
-
}
|
|
1943
|
-
function jsonRpcError(id, code, message, data) {
|
|
1944
|
-
const error = { code, message };
|
|
1945
|
-
if (data !== void 0) error.data = data;
|
|
1946
|
-
return { jsonrpc: "2.0", id, error };
|
|
1947
|
-
}
|
|
1948
2136
|
async function validateBearer(plaintext) {
|
|
1949
2137
|
const hash = hashToken(plaintext);
|
|
1950
2138
|
const res = await ddb.send(
|
|
@@ -1997,55 +2185,6 @@ function makeContext() {
|
|
|
1997
2185
|
cachedCtx = ctx;
|
|
1998
2186
|
return ctx;
|
|
1999
2187
|
}
|
|
2000
|
-
async function dispatchJsonRpc(req) {
|
|
2001
|
-
switch (req.method) {
|
|
2002
|
-
case "initialize":
|
|
2003
|
-
return jsonRpcResult(req.id, {
|
|
2004
|
-
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
2005
|
-
capabilities: { tools: {} },
|
|
2006
|
-
serverInfo: { name: "ampless-mcp", version: "0.2" }
|
|
2007
|
-
});
|
|
2008
|
-
case "notifications/initialized":
|
|
2009
|
-
return jsonRpcResult(req.id, null);
|
|
2010
|
-
case "tools/list":
|
|
2011
|
-
return jsonRpcResult(req.id, {
|
|
2012
|
-
tools: HTTP_TOOLS.map((t) => ({
|
|
2013
|
-
name: t.name,
|
|
2014
|
-
description: t.description,
|
|
2015
|
-
inputSchema: t.inputSchema
|
|
2016
|
-
}))
|
|
2017
|
-
});
|
|
2018
|
-
case "tools/call": {
|
|
2019
|
-
const params = req.params;
|
|
2020
|
-
if (!params?.name || typeof params.name !== "string") {
|
|
2021
|
-
return jsonRpcError(req.id, JSON_RPC_INVALID_PARAMS, "tools/call requires a `name` parameter");
|
|
2022
|
-
}
|
|
2023
|
-
const tool = HTTP_TOOLS.find((t) => t.name === params.name);
|
|
2024
|
-
if (!tool) {
|
|
2025
|
-
return jsonRpcError(req.id, JSON_RPC_METHOD_NOT_FOUND, `unknown tool: ${params.name}`);
|
|
2026
|
-
}
|
|
2027
|
-
const ctx = makeContext();
|
|
2028
|
-
try {
|
|
2029
|
-
const result = await dispatchToolCall(params.name, params.arguments ?? {}, ctx);
|
|
2030
|
-
return jsonRpcResult(req.id, {
|
|
2031
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
2032
|
-
});
|
|
2033
|
-
} catch (err2) {
|
|
2034
|
-
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
2035
|
-
console.error("[mcp-handler] tool dispatch failed", {
|
|
2036
|
-
tool: params.name,
|
|
2037
|
-
message
|
|
2038
|
-
});
|
|
2039
|
-
return jsonRpcResult(req.id, {
|
|
2040
|
-
isError: true,
|
|
2041
|
-
content: [{ type: "text", text: message }]
|
|
2042
|
-
});
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
default:
|
|
2046
|
-
return jsonRpcError(req.id, JSON_RPC_METHOD_NOT_FOUND, `Method not found: ${req.method}`);
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
2188
|
var handler = async (event) => {
|
|
2050
2189
|
if (event.requestContext?.http?.method === "OPTIONS") {
|
|
2051
2190
|
return { statusCode: 204, body: "" };
|
|
@@ -2064,32 +2203,37 @@ var handler = async (event) => {
|
|
|
2064
2203
|
return jsonResponse(401, { error: "invalid_token" });
|
|
2065
2204
|
}
|
|
2066
2205
|
await touchLastUsedAt(meta.hash);
|
|
2067
|
-
let
|
|
2206
|
+
let parsed;
|
|
2068
2207
|
try {
|
|
2069
2208
|
const body = event.isBase64Encoded ? Buffer.from(event.body ?? "", "base64").toString("utf8") : event.body ?? "";
|
|
2070
2209
|
if (!body) {
|
|
2071
2210
|
return jsonResponse(400, jsonRpcError(null, JSON_RPC_INVALID_REQUEST, "Empty body"));
|
|
2072
2211
|
}
|
|
2073
|
-
|
|
2212
|
+
parsed = JSON.parse(body);
|
|
2074
2213
|
} catch {
|
|
2075
2214
|
return jsonResponse(400, jsonRpcError(null, JSON_RPC_PARSE_ERROR, "Parse error"));
|
|
2076
2215
|
}
|
|
2077
|
-
if (req.jsonrpc !== "2.0" || typeof req.method !== "string") {
|
|
2078
|
-
return jsonResponse(
|
|
2079
|
-
400,
|
|
2080
|
-
jsonRpcError(req?.id ?? null, JSON_RPC_INVALID_REQUEST, "Invalid Request")
|
|
2081
|
-
);
|
|
2082
|
-
}
|
|
2083
2216
|
try {
|
|
2084
|
-
const
|
|
2085
|
-
|
|
2217
|
+
const result = await dispatchJsonRpcMessage(parsed, {
|
|
2218
|
+
tools: HTTP_TOOLS,
|
|
2219
|
+
getContext: makeContext,
|
|
2220
|
+
serverInfo: { name: "ampless-mcp", version: "0.2" }
|
|
2221
|
+
});
|
|
2222
|
+
if (result.status === "invalid") {
|
|
2223
|
+
return jsonResponse(400, result.body);
|
|
2224
|
+
}
|
|
2225
|
+
if (result.status === "no-content") {
|
|
2226
|
+
return { statusCode: 202, body: "" };
|
|
2227
|
+
}
|
|
2228
|
+
return jsonResponse(200, result.body);
|
|
2086
2229
|
} catch (err2) {
|
|
2230
|
+
const method = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed.method : void 0;
|
|
2087
2231
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
2088
|
-
console.error("[mcp-handler] dispatch threw", {
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
);
|
|
2232
|
+
console.error("[mcp-handler] dispatch threw", {
|
|
2233
|
+
method: typeof method === "string" ? method : void 0,
|
|
2234
|
+
message
|
|
2235
|
+
});
|
|
2236
|
+
return jsonResponse(500, jsonRpcError(null, JSON_RPC_INTERNAL_ERROR, message));
|
|
2093
2237
|
}
|
|
2094
2238
|
};
|
|
2095
2239
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampless/backend",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.90",
|
|
4
4
|
"description": "Amplify Gen 2 backend factories for ampless: auth, data, storage, event processors, API key renewer",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"@smithy/protocol-http": "^5.4.4",
|
|
70
70
|
"@smithy/signature-v4": "^5.4.4",
|
|
71
71
|
"fflate": "^0.8.3",
|
|
72
|
-
"@ampless/mcp-server": "1.0.0-beta.
|
|
73
|
-
"ampless": "1.0.0-beta.
|
|
72
|
+
"@ampless/mcp-server": "1.0.0-beta.67",
|
|
73
|
+
"ampless": "1.0.0-beta.60"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@aws-amplify/backend": "^1.19.0",
|