Fix registration & discovery papercuts#4
Merged
Conversation
Smooths the rough edges hit while driving the server through real registrations; the core search/execute model is unchanged. - add_api/add gain force (CLI: --force): upsert an existing id in place instead of failing. Overwrite is in-place (fresh addedAt invalidates serve's ops cache) and preserves same-id keystore secrets, so an OAuth API stays logged in across a base-URL correction. - New remove_api MCP tool plus a shared unregisterApi, so the agent can clean up a mistaken registration itself (entry + secrets + cached files). - OpenAPI base-URL derivation now reads document-, path-, and operation-level servers, and fails loudly when the result lands on a raw spec-hosting host (raw.githubusercontent.com, jsDelivr, githack, ...) rather than registering a base that points every call at a file CDN. - execute gains per-call check:false (bypass a stale spec enum that rejects a value the live API accepts) and timeoutMs (default 30s, capped 120s). check:false drops only --check; the net/env sandbox grants are unchanged. - search surfaces each param's description and allowed enum values (OpenAPI + GraphQL), so the model needn't guess valid arguments. - execute's description documents the GraphQL (client.query/mutate) and SOAP (client.Operation) client shapes, not just openapi-fetch. - New list_apis tool returns the registry as JSON.
There was a problem hiding this comment.
Pull request overview
This PR smooths the API registration and discovery workflow for anyapi-mcp by adding upsert/removal capabilities, improving OpenAPI base-URL derivation, and enriching the metadata surfaced by search/execute for better agent-driven usage.
Changes:
- Add “force upsert” registration and introduce a unified
unregisterApiused by both CLI (remove) and MCP (remove_api), plus a newlist_apistool. - Improve OpenAPI base URL derivation by scanning document/path/operation
serversand rejecting known raw-spec hosting hosts. - Enhance discovery/execution ergonomics: parameter
description/enumsurfaced in search, andexecuteaddscheck:falseandtimeoutMs.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/register.ts | Adds force upsert support, orphaned-secret cleanup, and unregisterApi for CLI/MCP removal. |
| src/operation.ts | Extends operation param metadata with description and enum, including size clamping helpers. |
| src/openapi.ts | Extracts/clamps param descriptions/enums and improves base URL resolution across server levels with raw-host detection. |
| src/graphql.ts | Adds param descriptions and GraphQL enum-value surfacing into the operation index. |
| src/execute/run.ts | Adds per-run sandbox options (check, timeoutMs) and clamps timeout values. |
| src/commands/serve.ts | Expands MCP server tools (list_apis, remove_api), extends execute args, and updates tool descriptions. |
| src/commands/remove.ts | Refactors CLI removal to call shared unregisterApi. |
| src/commands/add.ts | Adds --force and reports “Re-registered” when overwriting. |
| README.md | Documents new tools/flags and updated execute options/behavior. |
| CLAUDE.md | Updates architecture/invariants documentation to reflect new features and options. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+40
to
+47
| /** Cap an enum list, appending a count sentinel (not a real value) when truncated. */ | ||
| export function clampEnum(values: string[]): string[] | undefined { | ||
| if (values.length === 0) return undefined; | ||
| if (values.length <= MAX_ENUM_VALUES) return values; | ||
| return [ | ||
| ...values.slice(0, MAX_ENUM_VALUES), | ||
| `… (${values.length - MAX_ENUM_VALUES} more; see generated types)`, | ||
| ]; |
Comment on lines
+262
to
+264
| if (existing.typesPath !== entry.typesPath) { | ||
| await Deno.remove(existing.typesPath).catch(() => {}); | ||
| } |
- applyEnum caps the surfaced enum to real values only; an over-cap count is noted in the param description instead of a sentinel enum entry a caller might select and send to the API. - Overwrite's stale-types cleanup ignores only NotFound and rethrows other errors, matching the repo's other deletion sites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the registration & discovery papercuts hit while driving the server
through several real registrations. The execute-TypeScript-in-a-sandbox core is
untouched; these are all rough edges in registration and discovery.
Changes
Re-registering an existing id no longer dead-ends
add_api/addtakeforce(CLI:--force) to upsert an existingid in place instead of erroring. The overwrite uses a fresh
addedAt(soserve's ops cache invalidates) and preserves same-id keystore secrets, soan OAuth API stays logged in across a base-URL correction. Secrets the new auth
no longer references are cleaned up.
remove_apiMCP tool, sharing a singleunregisterApiwith theremoveCLI (entry + secrets + cached artifacts). The agent can now sweep upits own mistaken registrations.
Base-URL derivation reads path/operation-level servers
resolveBaseUrlnow scans document → path-item → operation levelserversbefore falling back to the spec host, and fails loudly when the derived
base lands on a known raw-spec-hosting host (raw.githubusercontent.com, gist,
githack, jsDelivr, statically) instead of registering a base that would send
every request to a file CDN. This is exactly the Open-Meteo case (no top-level
servers, an absolute server under/v1/forecast).executeopt-outscheck: falsedrops--checkfor one run, so a stale specenumdoesn'tblock a value the live API still accepts. It removes only
--check— the--allow-net/--allow-envsandbox grants are unconditional.timeoutMsraises the 30s default (capped at 120s) for long/paginated runs.Discovery
searchsurfaces each param'sdescriptionand allowedenumvalues(OpenAPI + GraphQL), size-capped, so the model needn't guess valid arguments.
execute's description documents the GraphQL (client.query/client.mutate)and SOAP (
client.Operation) client shapes, not just openapi-fetch.list_apistool returns the registry as JSON.Not included
Pagination-aware helpers — a real cross-protocol design question rather than a
mechanical fix; the configurable
timeoutMscovers the "long loops hit the 30swall" part. Can be designed separately.
Verification
deno task check && deno task lint && deno task fmtpass. Exercised end-to-endagainst the live Open-Meteo and Petstore specs in an isolated config/cache dir:
base URL derives to
https://api.open-meteo.com; a real call withdaily: ["snowfall_sum"](absent from the spec enum) returns HTTP 200 undercheck:false;--force,remove_api,list_apis, and the raw-host failure allbehave as intended. No test suite exists yet, so verification was manual.