MCP for SMM Panels: Connect Your AI Assistant to Your Account
What MCP is, how to connect Claude or any AI assistant to your panel account over OAuth 2.1, the 18 tools it gets, and what view-only permission blocks.
MCP, the Model Context Protocol, is an open standard that lets an AI assistant call the tools of an outside system directly, and on this panel it means your assistant can search the catalogue, price an order, place it, and track it using your own account. You do not paste your password anywhere. You do not write any glue code. You add one URL to your AI client, approve the connection on a panel screen you recognise, and from that point the assistant has a fixed, auditable set of eighteen tools pointed at exactly one account: yours.
This guide is the operational version of that sentence. It covers what an MCP server actually is, which two servers Panel Follows runs and which one you are meant to touch, what each of the eighteen tools does, how the OAuth 2.1 handshake works step by step, what the "View only" checkbox really switches off, the order in which the assistant is forced to work before it spends money, and the errors you will meet when something goes wrong. Every screen label below is the exact English text you will see, in quotes, and every limit is a real configured number rather than a round figure invented for a blog post.
One framing note before the detail. An assistant connected over MCP is not a new product, a new pricing tier, or a growth feature. It is a different way to reach the account you already have, sitting alongside the panel interface and the developer API. Nothing it does is invisible: every call it makes is written to an audit log, every order it opens appears in "My Orders" like any other order, and the money comes out of the same prepaid balance. If you already know how the panel works from the complete panel walkthrough, read this as the chapter on the fourth door into the same building.
What Is MCP, and What Does It Mean on an SMM Panel?
MCP is a published protocol for describing tools to a language model and letting the model call them over a normal network connection. Instead of a model guessing at an API from documentation it half-remembers, the server hands it a machine-readable list: here are the tools, here are their exact parameter names, here are the types, here is what each one is for and when to call it.
The practical difference is that the model stops improvising. A model reading prose documentation will invent a parameter called service_id when the real field is service. A model reading an MCP tool schema gets service handed to it, along with the note that it is the number from search_services results. The error rate collapses, not because the model got smarter, but because it stopped having to guess.
On a social media marketing panel, that turns a specific kind of work into conversation. Finding the right service in a catalogue of thousands is a search problem. Working out what 4,200 units cost when the rate is quoted per thousand is arithmetic. Checking which of your twenty running orders has stalled is a filtering problem. All three are things a model does well, and all three are things you currently do by clicking. Connecting an assistant does not add capability to the panel, it moves the tedious half of the work to a place where you can just ask.
There is a second, quieter benefit that matters more over time. Because the tools are typed and documented at the protocol level, the assistant reads the constraints before it acts. It sees that a service has a minimum of 100 and a maximum of 50,000. It sees that a service does not support cancellation. It sees that a price is quoted for a whole package rather than per thousand units. Those are the exact facts people get wrong at three in the morning, and they are now in the model's context automatically rather than depending on somebody remembering to check.
Who Actually Benefits From Connecting an Assistant?
Not everybody needs this. If you place one order a month for your own account, the order form is faster than typing a sentence, and you should stay on the order form. The value curve starts where repetition starts.
Four groups get something concrete out of it. Resellers who run a book of orders and need to know which ones are stuck, without opening twenty rows one at a time. Agencies handling several client accounts, where the question is usually comparative rather than transactional: which service gives the best rate on this platform, what did we spend for this client last month. Developers who are already building against the panel and want to prototype a call before writing the integration properly. And anyone who has ever mis-priced an order by a factor of a thousand, because the assistant is pushed to run a price preview before it can spend anything.
Here is the honest comparison of the three ways to reach the same account.
| Route | Best for | What it costs you | Where it falls down |
|---|---|---|---|
| Panel interface | Occasional orders, visual browsing, payments | Nothing to set up | Slow for repetition, nothing to script |
| MCP assistant | Ad-hoc questions, comparisons, guided ordering | One connection, approved once | Conversational, so not deterministic. Not for high volume |
| Developer API | Production integrations, high volume, your own front end | Engineering time | You have to build and maintain it |
Notice what is missing from that table: a claim that one route is better. They are different shapes. A reseller with a shop front needs the API and will keep needing it. That same reseller asking "which of my orders from this week are still pending" at breakfast is better served by an assistant. Adding funds, incidentally, only happens in the panel interface, and no assistant can do it. That gets its own section further down.
Two Separate MCP Servers: Which One Is Yours?
Panel Follows runs two MCP servers, and telling them apart is the first thing to get straight. They share the same core code but they answer to completely different people.
| User server | Admin server | |
|---|---|---|
| Endpoint | POST /api/mcp/user |
POST /api/mcp |
| Who connects | The panel's customer, meaning you | The panel owner |
| Identity | An OAuth 2.1 token or the account's API key | A single shared secret (MCP_SECRET) |
| Reach | Your own account only, 18 tools | The whole panel, 57 tools |
| Audit record | aiAuditLog with your account id attached |
aiAuditLog with no account id |
The user server is the one this guide is about. It is bound to exactly one account per request, and there is no parameter anywhere in its eighteen tools that lets it look at somebody else's data. It cannot change prices, cannot see providers, cannot touch other accounts, and cannot reach any administrative function. That is not a policy applied at runtime, it is the shape of the tools themselves: the ability was never built into them.
The admin server exists because the panel operator manages the panel through the same protocol. It gets one short section near the end, mostly so you know it exists and know that it is a completely separate door with a completely separate key. If the operator has not configured its secret, that endpoint is closed outright and answers HTTP 503.
Both servers share the same transport layer, which means the protocol behaviour described in the next section is identical on both. Only the tool set and the authentication differ.
What Is an MCP Server, Technically?
An MCP server is an HTTP endpoint that speaks JSON-RPC 2.0 and answers a small set of protocol methods. Here it is deliberately plain: stateless streamable HTTP, no session identifiers, no server-to-client stream.
| Aspect | Behaviour here |
|---|---|
| Transport | Streamable HTTP, stateless JSON-RPC 2.0 |
| Session state | None. No session id is kept between requests |
| SSE stream | Not offered. A GET returns HTTP 405 saying SSE is unsupported and to POST instead |
| Methods | initialize, tools/list, tools/call, prompts/list, prompts/get, ping, resources/list, resources/templates/list |
| Protocol version | Native 2025-06-18; 2025-03-26 and 2024-11-05 also accepted, negotiated during initialize |
| Batching | JSON-RPC arrays are supported. A body containing only notifications gets HTTP 202 with no body |
| Tool errors | Returned as content with isError: true, not as a protocol error |
| Output cap | Tool output is truncated at 100,000 characters |
Two of those rows deserve a sentence each, because they change how the thing behaves in practice.
The isError: true convention is why a mistake does not end the conversation. When you ask for a quantity below a service's minimum, the tool does not throw a protocol-level failure that leaves the client stuck. It returns readable text saying the quantity is out of range, the model reads that text, and it corrects itself and asks you for a number inside the allowed band. Errors become part of the dialogue instead of ending it.
The hints in tools/list are the other one. Every tool arrives tagged with readOnlyHint and destructiveHint, which is how a client knows, before it calls anything, that search_services is safe to run freely and create_order is not. Clients that ask for confirmation before destructive tool calls read exactly these flags. resources/list and resources/templates/list are answered for compatibility and come back empty, because this server exposes tools and prompts rather than documents.
Which 18 Tools Does Your Assistant Get?
Eighteen tools, grouped into five areas. Twelve of them only read, six of them write. The names below are exact, and they are what you will see if you ask your client to list the server's tools.
| Area | Tools |
|---|---|
| Account | get_account |
| Catalogue | list_platforms, list_categories, search_services, get_service |
| Orders | preview_order, create_order, create_orders_bulk, list_orders, get_order, cancel_order, refill_order |
| Refills | list_refills, get_refill |
| Automation | list_events, list_webhooks, create_webhook, delete_webhook |
The read and write split is worth memorising, because it is the same split the "View only" permission uses.
| Class | Tools | Marked destructive |
|---|---|---|
| Read-only (12) | get_account, list_platforms, list_categories, search_services, get_service, preview_order, list_orders, get_order, list_refills, get_refill, list_events, list_webhooks |
No |
| Writing, non-destructive (2) | refill_order, create_webhook |
No |
| Writing, destructive (4) | create_order, create_orders_bulk, cancel_order, delete_webhook |
Yes |
Note that preview_order sits in the read-only column. That is deliberate and it is the hinge of the whole design: pricing an order is a read, so a view-only assistant can still tell you exactly what something would cost. It just cannot buy it.
Every one of these tools mirrors a developer API endpoint, and the parameter names are identical to the ones in the API documentation. There is no second vocabulary to learn. If you know that an order takes service, link, and quantity, with runs and interval for drip-feed, and comments, username, posts, min, max, usernames, hashtag, hashtags, answer_number, groups, keywords, or media for the specialised types, you already know the MCP surface. One vocabulary, two ways in.
What Do the Read-Only Tools Actually Return?
The twelve read tools are where an assistant spends most of its time, so it is worth knowing what each one hands back. This is also the section to read if you are wondering how the model knows things nobody told it.
get_account returns your account id, your email, your available balance in USD, and your request limit. It is what the assistant checks before it prices anything, so that "you have enough for this" is a fact rather than an assumption.
list_platforms returns the platforms in the catalogue with a service count for each. list_categories returns the categories that contain at least one active service, as a slug, a name, and a count. Those two exist so the model learns the valid filter values from the server instead of guessing that a platform might be called "IG".
search_services is the workhorse. Its filters are search, platform, category, type, refill, cancel, dripfeed, min_rate, and max_rate. The page size defaults to 20 and caps at 50. That cap is not stinginess: every result lands in the model's context as text, and a 500-row page is both expensive and distracting. If more is needed, the assistant pages forward with a cursor.
get_service is the one that prevents most mistakes. For a single service it returns the price, the minimum and maximum, whether refill, cancellation, and drip-feed are supported, the average completion time, and, critically, the list of order fields that service requires. The model is instructed not to guess which fields are mandatory. It reads them here.
preview_order validates an order without opening it and returns charge, balance_after, and sufficient_balance. list_orders returns your orders newest first, filterable by the statuses pending, in_progress, completed, partial, canceled, refunded, and failed, defaulting to 20 records and capping at 100. get_order returns one order's current state including the start counter, the remaining quantity, the charge, and any provider error. list_refills and get_refill do the same for refill requests, with get_refill pulling a fresh status from the provider.
list_events walks your account's event feed oldest to newest, and list_webhooks lists your configured endpoints with their subscribed events and recent delivery results. All the listing tools page with a cursor: you pass starting_after, and the response carries next_cursor for the following page. That is how an assistant reads a long order history without pulling all of it at once. Browsing what is actually in the catalogue is easier on the eye in the services list, and the assistant is reading the same data.
See live pricing in the panel
Unit prices for follower, like, view and engagement services are listed live. Registration is free and you can browse the list before adding any balance.
How Do You Connect Your AI Assistant to the Panel?
Connecting takes about a minute and involves no key, no copying, and no configuration file editing if your client speaks OAuth. Open "AI assistant" in the panel menu, which lives at /en/dashboard/mcp.
- The page opens with a "Connection URL" box. Copy the URL. The hint under it states the rule plainly: "Add this URL to your AI client. You approve the connection here in the panel; your password is never shared with the client."
- Add that URL to your AI client as an MCP server. The exact command depends on the client, and the page gives you four ready-made samples, covered below.
- The client sends you back to the panel to approve the connection. Tick "Grant view-only access (cannot place orders)" first if that is what you want, then press "Approve connection".
That is it. The panel's own three steps say the same thing, ending with a suggestion to try something like "show me Instagram follower prices" or "how are my recent orders doing".
The "Setup by client" section on the same page carries four copyable samples. For a command-line client:
claude mcp add --transport http panel https://panelfollows.com/api/mcp/user
For editor-style clients that read a JSON config:
{ "mcpServers": { "panel": { "type": "http", "url": "https://panelfollows.com/api/mcp/user" } } }
For clients that can send headers, where you would rather use a key than the browser flow:
claude mcp add --transport http panel https://panelfollows.com/api/mcp/user \
--header "Authorization: Bearer pf_live_..."
And for a raw check that the endpoint answers, before you involve any client at all:
curl -s https://panelfollows.com/api/mcp/user \
-H "Authorization: Bearer pf_live_..." \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
The setup hint on the page sets the compatibility boundary honestly: "Any MCP client with OAuth 2.1 support works. Clients that can send headers may use an API key instead." That is the whole rule. There is no allow-list of blessed applications, because the server implements the standard rather than a vendor integration.
What Does the OAuth 2.1 Flow Do Behind the Scenes?
If you connect without a key, six things happen between pressing add and having a working assistant, and none of them need you to understand them. They are documented here because knowing the shape helps enormously when something breaks.
- The client asks without credentials and gets a 401. The response carries a
WWW-Authenticate: Bearerheader with anerrorvalue and, crucially, aresource_metadatapointer to/.well-known/oauth-protected-resource/api/mcp/user. That is RFC 9728, and it is how a client discovers where to authenticate without being told. - The client follows the discovery chain. It reads
/.well-known/oauth-protected-resource, which names the authorization server, then reads/.well-known/oauth-authorization-serverfor the endpoint list. That second document is RFC 8414. - The client registers itself. It posts to
/api/mcp/oauth/register, which is RFC 7591 dynamic client registration. The client it creates is a public client with no secret, because authentication is done with PKCE instead. Registration is rate limited to ten attempts per IP per hour, so a broken client cannot spray registrations at the panel. - Your browser opens. The client sends you to
/api/mcp/oauth/authorize, which redirects to the panel's own consent screen at/mcp/connect. If you are not signed in, you are sent to sign-in first and returned to the same consent screen afterwards, with the whole query string preserved, so the flow in your AI client is never abandoned halfway. - You approve. Optionally ticking the view-only box on the way through.
- The client exchanges the code for a token. It posts to
/api/mcp/oauth/token. PKCE with S256 is mandatory. Theplainmethod is not accepted at all, because OAuth 2.1 forbids it.
The discovery document advertises exactly what the server supports, and it is worth reading once if you are debugging a client:
| Metadata field | Value |
|---|---|
response_types_supported |
["code"] |
grant_types_supported |
["authorization_code", "refresh_token"] |
token_endpoint_auth_methods_supported |
["none"] |
code_challenge_methods_supported |
["S256"] |
authorization_response_iss_parameter_supported |
true (RFC 9207) |
resource_indicators_supported |
true (RFC 8707) |
Three details catch people out. Registered redirect URIs may be https, loopback http on 127.0.0.1 or localhost, or a custom scheme such as cursor:// or vscode://; plain http to a remote address is rejected. The redirect address in an authorization request has to match what was registered, and the only flexibility is the loopback port number, which is RFC 8252 behaviour for desktop clients that grab a random free port. And the PKCE verifier has to be between 43 and 128 characters, which is the spec's own range.
One more thing that matters for white-label operators. Every address in those discovery documents is generated from the origin of the incoming request. A customer connecting to a child panel on its own domain gets that domain as the issuer, and the main panel's address never appears anywhere in the flow. Revocation, when you want it, is a single click in the panel or a POST to /api/mcp/oauth/revoke.
When Does Connecting With an API Key Make More Sense?
The browser flow is the default because it needs no secret handling, but it is not the only route. The server accepts three Bearer values, and it tells them apart purely from the prefix, without ever touching the database first.
| Bearer value | What it is | Scope limits apply |
|---|---|---|
pf_mcp_... |
An OAuth access token from the browser flow | Yes, if you chose view-only |
pf_live_... |
A developer API key from the API page | No, always full access |
| 64 hex characters | A legacy reseller API key | No, always full access |
The X-Api-Key header works as well as Authorization: Bearer, for clients that only know one of the two.
Choose the key route in three situations. First, when the client cannot open a browser at all, which covers server-side agents, cron jobs, and anything running headless. Second, when you want the connection to survive without a refresh cycle, because a key stays valid until you revoke it while an access token has to be refreshed. Third, when you are testing, because a curl call with a key is the fastest way to prove the endpoint answers before you blame your client.
Choose OAuth otherwise, and specifically choose it for anything running on a machine you do not fully control. The reason is blunt: a key in a config file is a key that can be read by anything else on that machine, whereas an OAuth connection is revocable from the panel with one click, is visible in "Connected assistants", and can be restricted to view-only. A key has none of those three properties.
That last point is the one to internalise. A connection made with an API key is always fully privileged. Scope restriction is a property of OAuth tokens, not of keys. If you hand a key to an assistant expecting view-only behaviour, you have not restricted anything: the assistant gets all eighteen tools including the four destructive ones. If you want a read-only assistant, use the browser flow and tick the box. Keys are created and rotated on the API page at /en/dashboard/api, and the panel links to it from the MCP page with the note "Prefer connecting with a key? Create one here:".
What Do You See on the Approval Screen?
The consent screen at /mcp/connect is the only place where you grant anything, and it is a panel page on the panel's own domain, not something the AI client renders. That distinction is the whole security model of the browser flow, so it is worth looking at the screen properly once.
The heading names the client that asked, in the form "{client} wants to connect to your account", followed by "If you approve, this app can do the following on your behalf." Underneath that you get four things:
- "Account", showing the email address of the account being connected. If that is not the account you meant, stop here.
- "Will redirect to", showing the host the browser will be sent back to. This is the field that catches a client pretending to be something it is not.
- "What the assistant can do", the same three-line permission summary the dashboard page carries, quoted in full in the next section.
- "Grant view-only access (cannot place orders)", an unticked checkbox.
Then two buttons, "Approve connection" and "Deny". Approving shows "Redirecting…" while the code is issued and the browser is handed back to the client.
Two failure messages live on this screen. "This connection request is invalid or has expired. Start again from your client." means the authorization code request no longer resolves, usually because too much time passed or because the client is not registered. "Could not complete the request. Please try again." is the generic fallback. In both cases the fix is the same and it is not on this page: restart the flow from the AI client, because the client is the party that has to generate a fresh PKCE challenge.
The consent page is deliberately excluded from search engines, and it only means anything inside a flow a client started. Typing that address into a browser out of curiosity will not give you a connection to approve.
What Does "View Only" Actually Switch Off?
There are exactly two scopes, account:read and account:write. If a client asks for nothing in particular, it gets both. If you tick "Grant view-only access (cannot place orders)", the token is issued with account:read only, and something more interesting than a permission check happens.
The server does not merely refuse write calls on a read-only connection. It filters the tool list, so those six tools are never advertised to that client at all. The assistant does not see create_order. It does not see cancel_order. As far as that session is concerned, those tools do not exist.
That design choice is worth explaining, because it is the difference between a guardrail and a suggestion. Telling a model "do not call this tool" leaves compliance to the model, and a model under pressure from a persuasive user is not a security boundary. Removing the tool from the list means there is nothing to call. The instruction text also gains a line telling the model that this connection is read-only and that the user has to remove the view-only permission in the panel and reconnect if they want to order, so it explains the situation rather than looping on a tool it cannot find.
| Capability | Full access | View only |
|---|---|---|
| Search the catalogue, read service details | Yes | Yes |
| Read balance and account summary | Yes | Yes |
Price an order with preview_order |
Yes | Yes |
| List and inspect orders, refills, events | Yes | Yes |
| List webhooks | Yes | Yes |
| Place an order or a bulk batch | Yes | Not offered |
| Cancel an order | Yes | Not offered |
| Request a refill | Yes | Not offered |
| Create or delete a webhook | Yes | Not offered |
View-only is the setting to pick for anything shared, anything experimental, and anything running unattended. You lose nothing analytical: prices, comparisons, order histories, and event feeds are all reads. You lose the ability to spend, which for most connections is exactly what you wanted.
Unrecognised scopes are dropped silently. A client that asks for openid, profile, or email gets none of them, because this server does not issue identity tokens and does not pretend to. It grants what it recognises and ignores the rest.
In What Order Does the Assistant Place an Order?
The server ships a set of instructions to the model on every connection, and those instructions impose a fixed order of operations before anything is bought. This is the part that makes conversational ordering safe enough to use.
- Find the service.
search_services, filtered by platform, category, or free text. Note the service id. - Read the details.
get_servicefor the minimum and maximum, refill and cancellation support, average completion time, and the list of required order fields. - Price it.
preview_order, which returnschargeandsufficient_balance. The model is told to state both plainly rather than summarising them. - Get explicit confirmation from you. The instruction is unambiguous: do not call
create_orderwithout approval, because an order spends real money and cannot be undone. - Place it.
create_order, then report the order number back.
A realistic exchange looks roughly like this. You say you want about five thousand views on a particular Instagram reel and ask what the cheapest reasonable option is. The assistant searches, comes back with three or four candidates in a small table with rates, minimum and maximum quantities, whether refill is offered, and typical completion time. You pick one. It runs the preview and tells you the charge and what your balance would be afterwards. You say go ahead. It places the order and gives you the number. Five tool calls, one decision from you, and a price you saw before you agreed to it.
The instructions carry several more rules that only matter when they are missing. Amounts are USD and come back as decimal strings, and the model is told not to convert them to floating point and round. Quantity always has to sit inside the service's minimum and maximum. On comment-type services no quantity is sent at all, because one comment per line means the line count is the quantity. Cancellation only works when the service supports it and the order has not completed. Refill only works on completed orders on services that carry the guarantee, and it is free and does not touch your balance.
There is one rule about errors that is easy to overlook and important in practice. Error codes are fixed, for example insufficient_balance or quantity_out_of_range, while the message text is in your own language. The model is told to relay the message as it stands and not to invent a workaround. If the panel says the balance is short, you get told the balance is short, not a creative suggestion about how to get around it.
Reading Prices Correctly: per_1000 Against per_order
This is the single most expensive misreading available anywhere in the panel, and the MCP layer handles it by making the unit explicit rather than by hoping.
Every price the catalogue returns carries a pricing.unit field. When it reads per_1000, the number is the price for one thousand units, and the charge is that rate divided by a thousand and multiplied by your quantity. When it reads per_order, the number is the price of the entire package, and quantity does not multiply it at all.
pricing.unit |
What the number means | Example arithmetic |
|---|---|---|
per_1000 |
Rate for 1,000 units | For example, a rate of 1.20 for a quantity of 2,500 gives a charge of 3.00 |
per_order |
Price of the whole package | For example, a package listed at 22.00 costs 22.00, whatever the quantity field says |
Both example figures above are hypothetical and used only to show the shape of the calculation. The real rate is whatever the catalogue returns for the service you are asking about at the moment you ask.
Miss the distinction and you are wrong by a factor of a thousand, in the direction that looks appealing right up until the charge lands. The server's instructions call this out explicitly and the tool description for search_services repeats it, because it is worth saying twice. The signal to look for on a package service is a maximum quantity of 1.
The practical defence is preview_order, and it is why that tool is read-only and free to call. Whatever the rate looks like and however it is labelled, the preview returns the actual charge for the actual order, computed by the same code that will take the money. Ask the assistant to preview before you agree to anything, and the unit question stops mattering. If you have ever been caught by the same trap in the interface, the panel walkthrough explains how the same problem shows up there.
Test this on one post before you scale
The cheapest way to check the logic above is a small order on a single post, then compare the outcome against your own Insights data.
How Do Bulk Orders, Drip-Feed and Special Service Types Work?
Three things people assume will be awkward through an assistant, and none of them are, as long as you know the rules.
Bulk ordering. create_orders_bulk takes up to fifty orders in a single call. Each item accepts exactly the same fields as create_order. Items are processed in sequence, and this is the important part: if one fails, the rest still go through, and every item returns its own result. So a batch is not all-or-nothing, and after a partial failure you get a per-item report showing precisely which lines succeeded. To know the total before committing, run the items through preview_order individually first, because the bulk tool does not price the batch for you.
Drip-feed. Two optional fields, runs for the number of repeats and interval for the minutes between them. They only apply on services flagged as supporting drip-feed, which get_service reports. Ask the assistant to check that flag before it offers you the option, because a service without it will reject the parameters. Spacing delivery out is a scheduling choice and not a safety mechanism, which is the argument made at length in drip-feed and auto services.
Special service types. This is where get_service earns its place. Different service types demand different fields, and the model is told to read the fields list rather than guess.
| Field | Which services want it |
|---|---|
comments |
Custom comment services, one comment per line, line count is the quantity |
username |
Subscription services and the mention types that target an account |
posts, min, max |
Subscription services, covering a number of future posts with a per-post range |
usernames |
Mentions drawn from a supplied list, one per line |
hashtag, hashtags |
Hashtag-driven mention types |
answer_number |
Poll services, the option number to vote for |
groups |
Group invite services, one group per line |
keywords |
SEO services, one keyword per line |
media |
Types that pull from the engagement on a specific post |
The parameter names are identical to the developer API, so anything you learn here transfers straight to an integration and vice versa. If you are building rather than chatting, the API reference is the same surface with the same names.
What Can the Assistant Not Do?
The limits are worth stating as flatly as the panel states them, because a clear boundary is more useful than a long capability list. The dashboard page puts it in three lines, and those three lines are the whole truth.
"Search services, calculate prices, view your orders and balance." That is the read half.
"Place orders, cancel them and request refills. It asks for your confirmation before placing an order." That is the write half, and it exists only on a full-access connection.
"It cannot add funds, withdraw money, see your password or reach other accounts." That is the boundary, and none of it is negotiable through conversation.
Spelled out, the assistant cannot:
- Add funds. There is no top-up tool. Payment happens in the panel interface, on the "Add funds" page, and nowhere else. An assistant that finds your balance short can tell you so and can tell you what the shortfall is, and that is where its involvement ends.
- Withdraw money or move a balance. No such tool exists on this server.
- Change prices. Pricing is administrative and lives on the other server entirely.
- See your password. The panel never shares it with a client, which is the whole point of approving connections on a panel screen rather than typing credentials into an application.
- Reach another account. Every request is bound to one account, chosen by the token, not by a parameter.
- Open a support ticket. Tickets are a panel action. The model is told to send you to the panel rather than pretending.
- Cancel an order the service will not let it cancel. Cancellation is a per-service capability. Where it is unsupported, the tool returns
cancel_not_supportedand a ticket is the route. - Guarantee a refill. Refill only applies to completed orders on services carrying the guarantee. On services without it, a drop after completion is not made good, which is exactly the situation why followers drop and how refills work covers in detail.
None of those gaps are oversights waiting to be filled. Each one is a deliberate boundary between what is safe to do conversationally and what should stay a deliberate action in a browser.
Security: Your Password, the Tokens and Their Lifetimes
Nothing in this flow ever gives an AI client your password. Approval happens on a panel page, the client receives a token, and the token is what it uses from then on. Five kinds of secret circulate, and each has a prefix and a lifetime.
| Secret | Prefix | Lifetime |
|---|---|---|
| Authorization code | pf_mca_ |
10 minutes, single use |
| Access token | pf_mcp_ |
8 hours |
| Refresh token | pf_mcr_ |
90 days, rotated on every use |
| Client id | mcpc_ |
No expiry |
| API key (v3) | pf_live_ |
Until you revoke it |
Four properties of that table matter more than the numbers.
They are all high-entropy random values, 32 bytes of randomness encoded as base64url, so guessing is not a threat model. The prefixes exist so that a string appearing in a log or an error report is identifiable at a glance and so that secret-scanning tools have a pattern to match, and so that the server can tell an OAuth token from an API key without a database lookup.
The database stores only an HMAC-SHA256 digest. The plaintext is not kept anywhere. The digest key is derived from the panel's own secret, which means a leaked database backup on its own does not yield working tokens.
Refresh tokens rotate. Every time one is used, it is replaced. That is what makes the last property possible.
Reuse is treated as compromise. If an authorization code is redeemed twice, or a revoked refresh token turns up, every token that client holds for that account is revoked. The reasoning is that a code being used twice means somebody other than the legitimate client has a copy, and in that situation ending the whole session is the correct response even though it is inconvenient. If an assistant suddenly loses access with no explanation, this is one of the things to consider.
The eight-hour access token is short on purpose. In normal use you never notice it, because a well-behaved client refreshes silently in the background. You notice it when a client does not implement refresh properly, at which point you reconnect from the panel.
What Does the Audit Trail Keep?
Every tool call, on both servers, is written to an aiAuditLog row. That log is what makes an AI-driven account reviewable after the fact rather than a black box.
| Field recorded | Note |
|---|---|
| Client | Taken from the User-Agent, so you can tell which assistant called |
| Account id | Present on user-server calls, absent on admin-server calls |
| Tool name | The exact tool, for example create_order |
| Arguments | With secret-looking fields masked |
| Result | Written for writing tools, not for read tools |
| Error | The failure, when there was one |
| Duration | How long the call took |
Three behaviours in that table are deliberate and worth understanding.
Secret-looking argument fields are masked with *** before anything is stored. The masked names are apikey, api_key, secret, password, passphrase, and token. So if a client ever passes something sensitive as an argument, it does not end up sitting in a log table in the clear.
Results of read tools are not stored. A catalogue search returning fifty services would balloon the log for almost no investigative value, so only writing tools have their results recorded. That is the pair you actually need later: what was asked for, and what happened. Both the argument and result JSON are truncated at 8,000 characters, which keeps a pathological payload from filling the table.
Audit writing is best-effort. If the log write fails, the main operation is not broken. That is the right trade: a logging problem should not stop your order from going through.
The practical payoff is small but real. Because the log records which client called which tool, it is possible after the fact to see whether an order came from an assistant or from the panel. If you run a team, or an agency where several people touch the same account, that is the difference between a mystery and an answer. The broader operational side of running an account other people can touch is covered in scaling a social media agency.
How Do You See and Cut Connected Assistants?
Everything you have approved is listed on the same page you connected from, under "Connected assistants". If nothing is connected yet it reads "No AI assistant is connected yet."
Each connected assistant shows five things:
- The client name, which is whatever the client registered itself as.
- A badge reading either "Full access" or "View only", so the permission level is visible at a glance rather than hidden in a detail view.
- "Connected", the date the connection was approved.
- "Last used", the date of its most recent call, or "Never" if it has not called anything yet.
- A "Disconnect" button.
Pressing "Disconnect" asks first, with "This assistant will lose access to your account. Continue?" Confirm and you get "Disconnected." If something goes wrong you get "Could not disconnect." and the row stays.
Disconnecting is immediate and total. Every token that client holds for your account stops working, including refresh tokens, so it cannot quietly renew itself. The next call it makes gets a 401, and reconnecting means going through the whole browser approval flow again from the client side. There is nothing to clean up on your end.
Two habits are worth adopting. Check "Last used" occasionally, because a connection that has not been used in months is a connection you no longer need, and the cheapest security measure available is removing access nobody is exercising. And read the "Full access" badges specifically, because that is the list of clients that can spend your balance. If one of them surprises you, disconnect it and reconnect with view-only ticked.
Note the asymmetry with API keys. An API key connection does not appear in "Connected assistants", because there is no per-client record to show: the key is the credential, and every client using it looks identical. Revoking a key is done on the API page and cuts off everything using it at once. That is another argument for OAuth on anything you might want to revoke selectively.
Automation: The Event Feed and Webhooks
The assistant can also wire up automation, which is the part that turns a chat interface into something that keeps working while you are not chatting. Two tools read, two tools write.
list_events walks your account's event feed with a cursor, oldest to newest, optionally filtered by type. Eight event types exist and their names are never translated:
| Event | When it fires |
|---|---|
order.created |
A new order has been opened |
order.processing |
The provider has accepted it and started work |
order.completed |
The order finished |
order.partial |
Delivered in part, with the undelivered share refunded |
order.canceled |
The order was cancelled |
order.updated |
Something about the order changed |
refill.created |
A refill request was opened |
refill.updated |
A refill request changed state |
create_webhook registers an https address to receive those events, optionally narrowed to a subset. One detail matters enormously: the signing secret is shown once in the response and never again. If your assistant creates a webhook for you and you do not store the secret at that moment, you cannot recover it, and you will have to delete the endpoint and create a new one. Tell the assistant to show you the secret and put it somewhere safe before you move on. delete_webhook removes an endpoint permanently and drops its pending deliveries with it. list_webhooks shows what you have, what each is subscribed to, and how recent deliveries went.
The event feed exists specifically for people who cannot receive webhooks. If you are developing locally, or you do not have a fixed public address, or you simply do not want to run a listener, list_events gives you the same information by polling with a cursor. You store the last cursor you processed, ask for what came after it, and you have missed nothing. It is the difference between needing infrastructure and needing a loop.
For an assistant, the useful pattern is neither of those. It is simply asking. "Anything change on my orders since yesterday" is a list_events call with a filter, and the answer is a short summary instead of a table you have to read. The same feed and the same event names are documented for direct use in the developer API reference.
Ready-Made Prompts: order_status, find_service, reorder
MCP servers can publish prompts as well as tools, and this one publishes three. They appear in clients that support the prompts feature, usually as slash commands or a picker, and they exist so that the three most common jobs do not depend on you phrasing them well.
| Prompt | What it does | Arguments |
|---|---|---|
order_status |
Summarises recent orders and flags the stalled or short ones | count, defaults to 10 |
find_service |
Compares three to five services for a request and prices them, without ordering | request (required), quantity |
reorder |
Reopens the same order as a past one, after asking you to confirm | order_id (required) |
Each of these is a carefully written instruction rather than a magic function. order_status tells the model to call list_orders with the service details included, then list every order with its number, service name, status, quantity, remaining amount, and charge, then separately highlight anything incomplete or carrying a provider error and say what could be done about each: cancel, refill, or wait.
find_service is the one to reach for when you do not know what you want yet. It instructs the model to search, then present the three to five most sensible candidates in a comparison table covering price, minimum and maximum, refill guarantee, and average completion time, and if you supplied a quantity, to price the best candidate with preview_order. Its final instruction is the important one: do not place an order, present the options and the price and leave the decision to the user.
reorder reads a past order with get_order, runs preview_order for the same service, link, and quantity, shows you the current price, and asks for confirmation before doing anything. It also carries a warning instruction: if there is still a running order on the same link, say so first. That is a real trap rather than a theoretical one, since duplicate orders on a live link are commonly rejected by the provider.
If your client does not support prompts, you lose nothing structural. Asking "summarise my last ten orders and tell me which are stuck" gets you the same work done, just without the prewritten wording.
What Changes for Resellers and Child Panel Owners?
If you run a white-label child panel, MCP arrives on your panel already branded, and there is nothing for you to configure to make that happen.
Your customers see the same "AI assistant" menu item on your own domain. The connection URL they copy is your domain plus /api/mcp/user. The consent screen they approve is your domain plus /mcp/connect. And because every address in the OAuth discovery documents is generated from the origin of the incoming request, the issuer their client records is your domain too. The main panel's address does not appear anywhere in the flow, in any document, at any step. The white-label boundary holds all the way down to the protocol layer.
That has a consequence worth stating clearly, because it produces a confusing support ticket otherwise. An account created on a child panel exists only on that panel's domain. If one of your customers tries to connect an assistant to a different address, authentication fails, and the failure looks like a wrong credential rather than a wrong address. When a customer says the connection will not authenticate, the first thing to check is which domain their client is pointed at.
For your own account as a reseller, the assistant is most useful on the parts of the job that are repetitive and comparative. Which services in a category have the best rate right now. Which orders from this week have not completed. What a batch of fifty orders would cost before you commit to it. None of that is glamorous, and all of it is time you currently spend clicking. The business side of that work is covered in starting an SMM reseller business, and the commercial arrangement itself on the child panel page.
Two limits to keep in mind. There is no cross-account view: an assistant connected to your reseller account sees your account, not your customers' accounts, and there is no tool that changes that. And your customers connect their own assistants themselves, from their own logins, with their own approvals. You do not connect on their behalf and you do not see their connections.
On the Panel Owner's Side: the 57-Tool Admin Server
For completeness, since it is the same protocol: the panel itself is administered over MCP too, through a completely separate server.
It answers at POST /api/mcp, authenticates with a single shared secret held in the panel's own configuration, and exposes 57 tools spanning overview and search, users, orders, order requests, services, categories, providers, payments, support tickets, coupons, and settings. If that secret is not configured, the endpoint is closed and returns HTTP 503 rather than falling back to anything. The credential can arrive as Authorization: Bearer, as an X-MCP-Secret header, or as a key query parameter, and the comparison is constant-time.
The guard rails on that side are stricter than on the user side, as they should be. The last remaining active administrator cannot be demoted or banned, which removes the most obvious way to lock everybody out. Money operations run inside a transaction with a row lock, so a balance cannot be double-spent by two concurrent calls. And every single call lands in the same audit log described earlier, with the account id field empty to mark it as an administrative action.
The reason to mention it here at all is not to advertise it. It is to tell you that the panel you are ordering from is operated through the same protocol, with the same logging, under stricter constraints. That is context for how seriously the user-side boundaries are meant, not an invitation to go looking.
Open your account and order in minutes
Signing up is free and takes two steps. Top up with card, bank transfer or crypto, place your order and track delivery from the dashboard.
MCP, API, or the Panel: Which Should You Use When?
Three routes, one account. The question is not which is best but which fits the task in front of you.
| Task | Panel | MCP assistant | Developer API |
|---|---|---|---|
| Add funds | Only route | Not possible | Not possible |
| Browse the catalogue visually | Best | Workable | Possible but awkward |
| Compare five services on price and features | Slow | Best | Needs code |
| Place one careful order | Good | Good, with a price preview first | Overkill |
| Place two hundred orders | No | No | Only sensible route |
| Check which orders are stuck | Manual | Best | Good, needs code |
| Run a scheduled job | No | No | Yes |
| Open a support ticket | Only route | Not possible | Not possible |
| Set up a webhook | Not in the interface | Yes | Yes |
| Prototype a call before building | No | Best | That is the target |
A rule of thumb that holds up. If a human is deciding, use the panel or an assistant. If a program is deciding, use the API. If a human is deciding but does not want to do the reading, that is exactly what the assistant is for.
The three are not mutually exclusive and most serious users end up with two of them. A reseller runs the API for their shop front and keeps an assistant connected for the questions the shop front does not answer. An agency runs the panel for the visual work and an assistant for the comparisons. The commercial framing for each is on the reseller panel page and the agency page.
What Are the Rate Limits?
Two limits apply, and they are shared with the developer API rather than being separate allowances.
| Limit | Value | Response when exceeded |
|---|---|---|
| Per account | 600 requests per minute | HTTP 429 with Retry-After: 60 |
| Per IP, at the API layer | 900 requests per minute | HTTP 429 |
The per-account ceiling is the same number the API uses, and it is genuinely shared: it makes no difference whether a given request arrived over MCP or as a direct HTTP call, they count against the same budget. If you are running an integration at volume and also have an assistant connected, both draw from the same 600.
In practice this is not a limit a conversational assistant will ever reach. A thorough exchange, searching, reading a service, previewing, ordering, and checking, is a handful of calls. Six hundred a minute is a wall built for scripts, not conversations. You will only meet it if something has gone into a loop, which is itself the useful signal: a 429 on an assistant connection usually means a client is retrying badly rather than that you are being throttled for legitimate work.
If you do see one, the correct response is in the header. Retry-After: 60 means wait a minute. Retrying immediately makes it worse, because the failed attempts count too.
Troubleshooting: Common Errors and What They Mean
Most connection problems fall into a small set of causes, and the symptom usually identifies the cause exactly.
| Symptom | Cause | Fix |
|---|---|---|
401 invalid_token |
The 8-hour access token expired | A well-behaved client refreshes on its own. If it does not, reconnect from the panel |
405 in a browser |
Someone tried a GET, expecting SSE |
The protocol only accepts POST. This is not a fault |
429 |
The 600 requests per minute ceiling was crossed | Wait the number of seconds in Retry-After |
| "This connection request is invalid or has expired." | The authorization code passed its 10 minutes, or the client is not registered | Restart the flow from the client, not from the browser |
The token endpoint returns 400 |
The client sent plain PKCE |
Only S256 is accepted |
| The redirect is rejected | redirect_uri does not match what was registered |
The only flexibility is the loopback port number |
The endpoint returns 503 |
The admin server has no secret configured | Concerns the panel owner only, not customers |
| The account is not found | A child panel account was used on the wrong domain | Connect to the domain the account was created on |
Two of those deserve expanding, because the obvious reading is the wrong one.
The 405 on a GET is not a broken server. Some clients probe for a server-sent-events stream before falling back, and some people paste the URL into a browser to see if it is alive. Both produce a GET, and both get a 405 with an explanation that SSE is not supported and that JSON-RPC goes over POST. The endpoint is working exactly as designed. A curl POST with a tools/list body, like the sample earlier in this guide, is the real liveness check.
The expired authorization code is the one people try to fix from the wrong end. The code lives for ten minutes and is single use, which is generous for a flow that normally takes fifteen seconds. If you started a connection, walked away, and came back to an expired consent screen, reloading the panel page will not help, because the code and the PKCE challenge are generated by the client. Go back to the AI client and start the connection again from there.
And a reminder from the security section, because it produces a symptom that looks like something else entirely: if an authorization code is redeemed twice or a revoked refresh token is presented, every token that client holds for your account is revoked. An assistant that abruptly loses access with no expiry in sight may have tripped exactly that protection.
A Practical First Session With a Connected Assistant
If you have just connected, here is a sequence that proves each part works before you rely on any of it. It takes a few minutes and it is worth doing once.
- Ask what tools it has. The answer should list eighteen tools, or twelve if you connected view-only. If the count is twelve and you did not intend that, disconnect and reconnect without ticking the box.
- Ask for your balance. That is
get_account, and it proves authentication end to end. If this fails, nothing else will work, and the problem is the connection rather than anything you are asking. - Ask what platforms are available.
list_platforms, cheap, and it shows you the vocabulary the catalogue actually uses. - Ask it to compare a few services for something specific. Name a platform and a goal. You should get a small table with rates, minimum and maximum quantities, refill support, and average time. This is the moment where you find out whether the catalogue has what you assumed it had.
- Ask for a price on a specific quantity. That is
preview_order. Check the charge against your own arithmetic once, specifically to confirm you have understood whether the service is priced per thousand or per package. - Place one small order, at the service minimum. Confirm explicitly when it asks. Note the order number it gives you.
- Open "My Orders" in the panel and find that order. It should look exactly like an order you placed by hand, because it is one. That check is the point of the whole exercise: it tells you the assistant is a different door, not a different system.
- Ask about the order the next day.
get_ordergives you the start counter and the remaining quantity, which is the same evidence the panel row shows.
If step 6 makes you uncomfortable, that instinct is worth respecting: connect view-only, use the assistant for research and pricing, and keep placing orders yourself. The read half is most of the value for most people, and you can always widen the permission later by disconnecting and reconnecting. If you have not got an account yet, registration takes under a minute and the assistant can be connected the same day.
Frequently Asked Questions
What is MCP, in one sentence?
MCP, the Model Context Protocol, is an open standard that describes a system's tools to an AI assistant in a machine-readable way so the assistant can call them directly instead of guessing at an API. On this panel it means your assistant can search the catalogue, price orders, place them, and track them through your own account. It is a protocol, not a product, so any client that implements it can connect.
Do I have to give my AI assistant my panel password?
No, and there is no way to do so through this flow. You approve the connection on a panel page on the panel's own domain, and the client receives a token rather than a credential. The panel states it directly on the connection page: your password is never shared with the client, and the assistant cannot see it.
Can the assistant place an order without my permission?
The server instructs the model to price the order first, state the charge and whether your balance covers it, and get your explicit approval before calling create_order. If you want a stronger guarantee than an instruction, tick "Grant view-only access (cannot place orders)" when you approve the connection, because that removes the ordering tools from the list entirely rather than asking the model to avoid them. On a view-only connection there is no order tool to call.
Which AI clients are supported?
Any MCP client that supports OAuth 2.1, which is how the panel puts it on the setup card. Clients that can send HTTP headers can connect with an API key instead of the browser flow. There is no approved list of applications, because the server implements the published standard rather than integrating with specific vendors, and the setup section gives ready-made samples for command-line clients, editor-style clients that read a JSON config, header-based key connections, and a raw curl check.
How do I disconnect an assistant, and what happens afterwards?
Open "AI assistant" in the panel menu, find the row under "Connected assistants", and press "Disconnect". You are asked to confirm with "This assistant will lose access to your account. Continue?" and on success you see "Disconnected." Every token that client holds for your account stops working immediately, refresh tokens included, so it cannot renew itself, and reconnecting means going through the approval flow again from the client.
How long does an access token last, and will I have to reconnect?
An access token is valid for 8 hours and a refresh token for 90 days, rotating each time it is used. In normal use you never notice either number, because a properly built client refreshes the access token in the background without involving you. You only need to reconnect if your client does not implement refresh, or if a security event revoked the tokens, such as an authorization code being redeemed twice.
Can the assistant add funds to my balance?
No. There is no top-up tool, no withdrawal tool, and no way to move money on this server. The panel states it plainly: the assistant cannot add funds, withdraw money, see your password, or reach other accounts. It can tell you that your balance is short and by how much, and adding funds happens on the "Add funds" page in the panel interface.
Should I connect with an API key or with OAuth?
Use OAuth unless you have a reason not to, because an OAuth connection can be restricted to view-only, appears in "Connected assistants" with its permission badge and last-used date, and can be revoked with one click. Use a key when the client cannot open a browser, such as a headless or server-side agent, or when you want a credential that does not need refreshing. Remember that a key connection is always fully privileged: scope restriction only applies to OAuth tokens.
Can I see the orders the assistant placed in the panel?
Yes, and they are not marked differently. An order placed through MCP appears in "My Orders" like any other, with the same statuses, the same start and remaining counters, and the same refill and cancel buttons. The difference is recorded in the audit log rather than the interface, where every call keeps the client name, the tool called, the arguments, and the result, so it is possible to establish afterwards whether an order came from an assistant or from the panel.
Can child panel customers connect their own assistants?
Yes, and everything they see is on your domain. The connection URL, the consent screen, and the OAuth issuer are all generated from the origin of their request, so the main panel's address never appears in their flow. Remember that accounts are tied to the panel they were created on, so a customer connecting to the wrong domain will get an authentication failure that looks like a bad credential rather than a wrong address.
What happens if a tool returns an error?
Nothing breaks. Tool errors come back as readable text flagged with isError: true rather than as protocol failures, so the model reads the message and can correct itself, for example by asking you for a quantity inside the service's allowed range. Error codes are fixed, such as insufficient_balance or quantity_out_of_range, while the message text is in your own language, and the model is instructed to relay it as it stands rather than invent a workaround.
Does using MCP cost extra?
There is no additional charge for connecting an assistant or for the calls it makes. Orders cost what they cost, taken from the same prepaid balance as any order placed in the panel, and preview_order will tell you the exact charge before anything is spent. The rate limit of 600 requests per minute per account is shared with the developer API rather than being a separate paid allowance.
Can the assistant cancel an order or request a refill?
On a full-access connection it can call both, but neither is unconditional. Cancellation only works when the service supports it and the order has not completed, and where it is unsupported the tool returns cancel_not_supported and a support ticket is the route instead. Refill only applies to completed orders on services carrying the refill guarantee, it is free and does not touch your balance, and on services without the guarantee it is not available at all.
Where do I go if something is still unclear?
The connection page itself is the shortest reference, at /en/dashboard/mcp, because it shows your live connection URL, the setup samples for your client, and the permission summary in one screen. For the parameter names and endpoint behaviour behind each tool, the developer API documentation is the same surface written for integrators. For everything about how the panel itself works, the FAQ and the full panel walkthrough cover the ground this guide assumes.