If this connection lists call_platform_tool instead of the tools this workflow names, find each named tool with search_platform_tools, describe it with describe_platform_tool, then run it with call_platform_tool and the same arguments. Whether a tool exists for you is decided by describe_platform_tool with its exact name, not by tools/list or one page of search results: a name returned in not_found is unavailable to you, so say so rather than guess. The call itself still checks your access. When execute is also listed, a multi-step read may run in execute with tools.search, tools.describe and tools.call on platform--<tool name> paths. End each execute run at a confirmation point and ask the user before any write.
Use this workflow only through the authenticated Speakeasy AI Control Plane (AICP) Platform MCP connected as an external administrator. There is no move tool, so it follows the same guarded outcome a user would complete manually in the AICP dashboard: read the source registration, re-add the same reviewed source in an explicit target project, finish secure setup there, verify readiness, distribute to one exact target plugin, and only then optionally disable the source. The source stays enabled and untouched until the target is registered, connected, verified ready, and distributed. For a first-time addition with no source, use add-mcp-from-catalog or add-mcp-from-remote-url instead. It is not available to managed project assistants: an assistant acts only inside its own project and cannot name a second one, and the plugin inventory, access reads, forced readiness probes, and distribution this workflow depends on are external-client tools. Installing this package grants no organization or project access.
Safety rules
- Secrets never enter chat. Never ask the user to paste credentials, tokens, secret headers, or OAuth state into chat or tool arguments, and never claim that any secret, header value, or authorization travelled from the source. Secret entry and provider sign-in happen only in server-returned dashboard and authorization URLs.
- Show non-secret setup, provider, and authorization URLs only when a Platform MCP tool returns them; never reconstruct or invent a link.
- Keep the source project, source MCP, target project, and target plugin explicit. Never infer any of them from prior context, a display name, or the Default project, never silently substitute another target, and never let the source and target be the same project.
- Migration re-creates rather than moves: the target gets a new registration, a new endpoint address, and its own identity-provider attachment. State per attribute whether registration copied it, it is re-created only after the user confirms it, the user must re-enter it, or it cannot be reproduced. Nothing is dropped silently.
- Registration-scoped reads and lifecycle mutations on the source work only for the administrator who registered it. A not-found, invalid-request, or unsupported-target refusal on the source means the caller cannot probe or disable it; report that attribute as unreadable and hand off to the AICP dashboard rather than working around it.
- Registration in the target is private until a plugin carries it. Distribute only after fresh readiness from the same connection, and never disable the source until the target's live state has been verified and the user confirms retirement. Treat server-returned evidence as bounded and report every gap.
- A denial or conflict returns to fresh reads and user review. Never retry a mutation automatically or with another project, MCP, or plugin, and never disable the source to make the target succeed.
- Use
send_platform_mcp_feedbackonly after asking for consent, and never include identifiers, URLs, credentials, payloads, headers, logs, or attachments.
Workflow
- Call
list_projectswithlimit: 100to verify that the Platform MCP is authenticated and obtain the eligible projects. If authenticated discovery is unavailable, stop and ask the user to complete or repair AICP OAuth. Iftruncated: true, report that project discovery is incomplete and hand off to the AICP dashboard. Present the projects and ask the user to choose one exact source project and one exact, different target project in this organization; keep each project's ID and slug paired internally. - Call
find_mcpfor only the source project withlimit: 100, following everynext_cursorusingcursorwith the same project and noqueryuntil the list is complete. Present only entries withmodel: platform_managed; a dashboard-managed, hosted, tunnelled, or legacy server cannot be migrated with these tools, so report that and stop. Ask the user to choose one exact source MCP. - Call
get_mcpwith the source project ID and that exactmcp_id. Continue only when it returns a registration ID,registration.status: registered,registration.components_complete: true,visibility: private, andeffective_enabled: true. Adisabledsource was deliberately turned off, and apublicsource cannot be retired with these tools; in either case stop and report that state rather than re-creating it, because the target is always registered private and enabled regardless of the source. Retain itsname,slug,visibility,source.kind,source.provider,source.reference,distributions, and registration ID internally as the source shape. - Call
get_mcp_accessfor the source and retain its stored tool names and the roles that reach it for later comparison. - Call
get_mcp_client_admissionwith the source project slug and registration ID and retain the returnedmodeand custom client ID metadata URLs. If it answersinvalid_request, the admission setting cannot be read by this caller, most often because another administrator registered the source: mark admission as unreadable and skip every source-scoped probe and lifecycle step. - Call
list_pluginsfor the source project ID, following everynext_cursoruntil the list is complete, thenget_pluginfor each plugin, matchingservers[].mcp_slugagainst the sourceslug, becausedistributionslists only Platform-flow memberships. Record each carrying plugin's name, policy, and assignment set; these memberships are project-scoped and are not copied. - Present the migration inventory before any write. Copied by registration: backend kind, catalogue entry or canonical remote URL, and display name; the target is always created private and enabled, whatever the source's state. Re-created only after the user's explicit confirmation in later steps: the client admission mode, plugin membership, and audience, each by name. Re-collected from the user: the declared non-secret configuration values, which no tool reads back from the source. Cannot be copied: secret configuration, the identity-provider attachment and every user's authorization, the endpoint address and custom domains, custom client ID metadata URLs, and access-role rules pinned to the source MCP. Ask the user to acknowledge this inventory explicitly.
- Re-derive the candidate from the persisted source shape and branch by
source.kind:- For
reviewed_catalogue, callinspect_mcp_candidatewithprovider_keyset tosource.providerandcatalog_refset tosource.reference. Present the declared configuration fields and collect only the non-secret values from the user; stored source values are not readable and must be supplied again. - For
user_supplied_url, callinspect_mcp_candidatewithremote_urlset tosource.referenceand no catalogue selectors, and present the canonical URL and the sourcenamefor confirmation. If the entry no longer exists or inspection reports an error, stop without registering. Compare the returned tool names with the source access read and report differences as evidence rather than failure.
- For
- Present the bounded target change: the exact target project, the re-derived candidate, the non-secret configuration, and everything that must be redone. After explicit confirmation, for
reviewed_cataloguecallregister_catalog_mcpwith the exact target project slug,provider_key,catalog_ref,non_secret_config, and a fresh idempotency key; foruser_supplied_urlcallregister_remote_mcpwith the exact target project slug,remote_url, the sourcenameasdisplay_name, and a fresh idempotency key. Registration is private and does not distribute the MCP. Readnext_action:readyandstart_setupcontinue to the readiness check;continue_dashboard_setupandsecure_dashboard_setup_requiredreturn adashboard_setup_urlto present as-is in step 11.connect_sign_inmeans the server's OAuth client is set up automatically: continue to the readiness check, whoseupstream_identity_provider_not_configuredroute in step 11 connects it in chat, with the returneddashboard_setup_urlas the fallback. Anineligible_projectrefusal means the target project cannot receive it; afeature_unavailablerefusal means registration is unavailable right now; a plainconflictmeans the idempotency key was already used with different input. In every case report the refusal and stop; do not retry against another project or with altered input. - Call
find_mcpfor the target project and select only the entry whoseregistration.idequals the returned registration ID, then callget_mcpwith the target project ID and thatmcp_idto obtain itsversion,source.provider, andsource.reference. If the targetnamediffers from the sourcenameand the user wants it kept, callupdate_mcp_metadatawith the target project slug, registration ID,mcp_id, the source name, the immediately precedingversionasexpected_version, and a fresh idempotency key. - Call
get_mcp_readinesswith the target project slug and registration ID. Registration writes no readiness, so this first read checks the MCP once and returns that provider evidence. Only when it comes back unchecked (readiness_unavailable, freshnessunavailable) because that check was refused or failed, callget_mcp_readinessonce withforce: true; an unchecked result is not a reason to stop. A refused first check shares the forced-check limit, so if the forced call returnsrate_limited, wait at least a minute before forcing again. Then route secure setup from the exact target evidence:- For
upstream_identity_provider_not_configured, explain that AICP can attach the one authorization server discovered from the persisted source. Ask for explicit confirmation, then callattach_platform_mcp_identity_providerwithconfirmed: true, present its exactauthorization_urlas a clickable link, and wait for the user to use Connect or Authorize. If registration returnedconnect_sign_inand the user prefers the dashboard or attachment is refused, present the exactdashboard_setup_urlfrom registration instead. - For
upstream_authorization_required, callattach_platform_mcp_identity_provideragain withconfirmed: trueto retrieve the current server-issuedauthorization_urland wait for Connect or Authorize. - For
identity_chaining_configured, the organization's identity provider is configured to supply this server's access, which it does on the user's first call through the server; this is not provider evidence yet, so do not report it as ready. Callget_my_mcp_connection_statusfor the target MCP, give the user itsconnection_url, and ask them to make one call through it, then continue with step 12: once that call has obtained access, readiness reportsreadywith evidenceidentity_chaining_active. Fall back to theupstream_authorization_requiredroute only if the user asks to connect manually or their call fails. - For a
continue_dashboard_setuporsecure_dashboard_setup_requiredregistration result orrequired_header_missingevidence, present the exactdashboard_setup_url. If none was returned, callget_setup_handoffwith the target project slug, registration ID, and the target'ssource.providerasprovider_keyandsource.referenceascatalog_refexactly as the step-10get_mcpreturned them (for a remote URL these are the platform's direct-remote provider key and the canonical URL), never values retyped from the source project or supplied by the user, and present only its exactsetup_url. The user re-enters every secret there, never in chat; never request the resulting value.
- For
- After the user completes any secure handoff or first chained call, call
get_mcp_readinesswithforce: trueagain from the same connection. Do not rely on stale or inferred readiness. If the state is still notready, return to the routing in step 11 with the new evidence and repeat until it isreadyor the user stops. Forced probes are limited to three per minute for one registration, so never loop them to wait out a handoff. Arate_limitedrefusal means that budget is spent: callget_mcp_readinesswithoutforceto read the last stored check, which is already fresh from the prior probe, and force again only after a minute has passed. - When readiness is current and ready, report the server-returned evidence. Call
get_mcp_client_admissionwith the target project slug and registration ID and present the target's currentmodenext to the source mode. If the source mode was readable, differs from the target's, and appears in the target'sallowed_modes, explain what it admits and refuses, ask for explicit confirmation, then callset_mcp_client_admissionwith that exact mode andconfirmed: true. A source mode absent fromallowed_modescannot be written: report it and leave the target's mode unchanged. Known clients (presets) refuses an unlisted client at authorization with no fallback, so confirm the user accepts that before selecting it. Custom client ID metadata URLs are re-added only in the AICP dashboard. - Call
list_pluginsfor the target project ID, following everynext_cursoruntil the list is complete, present its plugins alongside the source carrying plugins by name, and ask the user which one exact target plugin should carry this MCP. Do not choose for them and do not assume the default plugin. If none fits, such as when the source plugin has no counterpart in the target project, offer to create one: agree its name, choose one idempotency key for this create, and callcreate_pluginfor the target project ID with that key andconfirmed: false. Nothing is created and no receipt is stored; theconfirmation_requiredrefusal carries the exactslugthe plugin would get. Show the user that slug as the plugin's permanent install name; never work a slug out yourself. Say whether the target is the organization's default project (where a new plugin reaches every member) or not (where it reaches no one until people are assigned to it). Only after the user confirms the name and slug, callcreate_pluginagain with the same input, the same idempotency key, andconfirmed: true. If it refuses, no plugin was created and this step has not finished: onslug_taken, calllist_pluginsfor the target project again and ask whether to use the existing plugin with that slug or a different slug, then preview and confirm a different slug only as a new create, with a fresh idempotency key used for both its calls; onidempotency_key_reused, repeat the original request exactly with its original key. Continue only once the user has chosen an existing plugin orcreate_pluginhas returned one, then callget_pluginfor that plugin. If the user wants the source audience mirrored, calllist_plugin_assignmentsfor the target project, present the complete replacement set, state that it changes who receives every MCP server in that plugin, ask for explicit confirmation, then callset_plugin_assignmentswith the immediately precedingassignment_versionasexpected_assignment_version, a fresh idempotency key, andconfirmed: true. - Confirm this exact distribution with the user. If more than a minute has passed since the fresh ready probe, call
get_mcp_readinesswithforce: trueagain from the same connection, then calldistribute_mcp_to_pluginwith the target project slug, the target'smcp_idfrom step 10, and that exact plugin. Reportnot_found,mcp_not_found,ambiguous_target,not_ready, orapproval_requiredas-is and ask again rather than retrying with a different plugin or project. On success, reportmessage. Whenawaiting_manual_syncis true, the target plugin's auto sync is off, so people get the target MCP only after the target project is synced by hand: offerrepublish_pluginfor that project, explaining that it republishes every plugin in it. After explicit confirmation, callrepublish_pluginwithproject_idset to the retained target project ID,pluginset to the exact target plugin selected in step 14,confirmed: true, and a caller-generatedidempotency_key; reuse that key only to retry the same request. If the call refuses or fails, report its reason and any returned dashboard URL, keep the source enabled and the cutover incomplete, and stop this cutover attempt without polling. For anenqueuedoralready_currentoutcome, re-readget_pluginfor that exact target plugin. Publication is asynchronous: continue only whenpublication_evidence.freshistrue. Otherwise report the pending, failed, unknown, or unavailable evidence, keep the source enabled and the cutover incomplete, and stop; resume verification on a later user request. - Access-role rules pinned to the source MCP do not cover the target. Report the roles the source access read listed and use the
manage-mcp-accessworkflow in the target project to re-author them; do not claim access parity. - After distribution, call
get_pluginfor that plugin andget_mcpfor the target again. Report the live attachment,publication_state, and readiness separately. Do not claim that users have the MCP unless the returned live state supports that conclusion. The source is still enabled at this point; do not claim the cutover is complete. - Retiring the source is a separate decision. Before any source retirement, re-read
get_pluginfor the exact target plugin and requirepublication_evidence.freshto betrue, regardless of whether distribution reportedawaiting_manual_sync. If freshness is false, unknown, or unavailable, keep the source enabled and the cutover incomplete; do not calldisable_mcpwhile delivery cannot be confirmed. Once freshness is confirmed and the user explicitly asks to retire the source, callget_mcpfor the source again and present its live state. Explain that disabling makes its endpoint unreachable for users and clears custom-domain roots while keeping the registration, plugins, and roles for rollback. After explicit confirmation, calldisable_mcpwith the source project slug, registration ID,mcp_id, the immediately precedingexpected_version, and a fresh idempotency key. Afeature_unavailableanswer with reasonunsupported_lifecycle_targeton a Platform-managed source means another administrator registered it; do not repeat its message verbatim, and tell the user to retire it in the AICP dashboard, where its registration keeps its project slot until removed. Aconflictmeans the source changed since the last read, for example it became public or was already disabled: callget_mcpagain, present the live state, and renew confirmation; a public source is retired only in the AICP dashboard. Never delete anything. - After disabling, call
get_mcpfor the source and for the target again and report both live states. Rollback stays available:enable_mcpon the source with its freshexpected_versionand a fresh idempotency key restores it, andremove_mcp_from_pluginwith the target project slug, the target'smcp_id, and plugin detaches the target membership this workflow created.
Provider authorization, secret re-entry, and dashboard-only attributes are the expected out-of-agent stops. Source and target selection, the migration inventory acknowledgement, non-secret configuration, and the registration, admission, distribution, and source retirement confirmations remain explicit conversation checkpoints. All selectors and mutation-control fields remain agent-operational.