(input: McpServerInput)
| 1079 | ); |
| 1080 | |
| 1081 | const addServer = (input: McpServerInput) => |
| 1082 | Effect.gen(function* () { |
| 1083 | const slug = normalizeSlug(input); |
| 1084 | const config = toIntegrationConfig(input); |
| 1085 | |
| 1086 | // Block re-adding an existing slug. The core `integrations.register` |
| 1087 | // primitive upserts (so boot re-registration is idempotent), but an |
| 1088 | // explicit add must NOT silently clobber an existing integration's |
| 1089 | // tools, connections, and policies. To add more auth, update the |
| 1090 | // existing integration instead. |
| 1091 | const existing = yield* ctx.core.integrations.get(slugFrom(slug)); |
| 1092 | if (existing) { |
| 1093 | return yield* new IntegrationAlreadyExistsError({ slug: slugFrom(slug) }); |
| 1094 | } |
| 1095 | |
| 1096 | yield* ctx.core.integrations |
| 1097 | .register({ |
| 1098 | slug: slugFrom(slug), |
| 1099 | name: input.name, |
| 1100 | description: input.description?.trim() || input.name, |
| 1101 | config, |
| 1102 | canRemove: true, |
| 1103 | canRefresh: true, |
| 1104 | }) |
| 1105 | .pipe( |
| 1106 | Effect.withSpan("mcp.plugin.register_integration", { |
| 1107 | attributes: { "mcp.integration.slug": slug }, |
| 1108 | }), |
| 1109 | ); |
| 1110 | |
| 1111 | // Auto-create the stdio server's default connection so its tools are |
| 1112 | // discovered immediately (without it the integration lands with zero |
| 1113 | // connections and therefore zero tools — the fresh-install "no tools |
| 1114 | // detected" report). Two cases connect on add: |
| 1115 | // • one-shot `env` VALUES were supplied (agent path) → bind them as |
| 1116 | // the connection's secrets. |
| 1117 | // • the server needs NO secret env at all → a no-auth connection. |
| 1118 | // When the server only DECLARES env var names (the UI path), the |
| 1119 | // secrets are still missing, so we leave the connection to the connect |
| 1120 | // step where the user enters one masked value per declared var. |
| 1121 | if (input.transport === "stdio") { |
| 1122 | const hasValues = input.env != null && Object.keys(input.env).length > 0; |
| 1123 | const declaresSecrets = stdioEnvVarNames(input).length > 0; |
| 1124 | if (hasValues || !declaresSecrets) { |
| 1125 | yield* ctx.connections |
| 1126 | .create({ |
| 1127 | owner: "org", |
| 1128 | name: ConnectionName.make("default"), |
| 1129 | integration: slugFrom(slug), |
| 1130 | template: AuthTemplateSlug.make(hasValues ? STDIO_ENV_TEMPLATE : "none"), |
| 1131 | values: hasValues ? { ...input.env } : {}, |
| 1132 | }) |
| 1133 | .pipe( |
| 1134 | // These can't arise right after a successful register with |
| 1135 | // valid inputs, but the channel must stay within |
| 1136 | // McpExtensionFailure; surface them as a connection error |
| 1137 | // rather than swallow a real failure. |
| 1138 | Effect.catchTags({ |
nothing calls this directly
no test coverage detected