SuperAPI AI Directory

expose-tools-on-mcp

Deployment & operations

View repository

Updated 11-02 22:48

About this skill

Put a tool on an MCP server

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. Publishing a tool to a project regenerates what the project can offer; it does not put that tool in front of anybody. Which tools one MCP server exposes is a separate, deliberate decision, and this workflow is how an administrator makes it: read the project's tools, read what the server exposes today, then add or remove named tools against the exact list that was read. When the project has no server that fits, the same workflow can create one whose tools are tools the project's functions already produce.

This is not the same as adding a server to a plugin. A plugin decides which servers people receive; this decides which tools one of those servers offers. For putting a whole server in front of people, use add-mcp-from-catalog, add-mcp-from-remote-url, or the plugin workflows instead.

It is not available to managed project assistants: the change reaches everyone holding an affected plugin the moment it commits, so it stays on the surface an administrator drives directly. Installing this package grants no organization or project access.

Safety rules

  • Only a server whose tools come from this project can be changed here. A server that fronts an upstream offers whatever that upstream offers, and a server set up before AICP tracked servers separately has no record to change; both refuse with a message pointing to the AICP dashboard. Report that refusal and stop rather than looking for another route.
  • Keep the project, the MCP server, and every tool explicit. Never infer any of them from prior context, a display name, or the Default project, and never substitute a similar-looking tool.
  • Never guess a tool identifier. Use an exact identifier a tool returned: for an addition, the one from the project's tool list; for a removal, that or the one from the server's own tool_exposure.tool_urns. Never type one out from a display name or from memory.
  • The project's tool list governs additions only. A tool missing from it cannot be added, most often because the deployment that produces it has not finished; say so rather than adding something else. A removal is not bound by it: a tool the project has stopped producing can still be sitting on the server, and taking that orphaned entry off is exactly what a removal is for.
  • State the blast radius before acting. Changing a server's tool list republishes every plugin that carries that server, and everyone holding one of those plugins receives the change immediately. Removing a tool takes it away from those same people, including anyone in the middle of using it.
  • Every change carries the exposure version from the read it was based on, a fresh idempotency key, and confirmed: true only after the user has confirmed the exact server and the exact tools. Never send confirmed: true on the user's behalf.
  • Never retry a mutation on your own initiative. Every conflict, denial and refusal goes back to the user: report what happened and what it means, and act again only when they ask. Never retry against a different server, and never widen the tool list to make a call succeed. What changes between outcomes is what you take back to them — a fresh read for a conflict, a wait for a rate limit, a dead end for a structural refusal — never whether they are involved.
  • A refusal saying the tool list is shared beyond what this change can reach is final, not a race. It means the same set of tools is offered by more MCP servers than can be changed from here, or by a server outside the chosen project, so re-reading and repeating reaches the same answer every time. Report it, send the user to the AICP dashboard, and stop; never loop back to a fresh read on it.
  • Nothing is dropped silently. A tool this project does not produce and the server does not already expose refuses the whole request by name and changes nothing. A tool already in the state the user asked for is reported as unchanged, not as a change.
  • Secrets never enter chat. This workflow needs none; never ask for or accept credentials, tokens, headers, or authorization values, and never present a link a tool did not return.
  • Use send_platform_mcp_feedback only after asking for consent, and never include identifiers, URLs, credentials, payloads, or logs.

Workflow

  1. Call list_projects with limit: 100 to confirm 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. If truncated: true, report that project discovery is incomplete and hand off to the AICP dashboard. Ask the user to choose one exact project.

  2. Call list_project_tools for that exact project. Use query to narrow by tool name or by the function or API document that produced it, and follow every next_cursor with cursor and the same filters until the list is complete. Each entry names the tool, its exact identifier, and the source it came from, so the user can tell two similarly named tools apart. If a tool the user wants added is absent, report that the project's latest completed deployment does not produce it yet and stop; publishing again and waiting for that deployment to finish is the fix, not a different tool. For a removal, do not stop here: a tool the project no longer produces can still be on the server, so carry on through step 3 — the user still has to choose the exact server — and then take the exact identifier from that server's own tool_exposure.tool_urns in step 4.

  3. Call find_mcp for that exact project with limit: 100, following every next_cursor with cursor and no query until the list is complete. Ask the user to choose one exact MCP server. Do not choose for them and do not assume there is only one. If the project has no server, or the user says none of them fits, offer to create one instead and follow the next paragraph; never create a server the user did not ask for.

    Creating a server. This is for an addition only, and only from tools a function produced: every tool must come from list_project_tools with source_kind function. A tool generated from an API document cannot be used here; say so and point the user at the AICP dashboard. Ask the user for the new server's name. First call create_mcp_from_functions with the exact project ID, the name, the exact tool identifiers, one new idempotency key for this creation, and without confirmed: true; it creates and records nothing and answers confirmation_required with a preview. Never work out or describe a slug yourself. Show the user the preview values as returned before asking for confirmation: toolset_slug is exact, and mcp_slug_prefix is only the start of the server's address, because a short suffix is added at creation — say that, and never present the prefix as the full address. When preview.would_join_default_plugin is true, this would be the organization's first server: say plainly that it joins the project's Default plugin on creation, so everyone holding that plugin receives it. Present the exact project, the name, the preview, who will receive the server, and each tool by name and identifier, and ask for explicit confirmation. After they confirm, call create_mcp_from_functions again with the same project, name and tools, the same idempotency key the preview used, and confirmed: true. Use a new key only when starting a new attempt after a refusal. A refusal naming tools means nothing was created: those tools are not produced by the project's latest completed deployment, so return to step 2 rather than creating a server without them. A refusal saying the name is taken means nothing was created: ask the user for another name. On success, report the full mcp_slug the result returns and the returned exposure (a fresh read, not the list that was asked for), and read index_signal exactly as step 10 describes. Then say who receives the server from the result, never from the preview: when added_to_default_plugin is true, tell the user it joined the project's Default plugin and everyone holding that plugin receives it. publication_requested means a refresh of their plugin was requested, not confirmed delivered: never tell the user people already have the server. When added_to_default_plugin is false, say plainly that the new server reaches nobody until it is put into a plugin, and offer the plugin workflow as the next step. Skip steps 4 to 8 for a server you just created; it already exposes exactly the confirmed tools.

  4. Call get_mcp with that project ID and that exact mcp_id. Read its tool_exposure. If tool_exposure is absent, this server's tools are not this project's to change: say so, point the user at the AICP dashboard, and stop. Otherwise present the tools it exposes today next to the tools the user wants added or removed, and say explicitly which of them are already in the requested state. When tool_exposure.next_tool_cursor is present, the list is only part of what the server exposes: call get_mcp again with the same project and server and tool_cursor set to it, and repeat until it is absent. Present the server's tools only once that read is complete. Keep tool_exposure.exposure_version from the page that completed the read internally; it describes the whole list and is the read this change will be checked against. A partial page carries no exposure version, so a change can never be confirmed against a list only partly read. A conflict while paging means the list changed mid-read: start again from the first page. When tool_exposure.truncated is true but no next_tool_cursor came back, the rest of the list cannot be read from this connection: say that the list shown is partial and that no change can be confirmed against it, stop before requesting confirmation or calling a mutation, and hand off to the AICP dashboard. Do not infer platform-tool absence, automatic-membership changes, or restored eligibility from a partial list.

  5. Present the exact change and its reach before asking for confirmation: the project, the server, each tool by name and identifier, and every plugin listed in the server's distributions. State that the change requests republication of affected plugins: supported clients receive updates on their normal refresh cycle, while locally installed ZIPs require replacement. For removals, warn that people holding the listed plugins lose the removed tools when the change reaches them. Check the server’s current tool list as well as the requested edit: if the edited toolset still contains any platform tool, the entire toolset is excluded from automatic distribution and proven automatic role-plugin memberships are removed, even when the edit only adds ordinary tools. Explicit manual memberships and MCP access are preserved. Removing the last platform tool restores eligibility for automatic distribution while preserving prior removal history; do not promise previously removed memberships will be reattached. When tool_exposure.shared_with_other_servers is above zero, say how many other MCP servers offer this same set of tools and that the change moves all of them together; the change is allowed only with permission to change every one, and is refused outright otherwise. The count covers only servers in the chosen project, so a change can still be refused as shared beyond this workflow's reach even when it reads zero. Ask for explicit confirmation of that exact list.

  6. After explicit confirmation, call add_tools_to_mcp — or remove_tools_from_mcp for a removal — with the exact project ID, the exact mcp_id, the exact tool identifiers, the exposure_version from the step-4 read, a fresh idempotency key, and confirmed: true. Send the identifiers exactly as they were returned — character for character from list_project_tools for an addition, or from the server's tool_exposure.tool_urns for a removal.

  7. Read the result before reporting anything. outcome: applied with an empty unchanged means every named tool moved. outcome: applied with a non-empty unchanged is a partial result: name which tools moved and which were already in the requested state. outcome: no_op means nothing needed changing, so do not announce a change or a republish. exposure is a fresh read taken after the change committed; report that list, not the one that was asked for. When it carries next_tool_cursor, it is only the first page: read the rest through get_mcp with tool_cursor before reporting the full list. When snapshot_scope is not fresh_read_after_commit, the change committed but could not be read back: say that rather than claiming the final state.

  8. A refusal naming tools means nothing was changed at all, not that part of the request landed. Present the named tools, explain that they are neither produced by this project nor already on the server, and return to step 2. A conflict means the server's tool list changed between the read and the write, so the change was refused to avoid overwriting somebody else's edit: return to step 4, present the list as it stands now, and ask the user to confirm again against it. Never reuse the old exposure version, and never retry with the same idempotency key and different tools. A refusal saying the tool list is shared beyond what this change can reach is the one refusal that does not return to a fresh read: it is about how the servers are set up, not about a race, so report it, point the user at the AICP dashboard, and stop. A rate-limit refusal is neither a race nor a dead end: nothing was changed and the same call will work later, so say that it was throttled and that the change has not been made, and repeat it unchanged only when the user asks you to — never on a timer of your own.

  9. Report any removed_plugin_ids as plugins whose automatic membership this edit removed; distributions lists the remaining memberships. Report the publication outcome separately from the change. publication_request and publish_signal describe whether a package refresh was requested, not that plugins or the people holding them have converged. Verify with a fresh get_mcp if the user needs the settled state, and never claim people already have the tool unless the returned live state supports that.

  10. Check index_signal on every applied change, and say something when it is neither requested nor not_required. A server set to dynamic tool selection cannot list any tools at all while its current tool list has no search index, so unavailable or request_failed means the change landed but that server may answer nothing until the platform's periodic sweep rebuilds the index. Tell the user plainly, say it normally recovers without action once the platform is healthy, and point them at the AICP dashboard if it does not. On a retry with the same idempotency key, rate_limited means only that this retry did not schedule the rebuild again because the retry allowance is spent; the original call already requested it, so report the original result and do not retry to get a different signal. Never present this as the change having failed — it did not — and never retry the mutation because of it.

  11. Removal is the counterpart of the same design and follows exactly the same steps, with the same boundary as step 8, except that its identifiers may come from the server's exposed list rather than the project's. A name that is neither produced by this project's latest deployment nor already on this MCP server is refused by name, so a mistyped identifier is never mistaken for "already gone". A tool the project does produce but the server does not expose is not refused: it comes back as unchanged, because there was nothing to remove. Report that as "it was not on the server", never as a refusal. A tool the project no longer produces can still be removed, because the server is still offering it — so a removal never waits on a deployment, and never asks the user to publish a tool again in order to take it away.