@tinycloud/mcp 0.5.0-beta.5 → 0.5.0-beta.7
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 +12 -5
- package/dist/cli.cjs +7 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +7 -4
- package/dist/cli.js.map +1 -1
- package/dist/http-cli.cjs +53 -78
- package/dist/http-cli.cjs.map +1 -1
- package/dist/http-cli.js +52 -75
- package/dist/http-cli.js.map +1 -1
- package/dist/http.cjs +53 -78
- package/dist/http.cjs.map +1 -1
- package/dist/http.js +52 -75
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +53 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +52 -75
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +7 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +7 -4
- package/dist/server.js.map +1 -1
- package/dist/tools.cjs +4 -1
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.js +4 -1
- package/dist/tools.js.map +1 -1
- package/generated/mcp-facts.json +1 -2
- package/package.json +3 -3
- package/skills/tinycloud-delegated-secrets/SKILL.md +1 -1
package/dist/http-cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
OAuthError,
|
|
20
20
|
OAuthErrorCode
|
|
21
21
|
} from "@modelcontextprotocol/server";
|
|
22
|
+
import { pkhDid } from "@tinycloud/node-sdk";
|
|
22
23
|
import { createRemoteJWKSet, errors, jwtVerify } from "jose";
|
|
23
24
|
var OWNER_DIDS_CLAIM = "https://tinycloud.xyz/owner_dids";
|
|
24
25
|
function createOpenKeyTokenVerifier(config) {
|
|
@@ -67,11 +68,11 @@ function authInfo(token, payload, config) {
|
|
|
67
68
|
);
|
|
68
69
|
}
|
|
69
70
|
const clientId = firstString(payload.azp, payload.client_id) ?? "unknown";
|
|
70
|
-
const ownerDids =
|
|
71
|
-
if (ownerDids
|
|
71
|
+
const ownerDids = canonicalOwnerDids(payload[OWNER_DIDS_CLAIM]);
|
|
72
|
+
if (ownerDids === void 0) {
|
|
72
73
|
throw new OAuthError(
|
|
73
74
|
OAuthErrorCode.InvalidToken,
|
|
74
|
-
"The OpenKey access token has no TinyCloud owner identity."
|
|
75
|
+
"The OpenKey access token has no valid TinyCloud owner identity."
|
|
75
76
|
);
|
|
76
77
|
}
|
|
77
78
|
return {
|
|
@@ -86,6 +87,21 @@ function authInfo(token, payload, config) {
|
|
|
86
87
|
}
|
|
87
88
|
};
|
|
88
89
|
}
|
|
90
|
+
var OWNER_DID = /^did:pkh:eip155:([1-9][0-9]{0,15}):(0x[0-9a-fA-F]{40})$/;
|
|
91
|
+
function canonicalOwnerDids(claim) {
|
|
92
|
+
if (!Array.isArray(claim) || claim.length === 0) return void 0;
|
|
93
|
+
const ownerDids = [];
|
|
94
|
+
for (const value of claim) {
|
|
95
|
+
const match = typeof value === "string" ? OWNER_DID.exec(value) : null;
|
|
96
|
+
if (match === null) return void 0;
|
|
97
|
+
try {
|
|
98
|
+
ownerDids.push(pkhDid(match[2], Number(match[1])));
|
|
99
|
+
} catch {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return [...new Set(ownerDids)];
|
|
104
|
+
}
|
|
89
105
|
function firstString(...values) {
|
|
90
106
|
return values.find((value) => typeof value === "string" && value.length > 0);
|
|
91
107
|
}
|
|
@@ -96,7 +112,9 @@ import { chmod, mkdir, readFile, readdir, rename, rm, writeFile } from "fs/promi
|
|
|
96
112
|
import { dirname, join } from "path";
|
|
97
113
|
import {
|
|
98
114
|
TinyCloudNode,
|
|
99
|
-
|
|
115
|
+
isCapabilitySubset,
|
|
116
|
+
principalDidEquals,
|
|
117
|
+
siweRuntimeDelegation
|
|
100
118
|
} from "@tinycloud/node-sdk";
|
|
101
119
|
import { TCWSessionManager, importKey, initPanicHook } from "@tinycloud/node-sdk-wasm";
|
|
102
120
|
import { invokeOperation } from "@tinycloud/operations";
|
|
@@ -211,8 +229,7 @@ var RemoteTenantStore = class {
|
|
|
211
229
|
if (pending.ownerDids.length > 0 && !pending.ownerDids.some((did) => samePrincipal(node.did, did))) {
|
|
212
230
|
throw new ApprovalOwnerMismatchError();
|
|
213
231
|
}
|
|
214
|
-
|
|
215
|
-
if (!permissionsCover(pending.permissions, granted)) {
|
|
232
|
+
if (!isCapabilitySubset(pending.permissions, node.getVerifiedSessionCapabilities()).subset) {
|
|
216
233
|
throw new Error("The approved delegation does not cover the requested account access.");
|
|
217
234
|
}
|
|
218
235
|
const profile = await readJson(this.#profilePath(tenant.stateRoot));
|
|
@@ -228,14 +245,25 @@ var RemoteTenantStore = class {
|
|
|
228
245
|
}
|
|
229
246
|
async #storeDelegation(tenant, pending, data) {
|
|
230
247
|
if (pending.requestId === void 0) throw new Error("Authority request is missing.");
|
|
231
|
-
const { node } = await this.#restoreApproval(tenant, data);
|
|
248
|
+
const { node, session } = await this.#restoreApproval(tenant, data);
|
|
232
249
|
if (!pending.ownerDids.some((did) => samePrincipal(node.did, did))) throw new ApprovalOwnerMismatchError();
|
|
233
|
-
const delegation =
|
|
250
|
+
const delegation = siweRuntimeDelegation(node, {
|
|
251
|
+
authorization: session.delegationHeader.Authorization,
|
|
252
|
+
cid: session.delegationCid,
|
|
253
|
+
spaceId: session.spaceId,
|
|
254
|
+
ownerAddress: session.address,
|
|
255
|
+
chainId: session.chainId,
|
|
256
|
+
siwe: session.siwe,
|
|
257
|
+
signature: session.signature,
|
|
258
|
+
host: this.#config.nodeHost
|
|
259
|
+
});
|
|
234
260
|
const result = await invokeOperation(
|
|
235
261
|
"tinycloud.auth.import",
|
|
236
262
|
1,
|
|
237
263
|
{ profile: PROFILE, stateRoot: tenant.stateRoot },
|
|
238
264
|
{
|
|
265
|
+
kind: "tinycloud.auth.delegation",
|
|
266
|
+
version: 1,
|
|
239
267
|
requestId: pending.requestId,
|
|
240
268
|
delegationCid: delegation.cid,
|
|
241
269
|
delegation
|
|
@@ -245,8 +273,10 @@ var RemoteTenantStore = class {
|
|
|
245
273
|
}
|
|
246
274
|
/**
|
|
247
275
|
* Verify the OpenKey callback as a SIWE session for this tenant's key. The
|
|
248
|
-
*
|
|
249
|
-
*
|
|
276
|
+
* callback must carry the full signed proof (SIWE, signature, address and
|
|
277
|
+
* chain), so the WASM verifier always runs: it binds the authorization bytes
|
|
278
|
+
* and CID to the signed SIWE, and `node.did` is the wallet that signed
|
|
279
|
+
* exactly the delegation being stored.
|
|
250
280
|
*/
|
|
251
281
|
async #restoreApproval(tenant, data) {
|
|
252
282
|
const session = sessionFromCallback(data, tenant.jwk);
|
|
@@ -426,7 +456,7 @@ function ensureWasm() {
|
|
|
426
456
|
}
|
|
427
457
|
function sessionFromCallback(data, privateJwk) {
|
|
428
458
|
const delegationHeader = data.delegationHeader;
|
|
429
|
-
if (!isRecord(delegationHeader) || typeof delegationHeader.Authorization !== "string" || typeof data.delegationCid !== "string" || typeof data.spaceId !== "string" || typeof data.verificationMethod !== "string") {
|
|
459
|
+
if (!isRecord(delegationHeader) || typeof delegationHeader.Authorization !== "string" || typeof data.delegationCid !== "string" || typeof data.spaceId !== "string" || typeof data.verificationMethod !== "string" || typeof data.address !== "string" || typeof data.chainId !== "number" || typeof data.siwe !== "string" || typeof data.signature !== "string") {
|
|
430
460
|
throw new Error("OpenKey returned an incomplete delegation.");
|
|
431
461
|
}
|
|
432
462
|
return {
|
|
@@ -435,63 +465,12 @@ function sessionFromCallback(data, privateJwk) {
|
|
|
435
465
|
spaceId: data.spaceId,
|
|
436
466
|
jwk: privateJwk,
|
|
437
467
|
verificationMethod: data.verificationMethod,
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
function portableFromCallback(data, permissions, host) {
|
|
445
|
-
const primary = permissions.find((permission) => permission.service !== "tinycloud.encryption") ?? permissions[0];
|
|
446
|
-
if (primary === void 0) throw new Error("The authority request has no permissions.");
|
|
447
|
-
const session = sessionFromCallback(data, {});
|
|
448
|
-
const expiry = delegationExpiry(data);
|
|
449
|
-
return {
|
|
450
|
-
cid: session.delegationCid,
|
|
451
|
-
delegationHeader: session.delegationHeader,
|
|
452
|
-
spaceId: session.spaceId,
|
|
453
|
-
path: primary.path,
|
|
454
|
-
actions: [...primary.actions],
|
|
455
|
-
resources: permissions.map((permission) => ({
|
|
456
|
-
service: permission.service.startsWith("tinycloud.") ? permission.service.slice("tinycloud.".length) : permission.service,
|
|
457
|
-
space: permission.service === "tinycloud.encryption" ? requiredPermissionSpace(permission) : session.spaceId,
|
|
458
|
-
path: permission.path,
|
|
459
|
-
actions: [...permission.actions]
|
|
460
|
-
})),
|
|
461
|
-
expiry,
|
|
462
|
-
delegateDID: session.verificationMethod,
|
|
463
|
-
ownerAddress: typeof data.address === "string" ? data.address : "",
|
|
464
|
-
chainId: typeof data.chainId === "number" ? data.chainId : 1,
|
|
465
|
-
host
|
|
468
|
+
address: data.address,
|
|
469
|
+
chainId: data.chainId,
|
|
470
|
+
siwe: data.siwe,
|
|
471
|
+
signature: data.signature
|
|
466
472
|
};
|
|
467
473
|
}
|
|
468
|
-
function delegationExpiry(data) {
|
|
469
|
-
for (const key of ["expiry", "expiresAt", "expirationTime"]) {
|
|
470
|
-
const parsed = parseDate(data[key]);
|
|
471
|
-
if (parsed !== void 0) return parsed;
|
|
472
|
-
}
|
|
473
|
-
if (typeof data.siwe === "string") {
|
|
474
|
-
const match = data.siwe.match(/^Expiration Time:\s*(.+)$/im);
|
|
475
|
-
const parsed = parseDate(match?.[1]?.trim());
|
|
476
|
-
if (parsed !== void 0) return parsed;
|
|
477
|
-
}
|
|
478
|
-
throw new Error("OpenKey did not return a delegation expiry.");
|
|
479
|
-
}
|
|
480
|
-
function permissionsCover(requested, granted) {
|
|
481
|
-
return requested.every((request) => granted.some(
|
|
482
|
-
(grant) => normalizeService(grant.service) === normalizeService(request.service) && typeof grant.space === "string" && typeof request.space === "string" && sameSpace(grant.space, request.space) && grant.path === request.path && request.actions.every((action) => grant.actions.includes(action))
|
|
483
|
-
));
|
|
484
|
-
}
|
|
485
|
-
function sameSpace(granted, requested) {
|
|
486
|
-
return granted === requested || granted.endsWith(`:${requested}`);
|
|
487
|
-
}
|
|
488
|
-
function normalizeService(service) {
|
|
489
|
-
return service.startsWith("tinycloud.") ? service : `tinycloud.${service}`;
|
|
490
|
-
}
|
|
491
|
-
function requiredPermissionSpace(permission) {
|
|
492
|
-
if (typeof permission.space !== "string") throw new Error("The authority request has no space.");
|
|
493
|
-
return permission.space;
|
|
494
|
-
}
|
|
495
474
|
function publicJwk(jwk) {
|
|
496
475
|
const copy = { ...jwk };
|
|
497
476
|
for (const field of ["d", "p", "q", "dp", "dq", "qi", "oth", "k"]) delete copy[field];
|
|
@@ -514,11 +493,6 @@ function isPermissionEntry(value) {
|
|
|
514
493
|
function isRecord(value) {
|
|
515
494
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
516
495
|
}
|
|
517
|
-
function parseDate(value) {
|
|
518
|
-
if (typeof value !== "string" && typeof value !== "number") return void 0;
|
|
519
|
-
const date = new Date(typeof value === "number" && value < 1e10 ? value * 1e3 : value);
|
|
520
|
-
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
521
|
-
}
|
|
522
496
|
async function secureDirectory(path) {
|
|
523
497
|
await mkdir(path, { recursive: true, mode: 448 });
|
|
524
498
|
await chmod(path, 448);
|
|
@@ -692,7 +666,10 @@ var TOOL_BINDINGS = [
|
|
|
692
666
|
readOnlyHint: false,
|
|
693
667
|
idempotentHint: true,
|
|
694
668
|
destructiveHint: false,
|
|
695
|
-
openWorldHint: false
|
|
669
|
+
openWorldHint: false,
|
|
670
|
+
// Hosted delegations must arrive through the owner/request-bound OpenKey callback,
|
|
671
|
+
// not a caller-supplied import artifact.
|
|
672
|
+
surfaces: ["local"]
|
|
696
673
|
},
|
|
697
674
|
{
|
|
698
675
|
name: "tinycloud_account_spaces_list",
|
|
@@ -864,7 +841,7 @@ function registerTinyCloudTools(server, selection, validator) {
|
|
|
864
841
|
// package.json
|
|
865
842
|
var package_default = {
|
|
866
843
|
name: "@tinycloud/mcp",
|
|
867
|
-
version: "0.5.0-beta.
|
|
844
|
+
version: "0.5.0-beta.7",
|
|
868
845
|
description: "TinyCloud delegated operations MCP server",
|
|
869
846
|
keywords: [
|
|
870
847
|
"tinycloud",
|
|
@@ -935,9 +912,9 @@ var package_default = {
|
|
|
935
912
|
dependencies: {
|
|
936
913
|
"@modelcontextprotocol/node": "2.0.0-beta.4",
|
|
937
914
|
"@modelcontextprotocol/server": "2.0.0-beta.4",
|
|
938
|
-
"@tinycloud/node-sdk": "3.2.0-beta.
|
|
915
|
+
"@tinycloud/node-sdk": "3.2.0-beta.6",
|
|
939
916
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
940
|
-
"@tinycloud/operations": "0.5.0-beta.
|
|
917
|
+
"@tinycloud/operations": "0.5.0-beta.7",
|
|
941
918
|
jose: "6.1.3"
|
|
942
919
|
},
|
|
943
920
|
devDependencies: {
|