前端的AI路之:用MCP做一个日程助理

话不多说,先演示一下吧。大概功能描述就是,告诉AI“添加日历,今天下午五点到六点,我要去万达吃饭”,然后AI自动将日程同步到日历。

准备工作

开发这个日程助理需要用到MCP、Mac(mac的日历能力)、Windsurf(运行mcp)。技术栈是Typescript。

思路

基于MCP我们可以做很多。关于这个日程助理,其实也是很简单一个尝试,其实就是再验证一下我对MCP的使用。因为Siri的原因,让我刚好有了这个想法,尝试一下自己搞个日程助理。


我的思路如下: 让大模型理解一下我的意图,然后执行相关操作。这也是我对MCP的理解(执行相关操作)。因此要做日程助理,那就很简单了。首先搞一个脚本,能够自动调用mac并添加日历,然后再包装成MCP,最后引入大模型就ok了。顺着这个思路,接下来就讲讲如何实现吧

实现

第一步:在mac上添加日历

这里我们需要先明确一个概念。mac上给日历添加日程,其实是就是给对应的日历类型添加日程。举个例子

左边红框其实就是日历类型,比如我要添加一个开发日程,其实就是先选择"开发"日历,然后在该日历下添加日程。因此如果我们想通过脚本形式创建日程,其实就是先看日历类型存在不存在,如果存在,就在该类型下添加一个日程。

因此这里第一步,我们先获取mac上有没有对应的日历,没有的话就创建一个。

1.1 查找日历


参考文档 mac查找日历


假定我们的日历类型叫做 日程助手。 这里我使用了applescript的语法,因为JavaScript的方式我这运行有问题。


```dart

import { execSync } from 'child_process';


function checkCalendarExists(calendarName) {


    const   Script = `tell application "Calendar"

set theCalendarName to "${calendarName}"

set theCalendar to first calendar where its name = theCalendarName

end tell`;



  // 执行并解析结果

  try {

    const result = execSync(`osascript  -e '${Script}'`, { 

      encoding: 'utf-8',

      stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

    });


    console.log(result);

    return true;

  } catch (error) {

    console.error('检测失败:', error.message);

    return false;

  }

}


// 使用示例

const calendarName = '日程助手';

const exists = checkCalendarExists(calendarName);

console.log(`日历 "${calendarName}" 存在:`, exists ? '✅ 是' : '❌ 否');




```

附赠检验结果

现在我们知道了怎么判断日历存不存在,那么接下来就是,在日历不存在的时候创建日历

1.2 日历创建


```dart

import { execSync } from 'child_process';



// 创建日历

function  createCalendar(calendarName) {

    const script = `tell application "Calendar"

        make new calendar with properties {name:"${calendarName}"}

    end tell`;


    try {


        execSync(`osascript -e '${script}'`, { 

            encoding: 'utf-8',

            stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

        });


        return true;

    } catch (e) {

        console.log('create fail', e)

        return false;

    }

}


// 检查日历是否存在

function checkCalendarExists(calendarName) {

    ....

}


// 使用示例

const calendarName = '日程助手';

const exists = checkCalendarExists(calendarName);

console.log(`日历 "${calendarName}" 存在:`, exists ? '✅ 是' : '❌ 否');


if (!exists) {

  const res =    createCalendar(calendarName);


  console.log(res ? '✅ 创建成功' : '❌ 创建失败')

}



```

运行结果

接下来就是第三步了,在日历“日程助手”下创建日程


1.3 创建日程


```dart

import { execSync } from 'child_process';


// 创建日程

function createCalendarEvent(calendarName, config) {


    const script = `var app = Application.currentApplication()

    app.includeStandardAdditions = true

    var Calendar = Application("Calendar")

     

    var eventStart = new Date(${config.startTime})

    var eventEnd = new Date(${config.endTime})

     

    var projectCalendars = Calendar.calendars.whose({name: "${calendarName}"})

    var projectCalendar = projectCalendars[0]

    var event = Calendar.Event({summary: "${config.title}", startDate: eventStart, endDate: eventEnd, description: "${config.description}"})

    projectCalendar.events.push(event)

    event`


    try {

        console.log('开始创建日程');

        execSync(`  osascript -l JavaScript -e '${script}'`, { 

            encoding: 'utf-8',

            stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

        });

        console.log('✅ 日程添加成功');

    } catch (error) {

        console.error('❌ 执行失败:', error);

    }


}


// 创建日历

function  createCalendar(calendarName) {

   ....

}


// 检查日历是否存在

function checkCalendarExists(calendarName) {


 ...

}



```

这里我们完善一下代码


```dart

import { execSync } from 'child_process';


function handleCreateEvent(config) {

    const calendarName = '日程助手';

    const exists = checkCalendarExists(calendarName);

    // console.log(`日历 "${calendarName}" 存在:`, exists ? '✅ 是' : '❌ 否');


    if (!exists) {

        const createRes =  createCalendar(calendarName);


        console.log(createRes ? '✅ 创建日历成功' : '❌ 创建日历失败')


        if (createRes) {

            createCalendarEvent(calendarName, config)

        }

    }  else {

        createCalendarEvent(calendarName, config)

    }

}


// 创建日程

function createCalendarEvent(calendarName, config) {


    const script = `var app = Application.currentApplication()

    app.includeStandardAdditions = true

    var Calendar = Application("Calendar")

     

    var eventStart = new Date(${config.startTime})

    var eventEnd = new Date(${config.endTime})

     

    var projectCalendars = Calendar.calendars.whose({name: "${calendarName}"})

    var projectCalendar = projectCalendars[0]

    var event = Calendar.Event({summary: "${config.title}", startDate: eventStart, endDate: eventEnd, description: "${config.description}"})

    projectCalendar.events.push(event)

    event`


    try {

        console.log('开始创建日程');

        execSync(`  osascript -l JavaScript -e '${script}'`, { 

            encoding: 'utf-8',

            stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

        });

        console.log('✅ 日程添加成功');

    } catch (error) {

        console.error('❌ 执行失败:', error);

    }


}


// 创建日历

function  createCalendar(calendarName) {

    const script = `tell application "Calendar"

        make new calendar with properties {name:"${calendarName}"}

    end tell`;


    try {


        execSync(`osascript -e '${script}'`, { 

            encoding: 'utf-8',

            stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

        });


        return true;

    } catch (e) {

        console.log('create fail', e)

        return false;

    }

}


// 检查日历是否存在

function checkCalendarExists(calendarName) {


    const   Script = `tell application "Calendar"

set theCalendarName to "${calendarName}"

set theCalendar to first calendar where its name = theCalendarName

end tell`;



  // 执行并解析结果

  try {

    const result = execSync(`osascript  -e '${Script}'`, { 

      encoding: 'utf-8',

      stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

    });


    return true;

  } catch (error) {

    return false;

  }

}



// 运行示例


const eventConfig = {

    title: '团队周会',

    startTime: 1744183538021,

    endTime: 1744442738000,

    description: '每周项目进度同步',

};


handleCreateEvent(eventConfig)



```

运行结果

这就是一个完善的,可以直接在终端运行的创建日程的脚本的。接下来我们要做的就是,让大模型理解这个脚本,并学会使用这个脚本

第二步: 定义MCP

基于第一步,我们已经完成了这个日程助理的基本功能,接下来就是借助MCP的能力,教会大模型知道有这个函数,以及怎么调用这个函数


```dart

// 引入 mcp

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

import { z } from "zod";


// 声明MCP服务

const server = new McpServer({

    name: "mcp_calendar",

    version: "1.0.0"

  });


...

// 添加日历函数 也就是告诉大模型 有这个东西以及怎么用

server.tool("add_mac_calendar", '给mac日历添加日程, 接受四个参数  startTime, endTime是起止时间(格式为YYYY-MM-DD HH:MM:SS) title是日历标题  description是日历描述', { startTime: z.string(), endTime: z.string(), title: z.string(), description: z.string() },

async ({ startTime, endTime, title, description }) => {

    const res =  handleCreateEvent({

        title: title,

        description: description,

        startTime: new Date(startTime).getTime(),

        endTime: new Date(endTime).getTime()

      });

      return {

        content: [{ type: "text", text: res ? '添加成功' : '添加失败' }]

      }

})



// 初始化服务

const transport = new StdioServerTransport();

await server.connect(transport);



```

这里附上完整的ts代码


```dart

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

import { execSync } from 'child_process';

import { z } from "zod";



export interface EventConfig {

    // 日程标题

  title: string;

  // 日程开始时间 毫秒时间戳

  startTime: number;

  // 日程结束时间 毫秒时间戳

  endTime: number;

//   日程描述

  description: string;

}


const server = new McpServer({

    name: "mcp_calendar",

    version: "1.0.0"

  });


function handleCreateEvent(config: EventConfig) {

    const calendarName = '日程助手';

    const exists = checkCalendarExists(calendarName);

    // console.log(`日历 "${calendarName}" 存在:`, exists ? '✅ 是' : '❌ 否');


    let res = false;


    if (!exists) {

        const createRes =  createCalendar(calendarName);


        console.log(createRes ? '✅ 创建日历成功' : '❌ 创建日历失败')


        if (createRes) {

           res = createCalendarEvent(calendarName, config)

        }

    }  else {

        res = createCalendarEvent(calendarName, config)

    }


    return res

}


// 创建日程

function createCalendarEvent(calendarName: string, config: EventConfig) {


    const script = `var app = Application.currentApplication()

    app.includeStandardAdditions = true

    var Calendar = Application("Calendar")

     

    var eventStart = new Date(${config.startTime})

    var eventEnd = new Date(${config.endTime})

     

    var projectCalendars = Calendar.calendars.whose({name: "${calendarName}"})

    var projectCalendar = projectCalendars[0]

    var event = Calendar.Event({summary: "${config.title}", startDate: eventStart, endDate: eventEnd, description: "${config.description}"})

    projectCalendar.events.push(event)

    event`


    try {

        console.log('开始创建日程');

        execSync(`  osascript -l JavaScript -e '${script}'`, { 

            encoding: 'utf-8',

            stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

        });

        console.log('✅ 日程添加成功');


        return true

    } catch (error) {

        console.error('❌ 执行失败:', error);

        return false

    }


}


// 创建日历

function  createCalendar(calendarName: string) {

    const script = `tell application "Calendar"

        make new calendar with properties {name:"${calendarName}"}

    end tell`;


    try {


        execSync(`osascript -e '${script}'`, { 

            encoding: 'utf-8',

            stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

        });


        return true;

    } catch (e) {

        console.log('create fail', e)

        return false;

    }

}


// 检查日历是否存在

function checkCalendarExists(calendarName: string) {


    const   Script = `tell application "Calendar"

set theCalendarName to "${calendarName}"

set theCalendar to first calendar where its name = theCalendarName

end tell`;



  // 执行并解析结果

  try {

    const result = execSync(`osascript  -e '${Script}'`, { 

      encoding: 'utf-8',

      stdio: ['pipe', 'pipe', 'ignore'] // 忽略错误输出

    });


    return true;

  } catch (error) {

    return false;

  }

}



server.tool("add_mac_calendar", '给mac日历添加日程, 接受四个参数  startTime, endTime是起止时间(格式为YYYY-MM-DD HH:MM:SS) title是日历标题  description是日历描述', { startTime: z.string(), endTime: z.string(), title: z.string(), description: z.string() },

async ({ startTime, endTime, title, description }) => {

    const res =  handleCreateEvent({

        title: title,

        description: description,

        startTime: new Date(startTime).getTime(),

        endTime: new Date(endTime).getTime()

      });

      return {

        content: [{ type: "text", text: res ? '添加成功' : '添加失败' }]

      }

})


const transport = new StdioServerTransport();

await server.connect(transport);




```

第三步: 导入Windsurf

在前文已经讲过如何引入到Windsurf,这里就不过多赘述了。 其实在build之后,完全可以引入其他支持MCP的软件基本都是可以的。

接下来就是愉快的调用时间啦。


资料推荐


💡[大模型中转API推荐](搜索  一步API)



©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容