Skip to content

🎃 Export a single connector as JSON or YAML, and accept YAML on import #1004

Open

Description

@helpcodeai

Part of the October Challenge #997.

Why

People who build a connector by hand want to move it: from a test instance to production, from one workspace to another, from self-hosted to Cloud, or into a Git repository so a colleague can review it. Today export and import only work for all connectors of a workspace at once, and only as JSON. YAML is easier to read and to diff in a pull request, especially with long tool descriptions and request templates.

What already exists (do not rebuild it):

  • GET /api/connectors/export-all (connectors.controller.ts:867-917) returns { version, exportedAt, secretsIncluded, connectors: [...] }. authConfig is never exported. Secret env vars and headers are exported only to admins; for other roles they are blanked and named in maskedEnvVars / maskedHeaders (toPublicConnector, connector-secrets.util.ts:267).
  • POST /api/connectors/import-all (:1461-1553) skips a connector whose name already exists in the organization, checks the licence and trial limit per connector, and reloads tools.
  • ImportAllDto (:505-535) accepts the export envelope as downloaded. The global ValidationPipe rejects unknown properties, so a single-connector file must have exactly the shape of one export-all entry.
  • The Connectors page has "Export" and "Import" buttons (connectors/page.tsx:139-181, :219-243). The connector detail page has no export.
  • js-yaml 4 is already a backend dependency (packages/backend/package.json:64), used by the OpenAPI parser to accept JSON or YAML (parsers/openapi.parser.ts:7, :166-185). The frontend has no YAML library.

Self-hosted vs Cloud

  • Self-hosted: enabled in Community and Business. No ee/ code.
  • Cloud (DEPLOYMENT_MODE=cloud): enabled, with the same rules as export-all / import-all. Each imported connector counts against the workspace's licence or trial limit through licenseGuard.checkCanCreateConnector, exactly as in import-all. YAML is parsed on the backend, within the 10 MB body limit (main.ts:53).
  • Who can use it: export, any member of the connector's organization (assertOrgMatch, :583-587), with secret values only for ADMIN, as in export-all. Import, any role except VIEWER (assertCanCreate, :589-593). When an admin's file contains secrets (secretsIncluded: true), the UI says that the file holds plaintext credentials and must be stored like a password.

What to build

  1. One mapping: extract the per-connector object built in exportAll into a function (for example toExportEntry(connector, includeSecrets)) used by both export endpoints, so the formats cannot drift.
  2. Export: GET /api/connectors/:id/export?format=json|yaml (default json). It returns the same envelope as export-all with one entry in connectors, so the file is accepted by every import path. YAML via yaml.dump(data, { schema: CORE_SCHEMA, noRefs: true, lineWidth: -1 }) with Content-Type: application/yaml. Both formats with Content-Disposition: attachment; filename="<connector-slug>.anythingmcp.<json|yaml>".
  3. Import of JSON or YAML text: POST /api/connectors/import with { content: string }, holding an export-all file or a single-connector file. Parse with yaml.load(content, { schema: CORE_SCHEMA }): JSON is valid YAML, so one parser covers both, and CORE_SCHEMA keeps exportedAt a string instead of turning it into a Date. Validate the result against ImportAllDto with plainToInstance + validate (whitelist, forbidNonWhitelisted), then run the same logic as import-all: move its loop into a method both endpoints call, and return the same result (message, created, skipped, tools, errors). A parse error returns 400 with the line and column.
  4. UI:
    • Connector detail page header (connectors/[id]/page.tsx:768-780): an "Export" menu with "JSON" and "YAML". Download with fetch + blob() like handleExportAll (connectors/page.tsx:139-160), and show the same message when secrets were withheld.
    • Connectors page import dialog (connectors/page.tsx:162-181, :271-300): the file input (:288) accepts .json,.yaml,.yml, and the dialog sends the raw text to the new endpoint instead of calling JSON.parse in the browser. Rename "Import from JSON" (:220, :241) to "Import from file".
  5. Docs: add export-all, import-all and the two new endpoints to the Connector Endpoints table in docs/api-reference.md:75-90, with a short YAML example.

Where to look

  • packages/backend/src/connectors/connectors.controller.ts:430-503 (ImportConnectorDto), :505-535 (ImportAllDto), :583-603 (assertOrgMatch, assertCanCreate, assertCanWrite), :867-917 (exportAll), :1461-1553 (importAll).
  • packages/backend/src/connectors/connector-secrets.util.ts:267: toPublicConnector() masking.
  • packages/backend/src/connectors/parsers/openapi.parser.ts:166-185: existing JSON-or-YAML decoding.
  • packages/backend/src/connectors/connectors.import-all.spec.ts:116, :178: tests that an export-all file imports as downloaded and stays in the caller's organization; copy them for the new endpoints.
  • packages/frontend/src/app/connectors/page.tsx:139-181, :219-243, :271-300; packages/frontend/src/app/connectors/[id]/page.tsx:768-780.
  • packages/frontend/src/lib/api.ts:422-425: exportAll / importAll clients.

Acceptance criteria

  • Round-trip spec: export one connector as JSON and as YAML, import both into a second organization, and get the same name, type, base URL, config, headers, env vars (admin export) and tools.
  • A single-connector JSON file is also accepted by the existing POST /api/connectors/import-all.
  • For non-admins, secrets are blanked and listed in maskedEnvVars / maskedHeaders; authConfig never appears in either format (asserted in a spec).
  • Exporting another organization's connector returns 403; a viewer gets 403 on import; invalid YAML returns 400 with line information; a file with an unknown property is rejected in YAML as it is in JSON.
  • cd packages/backend && npx jest src/connectors passes.
  • Playwright test: the Export menu on the connector page requests format=yaml, and the import dialog accepts a .yaml file (cd packages/frontend && npm run test:e2e).
  • docs/api-reference.md updated.

Out of scope

  • A collision policy (rename or replace on a name clash). Import keeps today's behaviour: a connector whose name exists is skipped.
  • Exporting more fields than export-all does today (non-secret authConfig keys, healthcheckPath, instructions, tool annotations). That changes the DTO and needs its own design.
  • Duplicating a connector or a tool within a workspace.
  • YAML output for export-all.

Size

M (1–2 days)

How to claim

Comment "I'd like to work on this" and we'll assign you. Rules in #997.

Activity

  1. GallaGit commented on Oct 20, 2026

    @GallaGit
    Contributor

    Hi! I'd like to work on this one. I'll follow the single-connector export/import approach outlined in the design doc (#979), reuse the existing export-all logic and connector-secrets.util.ts, add YAML export plus YAML import, and include tests. Could you assign it to me? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthacktoberfestGood for Hacktoberfest contributorshelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions