History

oauth

Rhys Sullivan committed 8 months ago

Commit d7a052e1 parent 08b6aa44 files · +66 −9Browse files

Unified · Split

4 files
  1. apps/executor/src/server/server.test.ts +50 −4
  2. apps/web/src/views/add-source.tsx +5 −1
  3. apps/web/src/views/source-editor.tsx +5 −3
  4. packages/control-plane/src/runtime/graphql-tools.ts +6 −1
apps/executor/src/server/server.test.ts+50−4
@@ -107,10 +107,12 @@ type ExecutorMcpClient = { · 106 unmodified lines
107107 close: () => Promise<void>;
108108 };
109109
110+type ElicitationHandler = NonNullable<Parameters<Client["setRequestHandler"]>[1]>;
111+
110112 const makeExecutorMcpClient = (input: {
111113 baseUrl: string;
112114 capabilities?: Record<string, unknown>;
113− onElicitation?: (request: { params: { message?: string } }) => Promise<unknown>;
115+ onElicitation?: ElicitationHandler;
114116 }) =>
115117 Effect.acquireRelease(
116118 Effect.promise<ExecutorMcpClient>(async () => {
@@ -898,6 +900,11 @@ describe("local-executor-server", () => { · 781 unmodified lines
898900
899901 it.scoped("updates an existing demo MCP source instead of creating a duplicate", () =>
900902 Effect.gen(function* () {
903+ const demoServer = yield* Effect.acquireRelease(
904+ Effect.promise(() => startMcpElicitationDemoServer()),
905+ (server) => Effect.promise(() => server.close()).pipe(Effect.orDie),
906+ );
907+
901908 const server = yield* createLocalExecutorServer({
902909 port: 0,
903910 localDataDir: ":memory:",
@@ -933,7 +940,7 @@ describe("local-executor-server", () => { · 29 unmodified lines
933940 const seeded = yield* seedDemoMcpSourceInWorkspace({
934941 client,
935942 workspaceId: installation.workspaceId,
936− endpoint: "http://127.0.0.1:58506/mcp",
943+ endpoint: demoServer.endpoint,
937944 name: "Demo",
938945 namespace: "demo",
939946 });
@@ -948,7 +955,7 @@ describe("local-executor-server", () => { · 8 unmodified lines
948955 });
949956
950957 expect(sources).toHaveLength(1);
951− expect(sources[0]?.endpoint).toBe("http://127.0.0.1:58506/mcp");
958+ expect(sources[0]?.endpoint).toBe(demoServer.endpoint);
952959 }),
953960 );
954961
@@ -1295,9 +1302,10 @@ describe("local-executor-server", () => { · 340 unmodified lines
12951302 accountId: installation.accountId,
12961303 });
12971304
1305+ const sourceId = SourceIdSchema.make("src_invalid_mcp_endpoint");
12981306 const now = Date.now();
12991307 yield* server.runtime.persistence.rows.sources.insert({
1300− id: SourceIdSchema.make("src_invalid_mcp_endpoint"),
1308+ id: sourceId,
13011309 workspaceId: installation.workspaceId,
13021310 name: "Demo",
13031311 kind: "mcp",
@@ -1316,6 +1324,44 @@ describe("local-executor-server", () => { · 12 unmodified lines
13161324 createdAt: now,
13171325 updatedAt: now,
13181326 });
1327+ yield* server.runtime.persistence.rows.toolArtifacts.replaceForSource({
1328+ workspaceId: installation.workspaceId,
1329+ sourceId,
1330+ artifacts: [{
1331+ artifact: {
1332+ workspaceId: installation.workspaceId,
1333+ path: "demo.gated_echo",
1334+ toolId: "gated_echo",
1335+ sourceId,
1336+ title: "gated_echo",
1337+ description: "Asks for approval before echoing a value",
1338+ searchNamespace: "demo.gated_echo",
1339+ searchText: "demo.gated_echo gated echo approval",
1340+ inputSchemaJson: JSON.stringify({
1341+ type: "object",
1342+ properties: {
1343+ value: {
1344+ type: "string",
1345+ },
1346+ },
1347+ required: ["value"],
1348+ additionalProperties: false,
1349+ }),
1350+ outputSchemaJson: null,
1351+ providerKind: "mcp",
1352+ mcpToolName: "gated_echo",
1353+ openApiMethod: null,
1354+ openApiPathTemplate: null,
1355+ openApiOperationHash: null,
1356+ openApiRawToolId: null,
1357+ openApiOperationId: null,
1358+ openApiTagsJson: null,
1359+ openApiRequestBodyRequired: null,
1360+ createdAt: now,
1361+ updatedAt: now,
1362+ },
1363+ }],
1364+ });
13191365
13201366 const failure = yield* client.executions
13211367 .create({
apps/web/src/views/add-source.tsx+5−1
@@ -441,6 +441,10 @@ const startSourceOAuthPopup = async (input: { · 440 unmodified lines
441441 return;
442442 }
443443 if (popup.closed) {
444+ // Stop polling — only run one final deferred check to give the
445+ // callback page time to write localStorage before we give up.
446+ window.clearInterval(closedPoll);
447+ closedPoll = 0;
444448 window.setTimeout(() => {
445449 const delayedStored = readStoredSourceOAuthPopupResult(input.sessionId);
446450 if (delayedStored) {
@@ -448,7 +452,7 @@ const startSourceOAuthPopup = async (input: { · 1 unmodified line
448452 return;
449453 }
450454 settleWithError("OAuth popup was closed before completion.");
451− }, 400);
455+ }, 1500);
452456 }
453457 }, 300);
454458 });
apps/web/src/views/source-editor.tsx+5−3
@@ -232,17 +232,19 @@ const startSourceOAuthPopup = async (input: { · 231 unmodified lines
232232 settleFromPayload(stored);
233233 return;
234234 }
235−
236235 if (popup.closed) {
236+ // Stop polling — only run one final deferred check to give the
237+ // callback page time to write localStorage before we give up.
238+ window.clearInterval(closedPoll);
239+ closedPoll = 0;
237240 window.setTimeout(() => {
238241 const delayedStored = readStoredSourceOAuthPopupResult(input.sessionId);
239242 if (delayedStored) {
240243 settleFromPayload(delayedStored);
241244 return;
242245 }
243−
244246 settleWithError("OAuth popup was closed before completion.");
245− }, 400);
247+ }, 1500);
246248 }
247249 }, 300);
248250 });
packages/control-plane/src/runtime/graphql-tools.ts+6−1
@@ -1116,9 +1116,14 @@ const invokeGraphqlHttpRequest = (input: GraphqlHttpInvocation) => · 1115 unmodified lines
11161116 catch: (cause) => graphqlToolError(`Failed decoding GraphQL response for ${input.path}`, cause),
11171117 });
11181118
1119+ const responseHeaders: Record<string, string> = {};
1120+ response.headers.forEach((value, key) => {
1121+ responseHeaders[key] = value;
1122+ });
1123+
11191124 return {
11201125 status: response.status,
1121− headers: sanitizeResponseHeaders(Object.fromEntries(response.headers)),
1126+ headers: sanitizeResponseHeaders(responseHeaders),
11221127 body,
11231128 isError: response.status >= 400 || hasGraphqlErrors(body),
11241129 };