History

Update executor version to 1.1.6, enhance tests, and add new source templates

- Bump version in package.json - Add tests for completed MCP return values in server.test.ts - Introduce new source templates for Neon MCP and Neon API in source-templates.ts - Refactor OpenAPI response handling in openapi-tools.ts and related tests - Improve result preview formatting in executor-mcp

Rhys Sullivan committed 8 months ago

Commit 9f5d7cf1 parent cc270618 files · +179 −125Browse files

Showing the changes to packages/codemode-openapi/src (2 of 8 files). Show all

Unified · Split

2 files
  1. packages/codemode-openapi/src/openapi-tools.test.ts +56 −37
  2. packages/codemode-openapi/src/openapi-tools.ts +31 −64
packages/codemode-openapi/src/openapi-tools.test.ts+56−37
@@ -305,13 +305,8 @@ describe("openapi-tools", () => { · 304 unmodified lines
305305 );
306306
307307 expect(result).toEqual({
308− ok: true,
309− status: 200,
310− headers: expect.any(Object),
311− body: {
312− full_name: "octocat/hello-world",
313− include: null,
314− },
308+ full_name: "octocat/hello-world",
309+ include: null,
315310 });
316311 expect(server.requests).toHaveLength(1);
317312 expect(server.requests[0]?.path).toBe("/repos/octocat/hello-world");
@@ -340,13 +335,8 @@ describe("openapi-tools", () => { · 22 unmodified lines
340335 );
341336
342337 expect(getResult).toEqual({
343− ok: true,
344− status: 200,
345− headers: expect.any(Object),
346− body: {
347− full_name: "octocat/hello-world",
348− include: "all",
349− },
338+ full_name: "octocat/hello-world",
339+ include: "all",
350340 });
351341
352342 const postResult = yield* Effect.promise(() =>
@@ -358,15 +348,10 @@ describe("openapi-tools", () => { · 5 unmodified lines
358348 );
359349
360350 expect(postResult).toEqual({
361− ok: true,
362− status: 201,
363− headers: expect.any(Object),
364− body: {
365− created: true,
366− owner: "octocat",
367− repo: "hello-world",
368− body: { title: "Bug report" },
369− },
351+ created: true,
352+ owner: "octocat",
353+ repo: "hello-world",
354+ body: { title: "Bug report" },
370355 });
371356
372357 const missingBody = yield* Effect.either(
@@ -398,6 +383,50 @@ describe("openapi-tools", () => { · 25 unmodified lines
398383 }),
399384 );
400385
386+ it.effect("throws on non-2xx OpenAPI responses", () =>
387+ Effect.gen(function* () {
388+ const manifest = yield* extractOpenApiManifest("generated", generatedOpenApiSpec);
389+ const httpClientLayer = Layer.succeed(
390+ HttpClient.HttpClient,
391+ HttpClient.make((request) =>
392+ Effect.sync(() =>
393+ HttpClientResponse.fromWeb(
394+ request,
395+ new Response(
396+ JSON.stringify({ error: "missing" }),
397+ {
398+ status: 404,
399+ headers: {
400+ "content-type": "application/json",
401+ },
402+ },
403+ ),
404+ ),
405+ ),
406+ ),
407+ );
408+ const tools = createOpenApiToolsFromManifest({
409+ manifest,
410+ baseUrl: "https://example.com",
411+ namespace: "source.generated",
412+ httpClientLayer,
413+ });
414+ const getRepo = resolveToolExecutor(tools, "source.generated.repos.getRepo");
415+ const failure = yield* Effect.either(
416+ Effect.tryPromise({
417+ try: () => getRepo({ owner: "octocat", repo: "hello-world" }),
418+ catch: (error: unknown) =>
419+ error instanceof Error ? error : new Error(String(error)),
420+ }),
421+ );
422+
423+ assertTrue(failure._tag === "Left");
424+ if (failure._tag === "Left" && failure.left instanceof Error) {
425+ assertInclude(failure.left.message, "HTTP 404");
426+ }
427+ }),
428+ );
429+
401430 it.effect("preserves base-path URLs when invoking tools", () =>
402431 Effect.gen(function* () {
403432 const manifest = yield* extractOpenApiManifest("generated", generatedOpenApiSpec);
@@ -438,13 +467,8 @@ describe("openapi-tools", () => { · 34 unmodified lines
438467 );
439468
440469 expect(result).toEqual({
441− ok: true,
442− status: 200,
443− headers: expect.any(Object),
444− body: {
445− full_name: "octocat/hello-world",
446− include: null,
447− },
470+ full_name: "octocat/hello-world",
471+ include: null,
448472 });
449473
450474 expect(capturedUrl).toBe("https://example.com/api/v3/repos/octocat/hello-world");
@@ -488,13 +512,8 @@ describe("openapi-tools", () => { · 37 unmodified lines
488512 );
489513
490514 expect(result).toEqual({
491− ok: true,
492− status: 200,
493− headers: expect.any(Object),
494− body: {
495− full_name: "octocat/hello-world",
496− include: null,
497− },
515+ full_name: "octocat/hello-world",
516+ include: null,
498517 });
499518 expect(server.requests).toHaveLength(1);
500519 expect(server.requests[0]?.path).toBe("/repos/octocat/hello-world");
packages/codemode-openapi/src/openapi-tools.ts+31−64
@@ -40,44 +40,6 @@ export class OpenApiToolInvocationError extends Data.TaggedError( · 39 unmodified lines
4040 details: string | null;
4141 }> {}
4242
43−const BLOCKED_RESPONSE_HEADER_NAMES = new Set([
44− "authorization",
45− "authentication-info",
46− "cookie",
47− "proxy-authenticate",
48− "proxy-authorization",
49− "set-cookie",
50− "set-cookie2",
51− "www-authenticate",
52− "x-api-key",
53− "x-auth-token",
54− "x-csrf-token",
55−]);
56−
57−const NOISY_RESPONSE_HEADER_NAMES = new Set([
58− "alt-svc",
59− "cf-ray",
60− "server",
61− "traceparent",
62− "tracestate",
63− "via",
64− "x-cache",
65− "x-cache-hits",
66− "x-powered-by",
67− "x-request-id",
68− "x-runtime",
69− "x-served-by",
70− "x-trace-id",
71−]);
72−
73−const NOISY_RESPONSE_HEADER_PREFIXES = [
74− "cf-",
75− "trace-",
76− "x-amz-cf-",
77− "x-b3-",
78− "x-cloud-trace-",
79−];
80−
8143 const isRecord = (value: unknown): value is Record<string, unknown> =>
8244 typeof value === "object" && value !== null && !Array.isArray(value);
8345
@@ -244,27 +206,7 @@ const normalizeHttpUrl = (value: string): string => { · 160 unmodified lines
244206 }
245207 };
246208
247−const shouldDropResponseHeader = (headerName: string): boolean =>
248− BLOCKED_RESPONSE_HEADER_NAMES.has(headerName)
249− || NOISY_RESPONSE_HEADER_NAMES.has(headerName)
250− || NOISY_RESPONSE_HEADER_PREFIXES.some((prefix) => headerName.startsWith(prefix));
251−
252−const sanitizeResponseHeaders = (
253− headers: Readonly<Record<string, string>>,
254−): Record<string, string> => {
255− const sanitized: Record<string, string> = {};
256−
257− for (const [rawName, rawValue] of Object.entries(headers)) {
258− const name = rawName.trim().toLowerCase();
259− if (name.length === 0 || shouldDropResponseHeader(name)) {
260− continue;
261− }
262209
263− sanitized[name] = rawValue.length > 4000 ? `${rawValue.slice(0, 4000)}...` : rawValue;
264− }
265−
266− return sanitized;
267−};
268210
269211 const decodeHttpClientResponseBody = (
270212 response: Awaited<ReturnType<HttpClient.HttpClient["execute"]>> extends Effect.Effect<
@@ -292,6 +234,28 @@ const decodeHttpClientResponseBody = ( · 21 unmodified lines
292234 );
293235 };
294236
237+const summarizeHttpResponseBody = (body: unknown): string | null => {
238+ if (body === null || body === undefined) {
239+ return null;
240+ }
241+
242+ if (typeof body === "string") {
243+ const trimmed = body.trim();
244+ if (trimmed.length === 0) {
245+ return null;
246+ }
247+
248+ return trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed;
249+ }
250+
251+ try {
252+ const serialized = JSON.stringify(body);
253+ return serialized.length > 500 ? `${serialized.slice(0, 500)}...` : serialized;
254+ } catch {
255+ return String(body);
256+ }
257+};
258+
295259 const inputSchemaFromTypingJson = (inputSchemaJson: string | undefined) => {
296260 if (!inputSchemaJson) {
297261 return unknownInputSchema;
@@ -491,12 +455,15 @@ export const createOpenApiToolsFromManifest = ( · 193 unmodified lines
491455 );
492456 const body = yield* decodeHttpClientResponseBody(response);
493457
494− return {
495− ok: response.status >= 200 && response.status < 300,
496− status: response.status,
497− headers: sanitizeResponseHeaders(response.headers),
498− body,
499− };
458+ if (response.status < 200 || response.status >= 300) {
459+ throw new OpenApiToolInvocationError({
460+ operation: "http_response",
461+ message: `OpenAPI request failed with HTTP ${response.status}`,
462+ details: summarizeHttpResponseBody(body),
463+ });
464+ }
465+
466+ return body;
500467 }).pipe(
501468 Effect.provide(httpClientLayer),
502469 ),

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t