Import and remove an MCP server's tools
Imports and refreshes an MCP server’s tools, and removes the tools you name in remove. For each imported remote tool, it creates one tool row, or refreshes the row if the tool is already imported. Removing a tool also detaches it from every agent and skill that referenced it. The intent must be explicit: if you omit a name, nothing is removed. Every requested name is checked against what the server currently offers, so a stale or invented name is rejected per item instead of stored. Values are re-read from the server on every call, so this is also the re-sync operation. projectId is required as a query parameter.
curl --request POST \
--url https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"import": [
"search_issues",
"create_issue"
],
"remove": [
"550e8400-e29b-41d4-a716-446655440000"
],
"reviewedToolNames": [
"search_issues",
"create_issue"
]
}
'import requests
url = "https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync"
payload = {
"import": ["search_issues", "create_issue"],
"remove": ["550e8400-e29b-41d4-a716-446655440000"],
"reviewedToolNames": ["search_issues", "create_issue"]
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
import: ['search_issues', 'create_issue'],
remove: ['550e8400-e29b-41d4-a716-446655440000'],
reviewedToolNames: ['search_issues', 'create_issue']
})
};
fetch('https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'import' => [
'search_issues',
'create_issue'
],
'remove' => [
'550e8400-e29b-41d4-a716-446655440000'
],
'reviewedToolNames' => [
'search_issues',
'create_issue'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync"
payload := strings.NewReader("{\n \"import\": [\n \"search_issues\",\n \"create_issue\"\n ],\n \"remove\": [\n \"550e8400-e29b-41d4-a716-446655440000\"\n ],\n \"reviewedToolNames\": [\n \"search_issues\",\n \"create_issue\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"import\": [\n \"search_issues\",\n \"create_issue\"\n ],\n \"remove\": [\n \"550e8400-e29b-41d4-a716-446655440000\"\n ],\n \"reviewedToolNames\": [\n \"search_issues\",\n \"create_issue\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"import\": [\n \"search_issues\",\n \"create_issue\"\n ],\n \"remove\": [\n \"550e8400-e29b-41d4-a716-446655440000\"\n ],\n \"reviewedToolNames\": [\n \"search_issues\",\n \"create_issue\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"items": [
{
"error": "<string>",
"status": "added",
"toolId": "507f1f77bcf86cd799439011",
"toolName": "search_issues",
"toolReferenceId": "550e8400-e29b-41d4-a716-446655440000"
}
],
"total": 2
}{
"detail": "request body validation failed",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
],
"instance": "/v1/{resource}",
"status": 400,
"title": "Bad Request",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "access denied: insufficient permissions",
"instance": "/v1/{resource}/{id}",
"status": 403,
"title": "Forbidden",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "the requested resource does not exist",
"instance": "/v1/{resource}/{id}",
"status": 404,
"title": "Not Found",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "internal server error",
"instance": "/v1/{resource}",
"status": 500,
"title": "Internal Server Error",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "the MCP server did not respond",
"instance": "/v1/{resource}/{id}",
"status": 502,
"title": "Bad Gateway",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}Authorizations
[Production] API key for production authentication
Path Parameters
The unique identifier for the mcp server.
Query Parameters
The unique identifier for the project.
Body
What to import and what to remove
Import names remote tools to import, exactly as discovery reported them. A name that already has a row is an idempotent refresh, which is what makes this the re-sync operation too.
["search_issues", "create_issue"]
Remove names imported rows by their tool referenceId. Deleting a row IS the opt-out, and it detaches the tool from every agent and skill that referenced it.
["550e8400-e29b-41d4-a716-446655440000"]
ReviewedToolNames is the catalogue the client actually DISPLAYED to the customer in the dialog it is now saving — every remote tool name it showed, imported or not.
It is what makes declining a tool a recordable decision, and it has to come
from the client because only the client knows what it rendered. The
customer saw the discovery that opened the dialog; the server discovers
again at Save, so a tool added in between would otherwise be stamped as
reviewed without ever having been shown, and newSinceReview would never
flag it.
OPTIONAL and back-compatible. Omit it and the server falls back to the
catalogue it discovers during this request, which is the previous
behaviour. An explicit empty array is a real answer ("the server offered
nothing") and is recorded as one, so send null/omit rather than [] when
you mean "I am not telling you".
["search_issues", "create_issue"]
curl --request POST \
--url https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"import": [
"search_issues",
"create_issue"
],
"remove": [
"550e8400-e29b-41d4-a716-446655440000"
],
"reviewedToolNames": [
"search_issues",
"create_issue"
]
}
'import requests
url = "https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync"
payload = {
"import": ["search_issues", "create_issue"],
"remove": ["550e8400-e29b-41d4-a716-446655440000"],
"reviewedToolNames": ["search_issues", "create_issue"]
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
import: ['search_issues', 'create_issue'],
remove: ['550e8400-e29b-41d4-a716-446655440000'],
reviewedToolNames: ['search_issues', 'create_issue']
})
};
fetch('https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'import' => [
'search_issues',
'create_issue'
],
'remove' => [
'550e8400-e29b-41d4-a716-446655440000'
],
'reviewedToolNames' => [
'search_issues',
'create_issue'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync"
payload := strings.NewReader("{\n \"import\": [\n \"search_issues\",\n \"create_issue\"\n ],\n \"remove\": [\n \"550e8400-e29b-41d4-a716-446655440000\"\n ],\n \"reviewedToolNames\": [\n \"search_issues\",\n \"create_issue\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"import\": [\n \"search_issues\",\n \"create_issue\"\n ],\n \"remove\": [\n \"550e8400-e29b-41d4-a716-446655440000\"\n ],\n \"reviewedToolNames\": [\n \"search_issues\",\n \"create_issue\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api-trial.cognigy.ai/v1/mcp-servers/{id}/tools/sync")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"import\": [\n \"search_issues\",\n \"create_issue\"\n ],\n \"remove\": [\n \"550e8400-e29b-41d4-a716-446655440000\"\n ],\n \"reviewedToolNames\": [\n \"search_issues\",\n \"create_issue\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"items": [
{
"error": "<string>",
"status": "added",
"toolId": "507f1f77bcf86cd799439011",
"toolName": "search_issues",
"toolReferenceId": "550e8400-e29b-41d4-a716-446655440000"
}
],
"total": 2
}{
"detail": "request body validation failed",
"errors": [
{
"field": "<string>",
"message": "<string>"
}
],
"instance": "/v1/{resource}",
"status": 400,
"title": "Bad Request",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "access denied: insufficient permissions",
"instance": "/v1/{resource}/{id}",
"status": 403,
"title": "Forbidden",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "the requested resource does not exist",
"instance": "/v1/{resource}/{id}",
"status": 404,
"title": "Not Found",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "internal server error",
"instance": "/v1/{resource}",
"status": 500,
"title": "Internal Server Error",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}{
"detail": "the MCP server did not respond",
"instance": "/v1/{resource}/{id}",
"status": 502,
"title": "Bad Gateway",
"traceId": "api--a4d2f126-bec4-4544-a049-7c0cf08524ed",
"type": "about:blank"
}