@typeship-ax/mcp 0.21.0 → 0.22.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/AGENTS.md +15 -11
- package/README.md +22 -53
- package/api.json +9998 -10118
- package/api.md +8983 -9120
- package/dist/arguments.d.ts +47 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/arguments.js +254 -0
- package/dist/core/http.d.ts +162 -19
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +381 -48
- package/dist/core/pagination.d.ts +42 -6
- package/dist/core/pagination.d.ts.map +1 -1
- package/dist/core/pagination.js +111 -17
- package/dist/credential-storage.d.ts +10 -3
- package/dist/credential-storage.d.ts.map +1 -1
- package/dist/credential-storage.js +15 -6
- package/dist/dates.d.ts +1 -1
- package/dist/dates.js +1 -1
- package/dist/errors.d.ts +20 -84
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -108
- package/dist/fields.d.ts +29 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +101 -0
- package/dist/index.d.ts +28 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -25
- package/dist/mcp-authorization.d.ts.map +1 -1
- package/dist/mcp-authorization.js +34 -10
- package/dist/mcp-protocol.d.ts +87 -44
- package/dist/mcp-protocol.d.ts.map +1 -1
- package/dist/mcp-protocol.js +552 -478
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +127 -28
- package/dist/named-credentials.d.ts +19 -0
- package/dist/named-credentials.d.ts.map +1 -1
- package/dist/named-credentials.js +81 -1
- package/dist/oauth-request.d.ts +7 -1
- package/dist/oauth-request.d.ts.map +1 -1
- package/dist/oauth-request.js +26 -4
- package/dist/oauth-session.d.ts +13 -1
- package/dist/oauth-session.d.ts.map +1 -1
- package/dist/oauth-session.js +34 -18
- package/dist/ops.d.ts +53 -5
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +49 -40
- package/dist/resources/api-keys.d.ts +10 -7
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +10 -31
- package/dist/resources/deliveries.d.ts +88 -4
- package/dist/resources/deliveries.d.ts.map +1 -1
- package/dist/resources/deliveries.js +95 -18
- package/dist/resources/drafts.d.ts +15 -15
- package/dist/resources/drafts.d.ts.map +1 -1
- package/dist/resources/drafts.js +11 -64
- package/dist/resources/files.d.ts +4 -4
- package/dist/resources/files.d.ts.map +1 -1
- package/dist/resources/files.js +3 -12
- package/dist/resources/generations.d.ts +14 -14
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +21 -45
- package/dist/resources/organization.d.ts +4 -4
- package/dist/resources/organization.d.ts.map +1 -1
- package/dist/resources/organization.js +3 -10
- package/dist/resources/{generate.d.ts → packages.d.ts} +16 -16
- package/dist/resources/packages.d.ts.map +1 -0
- package/dist/resources/{generate.js → packages.js} +13 -29
- package/dist/resources/projects.d.ts +50 -50
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +60 -116
- package/dist/resources/releases.d.ts +21 -16
- package/dist/resources/releases.d.ts.map +1 -1
- package/dist/resources/releases.js +18 -39
- package/dist/resources/spec-revisions.d.ts +15 -6
- package/dist/resources/spec-revisions.d.ts.map +1 -1
- package/dist/resources/spec-revisions.js +6 -28
- package/dist/resources/specs.d.ts +7 -7
- package/dist/resources/specs.d.ts.map +1 -1
- package/dist/resources/specs.js +6 -34
- package/dist/resources/targets.d.ts +48 -48
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +58 -114
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +78 -76
- package/dist/search.d.ts +54 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +421 -0
- package/dist/types.d.ts +499 -339
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -18
- package/dist/worker.js +2 -2
- package/package.json +5 -2
- package/server.json +5 -5
- package/src/arguments.ts +242 -0
- package/src/core/http.ts +457 -58
- package/src/core/pagination.ts +129 -18
- package/src/credential-storage.ts +16 -6
- package/src/dates.ts +1 -1
- package/src/errors.ts +46 -115
- package/src/fields.ts +91 -0
- package/src/index.ts +45 -28
- package/src/mcp-authorization.ts +29 -9
- package/src/mcp-protocol.ts +580 -428
- package/src/mcp.ts +113 -26
- package/src/named-credentials.ts +66 -1
- package/src/oauth-request.ts +32 -6
- package/src/oauth-session.ts +37 -19
- package/src/ops.ts +82 -44
- package/src/resources/api-keys.ts +34 -48
- package/src/resources/deliveries.ts +211 -30
- package/src/resources/drafts.ts +60 -107
- package/src/resources/files.ts +19 -20
- package/src/resources/generations.ts +57 -75
- package/src/resources/organization.ts +11 -16
- package/src/resources/{generate.ts → packages.ts} +43 -51
- package/src/resources/projects.ts +145 -200
- package/src/resources/releases.ts +48 -65
- package/src/resources/spec-revisions.ts +38 -47
- package/src/resources/specs.ts +39 -59
- package/src/resources/targets.ts +143 -193
- package/src/schemas.ts +78 -76
- package/src/search.ts +434 -0
- package/src/types.ts +538 -357
- package/src/worker.ts +2 -2
- package/dist/resources/generate.d.ts.map +0 -1
- package/dist/resources/publications.d.ts +0 -47
- package/dist/resources/publications.d.ts.map +0 -1
- package/dist/resources/publications.js +0 -70
- package/src/resources/publications.ts +0 -140
package/dist/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AAuBA,OAAO,EAAoC,KAAK,aAAa,EAAmB,MAAM,YAAY,CAAC;AAenG,OAAO,EAA8C,KAAK,6BAA6B,EAAE,KAAK,kCAAkC,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACpL,YAAY,EAAE,6BAA6B,EAAE,kCAAkC,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9H,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AAuBA,OAAO,EAAoC,KAAK,aAAa,EAAmB,MAAM,YAAY,CAAC;AAenG,OAAO,EAA8C,KAAK,6BAA6B,EAAE,KAAK,kCAAkC,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACpL,YAAY,EAAE,6BAA6B,EAAE,kCAAkC,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9H,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAma3D,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,EAAE,6BAA6B,CAAC;IAC9C;kFAC8E;IAC9E,aAAa,CAAC,EAAE,kCAAkC,CAAC;IACnD;;;;iFAI6E;IAC7E,cAAc,CAAC,SAAS,EAAE,YAAY,GAAG,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACjF;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAMjG;AAED;+EAC+E;AAC/E,wBAAsB,UAAU,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAErE"}
|
package/dist/mcp.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// Typeship — MCP server. Generated by Typeship — https://typeship.dev
|
|
3
3
|
// Speaks MCP 2026-07-28 and accepts the 2025-11-25 initialize handshake.
|
|
4
4
|
// HTTP is stateless (no sessions); the protocol layer and tool surface
|
|
5
|
-
// live in ./mcp-protocol.js, shared
|
|
6
|
-
//
|
|
5
|
+
// live in ./mcp-protocol.js, shared by both transports. Two transports,
|
|
6
|
+
// zero dependencies:
|
|
7
7
|
// node mcp.js stdio (newline-delimited JSON-RPC 2.0)
|
|
8
8
|
// createMcpHandler() configured Streamable HTTP handler
|
|
9
9
|
// Surface switches (flags or environment):
|
|
@@ -21,20 +21,20 @@ import { homedir, tmpdir } from "node:os";
|
|
|
21
21
|
import { basename, join } from "node:path";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
23
|
import { TypeshipClient, formatDebugEvent } from "./index.js";
|
|
24
|
-
import { asApiResult } from "./core/http.js";
|
|
24
|
+
import { asApiResult, mediaTypeForPath } from "./core/http.js";
|
|
25
25
|
import { GLOBALS, OMITTED_OPS, OPS, buildArgs } from "./ops.js";
|
|
26
|
-
import { DEFAULT_MAX_RESULT_CHARS, SUPPORTED_PROTOCOL_VERSIONS, McpAccountLinkRequired, argumentsError, asJsonRpc, binaryOutcome, callSharedTool, checkRequestHeaders, createStdioRpcHandler, dataOutcome, errorOutcome, handleRpc, isRpcOutcome, pageOutcome, parseIncludeList, prepareCall, resolveReferences, serverInstructions, takeCancelled, textError, toolDefinitions, visibleOps, } from "./mcp-protocol.js";
|
|
26
|
+
import { DEFAULT_MAX_RESULT_CHARS, SUPPORTED_PROTOCOL_VERSIONS, McpAccountLinkRequired, argumentsError, asJsonRpc, binaryOutcome, callSharedTool, checkRequestHeaders, createStdioRpcHandler, dataOutcome, errorOutcome, handleRpc, hiddenOperations, isRpcOutcome, pageOutcome, parseIncludeList, prepareCall, resolveReferences, serverInstructions, requiredScopes, takeCancelled, textError, toolDefinitions, unmatchedIncludes, unsupportedAuthOutcome, visibleOps, } from "./mcp-protocol.js";
|
|
27
27
|
import { fetchDocsText } from "./docs.js";
|
|
28
28
|
import { assertCredentialDestination, assertStoredIdentity } from "./oauth-session.js";
|
|
29
29
|
import { createCredentialStore } from "./credential-storage.js";
|
|
30
|
-
import { parseNamedCredentials, resolveNamedCredentials, namedCredentialAvailability } from "./named-credentials.js";
|
|
30
|
+
import { parseNamedCredentials, resolveNamedCredentials, namedCredentialAvailability, missingCredentials, parseExtraHeaders, applyExtraHeaders } from "./named-credentials.js";
|
|
31
31
|
import { resolveProfile, profileFlag, readProfileConfig } from "./auth-profiles.js";
|
|
32
32
|
import { createMcpAuthorizer, McpAuthorizationError } from "./mcp-authorization.js";
|
|
33
33
|
export { McpAccountLinkRequired } from "./mcp-protocol.js";
|
|
34
34
|
const BIN = "typeship";
|
|
35
35
|
const PKG_NAME = "@typeship-ax/mcp";
|
|
36
36
|
const SERVER_NAME = "typeship-mcp";
|
|
37
|
-
const SERVER_VERSION = "0.
|
|
37
|
+
const SERVER_VERSION = "0.22.0";
|
|
38
38
|
/** The MCP client's announced name (clientInfo in request _meta), for the User-Agent. */
|
|
39
39
|
let MCP_CLIENT_NAME = null;
|
|
40
40
|
function noteClientInfo(message) {
|
|
@@ -49,8 +49,8 @@ const NAMED_SCHEMES = { "apiKey": { "kind": "bearer", "options": ["bearerToken"]
|
|
|
49
49
|
const AUTH_SCALARS = [{ "option": "bearerToken", "flag": "token", "env": "TYPESHIP_TOKEN" }];
|
|
50
50
|
const BASIC = null;
|
|
51
51
|
const ENVIRONMENTS = {};
|
|
52
|
-
const DOCS_URL_DEFAULT = "https://typeship.dev";
|
|
53
|
-
const DOCS_INDEX_URL_DEFAULT =
|
|
52
|
+
const DOCS_URL_DEFAULT = "https://typeship.dev/docs";
|
|
53
|
+
const DOCS_INDEX_URL_DEFAULT = "https://typeship.dev/llms.txt";
|
|
54
54
|
/** "meta" collapses per-operation tools into search/read/execute so huge
|
|
55
55
|
* APIs don't flood agent context with hundreds of tools. */
|
|
56
56
|
const TOOL_MODE = "meta";
|
|
@@ -63,6 +63,10 @@ const IDENTITY_TOOL = null;
|
|
|
63
63
|
const ARGV = process.argv.slice(2);
|
|
64
64
|
const READ_ONLY = ARGV.includes("--read-only") || process.env["TYPESHIP_MCP_READ_ONLY"] === "1" || process.env["TYPESHIP_MCP_READ_ONLY"] === "true";
|
|
65
65
|
const INCLUDE = parseIncludeList(ARGV.includes("--tools") ? ARGV[ARGV.indexOf("--tools") + 1] : process.env["TYPESHIP_MCP_TOOLS"]);
|
|
66
|
+
/** How the operator set each switch, for errors that name it. */
|
|
67
|
+
const READ_ONLY_SWITCH = ARGV.includes("--read-only") ? "--read-only" : "TYPESHIP_MCP_READ_ONLY";
|
|
68
|
+
const TOOLS_SWITCH = ARGV.includes("--tools") ? "--tools" : "TYPESHIP_MCP_TOOLS";
|
|
69
|
+
const HEADER_ARGS = ARGV.flatMap((arg, index) => arg === "--header" && ARGV[index + 1] !== undefined ? [ARGV[index + 1]] : arg.startsWith("--header=") ? [arg.slice("--header=".length)] : []);
|
|
66
70
|
const MAX_RESULT_CHARS = Number(process.env["TYPESHIP_MCP_MAX_RESULT_CHARS"]) || DEFAULT_MAX_RESULT_CHARS;
|
|
67
71
|
/** One sentence on where credentials come from on each transport; goes
|
|
68
72
|
* into the instructions and into 401 results. */
|
|
@@ -87,7 +91,7 @@ function readJson(file) {
|
|
|
87
91
|
}
|
|
88
92
|
function makeClient(op) {
|
|
89
93
|
const profile = LOCAL_CREDENTIALS ? resolveProfile(configRoot(), { flag: profileFlag(ARGV), environment: process.env["TYPESHIP_PROFILE"] }) : null;
|
|
90
|
-
const store = createCredentialStore(profile?.directory ?? configRoot(), process.env["TYPESHIP_CREDENTIAL_STORE"], "TYPESHIP_CREDENTIAL_STORE");
|
|
94
|
+
const store = createCredentialStore(profile?.directory ?? configRoot(), BIN, process.env["TYPESHIP_CREDENTIAL_STORE"], "TYPESHIP_CREDENTIAL_STORE");
|
|
91
95
|
if (BASIC && (process.env[BASIC.envUser] !== undefined) !== (process.env[BASIC.envPass] !== undefined))
|
|
92
96
|
throw new Error("Supply both Basic-auth environment variables together, or use a complete named Basic credential.");
|
|
93
97
|
const envNamed = LOCAL_CREDENTIALS && process.env["TYPESHIP_CREDENTIALS"] !== undefined ? parseNamedCredentials(process.env["TYPESHIP_CREDENTIALS"], NAMED_SCHEMES) : {};
|
|
@@ -136,28 +140,74 @@ function makeClient(op) {
|
|
|
136
140
|
{ named: stored?.named, options: { ...stored?.scalars, ...(stored?.basic ? { basicAuth: stored.basic } : {}) } },
|
|
137
141
|
{ named: envNamed, options: envOptions },
|
|
138
142
|
]);
|
|
143
|
+
requireOperationCredentials(op, options);
|
|
139
144
|
for (const g of GLOBALS) {
|
|
140
145
|
const value = process.env["TYPESHIP_" + g.envSuffix];
|
|
141
146
|
if (value !== undefined)
|
|
142
147
|
options[g.option] = value;
|
|
143
148
|
}
|
|
144
149
|
if (process.env["TYPESHIP_DEBUG"] === "1") {
|
|
145
|
-
options.debug = (event) => process.stderr.write(formatDebugEvent(
|
|
150
|
+
options.debug = (event) => process.stderr.write(formatDebugEvent(SERVER_NAME, event) + "\n");
|
|
146
151
|
}
|
|
147
152
|
// The local MCP server identifies itself (surface + the client it serves, when announced).
|
|
148
|
-
options.defaultHeaders = { "User-Agent":
|
|
153
|
+
options.defaultHeaders = { "User-Agent": SERVER_NAME + "/" + SERVER_VERSION + (MCP_CLIENT_NAME ? " (client=" + MCP_CLIENT_NAME + ")" : "") };
|
|
154
|
+
// --header / TYPESHIP_HEADERS: headers the spec does not declare, applied last.
|
|
155
|
+
const extraHeaders = LOCAL_CREDENTIALS ? parseExtraHeaders(process.env["TYPESHIP_HEADERS"], HEADER_ARGS, "TYPESHIP_HEADERS") : {};
|
|
156
|
+
if (Object.keys(extraHeaders).length)
|
|
157
|
+
options.onRequest = (context) => { applyExtraHeaders(context.headers, extraHeaders); };
|
|
149
158
|
// Keep the SDK's machine-token cache while re-evaluating local configuration.
|
|
150
159
|
const key = createHash("sha256").update(JSON.stringify([options, config, profile?.name, stored?.oauth?.sessionId, null, process.env["TYPESHIP_DEBUG"]])).digest("hex");
|
|
151
160
|
if (clientInstance && clientKey === key)
|
|
152
161
|
return clientInstance;
|
|
153
162
|
const client = new TypeshipClient(options);
|
|
163
|
+
CLIENT_BASIC.set(client, { basicAuth: options.basicAuth, credentials: options.credentials });
|
|
154
164
|
clientKey = key;
|
|
155
165
|
CLIENT_CREDENTIALS.set(client, Object.keys(options.credentials ?? {}).length > 0 || AUTH_SCALARS.some((a) => options[a.option] !== undefined) || options.basicAuth !== undefined || options.bearerToken !== undefined || options.clientCredentials !== undefined);
|
|
156
166
|
return (clientInstance = client);
|
|
157
167
|
}
|
|
168
|
+
/** A required operation without one complete credential alternative stops
|
|
169
|
+
* before any request, as the CLI does, with the variables that supply it.
|
|
170
|
+
* Operations with no supported alternative are reported separately. */
|
|
171
|
+
class MissingCredentialsError extends Error {
|
|
172
|
+
outcome;
|
|
173
|
+
constructor(outcome) {
|
|
174
|
+
super("Missing credentials");
|
|
175
|
+
this.outcome = outcome;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function requireOperationCredentials(op, options) {
|
|
179
|
+
if (op.auth !== "required")
|
|
180
|
+
return;
|
|
181
|
+
const gap = missingCredentials(NAMED_SCHEMES, op.credentialOptions, options);
|
|
182
|
+
if (!gap || !gap.alternatives.length)
|
|
183
|
+
return;
|
|
184
|
+
const names = new Set(gap.missing.flat());
|
|
185
|
+
const needs = (option) => [...names].some((name) => NAMED_SCHEMES[name]?.options.includes(option));
|
|
186
|
+
const scalars = AUTH_SCALARS.filter((scalar) => needs(scalar.option));
|
|
187
|
+
throw new MissingCredentialsError(textError(op.tool + " needs one complete credential alternative. Missing security schemes: " + gap.missing.map((alternative) => alternative.join(" + ")).join(" OR ") + ". No request was sent.", "NO_AUTH", [
|
|
188
|
+
...scalars.map((scalar) => "Set " + scalar.env + " in the MCP server's environment."),
|
|
189
|
+
...(BASIC && needs("basicAuth") ? ["Set " + BASIC.envUser + " and " + BASIC.envPass + " in the MCP server's environment."] : []),
|
|
190
|
+
"Or set TYPESHIP_CREDENTIALS to a JSON object with every scheme in one alternative: " + gap.alternatives.map((alternative) => alternative.join(" + ")).join(" OR ") + ".",
|
|
191
|
+
"Credentials saved by '" + BIN + " login' are read on the next call without a restart.",
|
|
192
|
+
]));
|
|
193
|
+
}
|
|
158
194
|
let clientInstance;
|
|
159
195
|
let clientKey = "";
|
|
160
196
|
const CLIENT_CREDENTIALS = new WeakMap();
|
|
197
|
+
/** Each client's Basic credentials, for path arguments that default to the username. */
|
|
198
|
+
const CLIENT_BASIC = new WeakMap();
|
|
199
|
+
/** The Basic-auth username a path argument of op defaults to (Twilio's
|
|
200
|
+
* AccountSid): the named scheme's, else basicAuth's. */
|
|
201
|
+
function credentialUsername(op, client) {
|
|
202
|
+
const scheme = op.params.find((p) => p.credential)?.credential?.scheme;
|
|
203
|
+
const basic = scheme ? CLIENT_BASIC.get(client) : undefined;
|
|
204
|
+
if (!basic)
|
|
205
|
+
return undefined;
|
|
206
|
+
const named = basic.credentials?.[scheme];
|
|
207
|
+
const username = named && typeof named === "object" ? named.username
|
|
208
|
+
: basic.basicAuth?.username;
|
|
209
|
+
return typeof username === "string" && username !== "" ? username : undefined;
|
|
210
|
+
}
|
|
161
211
|
/** Resolve configuration and credentials for each tool call, including login/logout changes. */
|
|
162
212
|
function getClient(op) {
|
|
163
213
|
return makeClient(op);
|
|
@@ -168,10 +218,10 @@ async function callOperationRaw(op, rawArgs, remote, client) {
|
|
|
168
218
|
// Arguments are checked against the tool's schema first: unknown names,
|
|
169
219
|
// wrong types and missing requirements come back as one isError result,
|
|
170
220
|
// nothing reaches the API half-formed and nothing is dropped silently.
|
|
171
|
-
const prepared = prepareCall(op, rawArgs, { maxChars: MAX_RESULT_CHARS });
|
|
221
|
+
const prepared = prepareCall(op, rawArgs, { maxChars: MAX_RESULT_CHARS, credentialUsername: credentialUsername(op, client) });
|
|
172
222
|
if (!prepared.ok)
|
|
173
223
|
return prepared.outcome;
|
|
174
|
-
const { args, fields, maxChars } = prepared.call;
|
|
224
|
+
const { args, fields, optionalFields, maxChars } = prepared.call;
|
|
175
225
|
const values = {};
|
|
176
226
|
for (const p of op.params) {
|
|
177
227
|
const argument = p.kind === "header" && p.name === "If-Match" ? "if_match" : p.name;
|
|
@@ -184,14 +234,21 @@ async function callOperationRaw(op, rawArgs, remote, client) {
|
|
|
184
234
|
let rawBody = op.bodyStyle === "data" ? args.body : undefined;
|
|
185
235
|
if (LOCAL_PROCESS && !remote) {
|
|
186
236
|
for (const p of op.params) {
|
|
187
|
-
if (p.type !== "file"
|
|
237
|
+
if (p.type !== "file")
|
|
188
238
|
continue;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
239
|
+
const paths = values[p.name];
|
|
240
|
+
if (typeof paths !== "string" && !(Array.isArray(paths) && paths.every((path) => typeof path === "string")))
|
|
241
|
+
continue;
|
|
242
|
+
const files = [];
|
|
243
|
+
for (const path of Array.isArray(paths) ? paths : [paths]) {
|
|
244
|
+
try {
|
|
245
|
+
files.push(fileArgument(path));
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
fileIssues.push({ code: "INVALID_ARGUMENT", argument: p.name, message: p.name + ": cannot read " + path + " (" + e.message + ")" });
|
|
249
|
+
}
|
|
194
250
|
}
|
|
251
|
+
values[p.name] = Array.isArray(paths) ? files : files[0];
|
|
195
252
|
}
|
|
196
253
|
if (op.bodyKind === "binary" && typeof rawBody === "string") {
|
|
197
254
|
try {
|
|
@@ -204,13 +261,18 @@ async function callOperationRaw(op, rawArgs, remote, client) {
|
|
|
204
261
|
}
|
|
205
262
|
if (fileIssues.length > 0)
|
|
206
263
|
return argumentsError(op, fileIssues);
|
|
264
|
+
// A tool result is one value: a streamed response is not available here.
|
|
265
|
+
if (op.streamMethod && args[op.streamMethod.flag] === op.streamMethod.value) {
|
|
266
|
+
return argumentsError(op, [{ code: "INVALID_ARGUMENT", argument: op.streamMethod.flag, message: op.streamMethod.flag + ": streaming is not available over MCP, which returns one result per call. Call again without " + op.streamMethod.flag + " " + JSON.stringify(op.streamMethod.value) + " to get the complete response." }]);
|
|
267
|
+
}
|
|
207
268
|
const callArgs = buildArgs(op, values, rawBody, typeof args.select === "string" ? args.select : undefined);
|
|
208
269
|
const errorContext = {
|
|
209
270
|
authHint: remote ? AUTH_HINT_HTTP : AUTH_HINT_STDIO,
|
|
210
271
|
docsUrl: docsSource.docsUrl(),
|
|
211
272
|
hadCredential: !!remote || CLIENT_CREDENTIALS.get(client) === true,
|
|
273
|
+
requiredScopes: requiredScopes(op.security),
|
|
212
274
|
};
|
|
213
|
-
const shape = { fields, maxChars, pagination: op.pagination, args };
|
|
275
|
+
const shape = { fields, optionalFields, maxChars, pagination: op.pagination, args, safety: op.safety };
|
|
214
276
|
try {
|
|
215
277
|
const target = client[op.resource];
|
|
216
278
|
let result = await asApiResult(target[op.method](...callArgs));
|
|
@@ -230,7 +292,7 @@ async function callOperationRaw(op, rawArgs, remote, client) {
|
|
|
230
292
|
}
|
|
231
293
|
if (op.paginated) {
|
|
232
294
|
const page = result.data;
|
|
233
|
-
return pageOutcome(page.items, page.nextPageParams(), { ...shape, requestId: page.response.requestId });
|
|
295
|
+
return pageOutcome(page.items, page.nextPageParams(), { ...shape, requestId: page.response.requestId, carryArgs: op.params.filter((p) => p.required || p.credential).map((p) => p.name) });
|
|
234
296
|
}
|
|
235
297
|
// A binary body (the SDK hands back a Blob): an image block, or a file
|
|
236
298
|
// on disk, never "{}".
|
|
@@ -257,9 +319,24 @@ function referenceCacheFor(authHeader) {
|
|
|
257
319
|
return cache;
|
|
258
320
|
}
|
|
259
321
|
async function callOperation(op, rawArgs, remote) {
|
|
260
|
-
|
|
322
|
+
// A path argument that defaults to the Basic-auth username needs the
|
|
323
|
+
// configured credential before validation; a credential failure is
|
|
324
|
+
// reported below, after the arguments.
|
|
325
|
+
let username;
|
|
326
|
+
if (op.params.some((p) => p.credential && rawArgs[p.name] === undefined)) {
|
|
327
|
+
try {
|
|
328
|
+
username = credentialUsername(op, remote ? await remote.client() : getClient(op));
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
username = undefined;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const prepared = prepareCall(op, rawArgs, { maxChars: MAX_RESULT_CHARS, credentialUsername: username });
|
|
261
335
|
if (!prepared.ok)
|
|
262
336
|
return prepared.outcome;
|
|
337
|
+
const unsupported = unsupportedAuthOutcome(op);
|
|
338
|
+
if (unsupported)
|
|
339
|
+
return unsupported;
|
|
263
340
|
// One client/session for the entire tool, including name-to-ID lookups.
|
|
264
341
|
// If login changes during a lookup, the token callback rejects the old session.
|
|
265
342
|
let client;
|
|
@@ -269,6 +346,8 @@ async function callOperation(op, rawArgs, remote) {
|
|
|
269
346
|
catch (error) {
|
|
270
347
|
if (remote && error instanceof McpAccountLinkRequired)
|
|
271
348
|
throw error;
|
|
349
|
+
if (!remote && error instanceof MissingCredentialsError)
|
|
350
|
+
return error.outcome;
|
|
272
351
|
return textError(remote ? "API access is unavailable for this MCP account. Reconnect your API account or contact the server owner." : error.message);
|
|
273
352
|
}
|
|
274
353
|
const resolved = await resolveReferences(op, prepared.call.args, {
|
|
@@ -281,7 +360,7 @@ async function callOperation(op, rawArgs, remote) {
|
|
|
281
360
|
return resolved.outcome;
|
|
282
361
|
const args = {
|
|
283
362
|
...resolved.args,
|
|
284
|
-
...(prepared.call.fields ? { fields: prepared.call.fields.map((path) => path.join(".")) } : {}),
|
|
363
|
+
...(prepared.call.fields ? { fields: prepared.call.fields.map((path) => (prepared.call.optionalFields.includes(path.join(".")) ? "?" : "") + path.join(".")) } : {}),
|
|
285
364
|
};
|
|
286
365
|
return callOperationRaw(op, args, remote, client);
|
|
287
366
|
}
|
|
@@ -306,7 +385,7 @@ const HIDDEN_WRITES = READ_ONLY ? visibleOps(OPS, { include: INCLUDE, uploads: L
|
|
|
306
385
|
const HAS_UPLOADS = MCP_OPS.some((op) => op.bodyKind === "multipart" || op.bodyKind === "binary");
|
|
307
386
|
/** A local file as an upload part (multipart field or raw body). */
|
|
308
387
|
function fileArgument(path) {
|
|
309
|
-
return new File([readFileSync(path)], basename(path));
|
|
388
|
+
return new File([readFileSync(path)], basename(path), { type: mediaTypeForPath(path) });
|
|
310
389
|
}
|
|
311
390
|
/** Where binary responses land on a local server: a per-server temp dir. */
|
|
312
391
|
function saveBinary(bytes, _mediaType, suggestedName) {
|
|
@@ -319,6 +398,7 @@ function saveBinary(bytes, _mediaType, suggestedName) {
|
|
|
319
398
|
const docsSource = {
|
|
320
399
|
ops: MCP_OPS,
|
|
321
400
|
omittedOps: OMITTED_OPS,
|
|
401
|
+
hiddenOps: hiddenOperations(OPS, { readOnly: READ_ONLY, include: INCLUDE, uploads: LOCAL_PROCESS }, { readOnly: READ_ONLY_SWITCH, include: TOOLS_SWITCH }),
|
|
322
402
|
generatedOperationCount: OPS.length,
|
|
323
403
|
docsUrl: () => readJson("config.json")?.docsUrl ?? DOCS_URL_DEFAULT,
|
|
324
404
|
docsIndexUrl: () => readJson("config.json")?.docsUrl ? null : DOCS_INDEX_URL_DEFAULT,
|
|
@@ -329,9 +409,9 @@ function serverFor(remote) {
|
|
|
329
409
|
const source = remote ? { ...docsSource, ops: operations } : docsSource;
|
|
330
410
|
const website = docsSource.docsUrl();
|
|
331
411
|
return {
|
|
332
|
-
serverInfo: { name: SERVER_NAME, title: "
|
|
412
|
+
serverInfo: { name: SERVER_NAME, title: "Typeship", version: SERVER_VERSION, ...(website ? { websiteUrl: website } : {}) },
|
|
333
413
|
instructions: serverInstructions({
|
|
334
|
-
title: "
|
|
414
|
+
title: "Typeship",
|
|
335
415
|
ops: operations,
|
|
336
416
|
toolCount: operations.length,
|
|
337
417
|
generatedOperationCount: OPS.length,
|
|
@@ -451,13 +531,16 @@ async function authorizedHttp(request, authorizer, credentialsFor, apiBaseUrl) {
|
|
|
451
531
|
throw new Error("API credentials target a different API URL.");
|
|
452
532
|
const token = request.headers.get("authorization").slice(7);
|
|
453
533
|
const transport = credentials.fetch ?? fetch;
|
|
454
|
-
|
|
534
|
+
const created = new TypeshipClient({ ...credentials, baseUrl, fetch: async (input, init) => {
|
|
455
535
|
const headers = new Headers(init?.headers);
|
|
456
536
|
const destination = new URL(typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
|
|
457
537
|
if ([...headers.values()].some(value => value === token || value === "Bearer " + token) || [...destination.searchParams.values()].includes(token))
|
|
458
538
|
throw new Error("An MCP connection token cannot authorize an upstream API request.");
|
|
459
539
|
return transport(input, init);
|
|
460
540
|
} });
|
|
541
|
+
const { basicAuth, credentials: named } = credentials;
|
|
542
|
+
CLIENT_BASIC.set(created, { basicAuth, credentials: named });
|
|
543
|
+
return created;
|
|
461
544
|
})() };
|
|
462
545
|
let incoming;
|
|
463
546
|
try {
|
|
@@ -571,7 +654,23 @@ function invokedDirectly() {
|
|
|
571
654
|
return /mcp\.(js|mjs|ts)$/.test(entry);
|
|
572
655
|
}
|
|
573
656
|
}
|
|
657
|
+
/** A --tools value that names nothing would start a server with no
|
|
658
|
+
* operations, which reads to an agent like an empty API. */
|
|
659
|
+
function toolsSwitchProblem() {
|
|
660
|
+
if (ARGV.includes("--tools") && INCLUDE === undefined)
|
|
661
|
+
return "--tools needs a comma-separated list of resources, tool names, or resource.method.";
|
|
662
|
+
const unmatched = unmatchedIncludes(OPS, INCLUDE);
|
|
663
|
+
if (unmatched.length === 0)
|
|
664
|
+
return null;
|
|
665
|
+
const resources = [...new Set(OPS.map((op) => op.resource))];
|
|
666
|
+
return TOOLS_SWITCH + " names nothing in this API: " + unmatched.join(", ") + ". Resources: " + resources.slice(0, 30).join(", ") + (resources.length > 30 ? ", …" : "") + ".";
|
|
667
|
+
}
|
|
574
668
|
if (invokedDirectly()) {
|
|
669
|
+
const problem = toolsSwitchProblem();
|
|
670
|
+
if (problem !== null) {
|
|
671
|
+
process.stderr.write(SERVER_NAME + ": " + problem + "\n");
|
|
672
|
+
process.exit(2);
|
|
673
|
+
}
|
|
575
674
|
const httpFlag = process.argv.indexOf("--http");
|
|
576
675
|
if (httpFlag !== -1) {
|
|
577
676
|
const port = Number(process.argv[httpFlag + 1]) || Number(process.env.PORT) || 3000;
|
|
@@ -16,6 +16,25 @@ export declare function resolveNamedCredentials(schemes: CredentialSchemes, laye
|
|
|
16
16
|
}[]): NamedCredentials;
|
|
17
17
|
/** Make convenience and named inputs comparable without expanding combinations. */
|
|
18
18
|
export declare function namedCredentialAvailability(schemes: CredentialSchemes, options: Set<string>, named: NamedCredentials): Set<string>;
|
|
19
|
+
/** The schemes an interactive OAuth login session authenticates by name. When
|
|
20
|
+
* the API also declares a separate non-OAuth bearer scheme, the convenience
|
|
21
|
+
* bearer token belongs to that scheme, so the session must be passed to each
|
|
22
|
+
* OAuth scheme by name. Empty: the session is the convenience bearer token. */
|
|
23
|
+
export declare function oauthSessionSchemes(schemes: CredentialSchemes): string[];
|
|
24
|
+
/** Whether resolved client options satisfy one complete credential alternative
|
|
25
|
+
* of an operation. Returns null when satisfied (or when no credential is
|
|
26
|
+
* required); otherwise the named alternatives and the schemes each lacks. */
|
|
27
|
+
export declare function missingCredentials(schemes: CredentialSchemes, credentialOptions: string[][] | undefined, options: Record<string, unknown>): {
|
|
28
|
+
alternatives: string[][];
|
|
29
|
+
missing: string[][];
|
|
30
|
+
} | null;
|
|
19
31
|
/** Bound file/stdin reads before allocating a full credential document. */
|
|
20
32
|
export declare function readNamedCredentialsFile(input: string, schemes: CredentialSchemes): NamedCredentials;
|
|
33
|
+
/** Extra request headers from repeated `--header "Name: value"` flags and a
|
|
34
|
+
* `<PREFIX>_HEADERS` variable (a JSON object, or one `Name: value` per line).
|
|
35
|
+
* The escape hatch for credentials a spec does not declare. Flags win over
|
|
36
|
+
* the environment; values never appear in errors. */
|
|
37
|
+
export declare function parseExtraHeaders(environment: string | undefined, flags: readonly string[], variable: string): Record<string, string>;
|
|
38
|
+
/** Apply extra headers last, replacing any header of the same name. */
|
|
39
|
+
export declare function applyExtraHeaders(target: Record<string, string>, extra: Record<string, string>): void;
|
|
21
40
|
//# sourceMappingURL=named-credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"named-credentials.d.ts","sourceRoot":"","sources":["../src/named-credentials.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAEpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAqBlG;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAAE,GAAG,gBAAgB,CAa/J;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAKlI;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAkBpG"}
|
|
1
|
+
{"version":3,"file":"named-credentials.d.ts","sourceRoot":"","sources":["../src/named-credentials.ts"],"names":[],"mappings":"AAEA,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC9E,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAEpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAqBlG;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAAE,GAAG,gBAAgB,CAa/J;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAKlI;AAED;;;+EAG+E;AAC/E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAExE;AAED;;6EAE6E;AAC7E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,EAAE,EAAE,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,YAAY,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAA;CAAE,GAAG,IAAI,CASpM;AAED,2EAA2E;AAC3E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAkBpG;AAKD;;;qDAGqD;AACrD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0BrI;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAKrG"}
|
|
@@ -37,7 +37,7 @@ export function resolveNamedCredentials(schemes, layers) {
|
|
|
37
37
|
for (const layer of layers) {
|
|
38
38
|
for (const [name, scheme] of Object.entries(schemes)) {
|
|
39
39
|
for (const option of scheme.options) {
|
|
40
|
-
if (option === "clientCredentials")
|
|
40
|
+
if (option === "clientCredentials" || option === "refreshToken")
|
|
41
41
|
continue;
|
|
42
42
|
const value = layer.options?.[option];
|
|
43
43
|
if (value !== undefined)
|
|
@@ -57,6 +57,27 @@ export function namedCredentialAvailability(schemes, options, named) {
|
|
|
57
57
|
}
|
|
58
58
|
return options;
|
|
59
59
|
}
|
|
60
|
+
/** The schemes an interactive OAuth login session authenticates by name. When
|
|
61
|
+
* the API also declares a separate non-OAuth bearer scheme, the convenience
|
|
62
|
+
* bearer token belongs to that scheme, so the session must be passed to each
|
|
63
|
+
* OAuth scheme by name. Empty: the session is the convenience bearer token. */
|
|
64
|
+
export function oauthSessionSchemes(schemes) {
|
|
65
|
+
return Object.entries(schemes).filter(([, scheme]) => (scheme.options.includes("clientCredentials") || scheme.options.includes("refreshToken")) && !scheme.options.includes("bearerToken")).map(([name]) => name);
|
|
66
|
+
}
|
|
67
|
+
/** Whether resolved client options satisfy one complete credential alternative
|
|
68
|
+
* of an operation. Returns null when satisfied (or when no credential is
|
|
69
|
+
* required); otherwise the named alternatives and the schemes each lacks. */
|
|
70
|
+
export function missingCredentials(schemes, credentialOptions, options) {
|
|
71
|
+
const supplied = (key, value) => typeof value === "function" || (typeof value === "string" && value.length > 0)
|
|
72
|
+
|| (key === "clientCredentials" && value !== null && typeof value === "object")
|
|
73
|
+
|| (value !== null && typeof value === "object" && "username" in value && "password" in value && typeof value.username === "string" && value.username.length > 0 && typeof value.password === "string" && value.password.length > 0);
|
|
74
|
+
const named = Object.fromEntries(Object.entries((options.credentials ?? {})).filter(([name, value]) => supplied(name, value)));
|
|
75
|
+
const available = namedCredentialAvailability(schemes, new Set(Object.keys(options).filter((key) => key !== "credentials" && supplied(key, options[key]))), named);
|
|
76
|
+
if (credentialOptions?.some((alternative) => alternative.length > 0 && alternative.every((option) => available.has(option))))
|
|
77
|
+
return null;
|
|
78
|
+
const alternatives = (credentialOptions ?? []).filter((alternative) => alternative.length > 0 && alternative.every((option) => option.startsWith("credentials."))).map((alternative) => alternative.map((option) => option.slice("credentials.".length)));
|
|
79
|
+
return { alternatives, missing: alternatives.map((alternative) => alternative.filter((name) => !available.has("credentials." + name))) };
|
|
80
|
+
}
|
|
60
81
|
/** Bound file/stdin reads before allocating a full credential document. */
|
|
61
82
|
export function readNamedCredentialsFile(input, schemes) {
|
|
62
83
|
let fd;
|
|
@@ -84,3 +105,62 @@ export function readNamedCredentialsFile(input, schemes) {
|
|
|
84
105
|
}
|
|
85
106
|
return parseNamedCredentials(Buffer.concat(chunks).toString("utf8"), schemes);
|
|
86
107
|
}
|
|
108
|
+
const HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
109
|
+
const TRANSPORT_HEADERS = new Set(["host", "content-length", "content-type", "transfer-encoding", "connection"]);
|
|
110
|
+
/** Extra request headers from repeated `--header "Name: value"` flags and a
|
|
111
|
+
* `<PREFIX>_HEADERS` variable (a JSON object, or one `Name: value` per line).
|
|
112
|
+
* The escape hatch for credentials a spec does not declare. Flags win over
|
|
113
|
+
* the environment; values never appear in errors. */
|
|
114
|
+
export function parseExtraHeaders(environment, flags, variable) {
|
|
115
|
+
const entries = [];
|
|
116
|
+
const line = (text, source) => {
|
|
117
|
+
const colon = text.indexOf(":");
|
|
118
|
+
if (colon <= 0)
|
|
119
|
+
throw new Error(source + " expects \"Name: value\".");
|
|
120
|
+
entries.push([text.slice(0, colon).trim(), text.slice(colon + 1).trim()]);
|
|
121
|
+
};
|
|
122
|
+
if (environment !== undefined && environment.trim()) {
|
|
123
|
+
const text = environment.trim();
|
|
124
|
+
if (text.startsWith("{")) {
|
|
125
|
+
let value;
|
|
126
|
+
try {
|
|
127
|
+
value = JSON.parse(text);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
throw new Error(variable + " must be a JSON object of header names to values, or one \"Name: value\" per line.");
|
|
131
|
+
}
|
|
132
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.values(value).some((entry) => typeof entry !== "string"))
|
|
133
|
+
throw new Error(variable + " must map header names to string values.");
|
|
134
|
+
entries.push(...Object.entries(value));
|
|
135
|
+
}
|
|
136
|
+
else
|
|
137
|
+
for (const part of text.split(/\r?\n/))
|
|
138
|
+
if (part.trim())
|
|
139
|
+
line(part, variable);
|
|
140
|
+
}
|
|
141
|
+
for (const flag of flags)
|
|
142
|
+
line(flag, "--header");
|
|
143
|
+
const headers = {};
|
|
144
|
+
for (const [name, value] of entries) {
|
|
145
|
+
if (!HEADER_NAME.test(name))
|
|
146
|
+
throw new Error("Header names may contain only token characters. Check --header and " + variable + ".");
|
|
147
|
+
if (TRANSPORT_HEADERS.has(name.toLowerCase()))
|
|
148
|
+
throw new Error("The " + name + " header is set by the client and cannot be overridden.");
|
|
149
|
+
if (/[\r\n\0]/.test(value))
|
|
150
|
+
throw new Error("Header values cannot contain line breaks. Check --header and " + variable + ".");
|
|
151
|
+
for (const existing of Object.keys(headers))
|
|
152
|
+
if (existing.toLowerCase() === name.toLowerCase())
|
|
153
|
+
delete headers[existing];
|
|
154
|
+
headers[name] = value;
|
|
155
|
+
}
|
|
156
|
+
return headers;
|
|
157
|
+
}
|
|
158
|
+
/** Apply extra headers last, replacing any header of the same name. */
|
|
159
|
+
export function applyExtraHeaders(target, extra) {
|
|
160
|
+
for (const [name, value] of Object.entries(extra)) {
|
|
161
|
+
for (const existing of Object.keys(target))
|
|
162
|
+
if (existing.toLowerCase() === name.toLowerCase())
|
|
163
|
+
delete target[existing];
|
|
164
|
+
target[name] = value;
|
|
165
|
+
}
|
|
166
|
+
}
|
package/dist/oauth-request.d.ts
CHANGED
|
@@ -7,8 +7,14 @@ interface AuthenticationResponse {
|
|
|
7
7
|
status: number;
|
|
8
8
|
data?: Record<string, unknown>;
|
|
9
9
|
error?: DeviceOAuthError;
|
|
10
|
+
/** RFC 6749 `error` and `error_description` from a 400 or 401 body, as
|
|
11
|
+
* "code: description". Only those two fields, printable ASCII, bounded. */
|
|
12
|
+
providerError?: string;
|
|
10
13
|
}
|
|
11
|
-
/**
|
|
14
|
+
/** The provider's standard error fields, never any other body content. */
|
|
15
|
+
export declare function providerErrorOf(data: Record<string, unknown>): string | undefined;
|
|
16
|
+
/** Transport causes and error bodies beyond the standard OAuth `error` and
|
|
17
|
+
* `error_description` fields may contain credentials and are discarded. */
|
|
12
18
|
export declare function oauthJsonRequest(url: string | URL, init?: RequestInit, timeoutMs?: number): Promise<AuthenticationResponse>;
|
|
13
19
|
/** Device polling needs four protocol errors. Return only their exact names,
|
|
14
20
|
* never descriptions, error URIs, arbitrary error codes or other body fields. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-request.d.ts","sourceRoot":"","sources":["../src/oauth-request.ts"],"names":[],"mappings":"AACA,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,WAAW,GAAG,WAAW,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;gBAC5F,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAY7C;AAED,MAAM,MAAM,gBAAgB,GAAG,uBAAuB,GAAG,WAAW,GAAG,eAAe,GAAG,eAAe,CAAC;AACzG,UAAU,sBAAsB;
|
|
1
|
+
{"version":3,"file":"oauth-request.d.ts","sourceRoot":"","sources":["../src/oauth-request.ts"],"names":[],"mappings":"AACA,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,WAAW,GAAG,WAAW,GAAG,oBAAoB,GAAG,kBAAkB,CAAC;gBAC5F,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAY7C;AAED,MAAM,MAAM,gBAAgB,GAAG,uBAAuB,GAAG,WAAW,GAAG,eAAe,GAAG,eAAe,CAAC;AACzG,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzE;+EAC2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAKjF;AAED;2EAC2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAE,WAAgB,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAE/H;AACD;iFACiF;AACjF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAE,WAAgB,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAEjI;AACD,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAE,WAAgB,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAE7H"}
|
package/dist/oauth-request.js
CHANGED
|
@@ -13,7 +13,16 @@ export class OAuthResponseError extends Error {
|
|
|
13
13
|
this.name = "OAuthResponseError";
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
16
|
+
/** The provider's standard error fields, never any other body content. */
|
|
17
|
+
export function providerErrorOf(data) {
|
|
18
|
+
const code = typeof data.error === "string" && /^[\x20-\x21\x23-\x5b\x5d-\x7e]{1,64}$/.test(data.error) ? data.error : undefined;
|
|
19
|
+
if (!code)
|
|
20
|
+
return undefined;
|
|
21
|
+
const description = typeof data.error_description === "string" ? data.error_description.replace(/[^\x20-\x7e]/g, " ").trim().slice(0, 300) : "";
|
|
22
|
+
return description ? code + ": " + description : code;
|
|
23
|
+
}
|
|
24
|
+
/** Transport causes and error bodies beyond the standard OAuth `error` and
|
|
25
|
+
* `error_description` fields may contain credentials and are discarded. */
|
|
17
26
|
export function oauthJsonRequest(url, init = {}, timeoutMs = 30_000) {
|
|
18
27
|
return boundedRequest(url, init, timeoutMs, "json");
|
|
19
28
|
}
|
|
@@ -34,6 +43,9 @@ async function boundedRequest(url, init, timeoutMs, mode) {
|
|
|
34
43
|
let reader;
|
|
35
44
|
let timer;
|
|
36
45
|
let interrupted;
|
|
46
|
+
// An error status is already an answer: its body only adds the provider's
|
|
47
|
+
// explanation, so a stalled or malformed error body never becomes a failure.
|
|
48
|
+
let errorStatus;
|
|
37
49
|
let rejectDeadline;
|
|
38
50
|
const deadline = new Promise((_, reject) => { rejectDeadline = reject; });
|
|
39
51
|
const stop = (code) => { interrupted ??= code; rejectDeadline(new OAuthResponseError(interrupted)); controller.abort(); };
|
|
@@ -56,10 +68,12 @@ async function boundedRequest(url, init, timeoutMs, mode) {
|
|
|
56
68
|
void response.body?.cancel().catch(() => { });
|
|
57
69
|
throw new OAuthResponseError("request_failed");
|
|
58
70
|
}
|
|
59
|
-
if (mode === "status" || response.status !== 200 &&
|
|
71
|
+
if (mode === "status" || response.status !== 200 && response.status !== 400 && response.status !== 401) {
|
|
60
72
|
void response.body?.cancel().catch(() => { });
|
|
61
73
|
return { status: response.status };
|
|
62
74
|
}
|
|
75
|
+
if (response.status !== 200)
|
|
76
|
+
errorStatus = response.status;
|
|
63
77
|
if (!response.body)
|
|
64
78
|
throw new OAuthResponseError("invalid_response");
|
|
65
79
|
reader = response.body.getReader();
|
|
@@ -90,17 +104,25 @@ async function boundedRequest(url, init, timeoutMs, mode) {
|
|
|
90
104
|
data = JSON.parse(text);
|
|
91
105
|
}
|
|
92
106
|
catch {
|
|
107
|
+
if (response.status !== 200)
|
|
108
|
+
return { status: response.status };
|
|
93
109
|
throw new OAuthResponseError("invalid_response");
|
|
94
110
|
}
|
|
95
|
-
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
111
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
112
|
+
if (response.status !== 200)
|
|
113
|
+
return { status: response.status };
|
|
96
114
|
throw new OAuthResponseError("invalid_response");
|
|
115
|
+
}
|
|
97
116
|
if (response.status !== 200) {
|
|
98
117
|
const error = data.error;
|
|
99
|
-
|
|
118
|
+
const providerError = providerErrorOf(data);
|
|
119
|
+
return { status: response.status, ...(providerError ? { providerError } : {}), ...(mode === "device" && ["authorization_pending", "slow_down", "access_denied", "expired_token"].includes(String(error)) && typeof error === "string" ? { error: error } : {}) };
|
|
100
120
|
}
|
|
101
121
|
return { status: response.status, data: data };
|
|
102
122
|
}
|
|
103
123
|
catch (error) {
|
|
124
|
+
if (errorStatus !== undefined && interrupted !== "cancelled")
|
|
125
|
+
return { status: errorStatus };
|
|
104
126
|
if (interrupted)
|
|
105
127
|
throw new OAuthResponseError(interrupted);
|
|
106
128
|
if (error instanceof OAuthResponseError)
|
package/dist/oauth-session.d.ts
CHANGED
|
@@ -70,7 +70,13 @@ export interface CredentialStore {
|
|
|
70
70
|
read(): StoredCredentials | null;
|
|
71
71
|
withLock<T>(work: (store: LockedCredentials) => Promise<T>): Promise<T>;
|
|
72
72
|
}
|
|
73
|
+
/** The OS credential store or the saved credential file cannot be used. */
|
|
73
74
|
export declare class CredentialStorageError extends Error {
|
|
75
|
+
constructor(message?: string);
|
|
76
|
+
}
|
|
77
|
+
/** A saved login cannot be used as is; the remedy is to sign in again. */
|
|
78
|
+
export declare class OAuthSessionError extends Error {
|
|
79
|
+
constructor(message?: string);
|
|
74
80
|
}
|
|
75
81
|
export interface CredentialCodec {
|
|
76
82
|
readonly name: string;
|
|
@@ -101,6 +107,12 @@ export declare class FileCredentialStore implements CredentialStore {
|
|
|
101
107
|
/** Re-read on every request attempt; only one process may rotate the token.
|
|
102
108
|
* A failed or interrupted exchange requires login, since the server may have
|
|
103
109
|
* consumed its refresh token even when no response reached this process. */
|
|
104
|
-
export declare function oauthSessionToken(store: CredentialStore, config: SessionConfiguration, sessionId: string, tokenParams?: Record<string, string
|
|
110
|
+
export declare function oauthSessionToken(store: CredentialStore, config: SessionConfiguration, sessionId: string, tokenParams?: Record<string, string>, rejected?: string): Promise<string>;
|
|
111
|
+
/** A saved-session token for the request runtime: resolved before every
|
|
112
|
+
* attempt, and after a 401 (invalidate) the next resolve forces one refresh
|
|
113
|
+
* of the token the API rejected. */
|
|
114
|
+
export declare function sessionCredential(resolve: (rejected?: string) => Promise<string>): (() => Promise<string>) & {
|
|
115
|
+
invalidate(): void;
|
|
116
|
+
};
|
|
105
117
|
export {};
|
|
106
118
|
//# sourceMappingURL=oauth-session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-session.d.ts","sourceRoot":"","sources":["../src/oauth-session.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"oauth-session.d.ts","sourceRoot":"","sources":["../src/oauth-session.ts"],"names":[],"mappings":"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"}
|