Skip to content

Preserve operator config across OpenAPI re-imports (#6) - #168

Merged
keysersoft merged 1 commit into
mainfrom
keysersoft/reimport-preserve
May 12, 2026
Merged

keysersoft merged 1 commit into
mainfrom
keysersoft/reimport-preserve

Conversation

@keysersoft

Copy link
Copy Markdown
Contributor

Problem

Importing a spec twice into the same connector created tools on the first run and silently skipped on the second (`P2002` swallow in `createToolsFromParsed`). Two consequences:

  • Operator customisations made between imports — custom `responseMapping`, manual `isEnabled: false` — were lost on the next re-import.
  • Endpoints that disappeared from upstream stayed registered forever, with no signal they were stale. Role assignments pointed at zombie tools.

Fix

Turn `createToolsFromParsed` into a reconciliation step.

Schema (additive, nullable; no backfill needed)

```
mcp_tools.operation_id TEXT
mcp_tools.deprecated_at TIMESTAMP(3)
```

Parser

`ParsedTool.operationId` is populated from the source operation when present (works for 3.0 and 3.1).

Reconciliation

  1. Snapshot existing tools.
  2. For each parsed tool, look up by operationId first, fall back to (method, path).
  3. Match → `UPDATE` in place. Preserves `responseMapping` unless the spec declares a new one. Preserves the operator's manual `isEnabled` toggle. Clears `deprecatedAt` if previously set.
  4. No match → `CREATE`.
  5. Snapshot entries unmatched by any parsed tool get `deprecatedAt = now()` + `isEnabled = false`. They stay in the DB so `ToolRoleAccess` and invocation history survive.

Response shape

```json
{
"message": "Imported tools: created N, updated N, deprecated N",
"tools": [...],
"created": N,
"updated": N,
"deprecated": ["tool_name_1", ...],
"skipped": [] // kept for back-compat; always empty now
}
```

Frontend

Tool list shows a yellow `deprecated` badge next to the existing `enabled`/`disabled` pill, with a tooltip showing the date the tool fell off the spec.

Test plan

  • Parser spec asserts `operationId` is preserved on `ParsedTool` (new test in `openapi.parser.spec.ts`).
  • Full backend test suite green: 631 passed across 26 suites.
  • `tsc --noEmit` clean on backend and frontend.
  • After deploy: import spec → assign role → re-import same spec → role still on tool. Re-import a modified spec that drops one endpoint → tool gets `deprecated` badge, role assignment intact.

@keysersoft
keysersoft requested a review from mirkopoloni as a code owner May 12, 2026 07:40
Before this, importing a spec twice into the same connector created a
new row on first run and silently skipped on second run (P2002 swallow
in createToolsFromParsed). That had two consequences:

 - Any customisations the operator made between imports
   (responseMapping, isEnabled) were lost on the first re-import.
 - Endpoints that disappeared from the upstream spec stayed registered
   forever, with no visible signal that they were stale.

The fix turns createToolsFromParsed into a reconciliation step.

Schema (additive, nullable, no backfill needed):
  mcp_tools.operation_id     TEXT
  mcp_tools.deprecated_at    TIMESTAMP(3)

OpenAPI parser:
  ParsedTool.operationId is populated from the source operation when
  present. Survives 3.0 and 3.1 specs.

Reconciliation:
  1. Snapshot existing (non-deprecated) tools for the connector.
  2. For each parsed tool, look up by operationId first, then fall
     back to (method, path).
  3. Match → UPDATE in place. Preserves responseMapping when the spec
     doesn't declare a new one; preserves the operator's manual
     isEnabled toggle; clears deprecatedAt if previously set.
  4. No match → CREATE.
  5. Snapshot entries unmatched by any parsed tool get
     deprecatedAt = now() and isEnabled = false. They stay in the DB
     so ToolRoleAccess and invocation history survive; the UI shows a
     'deprecated' badge.

Response shape grows: { tools, created, updated, deprecated[], skipped[] }.
'skipped' is kept (always empty under the new strategy) for back-compat
with clients that consumed the previous shape.

Frontend:
  Tool list shows a yellow 'deprecated' badge with a tooltip pointing
  at the date the tool fell off the upstream spec.

Tests: 26 parser specs (added one for operationId preservation), full
backend suite 631 passing. Frontend tsc clean.
@keysersoft
keysersoft force-pushed the keysersoft/reimport-preserve branch from 4ed1036 to a313ece Compare May 12, 2026 07:42
@keysersoft
keysersoft merged commit c4c4d3f into main May 12, 2026
@keysersoft
keysersoft deleted the keysersoft/reimport-preserve branch May 12, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant