mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 12:53:32 +00:00
refactor(mcp): address review — split files, fix naming, stateless router
Split the monolithic mcp.router.ts into focused modules: - mcp.service.ts: coordination helpers (rundownListResponse, renameRundown, deleteRundown) - mcp.tools.ts: tool definitions (ontime_ prefix, annotation presets, shared field schemas, CHARACTER_LIMIT truncation) + handleToolCall dispatcher - mcp.prompts.ts: prompt definitions + handleGetPrompt - mcp.resources.ts: resource definitions (schema, live data, docs) + handleReadResource - mcp.server.ts: createMcpServer factory that wires the above modules - mcp.router.ts: thin stateless Express router (POST/GET405/DELETE405) Other changes per review comments: - Rename ontime_delete_event → ontime_delete_entry (entry vs event naming convention) - Remove hardcoded cue/colour prefix conventions from tool descriptions and prompts; replace with "ask the user what convention they prefer" - Remove automations tools (left for a later PR) - Add Bearer auth comment in authenticate.ts explaining the MCP use case - Extract McpSection component from FeaturePanel to isolate URL state re-renders - Update bulk_edit prompt to mention linkStart cascade behaviour - Add 405 comments explaining why GET/DELETE are not supported in stateless mode https://claude.ai/code/session_01U24MeuUacYXeQhbX3tatEe
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
|
||||
import { generateUrl } from '../../../../common/api/session';
|
||||
import CopyTag from '../../../../common/components/copy-tag/CopyTag';
|
||||
import useInfo from '../../../../common/hooks-query/useInfo';
|
||||
import { isOntimeCloud, serverURL } from '../../../../externals';
|
||||
import { isOntimeCloud } from '../../../../externals';
|
||||
import GenerateLinkFormExport from '../../../sharing/GenerateLinkFormExport';
|
||||
import type { PanelBaseProps } from '../../panel-list/PanelList';
|
||||
import * as Panel from '../../panel-utils/PanelUtils';
|
||||
import InfoNif from '../network-panel/NetworkInterfaces';
|
||||
import McpSection from './McpSection';
|
||||
import ReportSettings from './ReportSettings';
|
||||
import URLPresets from './URLPresets';
|
||||
|
||||
@@ -18,29 +14,6 @@ export default function FeaturePanel({ location }: PanelBaseProps) {
|
||||
const reportRef = useScrollIntoView<HTMLDivElement>('report', location);
|
||||
const mcpRef = useScrollIntoView<HTMLDivElement>('mcp', location);
|
||||
|
||||
const { data: infoData } = useInfo();
|
||||
const [mcpEndpointUrl, setMcpEndpointUrl] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const baseUrl = isOntimeCloud
|
||||
? serverURL
|
||||
: infoData.networkInterfaces.length > 0
|
||||
? `http://${infoData.networkInterfaces[0].address}:${infoData.serverPort}`
|
||||
: serverURL;
|
||||
|
||||
generateUrl({ baseUrl, path: '/mcp', authenticate: true, lockConfig: false, lockNav: false })
|
||||
.then(setMcpEndpointUrl)
|
||||
.catch(() => {
|
||||
setMcpEndpointUrl(`${baseUrl}/mcp`);
|
||||
});
|
||||
}, [infoData]);
|
||||
|
||||
const claudeDesktopConfig = JSON.stringify(
|
||||
{ mcpServers: { ontime: { url: mcpEndpointUrl } } },
|
||||
null,
|
||||
2,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Panel.Header>Sharing and reporting</Panel.Header>
|
||||
@@ -63,30 +36,7 @@ export default function FeaturePanel({ location }: PanelBaseProps) {
|
||||
</Panel.Section>
|
||||
</div>
|
||||
<div ref={mcpRef}>
|
||||
<Panel.Section>
|
||||
<Panel.Card>
|
||||
<Panel.SubHeader>MCP Server (AI Agent Integration)</Panel.SubHeader>
|
||||
<Panel.Paragraph>
|
||||
Connect AI agents (e.g. Claude Desktop) to Ontime via the Model Context Protocol endpoint.
|
||||
</Panel.Paragraph>
|
||||
<Panel.Divider />
|
||||
<Panel.Field
|
||||
title='MCP Endpoint URL'
|
||||
description='Use this URL in your MCP client configuration'
|
||||
/>
|
||||
{mcpEndpointUrl && (
|
||||
<CopyTag copyValue={mcpEndpointUrl}>{mcpEndpointUrl}</CopyTag>
|
||||
)}
|
||||
<Panel.Divider />
|
||||
<Panel.Field
|
||||
title='Claude Desktop config'
|
||||
description='Paste this into your claude_desktop_config.json under "mcpServers"'
|
||||
/>
|
||||
{mcpEndpointUrl && (
|
||||
<CopyTag copyValue={claudeDesktopConfig}>{claudeDesktopConfig}</CopyTag>
|
||||
)}
|
||||
</Panel.Card>
|
||||
</Panel.Section>
|
||||
<McpSection />
|
||||
</div>
|
||||
<div ref={reportRef}>
|
||||
<ReportSettings />
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { generateUrl } from '../../../../common/api/session';
|
||||
import CopyTag from '../../../../common/components/copy-tag/CopyTag';
|
||||
import useInfo from '../../../../common/hooks-query/useInfo';
|
||||
import { isOntimeCloud, serverURL } from '../../../../externals';
|
||||
import * as Panel from '../../panel-utils/PanelUtils';
|
||||
|
||||
/** MCP endpoint card — isolated so that URL state changes don't re-render the rest of FeaturePanel */
|
||||
export default function McpSection() {
|
||||
const { data: infoData } = useInfo();
|
||||
const [mcpEndpointUrl, setMcpEndpointUrl] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const baseUrl = isOntimeCloud
|
||||
? serverURL
|
||||
: infoData.networkInterfaces.length > 0
|
||||
? `http://${infoData.networkInterfaces[0].address}:${infoData.serverPort}`
|
||||
: serverURL;
|
||||
|
||||
generateUrl({ baseUrl, path: '/mcp', authenticate: true, lockConfig: false, lockNav: false })
|
||||
.then(setMcpEndpointUrl)
|
||||
.catch(() => {
|
||||
setMcpEndpointUrl(`${baseUrl}/mcp`);
|
||||
});
|
||||
}, [infoData]);
|
||||
|
||||
const mcpClientConfig = JSON.stringify({ mcpServers: { ontime: { url: mcpEndpointUrl } } }, null, 2);
|
||||
|
||||
return (
|
||||
<Panel.Section>
|
||||
<Panel.Card>
|
||||
<Panel.SubHeader>MCP Server</Panel.SubHeader>
|
||||
<Panel.Paragraph>Connect any MCP-compatible AI agent to Ontime using the endpoint below.</Panel.Paragraph>
|
||||
<Panel.Divider />
|
||||
<Panel.Field title='Endpoint URL' description='Add this URL to your MCP client settings' />
|
||||
{mcpEndpointUrl && <CopyTag copyValue={mcpEndpointUrl}>{mcpEndpointUrl}</CopyTag>}
|
||||
<Panel.Divider />
|
||||
<Panel.Field
|
||||
title='Client configuration snippet'
|
||||
description='Paste this into your AI agent settings under "mcpServers"'
|
||||
/>
|
||||
{mcpEndpointUrl && <CopyTag copyValue={mcpClientConfig}>{mcpClientConfig}</CopyTag>}
|
||||
</Panel.Card>
|
||||
</Panel.Section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user