Skip to content
Closed
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
27 changes: 27 additions & 0 deletions packages/cli/src/commands/new/templatehook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { z } from 'zod';

import { ProjectBuilder } from '../../project';

type TemplateHook = (builder: ProjectBuilder) => Promise<void>;

export const templateHooks: Record<string, TemplateHook> = {
'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: ');
Comment on lines +12 to +13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great if the template could provide these, and it would go through all the questions

rl.close();

// Validate the MCP URL
const result = z.string().url().safeParse(mcpUrl);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're putting this in "new" but it's very specific to a particular template.

if (!result.success) {
console.error('❌ Invalid MCP URL.');
process.exit(1);
}

// Add env variables
builder.addEnv('SERVER_NAME', serverName);
builder.addEnv('MCP_URL', mcpUrl);
}
};
11 changes: 9 additions & 2 deletions packages/cli/src/commands/new/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -65,8 +67,8 @@ export function Typescript(_: IContext): CommandModule<{}, z.infer<typeof ArgsSc
type: 'string',
describe: 'include M365 Agents Toolkit configuration',
choices: fs.readdirSync(atkPath)
.filter((type) => fs.existsSync(path.join(atkPath, type, 'typescript')))
.flat()
.filter((type) => fs.existsSync(path.join(atkPath, type, 'typescript')))
.flat()
})
.option('client-id', {
type: 'string',
Expand Down Expand Up @@ -133,6 +135,11 @@ export function Typescript(_: IContext): CommandModule<{}, z.infer<typeof ArgsSc
builder.addEnv('AZURE_OPENAI_ENDPOINT', process.env.AZURE_OPENAI_ENDPOINT);
}

// Template-specific logic
if (templateHooks[template]) {
await templateHooks[template](builder);
}

const project = builder.build();
await project.up();
console.log(
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/teams/v1.20/MicrosoftTeams.schema.json",
"version": "1.0.0",
"manifestVersion": "1.20",
"id": "$\{{TEAMS_APP_ID}}",
"name": {
"short": "{{ toKebabCase name }}-$\{{APP_NAME_SUFFIX}}",
"full": "{{ capitalize name }}"
},
"developer": {
"name": "Microsoft",
"websiteUrl": "https://microsoft.com",
"privacyUrl": "https://privacy.microsoft.com/privacystatement",
"termsOfUseUrl": "https://www.microsoft.com/legal/terms-of-use"
},
"description": {
"short": "Agent connected to MCP",
"full": "This agent integrates with a single MCP server to respond to messages."
},
"icons": {
"outline": "outline.png",
"color": "color.png"
},
"accentColor": "#FFFFFF",
"staticTabs": [
{
"entityId": "conversations",
"scopes": ["personal"]
},
{
"entityId": "about",
"scopes": ["personal"]
}
],
"bots": [
{
"botId": "$\{{BOT_ID}}",
"scopes": ["personal", "team", "groupChat"],
"isNotificationOnly": false,
}
],
"validDomains": [
"$\{{BOT_DOMAIN}}",
"*.botframework.com"
],
"webApplicationInfo": {
"id": "$\{{BOT_ID}}",
"resource": "api://botid-$\{{BOT_ID}}"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions packages/cli/templates/typescript/mcpagent/package.json.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "{{ toKebabCase name }}",
"version": "0.0.0",
"license": "MIT",
"private": true,
"main": "dist/index",
"types": "dist/index",
"files": [
"dist",
"README.md"
],
"scripts": {
"clean": "npx rimraf ./dist",
"build": "npx tsc",
"start": "node .",
"dev": "npx nodemon -w \"./src/**\" -e ts --exec \"node -r ts-node/register -r dotenv/config ./src/index.ts\"",
"inspect": "npx cross-env SERVER_PORT=9000 npx @modelcontextprotocol/inspector -e NODE_NO_WARNINGS=1 -e PORT=3978 node -r dotenv/config .",
"dev:teamsfx": "npx cross-env NODE_OPTIONS='--inspect=9239' npx env-cmd -f .env npm run dev",
"dev:teamsfx:testtool": "npx cross-env NODE_OPTIONS='--inspect=9239' npx env-cmd -f .env npm run dev",
"dev:teamsfx:launch-testtool": "npx env-cmd --silent -f env/.env.testtool teamsapptester start"
},
"dependencies": {
"@microsoft/teams.api": "preview",
"@microsoft/teams.apps": "preview",
"@microsoft/teams.cards": "preview",
"@microsoft/teams.common": "preview",
"@microsoft/teams.dev": "preview",
"@microsoft/teams.graph": "preview",
"@microsoft/teams.mcp": "preview",
"@microsoft/teams.mcpclient": "preview",
"@microsoft/teams.openai": "preview",
"@modelcontextprotocol/sdk": "^1.7.0"
},
"devDependencies": {
"@modelcontextprotocol/inspector": "^0.6.0",
"@types/node": "^22.5.4",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"nodemon": "^3.1.4",
"rimraf": "^6.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"env-cmd": "latest"
}
}
42 changes: 42 additions & 0 deletions packages/cli/templates/typescript/mcpagent/src/index.ts.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { App } from '@microsoft/teams.apps';
import { DevtoolsPlugin } from '@microsoft/teams.dev';
import { ChatPrompt } from '@microsoft/teams.ai';
import { McpClientPlugin } from '@microsoft/teams.mcpclient';
import { OpenAIChatModel } from '@microsoft/teams.openai';
import { ConsoleLogger } from '@microsoft/teams.common/logging';

const logger = new ConsoleLogger('{{ toKebabCase name }}', { level: 'debug' });

const app = new App({
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.
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,
}, [new McpClientPlugin({ logger })])
.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: 'message', text: res.content });
});

(async () => {
await app.start();
})();
26 changes: 26 additions & 0 deletions packages/cli/templates/typescript/mcpagent/tsconfig.json
Original file line number Diff line number Diff line change
@@ -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
}
}
13 changes: 13 additions & 0 deletions packages/cli/templates/typescript/mcpagent/tsup.config.js
Original file line number Diff line number Diff line change
@@ -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'],
};