Skip to content

Commit e1b50d2

Browse files
authored
Merge pull request #35 from dreamfactorysoftware/fix/custom-tool-save-on-create
fix: persist custom tools on service create and on re-save without ids
2 parents c0892eb + c2551c4 commit e1b50d2

2 files changed

Lines changed: 46 additions & 6 deletions

File tree

src/Models/McpCustomTool.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public static function getAllForService(int $serviceId): array
8585
public static function syncToolsForService(int $serviceId, array $tools): void
8686
{
8787
$existing = static::where('service_id', $serviceId)->get()->keyBy('id');
88+
$existingByName = $existing->keyBy('name');
8889
$receivedIds = [];
8990

9091
foreach ($tools as $toolData) {
@@ -104,8 +105,19 @@ public static function syncToolsForService(int $serviceId, array $tools): void
104105
}
105106
}
106107

108+
// Resolve the matching existing row — by id first, then by name.
109+
// The name fallback matters when the UI saves a tool right after
110+
// creating it: the server assigned an id, but the UI never
111+
// refreshed its form state so it resends with id missing and we'd
112+
// otherwise hit the (service_id, name) unique constraint.
113+
$tool = null;
107114
if (!empty($toolData['id']) && $existing->has($toolData['id'])) {
108115
$tool = $existing->get($toolData['id']);
116+
} elseif (!empty($toolData['name']) && $existingByName->has($toolData['name'])) {
117+
$tool = $existingByName->get($toolData['name']);
118+
}
119+
120+
if ($tool) {
109121
$tool->update($toolData);
110122
if ($tool->wasChanged(['storage_service_id', 'storage_path', 'scm_repository', 'scm_reference'])) {
111123
Cache::forget(self::buildScmCacheKey(

src/Models/McpServerConfig.php

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ public static function getConfig($id, $local_config = null, $protect = true)
5555

5656
/**
5757
* Override to handle custom_tools sync on save.
58+
*
59+
* On initial service create, $id is null here (the service has not been
60+
* inserted yet). The sync runs from storeConfig() below, which fires
61+
* after the service row is created with a real id.
5862
*/
5963
public static function setConfig($id, $config, $local_config = null)
6064
{
@@ -63,12 +67,36 @@ public static function setConfig($id, $config, $local_config = null)
6367

6468
parent::setConfig($id, $config, $local_config);
6569

66-
if (is_array($customTools)) {
67-
try {
68-
McpCustomTool::syncToolsForService($id, $customTools);
69-
} catch (\Exception $e) {
70-
// Table may not exist yet if migration hasn't run
71-
}
70+
if ($id && is_array($customTools)) {
71+
self::syncCustomTools((int) $id, $customTools);
72+
}
73+
}
74+
75+
/**
76+
* Override to sync custom_tools when a new service's config is stored
77+
* for the first time (post-insert, when the service id is known).
78+
*/
79+
public static function storeConfig($id, $config)
80+
{
81+
$customTools = $config['custom_tools'] ?? null;
82+
unset($config['custom_tools']);
83+
84+
parent::storeConfig($id, $config);
85+
86+
if ($id && is_array($customTools)) {
87+
self::syncCustomTools((int) $id, $customTools);
88+
}
89+
}
90+
91+
private static function syncCustomTools(int $serviceId, array $customTools): void
92+
{
93+
try {
94+
McpCustomTool::syncToolsForService($serviceId, $customTools);
95+
} catch (\Throwable $e) {
96+
\Log::error('Failed to sync MCP custom tools', [
97+
'service_id' => $serviceId,
98+
'error' => $e->getMessage(),
99+
]);
72100
}
73101
}
74102

0 commit comments

Comments
 (0)