@oharness/engine 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/dist/agent/agent.d.ts +196 -0
- package/dist/agent/agent.js +758 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/compaction.d.ts +68 -0
- package/dist/agent/compaction.js +179 -0
- package/dist/agent/compaction.js.map +1 -0
- package/dist/agent/hooks.d.ts +77 -0
- package/dist/agent/hooks.js +152 -0
- package/dist/agent/hooks.js.map +1 -0
- package/dist/agent/memory.d.ts +45 -0
- package/dist/agent/memory.js +147 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/overflow.d.ts +35 -0
- package/dist/agent/overflow.js +69 -0
- package/dist/agent/overflow.js.map +1 -0
- package/dist/agent/permissions.d.ts +181 -0
- package/dist/agent/permissions.js +289 -0
- package/dist/agent/permissions.js.map +1 -0
- package/dist/agent/prompt.d.ts +25 -0
- package/dist/agent/prompt.js +63 -0
- package/dist/agent/prompt.js.map +1 -0
- package/dist/agent/session.d.ts +108 -0
- package/dist/agent/session.js +231 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/agent/settings.d.ts +37 -0
- package/dist/agent/settings.js +113 -0
- package/dist/agent/settings.js.map +1 -0
- package/dist/agent/skills.d.ts +58 -0
- package/dist/agent/skills.js +225 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/store.d.ts +308 -0
- package/dist/agent/store.js +596 -0
- package/dist/agent/store.js.map +1 -0
- package/dist/agent/subagent.d.ts +44 -0
- package/dist/agent/subagent.js +138 -0
- package/dist/agent/subagent.js.map +1 -0
- package/dist/agent/titles.d.ts +8 -0
- package/dist/agent/titles.js +23 -0
- package/dist/agent/titles.js.map +1 -0
- package/dist/agent/workspace.d.ts +60 -0
- package/dist/agent/workspace.js +156 -0
- package/dist/agent/workspace.js.map +1 -0
- package/dist/artifacts/publish.d.ts +94 -0
- package/dist/artifacts/publish.js +104 -0
- package/dist/artifacts/publish.js.map +1 -0
- package/dist/artifacts/store.d.ts +324 -0
- package/dist/artifacts/store.js +500 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/auth/index.d.ts +13 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/manager.d.ts +83 -0
- package/dist/auth/manager.js +266 -0
- package/dist/auth/manager.js.map +1 -0
- package/dist/auth/oauth.d.ts +83 -0
- package/dist/auth/oauth.js +327 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/providers/account.d.ts +59 -0
- package/dist/auth/providers/account.js +282 -0
- package/dist/auth/providers/account.js.map +1 -0
- package/dist/auth/providers/anthropic.d.ts +24 -0
- package/dist/auth/providers/anthropic.js +209 -0
- package/dist/auth/providers/anthropic.js.map +1 -0
- package/dist/auth/providers/generic.d.ts +57 -0
- package/dist/auth/providers/generic.js +155 -0
- package/dist/auth/providers/generic.js.map +1 -0
- package/dist/auth/providers/official.d.ts +3 -0
- package/dist/auth/providers/official.js +90 -0
- package/dist/auth/providers/official.js.map +1 -0
- package/dist/auth/providers/ottoport.d.ts +3 -0
- package/dist/auth/providers/ottoport.js +60 -0
- package/dist/auth/providers/ottoport.js.map +1 -0
- package/dist/auth/store.d.ts +22 -0
- package/dist/auth/store.js +138 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/auth/types.d.ts +167 -0
- package/dist/auth/types.js +16 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/config.d.ts +197 -0
- package/dist/config.js +195 -0
- package/dist/config.js.map +1 -0
- package/dist/core/errors.d.ts +46 -0
- package/dist/core/errors.js +83 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/tokens.d.ts +12 -0
- package/dist/core/tokens.js +56 -0
- package/dist/core/tokens.js.map +1 -0
- package/dist/core/types.d.ts +91 -0
- package/dist/core/types.js +49 -0
- package/dist/core/types.js.map +1 -0
- package/dist/harness.d.ts +302 -0
- package/dist/harness.js +695 -0
- package/dist/harness.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +55 -0
- package/dist/mcp/client.js +199 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/manager.d.ts +77 -0
- package/dist/mcp/manager.js +325 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/mcp/oauth.d.ts +63 -0
- package/dist/mcp/oauth.js +302 -0
- package/dist/mcp/oauth.js.map +1 -0
- package/dist/mcp/protocol.d.ts +130 -0
- package/dist/mcp/protocol.js +14 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/registry.d.ts +52 -0
- package/dist/mcp/registry.js +100 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/registry.json +293 -0
- package/dist/mcp/secrets.d.ts +20 -0
- package/dist/mcp/secrets.js +75 -0
- package/dist/mcp/secrets.js.map +1 -0
- package/dist/mcp/transport.d.ts +51 -0
- package/dist/mcp/transport.js +158 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/paths.d.ts +37 -0
- package/dist/paths.js +63 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugins.d.ts +55 -0
- package/dist/plugins.js +201 -0
- package/dist/plugins.js.map +1 -0
- package/dist/providers/builtin.d.ts +23 -0
- package/dist/providers/builtin.js +141 -0
- package/dist/providers/builtin.js.map +1 -0
- package/dist/providers/catalog.d.ts +129 -0
- package/dist/providers/catalog.js +254 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/catalog.json +472 -0
- package/dist/providers/discovery.d.ts +16 -0
- package/dist/providers/discovery.js +161 -0
- package/dist/providers/discovery.js.map +1 -0
- package/dist/providers/http.d.ts +24 -0
- package/dist/providers/http.js +139 -0
- package/dist/providers/http.js.map +1 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/piai.d.ts +124 -0
- package/dist/providers/piai.js +551 -0
- package/dist/providers/piai.js.map +1 -0
- package/dist/providers/registry.d.ts +65 -0
- package/dist/providers/registry.js +195 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/resilient.d.ts +46 -0
- package/dist/providers/resilient.js +117 -0
- package/dist/providers/resilient.js.map +1 -0
- package/dist/providers/types.d.ts +182 -0
- package/dist/providers/types.js +50 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/schedule/cron.d.ts +35 -0
- package/dist/schedule/cron.js +125 -0
- package/dist/schedule/cron.js.map +1 -0
- package/dist/schedule/scheduler.d.ts +87 -0
- package/dist/schedule/scheduler.js +237 -0
- package/dist/schedule/scheduler.js.map +1 -0
- package/dist/storage/kv.d.ts +56 -0
- package/dist/storage/kv.js +173 -0
- package/dist/storage/kv.js.map +1 -0
- package/dist/storage/migrate.d.ts +11 -0
- package/dist/storage/migrate.js +93 -0
- package/dist/storage/migrate.js.map +1 -0
- package/dist/storage/project.d.ts +9 -0
- package/dist/storage/project.js +57 -0
- package/dist/storage/project.js.map +1 -0
- package/dist/storage/records.d.ts +32 -0
- package/dist/storage/records.js +48 -0
- package/dist/storage/records.js.map +1 -0
- package/dist/sync/client.d.ts +79 -0
- package/dist/sync/client.js +118 -0
- package/dist/sync/client.js.map +1 -0
- package/dist/sync/mirror.d.ts +85 -0
- package/dist/sync/mirror.js +389 -0
- package/dist/sync/mirror.js.map +1 -0
- package/dist/sync/protocol.d.ts +67 -0
- package/dist/sync/protocol.js +16 -0
- package/dist/sync/protocol.js.map +1 -0
- package/dist/sync/settings.d.ts +56 -0
- package/dist/sync/settings.js +60 -0
- package/dist/sync/settings.js.map +1 -0
- package/dist/tools/builtin/artifact.d.ts +16 -0
- package/dist/tools/builtin/artifact.js +134 -0
- package/dist/tools/builtin/artifact.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +3 -0
- package/dist/tools/builtin/bash.js +142 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/fs.d.ts +19 -0
- package/dist/tools/builtin/fs.js +280 -0
- package/dist/tools/builtin/fs.js.map +1 -0
- package/dist/tools/builtin/index.d.ts +22 -0
- package/dist/tools/builtin/index.js +42 -0
- package/dist/tools/builtin/index.js.map +1 -0
- package/dist/tools/builtin/mcp-resource.d.ts +3 -0
- package/dist/tools/builtin/mcp-resource.js +220 -0
- package/dist/tools/builtin/mcp-resource.js.map +1 -0
- package/dist/tools/builtin/memory.d.ts +10 -0
- package/dist/tools/builtin/memory.js +76 -0
- package/dist/tools/builtin/memory.js.map +1 -0
- package/dist/tools/builtin/recall.d.ts +13 -0
- package/dist/tools/builtin/recall.js +163 -0
- package/dist/tools/builtin/recall.js.map +1 -0
- package/dist/tools/builtin/search.d.ts +3 -0
- package/dist/tools/builtin/search.js +240 -0
- package/dist/tools/builtin/search.js.map +1 -0
- package/dist/tools/builtin/skill.d.ts +10 -0
- package/dist/tools/builtin/skill.js +46 -0
- package/dist/tools/builtin/skill.js.map +1 -0
- package/dist/tools/builtin/task.d.ts +9 -0
- package/dist/tools/builtin/task.js +68 -0
- package/dist/tools/builtin/task.js.map +1 -0
- package/dist/tools/builtin/todo.d.ts +20 -0
- package/dist/tools/builtin/todo.js +63 -0
- package/dist/tools/builtin/todo.js.map +1 -0
- package/dist/tools/define.d.ts +19 -0
- package/dist/tools/define.js +39 -0
- package/dist/tools/define.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +39 -0
- package/dist/tools/registry.js +101 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +112 -0
- package/dist/tools/types.js +22 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/util/id.d.ts +9 -0
- package/dist/util/id.js +19 -0
- package/dist/util/id.js.map +1 -0
- package/dist/util/json.d.ts +25 -0
- package/dist/util/json.js +100 -0
- package/dist/util/json.js.map +1 -0
- package/dist/util/jwt.d.ts +74 -0
- package/dist/util/jwt.js +177 -0
- package/dist/util/jwt.js.map +1 -0
- package/dist/util/logger.d.ts +13 -0
- package/dist/util/logger.js +59 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/queue.d.ts +26 -0
- package/dist/util/queue.js +106 -0
- package/dist/util/queue.js.map +1 -0
- package/dist/util/schema.d.ts +17 -0
- package/dist/util/schema.js +98 -0
- package/dist/util/schema.js.map +1 -0
- package/dist/util/shell.d.ts +20 -0
- package/dist/util/shell.js +24 -0
- package/dist/util/shell.js.map +1 -0
- package/official.json +4 -0
- package/package.json +38 -0
- package/skills/artifact-authoring/SKILL.md +81 -0
- package/skills/canvas/SKILL.md +85 -0
- package/skills/skill-authoring/SKILL.md +85 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { refreshCredential, startLoopbackFlow } from "../auth/oauth.js";
|
|
2
|
+
import { parseJson } from "../util/json.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth for remote MCP servers.
|
|
5
|
+
*
|
|
6
|
+
* The point of doing this rather than shipping a list of pre-registered
|
|
7
|
+
* integrations is dynamic client registration. With it, "which servers are
|
|
8
|
+
* supported" stops being a list somebody maintains: the server says where its
|
|
9
|
+
* authorization lives, we register against it on the spot, and any server that
|
|
10
|
+
* follows the spec works without anybody adding a row anywhere.
|
|
11
|
+
*
|
|
12
|
+
* The chain, all of it discovery, none of it configured:
|
|
13
|
+
*
|
|
14
|
+
* 401 + WWW-Authenticate the server says it wants a token, and where to
|
|
15
|
+
* ask about it (RFC 9728)
|
|
16
|
+
* protected-resource -> which authorization server, and for what
|
|
17
|
+
* resource identifier
|
|
18
|
+
* authorization-server -> authorize / token / register endpoints
|
|
19
|
+
* (RFC 8414, or OpenID discovery)
|
|
20
|
+
* register -> a client id, issued to us, now (RFC 7591)
|
|
21
|
+
* authorize + PKCE -> the loopback flow the providers already use
|
|
22
|
+
*
|
|
23
|
+
* Every step has a fallback, because plenty of deployed servers implement only
|
|
24
|
+
* part of this. A server with no metadata at all is assumed to be its own
|
|
25
|
+
* authorization server at the conventional endpoints, which is what the first
|
|
26
|
+
* generation of MCP servers did and what several still do.
|
|
27
|
+
*/
|
|
28
|
+
const CLIENT_NAME = "OHarness";
|
|
29
|
+
/** Where a server's credential lives in the shared store. */
|
|
30
|
+
export function mcpAuthKey(serverUrl) {
|
|
31
|
+
return `mcp:${canonicalResource(serverUrl)}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Everything needed to start a login, worked out from the server's URL and
|
|
35
|
+
* whatever it said in its 401.
|
|
36
|
+
*/
|
|
37
|
+
export async function discoverMcpAuth(serverUrl, challenge) {
|
|
38
|
+
const resource = canonicalResource(serverUrl);
|
|
39
|
+
const prm = await firstJson(protectedResourceUrls(serverUrl, challenge));
|
|
40
|
+
// No protected-resource document is not an error: it means the server is
|
|
41
|
+
// older than that part of the spec, and is very likely its own issuer.
|
|
42
|
+
const issuer = prm?.authorization_servers?.[0] ?? new URL(serverUrl).origin;
|
|
43
|
+
const metadata = await firstJson(metadataUrls(issuer));
|
|
44
|
+
const scopes = prm?.scopes_supported ?? metadata?.scopes_supported;
|
|
45
|
+
return {
|
|
46
|
+
authorizeUrl: metadata?.authorization_endpoint ?? join(issuer, "authorize"),
|
|
47
|
+
tokenUrl: metadata?.token_endpoint ?? join(issuer, "token"),
|
|
48
|
+
// Guessed only when there is no metadata at all. A document that exists
|
|
49
|
+
// and omits `registration_endpoint` is saying it does not register clients
|
|
50
|
+
// dynamically, and probing anyway turns a clear "you need a client id"
|
|
51
|
+
// into a 404 from somewhere the user has no reason to look.
|
|
52
|
+
...(metadata?.registration_endpoint
|
|
53
|
+
? { registrationUrl: metadata.registration_endpoint }
|
|
54
|
+
: metadata
|
|
55
|
+
? {}
|
|
56
|
+
: { registrationUrl: join(issuer, "register") }),
|
|
57
|
+
resource: prm?.resource ?? resource,
|
|
58
|
+
...(scopes?.length ? { scope: scopes.join(" ") } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The `resource_metadata` URL a 401 pointed at, if it pointed at one.
|
|
63
|
+
*
|
|
64
|
+
* Exported because it is the one piece of the challenge worth reading, and a
|
|
65
|
+
* caller that has the header should not have to re-derive the parse.
|
|
66
|
+
*/
|
|
67
|
+
export function resourceMetadataUrl(challenge) {
|
|
68
|
+
if (!challenge)
|
|
69
|
+
return undefined;
|
|
70
|
+
const match = /resource_metadata\s*=\s*"([^"]+)"/i.exec(challenge);
|
|
71
|
+
return match?.[1];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Where to look for the protected-resource document.
|
|
75
|
+
*
|
|
76
|
+
* The path-suffixed form comes first: one host can serve several MCP servers
|
|
77
|
+
* on different paths, and the bare form would collapse them into one answer.
|
|
78
|
+
*/
|
|
79
|
+
function protectedResourceUrls(serverUrl, challenge) {
|
|
80
|
+
const advertised = resourceMetadataUrl(challenge);
|
|
81
|
+
if (advertised)
|
|
82
|
+
return [advertised];
|
|
83
|
+
return wellKnown(serverUrl, "oauth-protected-resource");
|
|
84
|
+
}
|
|
85
|
+
function metadataUrls(issuer) {
|
|
86
|
+
return [
|
|
87
|
+
...wellKnown(issuer, "oauth-authorization-server"),
|
|
88
|
+
...wellKnown(issuer, "openid-configuration"),
|
|
89
|
+
// OpenID's own convention appends rather than inserts, and issuers with a
|
|
90
|
+
// path component often only answer here.
|
|
91
|
+
join(issuer, ".well-known/openid-configuration"),
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
/** Both spellings of a well-known URL: path inserted, and bare. */
|
|
95
|
+
function wellKnown(base, suffix) {
|
|
96
|
+
const url = new URL(base);
|
|
97
|
+
const path = url.pathname.replace(/^\/|\/$/g, "");
|
|
98
|
+
const bare = `${url.origin}/.well-known/${suffix}`;
|
|
99
|
+
return path ? [`${bare}/${path}`, bare] : [bare];
|
|
100
|
+
}
|
|
101
|
+
function join(base, path) {
|
|
102
|
+
return `${base.replace(/\/$/, "")}/${path}`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The resource identifier for a server URL.
|
|
106
|
+
*
|
|
107
|
+
* Query and fragment come off and the trailing slash goes, because this value
|
|
108
|
+
* has to match what the authorization server computes for the same server, and
|
|
109
|
+
* a token bound to a nearly-right audience is rejected in a way that is very
|
|
110
|
+
* hard to read.
|
|
111
|
+
*/
|
|
112
|
+
export function canonicalResource(serverUrl) {
|
|
113
|
+
const url = new URL(serverUrl);
|
|
114
|
+
url.search = "";
|
|
115
|
+
url.hash = "";
|
|
116
|
+
return url.toString().replace(/\/$/, "");
|
|
117
|
+
}
|
|
118
|
+
async function firstJson(urls) {
|
|
119
|
+
for (const url of urls) {
|
|
120
|
+
try {
|
|
121
|
+
const response = await fetch(url, {
|
|
122
|
+
headers: { accept: "application/json" },
|
|
123
|
+
signal: AbortSignal.timeout(10_000),
|
|
124
|
+
});
|
|
125
|
+
if (!response.ok)
|
|
126
|
+
continue;
|
|
127
|
+
const parsed = parseJson(await response.text());
|
|
128
|
+
if (parsed.ok)
|
|
129
|
+
return parsed.value;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// A candidate that does not resolve is the normal case — the whole
|
|
133
|
+
// point of trying several is that most of them will not be there.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
export async function registerMcpClient(registrationUrl, redirectUri, scope) {
|
|
139
|
+
const response = await fetch(registrationUrl, {
|
|
140
|
+
method: "POST",
|
|
141
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
142
|
+
body: JSON.stringify({
|
|
143
|
+
client_name: CLIENT_NAME,
|
|
144
|
+
redirect_uris: [redirectUri],
|
|
145
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
146
|
+
response_types: ["code"],
|
|
147
|
+
// A public client on a loopback redirect: there is nowhere on a user's
|
|
148
|
+
// machine to keep a client secret that the user cannot read anyway.
|
|
149
|
+
token_endpoint_auth_method: "none",
|
|
150
|
+
...(scope ? { scope } : {}),
|
|
151
|
+
}),
|
|
152
|
+
signal: AbortSignal.timeout(30_000),
|
|
153
|
+
});
|
|
154
|
+
const text = await response.text();
|
|
155
|
+
const parsed = parseJson(text);
|
|
156
|
+
if (!response.ok || !parsed.ok || !parsed.value.client_id) {
|
|
157
|
+
const detail = parsed.ok ? (parsed.value.error ?? text) : text;
|
|
158
|
+
throw new Error(`dynamic client registration failed (${response.status}): ${String(detail).slice(0, 200)}`);
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
clientId: parsed.value.client_id,
|
|
162
|
+
...(parsed.value.client_secret ? { clientSecret: parsed.value.client_secret } : {}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Begin a login. The returned `callback()` resolves when the browser comes
|
|
167
|
+
* back, and the credential is stored before it does.
|
|
168
|
+
*/
|
|
169
|
+
export async function beginMcpLogin(opts) {
|
|
170
|
+
const key = mcpAuthKey(opts.serverUrl);
|
|
171
|
+
const discovered = await discoverMcpAuth(opts.serverUrl, opts.challenge);
|
|
172
|
+
// A client id already issued to this server is reused rather than
|
|
173
|
+
// re-registered: registrations are not free, some servers rate-limit them,
|
|
174
|
+
// and re-authorizing should not mint a new identity every time.
|
|
175
|
+
const previous = await opts.store.get(key);
|
|
176
|
+
const remembered = previous?.type === "oauth" ? previous.metadata : undefined;
|
|
177
|
+
let client = opts.clientId
|
|
178
|
+
? { clientId: opts.clientId, ...(opts.clientSecret ? { clientSecret: opts.clientSecret } : {}) }
|
|
179
|
+
: remembered?.["clientId"]
|
|
180
|
+
? {
|
|
181
|
+
clientId: remembered["clientId"],
|
|
182
|
+
...(remembered["clientSecret"] ? { clientSecret: remembered["clientSecret"] } : {}),
|
|
183
|
+
}
|
|
184
|
+
: undefined;
|
|
185
|
+
const result = await startLoopbackFlow({
|
|
186
|
+
authorizeUrl: discovered.authorizeUrl,
|
|
187
|
+
tokenUrl: discovered.tokenUrl,
|
|
188
|
+
clientId: client?.clientId ?? "",
|
|
189
|
+
...(client?.clientSecret ? { clientSecret: client.clientSecret } : {}),
|
|
190
|
+
...(discovered.scope ? { scope: discovered.scope } : {}),
|
|
191
|
+
// RFC 8707. Sent in both requests: the authorization server uses it to
|
|
192
|
+
// pick an audience, and the token endpoint uses it to bind one.
|
|
193
|
+
extraParams: { resource: discovered.resource },
|
|
194
|
+
tokenParams: { resource: discovered.resource },
|
|
195
|
+
async prepare(redirectUri) {
|
|
196
|
+
if (!client) {
|
|
197
|
+
if (!discovered.registrationUrl) {
|
|
198
|
+
throw new Error("this server does not offer dynamic client registration — " +
|
|
199
|
+
"register a client with it and pass the client id");
|
|
200
|
+
}
|
|
201
|
+
client = await registerMcpClient(discovered.registrationUrl, redirectUri, discovered.scope);
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
clientId: client.clientId,
|
|
205
|
+
...(client.clientSecret ? { clientSecret: client.clientSecret } : {}),
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
if (result.method !== "auto")
|
|
210
|
+
throw new Error("expected a loopback flow");
|
|
211
|
+
const finish = result.callback;
|
|
212
|
+
return {
|
|
213
|
+
...result,
|
|
214
|
+
method: "auto",
|
|
215
|
+
async callback() {
|
|
216
|
+
const outcome = await finish();
|
|
217
|
+
if (outcome.type !== "success" || !("access" in outcome))
|
|
218
|
+
return outcome;
|
|
219
|
+
// What is stored is not just the tokens: without the endpoints and the
|
|
220
|
+
// client id beside them, a refresh an hour from now would have to redo
|
|
221
|
+
// the whole discovery chain to find out where to send it.
|
|
222
|
+
const credential = {
|
|
223
|
+
type: "oauth",
|
|
224
|
+
access: outcome.access,
|
|
225
|
+
refresh: outcome.refresh,
|
|
226
|
+
expires: outcome.expires,
|
|
227
|
+
baseUrl: canonicalResource(opts.serverUrl),
|
|
228
|
+
metadata: {
|
|
229
|
+
tokenUrl: discovered.tokenUrl,
|
|
230
|
+
resource: discovered.resource,
|
|
231
|
+
clientId: client.clientId,
|
|
232
|
+
...(client.clientSecret ? { clientSecret: client.clientSecret } : {}),
|
|
233
|
+
...(discovered.scope ? { scope: discovered.scope } : {}),
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
await opts.store.set(key, credential);
|
|
237
|
+
return outcome;
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/** Forget a server's credential; the next connection will ask again. */
|
|
242
|
+
export async function forgetMcpAuth(serverUrl, store) {
|
|
243
|
+
return store.remove(mcpAuthKey(serverUrl));
|
|
244
|
+
}
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Per-request headers
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
/** In-flight refreshes, so several tool calls at once share one. */
|
|
249
|
+
const refreshing = new Map();
|
|
250
|
+
/**
|
|
251
|
+
* The Authorization header for a server, or nothing if it has no credential.
|
|
252
|
+
*
|
|
253
|
+
* Resolved per request rather than snapshotted at connect: an access token
|
|
254
|
+
* outlives a long session by about an hour, and a header captured when the
|
|
255
|
+
* transport was built is a session that dies quietly in the middle.
|
|
256
|
+
*/
|
|
257
|
+
export async function mcpAuthHeaders(serverUrl, store) {
|
|
258
|
+
const key = mcpAuthKey(serverUrl);
|
|
259
|
+
const credential = await store.get(key);
|
|
260
|
+
if (!credential || credential.type !== "oauth")
|
|
261
|
+
return {};
|
|
262
|
+
const fresh = (await ensureFresh(key, credential, store)) ?? credential;
|
|
263
|
+
return { authorization: `Bearer ${fresh.access}` };
|
|
264
|
+
}
|
|
265
|
+
async function ensureFresh(key, credential, store) {
|
|
266
|
+
// A minute of margin: a token that expires while the request is in flight
|
|
267
|
+
// fails the same way as one that expired an hour ago.
|
|
268
|
+
const stale = credential.expires > 0 && Date.now() > credential.expires - 60_000;
|
|
269
|
+
if (!stale || !credential.refresh)
|
|
270
|
+
return credential;
|
|
271
|
+
const existing = refreshing.get(key);
|
|
272
|
+
if (existing)
|
|
273
|
+
return existing;
|
|
274
|
+
const metadata = credential.metadata ?? {};
|
|
275
|
+
const tokenUrl = metadata["tokenUrl"];
|
|
276
|
+
const clientId = metadata["clientId"];
|
|
277
|
+
if (!tokenUrl || !clientId)
|
|
278
|
+
return credential;
|
|
279
|
+
const attempt = refreshCredential(credential, {
|
|
280
|
+
tokenUrl,
|
|
281
|
+
clientId,
|
|
282
|
+
...(metadata["clientSecret"] ? { clientSecret: metadata["clientSecret"] } : {}),
|
|
283
|
+
...(metadata["scope"] ? { scope: metadata["scope"] } : {}),
|
|
284
|
+
...(metadata["resource"] ? { tokenParams: { resource: metadata["resource"] } } : {}),
|
|
285
|
+
})
|
|
286
|
+
.then(async (refreshed) => {
|
|
287
|
+
if (!refreshed)
|
|
288
|
+
return undefined;
|
|
289
|
+
// The metadata is not in the token response; carry it forward or the
|
|
290
|
+
// next refresh has nowhere to go.
|
|
291
|
+
const merged = { ...refreshed, metadata };
|
|
292
|
+
await store.set(key, merged);
|
|
293
|
+
return merged;
|
|
294
|
+
})
|
|
295
|
+
// A failed refresh is not fatal here. The stale token goes out, the server
|
|
296
|
+
// answers 401, and that path already knows how to ask for a new login.
|
|
297
|
+
.catch(() => undefined)
|
|
298
|
+
.finally(() => refreshing.delete(key));
|
|
299
|
+
refreshing.set(key, attempt);
|
|
300
|
+
return attempt;
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/mcp/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGxE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,GAAG,UAAU,CAAC;AAE/B,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,OAAO,OAAO,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;AAC/C,CAAC;AA6BD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,SAAkB;IAElB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,MAAM,SAAS,CAAoB,qBAAqB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IAE5F,yEAAyE;IACzE,uEAAuE;IACvE,MAAM,MAAM,GAAG,GAAG,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAiB,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAEvE,MAAM,MAAM,GAAG,GAAG,EAAE,gBAAgB,IAAI,QAAQ,EAAE,gBAAgB,CAAC;IACnE,OAAO;QACL,YAAY,EAAE,QAAQ,EAAE,sBAAsB,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;QAC3E,QAAQ,EAAE,QAAQ,EAAE,cAAc,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;QAC3D,wEAAwE;QACxE,2EAA2E;QAC3E,uEAAuE;QACvE,4DAA4D;QAC5D,GAAG,CAAC,QAAQ,EAAE,qBAAqB;YACjC,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,qBAAqB,EAAE;YACrD,CAAC,CAAC,QAAQ;gBACR,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;QACpD,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,QAAQ;QACnC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAA6B;IAC/D,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,oCAAoC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACnE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,SAAiB,EAAE,SAAkB;IAClE,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAClD,IAAI,UAAU;QAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IACpC,OAAO,SAAS,CAAC,SAAS,EAAE,0BAA0B,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO;QACL,GAAG,SAAS,CAAC,MAAM,EAAE,4BAA4B,CAAC;QAClD,GAAG,SAAS,CAAC,MAAM,EAAE,sBAAsB,CAAC;QAC5C,0EAA0E;QAC1E,yCAAyC;QACzC,IAAI,CAAC,MAAM,EAAE,kCAAkC,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,SAAS,SAAS,CAAC,IAAY,EAAE,MAAc;IAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,gBAAgB,MAAM,EAAE,CAAC;IACnD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,IAAY;IACtC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC/B,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;IAChB,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACd,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,SAAS,CAAI,IAAc;IACxC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;gBACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;aACpC,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,SAAS;YAC3B,MAAM,MAAM,GAAG,SAAS,CAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YACnD,IAAI,MAAM,CAAC,EAAE;gBAAE,OAAO,MAAM,CAAC,KAAK,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,mEAAmE;YACnE,kEAAkE;QACpE,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAWD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,eAAuB,EACvB,WAAmB,EACnB,KAAc;IAEd,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE;QAC5C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE;QAC3E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,WAAW,EAAE,WAAW;YACxB,aAAa,EAAE,CAAC,WAAW,CAAC;YAC5B,WAAW,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACpD,cAAc,EAAE,CAAC,MAAM,CAAC;YACxB,uEAAuE;YACvE,oEAAoE;YACpE,0BAA0B,EAAE,MAAM;YAClC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC5B,CAAC;QACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,SAAS,CAAiE,IAAI,CAAC,CAAC;IAC/F,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,MAAM,IAAI,KAAK,CACb,uCAAuC,QAAQ,CAAC,MAAM,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC3F,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS;QAChC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpF,CAAC;AACJ,CAAC;AAgBD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAqB;IACvD,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAEzE,kEAAkE;IAClE,2EAA2E;IAC3E,gEAAgE;IAChE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,QAAQ,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9E,IAAI,MAAM,GAAiC,IAAI,CAAC,QAAQ;QACtD,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;QAChG,CAAC,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC;YACxB,CAAC,CAAC;gBACE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC;gBAChC,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpF;YACH,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;QACrC,YAAY,EAAE,UAAU,CAAC,YAAY;QACrC,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,EAAE;QAChC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,uEAAuE;QACvE,gEAAgE;QAChE,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE;QAC9C,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE;QAC9C,KAAK,CAAC,OAAO,CAAC,WAAW;YACvB,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC;oBAChC,MAAM,IAAI,KAAK,CACb,2DAA2D;wBACzD,kDAAkD,CACrD,CAAC;gBACJ,CAAC;gBACD,MAAM,GAAG,MAAM,iBAAiB,CAC9B,UAAU,CAAC,eAAe,EAC1B,WAAW,EACX,UAAU,CAAC,KAAK,CACjB,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtE,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;IAE/B,OAAO;QACL,GAAG,MAAM;QACT,MAAM,EAAE,MAAM;QACd,KAAK,CAAC,QAAQ;YACZ,MAAM,OAAO,GAAG,MAAM,MAAM,EAAE,CAAC;YAC/B,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;gBAAE,OAAO,OAAO,CAAC;YAEzE,uEAAuE;YACvE,uEAAuE;YACvE,0DAA0D;YAC1D,MAAM,UAAU,GAAoB;gBAClC,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,OAAO,EAAE,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC1C,QAAQ,EAAE;oBACR,QAAQ,EAAE,UAAU,CAAC,QAAQ;oBAC7B,QAAQ,EAAE,UAAU,CAAC,QAAQ;oBAC7B,QAAQ,EAAE,MAAO,CAAC,QAAQ;oBAC1B,GAAG,CAAC,MAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACvE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACzD;aACF,CAAC;YACF,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YACtC,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,KAAgB;IACrE,OAAO,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,oEAAoE;AACpE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgD,CAAC;AAE3E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAiB,EACjB,KAAgB;IAEhB,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,UAAU,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IAE1D,MAAM,KAAK,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC;IACxE,OAAO,EAAE,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;AACrD,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,GAAW,EACX,UAA2B,EAC3B,KAAgB;IAEhB,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,GAAG,MAAM,CAAC;IACjF,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,OAAO;QAAE,OAAO,UAAU,CAAC;IAErD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;QAAE,OAAO,UAAU,CAAC;IAE9C,MAAM,OAAO,GAAG,iBAAiB,CAAC,UAAU,EAAE;QAC5C,QAAQ;QACR,QAAQ;QACR,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrF,CAAC;SACC,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;QACxB,IAAI,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QACjC,qEAAqE;QACrE,kCAAkC;QAClC,MAAM,MAAM,GAAoB,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,CAAC;QAC3D,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;QACF,2EAA2E;QAC3E,uEAAuE;SACtE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACtB,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Model Context Protocol types and JSON-RPC framing.
|
|
3
|
+
*
|
|
4
|
+
* Implemented directly rather than via an SDK so the harness has no runtime
|
|
5
|
+
* dependency for MCP and we control reconnect/timeout behaviour.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
8
|
+
export interface JsonRpcRequest {
|
|
9
|
+
jsonrpc: "2.0";
|
|
10
|
+
id: number | string;
|
|
11
|
+
method: string;
|
|
12
|
+
params?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface JsonRpcNotification {
|
|
15
|
+
jsonrpc: "2.0";
|
|
16
|
+
method: string;
|
|
17
|
+
params?: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface JsonRpcResponse {
|
|
20
|
+
jsonrpc: "2.0";
|
|
21
|
+
id: number | string;
|
|
22
|
+
result?: unknown;
|
|
23
|
+
error?: JsonRpcError;
|
|
24
|
+
}
|
|
25
|
+
export interface JsonRpcError {
|
|
26
|
+
code: number;
|
|
27
|
+
message: string;
|
|
28
|
+
data?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export type JsonRpcMessage = JsonRpcRequest | JsonRpcNotification | JsonRpcResponse;
|
|
31
|
+
export declare function isResponse(msg: JsonRpcMessage): msg is JsonRpcResponse;
|
|
32
|
+
export declare function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest;
|
|
33
|
+
export interface McpServerInfo {
|
|
34
|
+
name: string;
|
|
35
|
+
version: string;
|
|
36
|
+
}
|
|
37
|
+
export interface McpCapabilities {
|
|
38
|
+
tools?: {
|
|
39
|
+
listChanged?: boolean;
|
|
40
|
+
};
|
|
41
|
+
resources?: {
|
|
42
|
+
subscribe?: boolean;
|
|
43
|
+
listChanged?: boolean;
|
|
44
|
+
};
|
|
45
|
+
prompts?: {
|
|
46
|
+
listChanged?: boolean;
|
|
47
|
+
};
|
|
48
|
+
logging?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
export interface McpInitializeResult {
|
|
51
|
+
protocolVersion: string;
|
|
52
|
+
capabilities: McpCapabilities;
|
|
53
|
+
serverInfo: McpServerInfo;
|
|
54
|
+
instructions?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface McpToolDefinition {
|
|
57
|
+
name: string;
|
|
58
|
+
title?: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
inputSchema: Record<string, unknown>;
|
|
61
|
+
annotations?: {
|
|
62
|
+
readOnlyHint?: boolean;
|
|
63
|
+
destructiveHint?: boolean;
|
|
64
|
+
idempotentHint?: boolean;
|
|
65
|
+
openWorldHint?: boolean;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export type McpContent = {
|
|
69
|
+
type: "text";
|
|
70
|
+
text: string;
|
|
71
|
+
} | {
|
|
72
|
+
type: "image";
|
|
73
|
+
data: string;
|
|
74
|
+
mimeType: string;
|
|
75
|
+
} | {
|
|
76
|
+
type: "audio";
|
|
77
|
+
data: string;
|
|
78
|
+
mimeType: string;
|
|
79
|
+
} | {
|
|
80
|
+
type: "resource";
|
|
81
|
+
resource: {
|
|
82
|
+
uri: string;
|
|
83
|
+
text?: string;
|
|
84
|
+
blob?: string;
|
|
85
|
+
mimeType?: string;
|
|
86
|
+
};
|
|
87
|
+
} | {
|
|
88
|
+
type: "resource_link";
|
|
89
|
+
uri: string;
|
|
90
|
+
name?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
};
|
|
93
|
+
export interface McpToolCallResult {
|
|
94
|
+
content: McpContent[];
|
|
95
|
+
isError?: boolean;
|
|
96
|
+
structuredContent?: unknown;
|
|
97
|
+
}
|
|
98
|
+
export interface McpResource {
|
|
99
|
+
uri: string;
|
|
100
|
+
name?: string;
|
|
101
|
+
title?: string;
|
|
102
|
+
description?: string;
|
|
103
|
+
mimeType?: string;
|
|
104
|
+
}
|
|
105
|
+
export interface McpResourceContents {
|
|
106
|
+
contents: Array<{
|
|
107
|
+
uri: string;
|
|
108
|
+
mimeType?: string;
|
|
109
|
+
text?: string;
|
|
110
|
+
blob?: string;
|
|
111
|
+
}>;
|
|
112
|
+
}
|
|
113
|
+
/** What `prompts/get` returns: a rendered template as chat messages. */
|
|
114
|
+
export interface McpPromptResult {
|
|
115
|
+
description?: string;
|
|
116
|
+
messages?: Array<{
|
|
117
|
+
role?: string;
|
|
118
|
+
content?: unknown;
|
|
119
|
+
}>;
|
|
120
|
+
}
|
|
121
|
+
export interface McpPrompt {
|
|
122
|
+
name: string;
|
|
123
|
+
title?: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
arguments?: Array<{
|
|
126
|
+
name: string;
|
|
127
|
+
description?: string;
|
|
128
|
+
required?: boolean;
|
|
129
|
+
}>;
|
|
130
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Model Context Protocol types and JSON-RPC framing.
|
|
3
|
+
*
|
|
4
|
+
* Implemented directly rather than via an SDK so the harness has no runtime
|
|
5
|
+
* dependency for MCP and we control reconnect/timeout behaviour.
|
|
6
|
+
*/
|
|
7
|
+
export const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
8
|
+
export function isResponse(msg) {
|
|
9
|
+
return "id" in msg && !("method" in msg);
|
|
10
|
+
}
|
|
11
|
+
export function isRequest(msg) {
|
|
12
|
+
return "id" in msg && "method" in msg;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/mcp/protocol.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AA8BjD,MAAM,UAAU,UAAU,CAAC,GAAmB;IAC5C,OAAO,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAmB;IAC3C,OAAO,IAAI,IAAI,GAAG,IAAI,QAAQ,IAAI,GAAG,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { McpServerConfig } from "./client.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The connectors on offer, as data.
|
|
4
|
+
*
|
|
5
|
+
* A first connector should not require knowing a package name, so a short list
|
|
6
|
+
* of the ones published by the MCP project itself ships with the harness. It
|
|
7
|
+
* layers exactly like the model catalog: the bundled file, then the user's own,
|
|
8
|
+
* so adding a connector to the menu never means editing code.
|
|
9
|
+
*
|
|
10
|
+
* What this is not: an installer. Nothing is downloaded here — an entry is a
|
|
11
|
+
* command line and a description, and starting it is the same `addMcpServer`
|
|
12
|
+
* path a hand-written config takes.
|
|
13
|
+
*/
|
|
14
|
+
export interface ConnectorEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
/** Who publishes the server, shown so an entry is attributable. */
|
|
19
|
+
publisher?: string;
|
|
20
|
+
docsUrl?: string;
|
|
21
|
+
/** The vendor's site, from which the menu resolves an icon. */
|
|
22
|
+
siteUrl?: string;
|
|
23
|
+
/** Still carrying its placeholders; resolve with `connectorConfig`. */
|
|
24
|
+
config: McpServerConfig;
|
|
25
|
+
}
|
|
26
|
+
/** Where a user's own connector list lives; layered over the bundled one. */
|
|
27
|
+
export declare function userRegistryPath(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Every connector on offer, bundled first and the user's own over the top.
|
|
30
|
+
*
|
|
31
|
+
* A registry that fails to parse is skipped rather than thrown: the menu it
|
|
32
|
+
* feeds is an offer, and a broken file in one of two places should not take
|
|
33
|
+
* the composer down with it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function officialConnectors(): Promise<ConnectorEntry[]>;
|
|
36
|
+
/**
|
|
37
|
+
* One entry's configuration, with its *location* placeholders filled in.
|
|
38
|
+
*
|
|
39
|
+
* `${project}` and `${home}` are resolved here because a connector that works
|
|
40
|
+
* on files has to be told which files, and the answer is a path the registry
|
|
41
|
+
* cannot know when it is written.
|
|
42
|
+
*
|
|
43
|
+
* `${env:NAME}` is deliberately **left alone**. What comes back from here is
|
|
44
|
+
* what gets written to the user's config file, and a resolved secret in that
|
|
45
|
+
* file is a secret on disk in plain text. Those are expanded at connect time
|
|
46
|
+
* instead — see `expandSecrets` — so the file keeps the reference and only the
|
|
47
|
+
* running process ever sees the value.
|
|
48
|
+
*/
|
|
49
|
+
export declare function connectorConfig(entry: ConnectorEntry, where: {
|
|
50
|
+
project: string;
|
|
51
|
+
home: string;
|
|
52
|
+
}): McpServerConfig;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { userPath } from "../paths.js";
|
|
5
|
+
import { parseJson } from "../util/json.js";
|
|
6
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
/** Where a user's own connector list lives; layered over the bundled one. */
|
|
8
|
+
export function userRegistryPath() {
|
|
9
|
+
return process.env["OHARNESS_CONNECTORS"] ?? userPath("connectors.json");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Every connector on offer, bundled first and the user's own over the top.
|
|
13
|
+
*
|
|
14
|
+
* A registry that fails to parse is skipped rather than thrown: the menu it
|
|
15
|
+
* feeds is an offer, and a broken file in one of two places should not take
|
|
16
|
+
* the composer down with it.
|
|
17
|
+
*/
|
|
18
|
+
export async function officialConnectors() {
|
|
19
|
+
const byId = new Map();
|
|
20
|
+
for (const source of [path.join(HERE, "registry.json"), userRegistryPath()]) {
|
|
21
|
+
for (const entry of await readRegistry(source))
|
|
22
|
+
byId.set(entry.id, entry);
|
|
23
|
+
}
|
|
24
|
+
return [...byId.values()];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One entry's configuration, with its *location* placeholders filled in.
|
|
28
|
+
*
|
|
29
|
+
* `${project}` and `${home}` are resolved here because a connector that works
|
|
30
|
+
* on files has to be told which files, and the answer is a path the registry
|
|
31
|
+
* cannot know when it is written.
|
|
32
|
+
*
|
|
33
|
+
* `${env:NAME}` is deliberately **left alone**. What comes back from here is
|
|
34
|
+
* what gets written to the user's config file, and a resolved secret in that
|
|
35
|
+
* file is a secret on disk in plain text. Those are expanded at connect time
|
|
36
|
+
* instead — see `expandSecrets` — so the file keeps the reference and only the
|
|
37
|
+
* running process ever sees the value.
|
|
38
|
+
*/
|
|
39
|
+
export function connectorConfig(entry, where) {
|
|
40
|
+
const fill = (value) => value.replace(/\$\{(project|home)\}/g, (_, name) => name === "project" ? where.project : where.home);
|
|
41
|
+
const fillAll = (values) => Object.fromEntries(Object.entries(values).map(([key, value]) => [key, fill(value)]));
|
|
42
|
+
if (entry.config.type === "http") {
|
|
43
|
+
return {
|
|
44
|
+
...entry.config,
|
|
45
|
+
url: fill(entry.config.url),
|
|
46
|
+
// Headers were being passed through unfilled, which made a placeholder
|
|
47
|
+
// in one silently reach the server as literal text.
|
|
48
|
+
...(entry.config.headers ? { headers: fillAll(entry.config.headers) } : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
...entry.config,
|
|
53
|
+
command: fill(entry.config.command),
|
|
54
|
+
...(entry.config.args ? { args: entry.config.args.map(fill) } : {}),
|
|
55
|
+
...(entry.config.cwd ? { cwd: fill(entry.config.cwd) } : {}),
|
|
56
|
+
...(entry.config.env ? { env: fillAll(entry.config.env) } : {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async function readRegistry(file) {
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = await readFile(file, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
const parsed = parseJson(raw);
|
|
68
|
+
if (!parsed.ok)
|
|
69
|
+
return [];
|
|
70
|
+
const connectors = parsed.value.connectors;
|
|
71
|
+
if (!connectors || typeof connectors !== "object")
|
|
72
|
+
return [];
|
|
73
|
+
const out = [];
|
|
74
|
+
for (const [id, value] of Object.entries(connectors)) {
|
|
75
|
+
const entry = value;
|
|
76
|
+
// An entry with no runnable configuration is a typo, and offering it would
|
|
77
|
+
// put a row in the menu that can only ever fail.
|
|
78
|
+
if (!entry || typeof entry !== "object" || !isRunnable(entry.config))
|
|
79
|
+
continue;
|
|
80
|
+
out.push({
|
|
81
|
+
id,
|
|
82
|
+
label: typeof entry.label === "string" ? entry.label : id,
|
|
83
|
+
...(typeof entry.description === "string" ? { description: entry.description } : {}),
|
|
84
|
+
...(typeof entry.publisher === "string" ? { publisher: entry.publisher } : {}),
|
|
85
|
+
...(typeof entry.docsUrl === "string" ? { docsUrl: entry.docsUrl } : {}),
|
|
86
|
+
...(typeof entry.siteUrl === "string" ? { siteUrl: entry.siteUrl } : {}),
|
|
87
|
+
config: entry.config,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
function isRunnable(config) {
|
|
93
|
+
if (!config || typeof config !== "object")
|
|
94
|
+
return false;
|
|
95
|
+
const candidate = config;
|
|
96
|
+
if (candidate.type === "stdio")
|
|
97
|
+
return typeof candidate.command === "string";
|
|
98
|
+
return candidate.type === "http" && typeof candidate.url === "string";
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/mcp/registry.ts"],"names":[],"mappings":"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"}
|