How to integrate Dynamics365 MCP with Claude Agent SDK

Connect Claude Agent SDK to Dynamics365 MCP. Create a new sales lead for acme corp, list all open invoices for this quarter, and more using natural language, with authentication handled for you.

Dynamics365 logoDynamics365
Oauth2

Dynamics 365 is Microsoft's platform combining CRM, ERP, and productivity apps. It streamlines sales, marketing, service, and operations in one place.

119 Tools

Introduction

This guide walks you through connecting Dynamics365 to the Claude Agent SDK using the Composio tool router. By the end, you'll have a working Dynamics365 agent that can manage accounts and contacts, capture leads, track opportunities, create quotes and invoices, and resolve support cases through natural language commands.

This guide will help you understand how to give your Claude Agent SDK agent real control over a Dynamics365 account through Composio's Dynamics365 MCP server.

Before we dive in, let's take a quick look at the key ideas and tools involved.

Also integrate Dynamics365 with

TL;DR

Here's what you'll learn:
  • Get and set up your Claude/Anthropic and Composio API keys
  • Install the necessary dependencies
  • Initialize Composio and create a Tool Router session for Dynamics365
  • Configure an AI agent that can use Dynamics365 as a tool
  • Run a live chat session where you can ask the agent to perform Dynamics365 operations

What is Claude Agent SDK?

The Claude Agent SDK is Anthropic's official framework for building AI agents powered by Claude. It provides a streamlined interface for creating agents with MCP tool support and conversation management.

Key features include:

  • Native MCP Support: Built-in support for Model Context Protocol servers
  • Permission Modes: Control tool execution permissions
  • Streaming Responses: Real-time response streaming for interactive applications
  • Context Manager: Clean async context management for sessions

What is the Dynamics365 MCP server, and what's possible with it?

The Dynamics 365 MCP server is an implementation of the Model Context Protocol that connects your AI agent and assistants like Claude, Cursor, etc directly to your Dynamics 365 account. It provides structured and secure access to your CRM records, so your agent can create accounts and contacts, manage leads and opportunities, handle customer cases, and process invoices and sales orders on your behalf.

  • Account and contact creation: Have your agent create new customer accounts and contact records in Dynamics 365.
  • Lead and opportunity management: Let the agent create leads and opportunities, retrieve lead details, list all leads, and update records as deals progress.
  • Customer case handling: Direct your agent to open new customer cases and update existing cases as issues move toward resolution.
  • Invoice management: Instruct your agent to create invoices, retrieve individual invoices or a full invoice list, and update invoice details.
  • Sales order processing: Have your agent create sales orders and update them when order information changes.

What is the Composio tool router, and how does it fit here?

What is Composio SDK?

Composio's Composio SDK helps agents find the right tools for a task at runtime. You can plug in multiple toolkits (like Gmail, HubSpot, and GitHub), and the agent will identify the relevant app and action to complete multi-step workflows. This can reduce token usage and improve the reliability of tool calls. Read more here: Getting started with Composio SDK

The tool router generates a secure MCP URL that your agents can access to perform actions.

How the Composio SDK works

The Composio SDK follows a three-phase workflow:

  1. Discovery: Searches for tools matching your task and returns relevant toolkits with their details.
  2. Authentication: Checks for active connections. If missing, creates an auth config and returns a connection URL via Auth Link.
  3. Execution: Executes the action using the authenticated connection.

Step-by-step Guide

Step by step09 STEPS
1

Prerequisites

Before starting, make sure you have:
  • Composio API Key and Claude/Anthropic API Key
  • Primary know-how of Claude Agents SDK
  • A Dynamics365 account
  • Some knowledge of Python
2

Getting API Keys for Claude/Anthropic and Composio

Claude/Anthropic API Key
  • Go to the Anthropic Console and create an API key. You'll need credits to use the models.
  • Keep the API key safe.
Composio API Key
  • Log in to the Composio dashboard.
  • Navigate to your API settings and generate a new API key.
  • Store this key securely as you'll need it for authentication.
3

Install dependencies

npm install @anthropic-ai/claude-agent-sdk @composio/core dotenv

Install the Composio SDK and the Claude Agents SDK.

What's happening:

  • @composio/core provides Composio integration for Anthropic
  • @anthropic-ai/claude-agent-sdk is the core agent framework
  • dotenv/config loads environment variables
4

Set up environment variables

bash
COMPOSIO_API_KEY=your_composio_api_key_here
USER_ID=your_user_id_here
ANTHROPIC_API_KEY=your_anthropic_api_key_here

Create a .env file in your project root.

What's happening:

  • COMPOSIO_API_KEY authenticates with Composio
  • USER_ID identifies the user for session management
  • ANTHROPIC_API_KEY authenticates with Anthropic/Claude
5

Import dependencies

import 'dotenv/config';
import readline from 'node:readline';
import { Composio } from '@composio/core';
import { query, type Options } from "@anthropic-ai/claude-agent-sdk";

dotenv.config();
What's happening:
  • We're importing all necessary libraries including the Claude Agent SDK and Composio
  • The dotenv.config() function loads environment variables from your .env file
  • This setup prepares the foundation for connecting Claude with Dynamics365 functionality
6

Create a Composio instance and Tool Router session

async function chat() {
  const { COMPOSIO_API_KEY, USER_ID } = process.env;
  if (!COMPOSIO_API_KEY || !USER_ID) {
    throw new Error('COMPOSIO_API_KEY and USER_ID required in .env');
  }

  const composio = new Composio({ apiKey: COMPOSIO_API_KEY });

  // Create Tool Router session for Dynamics365
  const session = await composio.create(USER_ID, {
    toolkits: ['dynamics365'],
  });
  const mcpUrl = session?.mcp.url;
What's happening:
  • The function checks for the required COMPOSIO_API_KEY environment variable
  • We're creating a Composio instance using our API key
  • The create method creates a Tool Router session for Dynamics365
  • The returned url is the MCP server URL that your agent will use
7

Configure Claude Agent with MCP

const options: Options = {
  permissionMode: 'bypassPermissions',
  mcpServers: {
    composio: {
      type: 'http',
      url: mcpUrl,
      headers: { 'x-api-key': COMPOSIO_API_KEY }
    }
  },
  systemPrompt: 'You are a helpful assistant with access to Dynamics365 tools via Composio.',
  maxTurns: 10,
};
What's happening:
  • We're configuring the Claude Agent options with the MCP server URL
  • permissionMode: 'bypassPermissions' allows the agent to execute operations without asking for permission each time
  • The system prompt instructs the agent that it has access to Dynamics365
  • maxTurns: 10 limits the conversation length to prevent excessive API usage
8

Create client and start chat loop

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: 'You: '
  });

  console.log('\nChat started. Type "exit" to quit.\n');

  let isProcessing = false;

  async function ask(prompt: string) {
    isProcessing = true;
    rl.pause();

    process.stdout.write('Claude is thinking...');
    const stream = query({ prompt, options });

    let firstChunk = true;
    for await (const msg of stream) {
      const content = (msg as any).message?.content || (msg as any).content;
      if (Array.isArray(content)) {
        for (const block of content) {
          if (block.type === 'text' && block.text) {
            if (firstChunk) {
              process.stdout.write('\r\x1b[K');
              process.stdout.write('Claude: ');
              firstChunk = false;
            }
            process.stdout.write(block.text);
          }
        }
      }
    }
    process.stdout.write('\n\n');

    isProcessing = false;
    rl.resume();
    rl.prompt();
  }

  rl.on('line', async (line) => {
    if (isProcessing) return;

    const input = line.trim();
    if (input === 'exit') {
      rl.close();
      process.exit(0);
    }
    if (input) await ask(input);
    else rl.prompt();
  });

  await ask('What can you help me with?');
}
What's happening:
  • The readline interface is created to handle user input and output
  • The query function is used to send the user's input to the agent
  • The chat loop continues until the user types 'exit' or 'quit'
9

Run the application

try {
  await chat();
} catch (error) {
  console.error(error);
  process.exit(1);
}
What's happening:
  • The chat function is the entry point for the application
  • The try-catch block is used to handle any errors that occur

Complete Code

Here's the complete code to get you started with Dynamics365 and Claude Agent SDK:

import 'dotenv/config';
import readline from 'node:readline';
import { Composio } from '@composio/core';
import { query, type Options } from "@anthropic-ai/claude-agent-sdk";

async function chat() {
  const { COMPOSIO_API_KEY, USER_ID } = process.env;
  if (!COMPOSIO_API_KEY || !USER_ID) {
    throw new Error('COMPOSIO_API_KEY and USER_ID required in .env');
  }

  const composio = new Composio({ apiKey: COMPOSIO_API_KEY });
  const session = await composio.create(USER_ID, {
    toolkits: ['dynamics365']
  });
  const mcp_url = session?.mcp.url;

  const options: Options = {
    permissionMode: 'bypassPermissions',
    mcpServers: {
      composio: {
        type: 'http',
        url: mcp_url,
        headers: { 'x-api-key': COMPOSIO_API_KEY }
      }
    },
    systemPrompt: 'You are a helpful assistant with access to Dynamics365 tools via Composio.',
    maxTurns: 10,
  };

  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: 'You: '
  });

  console.log('\nChat started. Type "exit" to quit.\n');

  let isProcessing = false;

  async function ask(prompt: string) {
    isProcessing = true;
    rl.pause();

    process.stdout.write('Claude is thinking...');
    const stream = query({ prompt, options });

    let firstChunk = true;
    for await (const msg of stream) {
      const content = (msg as any).message?.content || (msg as any).content;
      if (Array.isArray(content)) {
        for (const block of content) {
          if (block.type === 'text' && block.text) {
            if (firstChunk) {
              process.stdout.write('\r\x1b[K');
              process.stdout.write('Claude: ');
              firstChunk = false;
            }
            process.stdout.write(block.text);
          }
        }
      }
    }
    process.stdout.write('\n\n');

    isProcessing = false;
    rl.resume();
    rl.prompt();
  }

  rl.on('line', async (line) => {
    if (isProcessing) return;

    const input = line.trim();
    if (input === 'exit') {
      rl.close();
      process.exit(0);
    }
    if (input) await ask(input);
    else rl.prompt();
  });

  await ask('What can you help me with?');
}

try {
  await chat();
} catch (error) {
  console.error(error);
  process.exit(1);
}

Conclusion

You've successfully built a Claude Agent SDK agent that can interact with Dynamics365 through Composio's Tool Router.

Key features:

  • Native MCP support through Claude's agent framework
  • Streaming responses for real-time interaction
  • Permission bypass for smooth automated workflows
You can extend this by adding more toolkits, implementing custom business logic, or building a web interface around the agent.
TOOLS

Supported Tools

Every Dynamics365 action and event your agent gets out of the box.

Add Contact to Account

Associate an existing contact with an existing account in Dynamics 365.

Assign User Security Role

Assign a security role to a system user in the connected Dataverse environment.

Associate Contact Account

Sets an existing contact's parent account, replacing any current parent customer.

Clear Account Description

Clear an existing account's description to null without deleting the account.

Create Annotation

Create a text or attachment note, optionally linked to a supported record.

Create appointment

Create a Dataverse appointment from writable logical column values and return the created row.

Create Email Activity

Create and store a draft email activity without sending it.

Create invoice line item

Create a Dataverse invoice line item from writable logical column values and return the created row.

Create opportunity line item

Create a Dataverse opportunity line item from writable logical column values and return the created row.

Create Phone Call

Record a CRM phone-call activity; this does not place a telephone call.

Create price list

Create a Dataverse price list from writable logical column values and return the created row.

Create Product

Create a Dataverse catalog product and return its selected representation.

Create Product Price List Item

Add an existing product and unit to an existing Dynamics price list.

Create quote

Create a Dataverse quote from writable logical column values and return the created row.

Create quote line item

Create a Dataverse quote line item from writable logical column values and return the created row.

Create sales order line item

Create a Dataverse sales order line item from writable logical column values and return the created row.

Create Social Activity

Create a Dataverse social activity and return its stored representation.

Create system user

Create a Dataverse system user from writable logical column values and return the created row.

Create Task

Create a Dataverse task activity and return its selected representation.

Create transaction currency

Create a Dataverse transaction currency from writable logical column values and return the created row.

Delete account

Delete a Dataverse account by GUID.

Delete appointment

Delete a Dataverse appointment by GUID.

Delete case

Delete a Dataverse case by GUID.

Delete contact

Delete a Dataverse contact by GUID.

Delete email activity

Delete a Dataverse email activity by GUID.

Delete invoice

Delete a Dataverse invoice by GUID.

Delete invoice line item

Delete a Dataverse invoice line item by GUID.

Delete lead

Delete a Dataverse lead by GUID.

Delete note

Delete a Dataverse note by GUID.

Delete opportunity

Delete a Dataverse opportunity by GUID.

Delete opportunity line item

Delete a Dataverse opportunity line item by GUID.

Delete price list

Delete a Dataverse price list by GUID.

Delete product

Delete a Dataverse product by GUID.

Delete quote

Delete a Dataverse quote by GUID.

Delete quote line item

Delete a Dataverse quote line item by GUID.

Delete sales order

Delete a Dataverse sales order by GUID.

Delete sales order line item

Delete a Dataverse sales order line item by GUID.

Delete system user

Request deletion of a Dataverse system user by GUID.

Delete task

Delete a Dataverse task by GUID.

Delete transaction currency

Delete a Dataverse transaction currency by GUID.

Disassociate Contact Account

Remove a contact's parent account relationship without deleting either record.

Create Account

Creates a new account entity record in Dynamics CRM using the Web API.

Create Case

Creates a new case (incident) in Dynamics CRM using the Web API.

Create Contact

Creates a new contact entity record in Dynamics CRM using the Web API.

Create Invoice

Creates a new invoice entity record in Dynamics CRM using the Web API.

Create Lead

Creates a new lead entity record in Dynamics CRM using the Web API.

Create Opportunity

Creates a new opportunity entity record in Dynamics CRM using the Web API.

Create Sales Order

Creates a new sales order entity record in Dynamics CRM using the Web API.

Dynamicscrm get a invoice

Retrieve a Dynamics CRM invoice by ID, returning selected invoice details such as its number, dates, billing and shipping addresses, amounts and status.

Dynamicscrm get a lead

Dynamicscrm get a lead

Dynamicscrm get all leads

Retrieve one page of Dynamics CRM leads with optional OData query options.

Update Case

Updates a case (incident) in Dynamics CRM using the Web API.

Update Invoice

Update a Dynamics CRM invoice by ID using the Web API.

Update Lead

Updates an existing Dynamics CRM lead using the Web API.

Update Opportunity

Updates an existing Dynamics CRM opportunity using the Web API.

Update Sales Order

Updates an existing Dynamics CRM sales order using the Web API.

Execute Batch

Execute a Dataverse multipart batch, optionally containing atomic change sets.

Get account

Retrieve a Dataverse account by its GUID, optionally selecting columns and expanding relationships.

Get all invoices action

Retrieve one page of Dynamics 365 invoices with optional OData query options.

Get appointment

Retrieve a Dataverse appointment by its GUID, optionally selecting columns and expanding relationships.

Get Attribute Metadata

Retrieve one Dataverse attribute definition by its metadata IDs.

Get case

Retrieve a Dataverse case by its GUID, optionally selecting columns and expanding relationships.

Get contact

Retrieve a Dataverse contact by its GUID, optionally selecting columns and expanding relationships.

Get email activity

Retrieve a Dataverse email activity by its GUID, optionally selecting columns and expanding relationships.

Get invoice line item

Retrieve a Dataverse invoice line item by its GUID, optionally selecting columns and expanding relationships.

Get note

Retrieve a Dataverse note by its GUID, optionally selecting columns and expanding relationships.

Get opportunity

Retrieve a Dataverse opportunity by its GUID, optionally selecting columns and expanding relationships.

Get opportunity line item

Retrieve a Dataverse opportunity line item by its GUID, optionally selecting columns and expanding relationships.

Get price list

Retrieve a Dataverse price list by its GUID, optionally selecting columns and expanding relationships.

Get product

Retrieve a Dataverse product by its GUID, optionally selecting columns and expanding relationships.

Get quote

Retrieve a Dataverse quote by its GUID, optionally selecting columns and expanding relationships.

Get quote line item

Retrieve a Dataverse quote line item by its GUID, optionally selecting columns and expanding relationships.

Get sales order

Retrieve a Dataverse sales order by its GUID, optionally selecting columns and expanding relationships.

Get sales order line item

Retrieve a Dataverse sales order line item by its GUID, optionally selecting columns and expanding relationships.

Get system user

Retrieve a Dataverse system user by its GUID, optionally selecting columns and expanding relationships.

Get task

Retrieve a Dataverse task by its GUID, optionally selecting columns and expanding relationships.

Get transaction currency

Retrieve a Dataverse transaction currency by its GUID, optionally selecting columns and expanding relationships.

List appointments

List one page of Dataverse appointments.

List Entity Definitions

Discover Dataverse table definitions and return caller-selected metadata.

List entity sets

List available Dataverse entity sets and their resource URLs, not table rows.

List invoice line items

List one page of Dataverse invoice line items.

List notes

List one page of Dataverse notes.

List opportunity line items

List one page of Dataverse opportunity line items.

List price lists

List one page of Dataverse price lists.

List products

List one page of Dataverse products.

List quote line items

List one page of Dataverse quote line items.

List quotes

List one page of Dataverse quotes.

List sales orders

List one page of Dataverse sales orders.

List system users

List one page of Dataverse system users.

List transaction currencies

List one page of Dataverse transaction currencies.

Query accounts

List or filter Dynamics accounts with projection, ordering, and bounded results.

Query campaigns

List, filter, expand, or aggregate Dynamics campaigns.

Query cases

Return one bounded page of cases; pass next_link back to fetch the next page.

Query contacts

List or filter Dynamics contacts with selected fields and ordering.

Query email activities

Return one bounded page of email activities; pass next_link back to fetch the next page.

Query Entity Data

Query contacts, incidents, phone calls, activity pointers, opportunities, or sales order details and return one page with its next link.

Query opportunities

Return one bounded page of opportunities; pass next_link back to fetch the next page.

Query phone calls

Return one bounded page of phone-call activities; pass next_link back to fetch the next page.

Query social activities

Return one bounded page of social activities; pass next_link back to fetch the next page.

Query tasks

Return one bounded page of tasks; pass next_link back to fetch the next page.

Remove Contact from Account

Remove an existing contact from an account without deleting either record.

Resolve Case

Resolve an active case by creating its incident-resolution activity.

Update account

Update only the supplied writable columns of an existing Dataverse account and return the updated row.

Update Account Name

Updates only the name of an existing Dynamics 365 account.

Update appointment

Update only the supplied writable columns of an existing Dataverse appointment and return the updated row.

Update contact

Update only the supplied writable columns of an existing Dataverse contact and return the updated row.

Update email activity

Update only the supplied writable columns of an existing Dataverse email activity and return the updated row.

Update invoice line item

Update only the supplied writable columns of an existing Dataverse invoice line item and return the updated row.

Update note

Update only the supplied writable columns of an existing Dataverse note and return the updated row.

Update opportunity line item

Update only the supplied writable columns of an existing Dataverse opportunity line item and return the updated row.

Update price list

Update only the supplied writable columns of an existing Dataverse price list and return the updated row.

Update product

Update only the supplied writable columns of an existing Dataverse product and return the updated row.

Update quote

Update only the supplied writable columns of an existing Dataverse quote and return the updated row.

Update quote line item

Update only the supplied writable columns of an existing Dataverse quote line item and return the updated row.

Update sales order line item

Update only the supplied writable columns of an existing Dataverse sales order line item and return the updated row.

Update system user

Update only the supplied writable columns of an existing Dataverse system user and return the updated row.

Update task

Update only the supplied writable columns of an existing Dataverse task and return the updated row.

Update transaction currency

Update only the supplied writable columns of an existing Dataverse transaction currency and return the updated row.

Get Caller Identity

Return the connected Dataverse caller's identity IDs.

FAQ

Frequently asked questions

Start with Dynamics365.It takes 30 seconds.

Managed auth, hosted MCP servers, and every Dynamics365 tool your agent needs.Free to start.

Start building