Просмотр исходного кода

fix(core): apply Figma OAuth config before startup

Aiden Cline 3 недель назад
Родитель
Сommit
d3bb1859e0

+ 6 - 1
.opencode/opencode.jsonc

@@ -12,7 +12,12 @@
       "description": "Contains opencode logs and data",
       "description": "Contains opencode logs and data",
     },
     },
   },
   },
-  "mcp": {},
+  "mcp": {
+    "figma": {
+      "type": "remote",
+      "url": "https://mcp.figma.com/mcp",
+    },
+  },
   "tools": {
   "tools": {
     "github-triage": false,
     "github-triage": false,
     "github-pr-search": false,
     "github-pr-search": false,

+ 0 - 25
opencode.jsonc

@@ -1,25 +0,0 @@
-{
-  "$schema": "https://opencode.ai/config.json",
-  "mcp": {
-    "servers": {
-      "everything": {
-        "type": "local",
-        "command": ["npx", "-y", "@modelcontextprotocol/server-everything"],
-        "disabled": false,
-      },
-      "context7": {
-        "type": "local",
-        "command": ["npx", "-y", "@upstash/context7-mcp"],
-        "disabled": false,
-      },
-      "posthog": {
-        "type": "remote",
-        "url": "https://mcp.posthog.com/mcp",
-      },
-      "figma": {
-        "type": "remote",
-        "url": "https://mcp.figma.com/mcp",
-      },
-    },
-  },
-}

+ 3 - 0
packages/core/src/mcp/index.ts

@@ -15,6 +15,7 @@ import { Integration } from "../integration"
 import { IntegrationConnection } from "../integration/connection"
 import { IntegrationConnection } from "../integration/connection"
 import { KeyedMutex } from "../effect/keyed-mutex"
 import { KeyedMutex } from "../effect/keyed-mutex"
 import { Location } from "../location"
 import { Location } from "../location"
+import { FigmaPlugin } from "../plugin/figma"
 import { waitForAbort } from "@opencode-ai/util/process"
 import { waitForAbort } from "@opencode-ai/util/process"
 import { State } from "../state"
 import { State } from "../state"
 import { MCPClient } from "./client"
 import { MCPClient } from "./client"
@@ -194,6 +195,7 @@ export const layer = (options?: Options) => Layer.effect(
     const urlElicitations = new Map<string, Form.ID>()
     const urlElicitations = new Map<string, Form.ID>()
     for (const entry of documents) {
     for (const entry of documents) {
       for (const [name, server] of Object.entries(entry.info.mcp?.servers ?? {})) {
       for (const [name, server] of Object.entries(entry.info.mcp?.servers ?? {})) {
+        FigmaPlugin.apply(server)
         runtime.set(ServerName.make(name), {
         runtime.set(ServerName.make(name), {
           config: { ...server, timeout: { ...timeout, ...server.timeout } },
           config: { ...server, timeout: { ...timeout, ...server.timeout } },
           status: { status: "pending" },
           status: { status: "pending" },
@@ -622,6 +624,7 @@ export const layer = (options?: Options) => Layer.effect(
             if (previous.integrationID) owned.delete(previous.integrationID)
             if (previous.integrationID) owned.delete(previous.integrationID)
             if (previous.registration) yield* previous.registration.dispose
             if (previous.registration) yield* previous.registration.dispose
           }
           }
+          FigmaPlugin.apply(config)
           const entry: ServerEntry = {
           const entry: ServerEntry = {
             config: { ...config, timeout: { ...timeout, ...config.timeout } },
             config: { ...config, timeout: { ...timeout, ...config.timeout } },
             status: { status: "pending" },
             status: { status: "pending" },

+ 12 - 23
packages/core/src/plugin/figma.ts

@@ -1,28 +1,17 @@
 export * as FigmaPlugin from "./figma"
 export * as FigmaPlugin from "./figma"
 
 
-import { define } from "@opencode-ai/plugin/v2/effect/plugin"
-import { Effect } from "effect"
-import { Config } from "../config"
+import { ConfigMCP } from "../config/mcp"
 
 
 const CLIENT_ID = "3zVHNs9kINDDrk8loekLZV"
 const CLIENT_ID = "3zVHNs9kINDDrk8loekLZV"
 
 
-export const Plugin = define({
-  id: "opencode.figma",
-  effect: Effect.fn(function* () {
-    const config = yield* Config.Service
-    const entries = yield* config.entries()
-    entries
-      .filter((entry): entry is Config.Document => entry.type === "document")
-      .flatMap((entry) => Object.values(entry.info.mcp?.servers ?? {}))
-      .forEach((server) => {
-        if (server.type !== "remote" || server.oauth === false) return
-        if (!URL.canParse(server.url) || new URL(server.url).hostname !== "mcp.figma.com") return
-        if (server.oauth?.client_id) return
-        if (server.oauth) {
-          Object.assign(server.oauth, { client_id: CLIENT_ID })
-          return
-        }
-        Object.assign(server, { oauth: { client_id: CLIENT_ID } })
-      })
-  }),
-})
+export function apply(server: typeof ConfigMCP.Server.Type) {
+  if (server.type !== "remote" || server.oauth === false) return server
+  if (!URL.canParse(server.url) || new URL(server.url).hostname !== "mcp.figma.com") return server
+  if (server.oauth?.client_id) return server
+  if (server.oauth) {
+    Object.assign(server.oauth, { client_id: CLIENT_ID })
+    return server
+  }
+  Object.assign(server, { oauth: { client_id: CLIENT_ID } })
+  return server
+}

+ 0 - 2
packages/core/src/plugin/internal.ts

@@ -48,7 +48,6 @@ import { WellKnown } from "../wellknown"
 import { WriteTool } from "../tool/write"
 import { WriteTool } from "../tool/write"
 import { AgentPlugin } from "./agent"
 import { AgentPlugin } from "./agent"
 import { CommandPlugin } from "./command"
 import { CommandPlugin } from "./command"
-import { FigmaPlugin } from "./figma"
 import { ModelsDevPlugin } from "./models-dev"
 import { ModelsDevPlugin } from "./models-dev"
 import { ProviderPlugins } from "./provider"
 import { ProviderPlugins } from "./provider"
 import { PluginRuntime } from "./runtime"
 import { PluginRuntime } from "./runtime"
@@ -127,7 +126,6 @@ export type InternalPlugin = Plugin<Requirements | Scope.Scope>
 
 
 const pre = [
 const pre = [
   WellKnownPlugin.Plugin,
   WellKnownPlugin.Plugin,
-  FigmaPlugin.Plugin,
   AgentPlugin.Plugin,
   AgentPlugin.Plugin,
   CommandPlugin.Plugin,
   CommandPlugin.Plugin,
   SkillPlugin.Plugin,
   SkillPlugin.Plugin,

+ 0 - 76
packages/core/test/config/plugin.test.ts

@@ -7,7 +7,6 @@ import { Config as ConfigSchema } from "@opencode-ai/schema/config"
 import { Plugin } from "@opencode-ai/schema/plugin"
 import { Plugin } from "@opencode-ai/schema/plugin"
 import { AgentV2 } from "@opencode-ai/core/agent"
 import { AgentV2 } from "@opencode-ai/core/agent"
 import { Catalog } from "@opencode-ai/core/catalog"
 import { Catalog } from "@opencode-ai/core/catalog"
-import { Config } from "@opencode-ai/core/config"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { LayerNode } from "@opencode-ai/util/effect/layer-node"
 import { EventV2 } from "@opencode-ai/core/event"
 import { EventV2 } from "@opencode-ai/core/event"
@@ -260,81 +259,6 @@ describe("PluginSupervisor config", () => {
     ),
     ),
   )
   )
 
 
-  it.live("adds the OpenCode client ID to configured Figma servers", () =>
-    withLocation(
-      {
-        mcp: {
-          servers: {
-            figma: {
-              type: "remote",
-              url: "https://mcp.figma.com/mcp",
-              oauth: { scope: "mcp:connect" },
-            },
-          },
-        },
-      },
-      Effect.gen(function* () {
-        yield* ready()
-        const config = yield* Config.Service
-        expect(Config.latest(yield* config.entries(), "mcp")?.servers?.figma).toMatchObject({
-          oauth: { client_id: "3zVHNs9kINDDrk8loekLZV", scope: "mcp:connect" },
-        })
-      }),
-    ),
-  )
-
-  it.live("preserves an existing Figma client ID", () =>
-    withLocation(
-      {
-        mcp: {
-          servers: {
-            figma: {
-              type: "remote",
-              url: "https://mcp.figma.com/mcp",
-              oauth: { client_id: "configured-client-id" },
-            },
-          },
-        },
-      },
-      Effect.gen(function* () {
-        yield* ready()
-        const config = yield* Config.Service
-        expect(Config.latest(yield* config.entries(), "mcp")?.servers?.figma).toMatchObject({
-          oauth: { client_id: "configured-client-id" },
-        })
-      }),
-    ),
-  )
-
-  it.live("does not enable OAuth or modify non-Figma servers", () =>
-    withLocation(
-      {
-        mcp: {
-          servers: {
-            disabled: {
-              type: "remote",
-              url: "https://mcp.figma.com/mcp",
-              oauth: false,
-            },
-            other: {
-              type: "remote",
-              url: "https://mcp.example.com/mcp",
-            },
-          },
-        },
-      },
-      Effect.gen(function* () {
-        yield* ready()
-        const config = yield* Config.Service
-        const servers = Config.latest(yield* config.entries(), "mcp")?.servers
-        expect(servers).toMatchObject({
-          disabled: { oauth: false },
-          other: { type: "remote", url: "https://mcp.example.com/mcp" },
-        })
-        expect(servers?.other).not.toHaveProperty("oauth")
-      }),
-    ),
-  )
 })
 })
 
 
 const ready = Effect.fnUntraced(function* () {
 const ready = Effect.fnUntraced(function* () {

+ 47 - 0
packages/core/test/plugin/figma.test.ts

@@ -0,0 +1,47 @@
+import { describe, expect, test } from "bun:test"
+import { ConfigMCP } from "@opencode-ai/core/config/mcp"
+import { FigmaPlugin } from "@opencode-ai/core/plugin/figma"
+
+describe("plugin.figma", () => {
+  test("adds the OpenCode client ID to configured Figma servers", () => {
+    const server = new ConfigMCP.Remote({
+      type: "remote",
+      url: "https://mcp.figma.com/mcp",
+      oauth: new ConfigMCP.OAuth({ scope: "mcp:connect" }),
+    })
+
+    FigmaPlugin.apply(server)
+
+    expect(server.oauth).toEqual({ client_id: "3zVHNs9kINDDrk8loekLZV", scope: "mcp:connect" })
+  })
+
+  test("preserves an existing client ID", () => {
+    const server = new ConfigMCP.Remote({
+      type: "remote",
+      url: "https://mcp.figma.com/mcp",
+      oauth: new ConfigMCP.OAuth({ client_id: "configured-client-id" }),
+    })
+
+    FigmaPlugin.apply(server)
+
+    expect(server.oauth).toEqual({ client_id: "configured-client-id" })
+  })
+
+  test("does not enable OAuth or modify non-Figma servers", () => {
+    const disabled = new ConfigMCP.Remote({
+      type: "remote",
+      url: "https://mcp.figma.com/mcp",
+      oauth: false,
+    })
+    const other = new ConfigMCP.Remote({
+      type: "remote",
+      url: "https://mcp.example.com/mcp",
+    })
+
+    FigmaPlugin.apply(disabled)
+    FigmaPlugin.apply(other)
+
+    expect(disabled.oauth).toBe(false)
+    expect(other.oauth).toBeUndefined()
+  })
+})