mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-19 11:41:04 +02:00
feat(mcp): generic MCP tool source with per-node function filtering (#301)
* feat(mcp): generic MCP tool source with per-node function filtering
Adds a Model Context Protocol tool category: connect a customer MCP
server and expose its tools to the agent, with optional per-node
allow-listing of individual MCP functions.
- ToolCategory.MCP enum + alembic migration
- MCP definition validator and collision-safe function-name namespacing
- McpToolSession wrapper: graceful-degrade, per-call open/close lifecycle
- CustomToolManager MCP branch (schemas + proxy handlers)
- Per-node mcp_tool_filters threaded through DTO/graph/engine
- Best-effort discovered_tools catalog cache + POST /tools/{uuid}/mcp/refresh
- UI: MCP create/edit config, tabbed ToolSelector with per-node toggles
* feat: refactor for code standardisation and documentation
---------
Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
This commit is contained in:
parent
0097974444
commit
75839f9de5
40 changed files with 3028 additions and 137 deletions
|
|
@ -205,6 +205,7 @@ function CanvasPreview({
|
|||
<ToolBadges
|
||||
toolUuids={data.tool_uuids}
|
||||
onStaleUuidsDetected={onStaleTools}
|
||||
mcpToolFilters={data.mcp_tool_filters}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -396,14 +397,22 @@ export const GenericNode = memo(({ data, selected, id, type }: GenericNodeProps)
|
|||
const spec = bySpecName.get(type);
|
||||
|
||||
// ── Form state ─────────────────────────────────────────────────────
|
||||
const [values, setValues] = useState<Record<string, unknown>>(() =>
|
||||
spec ? seedValues(data, spec) : {},
|
||||
// mcp_tool_filters is not a spec property, so seedValues won't carry it;
|
||||
// seed merges it back in alongside the spec-derived values.
|
||||
const seed = useCallback(
|
||||
() =>
|
||||
spec
|
||||
? { ...seedValues(data, spec), mcp_tool_filters: data.mcp_tool_filters }
|
||||
: {},
|
||||
[data, spec],
|
||||
);
|
||||
|
||||
const [values, setValues] = useState<Record<string, unknown>>(seed);
|
||||
|
||||
// Re-seed once the spec arrives (initial fetch race).
|
||||
useEffect(() => {
|
||||
if (spec && Object.keys(values).length === 0) {
|
||||
setValues(seedValues(data, spec));
|
||||
setValues(seed());
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [spec]);
|
||||
|
|
@ -464,7 +473,11 @@ export const GenericNode = memo(({ data, selected, id, type }: GenericNodeProps)
|
|||
const isDirty = useMemo(() => {
|
||||
if (!spec) return false;
|
||||
const baseline = seedValues(data, spec);
|
||||
return propertyNames.some((n) => values[n] !== baseline[n]);
|
||||
if (propertyNames.some((n) => values[n] !== baseline[n])) return true;
|
||||
return (
|
||||
JSON.stringify(values.mcp_tool_filters ?? {}) !==
|
||||
JSON.stringify(data.mcp_tool_filters ?? {})
|
||||
);
|
||||
}, [values, data, spec, propertyNames]);
|
||||
|
||||
const handleSave = async () => {
|
||||
|
|
@ -478,12 +491,12 @@ export const GenericNode = memo(({ data, selected, id, type }: GenericNodeProps)
|
|||
};
|
||||
|
||||
const handleOpenChange = (newOpen: boolean) => {
|
||||
if (newOpen && spec) setValues(seedValues(data, spec));
|
||||
if (newOpen && spec) setValues(seed());
|
||||
setOpen(newOpen);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open && spec) setValues(seedValues(data, spec));
|
||||
if (open && spec) setValues(seed());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, open]);
|
||||
|
||||
|
|
@ -562,6 +575,18 @@ export const GenericNode = memo(({ data, selected, id, type }: GenericNodeProps)
|
|||
tools: tools ?? [],
|
||||
documents: documents ?? [],
|
||||
recordings: recordings ?? [],
|
||||
mcpToolFilters:
|
||||
(values.mcp_tool_filters as
|
||||
| Record<string, string[]>
|
||||
| undefined) ?? {},
|
||||
onMcpToolFiltersChange: (next) =>
|
||||
setValues((prev) => ({
|
||||
...prev,
|
||||
mcp_tool_filters:
|
||||
Object.keys(next).length > 0
|
||||
? next
|
||||
: undefined,
|
||||
})),
|
||||
}}
|
||||
/>
|
||||
{type === "trigger" && (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue