From 3e50b65167f0be70e5085248abf21695c1937fd6 Mon Sep 17 00:00:00 2001 From: Teddy Arida-Moody Date: Fri, 8 Aug 2025 12:40:16 -0700 Subject: [PATCH 1/7] mcpagent cli command --- packages/cli/src/commands/new/templatehook.ts | 26 ++++++++++ packages/cli/src/commands/new/typescript.ts | 11 +++- .../typescript/mcpagent/appPackage/color.png | 3 ++ .../mcpagent/appPackage/manifest.json.hbs | 50 +++++++++++++++++++ .../mcpagent/appPackage/outline.png | 3 ++ .../typescript/mcpagent/package.json.hbs | 45 +++++++++++++++++ .../typescript/mcpagent/src/index.ts.hbs | 50 +++++++++++++++++++ .../typescript/mcpagent/tsconfig.json | 26 ++++++++++ .../typescript/mcpagent/tsup.config.js | 13 +++++ 9 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 packages/cli/src/commands/new/templatehook.ts create mode 100644 packages/cli/templates/typescript/mcpagent/appPackage/color.png create mode 100644 packages/cli/templates/typescript/mcpagent/appPackage/manifest.json.hbs create mode 100644 packages/cli/templates/typescript/mcpagent/appPackage/outline.png create mode 100644 packages/cli/templates/typescript/mcpagent/package.json.hbs create mode 100644 packages/cli/templates/typescript/mcpagent/src/index.ts.hbs create mode 100644 packages/cli/templates/typescript/mcpagent/tsconfig.json create mode 100644 packages/cli/templates/typescript/mcpagent/tsup.config.js diff --git a/packages/cli/src/commands/new/templatehook.ts b/packages/cli/src/commands/new/templatehook.ts new file mode 100644 index 000000000..fde5311dd --- /dev/null +++ b/packages/cli/src/commands/new/templatehook.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; + +import { ProjectBuilder } from '../../project'; + +type TemplateHook = (builder: ProjectBuilder, context: { + name: string; template: string; +}) => Promise; + +export const templateHooks: Record = { + 'mcpagent': async (builder, { name }) => { + const readline = await import('node:readline/promises'); + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + + const mcpUrl = await rl.question('Enter MCP server URL: '); + rl.close(); + + const result = z.string().url().safeParse(mcpUrl); + if (!result.success) { + console.error('❌ Invalid MCP URL.'); + process.exit(1); + } + + builder.addEnv('MCP_URL', mcpUrl); + builder.addEnv('AGENT_NAME', name); + } +}; \ No newline at end of file diff --git a/packages/cli/src/commands/new/typescript.ts b/packages/cli/src/commands/new/typescript.ts index f9955a048..8498dc76b 100644 --- a/packages/cli/src/commands/new/typescript.ts +++ b/packages/cli/src/commands/new/typescript.ts @@ -12,6 +12,8 @@ import { IContext } from '../../context'; import { Project } from '../../project'; import { Settings } from '../../settings'; +import { templateHooks } from './templatehook'; + const ArgsSchema = z.object({ name: z.string(), template: z.string(), @@ -65,8 +67,8 @@ export function Typescript(_: IContext): CommandModule<{}, z.infer fs.existsSync(path.join(atkPath, type, 'typescript'))) - .flat() + .filter((type) => fs.existsSync(path.join(atkPath, type, 'typescript'))) + .flat() }) .option('client-id', { type: 'string', @@ -133,6 +135,11 @@ export function Typescript(_: IContext): CommandModule<{}, z.infer { + const prompt = new ChatPrompt({ + instructions: + `You are a MCP Agent that is tied to the {{capitalize name}} MCP server. + Your main purpose is to call on tools available from the MCP server. + For ANY query related to {{capitalize name}}, use the tools to the best of your ability to answer.`, + model: new OpenAIChatModel({ + model: process.env.AOAI_MODEL!, + apiKey: process.env.AOAI_API_KEY!, + endpoint: process.env.AOAI_ENDPOINT!, + apiVersion: '2025-04-01-preview' + }), + logger, + }, [new McpClientPlugin({ logger })]) + .usePlugin('mcpClient', { + url: process.env.MCP_URL, + }); + + return prompt; +} + +const prompt = createMCPAgent('{{capitalize name}}', process.env.MCP_URL); + +app.on('message', async ({ send, activity }) => { + await send({ type: 'typing' }); + const res = await prompt.send(activity.text); + console.log(res); + + await send({ type: 'message', text: res.content }); +}); + +(async () => { + await app.start(); +})(); \ No newline at end of file diff --git a/packages/cli/templates/typescript/mcpagent/tsconfig.json b/packages/cli/templates/typescript/mcpagent/tsconfig.json new file mode 100644 index 000000000..c4efba63c --- /dev/null +++ b/packages/cli/templates/typescript/mcpagent/tsconfig.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "module": "NodeNext", + "target": "ESNext", + "moduleResolution": "NodeNext", + "strict": true, + "noImplicitAny": true, + "declaration": true, + "inlineSourceMap": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": false, + "resolveJsonModule": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "pretty": true, + "outDir": "dist", + "rootDir": "src", + "types": ["node"] + }, + "ts-node": { + "transpileOnly": true + } +} diff --git a/packages/cli/templates/typescript/mcpagent/tsup.config.js b/packages/cli/templates/typescript/mcpagent/tsup.config.js new file mode 100644 index 000000000..32277a72f --- /dev/null +++ b/packages/cli/templates/typescript/mcpagent/tsup.config.js @@ -0,0 +1,13 @@ +/** @type {import('tsup').Options} */ +module.exports = { + dts: true, + minify: false, + bundle: false, + sourcemap: true, + treeshake: true, + splitting: true, + clean: true, + outDir: 'dist', + entry: ['src/index.ts'], + format: ['cjs'], +}; From c272a53a5fa57c1a5e5339daecb4920d1b44663e Mon Sep 17 00:00:00 2001 From: Teddy Arida-Moody Date: Fri, 8 Aug 2025 21:38:34 -0700 Subject: [PATCH 2/7] adding mcpagent template to cli --- packages/cli/src/commands/new/templatehook.ts | 9 ++-- .../typescript/mcpagent/src/index.ts.hbs | 50 ++++++++----------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/packages/cli/src/commands/new/templatehook.ts b/packages/cli/src/commands/new/templatehook.ts index fde5311dd..07deb646a 100644 --- a/packages/cli/src/commands/new/templatehook.ts +++ b/packages/cli/src/commands/new/templatehook.ts @@ -7,20 +7,23 @@ type TemplateHook = (builder: ProjectBuilder, context: { }) => Promise; export const templateHooks: Record = { - 'mcpagent': async (builder, { name }) => { + 'mcpagent': async (builder) => { const readline = await import('node:readline/promises'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const serverName = await rl.question('Enter MCP server name: '); const mcpUrl = await rl.question('Enter MCP server URL: '); rl.close(); + // Validate the MCP URL const result = z.string().url().safeParse(mcpUrl); if (!result.success) { console.error('❌ Invalid MCP URL.'); process.exit(1); } + // Add env variables + builder.addEnv('SERVER_NAME', serverName); builder.addEnv('MCP_URL', mcpUrl); - builder.addEnv('AGENT_NAME', name); } -}; \ No newline at end of file +}; diff --git a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs index ed05b6d23..6d7140e8f 100644 --- a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs +++ b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs @@ -5,37 +5,31 @@ import { McpClientPlugin } from '@microsoft/teams.mcpclient'; import { OpenAIChatModel } from '@microsoft/teams.openai'; import { ConsoleLogger } from '@microsoft/teams.common/logging'; +const logger = new ConsoleLogger("learn-agent", { level: 'debug' }); + const app = new App({ - plugins: [ - new DevtoolsPlugin() - ] +plugins: [ +new DevtoolsPlugin(), +logger +] }); -const logger = new ConsoleLogger("{{ toKebabCase name }}", { level: 'debug' }); - - -export const createMCPAgent = (mcpServerName: string, mcpServerLink: string) => { - const prompt = new ChatPrompt({ - instructions: - `You are a MCP Agent that is tied to the {{capitalize name}} MCP server. - Your main purpose is to call on tools available from the MCP server. - For ANY query related to {{capitalize name}}, use the tools to the best of your ability to answer.`, - model: new OpenAIChatModel({ - model: process.env.AOAI_MODEL!, - apiKey: process.env.AOAI_API_KEY!, - endpoint: process.env.AOAI_ENDPOINT!, - apiVersion: '2025-04-01-preview' - }), - logger, - }, [new McpClientPlugin({ logger })]) - .usePlugin('mcpClient', { - url: process.env.MCP_URL, - }); - - return prompt; -} - -const prompt = createMCPAgent('{{capitalize name}}', process.env.MCP_URL); +const prompt = new ChatPrompt({ + instructions: + `You are a MCP Agent that is tied to the ${process.env.SERVER_NAME} MCP server. +Your main purpose is to call on tools available from the MCP server. +For ANY query related to ${process.env.SERVER_NAME}, use the tools to the best of your ability to answer.`, + model: new OpenAIChatModel({ + model: process.env.AOAI_MODEL!, + apiKey: process.env.AOAI_API_KEY!, + endpoint: process.env.AOAI_ENDPOINT!, + apiVersion: '2025-04-01-preview' + }), + logger, +}, [new McpClientPlugin({ logger })]) + .usePlugin('mcpClient', { + url: process.env.MCP_URL, + }); app.on('message', async ({ send, activity }) => { await send({ type: 'typing' }); From 34f4a2962891f3051c0a2d19d190a50bc7975764 Mon Sep 17 00:00:00 2001 From: Teddy Arida-Moody Date: Fri, 8 Aug 2025 21:42:52 -0700 Subject: [PATCH 3/7] removed unnecessary param for hook --- packages/cli/src/commands/new/templatehook.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli/src/commands/new/templatehook.ts b/packages/cli/src/commands/new/templatehook.ts index 07deb646a..777f72032 100644 --- a/packages/cli/src/commands/new/templatehook.ts +++ b/packages/cli/src/commands/new/templatehook.ts @@ -2,9 +2,7 @@ import { z } from 'zod'; import { ProjectBuilder } from '../../project'; -type TemplateHook = (builder: ProjectBuilder, context: { - name: string; template: string; -}) => Promise; +type TemplateHook = (builder: ProjectBuilder) => Promise; export const templateHooks: Record = { 'mcpagent': async (builder) => { From 46dab08a0d3b282d0605e0f504ee776e8c35e1f3 Mon Sep 17 00:00:00 2001 From: Teddy Arida-Moody Date: Fri, 8 Aug 2025 21:53:41 -0700 Subject: [PATCH 4/7] added option for AOAI_MODEL --- packages/cli/src/commands/new/typescript.ts | 2 +- packages/cli/templates/typescript/mcpagent/src/index.ts.hbs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/new/typescript.ts b/packages/cli/src/commands/new/typescript.ts index 8498dc76b..2fd823645 100644 --- a/packages/cli/src/commands/new/typescript.ts +++ b/packages/cli/src/commands/new/typescript.ts @@ -137,7 +137,7 @@ export function Typescript(_: IContext): CommandModule<{}, z.infer Date: Fri, 8 Aug 2025 22:06:53 -0700 Subject: [PATCH 5/7] changed env vars to match expected from cli --- packages/cli/templates/typescript/mcpagent/src/index.ts.hbs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs index 9318ffd02..65d4ca2bb 100644 --- a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs +++ b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs @@ -20,9 +20,9 @@ const prompt = new ChatPrompt({ Your main purpose is to call on tools available from the MCP server. For ANY query related to ${process.env.SERVER_NAME}, use the tools to the best of your ability to answer.`, model: new OpenAIChatModel({ - model: process.env.AOAI_MODEL! | 'gpt-4o', - apiKey: process.env.AOAI_API_KEY!, - endpoint: process.env.AOAI_ENDPOINT!, + model: 'gpt-4o', + apiKey: process.env.AZURE_OPENAI_API_KEY!, + endpoint: process.env.AZURE_OPENAI_ENDPOINT!, apiVersion: '2025-04-01-preview' }), logger, From 3102fa9ce3c1c1cf03429b50741ce4b7851ae2cb Mon Sep 17 00:00:00 2001 From: Teddy Arida-Moody Date: Fri, 8 Aug 2025 22:36:51 -0700 Subject: [PATCH 6/7] small tweaks --- .../typescript/mcpagent/package.json.hbs | 2 +- .../typescript/mcpagent/src/index.ts.hbs | 40 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/packages/cli/templates/typescript/mcpagent/package.json.hbs b/packages/cli/templates/typescript/mcpagent/package.json.hbs index fe34ed93f..4998d2884 100644 --- a/packages/cli/templates/typescript/mcpagent/package.json.hbs +++ b/packages/cli/templates/typescript/mcpagent/package.json.hbs @@ -27,7 +27,7 @@ "@microsoft/teams.dev": "preview", "@microsoft/teams.graph": "preview", "@microsoft/teams.mcp": "preview", - "@microsoft/teams.mcpclient": "^2.0.0-preview.8", + "@microsoft/teams.mcpclient": "preview", "@microsoft/teams.openai": "preview", "@modelcontextprotocol/sdk": "^1.7.0" }, diff --git a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs index 65d4ca2bb..054ea5563 100644 --- a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs +++ b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs @@ -5,40 +5,38 @@ import { McpClientPlugin } from '@microsoft/teams.mcpclient'; import { OpenAIChatModel } from '@microsoft/teams.openai'; import { ConsoleLogger } from '@microsoft/teams.common/logging'; -const logger = new ConsoleLogger("learn-agent", { level: 'debug' }); +const logger = new ConsoleLogger('{{ toKebabCase name }}', { level: 'debug' }); const app = new App({ -plugins: [ -new DevtoolsPlugin(), +plugins: [ new DevtoolsPlugin() ], logger -] }); const prompt = new ChatPrompt({ - instructions: - `You are a MCP Agent that is tied to the ${process.env.SERVER_NAME} MCP server. +instructions: +`You are an MCP Agent that is tied to the ${process.env.SERVER_NAME} MCP server. Your main purpose is to call on tools available from the MCP server. For ANY query related to ${process.env.SERVER_NAME}, use the tools to the best of your ability to answer.`, - model: new OpenAIChatModel({ - model: 'gpt-4o', - apiKey: process.env.AZURE_OPENAI_API_KEY!, - endpoint: process.env.AZURE_OPENAI_ENDPOINT!, - apiVersion: '2025-04-01-preview' - }), - logger, +model: new OpenAIChatModel({ +model: 'gpt-4o', +apiKey: process.env.AZURE_OPENAI_API_KEY!, +endpoint: process.env.AZURE_OPENAI_ENDPOINT!, +apiVersion: '2025-04-01-preview' +}), +logger, }, [new McpClientPlugin({ logger })]) - .usePlugin('mcpClient', { - url: process.env.MCP_URL, - }); +.usePlugin('mcpClient', { +url: process.env.MCP_URL!, +}); app.on('message', async ({ send, activity }) => { - await send({ type: 'typing' }); - const res = await prompt.send(activity.text); - console.log(res); +await send({ type: 'typing' }); +const res = await prompt.send(activity.text); +console.log(res); - await send({ type: 'message', text: res.content }); +await send({ type: 'message', text: res.content }); }); (async () => { - await app.start(); +await app.start(); })(); \ No newline at end of file From fe575961573becda9e8ef6f8f169dec2dace170a Mon Sep 17 00:00:00 2001 From: Teddy Arida-Moody Date: Fri, 8 Aug 2025 22:53:14 -0700 Subject: [PATCH 7/7] adjusted spacing --- .../typescript/mcpagent/src/index.ts.hbs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs index 054ea5563..3c856c094 100644 --- a/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs +++ b/packages/cli/templates/typescript/mcpagent/src/index.ts.hbs @@ -8,35 +8,35 @@ import { ConsoleLogger } from '@microsoft/teams.common/logging'; const logger = new ConsoleLogger('{{ toKebabCase name }}', { level: 'debug' }); const app = new App({ -plugins: [ new DevtoolsPlugin() ], -logger + plugins: [new DevtoolsPlugin()], + logger }); const prompt = new ChatPrompt({ -instructions: -`You are an MCP Agent that is tied to the ${process.env.SERVER_NAME} MCP server. + instructions: + `You are an MCP Agent that is tied to the ${process.env.SERVER_NAME} MCP server. Your main purpose is to call on tools available from the MCP server. For ANY query related to ${process.env.SERVER_NAME}, use the tools to the best of your ability to answer.`, -model: new OpenAIChatModel({ -model: 'gpt-4o', -apiKey: process.env.AZURE_OPENAI_API_KEY!, -endpoint: process.env.AZURE_OPENAI_ENDPOINT!, -apiVersion: '2025-04-01-preview' -}), -logger, + model: new OpenAIChatModel({ + model: 'gpt-4o', + apiKey: process.env.AZURE_OPENAI_API_KEY!, + endpoint: process.env.AZURE_OPENAI_ENDPOINT!, + apiVersion: '2025-04-01-preview' + }), + logger, }, [new McpClientPlugin({ logger })]) -.usePlugin('mcpClient', { -url: process.env.MCP_URL!, -}); + .usePlugin('mcpClient', { + url: process.env.MCP_URL!, + }); app.on('message', async ({ send, activity }) => { -await send({ type: 'typing' }); -const res = await prompt.send(activity.text); -console.log(res); + await send({ type: 'typing' }); + const res = await prompt.send(activity.text); + console.log(res); -await send({ type: 'message', text: res.content }); + await send({ type: 'message', text: res.content }); }); (async () => { -await app.start(); + await app.start(); })(); \ No newline at end of file