Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { registerDownloadDemoStepPrettyHTMLTool } from './tools/download-demo-st
import { VERSION } from './constants';
import { registerCapturePagePrompt } from './tools/capture-page-prompt';
import { registerImportPagePrompt } from './tools/import-page-prompt';
import { registerQueryUimap } from './tools/query-uimap';
import { registerFindUimapPath } from './tools/find-uimap-path';
import { registerReportUimapFeedback } from './tools/report-uimap-feedback';

async function main() {
const server = new McpServer({
Expand All @@ -16,6 +19,11 @@ async function main() {
registerDownloadDemoStepPrettyHTMLTool(server);
registerCapturePagePrompt(server);
registerImportPagePrompt(server);

// Uimap tools
registerQueryUimap(server);
registerFindUimapPath(server);
registerReportUimapFeedback(server);

await server.connect(new StdioServerTransport());
}
Expand Down
46 changes: 46 additions & 0 deletions src/tools/find-uimap-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { getApiFetch } from '../utils/request';
import { createToolExecuter } from './base';

const Name = 'find_uimap_path';

const Description = `
Find the shortest navigation path between two URLs using an existing Uimap.

Use this when you:
- Know your current page and target page
- Need step-by-step navigation instructions
- Want to verify a navigation path exists
`;

const Schema = z.object({
url: z.string().describe('DemoWay app URL (e.g., "https://app.demoway.com")'),
uimapId: z.string().describe('The Uimap ID to use for path finding'),
from: z.string().describe('Source URL (current page)'),
to: z.string().describe('Target URL (destination page)'),
});

const executer = createToolExecuter<typeof Schema.shape>(async ({ url, uimapId, from, to }, extra) => {
const $apiFetch = getApiFetch(url);
const result = await $apiFetch(`/api/uimap/${uimapId}/path`, {
query: {
from,
to,
},
signal: extra.signal,
});

return {
content: [
{
type: 'text',
text: JSON.stringify(result),
},
],
};
});

export function registerFindUimapPath(server: McpServer) {
server.tool(Name, Description, Schema.shape, executer);
}
51 changes: 51 additions & 0 deletions src/tools/query-uimap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { getApiFetch } from '../utils/request';
import { createToolExecuter } from './base';

const Name = 'query_uimap';

const Description = `
Query Uimap to get the best action plan for completing a task on a specific domain.
This tool returns a structured action plan including:
1. Primary approach: Direct navigation to target page with actions
2. Fallback approach: Step-by-step navigation from entry point

Use this when you need to:
- Navigate to a specific page on a website
- Perform a task like "add a DNS record" or "create a new project"
- Understand the navigation structure of a website
`;

const Schema = z.object({
url: z.string().describe('DemoWay app URL (e.g., "https://app.demoway.com")'),
domain: z.string().describe('The target domain to query (e.g., "dash.cloudflare.com")'),
task: z.string().describe('The task description (e.g., "add a DNS A record")'),
uimapId: z.string().optional().describe('Optional: specific Uimap ID to use (if not provided, best available map will be selected)'),
});

const executer = createToolExecuter<typeof Schema.shape>(async ({ url, domain, task, uimapId }, extra) => {
const $apiFetch = getApiFetch(url);
const result = await $apiFetch('/api/uimap/query', {
method: 'POST',
body: {
domain,
task,
uimapId,
},
signal: extra.signal,
});

return {
content: [
{
type: 'text',
text: JSON.stringify(result),
},
],
};
});

export function registerQueryUimap(server: McpServer) {
server.tool(Name, Description, Schema.shape, executer);
}
57 changes: 57 additions & 0 deletions src/tools/report-uimap-feedback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { getApiFetch } from '../utils/request';
import { createToolExecuter } from './base';

const Name = 'report_uimap_feedback';

const Description = `
Report feedback about Uimap path execution results.
This helps improve Uimap quality by tracking which paths work and which don't.

Use this tool after executing a path to:
- Report successful execution (improves map score)
- Report failed execution with details (helps identify issues)

The feedback will be used to:
1. Calculate Uimap scores
2. Rank maps by reliability
3. Identify outdated or broken paths
`;

const Schema = z.object({
url: z.string().describe('DemoWay app URL (e.g., "https://app.demoway.com")'),
uimapId: z.string().describe('The Uimap ID that was used'),
task: z.string().describe('The task that was attempted'),
success: z.boolean().describe('Whether the execution was successful'),
failedAtStep: z.number().optional().describe('If failed, which step number failed (1-based)'),
failReason: z.string().optional().describe('If failed, description of what went wrong'),
});

const executer = createToolExecuter<typeof Schema.shape>(async ({ url, uimapId, task, success, failedAtStep, failReason }, extra) => {
const $apiFetch = getApiFetch(url);
const result = await $apiFetch('/api/uimap/feedback', {
method: 'POST',
body: {
uimapId,
task,
success,
failedAtStep,
failReason,
},
signal: extra.signal,
});

return {
content: [
{
type: 'text',
text: JSON.stringify(result),
},
],
};
});

export function registerReportUimapFeedback(server: McpServer) {
server.tool(Name, Description, Schema.shape, executer);
}