@typeship-ax/mcp 0.6.0 → 0.8.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.
Files changed (79) hide show
  1. package/README.md +1 -1
  2. package/api.json +5597 -3010
  3. package/api.md +433 -54
  4. package/dist/core/http.d.ts +6 -92
  5. package/dist/core/http.d.ts.map +1 -1
  6. package/dist/core/http.js +70 -209
  7. package/dist/core/pagination.d.ts.map +1 -1
  8. package/dist/core/pagination.js +6 -34
  9. package/dist/dates.d.ts +0 -2
  10. package/dist/dates.d.ts.map +1 -1
  11. package/dist/dates.js +0 -1
  12. package/dist/docs.d.ts +11 -0
  13. package/dist/docs.d.ts.map +1 -0
  14. package/dist/docs.js +114 -0
  15. package/dist/errors.d.ts +27 -27
  16. package/dist/errors.d.ts.map +1 -1
  17. package/dist/errors.js +7 -7
  18. package/dist/index.d.ts +19 -11
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +23 -13
  21. package/dist/mcp-protocol.d.ts +19 -24
  22. package/dist/mcp-protocol.d.ts.map +1 -1
  23. package/dist/mcp-protocol.js +160 -124
  24. package/dist/mcp.js +16 -19
  25. package/dist/ops.d.ts +5 -0
  26. package/dist/ops.d.ts.map +1 -1
  27. package/dist/ops.js +31 -17
  28. package/dist/resources/account.d.ts +2 -2
  29. package/dist/resources/account.d.ts.map +1 -1
  30. package/dist/resources/api-keys.d.ts +10 -5
  31. package/dist/resources/api-keys.d.ts.map +1 -1
  32. package/dist/resources/api-keys.js +3 -1
  33. package/dist/resources/definition-revisions.d.ts +58 -0
  34. package/dist/resources/definition-revisions.d.ts.map +1 -0
  35. package/dist/resources/definition-revisions.js +110 -0
  36. package/dist/resources/definitions.d.ts +24 -0
  37. package/dist/resources/definitions.d.ts.map +1 -0
  38. package/dist/resources/definitions.js +51 -0
  39. package/dist/resources/generate.d.ts +5 -5
  40. package/dist/resources/generate.d.ts.map +1 -1
  41. package/dist/resources/generate.js +3 -3
  42. package/dist/resources/generations.d.ts +3 -3
  43. package/dist/resources/generations.d.ts.map +1 -1
  44. package/dist/resources/generations.js +1 -1
  45. package/dist/resources/projects.d.ts +66 -26
  46. package/dist/resources/projects.d.ts.map +1 -1
  47. package/dist/resources/projects.js +87 -13
  48. package/dist/resources/targets.d.ts +86 -0
  49. package/dist/resources/targets.d.ts.map +1 -0
  50. package/dist/resources/targets.js +184 -0
  51. package/dist/schemas.d.ts.map +1 -1
  52. package/dist/schemas.js +119 -62
  53. package/dist/types.d.ts +1761 -222
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/types.js +9 -3
  56. package/package.json +1 -1
  57. package/src/core/http.ts +75 -293
  58. package/src/core/pagination.ts +6 -30
  59. package/src/dates.ts +0 -1
  60. package/src/docs.ts +101 -0
  61. package/src/errors.ts +30 -30
  62. package/src/index.ts +23 -13
  63. package/src/mcp-protocol.ts +170 -120
  64. package/src/mcp.ts +22 -22
  65. package/src/ops.ts +43 -17
  66. package/src/resources/account.ts +3 -3
  67. package/src/resources/api-keys.ts +22 -7
  68. package/src/resources/definition-revisions.ts +198 -0
  69. package/src/resources/definitions.ts +97 -0
  70. package/src/resources/generate.ts +6 -6
  71. package/src/resources/generations.ts +4 -4
  72. package/src/resources/projects.ts +182 -37
  73. package/src/resources/targets.ts +346 -0
  74. package/src/schemas.ts +119 -62
  75. package/src/types.ts +1947 -281
  76. package/dist/resources/spec-revisions.d.ts +0 -47
  77. package/dist/resources/spec-revisions.d.ts.map +0 -1
  78. package/dist/resources/spec-revisions.js +0 -90
  79. package/src/resources/spec-revisions.ts +0 -150
@@ -15,6 +15,7 @@
15
15
  * Spec: https://modelcontextprotocol.io/specification/2026-07-28
16
16
  */
17
17
  import { dateKindOf, relativeDate } from "./dates.js";
18
+ import { resolveDocsContentUrl } from "./docs.js";
18
19
  export const MCP_PROTOCOL_VERSION = "2026-07-28";
19
20
  /** Revisions served. Legacy (initialize-handshake) revisions are not; an
20
21
  * initialize request gets an error naming this list, as the spec asks of
@@ -185,7 +186,7 @@ export async function handleRpc(server, incoming) {
185
186
  const started = Date.now();
186
187
  const outcome = await server.callTool(name, args);
187
188
  if (outcome === undefined)
188
- return rpcError(id, -32602, "Unknown tool: " + name, 400);
189
+ return rpcError(id, -32602, server.unknownToolMessage?.(name) ?? "Unknown tool: " + name, 400);
189
190
  if (server.afterToolCall)
190
191
  await server.afterToolCall(name, outcome, Date.now() - started);
191
192
  // A tool that declares an outputSchema MUST return structuredContent,
@@ -275,8 +276,18 @@ export function exampleFromSchema(schema, field = "value", depth = 0) {
275
276
  return node.examples[0];
276
277
  if (node.default !== undefined)
277
278
  return node.default;
278
- if (Array.isArray(node.enum) && node.enum.length > 0)
279
+ if (Array.isArray(node.enum) && node.enum.length > 0) {
280
+ if (typeof node.pattern === "string") {
281
+ try {
282
+ const pattern = new RegExp(node.pattern);
283
+ const matching = node.enum.find((value) => typeof value === "string" && pattern.test(value));
284
+ if (matching !== undefined)
285
+ return matching;
286
+ }
287
+ catch { /* malformed patterns are ignored for examples */ }
288
+ }
279
289
  return node.enum.find((v) => v !== null) ?? node.enum[0];
290
+ }
280
291
  const variants = (Array.isArray(node.oneOf) ? node.oneOf : Array.isArray(node.anyOf) ? node.anyOf : null);
281
292
  if (variants) {
282
293
  const useful = variants.find((v) => v && typeof v === "object" && v.type !== "null") ?? variants[0];
@@ -316,7 +327,10 @@ export function exampleFromSchema(schema, field = "value", depth = 0) {
316
327
  if (type === "string" || type === undefined) {
317
328
  const format = typeof node.format === "string" ? node.format : "";
318
329
  const lower = field.toLowerCase();
319
- let value = format === "date-time" ? "2026-01-15T12:00:00Z"
330
+ const min = typeof node.minLength === "number" ? node.minLength : 0;
331
+ const max = typeof node.maxLength === "number" ? node.maxLength : undefined;
332
+ const patterned = typeof node.pattern === "string" ? exampleMatchingPattern(node.pattern, min, max) : null;
333
+ let value = patterned ?? (format === "date-time" ? "2026-01-15T12:00:00Z"
320
334
  : format === "date" ? "2026-01-15"
321
335
  : format === "email" || lower.includes("email") ? "person@example.com"
322
336
  : (format === "uri" || format === "url" || lower.endsWith("url")) && (lower.includes("webhook") || lower.includes("callback")) ? "https://example.com/webhook"
@@ -327,16 +341,47 @@ export function exampleFromSchema(schema, field = "value", depth = 0) {
327
341
  : lower.includes("version") ? "1.0.0"
328
342
  : /(^|_)id$|Id$/.test(field) ? (lower === "id" ? "id" : field.replace(/[_-]?id$/i, "")) + "_123"
329
343
  : lower.includes("name") ? "example"
330
- : "value";
331
- const min = typeof node.minLength === "number" ? node.minLength : 0;
344
+ : "value");
332
345
  while (value.length < min)
333
346
  value += "x";
334
- if (typeof node.maxLength === "number")
335
- value = value.slice(0, node.maxLength);
347
+ if (max !== undefined)
348
+ value = value.slice(0, max);
336
349
  return value;
337
350
  }
338
351
  return null;
339
352
  }
353
+ /** A useful value for the common API-id pattern (`^agt_`, `^src_[a-z0-9]+$`).
354
+ * Full regex generation would be surprising and heavyweight; an anchored
355
+ * literal prefix plus ordinary id suffix covers the schemas that use a
356
+ * pattern to communicate a typed identifier. Every candidate is checked by
357
+ * the actual RegExp before it is returned. */
358
+ function exampleMatchingPattern(pattern, minLength, maxLength) {
359
+ let regex;
360
+ try {
361
+ regex = new RegExp(pattern);
362
+ }
363
+ catch {
364
+ return null;
365
+ }
366
+ const match = /^\^((?:\\.|[A-Za-z0-9_-])+)/.exec(pattern);
367
+ const prefix = match?.[1]?.replace(/\\(.)/g, "$1") ?? "";
368
+ const fit = (candidate) => {
369
+ let value = candidate;
370
+ while (value.length < minLength)
371
+ value += "x";
372
+ if (maxLength !== undefined)
373
+ value = value.slice(0, maxLength);
374
+ return value;
375
+ };
376
+ const candidates = [prefix + "123", prefix + "example", prefix, "example", "value"];
377
+ for (const candidate of candidates) {
378
+ const value = fit(candidate);
379
+ regex.lastIndex = 0;
380
+ if (regex.test(value))
381
+ return value;
382
+ }
383
+ return null;
384
+ }
340
385
  export function exampleArgumentsFromSchema(inputSchema) {
341
386
  const value = exampleFromSchema(inputSchema, "arguments");
342
387
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
@@ -400,7 +445,7 @@ export const EXECUTE_TOOL = {
400
445
  inputSchema: {
401
446
  type: "object",
402
447
  properties: {
403
- operation: { type: "string", description: "Operation tool name, e.g. accounts_create" },
448
+ operation: { type: "string", description: "Operation tool name returned by search_docs" },
404
449
  arguments: { type: "object", description: "Operation arguments keyed by parameter name" },
405
450
  confirm: { type: "boolean", description: "Required and must be true for destructive operations. Omit for reads and ordinary writes." },
406
451
  },
@@ -433,36 +478,23 @@ export function parseIncludeList(value) {
433
478
  * three-tool "meta" shape (search_docs, read_docs, execute) that keeps huge
434
479
  * APIs from flooding an agent's context. Deterministic order.
435
480
  */
436
- export function toolDefinitions(ops, mode) {
481
+ export function toolDefinitions(ops, mode, omittedOps = []) {
437
482
  if (mode === "meta") {
483
+ const execute = structuredClone(EXECUTE_TOOL);
484
+ const operation = execute.inputSchema.properties.operation;
485
+ if (ops[0])
486
+ operation.examples = [ops[0].tool];
487
+ const coverage = omittedOps.length > 0
488
+ ? " Generated " + ops.length + " of " + (ops.length + omittedOps.length) + " operations; search_docs names operations omitted by the plan limit."
489
+ : "";
438
490
  return [
439
- { ...SEARCH_DOCS_TOOL, description: "Search this API's " + ops.length + " operations and, when a docs site is configured, its guides. Start here to find the operation you need." },
491
+ { ...SEARCH_DOCS_TOOL, description: "Search this API's " + ops.length + " generated operations and, when a docs site is configured, its guides. Start here to find the operation you need." + coverage },
440
492
  { ...READ_DOCS_TOOL, description: "Read an operation's full reference (arguments, schemas, authentication, safety and example) by tool name, or a docs-site guide page." },
441
- EXECUTE_TOOL,
493
+ execute,
442
494
  ];
443
495
  }
444
496
  return [...ops.map(operationTool), SEARCH_DOCS_TOOL, READ_DOCS_TOOL];
445
497
  }
446
- /** How big a tools/list is, as agents pay for it. Characters of JSON and a
447
- * rough token count (one token per four characters, the usual estimate for
448
- * JSON). Generation and `<bin> mcp` report it. */
449
- export function toolsListSize(tools) {
450
- const chars = JSON.stringify(tools).length;
451
- return { tools: tools.length, chars, approxTokens: Math.round(chars / 4) };
452
- }
453
- /** Keep automatic per-operation discovery under roughly 10k tokens. The
454
- * schema, not merely the operation count, determines what an agent pays. */
455
- export const MCP_AUTO_MAX_TOOLS_LIST_CHARS = 40_000;
456
- export function resolveToolMode(ops, requested = "auto") {
457
- if (requested === "operations" || requested === "meta") {
458
- return { mode: requested, ...toolsListSize(toolDefinitions(ops, requested)) };
459
- }
460
- const operations = toolsListSize(toolDefinitions(ops, "operations"));
461
- const mode = ops.length > 100 || operations.chars > MCP_AUTO_MAX_TOOLS_LIST_CHARS ? "meta" : "operations";
462
- return mode === "operations"
463
- ? { mode, ...operations }
464
- : { mode, ...toolsListSize(toolDefinitions(ops, mode)) };
465
- }
466
498
  /** Resolve an operation by tool name or dotted resource.method. */
467
499
  export function findOperation(ops, wanted) {
468
500
  return ops.find((o) => o.tool === wanted)
@@ -480,6 +512,10 @@ export function serverInstructions(input) {
480
512
  parts.push(input.mode === "meta"
481
513
  ? input.title + " as MCP tools: search_docs, read_docs and execute over " + input.toolCount + " operations. Start with search_docs to find an operation, read_docs <tool> for its full argument reference, then execute it by name. Destructive operations require confirm: true on execute."
482
514
  : input.title + " as MCP tools: one tool per operation (" + input.toolCount + "), plus search_docs to find operations and read_docs <tool> for an operation's full argument reference.");
515
+ if (input.omittedOps?.length) {
516
+ const generated = input.generatedOperationCount ?? input.toolCount;
517
+ parts.push("Plan-limited generation: generated " + generated + " of " + (generated + input.omittedOps.length) + " operations. Omitted operations: " + input.omittedOps.map((op) => op.tool + " (" + op.httpMethod + " " + op.path + ")").join(", ") + ". Calling or searching for one returns PLAN_LIMIT with upgrade next_steps.");
518
+ }
483
519
  parts.push("Arguments use the API's wire names; an unknown, mistyped or missing argument returns an isError result listing each problem (nothing is dropped silently), and obvious forms are coerced (\"true\" to boolean, \"3\" to number, enum case).");
484
520
  parts.push("Paginated tools return items, hasMore and nextPage (the exact arguments for the following page). Pass fields (dotted paths) to keep only the result keys you need; oversized results are cut to whole items or keys with a truncated note saying how to ask for less.");
485
521
  parts.push("Errors carry error, code, message, status, the API's body and next_steps.");
@@ -786,12 +822,17 @@ export function pageOutcome(items, nextPage, options = {}) {
786
822
  const fields = options.fields ?? null;
787
823
  const maxChars = options.maxChars ?? DEFAULT_MAX_RESULT_CHARS;
788
824
  const shown = projectFields(items, fields);
789
- const full = { items: shown, hasMore: nextPage !== null, ...(nextPage !== null ? { nextPage } : {}) };
825
+ const full = {
826
+ items: shown,
827
+ hasMore: nextPage !== null,
828
+ ...(nextPage !== null ? { nextPage } : {}),
829
+ ...(options.requestId ? { request_id: options.requestId } : {}),
830
+ };
790
831
  const text = JSON.stringify(full);
791
832
  if (text.length <= maxChars) {
792
833
  return { text, isError: false, structured: full };
793
834
  }
794
- const overhead = JSON.stringify({ items: [], hasMore: true, nextPage: nextPage ?? {}, truncated: { omitted: 0, of: 0, reason: "x".repeat(160), next_steps: ["x".repeat(220), "x".repeat(120)] } }).length;
835
+ const overhead = JSON.stringify({ items: [], hasMore: true, nextPage: nextPage ?? {}, ...(options.requestId ? { request_id: options.requestId } : {}), truncated: { omitted: 0, of: 0, reason: "x".repeat(160), next_steps: ["x".repeat(220), "x".repeat(120)] } }).length;
795
836
  const k = itemsThatFit(shown, Math.max(0, maxChars - overhead));
796
837
  const omitted = shown.length - k;
797
838
  const pg = options.pagination;
@@ -818,6 +859,7 @@ export function pageOutcome(items, nextPage, options = {}) {
818
859
  items: shown.slice(0, k),
819
860
  hasMore: resume !== null ? true : nextPage !== null,
820
861
  ...(resume !== null ? { nextPage: resume } : nextPage !== null ? { nextPage } : {}),
862
+ ...(options.requestId ? { request_id: options.requestId } : {}),
821
863
  truncated: {
822
864
  omitted,
823
865
  of: shown.length,
@@ -961,6 +1003,7 @@ export function classifyError(error, context = {}) {
961
1003
  return { code: "NETWORK_ERROR", nextSteps: ["The API could not be reached (network, DNS, TLS or timeout). Retry once with backoff; do not loop."] };
962
1004
  }
963
1005
  const status = typeof e.status === "number" ? e.status : 0;
1006
+ const body = e.body;
964
1007
  const auth = context.authHint ? context.authHint.trim().replace(/[.]?$/, ".") : null;
965
1008
  if (status === 401) {
966
1009
  return context.hadCredential
@@ -977,6 +1020,15 @@ export function classifyError(error, context = {}) {
977
1020
  const retryAfter = extractRetryAfter(e);
978
1021
  return { code: "RATE_LIMITED", nextSteps: [retryAfter ? "Wait " + retryAfter + " seconds, then call again." : "Back off and retry once; the request was already retried with the server's Retry-After."] };
979
1022
  }
1023
+ if (status === 422 && body?.errors?.[0]?.code === "spec_error") {
1024
+ return {
1025
+ code: "SPEC_INVALID",
1026
+ nextSteps: [
1027
+ "The API rejected the spec it was given; its error message names the invalid part.",
1028
+ context.docsUrl ? "Use search_docs with the error message, fix the spec, then call again." : "Fix the spec, then call again.",
1029
+ ],
1030
+ };
1031
+ }
980
1032
  if (status === 400 || status === 409 || status === 413 || status === 422) {
981
1033
  return { code: "INVALID_REQUEST", nextSteps: ["Read body for the field the API named, fix that argument and call again."] };
982
1034
  }
@@ -1003,11 +1055,16 @@ function notFoundNextSteps(message, body) {
1003
1055
  export function errorOutcome(error, context = {}) {
1004
1056
  const e = error;
1005
1057
  const { code, nextSteps } = classifyError(error, context);
1058
+ const bodyRequestId = e?.body && typeof e.body === "object" && !Array.isArray(e.body)
1059
+ ? e.body.request_id ?? e.body.requestId
1060
+ : undefined;
1061
+ const requestId = e?.response?.requestId ?? (typeof bodyRequestId === "string" ? bodyRequestId : undefined);
1006
1062
  const structured = {
1007
1063
  error: e?.name ?? "Error",
1008
1064
  code,
1009
1065
  message: e?.message,
1010
1066
  ...(typeof e?.status === "number" ? { status: e.status } : {}),
1067
+ ...(requestId ? { request_id: requestId } : {}),
1011
1068
  ...(e?.body !== undefined ? { body: e.body } : {}),
1012
1069
  ...(context.docsUrl ? { docs_url: context.docsUrl } : {}),
1013
1070
  next_steps: nextSteps,
@@ -1018,32 +1075,29 @@ export function textError(text, code = "CALL_FAILED", nextSteps = []) {
1018
1075
  const structured = { error: "Error", code, message: text, next_steps: nextSteps };
1019
1076
  return { text: JSON.stringify(structured), isError: true, structured };
1020
1077
  }
1021
- function docsPageUrl(source, pathOrFile) {
1022
- const base = source.docsUrl();
1023
- if (base === null)
1024
- return null;
1025
- try {
1026
- const baseUrl = new URL(base);
1027
- if (baseUrl.protocol !== "https:" && baseUrl.protocol !== "http:")
1028
- return null;
1029
- const target = /^https?:\/\//.test(pathOrFile)
1030
- ? new URL(pathOrFile)
1031
- : new URL(pathOrFile.replace(/^\/+/, ""), baseUrl.toString().replace(/\/+$/, "") + "/");
1032
- // llms.txt commonly contains absolute links, but a docs tool is not a
1033
- // general-purpose URL fetcher. Keeping every page on the configured
1034
- // origin prevents an agent from turning hosted MCP into an SSRF proxy.
1035
- if (target.origin !== baseUrl.origin || target.username || target.password)
1036
- return null;
1037
- return target.toString();
1038
- }
1039
- catch {
1040
- return null;
1041
- }
1042
- }
1043
1078
  async function fetchDocs(source, pathOrFile) {
1044
- const url = docsPageUrl(source, pathOrFile);
1079
+ const url = resolveDocsContentUrl(source.docsUrl(), source.docsIndexUrl?.() ?? null, pathOrFile);
1045
1080
  return url === null ? null : source.fetchText(url);
1046
1081
  }
1082
+ function coverageText(source) {
1083
+ const omitted = source.omittedOps ?? [];
1084
+ const generated = source.generatedOperationCount ?? source.ops.length;
1085
+ return omitted.length > 0
1086
+ ? "Coverage: generated " + generated + " of " + (generated + omitted.length) + " operations. Omitted by the plan limit: " + omitted.map((op) => op.tool + " (" + op.httpMethod + " " + op.path + ")").join(", ") + "."
1087
+ : null;
1088
+ }
1089
+ function omittedPlanLimit(ops, requested) {
1090
+ const structured = {
1091
+ error: "PlanLimitError",
1092
+ code: "PLAN_LIMIT",
1093
+ message: requested
1094
+ ? "The operation " + requested + " exists in the API Definition but was omitted from this generated package by its plan limit."
1095
+ : "Matching operations exist in the API Definition but were omitted from this generated package by its plan limit.",
1096
+ omitted_operations: ops.map((op) => ({ tool: op.tool, method: op.httpMethod, path: op.path })),
1097
+ next_steps: ["Upgrade at https://typeship.dev/pricing and regenerate the package without the operation cap.", "Do not invent or retry an omitted operation against this generated package."],
1098
+ };
1099
+ return { text: JSON.stringify(structured), isError: true, structured };
1100
+ }
1047
1101
  export function referenceText(op) {
1048
1102
  const safety = operationSafety(op);
1049
1103
  const example = op.exampleArguments ?? exampleArgumentsFromSchema(op.inputSchema);
@@ -1114,12 +1168,20 @@ export function searchScore(op, query) {
1114
1168
  return score;
1115
1169
  }
1116
1170
  export async function docsSearch(source, query, page = 1) {
1117
- const term = query.toLowerCase();
1171
+ const term = query.trim().toLowerCase();
1172
+ const terms = searchTerms(query);
1118
1173
  const sections = [];
1119
1174
  const ranked = source.ops
1120
1175
  .map((op) => ({ op, score: searchScore(op, query) }))
1121
1176
  .filter((r) => r.score > 0)
1122
1177
  .sort((a, b) => b.score - a.score || a.op.tool.localeCompare(b.op.tool));
1178
+ const omittedRanked = (source.omittedOps ?? [])
1179
+ .map((op) => ({ op, score: searchScore(op, query) }))
1180
+ .filter((result) => result.score > 0)
1181
+ .sort((a, b) => b.score - a.score || a.op.tool.localeCompare(b.op.tool));
1182
+ const exactOmitted = findOperation(source.omittedOps ?? [], query);
1183
+ if (exactOmitted)
1184
+ return omittedPlanLimit([exactOmitted], exactOmitted.tool);
1123
1185
  const pageIndex = Math.max(1, Math.floor(page)) - 1;
1124
1186
  const slice = ranked.slice(pageIndex * SEARCH_PAGE_SIZE, (pageIndex + 1) * SEARCH_PAGE_SIZE);
1125
1187
  if (slice.length > 0) {
@@ -1132,31 +1194,56 @@ export async function docsSearch(source, query, page = 1) {
1132
1194
  sections.push("No reference matches on page " + (pageIndex + 1) + "; there are " + Math.ceil(ranked.length / SEARCH_PAGE_SIZE) + " pages.");
1133
1195
  }
1134
1196
  const prose = await fetchDocs(source, "llms-full.txt");
1197
+ let proseMatchCount = 0;
1135
1198
  if (prose !== null) {
1136
1199
  let heading = "";
1137
1200
  const proseMatches = [];
1138
1201
  for (const line of prose.split("\n")) {
1139
1202
  if (/^#{1,3} /.test(line))
1140
1203
  heading = line.replace(/^#+ /, "").trim();
1141
- else if (line.toLowerCase().includes(term) && proseMatches.length < 15) {
1142
- proseMatches.push("- [" + heading + "] " + line.trim().slice(0, 160));
1204
+ else {
1205
+ const lowerHeading = heading.toLowerCase();
1206
+ const lowerLine = line.toLowerCase();
1207
+ const matched = terms.filter((word) => lowerHeading.includes(word) || lowerLine.includes(word));
1208
+ if (matched.length > 0) {
1209
+ const allTerms = matched.length === terms.length;
1210
+ proseMatches.push({
1211
+ heading,
1212
+ excerpt: line.trim().slice(0, 160),
1213
+ score: matched.length * 10 + (allTerms ? 50 : 0) + (lowerHeading.includes(term) || lowerLine.includes(term) ? 25 : 0),
1214
+ });
1215
+ }
1143
1216
  }
1144
1217
  }
1145
- if (proseMatches.length > 0)
1146
- sections.push("Guide matches:\n" + proseMatches.join("\n"));
1218
+ proseMatches.sort((a, b) => b.score - a.score || a.heading.localeCompare(b.heading) || a.excerpt.localeCompare(b.excerpt));
1219
+ proseMatchCount = proseMatches.length;
1220
+ if (proseMatches.length > 0) {
1221
+ sections.push("Guide matches (best first):\n" + proseMatches.slice(0, 15).map((match) => "- [" + match.heading + "] " + match.excerpt).join("\n"));
1222
+ }
1223
+ }
1224
+ if (omittedRanked.length > 0 && ranked.length === 0 && proseMatchCount === 0) {
1225
+ return omittedPlanLimit(omittedRanked.map((result) => result.op));
1147
1226
  }
1227
+ if (omittedRanked.length > 0) {
1228
+ sections.push("Operations omitted by the plan limit:\n" + omittedRanked.slice(0, SEARCH_PAGE_SIZE).map((result) => "- " + result.op.tool + ": " + (result.op.summary ?? result.op.httpMethod + " " + result.op.path)).join("\n"));
1229
+ }
1230
+ const coverage = coverageText(source);
1148
1231
  if (sections.length === 0) {
1149
1232
  return {
1150
- text: "No matches for: " + query + (source.docsUrl() === null ? " (no docs site configured; only the API reference was searched)" : ""),
1233
+ text: (coverage ? coverage + "\n\n" : "") + "No matches for: " + query + (source.docsUrl() === null && (source.docsIndexUrl?.() ?? null) === null ? " (a docs URL was not provided at generate time; only the API reference was searched)" : ""),
1151
1234
  isError: false,
1152
1235
  };
1153
1236
  }
1154
- return { text: sections.join("\n\n"), isError: false };
1237
+ return { text: [...(coverage ? [coverage] : []), ...sections].join("\n\n"), isError: false };
1155
1238
  }
1156
1239
  export async function docsRead(source, page) {
1157
1240
  const opMatch = findOperation(source.ops, page);
1241
+ const coverage = coverageText(source);
1158
1242
  if (opMatch)
1159
- return { text: referenceText(opMatch), isError: false };
1243
+ return { text: [...(coverage ? [coverage] : []), referenceText(opMatch)].join("\n\n"), isError: false };
1244
+ const omittedMatch = findOperation(source.omittedOps ?? [], page);
1245
+ if (omittedMatch)
1246
+ return omittedPlanLimit([omittedMatch], omittedMatch.tool);
1160
1247
  let target = page;
1161
1248
  if (!/^https?:\/\//.test(target)) {
1162
1249
  const index = await fetchDocs(source, "llms.txt");
@@ -1167,11 +1254,11 @@ export async function docsRead(source, page) {
1167
1254
  }
1168
1255
  const text = await fetchDocs(source, target);
1169
1256
  if (text === null) {
1170
- return textError(source.docsUrl() === null
1171
- ? "No docs site is configured for this server, and no operation matches \"" + page + "\"."
1257
+ return textError(source.docsUrl() === null && (source.docsIndexUrl?.() ?? null) === null
1258
+ ? "A docs URL was not provided at generate time, and no generated operation matches \"" + page + "\"."
1172
1259
  : "Couldn't fetch \"" + page + "\". Use search_docs to find pages.", "NOT_FOUND", ["search_docs finds operations and guide pages."]);
1173
1260
  }
1174
- return { text, isError: false };
1261
+ return { text: [...(coverage ? [coverage] : []), text].join("\n\n"), isError: false };
1175
1262
  }
1176
1263
  /**
1177
1264
  * Dispatch for the shared tools (search_docs, read_docs, execute); returns
@@ -1193,8 +1280,12 @@ export async function callSharedTool(name, args, source, runOperation) {
1193
1280
  if (typeof args.operation !== "string")
1194
1281
  return argumentsError({ tool: name }, [{ code: "MISSING_ARGUMENT", argument: "operation", message: "execute requires an operation name." }]);
1195
1282
  const target = findOperation(source.ops, args.operation);
1196
- if (!target)
1197
- return textError("Unknown operation: " + args.operation + ".", "NOT_FOUND", ["search_docs finds operations by name, path or description."]);
1283
+ if (!target) {
1284
+ const omitted = findOperation(source.omittedOps ?? [], args.operation);
1285
+ return omitted
1286
+ ? omittedPlanLimit([omitted], omitted.tool)
1287
+ : textError("Unknown operation: " + args.operation + ".", "NOT_FOUND", ["search_docs finds operations by name, path or description."]);
1288
+ }
1198
1289
  if (operationSafety(target) === "destructive" && args.confirm !== true) {
1199
1290
  return textError("The destructive operation " + target.tool + " requires explicit confirmation.", "CONFIRMATION_REQUIRED", ["Review read_docs " + target.tool + ", then retry execute with confirm: true if the destructive effect is intended."]);
1200
1291
  }
@@ -1205,58 +1296,3 @@ export async function callSharedTool(name, args, source, runOperation) {
1205
1296
  }
1206
1297
  return undefined;
1207
1298
  }
1208
- /** A fetch for docs pages: markdown preferred, same-origin redirects only,
1209
- * a 10s deadline, and a 2 MB streaming cap. The caller already constrained
1210
- * the first URL to its configured docs origin; redirects must not escape it. */
1211
- export const MAX_DOCS_TEXT_BYTES = 2_000_000;
1212
- export async function fetchDocsText(url) {
1213
- try {
1214
- const allowedOrigin = new URL(url).origin;
1215
- let current = url;
1216
- const signal = AbortSignal.timeout(10_000);
1217
- for (let redirects = 0; redirects <= 3; redirects += 1) {
1218
- const response = await fetch(current, {
1219
- headers: { Accept: "text/markdown, text/plain, */*" },
1220
- redirect: "manual",
1221
- signal,
1222
- });
1223
- if (response.status >= 300 && response.status < 400) {
1224
- const location = response.headers.get("location");
1225
- if (!location || redirects === 3)
1226
- return null;
1227
- const next = new URL(location, current);
1228
- if (next.origin !== allowedOrigin || next.username || next.password)
1229
- return null;
1230
- current = next.toString();
1231
- continue;
1232
- }
1233
- if (!response.ok)
1234
- return null;
1235
- const declared = Number(response.headers.get("content-length"));
1236
- if (Number.isFinite(declared) && declared > MAX_DOCS_TEXT_BYTES)
1237
- return null;
1238
- if (!response.body)
1239
- return "";
1240
- const reader = response.body.getReader();
1241
- const decoder = new TextDecoder();
1242
- let bytes = 0;
1243
- let text = "";
1244
- for (;;) {
1245
- const chunk = await reader.read();
1246
- if (chunk.done)
1247
- break;
1248
- bytes += chunk.value.byteLength;
1249
- if (bytes > MAX_DOCS_TEXT_BYTES) {
1250
- await reader.cancel();
1251
- return null;
1252
- }
1253
- text += decoder.decode(chunk.value, { stream: true });
1254
- }
1255
- return text + decoder.decode();
1256
- }
1257
- return null;
1258
- }
1259
- catch {
1260
- return null;
1261
- }
1262
- }
package/dist/mcp.js CHANGED
@@ -20,12 +20,13 @@ import { homedir, tmpdir } from "node:os";
20
20
  import { basename, join } from "node:path";
21
21
  import { fileURLToPath } from "node:url";
22
22
  import { TypeshipClient, formatDebugEvent } from "./index.js";
23
- import { GLOBALS, OPS, buildArgs } from "./ops.js";
24
- import { DEFAULT_MAX_RESULT_CHARS, SUPPORTED_PROTOCOL_VERSIONS, argumentsError, asJsonRpc, binaryOutcome, callSharedTool, checkRequestHeaders, dataOutcome, errorOutcome, fetchDocsText, handleRpc, isRpcOutcome, pageOutcome, parseIncludeList, prepareCall, serverInstructions, takeCancelled, textError, toolDefinitions, visibleOps, } from "./mcp-protocol.js";
23
+ import { GLOBALS, OMITTED_OPS, OPS, buildArgs } from "./ops.js";
24
+ import { DEFAULT_MAX_RESULT_CHARS, SUPPORTED_PROTOCOL_VERSIONS, argumentsError, asJsonRpc, binaryOutcome, callSharedTool, checkRequestHeaders, dataOutcome, errorOutcome, handleRpc, isRpcOutcome, pageOutcome, parseIncludeList, prepareCall, serverInstructions, takeCancelled, textError, toolDefinitions, visibleOps, } from "./mcp-protocol.js";
25
+ import { fetchDocsText } from "./docs.js";
25
26
  const BIN = "typeship";
26
27
  const PKG_NAME = "@typeship-ax/mcp";
27
28
  const SERVER_NAME = "typeship-mcp";
28
- const SERVER_VERSION = "0.6.0";
29
+ const SERVER_VERSION = "0.8.0";
29
30
  /** The MCP client's announced name (clientInfo in request _meta), for the User-Agent. */
30
31
  let MCP_CLIENT_NAME = null;
31
32
  function noteClientInfo(message) {
@@ -39,9 +40,9 @@ function noteClientInfo(message) {
39
40
  const DEFAULT_BASE_URL = "https://typeship.dev/api/v1";
40
41
  const AUTH_SCALARS = [{ "option": "bearerToken", "flag": "token", "env": "TYPESHIP_TOKEN" }];
41
42
  const BASIC = null;
42
- const OAUTH_TOKEN_URL = null;
43
43
  const ENVIRONMENTS = {};
44
44
  const DOCS_URL_DEFAULT = "https://typeship.dev";
45
+ const DOCS_INDEX_URL_DEFAULT = null;
45
46
  /** "meta" collapses per-operation tools into search/read/execute so huge
46
47
  * APIs don't flood agent context with hundreds of tools. */
47
48
  const TOOL_MODE = "meta";
@@ -78,7 +79,6 @@ function readJson(file) {
78
79
  function makeClient() {
79
80
  const stored = readJson("credentials.json");
80
81
  const config = readJson("config.json");
81
- const options = {};
82
82
  const baseUrl = process.env["TYPESHIP_BASE_URL"]
83
83
  ?? config?.baseUrl
84
84
  ?? (config?.environment !== undefined ? ENVIRONMENTS[config.environment] : undefined)
@@ -88,7 +88,7 @@ function makeClient() {
88
88
  // instead of a server that vanished mid-conversation.
89
89
  throw new Error("No base URL configured: set TYPESHIP_BASE_URL in the MCP server's environment, or run '" + BIN + " config set base-url <url>'.");
90
90
  }
91
- options.baseUrl = baseUrl;
91
+ const options = { baseUrl };
92
92
  for (const a of AUTH_SCALARS) {
93
93
  const v = process.env[a.env] ?? stored?.scalars?.[a.option];
94
94
  if (v !== undefined)
@@ -100,17 +100,6 @@ function makeClient() {
100
100
  if (username !== undefined && password !== undefined)
101
101
  options.basicAuth = { username, password };
102
102
  }
103
- const oauthClientId = process.env["TYPESHIP_CLIENT_ID"];
104
- const oauthClientSecret = process.env["TYPESHIP_CLIENT_SECRET"];
105
- if ((oauthClientId === undefined) !== (oauthClientSecret === undefined)) {
106
- throw new Error("OAuth client credentials are incomplete: set both TYPESHIP_CLIENT_ID and TYPESHIP_CLIENT_SECRET in the MCP server's environment.");
107
- }
108
- if (oauthClientId !== undefined && oauthClientSecret !== undefined) {
109
- const tokenUrl = process.env["TYPESHIP_TOKEN_URL"] ?? OAUTH_TOKEN_URL ?? undefined;
110
- if (!tokenUrl)
111
- throw new Error("OAuth client credentials need a token URL: set TYPESHIP_TOKEN_URL in the MCP server's environment.");
112
- options.clientCredentials = { clientId: oauthClientId, clientSecret: oauthClientSecret, tokenUrl };
113
- }
114
103
  if (options.bearerToken === undefined && stored?.oauth?.accessToken !== undefined) {
115
104
  options.bearerToken = stored.oauth.accessToken;
116
105
  }
@@ -187,7 +176,7 @@ async function callOperation(op, rawArgs, authHeader) {
187
176
  return errorOutcome(result.error, errorContext);
188
177
  if (op.paginated) {
189
178
  const page = result.data;
190
- return pageOutcome(page.items, page.nextPageParams(), shape);
179
+ return pageOutcome(page.items, page.nextPageParams(), { ...shape, requestId: page.response.requestId });
191
180
  }
192
181
  // A binary body (the SDK hands back a Blob): an image block, or a file
193
182
  // on disk, never "{}".
@@ -224,7 +213,10 @@ function saveBinary(bytes, _mediaType, suggestedName) {
224
213
  }
225
214
  const docsSource = {
226
215
  ops: MCP_OPS,
216
+ omittedOps: OMITTED_OPS,
217
+ generatedOperationCount: OPS.length,
227
218
  docsUrl: () => readJson("config.json")?.docsUrl ?? DOCS_URL_DEFAULT,
219
+ docsIndexUrl: () => readJson("config.json")?.docsUrl ? null : DOCS_INDEX_URL_DEFAULT,
228
220
  fetchText: fetchDocsText,
229
221
  };
230
222
  /** The MCP server for one request context (the HTTP transport passes the
@@ -236,6 +228,8 @@ function serverFor(authHeader) {
236
228
  instructions: serverInstructions({
237
229
  title: "typeship",
238
230
  toolCount: MCP_OPS.length,
231
+ generatedOperationCount: OPS.length,
232
+ omittedOps: docsSource.omittedOps,
239
233
  mode: TOOL_MODE,
240
234
  readOnly: READ_ONLY,
241
235
  hiddenWrites: HIDDEN_WRITES,
@@ -245,7 +239,10 @@ function serverFor(authHeader) {
245
239
  custom: CUSTOM_INSTRUCTIONS,
246
240
  }),
247
241
  toolsTtlMs: TOOLS_TTL_MS,
248
- listTools: () => toolDefinitions(docsSource.ops, TOOL_MODE),
242
+ listTools: () => toolDefinitions(docsSource.ops, TOOL_MODE, docsSource.omittedOps),
243
+ unknownToolMessage: TOOL_MODE === "meta"
244
+ ? (name) => "Unknown tool: " + name + ". This server uses compact mode; call search_docs to discover an operation, then call execute with operation: \"" + name + "\"."
245
+ : undefined,
249
246
  callTool: async (name, args) => {
250
247
  const shared = await callSharedTool(name, args, docsSource, (op, opArgs) => callOperation(op, opArgs, authHeader));
251
248
  if (shared !== undefined)
package/dist/ops.d.ts CHANGED
@@ -96,6 +96,11 @@ export interface OpSpec {
96
96
  outputSchema?: Record<string, unknown>;
97
97
  }
98
98
  export declare const OPS: OpSpec[];
99
+ /** Operations present in the API Definition but omitted by this
100
+ * generation's operation cap. Kept deliberately small: enough for honest
101
+ * discovery and PLAN_LIMIT errors, never enough to call an absent method. */
102
+ export type OmittedOpSpec = Pick<OpSpec, "resource" | "method" | "command" | "commandAlias" | "tool" | "httpMethod" | "path" | "summary" | "description" | "toolDescription" | "paginated" | "select" | "safety" | "auth" | "params" | "inputSchema">;
103
+ export declare const OMITTED_OPS: OmittedOpSpec[];
99
104
  /** Parameters settable once on the client (globals option): CLI/MCP fill
100
105
  * them from flags or environment; the SDK falls back per request. */
101
106
  export declare const GLOBALS: {
package/dist/ops.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../src/ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../src/ops.ts"],"names":[],"mappings":"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"}