agent-authority 0.1.0
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/CHANGELOG.md +118 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +91 -0
- package/README.md +553 -0
- package/dist/a2a.d.ts +73 -0
- package/dist/a2a.d.ts.map +1 -0
- package/dist/a2a.js +117 -0
- package/dist/a2a.js.map +1 -0
- package/dist/audit.d.ts +12 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +52 -0
- package/dist/audit.js.map +1 -0
- package/dist/behalf.d.ts +173 -0
- package/dist/behalf.d.ts.map +1 -0
- package/dist/behalf.js +475 -0
- package/dist/behalf.js.map +1 -0
- package/dist/capability.d.ts +56 -0
- package/dist/capability.d.ts.map +1 -0
- package/dist/capability.js +176 -0
- package/dist/capability.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +273 -0
- package/dist/cli.js.map +1 -0
- package/dist/control-plane.d.ts +57 -0
- package/dist/control-plane.d.ts.map +1 -0
- package/dist/control-plane.js +332 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/crypto.d.ts +68 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +105 -0
- package/dist/crypto.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/lint.d.ts +17 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +75 -0
- package/dist/lint.js.map +1 -0
- package/dist/mandate.d.ts +99 -0
- package/dist/mandate.d.ts.map +1 -0
- package/dist/mandate.js +141 -0
- package/dist/mandate.js.map +1 -0
- package/dist/mcp-server.d.ts +26 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +111 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp.d.ts +63 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +123 -0
- package/dist/mcp.js.map +1 -0
- package/dist/persist.d.ts +51 -0
- package/dist/persist.d.ts.map +1 -0
- package/dist/persist.js +150 -0
- package/dist/persist.js.map +1 -0
- package/dist/quickstart.d.ts +63 -0
- package/dist/quickstart.d.ts.map +1 -0
- package/dist/quickstart.js +171 -0
- package/dist/quickstart.js.map +1 -0
- package/dist/remote.d.ts +93 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +120 -0
- package/dist/remote.js.map +1 -0
- package/dist/seal.d.ts +12 -0
- package/dist/seal.d.ts.map +1 -0
- package/dist/seal.js +96 -0
- package/dist/seal.js.map +1 -0
- package/dist/store.d.ts +119 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +139 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +173 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/llms.txt +106 -0
- package/package.json +107 -0
- package/schemas/capability.schema.json +14 -0
- package/schemas/mandate.schema.json +68 -0
- package/vectors/mandate-vector.json +63 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behalf — the reference implementation of agent authority.
|
|
3
|
+
*
|
|
4
|
+
* Five verbs, one primitive (the Mandate):
|
|
5
|
+
*
|
|
6
|
+
* import { Behalf } from "agent-authority";
|
|
7
|
+
*
|
|
8
|
+
* const mandate = await Behalf.grant({ // 1. GRANT
|
|
9
|
+
* principal: user.id,
|
|
10
|
+
* agent: "research-agent",
|
|
11
|
+
* can: ["read:calendar", "spend:usd<=50"],
|
|
12
|
+
* expiresIn: "1h",
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* await mandate.authorize("spend:usd=20"); // 2. AUTHORIZE
|
|
16
|
+
* const child = mandate.attenuate({ can: ["read:calendar"], expiresIn: "10m" }); // 3. DELEGATE
|
|
17
|
+
* await Behalf.revoke(mandate.id); // 4. REVOKE
|
|
18
|
+
* const trail = await Behalf.audit(mandate.id); // 5. AUDIT
|
|
19
|
+
*/
|
|
20
|
+
export { Behalf, createBehalf, BehalfDelegationError, type BehalfConfig, } from "./behalf.js";
|
|
21
|
+
export { Mandate } from "./mandate.js";
|
|
22
|
+
export { newKeyPair, exportPublicKey, importPublicKey, exportPrivateKey, importPrivateKey, type KeyPair, } from "./crypto.js";
|
|
23
|
+
export { parse as parseCapability, permits, isNarrowing, type Capability, } from "./capability.js";
|
|
24
|
+
export { MemoryRevocationStore, MemoryAuditStore, MemoryRateStore, TokenBucketRateStore, MemoryConsentStore, MemoryPolicyStore, CachingRevocationStore, type RevocationStore, type AuditStore, type RateStore, type ConsentStore, type PolicyStore, type CacheOptions, } from "./store.js";
|
|
25
|
+
export { FileRevocationStore, FileAuditStore, FileConsentStore, FilePolicyStore, FileRateStore, } from "./persist.js";
|
|
26
|
+
export { createControlPlane, type ControlPlane, type ControlPlaneOptions, type ConsentRecord, } from "./control-plane.js";
|
|
27
|
+
export { HttpRevocationStore, HttpAuditStore, HttpRateStore, ControlPlaneClient, controlPlaneConsent, type RemoteOptions, type ConsentProviderOptions, type ConsentRequest, } from "./remote.js";
|
|
28
|
+
export { newSealKeyPair, seal, unseal, type SealKeyPair } from "./seal.js";
|
|
29
|
+
export { verify as verifyAuditLog } from "./audit.js";
|
|
30
|
+
export { lint, isClean, type LintFinding, type LintLevel } from "./lint.js";
|
|
31
|
+
export { generateQuickstart, findSurface, listSurfaces, DEFAULT_SURFACES, type Surface, type QuickstartOptions, type Quickstart, } from "./quickstart.js";
|
|
32
|
+
export { present, behalfFetch, authorizeIncoming, guard, MANDATE_HEADER, type PresentOptions, type GuardOptions, type GuardedRequest, } from "./a2a.js";
|
|
33
|
+
export { BehalfError, AuthorizationError, WideningError, IntegrityError, CapabilityParseError, } from "./errors.js";
|
|
34
|
+
export type { Caveat, Block, Proof, MandateToken, GrantOptions, AttenuateOptions, AuditEntry, AuditIntegrity, } from "./types.js";
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,MAAM,EACN,YAAY,EACZ,qBAAqB,EACrB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,OAAO,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,IAAI,eAAe,EACxB,OAAO,EACP,WAAW,EACX,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,KAAK,OAAO,EACZ,KAAK,iBAAiB,EACtB,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,OAAO,EACP,WAAW,EACX,iBAAiB,EACjB,KAAK,EACL,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,MAAM,EACN,KAAK,EACL,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behalf — the reference implementation of agent authority.
|
|
3
|
+
*
|
|
4
|
+
* Five verbs, one primitive (the Mandate):
|
|
5
|
+
*
|
|
6
|
+
* import { Behalf } from "agent-authority";
|
|
7
|
+
*
|
|
8
|
+
* const mandate = await Behalf.grant({ // 1. GRANT
|
|
9
|
+
* principal: user.id,
|
|
10
|
+
* agent: "research-agent",
|
|
11
|
+
* can: ["read:calendar", "spend:usd<=50"],
|
|
12
|
+
* expiresIn: "1h",
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* await mandate.authorize("spend:usd=20"); // 2. AUTHORIZE
|
|
16
|
+
* const child = mandate.attenuate({ can: ["read:calendar"], expiresIn: "10m" }); // 3. DELEGATE
|
|
17
|
+
* await Behalf.revoke(mandate.id); // 4. REVOKE
|
|
18
|
+
* const trail = await Behalf.audit(mandate.id); // 5. AUDIT
|
|
19
|
+
*/
|
|
20
|
+
export { Behalf, createBehalf, BehalfDelegationError, } from "./behalf.js";
|
|
21
|
+
export { Mandate } from "./mandate.js";
|
|
22
|
+
export { newKeyPair, exportPublicKey, importPublicKey, exportPrivateKey, importPrivateKey, } from "./crypto.js";
|
|
23
|
+
export { parse as parseCapability, permits, isNarrowing, } from "./capability.js";
|
|
24
|
+
export { MemoryRevocationStore, MemoryAuditStore, MemoryRateStore, TokenBucketRateStore, MemoryConsentStore, MemoryPolicyStore, CachingRevocationStore, } from "./store.js";
|
|
25
|
+
export { FileRevocationStore, FileAuditStore, FileConsentStore, FilePolicyStore, FileRateStore, } from "./persist.js";
|
|
26
|
+
export { createControlPlane, } from "./control-plane.js";
|
|
27
|
+
export { HttpRevocationStore, HttpAuditStore, HttpRateStore, ControlPlaneClient, controlPlaneConsent, } from "./remote.js";
|
|
28
|
+
export { newSealKeyPair, seal, unseal } from "./seal.js";
|
|
29
|
+
export { verify as verifyAuditLog } from "./audit.js";
|
|
30
|
+
export { lint, isClean } from "./lint.js";
|
|
31
|
+
export { generateQuickstart, findSurface, listSurfaces, DEFAULT_SURFACES, } from "./quickstart.js";
|
|
32
|
+
export { present, behalfFetch, authorizeIncoming, guard, MANDATE_HEADER, } from "./a2a.js";
|
|
33
|
+
export { BehalfError, AuthorizationError, WideningError, IntegrityError, CapabilityParseError, } from "./errors.js";
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,MAAM,EACN,YAAY,EACZ,qBAAqB,GAEtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,IAAI,eAAe,EACxB,OAAO,EACP,WAAW,GAEZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,GAOvB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,GAInB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,mBAAmB,GAIpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAoB,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAoC,MAAM,WAAW,CAAC;AAC5E,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,gBAAgB,GAIjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,OAAO,EACP,WAAW,EACX,iBAAiB,EACjB,KAAK,EACL,cAAc,GAIf,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC"}
|
package/dist/lint.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability linting. The grammar permits broad or unbounded scopes (`*`, an
|
|
3
|
+
* unconstrained `spend:`, ...); the spec says these are "discouraged; lint
|
|
4
|
+
* warns". This surfaces them so humans and agents write tight scopes by default.
|
|
5
|
+
*/
|
|
6
|
+
export type LintLevel = "error" | "warn" | "info";
|
|
7
|
+
export interface LintFinding {
|
|
8
|
+
capability: string;
|
|
9
|
+
level: LintLevel;
|
|
10
|
+
rule: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
/** Lint a set of capability strings; returns findings ordered by input. */
|
|
14
|
+
export declare function lint(capabilities: string[]): LintFinding[];
|
|
15
|
+
/** True if linting found nothing at `warn` or `error` level. */
|
|
16
|
+
export declare function isClean(capabilities: string[]): boolean;
|
|
17
|
+
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD,2EAA2E;AAC3E,wBAAgB,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAoE1D;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAEvD"}
|
package/dist/lint.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { parse } from "./capability.js";
|
|
2
|
+
import { CapabilityParseError } from "./errors.js";
|
|
3
|
+
/** Verbs that move a quantity and should carry an amount limit. */
|
|
4
|
+
const QUANTITATIVE_VERBS = new Set(["spend", "pay", "transfer", "charge", "withdraw", "refund"]);
|
|
5
|
+
/** Verbs that fan out and should usually carry a rate limit. */
|
|
6
|
+
const RATE_VERBS = new Set(["send", "email", "sms", "notify", "post", "publish", "call"]);
|
|
7
|
+
/** Lint a set of capability strings; returns findings ordered by input. */
|
|
8
|
+
export function lint(capabilities) {
|
|
9
|
+
const findings = [];
|
|
10
|
+
const seen = new Set();
|
|
11
|
+
for (const raw of capabilities) {
|
|
12
|
+
if (seen.has(raw)) {
|
|
13
|
+
findings.push({
|
|
14
|
+
capability: raw,
|
|
15
|
+
level: "warn",
|
|
16
|
+
rule: "duplicate",
|
|
17
|
+
message: "duplicate capability; remove the repeat",
|
|
18
|
+
});
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
seen.add(raw);
|
|
22
|
+
let cap;
|
|
23
|
+
try {
|
|
24
|
+
cap = parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
findings.push({
|
|
28
|
+
capability: raw,
|
|
29
|
+
level: "error",
|
|
30
|
+
rule: "unparseable",
|
|
31
|
+
message: e instanceof CapabilityParseError ? e.message : String(e),
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (cap.wildcard) {
|
|
36
|
+
findings.push({
|
|
37
|
+
capability: raw,
|
|
38
|
+
level: "warn",
|
|
39
|
+
rule: "wildcard",
|
|
40
|
+
message: "`*` grants unlimited authority; grant specific capabilities instead",
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (cap.verb === "*" || cap.resource === "*") {
|
|
45
|
+
findings.push({
|
|
46
|
+
capability: raw,
|
|
47
|
+
level: "warn",
|
|
48
|
+
rule: "wildcard-part",
|
|
49
|
+
message: "a `*` verb or resource is overly broad; name it explicitly",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (QUANTITATIVE_VERBS.has(cap.verb) && !cap.amount) {
|
|
53
|
+
findings.push({
|
|
54
|
+
capability: raw,
|
|
55
|
+
level: "warn",
|
|
56
|
+
rule: "unbounded-amount",
|
|
57
|
+
message: `"${cap.verb}" has no limit; add a cap like "${cap.verb}:${cap.resource}<=50"`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (RATE_VERBS.has(cap.verb) && !cap.rate) {
|
|
61
|
+
findings.push({
|
|
62
|
+
capability: raw,
|
|
63
|
+
level: "info",
|
|
64
|
+
rule: "no-rate-limit",
|
|
65
|
+
message: `"${cap.verb}" has no rate limit; consider "${raw} rate<=10/h"`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return findings;
|
|
70
|
+
}
|
|
71
|
+
/** True if linting found nothing at `warn` or `error` level. */
|
|
72
|
+
export function isClean(capabilities) {
|
|
73
|
+
return !lint(capabilities).some((f) => f.level !== "info");
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=lint.js.map
|
package/dist/lint.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { AttenuateOptions, AuditEntry, MandateToken, Proof } from "./types.js";
|
|
2
|
+
import { type KeyPair } from "./crypto.js";
|
|
3
|
+
import type { KeyObject } from "node:crypto";
|
|
4
|
+
/**
|
|
5
|
+
* The engine that actually verifies and enforces — implemented by {@link Behalf}.
|
|
6
|
+
* Kept as an interface here to avoid a circular import with the Mandate wrapper.
|
|
7
|
+
*/
|
|
8
|
+
export interface Engine {
|
|
9
|
+
authorizeAsHolder(token: MandateToken, action: string, delegationKey: KeyObject | undefined): Promise<void>;
|
|
10
|
+
attenuate(token: MandateToken, delegationKey: KeyObject | undefined, opts: AttenuateOptions): Mandate;
|
|
11
|
+
provePossession(token: MandateToken, delegationKey: KeyObject, action: string, nonce?: string, agentKeys?: KeyObject[]): Proof;
|
|
12
|
+
revoke(id: string): Promise<void>;
|
|
13
|
+
audit(id: string): Promise<AuditEntry[]>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A handle to a capability token. Construction is internal — obtain one via
|
|
17
|
+
* `Behalf.grant(...)`, `mandate.attenuate(...)`, or `Behalf.import(token)`.
|
|
18
|
+
*
|
|
19
|
+
* A mandate obtained from `grant`/`attenuate` also carries an in-memory
|
|
20
|
+
* `delegationKey` (the Ed25519 private key that authorizes the next block), so
|
|
21
|
+
* it can be further attenuated. A mandate restored via `import` has only the
|
|
22
|
+
* public token: it can be verified, authorized, and audited, but not delegated.
|
|
23
|
+
*/
|
|
24
|
+
export declare class Mandate {
|
|
25
|
+
/** The serializable wire token. */
|
|
26
|
+
readonly token: MandateToken;
|
|
27
|
+
private readonly engine;
|
|
28
|
+
/** Private key for the next attenuation block; undefined if imported. */
|
|
29
|
+
private readonly delegationKey?;
|
|
30
|
+
constructor(
|
|
31
|
+
/** The serializable wire token. */
|
|
32
|
+
token: MandateToken, engine: Engine,
|
|
33
|
+
/** Private key for the next attenuation block; undefined if imported. */
|
|
34
|
+
delegationKey?: KeyObject | undefined);
|
|
35
|
+
/** Every caveat across every block, in chain order. */
|
|
36
|
+
private get caveats();
|
|
37
|
+
/** This mandate's own id (the deepest id in the chain). */
|
|
38
|
+
get id(): string;
|
|
39
|
+
/** Full chain of ids, root → this mandate. Revoking any kills this one. */
|
|
40
|
+
get chain(): string[];
|
|
41
|
+
/** The principal that authorized the root grant, if present. */
|
|
42
|
+
get principal(): string | undefined;
|
|
43
|
+
/** The agent this mandate is currently bound to (latest binding wins). */
|
|
44
|
+
get agent(): string | undefined;
|
|
45
|
+
/** Effective expiry (earliest expires caveat). */
|
|
46
|
+
get expiresAt(): number | undefined;
|
|
47
|
+
/** True if this mandate carries the secret needed to delegate further. */
|
|
48
|
+
get canDelegate(): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Prove authority for a concrete action. Resolves if permitted; throws
|
|
51
|
+
* {@link AuthorizationError} otherwise. Always writes an audit record.
|
|
52
|
+
*
|
|
53
|
+
* Requires this mandate to hold its delegation key (i.e. it came from
|
|
54
|
+
* `grant`/`attenuate`, not `import`): authorization includes a proof of
|
|
55
|
+
* possession of the chain's terminal key, which closes truncation and makes a
|
|
56
|
+
* serialized token unusable as a bare bearer credential. To authorize a
|
|
57
|
+
* mandate you received from elsewhere, the holder must present a proof — see
|
|
58
|
+
* `agent-authority/a2a`, or use `engine.inspect()` for an advisory (no-possession)
|
|
59
|
+
* check.
|
|
60
|
+
*/
|
|
61
|
+
authorize(action: string): Promise<void>;
|
|
62
|
+
/** Hand a narrowed mandate to a sub-agent. Can only shrink scope. */
|
|
63
|
+
attenuate(opts: AttenuateOptions): Mandate;
|
|
64
|
+
/**
|
|
65
|
+
* Mint a fresh proof of possession for performing `action`, to present this
|
|
66
|
+
* mandate across a trust boundary (e.g. an A2A call). Bound to the action and
|
|
67
|
+
* the exact chain. Requires the delegation key, so only the legitimate holder
|
|
68
|
+
* can produce it.
|
|
69
|
+
*/
|
|
70
|
+
prove(action: string, opts?: {
|
|
71
|
+
nonce?: string;
|
|
72
|
+
agentKeys?: KeyPair[];
|
|
73
|
+
}): Proof;
|
|
74
|
+
/** Revoke this mandate and its entire downstream chain. */
|
|
75
|
+
revoke(): Promise<void>;
|
|
76
|
+
/** This mandate's hash-chained audit trail. */
|
|
77
|
+
audit(): Promise<AuditEntry[]>;
|
|
78
|
+
/** Compact, transmittable string form (base64url JSON of the public token). */
|
|
79
|
+
serialize(): string;
|
|
80
|
+
/**
|
|
81
|
+
* Transferable holder credential: the token PLUS its delegation key, so the
|
|
82
|
+
* recipient can authorize, prove, and attenuate after `engine.import(...)`.
|
|
83
|
+
* This is how a delegated mandate is handed to a sub-agent in another process.
|
|
84
|
+
*
|
|
85
|
+
* TREAT AS A SECRET: anyone holding this string can exercise the mandate's
|
|
86
|
+
* full authority until expiry/revocation. Deliver only over a secure channel.
|
|
87
|
+
* Use `serialize()` for the public, presentation-only form.
|
|
88
|
+
*/
|
|
89
|
+
serializeWithKey(): string;
|
|
90
|
+
/**
|
|
91
|
+
* Holder credential, encrypted to a recipient's X25519 sealing key — so the
|
|
92
|
+
* credential is unreadable in transit/at rest to anyone but the intended
|
|
93
|
+
* agent. Open it with `engine.importSealed(sealed, recipientKeyPair)`. This is
|
|
94
|
+
* `serializeWithKey()` wrapped in `seal()`; use it when the delivery channel
|
|
95
|
+
* isn't fully trusted. (Defense-in-depth on top of `bindAgent`.)
|
|
96
|
+
*/
|
|
97
|
+
sealForRecipient(recipientPublicKey: string): string;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=mandate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mandate.d.ts","sourceRoot":"","sources":["../src/mandate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAU,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,EAAoB,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,iBAAiB,CACf,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,SAAS,GAAG,SAAS,GACnC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,GAAG,SAAS,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC;IACtG,eAAe,CACb,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,SAAS,EACxB,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,SAAS,EAAE,GACtB,KAAK,CAAC;IACT,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,qBAAa,OAAO;IAEhB,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,YAAY;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;;IAJ/B,mCAAmC;IAC1B,KAAK,EAAE,YAAY,EACX,MAAM,EAAE,MAAM;IAC/B,yEAAyE;IACxD,aAAa,CAAC,EAAE,SAAS,YAAA;IAG5C,uDAAuD;IACvD,OAAO,KAAK,OAAO,GAElB;IAED,2DAA2D;IAC3D,IAAI,EAAE,IAAI,MAAM,CAGf;IAED,2EAA2E;IAC3E,IAAI,KAAK,IAAI,MAAM,EAAE,CAIpB;IAED,gEAAgE;IAChE,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAGlC;IAED,0EAA0E;IAC1E,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAI9B;IAED,kDAAkD;IAClD,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAMlC;IAED,0EAA0E;IAC1E,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,qEAAqE;IACrE,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO;IAI1C;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAA;KAAO,GAAG,KAAK;IAQlF,2DAA2D;IAC3D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvB,+CAA+C;IAC/C,KAAK,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IAI9B,+EAA+E;IAC/E,SAAS,IAAI,MAAM;IAInB;;;;;;;;OAQG;IACH,gBAAgB,IAAI,MAAM;IAQ1B;;;;;;OAMG;IACH,gBAAgB,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM;CAGrD"}
|
package/dist/mandate.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { exportPrivateKey } from "./crypto.js";
|
|
2
|
+
import { seal } from "./seal.js";
|
|
3
|
+
/**
|
|
4
|
+
* A handle to a capability token. Construction is internal — obtain one via
|
|
5
|
+
* `Behalf.grant(...)`, `mandate.attenuate(...)`, or `Behalf.import(token)`.
|
|
6
|
+
*
|
|
7
|
+
* A mandate obtained from `grant`/`attenuate` also carries an in-memory
|
|
8
|
+
* `delegationKey` (the Ed25519 private key that authorizes the next block), so
|
|
9
|
+
* it can be further attenuated. A mandate restored via `import` has only the
|
|
10
|
+
* public token: it can be verified, authorized, and audited, but not delegated.
|
|
11
|
+
*/
|
|
12
|
+
export class Mandate {
|
|
13
|
+
token;
|
|
14
|
+
engine;
|
|
15
|
+
delegationKey;
|
|
16
|
+
constructor(
|
|
17
|
+
/** The serializable wire token. */
|
|
18
|
+
token, engine,
|
|
19
|
+
/** Private key for the next attenuation block; undefined if imported. */
|
|
20
|
+
delegationKey) {
|
|
21
|
+
this.token = token;
|
|
22
|
+
this.engine = engine;
|
|
23
|
+
this.delegationKey = delegationKey;
|
|
24
|
+
}
|
|
25
|
+
/** Every caveat across every block, in chain order. */
|
|
26
|
+
get caveats() {
|
|
27
|
+
return this.token.blocks.flatMap((b) => b.caveats);
|
|
28
|
+
}
|
|
29
|
+
/** This mandate's own id (the deepest id in the chain). */
|
|
30
|
+
get id() {
|
|
31
|
+
const ids = this.chain;
|
|
32
|
+
return ids[ids.length - 1];
|
|
33
|
+
}
|
|
34
|
+
/** Full chain of ids, root → this mandate. Revoking any kills this one. */
|
|
35
|
+
get chain() {
|
|
36
|
+
const ids = [this.token.id];
|
|
37
|
+
for (const c of this.caveats)
|
|
38
|
+
if (c.t === "id")
|
|
39
|
+
ids.push(c.id);
|
|
40
|
+
return ids;
|
|
41
|
+
}
|
|
42
|
+
/** The principal that authorized the root grant, if present. */
|
|
43
|
+
get principal() {
|
|
44
|
+
for (const c of this.caveats)
|
|
45
|
+
if (c.t === "principal")
|
|
46
|
+
return c.principal;
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
/** The agent this mandate is currently bound to (latest binding wins). */
|
|
50
|
+
get agent() {
|
|
51
|
+
let agent;
|
|
52
|
+
for (const c of this.caveats)
|
|
53
|
+
if (c.t === "agent")
|
|
54
|
+
agent = c.agent;
|
|
55
|
+
return agent;
|
|
56
|
+
}
|
|
57
|
+
/** Effective expiry (earliest expires caveat). */
|
|
58
|
+
get expiresAt() {
|
|
59
|
+
let earliest;
|
|
60
|
+
for (const c of this.caveats) {
|
|
61
|
+
if (c.t === "expires")
|
|
62
|
+
earliest = earliest === undefined ? c.at : Math.min(earliest, c.at);
|
|
63
|
+
}
|
|
64
|
+
return earliest;
|
|
65
|
+
}
|
|
66
|
+
/** True if this mandate carries the secret needed to delegate further. */
|
|
67
|
+
get canDelegate() {
|
|
68
|
+
return this.delegationKey !== undefined;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Prove authority for a concrete action. Resolves if permitted; throws
|
|
72
|
+
* {@link AuthorizationError} otherwise. Always writes an audit record.
|
|
73
|
+
*
|
|
74
|
+
* Requires this mandate to hold its delegation key (i.e. it came from
|
|
75
|
+
* `grant`/`attenuate`, not `import`): authorization includes a proof of
|
|
76
|
+
* possession of the chain's terminal key, which closes truncation and makes a
|
|
77
|
+
* serialized token unusable as a bare bearer credential. To authorize a
|
|
78
|
+
* mandate you received from elsewhere, the holder must present a proof — see
|
|
79
|
+
* `agent-authority/a2a`, or use `engine.inspect()` for an advisory (no-possession)
|
|
80
|
+
* check.
|
|
81
|
+
*/
|
|
82
|
+
authorize(action) {
|
|
83
|
+
return this.engine.authorizeAsHolder(this.token, action, this.delegationKey);
|
|
84
|
+
}
|
|
85
|
+
/** Hand a narrowed mandate to a sub-agent. Can only shrink scope. */
|
|
86
|
+
attenuate(opts) {
|
|
87
|
+
return this.engine.attenuate(this.token, this.delegationKey, opts);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Mint a fresh proof of possession for performing `action`, to present this
|
|
91
|
+
* mandate across a trust boundary (e.g. an A2A call). Bound to the action and
|
|
92
|
+
* the exact chain. Requires the delegation key, so only the legitimate holder
|
|
93
|
+
* can produce it.
|
|
94
|
+
*/
|
|
95
|
+
prove(action, opts = {}) {
|
|
96
|
+
if (!this.delegationKey) {
|
|
97
|
+
throw new Error("cannot prove possession: this mandate was imported without its key");
|
|
98
|
+
}
|
|
99
|
+
const agentKeys = opts.agentKeys?.map((k) => k.privateKey);
|
|
100
|
+
return this.engine.provePossession(this.token, this.delegationKey, action, opts.nonce, agentKeys);
|
|
101
|
+
}
|
|
102
|
+
/** Revoke this mandate and its entire downstream chain. */
|
|
103
|
+
revoke() {
|
|
104
|
+
return this.engine.revoke(this.id);
|
|
105
|
+
}
|
|
106
|
+
/** This mandate's hash-chained audit trail. */
|
|
107
|
+
audit() {
|
|
108
|
+
return this.engine.audit(this.id);
|
|
109
|
+
}
|
|
110
|
+
/** Compact, transmittable string form (base64url JSON of the public token). */
|
|
111
|
+
serialize() {
|
|
112
|
+
return Buffer.from(JSON.stringify(this.token), "utf8").toString("base64url");
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Transferable holder credential: the token PLUS its delegation key, so the
|
|
116
|
+
* recipient can authorize, prove, and attenuate after `engine.import(...)`.
|
|
117
|
+
* This is how a delegated mandate is handed to a sub-agent in another process.
|
|
118
|
+
*
|
|
119
|
+
* TREAT AS A SECRET: anyone holding this string can exercise the mandate's
|
|
120
|
+
* full authority until expiry/revocation. Deliver only over a secure channel.
|
|
121
|
+
* Use `serialize()` for the public, presentation-only form.
|
|
122
|
+
*/
|
|
123
|
+
serializeWithKey() {
|
|
124
|
+
if (!this.delegationKey) {
|
|
125
|
+
throw new Error("cannot export with key: this mandate was imported without its key");
|
|
126
|
+
}
|
|
127
|
+
const payload = { token: this.token, key: exportPrivateKey(this.delegationKey) };
|
|
128
|
+
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Holder credential, encrypted to a recipient's X25519 sealing key — so the
|
|
132
|
+
* credential is unreadable in transit/at rest to anyone but the intended
|
|
133
|
+
* agent. Open it with `engine.importSealed(sealed, recipientKeyPair)`. This is
|
|
134
|
+
* `serializeWithKey()` wrapped in `seal()`; use it when the delivery channel
|
|
135
|
+
* isn't fully trusted. (Defense-in-depth on top of `bindAgent`.)
|
|
136
|
+
*/
|
|
137
|
+
sealForRecipient(recipientPublicKey) {
|
|
138
|
+
return seal(this.serializeWithKey(), recipientPublicKey);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=mandate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mandate.js","sourceRoot":"","sources":["../src/mandate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Behalf } from "./behalf.js";
|
|
3
|
+
interface JsonRpcRequest {
|
|
4
|
+
jsonrpc: "2.0";
|
|
5
|
+
id?: string | number | null;
|
|
6
|
+
method: string;
|
|
7
|
+
params?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
interface JsonRpcResponse {
|
|
10
|
+
jsonrpc: "2.0";
|
|
11
|
+
id: string | number | null;
|
|
12
|
+
result?: unknown;
|
|
13
|
+
error?: {
|
|
14
|
+
code: number;
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface McpServer {
|
|
19
|
+
/** Handle one JSON-RPC request; returns a response, or null for notifications. */
|
|
20
|
+
dispatch(req: JsonRpcRequest): Promise<JsonRpcResponse | null>;
|
|
21
|
+
/** Wire the server to stdin/stdout and run until the stream closes. */
|
|
22
|
+
start(): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function createMcpServer(engine?: Behalf): McpServer;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=mcp-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAarC,UAAU,cAAc;IACtB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,UAAU,eAAe;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAC/D,uEAAuE;IACvE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,eAAe,CAAC,MAAM,GAAE,MAAuB,GAAG,SAAS,CAiF1E"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { realpathSync } from "node:fs";
|
|
5
|
+
import { Behalf } from "./behalf.js";
|
|
6
|
+
import { behalfMcpTools } from "./mcp.js";
|
|
7
|
+
/**
|
|
8
|
+
* A minimal, dependency-free MCP server over stdio (newline-delimited JSON-RPC
|
|
9
|
+
* 2.0). It exposes the Behalf discovery tools — `request_mandate`,
|
|
10
|
+
* `present_mandate`, `check_authority` — so any MCP client (Claude Desktop,
|
|
11
|
+
* Cursor, ...) can obtain and use authority natively. Staying SDK-free keeps the
|
|
12
|
+
* "thin, near-zero-deps" principle: the wire protocol is small enough to own.
|
|
13
|
+
*/
|
|
14
|
+
const PROTOCOL_VERSION = "2024-11-05";
|
|
15
|
+
export function createMcpServer(engine = Behalf.default) {
|
|
16
|
+
const tools = behalfMcpTools(engine);
|
|
17
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
18
|
+
async function dispatch(req) {
|
|
19
|
+
const id = req.id ?? null;
|
|
20
|
+
const ok = (result) => ({ jsonrpc: "2.0", id, result });
|
|
21
|
+
const err = (code, message) => ({
|
|
22
|
+
jsonrpc: "2.0",
|
|
23
|
+
id,
|
|
24
|
+
error: { code, message },
|
|
25
|
+
});
|
|
26
|
+
switch (req.method) {
|
|
27
|
+
case "initialize":
|
|
28
|
+
return ok({
|
|
29
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
30
|
+
capabilities: { tools: {} },
|
|
31
|
+
serverInfo: { name: "agent-authority", version: "0.1.0" },
|
|
32
|
+
});
|
|
33
|
+
case "notifications/initialized":
|
|
34
|
+
case "initialized":
|
|
35
|
+
return null; // notification, no reply
|
|
36
|
+
case "ping":
|
|
37
|
+
return ok({});
|
|
38
|
+
case "tools/list":
|
|
39
|
+
return ok({
|
|
40
|
+
tools: tools.map((t) => ({
|
|
41
|
+
name: t.name,
|
|
42
|
+
description: t.description,
|
|
43
|
+
inputSchema: t.inputSchema,
|
|
44
|
+
})),
|
|
45
|
+
});
|
|
46
|
+
case "tools/call": {
|
|
47
|
+
const name = req.params?.name;
|
|
48
|
+
const args = req.params?.arguments ?? {};
|
|
49
|
+
const tool = byName.get(name);
|
|
50
|
+
if (!tool)
|
|
51
|
+
return err(-32602, `unknown tool "${name}"`);
|
|
52
|
+
try {
|
|
53
|
+
const result = await tool.handler(args);
|
|
54
|
+
return ok({
|
|
55
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
56
|
+
isError: false,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
return ok({
|
|
61
|
+
content: [{ type: "text", text: e.message }],
|
|
62
|
+
isError: true,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
// Ignore other notifications; error on unknown requests.
|
|
68
|
+
if (req.id === undefined)
|
|
69
|
+
return null;
|
|
70
|
+
return err(-32601, `method not found: ${req.method}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function start() {
|
|
74
|
+
const rl = createInterface({ input: process.stdin });
|
|
75
|
+
rl.on("line", (line) => {
|
|
76
|
+
const trimmed = line.trim();
|
|
77
|
+
if (!trimmed)
|
|
78
|
+
return;
|
|
79
|
+
let req;
|
|
80
|
+
try {
|
|
81
|
+
req = JSON.parse(trimmed);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return; // ignore malformed input
|
|
85
|
+
}
|
|
86
|
+
dispatch(req).then((res) => {
|
|
87
|
+
if (res)
|
|
88
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return { dispatch, start };
|
|
93
|
+
}
|
|
94
|
+
// Allow `node dist/mcp-server.js` (and the `agent-authority-mcp` bin) to run the server,
|
|
95
|
+
// while never auto-starting when imported (e.g. by tests). Compares real paths
|
|
96
|
+
// so an npm bin symlink still resolves to this module.
|
|
97
|
+
function runningAsMain() {
|
|
98
|
+
const entry = process.argv[1];
|
|
99
|
+
if (!entry)
|
|
100
|
+
return false;
|
|
101
|
+
try {
|
|
102
|
+
return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (runningAsMain()) {
|
|
109
|
+
createMcpServer().start();
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
|