@parlor/server 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +19 -2
- package/dist/access.d.ts +27 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +41 -0
- package/dist/access.js.map +1 -0
- package/dist/actions.d.ts +43 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +169 -0
- package/dist/actions.js.map +1 -0
- package/dist/auth.d.ts +97 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +11 -0
- package/dist/auth.js.map +1 -0
- package/dist/body.d.ts +7 -0
- package/dist/body.d.ts.map +1 -0
- package/dist/body.js +29 -0
- package/dist/body.js.map +1 -0
- package/dist/caller.d.ts +21 -0
- package/dist/caller.d.ts.map +1 -0
- package/dist/caller.js +46 -0
- package/dist/caller.js.map +1 -0
- package/dist/cors.d.ts +9 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +25 -0
- package/dist/cors.js.map +1 -0
- package/dist/create-server.d.ts +43 -0
- package/dist/create-server.d.ts.map +1 -0
- package/dist/create-server.js +422 -0
- package/dist/create-server.js.map +1 -0
- package/dist/dev-auth.d.ts +17 -0
- package/dist/dev-auth.d.ts.map +1 -0
- package/dist/dev-auth.js +146 -0
- package/dist/dev-auth.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-calls.d.ts +7 -0
- package/dist/mcp-calls.d.ts.map +1 -0
- package/dist/mcp-calls.js +30 -0
- package/dist/mcp-calls.js.map +1 -0
- package/dist/mcp.d.ts +29 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +198 -0
- package/dist/mcp.js.map +1 -0
- package/dist/migrations.d.ts +10 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +81 -0
- package/dist/migrations.js.map +1 -0
- package/dist/plugin.d.ts +117 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +5 -0
- package/dist/plugin.js.map +1 -0
- package/dist/protocol.d.ts +18 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +7 -0
- package/dist/protocol.js.map +1 -0
- package/dist/revocation-horizon.d.ts +6 -0
- package/dist/revocation-horizon.d.ts.map +1 -0
- package/dist/revocation-horizon.js +6 -0
- package/dist/revocation-horizon.js.map +1 -0
- package/dist/scope.d.ts +6 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +4 -0
- package/dist/scope.js.map +1 -0
- package/package.json +41 -3
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DEV_USER_ID } from "@parlor/core";
|
|
2
|
+
import { type AuthProvider } from "./auth.js";
|
|
3
|
+
export declare const DEV_AUTH_PLUGIN = "dev-auth";
|
|
4
|
+
export { DEV_USER_ID };
|
|
5
|
+
/** `POST /_parlor/dev-auth/host`: mints the dev host token for a loopback caller. */
|
|
6
|
+
export declare const DEV_HOST_PATH = "/host";
|
|
7
|
+
export interface DevAuthOptions {
|
|
8
|
+
readonly secret?: Uint8Array | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* What `parlor dev` installs when the app has no auth provider. Not enforcing, so the
|
|
12
|
+
* browser and external MCP clients keep today's anonymous, header-declared behavior; it
|
|
13
|
+
* only adds the host and turn tokens the local inline agent needs. Both are signed with a
|
|
14
|
+
* per-process secret, so restarting the server revokes them all.
|
|
15
|
+
*/
|
|
16
|
+
export declare function devAuth(options?: DevAuthOptions): AuthProvider;
|
|
17
|
+
//# sourceMappingURL=dev-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-auth.d.ts","sourceRoot":"","sources":["../src/dev-auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,WAAW,EAGZ,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAe,KAAK,YAAY,EAAuB,MAAM,WAAW,CAAC;AAEhF,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,CAAC;AAEvB,qFAAqF;AACrF,eAAO,MAAM,aAAa,UAAU,CAAC;AAErC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC1C;AA4CD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,YAAY,CAiHlE"}
|
package/dist/dev-auth.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { ANONYMOUS, asId, DEV_USER_ID, } from "@parlor/core";
|
|
3
|
+
import { AuthFailure } from "./auth.js";
|
|
4
|
+
export const DEV_AUTH_PLUGIN = "dev-auth";
|
|
5
|
+
export { DEV_USER_ID };
|
|
6
|
+
/** `POST /_parlor/dev-auth/host`: mints the dev host token for a loopback caller. */
|
|
7
|
+
export const DEV_HOST_PATH = "/host";
|
|
8
|
+
const HOST_TYP = "parlor-dev-host+jwt";
|
|
9
|
+
const TURN_TYP = "parlor-turn+jwt";
|
|
10
|
+
const HOST_AUDIENCE = "parlor:host";
|
|
11
|
+
const TURN_AUDIENCE = "parlor:mcp";
|
|
12
|
+
const DEV_GRANT = asId("grant", "dev");
|
|
13
|
+
const DEV_HOST = asId("host", "dev");
|
|
14
|
+
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
15
|
+
function decode(segment) {
|
|
16
|
+
if (!segment)
|
|
17
|
+
return undefined;
|
|
18
|
+
try {
|
|
19
|
+
const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
|
|
20
|
+
return typeof value === "object" && value !== null ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function isLoopback(ip) {
|
|
27
|
+
if (ip === undefined)
|
|
28
|
+
return false;
|
|
29
|
+
return ip === "::1" || ip.startsWith("127.") || ip.startsWith("::ffff:127.");
|
|
30
|
+
}
|
|
31
|
+
const refuse = (message) => Response.json({ error: { code: "forbidden", message } }, { status: 403 });
|
|
32
|
+
/**
|
|
33
|
+
* What `parlor dev` installs when the app has no auth provider. Not enforcing, so the
|
|
34
|
+
* browser and external MCP clients keep today's anonymous, header-declared behavior; it
|
|
35
|
+
* only adds the host and turn tokens the local inline agent needs. Both are signed with a
|
|
36
|
+
* per-process secret, so restarting the server revokes them all.
|
|
37
|
+
*/
|
|
38
|
+
export function devAuth(options = {}) {
|
|
39
|
+
const secret = options.secret ?? randomBytes(32);
|
|
40
|
+
const mac = (input) => createHmac("sha256", secret).update(input).digest("base64url");
|
|
41
|
+
const sign = (typ, claims) => {
|
|
42
|
+
const input = `${encode({ alg: "HS256", typ })}.${encode(claims)}`;
|
|
43
|
+
return `${input}.${mac(input)}`;
|
|
44
|
+
};
|
|
45
|
+
const verify = (token, typ) => {
|
|
46
|
+
const [header, payload, signature, ...rest] = token.split(".");
|
|
47
|
+
if (rest.length > 0 || !header || !payload || !signature)
|
|
48
|
+
return undefined;
|
|
49
|
+
const expected = Buffer.from(mac(`${header}.${payload}`));
|
|
50
|
+
const given = Buffer.from(signature);
|
|
51
|
+
if (given.length !== expected.length || !timingSafeEqual(given, expected)) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return decode(header)?.typ === typ ? decode(payload) : undefined;
|
|
55
|
+
};
|
|
56
|
+
const typOf = (token) => decode(token.split(".")[0])?.typ;
|
|
57
|
+
const hostOf = (token) => {
|
|
58
|
+
if (verify(token, HOST_TYP)?.aud !== HOST_AUDIENCE) {
|
|
59
|
+
throw new AuthFailure("invalid", "This computer is not connected.");
|
|
60
|
+
}
|
|
61
|
+
return { kind: "host", userId: DEV_USER_ID, grantId: DEV_GRANT, hostId: DEV_HOST };
|
|
62
|
+
};
|
|
63
|
+
const agentOf = (token) => {
|
|
64
|
+
const claims = verify(token, TURN_TYP);
|
|
65
|
+
if (claims?.aud !== TURN_AUDIENCE ||
|
|
66
|
+
typeof claims.exp !== "number" ||
|
|
67
|
+
!claims.hid ||
|
|
68
|
+
!claims.thr ||
|
|
69
|
+
!claims.trn ||
|
|
70
|
+
typeof claims.cid !== "string" ||
|
|
71
|
+
!claims.agt) {
|
|
72
|
+
throw new AuthFailure("invalid", "The turn token is not valid.");
|
|
73
|
+
}
|
|
74
|
+
if (claims.exp * 1000 <= Date.now()) {
|
|
75
|
+
throw new AuthFailure("expired", "The turn is over.");
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
kind: "agent",
|
|
79
|
+
userId: DEV_USER_ID,
|
|
80
|
+
grantId: DEV_GRANT,
|
|
81
|
+
agent: { name: claims.agt, verified: true },
|
|
82
|
+
turn: {
|
|
83
|
+
threadId: asId("thread", claims.thr),
|
|
84
|
+
turnId: asId("turn", claims.trn),
|
|
85
|
+
hostId: asId("host", claims.hid),
|
|
86
|
+
clientId: claims.cid,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
name: DEV_AUTH_PLUGIN,
|
|
92
|
+
enforcing: false,
|
|
93
|
+
setup: () => ({
|
|
94
|
+
routes: [
|
|
95
|
+
{
|
|
96
|
+
method: "POST",
|
|
97
|
+
path: DEV_HOST_PATH,
|
|
98
|
+
access: "public",
|
|
99
|
+
handle: async (request) => {
|
|
100
|
+
if (request.headers.has("origin")) {
|
|
101
|
+
return refuse("The dev host token is not for web pages.");
|
|
102
|
+
}
|
|
103
|
+
if (!isLoopback(request.ip)) {
|
|
104
|
+
return refuse("The dev host token is only for this computer.");
|
|
105
|
+
}
|
|
106
|
+
return Response.json({
|
|
107
|
+
token: sign(HOST_TYP, { aud: HOST_AUDIENCE, hid: DEV_HOST }),
|
|
108
|
+
hostId: DEV_HOST,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
}),
|
|
114
|
+
authenticate: async (request) => {
|
|
115
|
+
const match = /^(\w+)\s+(\S+)$/.exec(request.headers.get("authorization") ?? "");
|
|
116
|
+
const scheme = match?.[1]?.toLowerCase();
|
|
117
|
+
const token = match?.[2];
|
|
118
|
+
if (!token)
|
|
119
|
+
return ANONYMOUS;
|
|
120
|
+
if (scheme === "parlorhost")
|
|
121
|
+
return hostOf(token);
|
|
122
|
+
if (scheme === "bearer" && typOf(token) === TURN_TYP)
|
|
123
|
+
return agentOf(token);
|
|
124
|
+
return ANONYMOUS;
|
|
125
|
+
},
|
|
126
|
+
challenge: () => Response.json({ error: { code: "unauthenticated", message: "The turn is over." } }, { status: 401 }),
|
|
127
|
+
revoked: () => new AbortController().signal,
|
|
128
|
+
delegate: async (host, turn) => {
|
|
129
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
130
|
+
const exp = iat + Math.ceil(turn.ttlMs / 1000);
|
|
131
|
+
const claims = {
|
|
132
|
+
aud: TURN_AUDIENCE,
|
|
133
|
+
sub: host.userId,
|
|
134
|
+
hid: host.hostId,
|
|
135
|
+
thr: turn.threadId,
|
|
136
|
+
trn: turn.turnId,
|
|
137
|
+
cid: turn.clientId,
|
|
138
|
+
agt: turn.agent,
|
|
139
|
+
iat,
|
|
140
|
+
exp,
|
|
141
|
+
};
|
|
142
|
+
return { token: sign(TURN_TYP, claims), expiresAt: exp * 1000 };
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=dev-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-auth.js","sourceRoot":"","sources":["../src/dev-auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nimport {\n ANONYMOUS,\n type AgentPrincipal,\n asId,\n DEV_USER_ID,\n type HostPrincipal,\n type Principal,\n} from \"@parlor/core\";\n\nimport { AuthFailure, type AuthProvider, type TurnDelegation } from \"./auth.js\";\n\nexport const DEV_AUTH_PLUGIN = \"dev-auth\";\n\nexport { DEV_USER_ID };\n\n/** `POST /_parlor/dev-auth/host`: mints the dev host token for a loopback caller. */\nexport const DEV_HOST_PATH = \"/host\";\n\nexport interface DevAuthOptions {\n readonly secret?: Uint8Array | undefined;\n}\n\nconst HOST_TYP = \"parlor-dev-host+jwt\";\nconst TURN_TYP = \"parlor-turn+jwt\";\nconst HOST_AUDIENCE = \"parlor:host\";\nconst TURN_AUDIENCE = \"parlor:mcp\";\nconst DEV_GRANT = asId(\"grant\", \"dev\");\nconst DEV_HOST = asId(\"host\", \"dev\");\n\ninterface TurnClaims {\n readonly aud: typeof TURN_AUDIENCE;\n readonly sub: string;\n readonly hid: string;\n readonly thr: string;\n readonly trn: string;\n readonly cid: string;\n readonly agt: string;\n readonly iat: number;\n readonly exp: number;\n}\n\ntype Fields = Readonly<Record<string, unknown>>;\n\nconst encode = (value: unknown) =>\n Buffer.from(JSON.stringify(value)).toString(\"base64url\");\n\nfunction decode(segment: string | undefined): Fields | undefined {\n if (!segment) return undefined;\n try {\n const value: unknown = JSON.parse(Buffer.from(segment, \"base64url\").toString(\"utf8\"));\n return typeof value === \"object\" && value !== null ? (value as Fields) : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isLoopback(ip: string | undefined): boolean {\n if (ip === undefined) return false;\n return ip === \"::1\" || ip.startsWith(\"127.\") || ip.startsWith(\"::ffff:127.\");\n}\n\nconst refuse = (message: string) =>\n Response.json({ error: { code: \"forbidden\", message } }, { status: 403 });\n\n/**\n * What `parlor dev` installs when the app has no auth provider. Not enforcing, so the\n * browser and external MCP clients keep today's anonymous, header-declared behavior; it\n * only adds the host and turn tokens the local inline agent needs. Both are signed with a\n * per-process secret, so restarting the server revokes them all.\n */\nexport function devAuth(options: DevAuthOptions = {}): AuthProvider {\n const secret = options.secret ?? randomBytes(32);\n const mac = (input: string) =>\n createHmac(\"sha256\", secret).update(input).digest(\"base64url\");\n const sign = (typ: string, claims: object) => {\n const input = `${encode({ alg: \"HS256\", typ })}.${encode(claims)}`;\n return `${input}.${mac(input)}`;\n };\n const verify = (token: string, typ: string): Fields | undefined => {\n const [header, payload, signature, ...rest] = token.split(\".\");\n if (rest.length > 0 || !header || !payload || !signature) return undefined;\n const expected = Buffer.from(mac(`${header}.${payload}`));\n const given = Buffer.from(signature);\n if (given.length !== expected.length || !timingSafeEqual(given, expected)) {\n return undefined;\n }\n return decode(header)?.typ === typ ? decode(payload) : undefined;\n };\n const typOf = (token: string) => decode(token.split(\".\")[0])?.typ;\n\n const hostOf = (token: string): HostPrincipal => {\n if (verify(token, HOST_TYP)?.aud !== HOST_AUDIENCE) {\n throw new AuthFailure(\"invalid\", \"This computer is not connected.\");\n }\n return { kind: \"host\", userId: DEV_USER_ID, grantId: DEV_GRANT, hostId: DEV_HOST };\n };\n\n const agentOf = (token: string): AgentPrincipal => {\n const claims = verify(token, TURN_TYP) as Partial<TurnClaims> | undefined;\n if (\n claims?.aud !== TURN_AUDIENCE ||\n typeof claims.exp !== \"number\" ||\n !claims.hid ||\n !claims.thr ||\n !claims.trn ||\n typeof claims.cid !== \"string\" ||\n !claims.agt\n ) {\n throw new AuthFailure(\"invalid\", \"The turn token is not valid.\");\n }\n if (claims.exp * 1000 <= Date.now()) {\n throw new AuthFailure(\"expired\", \"The turn is over.\");\n }\n return {\n kind: \"agent\",\n userId: DEV_USER_ID,\n grantId: DEV_GRANT,\n agent: { name: claims.agt, verified: true },\n turn: {\n threadId: asId(\"thread\", claims.thr),\n turnId: asId(\"turn\", claims.trn),\n hostId: asId(\"host\", claims.hid),\n clientId: claims.cid,\n },\n };\n };\n\n return {\n name: DEV_AUTH_PLUGIN,\n enforcing: false,\n setup: () => ({\n routes: [\n {\n method: \"POST\",\n path: DEV_HOST_PATH,\n access: \"public\",\n handle: async (request) => {\n if (request.headers.has(\"origin\")) {\n return refuse(\"The dev host token is not for web pages.\");\n }\n if (!isLoopback((request as Request & { readonly ip?: string }).ip)) {\n return refuse(\"The dev host token is only for this computer.\");\n }\n return Response.json({\n token: sign(HOST_TYP, { aud: HOST_AUDIENCE, hid: DEV_HOST }),\n hostId: DEV_HOST,\n });\n },\n },\n ],\n }),\n authenticate: async (request): Promise<Principal> => {\n const match = /^(\\w+)\\s+(\\S+)$/.exec(request.headers.get(\"authorization\") ?? \"\");\n const scheme = match?.[1]?.toLowerCase();\n const token = match?.[2];\n if (!token) return ANONYMOUS;\n if (scheme === \"parlorhost\") return hostOf(token);\n if (scheme === \"bearer\" && typOf(token) === TURN_TYP) return agentOf(token);\n return ANONYMOUS;\n },\n challenge: () =>\n Response.json(\n { error: { code: \"unauthenticated\", message: \"The turn is over.\" } },\n { status: 401 },\n ),\n revoked: () => new AbortController().signal,\n delegate: async (host: HostPrincipal, turn: TurnDelegation) => {\n const iat = Math.floor(Date.now() / 1000);\n const exp = iat + Math.ceil(turn.ttlMs / 1000);\n const claims: TurnClaims = {\n aud: TURN_AUDIENCE,\n sub: host.userId,\n hid: host.hostId,\n thr: turn.threadId,\n trn: turn.turnId,\n cid: turn.clientId,\n agt: turn.agent,\n iat,\n exp,\n };\n return { token: sign(TURN_TYP, claims), expiresAt: exp * 1000 };\n },\n };\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { type CallerOf, type KindsOf, requireAccess, requireSurface, ROUTE_ACCESS, type Surface, } from "./access.js";
|
|
2
|
+
export { type ActionRun, type ActionRunner, type Confirm, createActionRunner, type RunOptions, } from "./actions.js";
|
|
3
|
+
export { type AccountProfile, AuthFailure, type AuthFailureReason, type AuthProvider, type Delegation, type DeviceKeys, type DeviceVouch, HOST_GRANTS_TAG, type PresenceKey, type TurnDelegation, } from "./auth.js";
|
|
4
|
+
export { readBoundedText } from "./body.js";
|
|
5
|
+
export { REVOCATION_HORIZON_MS } from "./revocation-horizon.js";
|
|
6
|
+
export { type Caller, type CallerResolver, clientOf, createCallerResolver, sourceFor, sourceOf, } from "./caller.js";
|
|
7
|
+
export { type CorsConfig, isAllowedOrigin, SHELL_ORIGINS } from "./cors.js";
|
|
8
|
+
export { createServer, type FrameworkServer, type ServerOptions, type SyncOptions, } from "./create-server.js";
|
|
9
|
+
export { DEV_AUTH_PLUGIN, DEV_HOST_PATH, DEV_USER_ID, devAuth, type DevAuthOptions, } from "./dev-auth.js";
|
|
10
|
+
export { createMcpHandler, type McpOptions } from "./mcp.js";
|
|
11
|
+
export { type ActionAudit, type AnyPluginRoute, type ConfirmationAnswer, type Migration, type PluginCaller, type PluginHost, type PluginRoute, pluginRoute, type PluginRuntime, type PublicPluginRoute, type ServerPlugin, } from "./plugin.js";
|
|
12
|
+
export { CLIENT_HEADER, DEFAULT_BASE_PATH, type Manifest, PROTOCOL_VERSION, SESSION_TOKEN_HEADER, SOURCE_HEADER, } from "./protocol.js";
|
|
13
|
+
export { DEFAULT_SCOPE, defaultScope, type ScopeResolver } from "./scope.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,KAAK,OAAO,GACb,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,kBAAkB,EAClB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,cAAc,EACnB,WAAW,EACX,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,cAAc,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,QAAQ,EACR,oBAAoB,EACpB,SAAS,EACT,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EACL,YAAY,EACZ,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,OAAO,EACP,KAAK,cAAc,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,KAAK,QAAQ,EACb,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { requireAccess, requireSurface, ROUTE_ACCESS, } from "./access.js";
|
|
2
|
+
export { createActionRunner, } from "./actions.js";
|
|
3
|
+
export { AuthFailure, HOST_GRANTS_TAG, } from "./auth.js";
|
|
4
|
+
export { readBoundedText } from "./body.js";
|
|
5
|
+
export { REVOCATION_HORIZON_MS } from "./revocation-horizon.js";
|
|
6
|
+
export { clientOf, createCallerResolver, sourceFor, sourceOf, } from "./caller.js";
|
|
7
|
+
export { isAllowedOrigin, SHELL_ORIGINS } from "./cors.js";
|
|
8
|
+
export { createServer, } from "./create-server.js";
|
|
9
|
+
export { DEV_AUTH_PLUGIN, DEV_HOST_PATH, DEV_USER_ID, devAuth, } from "./dev-auth.js";
|
|
10
|
+
export { createMcpHandler } from "./mcp.js";
|
|
11
|
+
export { pluginRoute, } from "./plugin.js";
|
|
12
|
+
export { CLIENT_HEADER, DEFAULT_BASE_PATH, PROTOCOL_VERSION, SESSION_TOKEN_HEADER, SOURCE_HEADER, } from "./protocol.js";
|
|
13
|
+
export { DEFAULT_SCOPE, defaultScope } from "./scope.js";
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,aAAa,EACb,cAAc,EACd,YAAY,GAEb,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,kBAAkB,GAEnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,WAAW,EAMX,eAAe,GAGhB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAGL,QAAQ,EACR,oBAAoB,EACpB,SAAS,EACT,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAmB,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EACL,YAAY,GAIb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,aAAa,EACb,WAAW,EACX,OAAO,GAER,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAmB,MAAM,UAAU,CAAC;AAC7D,OAAO,EAQL,WAAW,GAIZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,aAAa,EACb,iBAAiB,EAEjB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAsB,MAAM,YAAY,CAAC","sourcesContent":["export {\n type CallerOf,\n type KindsOf,\n requireAccess,\n requireSurface,\n ROUTE_ACCESS,\n type Surface,\n} from \"./access.js\";\nexport {\n type ActionRun,\n type ActionRunner,\n type Confirm,\n createActionRunner,\n type RunOptions,\n} from \"./actions.js\";\nexport {\n type AccountProfile,\n AuthFailure,\n type AuthFailureReason,\n type AuthProvider,\n type Delegation,\n type DeviceKeys,\n type DeviceVouch,\n HOST_GRANTS_TAG,\n type PresenceKey,\n type TurnDelegation,\n} from \"./auth.js\";\nexport { readBoundedText } from \"./body.js\";\nexport { REVOCATION_HORIZON_MS } from \"./revocation-horizon.js\";\nexport {\n type Caller,\n type CallerResolver,\n clientOf,\n createCallerResolver,\n sourceFor,\n sourceOf,\n} from \"./caller.js\";\nexport { type CorsConfig, isAllowedOrigin, SHELL_ORIGINS } from \"./cors.js\";\nexport {\n createServer,\n type FrameworkServer,\n type ServerOptions,\n type SyncOptions,\n} from \"./create-server.js\";\nexport {\n DEV_AUTH_PLUGIN,\n DEV_HOST_PATH,\n DEV_USER_ID,\n devAuth,\n type DevAuthOptions,\n} from \"./dev-auth.js\";\nexport { createMcpHandler, type McpOptions } from \"./mcp.js\";\nexport {\n type ActionAudit,\n type AnyPluginRoute,\n type ConfirmationAnswer,\n type Migration,\n type PluginCaller,\n type PluginHost,\n type PluginRoute,\n pluginRoute,\n type PluginRuntime,\n type PublicPluginRoute,\n type ServerPlugin,\n} from \"./plugin.js\";\nexport {\n CLIENT_HEADER,\n DEFAULT_BASE_PATH,\n type Manifest,\n PROTOCOL_VERSION,\n SESSION_TOKEN_HEADER,\n SOURCE_HEADER,\n} from \"./protocol.js\";\nexport { DEFAULT_SCOPE, defaultScope, type ScopeResolver } from \"./scope.js\";\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RequestId } from "@modelcontextprotocol/server";
|
|
2
|
+
export declare class McpCalls {
|
|
3
|
+
#private;
|
|
4
|
+
run<T>(request: Request | undefined, id: RequestId, signals: readonly AbortSignal[], work: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
5
|
+
cancel(request: Request, id: RequestId, reason: unknown): void;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=mcp-calls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-calls.d.ts","sourceRoot":"","sources":["../src/mcp-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,qBAAa,QAAQ;;IAGb,GAAG,CAAC,CAAC,EACT,OAAO,EAAE,OAAO,GAAG,SAAS,EAC5B,EAAE,EAAE,SAAS,EACb,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC,CAaZ;IAED,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAG7D;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class McpCalls {
|
|
2
|
+
#inFlight = new Map();
|
|
3
|
+
async run(request, id, signals, work) {
|
|
4
|
+
if (!request)
|
|
5
|
+
return work(AbortSignal.any([...signals]));
|
|
6
|
+
const key = keyOf(request, id);
|
|
7
|
+
const cancellation = new AbortController();
|
|
8
|
+
const calls = this.#inFlight.get(key) ?? new Set();
|
|
9
|
+
calls.add(cancellation);
|
|
10
|
+
this.#inFlight.set(key, calls);
|
|
11
|
+
try {
|
|
12
|
+
return await work(AbortSignal.any([...signals, cancellation.signal]));
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
calls.delete(cancellation);
|
|
16
|
+
if (calls.size === 0)
|
|
17
|
+
this.#inFlight.delete(key);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
cancel(request, id, reason) {
|
|
21
|
+
for (const call of this.#inFlight.get(keyOf(request, id)) ?? [])
|
|
22
|
+
call.abort(reason ?? new DOMException("The call was cancelled.", "AbortError"));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const IDENTIFYING_HEADERS = ["authorization", "mcp-session-id", "user-agent"];
|
|
26
|
+
const keyOf = (request, id) => JSON.stringify([
|
|
27
|
+
...IDENTIFYING_HEADERS.map((header) => request.headers.get(header) ?? ""),
|
|
28
|
+
id,
|
|
29
|
+
]);
|
|
30
|
+
//# sourceMappingURL=mcp-calls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-calls.js","sourceRoot":"","sources":["../src/mcp-calls.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,QAAQ;IACV,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;IAE7D,KAAK,CAAC,GAAG,CACP,OAA4B,EAC5B,EAAa,EACb,OAA+B,EAC/B,IAAyC;QAEzC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,YAAY,GAAG,IAAI,eAAe,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,EAAmB,CAAC;QACpE,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACxB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;gBAAS,CAAC;YACT,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAgB,EAAE,EAAa,EAAE,MAAe;QACrD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;YAC7D,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,YAAY,CAAC,yBAAyB,EAAE,YAAY,CAAC,CAAC,CAAC;IACpF,CAAC;CACF;AAED,MAAM,mBAAmB,GAAG,CAAC,eAAe,EAAE,gBAAgB,EAAE,YAAY,CAAU,CAAC;AAEvF,MAAM,KAAK,GAAG,CAAC,OAAgB,EAAE,EAAa,EAAU,EAAE,CACxD,IAAI,CAAC,SAAS,CAAC;IACb,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACzE,EAAE;CACH,CAAC,CAAC","sourcesContent":["import type { RequestId } from \"@modelcontextprotocol/server\";\n\nexport class McpCalls {\n readonly #inFlight = new Map<string, Set<AbortController>>();\n\n async run<T>(\n request: Request | undefined,\n id: RequestId,\n signals: readonly AbortSignal[],\n work: (signal: AbortSignal) => Promise<T>,\n ): Promise<T> {\n if (!request) return work(AbortSignal.any([...signals]));\n const key = keyOf(request, id);\n const cancellation = new AbortController();\n const calls = this.#inFlight.get(key) ?? new Set<AbortController>();\n calls.add(cancellation);\n this.#inFlight.set(key, calls);\n try {\n return await work(AbortSignal.any([...signals, cancellation.signal]));\n } finally {\n calls.delete(cancellation);\n if (calls.size === 0) this.#inFlight.delete(key);\n }\n }\n\n cancel(request: Request, id: RequestId, reason: unknown): void {\n for (const call of this.#inFlight.get(keyOf(request, id)) ?? [])\n call.abort(reason ?? new DOMException(\"The call was cancelled.\", \"AbortError\"));\n }\n}\n\nconst IDENTIFYING_HEADERS = [\"authorization\", \"mcp-session-id\", \"user-agent\"] as const;\n\nconst keyOf = (request: Request, id: RequestId): string =>\n JSON.stringify([\n ...IDENTIFYING_HEADERS.map((header) => request.headers.get(header) ?? \"\"),\n id,\n ]);\n"]}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type McpHttpHandler, type ServerContext } from "@modelcontextprotocol/server";
|
|
2
|
+
import type { AgentOrigin } from "@parlor/agent-ui";
|
|
3
|
+
import { type AgentUi } from "@parlor/agent-ui/server";
|
|
4
|
+
import { type ActionMap } from "@parlor/core";
|
|
5
|
+
import type { ActionRegistry } from "@parlor/core/registry";
|
|
6
|
+
import type { ActionRunner } from "./actions.js";
|
|
7
|
+
import type { CallerResolver } from "./caller.js";
|
|
8
|
+
export interface McpOptions<T extends ActionMap> {
|
|
9
|
+
readonly actions: ActionRegistry<T>;
|
|
10
|
+
readonly runner: ActionRunner;
|
|
11
|
+
readonly name?: string | undefined;
|
|
12
|
+
readonly version?: string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Which app these tools belong to, told to every MCP client, so an agent connected to
|
|
15
|
+
* several Parlor apps keeps them apart.
|
|
16
|
+
*/
|
|
17
|
+
readonly app?: {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly description?: string | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
readonly agentUi?: AgentUi | undefined;
|
|
22
|
+
readonly callers?: CallerResolver | undefined;
|
|
23
|
+
}
|
|
24
|
+
export declare function appIdentity(app: McpOptions<ActionMap>["app"], origin: string | undefined): string;
|
|
25
|
+
export declare const PROGRESS_INTERVAL_MS = 10000;
|
|
26
|
+
export declare function originOf(ctx: ServerContext): AgentOrigin;
|
|
27
|
+
export declare function withProgress<T>(ctx: ServerContext, work: () => Promise<T>, intervalMs?: number): Promise<T>;
|
|
28
|
+
export declare function createMcpHandler<T extends ActionMap>(options: McpOptions<T>): McpHttpHandler;
|
|
29
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EAGnB,KAAK,aAAa,EAEnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,KAAK,OAAO,EAAmC,MAAM,yBAAyB,CAAC;AACxF,OAAO,EAGL,KAAK,SAAS,EAGf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,SAAS;IAC7C,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EACT;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GACpE,SAAS,CAAC;IACd,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CAC/C;AASD,wBAAgB,WAAW,CACzB,GAAG,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,EACjC,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,MAAM,CAKR;AA+DD,eAAO,MAAM,oBAAoB,QAAS,CAAC;AA4B3C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,WAAW,CAQxD;AAED,wBAAsB,YAAY,CAAC,CAAC,EAClC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,UAAU,GAAE,MAA6B,GACxC,OAAO,CAAC,CAAC,CAAC,CAqBZ;AAED,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,SAAS,EAClD,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GACrB,cAAc,CAiIhB"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { CLIENT_INFO_META_KEY, createMcpHandler as createSdkHandler, McpServer, } from "@modelcontextprotocol/server";
|
|
2
|
+
import { UI_MANIFEST_RESOURCE, UiFailure } from "@parlor/agent-ui/server";
|
|
3
|
+
import { ActionError, isMutating, } from "@parlor/core";
|
|
4
|
+
import { McpCalls } from "./mcp-calls.js";
|
|
5
|
+
/** The public origin first, then the one `parlor dev` serves the app on, then the request's. */
|
|
6
|
+
function appOrigin(request) {
|
|
7
|
+
const configured = process.env.PARLOR_ORIGIN || process.env.PARLOR_WEB_ORIGIN;
|
|
8
|
+
if (configured)
|
|
9
|
+
return new URL(configured).origin;
|
|
10
|
+
return request ? new URL(request.url).origin : undefined;
|
|
11
|
+
}
|
|
12
|
+
export function appIdentity(app, origin) {
|
|
13
|
+
const where = origin ? ` at ${origin}` : "";
|
|
14
|
+
const what = app?.description ? ` ${app.description.trim()}` : "";
|
|
15
|
+
const who = app ? app.name : "this app";
|
|
16
|
+
return `These tools belong to ${who}${where}.${what} They act only on ${who}, as the signed-in user; any other app's tools are a separate server.`;
|
|
17
|
+
}
|
|
18
|
+
function toolResult(result) {
|
|
19
|
+
const text = result === undefined ? "ok" : JSON.stringify(result, null, 2);
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: "text", text }],
|
|
22
|
+
...(result !== null && typeof result === "object" && !Array.isArray(result)
|
|
23
|
+
? { structuredContent: result }
|
|
24
|
+
: {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function errorBodyOf(error) {
|
|
28
|
+
if (error instanceof UiFailure)
|
|
29
|
+
return error.error;
|
|
30
|
+
if (error instanceof ActionError) {
|
|
31
|
+
return {
|
|
32
|
+
code: error.code,
|
|
33
|
+
message: error.message,
|
|
34
|
+
...(error.issues
|
|
35
|
+
? {
|
|
36
|
+
issues: error.issues.map((issue) => ({
|
|
37
|
+
path: issue.path.join("."),
|
|
38
|
+
message: issue.message,
|
|
39
|
+
})),
|
|
40
|
+
}
|
|
41
|
+
: {}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { code: "internal", message: "Internal error" };
|
|
45
|
+
}
|
|
46
|
+
function toolError(error) {
|
|
47
|
+
return {
|
|
48
|
+
isError: true,
|
|
49
|
+
content: [
|
|
50
|
+
{ type: "text", text: JSON.stringify({ error: errorBodyOf(error) }, null, 2) },
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The SDK answers bad arguments in plain text before the handler runs. A schema that lists
|
|
56
|
+
* as the original but accepts anything leaves the check to the handler, which fails in the
|
|
57
|
+
* one error body.
|
|
58
|
+
*/
|
|
59
|
+
function unvalidated(schema) {
|
|
60
|
+
return { "~standard": { ...schema["~standard"], validate: (value) => ({ value }) } };
|
|
61
|
+
}
|
|
62
|
+
function actionDescription(action) {
|
|
63
|
+
return action.confirm
|
|
64
|
+
? `${action.description} When an agent calls it, the user must approve it in the app first; the call waits for that answer and fails with declined, confirmation_timeout or confirmation_unavailable without running.`
|
|
65
|
+
: action.description;
|
|
66
|
+
}
|
|
67
|
+
export const PROGRESS_INTERVAL_MS = 10_000;
|
|
68
|
+
async function verifiedOrigin(callers, request, ctx) {
|
|
69
|
+
const caller = callers && request ? await callers(request) : undefined;
|
|
70
|
+
const principal = caller?.principal;
|
|
71
|
+
if (principal?.kind === "agent" && principal.turn) {
|
|
72
|
+
return { via: "agent", name: principal.agent.name };
|
|
73
|
+
}
|
|
74
|
+
if (principal?.kind === "agent" && principal.agent.verified) {
|
|
75
|
+
return { via: "mcp", name: principal.agent.name };
|
|
76
|
+
}
|
|
77
|
+
return originOf(ctx);
|
|
78
|
+
}
|
|
79
|
+
async function turnClientOf(callers, request) {
|
|
80
|
+
const principal = callers && request ? (await callers(request)).principal : undefined;
|
|
81
|
+
return principal?.kind === "agent" && principal.turn
|
|
82
|
+
? { clientId: principal.turn.clientId }
|
|
83
|
+
: undefined;
|
|
84
|
+
}
|
|
85
|
+
export function originOf(ctx) {
|
|
86
|
+
const envelope = ctx.mcpReq.envelope;
|
|
87
|
+
const info = envelope?.[CLIENT_INFO_META_KEY];
|
|
88
|
+
const name = typeof info === "object" && info !== null
|
|
89
|
+
? info.name
|
|
90
|
+
: undefined;
|
|
91
|
+
return typeof name === "string" && name !== "" ? { via: "mcp", name } : { via: "mcp" };
|
|
92
|
+
}
|
|
93
|
+
export async function withProgress(ctx, work, intervalMs = PROGRESS_INTERVAL_MS) {
|
|
94
|
+
const progressToken = ctx.mcpReq._meta?.progressToken;
|
|
95
|
+
if (progressToken === undefined)
|
|
96
|
+
return work();
|
|
97
|
+
let progress = 0;
|
|
98
|
+
const timer = setInterval(() => {
|
|
99
|
+
ctx.mcpReq
|
|
100
|
+
.notify({
|
|
101
|
+
method: "notifications/progress",
|
|
102
|
+
params: {
|
|
103
|
+
progressToken,
|
|
104
|
+
progress: ++progress,
|
|
105
|
+
message: "Waiting for the user to approve in the app",
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
.catch(() => { });
|
|
109
|
+
}, intervalMs);
|
|
110
|
+
try {
|
|
111
|
+
return await work();
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
clearInterval(timer);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function createMcpHandler(options) {
|
|
118
|
+
const { actions, runner, agentUi, callers, app, name = app?.name ?? "parlor", version = "0.0.0", } = options;
|
|
119
|
+
const calls = new McpCalls();
|
|
120
|
+
const inCall = (ctx, toolCtx, work) => calls.run(ctx.requestInfo, toolCtx.mcpReq.id, [...(ctx.requestInfo ? [ctx.requestInfo.signal] : []), toolCtx.mcpReq.signal], work);
|
|
121
|
+
const factory = (ctx) => {
|
|
122
|
+
const instructions = [
|
|
123
|
+
appIdentity(app, appOrigin(ctx.requestInfo)),
|
|
124
|
+
agentUi?.instructions("mcp"),
|
|
125
|
+
]
|
|
126
|
+
.filter(Boolean)
|
|
127
|
+
.join("\n\n");
|
|
128
|
+
const server = new McpServer({ name, version, ...(app ? { title: app.name } : {}) }, { instructions });
|
|
129
|
+
server.server.setNotificationHandler("notifications/cancelled", (notification) => {
|
|
130
|
+
const { requestId, reason } = notification.params;
|
|
131
|
+
if (ctx.requestInfo && requestId !== undefined)
|
|
132
|
+
calls.cancel(ctx.requestInfo, requestId, reason);
|
|
133
|
+
});
|
|
134
|
+
for (const actionName of actions.names()) {
|
|
135
|
+
const action = actions.get(actionName);
|
|
136
|
+
if (!action)
|
|
137
|
+
continue;
|
|
138
|
+
server.registerTool(actionName, {
|
|
139
|
+
description: actionDescription(action),
|
|
140
|
+
inputSchema: unvalidated(action.input),
|
|
141
|
+
annotations: { readOnlyHint: !isMutating(action) },
|
|
142
|
+
}, async (args, toolCtx) => {
|
|
143
|
+
try {
|
|
144
|
+
const origin = await verifiedOrigin(callers, ctx.requestInfo, toolCtx);
|
|
145
|
+
const result = await inCall(ctx, toolCtx, (signal) => {
|
|
146
|
+
const run = () => runner.run(actionName, args, ctx.requestInfo, "mcp", { signal, origin });
|
|
147
|
+
return action.confirm ? withProgress(toolCtx, run) : run();
|
|
148
|
+
});
|
|
149
|
+
return toolResult(result.data);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return toolError(error);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const uiTools = agentUi?.tools("mcp") ?? [];
|
|
157
|
+
const uiManifest = uiTools.length > 0 ? agentUi?.manifest() : undefined;
|
|
158
|
+
if (uiManifest) {
|
|
159
|
+
server.registerResource("ui-manifest", UI_MANIFEST_RESOURCE, {
|
|
160
|
+
description: "The app's pages and the views they show, with full JSON schemas for each view's value.",
|
|
161
|
+
mimeType: "application/json",
|
|
162
|
+
}, (uri) => ({
|
|
163
|
+
contents: [
|
|
164
|
+
{
|
|
165
|
+
uri: uri.href,
|
|
166
|
+
mimeType: "application/json",
|
|
167
|
+
text: JSON.stringify(uiManifest, null, 2),
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
for (const tool of uiTools) {
|
|
173
|
+
server.registerTool(tool.name, {
|
|
174
|
+
description: tool.description,
|
|
175
|
+
inputSchema: unvalidated(tool.input),
|
|
176
|
+
annotations: { readOnlyHint: tool.readOnly },
|
|
177
|
+
}, async (args, toolCtx) => {
|
|
178
|
+
try {
|
|
179
|
+
const input = tool.input.safeParse(args);
|
|
180
|
+
if (!input.success) {
|
|
181
|
+
throw new ActionError("invalid_input", `Invalid input for "${tool.name}"`, {
|
|
182
|
+
issues: input.error.issues,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return toolResult(await inCall(ctx, toolCtx, async (signal) => tool.run(input.data, await agentUi.contextFor(ctx.requestInfo, await verifiedOrigin(callers, ctx.requestInfo, toolCtx), signal, await turnClientOf(callers, ctx.requestInfo)))));
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (!(error instanceof UiFailure))
|
|
189
|
+
console.error("[parlor] UI tool failed:", error);
|
|
190
|
+
return toolError(error);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return server;
|
|
195
|
+
};
|
|
196
|
+
return createSdkHandler(factory);
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CLIENT_INFO_META_KEY,\n type CallToolResult,\n createMcpHandler as createSdkHandler,\n type McpHttpHandler,\n type McpRequestContext,\n McpServer,\n type ServerContext,\n type StandardSchemaWithJSON,\n} from \"@modelcontextprotocol/server\";\nimport type { AgentOrigin, UiError } from \"@parlor/agent-ui\";\nimport { type AgentUi, UI_MANIFEST_RESOURCE, UiFailure } from \"@parlor/agent-ui/server\";\nimport {\n ActionError,\n type ActionErrorCode,\n type ActionMap,\n type AnyAction,\n isMutating,\n} from \"@parlor/core\";\nimport type { ActionRegistry } from \"@parlor/core/registry\";\n\nimport type { ActionRunner } from \"./actions.js\";\nimport type { CallerResolver } from \"./caller.js\";\nimport { McpCalls } from \"./mcp-calls.js\";\n\nexport interface McpOptions<T extends ActionMap> {\n readonly actions: ActionRegistry<T>;\n readonly runner: ActionRunner;\n readonly name?: string | undefined;\n readonly version?: string | undefined;\n /**\n * Which app these tools belong to, told to every MCP client, so an agent connected to\n * several Parlor apps keeps them apart.\n */\n readonly app?:\n | { readonly name: string; readonly description?: string | undefined }\n | undefined;\n readonly agentUi?: AgentUi | undefined;\n readonly callers?: CallerResolver | undefined;\n}\n\n/** The public origin first, then the one `parlor dev` serves the app on, then the request's. */\nfunction appOrigin(request: Request | undefined): string | undefined {\n const configured = process.env.PARLOR_ORIGIN || process.env.PARLOR_WEB_ORIGIN;\n if (configured) return new URL(configured).origin;\n return request ? new URL(request.url).origin : undefined;\n}\n\nexport function appIdentity(\n app: McpOptions<ActionMap>[\"app\"],\n origin: string | undefined,\n): string {\n const where = origin ? ` at ${origin}` : \"\";\n const what = app?.description ? ` ${app.description.trim()}` : \"\";\n const who = app ? app.name : \"this app\";\n return `These tools belong to ${who}${where}.${what} They act only on ${who}, as the signed-in user; any other app's tools are a separate server.`;\n}\n\nfunction toolResult(result: unknown): CallToolResult {\n const text = result === undefined ? \"ok\" : JSON.stringify(result, null, 2);\n return {\n content: [{ type: \"text\", text }],\n ...(result !== null && typeof result === \"object\" && !Array.isArray(result)\n ? { structuredContent: result as Record<string, unknown> }\n : {}),\n };\n}\n\ninterface ToolErrorBody {\n readonly code: UiError[\"code\"] | ActionErrorCode;\n readonly message: string;\n readonly issues?:\n | readonly { readonly path: string; readonly message: string }[]\n | undefined;\n}\n\nfunction errorBodyOf(error: unknown): ToolErrorBody {\n if (error instanceof UiFailure) return error.error;\n if (error instanceof ActionError) {\n return {\n code: error.code,\n message: error.message,\n ...(error.issues\n ? {\n issues: error.issues.map((issue) => ({\n path: issue.path.join(\".\"),\n message: issue.message,\n })),\n }\n : {}),\n };\n }\n return { code: \"internal\", message: \"Internal error\" };\n}\n\nfunction toolError(error: unknown): CallToolResult {\n return {\n isError: true,\n content: [\n { type: \"text\", text: JSON.stringify({ error: errorBodyOf(error) }, null, 2) },\n ],\n };\n}\n\n/**\n * The SDK answers bad arguments in plain text before the handler runs. A schema that lists\n * as the original but accepts anything leaves the check to the handler, which fails in the\n * one error body.\n */\nfunction unvalidated(schema: StandardSchemaWithJSON): StandardSchemaWithJSON {\n return { \"~standard\": { ...schema[\"~standard\"], validate: (value) => ({ value }) } };\n}\n\nfunction actionDescription(action: AnyAction): string {\n return action.confirm\n ? `${action.description} When an agent calls it, the user must approve it in the app first; the call waits for that answer and fails with declined, confirmation_timeout or confirmation_unavailable without running.`\n : action.description;\n}\n\nexport const PROGRESS_INTERVAL_MS = 10_000;\n\nasync function verifiedOrigin(\n callers: CallerResolver | undefined,\n request: Request | undefined,\n ctx: ServerContext,\n): Promise<AgentOrigin> {\n const caller = callers && request ? await callers(request) : undefined;\n const principal = caller?.principal;\n if (principal?.kind === \"agent\" && principal.turn) {\n return { via: \"agent\", name: principal.agent.name };\n }\n if (principal?.kind === \"agent\" && principal.agent.verified) {\n return { via: \"mcp\", name: principal.agent.name };\n }\n return originOf(ctx);\n}\n\nasync function turnClientOf(\n callers: CallerResolver | undefined,\n request: Request | undefined,\n): Promise<{ readonly clientId: string } | undefined> {\n const principal = callers && request ? (await callers(request)).principal : undefined;\n return principal?.kind === \"agent\" && principal.turn\n ? { clientId: principal.turn.clientId }\n : undefined;\n}\n\nexport function originOf(ctx: ServerContext): AgentOrigin {\n const envelope: Readonly<Record<string, unknown>> | undefined = ctx.mcpReq.envelope;\n const info = envelope?.[CLIENT_INFO_META_KEY];\n const name =\n typeof info === \"object\" && info !== null\n ? (info as { name?: unknown }).name\n : undefined;\n return typeof name === \"string\" && name !== \"\" ? { via: \"mcp\", name } : { via: \"mcp\" };\n}\n\nexport async function withProgress<T>(\n ctx: ServerContext,\n work: () => Promise<T>,\n intervalMs: number = PROGRESS_INTERVAL_MS,\n): Promise<T> {\n const progressToken = ctx.mcpReq._meta?.progressToken;\n if (progressToken === undefined) return work();\n let progress = 0;\n const timer = setInterval(() => {\n ctx.mcpReq\n .notify({\n method: \"notifications/progress\",\n params: {\n progressToken,\n progress: ++progress,\n message: \"Waiting for the user to approve in the app\",\n },\n })\n .catch(() => {});\n }, intervalMs);\n try {\n return await work();\n } finally {\n clearInterval(timer);\n }\n}\n\nexport function createMcpHandler<T extends ActionMap>(\n options: McpOptions<T>,\n): McpHttpHandler {\n const {\n actions,\n runner,\n agentUi,\n callers,\n app,\n name = app?.name ?? \"parlor\",\n version = \"0.0.0\",\n } = options;\n\n const calls = new McpCalls();\n\n const inCall = <R>(\n ctx: McpRequestContext,\n toolCtx: ServerContext,\n work: (signal: AbortSignal) => Promise<R>,\n ): Promise<R> =>\n calls.run(\n ctx.requestInfo,\n toolCtx.mcpReq.id,\n [...(ctx.requestInfo ? [ctx.requestInfo.signal] : []), toolCtx.mcpReq.signal],\n work,\n );\n\n const factory = (ctx: McpRequestContext) => {\n const instructions = [\n appIdentity(app, appOrigin(ctx.requestInfo)),\n agentUi?.instructions(\"mcp\"),\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n const server = new McpServer(\n { name, version, ...(app ? { title: app.name } : {}) },\n { instructions },\n );\n server.server.setNotificationHandler(\"notifications/cancelled\", (notification) => {\n const { requestId, reason } = notification.params;\n if (ctx.requestInfo && requestId !== undefined)\n calls.cancel(ctx.requestInfo, requestId, reason);\n });\n for (const actionName of actions.names()) {\n const action = actions.get(actionName);\n if (!action) continue;\n server.registerTool(\n actionName,\n {\n description: actionDescription(action),\n inputSchema: unvalidated(action.input),\n annotations: { readOnlyHint: !isMutating(action) },\n },\n async (args, toolCtx) => {\n try {\n const origin = await verifiedOrigin(callers, ctx.requestInfo, toolCtx);\n const result = await inCall(ctx, toolCtx, (signal) => {\n const run = () =>\n runner.run(actionName, args, ctx.requestInfo, \"mcp\", { signal, origin });\n return action.confirm ? withProgress(toolCtx, run) : run();\n });\n return toolResult(result.data);\n } catch (error) {\n return toolError(error);\n }\n },\n );\n }\n const uiTools = agentUi?.tools(\"mcp\") ?? [];\n const uiManifest = uiTools.length > 0 ? agentUi?.manifest() : undefined;\n if (uiManifest) {\n server.registerResource(\n \"ui-manifest\",\n UI_MANIFEST_RESOURCE,\n {\n description:\n \"The app's pages and the views they show, with full JSON schemas for each view's value.\",\n mimeType: \"application/json\",\n },\n (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(uiManifest, null, 2),\n },\n ],\n }),\n );\n }\n for (const tool of uiTools) {\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: unvalidated(tool.input),\n annotations: { readOnlyHint: tool.readOnly },\n },\n async (args, toolCtx) => {\n try {\n const input = tool.input.safeParse(args);\n if (!input.success) {\n throw new ActionError(\"invalid_input\", `Invalid input for \"${tool.name}\"`, {\n issues: input.error.issues,\n });\n }\n return toolResult(\n await inCall(ctx, toolCtx, async (signal) =>\n tool.run(\n input.data,\n await agentUi!.contextFor(\n ctx.requestInfo,\n await verifiedOrigin(callers, ctx.requestInfo, toolCtx),\n signal,\n await turnClientOf(callers, ctx.requestInfo),\n ),\n ),\n ),\n );\n } catch (error) {\n if (!(error instanceof UiFailure))\n console.error(\"[parlor] UI tool failed:\", error);\n return toolError(error);\n }\n },\n );\n }\n return server;\n };\n\n return createSdkHandler(factory);\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DataProvider } from "@parlor/core";
|
|
2
|
+
import type { ServerPlugin } from "./plugin.js";
|
|
3
|
+
export declare function checkMigrations(plugin: ServerPlugin): void;
|
|
4
|
+
/**
|
|
5
|
+
* PGlite refuses several commands in one prepared statement, so a migration is run one
|
|
6
|
+
* statement at a time. Quotes, comments and dollar-quoted bodies may hold semicolons.
|
|
7
|
+
*/
|
|
8
|
+
export declare function splitStatements(script: string): string[];
|
|
9
|
+
export declare function applyMigrations(database: DataProvider, plugins: readonly ServerPlugin[]): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=migrations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAS1D;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BxD;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,SAAS,YAAY,EAAE,GAC/B,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|