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
19 changes: 19 additions & 0 deletions integrations/outlook/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "es5",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "avoid",
"rangeStart": 0,
"rangeEnd": null,
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"endOfLine": "lf"
}
72 changes: 72 additions & 0 deletions integrations/outlook/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import js from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
import eslintPluginPrettier from 'eslint-plugin-prettier';
import unusedImports from 'eslint-plugin-unused-imports';
import typescriptEslint from 'typescript-eslint';

export default [
js.configs.recommended,
...typescriptEslint.configs.recommended,
eslintConfigPrettier,
{
files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'],
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module',
globals: {
console: 'readonly',
process: 'readonly',
Buffer: 'readonly',
__dirname: 'readonly',
__filename: 'readonly',
global: 'readonly',
},
},
plugins: {
import: importPlugin,
prettier: eslintPluginPrettier,
'unused-imports': unusedImports,
},
rules: {
'prettier/prettier': 'error',
'no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': [
'warn',
{
vars: 'all',
varsIgnorePattern: '^_',
args: 'after-used',
argsIgnorePattern: '^_',
},
],
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
],
'newlines-between': 'always',
},
],
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'warn',
},
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
},
];
60 changes: 60 additions & 0 deletions integrations/outlook/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "@core/outlook",
"version": "0.1.0",
"description": "Microsoft Outlook integration for Core",
"main": "./bin/index.js",
"module": "./bin/index.mjs",
"type": "module",
"files": [
"outlook",
"bin"
],
"bin": {
"outlook": "./bin/index.js"
},
"scripts": {
"build": "rimraf dist && bun build src/index.ts --outfile dist/index.js --target node --minify",
"lint": "eslint --ext js,ts,tsx src/ --fix",
"prettier": "prettier --config .prettierrc --write ."
},
"devDependencies": {
"@babel/preset-typescript": "^7.26.0",
"@types/node": "^18.0.20",
"@types/turndown": "^5.0.5",
"eslint": "^9.24.0",
"eslint-config-prettier": "^10.1.2",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-unused-imports": "^2.0.0",
"prettier": "^3.4.2",
"rimraf": "^3.0.2",
"tslib": "^2.8.1",
"typescript": "^4.7.2",
"@modelcontextprotocol/sdk": "^0.4.0",
"zod": "^3.22.4",
"zod-to-json-schema": "^3.22.1"
},
"publishConfig": {
"access": "public"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"dependencies": {
"axios": "^1.7.9",
"commander": "^12.0.0",
"@redplanethq/sdk": "0.1.9",
"turndown": "^7.2.0"
}
}
55 changes: 55 additions & 0 deletions integrations/outlook/src/account-create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import axios from 'axios';

export async function integrationCreate(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data: any
) {
const { oauthResponse, oauthParams } = data;

// Fetch user information from Microsoft Graph
let userEmail = null;
let userId = null;
let displayName = null;

try {
const userInfoResponse = await axios.get('https://graph.microsoft.com/v1.0/me', {
headers: {
Authorization: `Bearer ${oauthResponse.access_token}`,
},
});

userEmail = userInfoResponse.data.mail || userInfoResponse.data.userPrincipalName;
userId = userInfoResponse.data.id;
displayName = userInfoResponse.data.displayName;
} catch (error) {
console.error('Error fetching user info:', error);
}

const integrationConfiguration = {
access_token: oauthResponse.access_token,
refresh_token: oauthResponse.refresh_token,
client_id: oauthResponse.client_id,
client_secret: oauthResponse.client_secret,
token_type: oauthResponse.token_type,
expires_in: oauthResponse.expires_in,
expires_at: oauthResponse.expires_at,
scope: oauthResponse.scope,
userEmail: userEmail,
userId: userId,
displayName: displayName,
redirect_uri: oauthParams.redirect_uri || null,
};

const payload = {
settings: {},
accountId: integrationConfiguration.userEmail || integrationConfiguration.userId,
config: integrationConfiguration,
};

return [
{
type: 'account',
data: payload,
},
];
}
109 changes: 109 additions & 0 deletions integrations/outlook/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
IntegrationCLI,
IntegrationEventPayload,
IntegrationEventType,
Spec,
} from '@redplanethq/sdk';

import { integrationCreate } from './account-create';
import { handleSchedule } from './schedule';
import { getTools, callTool } from './mcp';
import { fileURLToPath } from 'url';

export async function run(eventPayload: IntegrationEventPayload) {
switch (eventPayload.event) {
case IntegrationEventType.SETUP:
return await integrationCreate(eventPayload.eventBody);

case IntegrationEventType.SYNC:
return await handleSchedule(eventPayload.config, eventPayload.state);

case IntegrationEventType.GET_TOOLS: {
const tools = await getTools();

return tools;
}

case IntegrationEventType.CALL_TOOL: {
const integrationDefinition = eventPayload.integrationDefinition;

if (!integrationDefinition) {
return null;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = eventPayload.config as any;
const { name, arguments: args } = eventPayload.eventBody;

const result = await callTool(
name,
args,
integrationDefinition.config.clientId,
integrationDefinition.config.clientSecret,
config?.redirect_uri,
config
);

return result;
}

default:
return { message: `The event payload type is ${eventPayload.event}` };
}
}

class OutlookCLI extends IntegrationCLI {
constructor() {
super('outlook', '1.0.0');
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected async handleEvent(eventPayload: IntegrationEventPayload): Promise<any> {
return await run(eventPayload);
}

protected async getSpec(): Promise<Spec> {
return {
name: 'Outlook extension',
key: 'outlook',
description:
'Connect your workspace to Microsoft Outlook. Monitor emails, manage calendar events, and handle contacts via Microsoft Graph API',
icon: 'outlook',
schedule: {
frequency: '*/15 * * * *',
},
auth: {
OAuth2: {
token_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
authorization_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
scopes: [
'openid',
'profile',
'email',
'offline_access',
'https://graph.microsoft.com/Mail.ReadWrite',
'https://graph.microsoft.com/Mail.Send',
'https://graph.microsoft.com/Calendars.ReadWrite',
'https://graph.microsoft.com/Contacts.ReadWrite',
'https://graph.microsoft.com/User.Read',
],
scope_identifier: 'scope',
scope_separator: ' ',
token_params: {},
authorization_params: {
response_type: 'code',
},
},
},
} as any;
}
}

function main() {
const outlookCLI = new OutlookCLI();
outlookCLI.parse();
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}
14 changes: 14 additions & 0 deletions integrations/outlook/src/mcp/generated-tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Auto-generated tool stubs for Microsoft Outlook (Graph API)
// Custom tools are defined in index.ts

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const generatedTools: any[] = [];

export async function handleGeneratedTool(
name: string,
_args: Record<string, unknown>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_client: any
): Promise<{ content: Array<{ type: string; text: string }> }> {
throw new Error(`Unknown generated tool: ${name}`);
}
Loading