@tinycloud/mcp 0.8.3-beta.14 → 0.8.3-beta.16
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/README.md +17 -42
- package/dist/cli.cjs +3 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/http-cli.cjs +203 -7
- package/dist/http-cli.cjs.map +1 -1
- package/dist/http-cli.js +203 -7
- package/dist/http-cli.js.map +1 -1
- package/dist/http.cjs +203 -7
- package/dist/http.cjs.map +1 -1
- package/dist/http.js +203 -7
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +203 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +203 -7
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +3 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/dist/server.js.map +1 -1
- package/package.json +3 -3
- package/skills/tinycloud-delegated-secrets/SKILL.md +17 -3
package/dist/http-cli.cjs
CHANGED
|
@@ -1555,7 +1555,7 @@ function outcomeCode(result) {
|
|
|
1555
1555
|
// package.json
|
|
1556
1556
|
var package_default = {
|
|
1557
1557
|
name: "@tinycloud/mcp",
|
|
1558
|
-
version: "0.8.3-beta.
|
|
1558
|
+
version: "0.8.3-beta.16",
|
|
1559
1559
|
description: "TinyCloud delegated operations MCP server",
|
|
1560
1560
|
keywords: [
|
|
1561
1561
|
"tinycloud",
|
|
@@ -1626,9 +1626,9 @@ var package_default = {
|
|
|
1626
1626
|
dependencies: {
|
|
1627
1627
|
"@modelcontextprotocol/node": "2.0.0-beta.5",
|
|
1628
1628
|
"@modelcontextprotocol/server": "2.0.0-beta.5",
|
|
1629
|
-
"@tinycloud/node-sdk": "3.8.0-beta.
|
|
1629
|
+
"@tinycloud/node-sdk": "3.8.0-beta.15",
|
|
1630
1630
|
"@tinycloud/node-sdk-wasm": "1.7.8",
|
|
1631
|
-
"@tinycloud/operations": "0.7.7-beta.
|
|
1631
|
+
"@tinycloud/operations": "0.7.7-beta.14",
|
|
1632
1632
|
jose: "6.2.4"
|
|
1633
1633
|
},
|
|
1634
1634
|
devDependencies: {
|
|
@@ -1652,6 +1652,8 @@ var MCP_SERVER_NAME = "tinycloud-mcp";
|
|
|
1652
1652
|
|
|
1653
1653
|
// src/http.ts
|
|
1654
1654
|
var REQUIRED_SCOPE = "tinycloud:mcp";
|
|
1655
|
+
var LOCAL_CLI_PACKAGE = ["@tinycloud", "cli"].join("/");
|
|
1656
|
+
var LOCAL_MCP_PACKAGE = ["@tinycloud", "mcp"].join("/");
|
|
1655
1657
|
function createHostedMcpApp(config) {
|
|
1656
1658
|
const resourceUrl = canonicalResourceUrl(config.publicUrl);
|
|
1657
1659
|
const pluginUrl = new URL("./chatgpt/mcp", resourceUrl);
|
|
@@ -1731,14 +1733,173 @@ function createHostedSurface(config, resourceUrl, surface, tokenStatus) {
|
|
|
1731
1733
|
const allowedHosts = [resourceUrl.hostname];
|
|
1732
1734
|
return {
|
|
1733
1735
|
async fetch(request) {
|
|
1734
|
-
const metadata = (0, import_server4.oauthMetadataResponse)(request, metadataOptions);
|
|
1735
|
-
if (metadata !== void 0) return metadata;
|
|
1736
1736
|
const url = new URL(request.url);
|
|
1737
|
+
if (url.pathname !== "/healthz") {
|
|
1738
|
+
const hostFailure = (0, import_server4.hostHeaderValidationResponse)(request, allowedHosts);
|
|
1739
|
+
if (hostFailure !== void 0) return hostFailure;
|
|
1740
|
+
}
|
|
1741
|
+
if (url.pathname === "/.well-known/oauth-protected-resource" && surface === "hosted") {
|
|
1742
|
+
const metadataUrl = new URL((0, import_server4.getOAuthProtectedResourceMetadataUrl)(resourceUrl));
|
|
1743
|
+
const metadataRequest = new Request(metadataUrl.toString(), {
|
|
1744
|
+
method: request.method,
|
|
1745
|
+
headers: request.headers
|
|
1746
|
+
});
|
|
1747
|
+
const response = (0, import_server4.oauthMetadataResponse)(metadataRequest, metadataOptions);
|
|
1748
|
+
if (response !== void 0) return withBearerMethodsSupported(response);
|
|
1749
|
+
}
|
|
1750
|
+
const metadata = (0, import_server4.oauthMetadataResponse)(request, metadataOptions);
|
|
1751
|
+
if (metadata !== void 0) {
|
|
1752
|
+
return surface === "hosted" ? withBearerMethodsSupported(metadata) : metadata;
|
|
1753
|
+
}
|
|
1754
|
+
if (url.pathname === "/.well-known/ai-catalog.json" && surface === "hosted") {
|
|
1755
|
+
if (request.method === "OPTIONS") {
|
|
1756
|
+
return publicMetadataCors(new Response(null, { status: 204 }), request);
|
|
1757
|
+
}
|
|
1758
|
+
if (request.method !== "GET") return methodNotAllowed("GET");
|
|
1759
|
+
return publicMetadataCors(Response.json({
|
|
1760
|
+
specVersion: "1.0",
|
|
1761
|
+
host: {
|
|
1762
|
+
displayName: "TinyCloud, Inc.",
|
|
1763
|
+
identifier: "https://mcp.tinycloud.xyz",
|
|
1764
|
+
documentationUrl: "https://docs.tinycloud.xyz/cli/mcp"
|
|
1765
|
+
},
|
|
1766
|
+
entries: [{
|
|
1767
|
+
identifier: "urn:air:tinycloud.xyz:mcp:tinycloud-mcp",
|
|
1768
|
+
displayName: "TinyCloud MCP",
|
|
1769
|
+
type: "application/json",
|
|
1770
|
+
description: "Hosted Streamable HTTP MCP server for delegated TinyCloud operations.",
|
|
1771
|
+
url: new URL("/.well-known/mcp/server-card.json", resourceUrl).toString(),
|
|
1772
|
+
representativeQueries: [
|
|
1773
|
+
"How do I connect to TinyCloud's hosted MCP server?",
|
|
1774
|
+
"Which endpoint provides TinyCloud's delegated MCP operations?",
|
|
1775
|
+
"How can I connect to TinyCloud MCP using local stdio?"
|
|
1776
|
+
]
|
|
1777
|
+
}]
|
|
1778
|
+
}, {
|
|
1779
|
+
headers: {
|
|
1780
|
+
"cache-control": "public, max-age=300",
|
|
1781
|
+
"content-type": "application/json; charset=utf-8"
|
|
1782
|
+
}
|
|
1783
|
+
}), request);
|
|
1784
|
+
}
|
|
1785
|
+
if (url.pathname === "/.well-known/poppy.json" && surface === "hosted") {
|
|
1786
|
+
if (request.method === "OPTIONS") {
|
|
1787
|
+
return publicMetadataCors(new Response(null, { status: 204 }), request);
|
|
1788
|
+
}
|
|
1789
|
+
if (request.method !== "GET") return methodNotAllowed("GET");
|
|
1790
|
+
return publicMetadataCors(Response.json({
|
|
1791
|
+
protocol_version: "0.1",
|
|
1792
|
+
organization: {
|
|
1793
|
+
name: "TinyCloud, Inc.",
|
|
1794
|
+
domain: resourceUrl.hostname
|
|
1795
|
+
},
|
|
1796
|
+
web: {}
|
|
1797
|
+
}, {
|
|
1798
|
+
headers: {
|
|
1799
|
+
"cache-control": "public, max-age=300",
|
|
1800
|
+
"content-type": "application/json; charset=utf-8"
|
|
1801
|
+
}
|
|
1802
|
+
}), request);
|
|
1803
|
+
}
|
|
1804
|
+
if (url.pathname === "/auth.md" && surface === "hosted") {
|
|
1805
|
+
if (request.method === "OPTIONS") {
|
|
1806
|
+
return publicMetadataCors(new Response(null, { status: 204 }), request);
|
|
1807
|
+
}
|
|
1808
|
+
if (request.method !== "GET") return methodNotAllowed("GET");
|
|
1809
|
+
const issuerUrl = new URL(issuer);
|
|
1810
|
+
const authorizationServerMetadataUrl = new URL(
|
|
1811
|
+
`/.well-known/oauth-authorization-server${issuerUrl.pathname}`,
|
|
1812
|
+
issuerUrl.origin
|
|
1813
|
+
);
|
|
1814
|
+
const registrationEndpoint = config.oauthMetadata.registration_endpoint;
|
|
1815
|
+
const registrationNote = typeof registrationEndpoint === "string" ? `OpenKey's OAuth \`registration_endpoint\` is ${registrationEndpoint}; it registers OAuth clients, not TinyCloud users or agents.` : "The authorization server does not advertise an OAuth client registration endpoint.";
|
|
1816
|
+
const authDoc = [
|
|
1817
|
+
"# TinyCloud MCP auth.md",
|
|
1818
|
+
"",
|
|
1819
|
+
`Protected resource: ${resourceUrl}.`,
|
|
1820
|
+
"",
|
|
1821
|
+
"Send an OAuth access token in the `Authorization: Bearer` header.",
|
|
1822
|
+
"",
|
|
1823
|
+
`Protected Resource Metadata: ${new URL("/.well-known/oauth-protected-resource", resourceUrl)}.`,
|
|
1824
|
+
`Authorization server issuer: ${issuer}.`,
|
|
1825
|
+
`Authorization server metadata: ${authorizationServerMetadataUrl}.`,
|
|
1826
|
+
`Scope: \`${REQUIRED_SCOPE}\`.`,
|
|
1827
|
+
"",
|
|
1828
|
+
"Hosted access uses OpenKey OAuth.",
|
|
1829
|
+
registrationNote,
|
|
1830
|
+
"TinyCloud does not host an Auth.md agent-enrollment or user-registration endpoint for this service.",
|
|
1831
|
+
"No `agent_auth` registration block is published because no agent-registration endpoint is available.",
|
|
1832
|
+
"",
|
|
1833
|
+
`Local stdio is a separate option configured with \`${LOCAL_CLI_PACKAGE}\` and \`${LOCAL_MCP_PACKAGE}\`.`,
|
|
1834
|
+
"",
|
|
1835
|
+
"Connection steps: https://docs.tinycloud.xyz/cli/mcp"
|
|
1836
|
+
].join("\n") + "\n";
|
|
1837
|
+
return publicMetadataCors(publicText(authDoc, "text/markdown; charset=utf-8"), request);
|
|
1838
|
+
}
|
|
1839
|
+
if (url.pathname === "/robots.txt" && surface === "hosted") {
|
|
1840
|
+
return request.method === "GET" ? publicText(`User-agent: *
|
|
1841
|
+
Allow: /
|
|
1842
|
+
Sitemap: ${new URL("/sitemap.xml", resourceUrl)}
|
|
1843
|
+
Agentmap: ${new URL("/.well-known/ai-catalog.json", resourceUrl)}
|
|
1844
|
+
`, "text/plain; charset=utf-8") : methodNotAllowed("GET");
|
|
1845
|
+
}
|
|
1846
|
+
if ((url.pathname === "/mcp/server-card" || url.pathname === "/.well-known/mcp/server-card.json") && surface === "hosted") {
|
|
1847
|
+
if (request.method === "OPTIONS") {
|
|
1848
|
+
return publicMetadataCors(new Response(null, { status: 204 }), request);
|
|
1849
|
+
}
|
|
1850
|
+
if (request.method !== "GET") return methodNotAllowed("GET");
|
|
1851
|
+
return publicMetadataCors(Response.json({
|
|
1852
|
+
$schema: "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
|
|
1853
|
+
name: `xyz.tinycloud/${MCP_SERVER_NAME}`,
|
|
1854
|
+
title: "TinyCloud MCP",
|
|
1855
|
+
version: MCP_VERSION,
|
|
1856
|
+
description: "MCP server for delegated TinyCloud operations.",
|
|
1857
|
+
websiteUrl: "https://docs.tinycloud.xyz/cli/mcp",
|
|
1858
|
+
repository: { source: "github", url: "https://github.com/TinyCloudLabs/js-sdk", subfolder: "packages/mcp" },
|
|
1859
|
+
remotes: [{ type: "streamable-http", url: resourceUrl.toString() }]
|
|
1860
|
+
}, { headers: { "cache-control": "public, max-age=300" } }), request);
|
|
1861
|
+
}
|
|
1862
|
+
if (url.pathname === "/llms.txt" && surface === "hosted") {
|
|
1863
|
+
return request.method === "GET" ? publicText(`# TinyCloud MCP
|
|
1864
|
+
|
|
1865
|
+
Hosted Streamable HTTP endpoint: https://mcp.tinycloud.xyz/mcp (OpenKey OAuth).
|
|
1866
|
+
|
|
1867
|
+
Connection documentation: https://docs.tinycloud.xyz/cli/mcp
|
|
1868
|
+
|
|
1869
|
+
Local alternative: install ${LOCAL_CLI_PACKAGE} and ${LOCAL_MCP_PACKAGE}, then set up tinycloud-mcp as a stdio server in your MCP client.
|
|
1870
|
+
`, "text/plain; charset=utf-8") : methodNotAllowed("GET");
|
|
1871
|
+
}
|
|
1872
|
+
if (url.pathname === "/sitemap.xml" && surface === "hosted") {
|
|
1873
|
+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>${new URL("/", resourceUrl)}</loc></url></urlset>`;
|
|
1874
|
+
return request.method === "GET" ? publicText(sitemap, "application/xml; charset=utf-8") : methodNotAllowed("GET");
|
|
1875
|
+
}
|
|
1876
|
+
if (url.pathname === "/" && surface === "hosted") {
|
|
1877
|
+
if (request.method !== "GET" && request.method !== "HEAD") return methodNotAllowed("GET, HEAD");
|
|
1878
|
+
const markdown = request.headers.get("accept")?.includes("text/markdown") ?? false;
|
|
1879
|
+
const content = markdown ? `# TinyCloud MCP
|
|
1880
|
+
|
|
1881
|
+
Hosted endpoint: <https://mcp.tinycloud.xyz/mcp> (Streamable HTTP, OpenKey OAuth).
|
|
1882
|
+
|
|
1883
|
+
[Connection documentation](https://docs.tinycloud.xyz/cli/mcp).
|
|
1884
|
+
|
|
1885
|
+
Local alternative: install \`${LOCAL_CLI_PACKAGE}\` and \`${LOCAL_MCP_PACKAGE}\` and connect the \`tinycloud-mcp\` stdio server.
|
|
1886
|
+
` : `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="ai-catalog" href="/.well-known/ai-catalog.json"><title>TinyCloud MCP</title></head><main><h1>TinyCloud MCP</h1><p>Hosted MCP endpoint: <code>https://mcp.tinycloud.xyz/mcp</code> (Streamable HTTP, OpenKey OAuth).</p><p><a href="https://docs.tinycloud.xyz/cli/mcp">Connection documentation</a></p><p>Local alternative: install <code>${LOCAL_CLI_PACKAGE}</code> and <code>${LOCAL_MCP_PACKAGE}</code> and connect the <code>tinycloud-mcp</code> stdio server.</p></main></html>`;
|
|
1887
|
+
const headers = new Headers({
|
|
1888
|
+
"content-type": markdown ? "text/markdown; charset=utf-8" : "text/html; charset=utf-8",
|
|
1889
|
+
"cache-control": "public, max-age=300",
|
|
1890
|
+
"content-security-policy": "default-src 'none'; base-uri 'none'; frame-ancestors 'none'",
|
|
1891
|
+
"x-content-type-options": "nosniff",
|
|
1892
|
+
vary: "Accept"
|
|
1893
|
+
});
|
|
1894
|
+
headers.set(
|
|
1895
|
+
"link",
|
|
1896
|
+
`<${new URL("/mcp/server-card", resourceUrl)}>; rel="describedby"; type="application/json", <https://docs.tinycloud.xyz/cli/mcp>; rel="service-doc"`
|
|
1897
|
+
);
|
|
1898
|
+
return new Response(request.method === "HEAD" ? null : content, { headers });
|
|
1899
|
+
}
|
|
1737
1900
|
if (url.pathname === "/healthz") {
|
|
1738
1901
|
return request.method === "GET" ? json({ ok: true }) : methodNotAllowed("GET");
|
|
1739
1902
|
}
|
|
1740
|
-
const hostFailure = (0, import_server4.hostHeaderValidationResponse)(request, allowedHosts);
|
|
1741
|
-
if (hostFailure !== void 0) return hostFailure;
|
|
1742
1903
|
if (url.pathname === connectPath) {
|
|
1743
1904
|
if (request.method !== "GET") return methodNotAllowed("GET");
|
|
1744
1905
|
try {
|
|
@@ -1908,6 +2069,41 @@ function callbackCors(response, openkeyHost) {
|
|
|
1908
2069
|
function json(value, status = 200) {
|
|
1909
2070
|
return Response.json(value, { status, headers: { "cache-control": "no-store" } });
|
|
1910
2071
|
}
|
|
2072
|
+
async function withBearerMethodsSupported(response) {
|
|
2073
|
+
if (response.status === 204 || response.body === null) return response;
|
|
2074
|
+
const metadata = await response.json();
|
|
2075
|
+
const headers = new Headers(response.headers);
|
|
2076
|
+
headers.delete("content-length");
|
|
2077
|
+
headers.delete("content-encoding");
|
|
2078
|
+
return new Response(JSON.stringify({ ...metadata, bearer_methods_supported: ["header"] }), {
|
|
2079
|
+
status: response.status,
|
|
2080
|
+
statusText: response.statusText,
|
|
2081
|
+
headers
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
2084
|
+
function publicMetadataCors(response, request) {
|
|
2085
|
+
const headers = new Headers(response.headers);
|
|
2086
|
+
headers.set("access-control-allow-origin", "*");
|
|
2087
|
+
headers.set("access-control-allow-methods", "GET, OPTIONS");
|
|
2088
|
+
const requestedHeaders = request.method === "OPTIONS" ? request.headers.get("access-control-request-headers") : null;
|
|
2089
|
+
if (requestedHeaders !== null) {
|
|
2090
|
+
headers.set("access-control-allow-headers", requestedHeaders);
|
|
2091
|
+
}
|
|
2092
|
+
headers.set(
|
|
2093
|
+
"vary",
|
|
2094
|
+
requestedHeaders === null ? "Origin" : "Origin, Access-Control-Request-Headers"
|
|
2095
|
+
);
|
|
2096
|
+
return new Response(response.body, { status: response.status, headers });
|
|
2097
|
+
}
|
|
2098
|
+
function publicText(text, contentType) {
|
|
2099
|
+
return new Response(text, {
|
|
2100
|
+
headers: {
|
|
2101
|
+
"content-type": contentType,
|
|
2102
|
+
"cache-control": "public, max-age=300",
|
|
2103
|
+
"x-content-type-options": "nosniff"
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
1911
2107
|
function methodNotAllowed(allow) {
|
|
1912
2108
|
return new Response("Method not allowed", { status: 405, headers: { allow } });
|
|
1913
2109
|
}
|