Migrate and change
Migration guide
Move from today's 13 MCP tools and the RunExtract endpoint to v2. A table maps every old call to its replacement, with worked before and after examples.
Every one of today's 13 MCP tools has a replacement among the 22 new ones, and the legacy download endpoint has one too. The plan is a 12 month window from the v2 launch, with 3 emailed notices, in which today's tools, keys and installs keep working while you move. Installs at today's MCP URL, https://mcp.mydatahub.uk, are planned to keep working: it passes MCP through to the new server at https://mydatahub.emailmovers.com/mcp for at least 12 months, with no redirect, and credit balances and saved lists are planned to carry over.
- The
X-Api-Keyheader andPOST /api/RunExtract. The v2 API does not accept the header, has no such route and sends noDeprecationorSunsetheader. - Tokens from today's OAuth issuer. The v2 API refuses any bearer token that is not an
mdh_key. - The redirect from
agents.mydatahub.ukto this site, page for page.
Moving traffic to v2 without them would break today's integrations and installs. The launch has to leave today's endpoints where they are until they exist (to confirm).
What changes
| Today | After migration, as planned |
|---|---|
| 13 MCP tools that hold a session on the server | 22 tools where every call carries its own audience |
Numeric structureId values | Field names such as company.employee_band, from list_filters |
incEx codes 0, 1 and 2 | in to include, and in under not to exclude |
create_data_extract with confirmed true | quote_order, then create_order with a person's confirmation or an agent key |
POST /api/RunExtract with X-Api-Key | GET /v1/orders/{id}/records with Authorization: Bearer |
| Credit balance | An opening ledger entry, one for one, with licence dates unchanged |
| Saved lists | Audiences with aud_ ids, in the same organisation |
| 29 export bundles, five of them tests | Named field sets, 1 credit a record each. The test bundles are removed |
agents.mydatahub.uk | This site, with a permanent redirect page for page (not built) |
The plan is for the old tools, the X-Api-Key header and POST /api/RunExtract to stay available for 12 months from the v2 launch date, with 3 emailed notices, and then to return 410, with Deprecation and Sunset headers on legacy download responses (RFC 9745 and RFC 8594). Only the old MCP tools are built so far. The v2 API has no X-Api-Key handling, no /api/RunExtract route and no deprecation headers yet, so those parts of the plan are to confirm.
The 13 tools
The old tools are listed after the 22 new ones, with "Deprecated" in their titles and the replacement named in each description. preview_records is the thirteenth: the new tool keeps the name and replaces it in place, so a name appears once in tools/list. The last column says what each old tool does on the v2 server now.
| Today's tool | Replaced by | What the replacement does | During the window |
|---|---|---|---|
available_selection_categories | list_filters | It returns every field by name, such as company.employee_band, with its type, operators and value count. | Runs list_filters and answers with its result. |
available_selection_values | search_filter_values | It takes the field name and search text and returns codes with contact and company counts. | Runs search_filter_values. A structureId with no field, or an offset above 0, is refused with a next step. |
search_discrete_values | search_filter_values | It returns codes with counts and did_you_mean for near misses. | Runs search_filter_values with the first value as the search text. A structureId with no field is refused with a next step. |
create_search | compile_audience, then count_audience | count_audience takes the whole audience definition in one call, so there is no session state to build up, and compile_audience turns a brief into that definition. | Counts the filters you send with count_audience. A range, or a structureId with no field, is refused with a next step. Nothing is kept for later calls. |
clear_search | count_audience | There is nothing to clear: every count_audience call carries its own audience. | Succeeds and does nothing, because there is no session to clear. |
get_count | count_audience | It returns Total, New to you and Emailable with breakdowns, quality, the compliance summary, a price estimate and a count id. | Answers an error that names count_audience, because the filters it read were held in a session that no longer exists. |
preview_records | preview_records | The new preview_records keeps the name and takes an audience or a count_id instead; a call in the old form fails validation with a next_step naming count_audience. | Not listed under its old form, because the new tool has the same name. A call in the old form fails validation with a next step that names count_audience. |
save_selections | save_audience | It saves an audience definition shared across the organisation; saved lists carry over as audiences (aud_). | Answers an error that names save_audience, because the filters it saved were held in a session that no longer exists. |
get_saved_selections | list_audiences | Saved lists carry over as audiences (aud_) in the same organisation, with their current counts. | Runs list_audiences. An offset above 0 is refused: pass next_cursor instead. |
create_data_extract | quote_order, then create_order | The flag is ignored, since a flag set by a model is never consent: the call makes a machine quote for the list, and the user confirms the spend in a form or on mydatahub.emailmovers.com, or an agent key covers it within its limits; otherwise nothing is charged and it returns the approval_url. | Ignores confirmed. It needs the aud_ id of a saved audience, counts it, makes a machine quote and goes through the same confirmation as create_order. With neither a person's approval nor an agent key, nothing is charged and it returns the approval_url. |
get_credits | get_account | It returns the balance by source, the plan, per-action prices, the rate limit and any agent key budget; balances carry over one for one. | Runs get_account and answers with its result. |
get_packs | list_field_sets | The old bundles become named field sets with their exact columns; the five test bundles are removed. | Runs list_field_sets and answers with its result. |
get_pack_details | list_field_sets | list_field_sets returns every field set with its exact columns and credits per record. | Runs list_field_sets and adds that the old bundle ids became field set names. |
What the old tools do meanwhile
During the window each deprecated tool answers as the last column above says. Nine run the tool that replaces them, three return an error that names the replacement, and clear_search succeeds and does nothing.
- The answer is the new tool's result, with a "Deprecated:" line first and the replacement in
_meta["uk.mydatahub/deprecated"]. - A numeric
structureIdis mapped to a field name (the tables below),searchbecomesq, andlimitis capped at 100. Anoffsetabove 0 has no equivalent: passnext_cursorinstead. create_searchturns its filters into an audience and counts it. Includes becomein, and excludes go undernot. A range filter has no equivalent.get_countandsave_selectionsread and wrote filters held in a server session, and sessions no longer exist. They return an error that namescount_audienceandsave_audience.preview_recordsin the old form fails validation with anext_stepthat namescount_audience.clear_searchsucceeds and does nothing, because there is nothing to clear.create_data_extractignoresconfirmed. It makes a machine quote for the saved list and goes through the same confirmation ascreate_order. With neither a person's approval nor an agent key, nothing is charged and it returns theapproval_url.
A retried create_data_extract for the same list, field set and volume reuses the live quote and the same idempotency key, so it finds the order it made and cannot buy a second batch. For another batch, call quote_order and then create_order with a new idempotency_key. A saved list's old numeric id has no mapping: call list_audiences to find its aud_ id.
Old ids to field names
These old structureId values each map to a field. Several old ids can stand for one field.
Old structureId | New field |
|---|---|
| 1 | site.region |
| 2 | site.town |
| 3 | site.postcode |
| 5 | company.turnover_band |
| 6 | company.employee_band |
| 8 | email.type |
| 9, 20 | company.business_class |
| 10, 11, 12 | company.sic_2007 |
| 14, 18, 31 | contact.job_function |
| 17 | company.fleet_band |
| 21 | site.county |
| 27 | company.type |
| 28 | company.status |
| 30 | contact.job_level_rank |
These old ids have no field. A call that names one is refused with a next step.
Old structureId | What the old catalogue called it | What to use now |
|---|---|---|
| 15 | Company name | lookup_company finds a company by its name. |
| 34 | Email | enrich_contact looks a person up by email address. |
| 23 | Email universe | The defaults, which exclude generic addresses and dissolved companies. email.exists and email.type are the filters. |
| 22 | Phone universe | phone.exists. |
| 24 | Mail universe | No equivalent field. |
| 19 | TPS | phone.tps_ctps_status. Whenever a phone is requested, numbers a screen found TPS or CTPS registered are left out by default; numbers not screened are kept and say so. |
| 13 | Tag | No equivalent field: tags were for internal use. |
| 25, 26 | Matched jobs and Match prospects | No equivalent field: they were internal matching references. |
| 29 | Research | No equivalent field: an internal research universe. |
| 36 | Internal quality flag | No equivalent field. |
Old bundles to field sets
A telephone bundle becomes standard_telephone, the Full bundle becomes full, and every other bundle is delivered as standard. Check the columns with list_field_sets before you rely on one.
Old packId | What the old catalogue called it | Field set |
|---|---|---|
| 545 | Test1 | standard, because the test bundles are removed |
| 548 | Standard | standard |
| 549 | Postal | standard |
| 550 | Telephone | standard_telephone |
| 551 | Postal and Telephone | standard_telephone |
| 552 | Business Class | standard |
| 553 | Job Level | standard |
| 554 | Job Function | standard |
| 555 | Full | full |
| 591 | Linked In Pack | standard |
| 599 | Job_Level_NEW | standard |
| 607 | TEST | standard, because the test bundles are removed |
| 613 | Eventbrite Pack | standard, because the test bundles are removed |
| 615 | Testtag Pack | standard, because the test bundles are removed |
| 618 | Sample | standard |
| 622 | Boost + LinkedIn | standard |
| 762 | Phone Pack | standard_telephone |
| 783 | Website Only | standard |
| 822 | Data with Tel | standard_telephone |
| 824 | Telephone Pack | standard_telephone |
| 826 | Email Pack | standard |
| 837 | STANDARD PLUS LINKEDIN | standard |
| 843 | STANDARD PLUS WEBISTE | standard |
| 848 | Skeleton | standard |
| 887 | Company Number | standard |
| 906 | Agent Pack | standard |
| 936 | 84013 | standard, because the test bundles are removed |
| 968 | TEL_JOB_LEVEL | standard_telephone |
| 1036 | Linkedin PLUS TEL | standard_telephone |
Before and after
The values in the old calls are illustrative. Use list_filters and search_filter_values for the codes your account sees.
1. Counting without a session
Before, an agent built the count up on the server, one call at a time.
available_selection_categories, to learn the numeric ids.available_selection_valueswith{ "structureId": 6 }, to learn the codes.create_searchwith the filters to include or exclude.get_count, to read the number held in the session.
{
"selections": [
{ "structure": 6, "fieldValue": "E", "incEx": 1 },
{ "structure": 28, "fieldValue": "dissolved", "incEx": 2 }
]
}After, one call carries everything and returns the count, the breakdowns and an id.
{
"name": "count_audience",
"arguments": {
"audience": {
"schema": "mydatahub.audience/1",
"dataset": "uk_contacts",
"where": {
"all": [
{ "field": "company.employee_band", "op": "gte", "value": "50-99" },
{ "not": { "field": "company.status", "op": "in", "values": ["dissolved"] } }
]
}
}
}
}There is nothing to clear and nothing to remember between calls. Dissolved companies are already excluded by default, and the exclusion above is only to show the shape. See The audience definition.
2. Finding a value
Before, available_selection_values took a numeric id and paged with offset.
{ "structureId": 10, "search": "manufact", "limit": 50 }After, search_filter_values takes the field name and pages with a cursor. It returns codes with contact and company counts, and did_you_mean for near misses.
{ "dimension": "company.sic_2007", "q": "manufact", "limit": 50 }3. Saving and listing
Before, save_selections saved whatever the session held, and get_saved_selections listed the saved lists. After, you save a definition by name and list audiences with their current counts.
{
"name": "Manufacturers, 50 or more staff",
"audience": {
"schema": "mydatahub.audience/1",
"where": {
"all": [
{ "field": "company.sic_2007", "op": "in", "values": ["C"] },
{ "field": "company.employee_band", "op": "gte", "value": "50-99" }
]
}
}
}That is the input of save_audience. Audiences are shared across your organisation, so the old forUserGroup and days arguments are not needed.
4. Ordering
Before, the model's own flag spent the credits.
{ "savedSelectionId": 1234, "packId": 548, "volumeRequired": 500, "confirmed": true }That is create_data_extract. The old packId 548 is the Standard bundle. After, a quote comes first and a person confirms.
quote_orderwith{ "count_id": "cnt_7K2PQ4MAXN3R6TBY5WDZ", "field_set": "standard", "limit": 500 }returns a machine quote.create_orderwith the quote and an idempotency key. The host asks the person to approve, or an agent key within its limits places the order.
{
"name": "create_order",
"arguments": {
"quote_id": "qte_01JB4M2R6T1Y5W8ZAK8Q2V7XN3",
"idempotency_key": "6f1c3b2a-9d4e-4f8a-b1c2-7e5d9a0f3c21",
"name": "Leeds marketing Q4"
}
}Every old packId and its field set is in Old bundles to field sets. See Spending safely.
5. Getting the records
Before, the legacy download endpoint paged records by number. It took OrderId, pageNumber and pageSize, and the key travelled in the X-Api-Key header.
POST https://app.mydatahub.uk/api/RunExtract
X-Api-Key: <your key>After, GET /v1/orders/{id}/records pages by cursor with a bearer token. The default page is 50 records and the maximum 500, and a page can hold fewer when it reaches about 8,000 tokens.
curl "https://mydatahub.emailmovers.com/v1/orders/ord_01JB4N9S3W2X6Y7Z8A1B2C3D4E/records?limit=500" \
-H "Authorization: Bearer $MYDATAHUB_API_KEY"Follow next_cursor until has_more is false. Over MCP the tool is fetch_order_records, with at most 100 rows a page.
The plan is for legacy download responses to carry Deprecation and Sunset headers until the endpoint returns 410. The v2 API does not serve that endpoint yet.
6. Checking the balance
Before, get_credits returned the balance. After, get_account takes no arguments and returns the balance by source, the plan, per-action prices, the rate limit and any agent key budget.
{ "name": "get_account", "arguments": {} }Migration checklist
- Make an
mdh_test_key in the app, and point your integration at it. Test keys return synthetic data and never charge. - Replace
X-Api-KeywithAuthorization: Bearer. - Replace the session calls with one
count_audiencecall per audience. - Replace
create_data_extractand the legacy download endpoint withquote_order,create_orderandfetch_order_records. - Decide how spends are confirmed: a person in the app, or an agent key with a budget and a per-order cap. See Authentication.
- Subscribe to
order.readyinstead of polling. See Webhooks. - Switch to an
mdh_live_key when the test run passes.
Questions
Do I need to reinstall the connector? No. https://mcp.mydatahub.uk keeps serving today's product after launch, so an install made there keeps working as it is.
Once the new server supports OAuth sign-in from the client, that address passes MCP through to https://mydatahub.emailmovers.com/mcp with no redirect. To use the 22 new tools before then, add the new server with a MyDataHub API key, as the Quickstart for agents shows.
Will my saved lists and credits survive? That is the plan. Saved lists become audiences in the same organisation, and the credit balance carries over one for one with licence dates unchanged (to confirm).
Can an old agent still spend? Only through a person's confirmation or an agent key. A flag set by a model is never consent, in the old tool as in the new ones.
What happens when the window closes? The plan is that the old tools stop being listed, and that the old header and endpoint return 410. See Changelog and deprecation policy.
Next steps
- Start with the Quickstart for agents or the Quickstart for developers.
- Browse the replacements in the MCP tool reference.